From 0a5e031e7b3c4e51250a5844418b2f36aaefc1e3 Mon Sep 17 00:00:00 2001 From: Shinrai Date: Mon, 28 Sep 2026 10:08:18 -0700 Subject: [PATCH 1/2] fix(typescript): ship strict mode's type-generation worker in the package `typescript: { mode: "strict" }` forks a worker to generate the api's declaration file, but the worker lived in tools/build/ and imported ../../src/..., and neither tools/ nor src/ is in package.json `files`. Strict mode could only work inside this repository; from an npm install it failed with MODULE_NOT_FOUND for tools/build/generate-types-worker.mjs. The worker now lives in src/lib/processors/type-generation-worker.mjs, ships as dist/lib/processors/, and imports only @cldmv/slothlet self-references, so it resolves to src/ under the slothlet-dev condition and to dist/ when installed. The loader resolves it with typeGenerationWorkerPath() on the Node-only strict path (path/url are null in browser mode, and the literal new URL("./x", import.meta.url) form makes vite load the worker into the current process). The worker's logic is exported (runTypeGeneration, runIfEntry) and only runs when the file is the process's entry script, so importing it never starts a build or exits the importer, and tests drive it in-process. A missing SLOTHLET_CONFIG raises TS_TYPE_GENERATION_WORKER_NO_CONFIG. Fixes #500 --- src/lib/i18n/languages/de-de.json | 4 +- src/lib/i18n/languages/en-gb.json | 4 +- src/lib/i18n/languages/en-us.json | 4 +- src/lib/i18n/languages/es-es.json | 4 +- src/lib/i18n/languages/es-mx.json | 4 +- src/lib/i18n/languages/fr-fr.json | 4 +- src/lib/i18n/languages/hi-in.json | 4 +- src/lib/i18n/languages/ja-jp.json | 6 +- src/lib/i18n/languages/ko-kr.json | 4 +- src/lib/i18n/languages/pt-br.json | 4 +- src/lib/i18n/languages/ru-ru.json | 4 +- src/lib/i18n/languages/zh-cn.json | 4 +- src/lib/processors/loader.mjs | 22 ++- src/lib/processors/type-generation-worker.mjs | 85 ++++++++++ ...cript-strict-mode-packaged.test.vitest.mjs | 154 ++++++++++++++++++ tools/build/generate-types-worker.mjs | 69 -------- tools/ci/i18n-latin-tokens-accepted.json | 19 ++- types/src/lib/processors/loader.d.mts | 12 ++ types/src/lib/processors/loader.d.mts.map | 2 +- .../processors/type-generation-worker.d.mts | 31 ++++ .../type-generation-worker.d.mts.map | 1 + 21 files changed, 356 insertions(+), 89 deletions(-) create mode 100644 src/lib/processors/type-generation-worker.mjs create mode 100644 tests/vitests/suites/typescript/typescript-strict-mode-packaged.test.vitest.mjs delete mode 100644 tools/build/generate-types-worker.mjs create mode 100644 types/src/lib/processors/type-generation-worker.d.mts create mode 100644 types/src/lib/processors/type-generation-worker.d.mts.map diff --git a/src/lib/i18n/languages/de-de.json b/src/lib/i18n/languages/de-de.json index 7df022983..929bd3f54 100644 --- a/src/lib/i18n/languages/de-de.json +++ b/src/lib/i18n/languages/de-de.json @@ -353,7 +353,9 @@ "TS_TYPE_GENERATION_FAILED": "Typ-Generierung fehlgeschlagen: {error}", "HINT_TS_TYPE_GENERATION_FAILED": "Der Kindprozess, der die TypeScript-Deklarationen generiert, ist auf einen Fehler gestoßen. Prüfen Sie die Fehlermeldung auf Details und stellen Sie sicher, dass die TypeScript-Quelldateien gültig sind.", "TS_TYPE_GENERATION_FORK_FAILED": "Fehler beim Forken des Typ-Generierungs-Prozesses: {error}", - "HINT_TS_TYPE_GENERATION_FORK_FAILED": "Der Worker-Prozess zur Typ-Generierung konnte nicht gestartet werden. Stellen Sie sicher, dass Node.js Berechtigungen zum Forken von Kindprozessen hat und das Script tools/build/generate-types-worker.mjs existiert.", + "HINT_TS_TYPE_GENERATION_FORK_FAILED": "Der Worker-Prozess zur Typ-Generierung konnte nicht gestartet werden. Stellen Sie sicher, dass Node.js Berechtigungen zum Forken von Kindprozessen hat und das Script lib/processors/type-generation-worker.mjs existiert.", + "TS_TYPE_GENERATION_WORKER_NO_CONFIG": "Der Worker zur Typ-Generierung wurde ohne seine Konfiguration gestartet (SLOTHLET_CONFIG ist nicht gesetzt).", + "HINT_TS_TYPE_GENERATION_WORKER_NO_CONFIG": "Dieser Worker wird vom strikten TypeScript-Modus mit seiner Konfiguration in der Umgebungsvariable SLOTHLET_CONFIG gestartet. Lassen Sie slothlet ihn forken, statt die Datei direkt auszuführen.", "TS_TYPE_GENERATION_PROCESS_EXITED": "Prozess zur Typ-Generierung mit Code {code} beendet: {output}", "HINT_TS_TYPE_GENERATION_PROCESS_EXITED": "Der Kindprozess zur Typ-Generierung wurde mit einem Nicht-Null-Code beendet. Prüfen Sie die Ausgabe auf Compiler-Fehler und stellen Sie sicher, dass die TypeScript-Quellen gültig sind.", "TS_TYPE_CHECK_ERRORS": "TypeScript-Typfehler in '{filePath}' gefunden:\n{errors}", diff --git a/src/lib/i18n/languages/en-gb.json b/src/lib/i18n/languages/en-gb.json index 69a6f5193..1c491eefe 100644 --- a/src/lib/i18n/languages/en-gb.json +++ b/src/lib/i18n/languages/en-gb.json @@ -353,7 +353,9 @@ "TS_TYPE_GENERATION_FAILED": "Type generation failed: {error}", "HINT_TS_TYPE_GENERATION_FAILED": "The child process that generates TypeScript declarations encountered an error. Check the error message for details and ensure the TypeScript source files are valid.", "TS_TYPE_GENERATION_FORK_FAILED": "Failed to fork the type generation process: {error}", - "HINT_TS_TYPE_GENERATION_FORK_FAILED": "The type generation worker process could not be started. Ensure Node.js has permission to fork child processes and that the tools/build/generate-types-worker.mjs script exists.", + "HINT_TS_TYPE_GENERATION_FORK_FAILED": "The type generation worker process could not be started. Ensure Node.js has permission to fork child processes and that the lib/processors/type-generation-worker.mjs script exists.", + "TS_TYPE_GENERATION_WORKER_NO_CONFIG": "The type generation worker was started without its configuration (SLOTHLET_CONFIG is not set).", + "HINT_TS_TYPE_GENERATION_WORKER_NO_CONFIG": "This worker is started by strict TypeScript mode with its configuration in the SLOTHLET_CONFIG environment variable. Let slothlet fork it rather than running the file directly.", "TS_TYPE_GENERATION_PROCESS_EXITED": "Type generation process exited with code {code}: {output}", "HINT_TS_TYPE_GENERATION_PROCESS_EXITED": "The type generation child process exited with a non-zero code. Check the output for compiler errors and verify the TypeScript sources are valid.", "TS_TYPE_CHECK_ERRORS": "TypeScript type errors found in '{filePath}':\n{errors}", diff --git a/src/lib/i18n/languages/en-us.json b/src/lib/i18n/languages/en-us.json index ee411e401..3abf3df9c 100644 --- a/src/lib/i18n/languages/en-us.json +++ b/src/lib/i18n/languages/en-us.json @@ -353,7 +353,9 @@ "TS_TYPE_GENERATION_FAILED": "Type generation failed: {error}", "HINT_TS_TYPE_GENERATION_FAILED": "The child process that generates TypeScript declarations encountered an error. Check the error message for details and ensure the TypeScript source files are valid.", "TS_TYPE_GENERATION_FORK_FAILED": "Failed to fork the type generation process: {error}", - "HINT_TS_TYPE_GENERATION_FORK_FAILED": "The type generation worker process could not be started. Ensure Node.js has permission to fork child processes and that the tools/build/generate-types-worker.mjs script exists.", + "HINT_TS_TYPE_GENERATION_FORK_FAILED": "The type generation worker process could not be started. Ensure Node.js has permission to fork child processes and that the lib/processors/type-generation-worker.mjs script exists.", + "TS_TYPE_GENERATION_WORKER_NO_CONFIG": "The type generation worker was started without its configuration (SLOTHLET_CONFIG is not set).", + "HINT_TS_TYPE_GENERATION_WORKER_NO_CONFIG": "This worker is started by strict TypeScript mode with its configuration in the SLOTHLET_CONFIG environment variable. Let slothlet fork it rather than running the file directly.", "TS_TYPE_GENERATION_PROCESS_EXITED": "Type generation process exited with code {code}: {output}", "HINT_TS_TYPE_GENERATION_PROCESS_EXITED": "The type generation child process exited with a non-zero code. Check the output for compiler errors and verify the TypeScript sources are valid.", "TS_TYPE_CHECK_ERRORS": "TypeScript type errors found in '{filePath}':\n{errors}", diff --git a/src/lib/i18n/languages/es-es.json b/src/lib/i18n/languages/es-es.json index 1c53dc7f7..e66ad48e4 100644 --- a/src/lib/i18n/languages/es-es.json +++ b/src/lib/i18n/languages/es-es.json @@ -353,7 +353,9 @@ "TS_TYPE_GENERATION_FAILED": "La generación de tipos falló: {error}", "HINT_TS_TYPE_GENERATION_FAILED": "El proceso hijo que genera las declaraciones de TypeScript encontró un error. Comprueba el mensaje de error para más detalles y asegúrate de que los ficheros fuente de TypeScript son válidos.", "TS_TYPE_GENERATION_FORK_FAILED": "Error al bifurcar (fork) el proceso de generación de tipos: {error}", - "HINT_TS_TYPE_GENERATION_FORK_FAILED": "El proceso trabajador de generación de tipos no pudo iniciarse. Asegúrate de que Node.js tiene permiso para bifurcar procesos hijos y que el script tools/build/generate-types-worker.mjs existe.", + "HINT_TS_TYPE_GENERATION_FORK_FAILED": "El proceso trabajador de generación de tipos no pudo iniciarse. Asegúrate de que Node.js tiene permiso para bifurcar procesos hijos y que el script lib/processors/type-generation-worker.mjs existe.", + "TS_TYPE_GENERATION_WORKER_NO_CONFIG": "El trabajador de generación de tipos se inició sin su configuración (SLOTHLET_CONFIG no está definida).", + "HINT_TS_TYPE_GENERATION_WORKER_NO_CONFIG": "El modo estricto de TypeScript inicia este trabajador con su configuración en la variable de entorno SLOTHLET_CONFIG. Deja que slothlet lo bifurque en lugar de ejecutar el archivo directamente.", "TS_TYPE_GENERATION_PROCESS_EXITED": "El proceso de generación de tipos terminó con el código {code}: {output}", "HINT_TS_TYPE_GENERATION_PROCESS_EXITED": "El proceso hijo de generación de tipos terminó con un código distinto de cero. Comprueba la salida en busca de errores del compilador y verifica que las fuentes de TypeScript son válidas.", "TS_TYPE_CHECK_ERRORS": "Errores de tipo TypeScript encontrados en '{filePath}':\n{errors}", diff --git a/src/lib/i18n/languages/es-mx.json b/src/lib/i18n/languages/es-mx.json index 3d5a215ea..ebc73f8e7 100644 --- a/src/lib/i18n/languages/es-mx.json +++ b/src/lib/i18n/languages/es-mx.json @@ -353,7 +353,9 @@ "TS_TYPE_GENERATION_FAILED": "La generación de tipos falló: {error}", "HINT_TS_TYPE_GENERATION_FAILED": "El proceso hijo que genera las declaraciones de TypeScript encontró un error. Compruebe el mensaje de error para más detalles y asegúrese de que los archivos fuente de TypeScript son válidos.", "TS_TYPE_GENERATION_FORK_FAILED": "Error al bifurcar (fork) el proceso de generación de tipos: {error}", - "HINT_TS_TYPE_GENERATION_FORK_FAILED": "El proceso trabajador de generación de tipos no pudo iniciarse. Asegúrese de que Node.js tiene permiso para bifurcar procesos hijos y que el script tools/build/generate-types-worker.mjs existe.", + "HINT_TS_TYPE_GENERATION_FORK_FAILED": "El proceso trabajador de generación de tipos no pudo iniciarse. Asegúrese de que Node.js tiene permiso para bifurcar procesos hijos y que el script lib/processors/type-generation-worker.mjs existe.", + "TS_TYPE_GENERATION_WORKER_NO_CONFIG": "El trabajador de generación de tipos se inició sin su configuración (SLOTHLET_CONFIG no está definida).", + "HINT_TS_TYPE_GENERATION_WORKER_NO_CONFIG": "El modo estricto de TypeScript inicia este trabajador con su configuración en la variable de entorno SLOTHLET_CONFIG. Deje que slothlet lo bifurque en lugar de ejecutar el archivo directamente.", "TS_TYPE_GENERATION_PROCESS_EXITED": "El proceso de generación de tipos terminó con el código {code}: {output}", "HINT_TS_TYPE_GENERATION_PROCESS_EXITED": "El proceso hijo de generación de tipos terminó con un código distinto de cero. Compruebe la salida en busca de errores del compilador y verifique que las fuentes de TypeScript son válidas.", "TS_TYPE_CHECK_ERRORS": "Errores de tipo TypeScript encontrados en '{filePath}':\n{errors}", diff --git a/src/lib/i18n/languages/fr-fr.json b/src/lib/i18n/languages/fr-fr.json index fe61708ee..7703c24a9 100644 --- a/src/lib/i18n/languages/fr-fr.json +++ b/src/lib/i18n/languages/fr-fr.json @@ -353,7 +353,9 @@ "TS_TYPE_GENERATION_FAILED": "La génération de types a échoué : {error}", "HINT_TS_TYPE_GENERATION_FAILED": "Le processus enfant qui génère les déclarations TypeScript a rencontré une erreur. Vérifiez le message d'erreur pour les détails et assurez-vous que les fichiers sources TypeScript sont valides.", "TS_TYPE_GENERATION_FORK_FAILED": "Échec du fork du processus de génération de types : {error}", - "HINT_TS_TYPE_GENERATION_FORK_FAILED": "Le processus travailleur de génération de types n'a pas pu être démarré. Assurez-vous que Node.js a la permission de forker des processus enfants et que le script tools/build/generate-types-worker.mjs existe.", + "HINT_TS_TYPE_GENERATION_FORK_FAILED": "Le processus travailleur de génération de types n'a pas pu être démarré. Assurez-vous que Node.js a la permission de forker des processus enfants et que le script lib/processors/type-generation-worker.mjs existe.", + "TS_TYPE_GENERATION_WORKER_NO_CONFIG": "Le travailleur de génération de types a été démarré sans sa configuration (SLOTHLET_CONFIG n'est pas définie).", + "HINT_TS_TYPE_GENERATION_WORKER_NO_CONFIG": "Ce travailleur est démarré par le mode TypeScript strict avec sa configuration dans la variable d'environnement SLOTHLET_CONFIG. Laissez slothlet le forker plutôt que d'exécuter le fichier directement.", "TS_TYPE_GENERATION_PROCESS_EXITED": "Le processus de génération de types s'est arrêté avec le code {code} : {output}", "HINT_TS_TYPE_GENERATION_PROCESS_EXITED": "Le processus enfant de génération de types s'est arrêté avec un code non nul. Vérifiez la sortie pour les erreurs de compilation et vérifiez que les sources TypeScript sont valides.", "TS_TYPE_CHECK_ERRORS": "Erreurs de type TypeScript trouvées dans '{filePath}' :\n{errors}", diff --git a/src/lib/i18n/languages/hi-in.json b/src/lib/i18n/languages/hi-in.json index 7d55ab82b..9767e62cb 100644 --- a/src/lib/i18n/languages/hi-in.json +++ b/src/lib/i18n/languages/hi-in.json @@ -353,7 +353,9 @@ "TS_TYPE_GENERATION_FAILED": "प्रकार पीढ़ी विफल: {error}", "HINT_TS_TYPE_GENERATION_FAILED": "टाइपस्क्रिप्ट घोषणाएं उत्पन्न करने वाली चाइल्ड प्रोसेस में त्रुटि हुई। विवरण के लिए त्रुटि संदेश की जांच करें और सुनिश्चित करें कि टाइपस्क्रिप्ट स्रोत फ़ाइलें मान्य हैं।", "TS_TYPE_GENERATION_FORK_FAILED": "प्रकार पीढ़ी प्रोसेस को फोर्क (fork) करने में विफल: {error}", - "HINT_TS_TYPE_GENERATION_FORK_FAILED": "प्रकार पीढ़ी वर्कर प्रोसेस शुरू नहीं किया जा सका। सुनिश्चित करें कि Node.js के पास चाइल्ड प्रोसेस फोर्क करने की अनुमति है और tools/build/generate-types-worker.mjs स्क्रिप्ट मौजूद है।", + "HINT_TS_TYPE_GENERATION_FORK_FAILED": "प्रकार पीढ़ी वर्कर प्रोसेस शुरू नहीं किया जा सका। सुनिश्चित करें कि Node.js के पास चाइल्ड प्रोसेस फोर्क करने की अनुमति है और lib/processors/type-generation-worker.mjs स्क्रिप्ट मौजूद है।", + "TS_TYPE_GENERATION_WORKER_NO_CONFIG": "प्रकार पीढ़ी वर्कर अपने कॉन्फ़िगरेशन के बिना शुरू किया गया (SLOTHLET_CONFIG सेट नहीं है)।", + "HINT_TS_TYPE_GENERATION_WORKER_NO_CONFIG": "यह वर्कर TypeScript strict मोड द्वारा SLOTHLET_CONFIG पर्यावरण चर में कॉन्फ़िगरेशन के साथ शुरू किया जाता है। फ़ाइल को सीधे चलाने के बजाय slothlet को इसे फोर्क करने दें।", "TS_TYPE_GENERATION_PROCESS_EXITED": "प्रकार पीढ़ी प्रोसेस कोड {code} के साथ बाहर निकला: {output}", "HINT_TS_TYPE_GENERATION_PROCESS_EXITED": "प्रकार पीढ़ी चाइल्ड प्रोसेस गैर-शून्य कोड के साथ समाप्त हुई। कंपाइलर त्रुटियों के लिए आउटपुट की जांच करें और सत्यापित करें कि टाइपस्क्रिप्ट स्रोत मान्य हैं।", "TS_TYPE_CHECK_ERRORS": "'{filePath}' में टाइपस्क्रिप्ट प्रकार त्रुटियाँ मिलीं:\n{errors}", diff --git a/src/lib/i18n/languages/ja-jp.json b/src/lib/i18n/languages/ja-jp.json index 68b4d80f7..31180ee05 100644 --- a/src/lib/i18n/languages/ja-jp.json +++ b/src/lib/i18n/languages/ja-jp.json @@ -352,8 +352,10 @@ "HINT_TS_STRICT_REQUIRES_INTERFACE_NAME": "'types' 構成に 'interfaceName' を追加してください (例: types: { output: './types/api.d.ts', interfaceName: 'MyAPI' })。", "TS_TYPE_GENERATION_FAILED": "型生成に失敗しました: {error}", "HINT_TS_TYPE_GENERATION_FAILED": "TypeScript 宣言を生成する子プロセスでエラーが発生しました。エラーメッセージで詳細を確認し、TypeScript ソースファイルが有効であることを確認してください。", - "TS_TYPE_GENERATION_FORK_FAILED": "型生成ワーカープロセスを開始できませんでした。 Node.js に子プロセスをフォークする権限があること、および tools/build/generate-types-worker.mjs スクリプトが存在することを確認してください。", - "HINT_TS_TYPE_GENERATION_FORK_FAILED": "Node.js がプロセスをフォークできること、および tools/build/generate-types-worker.mjs が存在することを確認してください。", + "TS_TYPE_GENERATION_FORK_FAILED": "型生成ワーカープロセスを開始できませんでした。 Node.js に子プロセスをフォークする権限があること、および lib/processors/type-generation-worker.mjs スクリプトが存在することを確認してください。", + "HINT_TS_TYPE_GENERATION_FORK_FAILED": "Node.js がプロセスをフォークできること、および lib/processors/type-generation-worker.mjs が存在することを確認してください。", + "TS_TYPE_GENERATION_WORKER_NO_CONFIG": "型生成ワーカーが設定なしで起動されました(SLOTHLET_CONFIG が設定されていません)。", + "HINT_TS_TYPE_GENERATION_WORKER_NO_CONFIG": "このワーカーは TypeScript の strict モードが SLOTHLET_CONFIG 環境変数に設定を入れて起動します。ファイルを直接実行せず、slothlet にフォークさせてください。", "TS_TYPE_GENERATION_PROCESS_EXITED": "型生成プロセスがコード {code} で終了しました: {output}", "HINT_TS_TYPE_GENERATION_PROCESS_EXITED": "型生成子プロセスがゼロ以外のコードで終了しました。コンパイラエラーがないか出力を確認し、TypeScript ソースが有効であることを検証してください。", "TS_TYPE_CHECK_ERRORS": "'{filePath}' で TypeScript の型エラーが見つかりました:\n{errors}", diff --git a/src/lib/i18n/languages/ko-kr.json b/src/lib/i18n/languages/ko-kr.json index 65938385b..2c03edd90 100644 --- a/src/lib/i18n/languages/ko-kr.json +++ b/src/lib/i18n/languages/ko-kr.json @@ -353,7 +353,9 @@ "TS_TYPE_GENERATION_FAILED": "타입 생성 실패: {error}", "HINT_TS_TYPE_GENERATION_FAILED": "TypeScript 선언을 생성하는 하위 프로세스에서 오류가 발생했습니다. 오류 메시지에서 세부 정보를 확인하고 TypeScript 소스 파일이 유효한지 확인하십시오.", "TS_TYPE_GENERATION_FORK_FAILED": "타입 생성 프로세스 분기(fork) 실패: {error}", - "HINT_TS_TYPE_GENERATION_FORK_FAILED": "타입 생성 워커 프로세스를 시작할 수 없습니다. Node.js에 하위 프로세스 분기 권한이 있는지, tools/build/generate-types-worker.mjs 스크립트가 존재하는지 확인하십시오.", + "HINT_TS_TYPE_GENERATION_FORK_FAILED": "타입 생성 워커 프로세스를 시작할 수 없습니다. Node.js에 하위 프로세스 분기 권한이 있는지, lib/processors/type-generation-worker.mjs 스크립트가 존재하는지 확인하십시오.", + "TS_TYPE_GENERATION_WORKER_NO_CONFIG": "타입 생성 워커가 구성 없이 시작되었습니다 (SLOTHLET_CONFIG가 설정되지 않음).", + "HINT_TS_TYPE_GENERATION_WORKER_NO_CONFIG": "이 워커는 TypeScript strict 모드가 SLOTHLET_CONFIG 환경 변수에 구성을 담아 시작합니다. 파일을 직접 실행하지 말고 slothlet이 포크하도록 하십시오.", "TS_TYPE_GENERATION_PROCESS_EXITED": "타입 생성 프로세스가 종료 코드 {code}로 종료됨: {output}", "HINT_TS_TYPE_GENERATION_PROCESS_EXITED": "타입 생성 하위 프로세스가 0이 아닌 코드로 종료되었습니다. 컴파일러 오류가 있는지 출력을 확인하고 TypeScript 소스가 유효한지 확인하십시오.", "TS_TYPE_CHECK_ERRORS": "'{filePath}'에서 TypeScript 타입 오류 발견:\n{errors}", diff --git a/src/lib/i18n/languages/pt-br.json b/src/lib/i18n/languages/pt-br.json index d0bf1e936..9589f562c 100644 --- a/src/lib/i18n/languages/pt-br.json +++ b/src/lib/i18n/languages/pt-br.json @@ -353,7 +353,9 @@ "TS_TYPE_GENERATION_FAILED": "Falha na geração de tipos: {error}", "HINT_TS_TYPE_GENERATION_FAILED": "O processo filho que gera as declarações do TypeScript encontrou um erro. Verifique a mensagem de erro para obter detalhes e certifique-se de que os arquivos de origem do TypeScript são válidos.", "TS_TYPE_GENERATION_FORK_FAILED": "Falha ao criar (fork) o processo de geração de tipos: {error}", - "HINT_TS_TYPE_GENERATION_FORK_FAILED": "O processo de execução da geração de tipos não pôde ser iniciado. Certifique-se de que o Node.js tenha permissão para criar processos filhos e que o script tools/build/generate-types-worker.mjs exista.", + "HINT_TS_TYPE_GENERATION_FORK_FAILED": "O processo de execução da geração de tipos não pôde ser iniciado. Certifique-se de que o Node.js tenha permissão para criar processos filhos e que o script lib/processors/type-generation-worker.mjs exista.", + "TS_TYPE_GENERATION_WORKER_NO_CONFIG": "O worker de geração de tipos foi iniciado sem sua configuração (SLOTHLET_CONFIG não está definida).", + "HINT_TS_TYPE_GENERATION_WORKER_NO_CONFIG": "Este worker é iniciado pelo modo estrito do TypeScript com sua configuração na variável de ambiente SLOTHLET_CONFIG. Deixe o slothlet criá-lo em vez de executar o arquivo diretamente.", "TS_TYPE_GENERATION_PROCESS_EXITED": "O processo de geração de tipos saiu com o código {code}: {output}", "HINT_TS_TYPE_GENERATION_PROCESS_EXITED": "O processo filho de geração de tipos saiu com um código diferente de zero. Verifique a saída em busca de erros do compilador e verifique se as fontes do TypeScript são válidas.", "TS_TYPE_CHECK_ERRORS": "Erros de tipo TypeScript encontrados em '{filePath}':\n{errors}", diff --git a/src/lib/i18n/languages/ru-ru.json b/src/lib/i18n/languages/ru-ru.json index a7a6164cb..e61d7ecbb 100644 --- a/src/lib/i18n/languages/ru-ru.json +++ b/src/lib/i18n/languages/ru-ru.json @@ -353,7 +353,9 @@ "TS_TYPE_GENERATION_FAILED": "Ошибка генерации типов: {error}", "HINT_TS_TYPE_GENERATION_FAILED": "Дочерний процесс генерации типов завершился с ошибкой. Проверьте вывод.", "TS_TYPE_GENERATION_FORK_FAILED": "Не удалось форкнуть процесс генерации типов: {error}", - "HINT_TS_TYPE_GENERATION_FORK_FAILED": "Убедитесь, что Node.js может форкать процессы и существует tools/build/generate-types-worker.mjs.", + "HINT_TS_TYPE_GENERATION_FORK_FAILED": "Убедитесь, что Node.js может форкать процессы и существует lib/processors/type-generation-worker.mjs.", + "TS_TYPE_GENERATION_WORKER_NO_CONFIG": "Процесс генерации типов запущен без конфигурации (SLOTHLET_CONFIG не задана).", + "HINT_TS_TYPE_GENERATION_WORKER_NO_CONFIG": "Этот процесс запускается строгим режимом TypeScript с конфигурацией в переменной окружения SLOTHLET_CONFIG. Позвольте slothlet запустить его, а не выполняйте файл напрямую.", "TS_TYPE_GENERATION_PROCESS_EXITED": "Процесс генерации типов завершился с кодом {code}: {output}", "HINT_TS_TYPE_GENERATION_PROCESS_EXITED": "Проверьте вывод дочернего процесса на ошибки компилятора.", "TS_TYPE_CHECK_ERRORS": "Ошибки типов TypeScript в '{filePath}':\n{errors}", diff --git a/src/lib/i18n/languages/zh-cn.json b/src/lib/i18n/languages/zh-cn.json index b08e38797..48190e840 100644 --- a/src/lib/i18n/languages/zh-cn.json +++ b/src/lib/i18n/languages/zh-cn.json @@ -353,7 +353,9 @@ "TS_TYPE_GENERATION_FAILED": "类型生成失败:{error}", "HINT_TS_TYPE_GENERATION_FAILED": "生成 TypeScript 声明的子进程遇到错误。请检查错误信息并确保 TypeScript 源文件有效。", "TS_TYPE_GENERATION_FORK_FAILED": "无法派生类型生成进程:{error}", - "HINT_TS_TYPE_GENERATION_FORK_FAILED": "类型生成工作进程无法启动。请确保 Node.js 有权限派生子进程,并且存在 tools/build/generate-types-worker.mjs 脚本。", + "HINT_TS_TYPE_GENERATION_FORK_FAILED": "类型生成工作进程无法启动。请确保 Node.js 有权限派生子进程,并且存在 lib/processors/type-generation-worker.mjs 脚本。", + "TS_TYPE_GENERATION_WORKER_NO_CONFIG": "类型生成工作进程在没有配置的情况下启动(未设置 SLOTHLET_CONFIG)。", + "HINT_TS_TYPE_GENERATION_WORKER_NO_CONFIG": "此工作进程由 TypeScript strict 模式启动,其配置位于 SLOTHLET_CONFIG 环境变量中。请让 slothlet 派生它,而不是直接运行该文件。", "TS_TYPE_GENERATION_PROCESS_EXITED": "类型生成进程以代码 {code} 退出:{output}", "HINT_TS_TYPE_GENERATION_PROCESS_EXITED": "类型生成子进程以非零代码退出。检查输出中的编译器错误并验证 TypeScript 源是否有效。", "TS_TYPE_CHECK_ERRORS": "在 '{filePath}' 中发现 TypeScript 类型错误:\n{errors}", diff --git a/src/lib/processors/loader.mjs b/src/lib/processors/loader.mjs index 12acab40c..2e9fb26c3 100644 --- a/src/lib/processors/loader.mjs +++ b/src/lib/processors/loader.mjs @@ -131,6 +131,21 @@ function compileHidden(globs) { * @extends ComponentBase * @package */ +/** + * Absolute path of the worker strict TypeScript mode forks to generate the api's declaration file. It + * lives next to this module, so it ships wherever the loader does (`dist/lib/processors/` when + * installed) (#500). Resolved on call, from the Node-only strict-mode path: `path`/`url` are `null` + * in browser mode, and the literal `new URL("./…", import.meta.url)` form is avoided because bundlers + * and vite treat it as a module reference and load the worker into the current process. + * @returns {string} Absolute path of `type-generation-worker.mjs`. + * @internal + * @example + * fork(typeGenerationWorkerPath(), [], { stdio: ["pipe", "pipe", "pipe", "ipc"] }); + */ +export function typeGenerationWorkerPath() { + return path.join(path.dirname(url.fileURLToPath(import.meta.url)), "type-generation-worker.mjs"); +} + export class Loader extends ComponentBase { static slothletProperty = "loader"; @@ -186,12 +201,9 @@ export class Loader extends ComponentBase { // Generate types if not already generated for this instance if (!this.slothlet._typesGenerated) { const { fork } = await import("child_process"); - const path = await import("path"); - const { fileURLToPath } = await import("url"); - // Get the path to the type generation script (in tools/ not src/tools/) - const __dirname = path.dirname(fileURLToPath(import.meta.url)); - const scriptPath = path.resolve(__dirname, "../../../tools/build/generate-types-worker.mjs"); + // The worker ships next to this file (src/lib/processors → dist/lib/processors). + const scriptPath = typeGenerationWorkerPath(); // Prepare config for child process // Note: Child process needs 'dir' not 'root', and should use eager mode diff --git a/src/lib/processors/type-generation-worker.mjs b/src/lib/processors/type-generation-worker.mjs new file mode 100644 index 000000000..21456d773 --- /dev/null +++ b/src/lib/processors/type-generation-worker.mjs @@ -0,0 +1,85 @@ +/** + * @Project: @cldmv/slothlet + * @Filename: /src/lib/processors/type-generation-worker.mjs + * @Date: 2026-02-14T18:14:33-08:00 (1771121673) + * @Author: Nate Corcoran + * @Email: + * ----- + * @Last modified by: Nate Corcoran (Shinrai@users.noreply.github.com) + * @Last modified time: 2026-03-01 20:22:18 -08:00 (1772425338) + * ----- + * @Copyright: Copyright (c) 2013-2026 Catalyzed Motivation Inc. All rights reserved. + */ + +/** + * @fileoverview Worker for strict TypeScript mode's type generation. + * @module @cldmv/slothlet/processors/type-generation-worker + * @internal + * + * @description + * Strict mode forks this file (see `typeGenerationWorkerPath()` in `loader.mjs`) to build an eager, + * fast-mode instance of the api and write its declaration file in a separate process, so the build + * never shares a module cache with the instance being loaded. The config arrives in the + * `SLOTHLET_CONFIG` environment variable, and the result goes back to the parent over IPC as + * `{ type: "success" }` or `{ type: "error", error }`. + * + * It ships in `dist/lib/processors/` and imports only `@cldmv/slothlet` self-references, which + * resolve to `src/` under the `slothlet-dev` condition and to `dist/` in an installed package (#500). + * Importing the module does nothing; only running it as the process's entry script starts a build. + */ + +import { fileURLToPath } from "node:url"; +import { resolve } from "node:path"; +import slothlet from "@cldmv/slothlet"; +import { generateTypes } from "@cldmv/slothlet/processors/type-generator"; +import { SlothletError } from "@cldmv/slothlet/errors"; + +/** + * Build the api described by `configJson` and write its declaration file. + * @param {string|undefined} configJson - The serialized worker config (`SLOTHLET_CONFIG`): the + * slothlet config for an eager, fast-mode instance plus `types` (`{ output, interfaceName, ... }`). + * @param {((message: {type: string, error?: string}) => void)|undefined} send - IPC sender to the + * parent (`process.send` in the fork); `undefined` when there is no IPC channel. + * @returns {Promise} The process exit code: `0` on success, `1` on failure. + * @example + * const code = await runTypeGeneration(process.env.SLOTHLET_CONFIG, process.send?.bind(process)); + */ +export async function runTypeGeneration(configJson, send) { + let api = null; + try { + if (!configJson) { + throw new SlothletError("TS_TYPE_GENERATION_WORKER_NO_CONFIG", {}, null, { validationError: true }); + } + const config = JSON.parse(configJson); + api = await slothlet(config); + await generateTypes(api, config.types); + send?.({ type: "success" }); + return 0; + } catch (error) { + send?.({ type: "error", error: error.message }); + return 1; + } finally { + await api?.slothlet.shutdown(); + } +} + +/** + * Run the type generation and hand its exit code to `exit`, but only when `argv1` is this file — + * i.e. when the module is the process's entry script (the strict-mode fork). Any other import is a + * no-op. + * @param {string|undefined} argv1 - The entry script path (`process.argv[1]`). + * @param {string|undefined} configJson - The serialized worker config (`SLOTHLET_CONFIG`). + * @param {((message: {type: string, error?: string}) => void)|undefined} send - IPC sender to the parent. + * @param {(code: number) => void} exit - Called with the exit code. + * @returns {Promise|null} The pending run, or `null` when this module is not the entry script. + * @example + * runIfEntry(process.argv[1], process.env.SLOTHLET_CONFIG, process.send?.bind(process), process.exit.bind(process)); + */ +export function runIfEntry(argv1, configJson, send, exit) { + if (!argv1 || resolve(argv1) !== fileURLToPath(import.meta.url)) { + return null; + } + return runTypeGeneration(configJson, send).then(exit); +} + +runIfEntry(process.argv[1], process.env.SLOTHLET_CONFIG, process.send?.bind(process), process.exit.bind(process)); diff --git a/tests/vitests/suites/typescript/typescript-strict-mode-packaged.test.vitest.mjs b/tests/vitests/suites/typescript/typescript-strict-mode-packaged.test.vitest.mjs new file mode 100644 index 000000000..f6c2a01f9 --- /dev/null +++ b/tests/vitests/suites/typescript/typescript-strict-mode-packaged.test.vitest.mjs @@ -0,0 +1,154 @@ +/** + * @Project: @cldmv/slothlet + * @Filename: /tests/vitests/suites/typescript/typescript-strict-mode-packaged.test.vitest.mjs + * @Date: 2026-09-28 09:44:57 -07:00 (1790613897) + * @Author: Shinrai + * @Email: + * ----- + * @Last modified by: Shinrai (Shinrai@users.noreply.github.com) + * @Last modified time: 2026-09-28 09:53:16 -07:00 (1790614396) + * ----- + * @Copyright: Copyright (c) 2013-2026 Catalyzed Motivation Inc. All rights reserved. + */ + +/** + * @fileoverview Regression (#500): strict TypeScript mode forks a type-generation worker, and that worker + * must ship in the published package. It lived in `tools/build/` and imported `../../src/...`; neither + * `tools/` nor `src/` is in `package.json` `files`, so strict mode could only work inside this repo. + * + * The worker must sit inside the source tree the build copies into `dist/` (`src/lib/...` → + * `dist/lib/...`), and import only Node builtins or `@cldmv/slothlet` self-references, which resolve + * to `src/` under the `slothlet-dev` condition and to `dist/` in an installed package. + * + * @module tests/vitests/suites/typescript/typescript-strict-mode-packaged + */ + +import { describe, it, expect, afterAll } from "vitest"; +import { readFileSync, existsSync } from "node:fs"; +import { mkdir, writeFile, rm } from "node:fs/promises"; +import { builtinModules } from "node:module"; +import path from "node:path"; +import { fileURLToPath } from "node:url"; +import { typeGenerationWorkerPath } from "@cldmv/slothlet/processors/loader"; +import { runTypeGeneration, runIfEntry } from "@cldmv/slothlet/processors/type-generation-worker"; +import { makeTestTmpDir } from "../../setup/test-fixtures-tmp.mjs"; + +const REPO_ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../../../.."); +const LIB_ROOT = path.join(REPO_ROOT, "src", "lib"); + +describe("strict mode type-generation worker ships with the package (#500)", () => { + it("forks a worker inside the lib tree the build copies into dist/", () => { + const workerPath = typeGenerationWorkerPath(); + expect(existsSync(workerPath)).toBe(true); + const relative = path.relative(LIB_ROOT, workerPath); + expect(relative.startsWith("..") || path.isAbsolute(relative)).toBe(false); + }); + + it("imports only Node builtins and @cldmv/slothlet self-references", () => { + const source = readFileSync(typeGenerationWorkerPath(), "utf8"); + const specifiers = [...source.matchAll(/^\s*import\s+(?:[^"']*?\s+from\s+)?["']([^"']+)["']/gm)].map((m) => m[1]); + expect(specifiers.length).toBeGreaterThan(0); + const builtins = new Set([...builtinModules, ...builtinModules.map((name) => `node:${name}`)]); + const unpublished = specifiers.filter((spec) => !builtins.has(spec) && !spec.startsWith("@cldmv/slothlet")); + expect(unpublished).toEqual([]); + }); + + it("does nothing when imported rather than run as the process's entry script", async () => { + // Importing the worker (a bundler following the reference, a test, a tool scanning the package) + // must not start a build or call process.exit on the importer. + const before = process.exitCode; + await import(typeGenerationWorkerPath()); + expect(process.exitCode).toBe(before); + }); + + it("is inside the published file list", () => { + const pkg = JSON.parse(readFileSync(path.join(REPO_ROOT, "package.json"), "utf8")); + expect(pkg.files).toContain("dist/"); + }); +}); + +describe("type-generation worker, driven in-process (#500)", () => { + const roots = []; + + afterAll(async () => { + await Promise.allSettled(roots.map((root) => rm(root, { recursive: true, force: true }))); + }); + + /** + * A tmp api folder with one leaf, plus the worker config that builds it. + * @returns {Promise<{configJson: string, output: string}>} + */ + async function workerFixture() { + const root = await makeTestTmpDir("type-generation-worker"); + roots.push(root); + const apiDir = path.join(root, "api"); + await mkdir(apiDir, { recursive: true }); + await writeFile( + path.join(apiDir, "math.mjs"), + "/** @param {number} a @param {number} b @returns {number} */\nexport function add(a, b) { return a + b; }\n", + "utf8" + ); + const output = path.join(root, "types", "api.d.ts"); + const configJson = JSON.stringify({ + base: apiDir, + mode: "eager", + silent: true, + typescript: { enabled: true, mode: "fast" }, + types: { output, interfaceName: "WorkerApi" } + }); + return { configJson, output }; + } + + it("builds the api, writes the declaration, and reports success", async () => { + const { configJson, output } = await workerFixture(); + const messages = []; + const code = await runTypeGeneration(configJson, (message) => messages.push(message)); + expect(code).toBe(0); + expect(messages).toEqual([{ type: "success" }]); + expect(readFileSync(output, "utf8")).toContain("interface WorkerApi"); + }); + + it("reports an error and exit code 1 when SLOTHLET_CONFIG is missing", async () => { + const messages = []; + const code = await runTypeGeneration(undefined, (message) => messages.push(message)); + expect(code).toBe(1); + expect(messages).toHaveLength(1); + expect(messages[0].type).toBe("error"); + expect(messages[0].error).toMatch(/SLOTHLET_CONFIG/); + }); + + it("reports an error and exit code 1 when type generation fails", async () => { + const { configJson } = await workerFixture(); + const broken = JSON.parse(configJson); + delete broken.types.output; + const messages = []; + const code = await runTypeGeneration(JSON.stringify(broken), (message) => messages.push(message)); + expect(code).toBe(1); + expect(messages[0].type).toBe("error"); + }); + + it("works without an IPC channel", async () => { + const { configJson } = await workerFixture(); + await expect(runTypeGeneration(configJson, undefined)).resolves.toBe(0); + await expect(runTypeGeneration(undefined, undefined)).resolves.toBe(1); + }); + + it("runs only when the worker is the process's entry script", async () => { + const exits = []; + expect(runIfEntry(undefined, "{}", undefined, (code) => exits.push(code))).toBeNull(); + expect(runIfEntry(path.join(REPO_ROOT, "index.mjs"), "{}", undefined, (code) => exits.push(code))).toBeNull(); + expect(exits).toEqual([]); + + const { configJson, output } = await workerFixture(); + const messages = []; + await runIfEntry( + typeGenerationWorkerPath(), + configJson, + (message) => messages.push(message), + (code) => exits.push(code) + ); + expect(exits).toEqual([0]); + expect(messages).toEqual([{ type: "success" }]); + expect(readFileSync(output, "utf8")).toContain("interface WorkerApi"); + }); +}); diff --git a/tools/build/generate-types-worker.mjs b/tools/build/generate-types-worker.mjs deleted file mode 100644 index 4b2955e9d..000000000 --- a/tools/build/generate-types-worker.mjs +++ /dev/null @@ -1,69 +0,0 @@ -#!/usr/bin/env node -/** - * @Project: @cldmv/slothlet - * @Filename: /tools/build/generate-types-worker.mjs - * @Date: 2026-02-14T18:14:33-08:00 (1771121673) - * @Author: Nate Corcoran - * @Email: - * ----- - * @Last modified by: Nate Corcoran (Shinrai@users.noreply.github.com) - * @Last modified time: 2026-03-01 20:22:18 -08:00 (1772425338) - * ----- - * @Copyright: Copyright (c) 2013-2026 Catalyzed Motivation Inc. All rights reserved. - */ - -/** - * Worker script for generating TypeScript declaration files - * This runs in a forked child process to avoid module cache conflicts - */ - -import slothlet from "../../index.mjs"; -import { generateTypes } from "../../src/lib/processors/type-generator.mjs"; - -async function main() { - try { - // Get config from environment - const configJson = process.env.SLOTHLET_CONFIG; - if (!configJson) { - throw new Error("SLOTHLET_CONFIG environment variable not set"); - } - - const config = JSON.parse(configJson); - - // Load API in eager mode with fast TypeScript mode - const api = await slothlet(config); - - try { - // Generate types from loaded API - await generateTypes(api, config.types); - - // Send success message to parent - if (process.send) { - process.send({ type: "success" }); - } - - // Shutdown cleanly - await api.slothlet.shutdown(); - - process.exit(0); - } catch (error) { - // Send error message to parent - if (process.send) { - process.send({ type: "error", error: error.message }); - } - - await api.slothlet.shutdown(); - process.exit(1); - } - } catch (error) { - console.error("Type generation worker failed:", error); - - if (process.send) { - process.send({ type: "error", error: error.message }); - } - - process.exit(1); - } -} - -main(); diff --git a/tools/ci/i18n-latin-tokens-accepted.json b/tools/ci/i18n-latin-tokens-accepted.json index a4e1f8dc3..464d84371 100644 --- a/tools/ci/i18n-latin-tokens-accepted.json +++ b/tools/ci/i18n-latin-tokens-accepted.json @@ -185,7 +185,19 @@ "Hook", "data" ], - "external_tooling_paths": ["npm", "install", "tools", "cldmv", "worker", "ALS"], + "external_tooling_paths": [ + "npm", + "install", + "tools", + "cldmv", + "worker", + "ALS", + "lib", + "processors", + "generation", + "SLOTHLET", + "CONFIG" + ], "fixture_or_example_names": ["math", "auth", "synthetic", "syntheticName"], "misc_remaining_debug_abbreviations": [ "IN", @@ -440,6 +452,11 @@ "cldmv", "worker", "ALS", + "lib", + "processors", + "generation", + "SLOTHLET", + "CONFIG", "math", "auth", "synthetic", diff --git a/types/src/lib/processors/loader.d.mts b/types/src/lib/processors/loader.d.mts index 86e5710d0..660415ef5 100644 --- a/types/src/lib/processors/loader.d.mts +++ b/types/src/lib/processors/loader.d.mts @@ -28,6 +28,18 @@ export function warnIfCoverageWithoutImporter(config: object, { worker, external * @extends ComponentBase * @package */ +/** + * Absolute path of the worker strict TypeScript mode forks to generate the api's declaration file. It + * lives next to this module, so it ships wherever the loader does (`dist/lib/processors/` when + * installed) (#500). Resolved on call, from the Node-only strict-mode path: `path`/`url` are `null` + * in browser mode, and the literal `new URL("./…", import.meta.url)` form is avoided because bundlers + * and vite treat it as a module reference and load the worker into the current process. + * @returns {string} Absolute path of `type-generation-worker.mjs`. + * @internal + * @example + * fork(typeGenerationWorkerPath(), [], { stdio: ["pipe", "pipe", "pipe", "ipc"] }); + */ +export function typeGenerationWorkerPath(): string; export class Loader extends ComponentBase { static slothletProperty: string; /** diff --git a/types/src/lib/processors/loader.d.mts.map b/types/src/lib/processors/loader.d.mts.map index d17743c75..7c73d7f1a 100644 --- a/types/src/lib/processors/loader.d.mts.map +++ b/types/src/lib/processors/loader.d.mts.map @@ -1 +1 @@ -{"version":3,"file":"loader.d.mts","sourceRoot":"","sources":["../../../../src/lib/processors/loader.mjs"],"names":[],"mappings":"AA4DA;;;;;;;;;;;;;;;;;;;GAmBG;AACH,sDAjBW,MAAM,6BAEd;IAAqC,MAAM,GAAnC,MAAM,GAAC,SAAS;IACI,YAAY;CAExC,GAAU,OAAO,CAmBnB;AAwCD;;;;;GAKG;AACH;IACC,gCAAmC;IAEnC;;;;OAIG;IACH,sBAHW,MAAM,EAKhB;IAED;;;;;;;;OAQG;IACH,4BAPW,MAAM,eACN,MAAM,aACN,MAAM,cACN,MAAM,GAAC,IAAI,GACT,OAAO,CAAC,MAAM,CAAC,CA0M3B;IAyED;;;;;;;;;;;;;;OAcG;IACH,0BAbW,MAAM,YAEd;QAA0B,UAAU;QACX,YAAY;QACZ,QAAQ;QACD,UAAU;QACM,MAAM;QAE5B,iBAAiB;QAClB,OAAO;KAChC,GAAU,OAAO,CAAC,MAAM,CAAC,CAqJ3B;IA8PD;;;;;OAKG;IACH,8BAJW,MAAM,GACJ,MAAM,CA+ClB;;CACD;8BAp2B6B,2BAA2B"} \ No newline at end of file +{"version":3,"file":"loader.d.mts","sourceRoot":"","sources":["../../../../src/lib/processors/loader.mjs"],"names":[],"mappings":"AA4DA;;;;;;;;;;;;;;;;;;;GAmBG;AACH,sDAjBW,MAAM,6BAEd;IAAqC,MAAM,GAAnC,MAAM,GAAC,SAAS;IACI,YAAY;CAExC,GAAU,OAAO,CAmBnB;AAwCD;;;;;GAKG;AACH;;;;;;;;;;GAUG;AACH,4CALa,MAAM,CAOlB;AAED;IACC,gCAAmC;IAEnC;;;;OAIG;IACH,sBAHW,MAAM,EAKhB;IAED;;;;;;;;OAQG;IACH,4BAPW,MAAM,eACN,MAAM,aACN,MAAM,cACN,MAAM,GAAC,IAAI,GACT,OAAO,CAAC,MAAM,CAAC,CAuM3B;IAyED;;;;;;;;;;;;;;OAcG;IACH,0BAbW,MAAM,YAEd;QAA0B,UAAU;QACX,YAAY;QACZ,QAAQ;QACD,UAAU;QACM,MAAM;QAE5B,iBAAiB;QAClB,OAAO;KAChC,GAAU,OAAO,CAAC,MAAM,CAAC,CAqJ3B;IA8PD;;;;;OAKG;IACH,8BAJW,MAAM,GACJ,MAAM,CA+ClB;;CACD;8BAh3B6B,2BAA2B"} \ No newline at end of file diff --git a/types/src/lib/processors/type-generation-worker.d.mts b/types/src/lib/processors/type-generation-worker.d.mts new file mode 100644 index 000000000..86e32337e --- /dev/null +++ b/types/src/lib/processors/type-generation-worker.d.mts @@ -0,0 +1,31 @@ +/** + * Build the api described by `configJson` and write its declaration file. + * @param {string|undefined} configJson - The serialized worker config (`SLOTHLET_CONFIG`): the + * slothlet config for an eager, fast-mode instance plus `types` (`{ output, interfaceName, ... }`). + * @param {((message: {type: string, error?: string}) => void)|undefined} send - IPC sender to the + * parent (`process.send` in the fork); `undefined` when there is no IPC channel. + * @returns {Promise} The process exit code: `0` on success, `1` on failure. + * @example + * const code = await runTypeGeneration(process.env.SLOTHLET_CONFIG, process.send?.bind(process)); + */ +export function runTypeGeneration(configJson: string | undefined, send: ((message: { + type: string; + error?: string; +}) => void) | undefined): Promise; +/** + * Run the type generation and hand its exit code to `exit`, but only when `argv1` is this file — + * i.e. when the module is the process's entry script (the strict-mode fork). Any other import is a + * no-op. + * @param {string|undefined} argv1 - The entry script path (`process.argv[1]`). + * @param {string|undefined} configJson - The serialized worker config (`SLOTHLET_CONFIG`). + * @param {((message: {type: string, error?: string}) => void)|undefined} send - IPC sender to the parent. + * @param {(code: number) => void} exit - Called with the exit code. + * @returns {Promise|null} The pending run, or `null` when this module is not the entry script. + * @example + * runIfEntry(process.argv[1], process.env.SLOTHLET_CONFIG, process.send?.bind(process), process.exit.bind(process)); + */ +export function runIfEntry(argv1: string | undefined, configJson: string | undefined, send: ((message: { + type: string; + error?: string; +}) => void) | undefined, exit: (code: number) => void): Promise | null; +//# sourceMappingURL=type-generation-worker.d.mts.map \ No newline at end of file diff --git a/types/src/lib/processors/type-generation-worker.d.mts.map b/types/src/lib/processors/type-generation-worker.d.mts.map new file mode 100644 index 000000000..74e95e5dc --- /dev/null +++ b/types/src/lib/processors/type-generation-worker.d.mts.map @@ -0,0 +1 @@ +{"version":3,"file":"type-generation-worker.d.mts","sourceRoot":"","sources":["../../../../src/lib/processors/type-generation-worker.mjs"],"names":[],"mappings":"AAoCA;;;;;;;;;GASG;AACH,8CARW,MAAM,GAAC,SAAS,QAEhB,CAAC,CAAC,OAAO,EAAE;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAC,KAAK,IAAI,CAAC,GAAC,SAAS,GAE3D,OAAO,CAAC,MAAM,CAAC,CAqB3B;AAED;;;;;;;;;;;GAWG;AACH,kCARW,MAAM,GAAC,SAAS,cAChB,MAAM,GAAC,SAAS,QAChB,CAAC,CAAC,OAAO,EAAE;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAC,KAAK,IAAI,CAAC,GAAC,SAAS,QAC7D,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,GACpB,OAAO,CAAC,IAAI,CAAC,GAAC,IAAI,CAS9B"} \ No newline at end of file From fbd6862cd8754cb55d8b95ed0ffe22952c37f38d Mon Sep 17 00:00:00 2001 From: Shinrai Date: Mon, 28 Sep 2026 11:26:15 -0700 Subject: [PATCH 2/2] docs(typescript): use jsdoc-parseable callback types in the type-generation worker jsdoc's type parser rejects TypeScript arrow-function types, so docs:build failed on the worker's `send`/`exit` params. They now reference @callback typedefs (WorkerMessageSender, WorkerExit); docs/generated/API.md is regenerated. --- docs/generated/API.md | 4 +- src/lib/processors/type-generation-worker.mjs | 22 +++++++++-- .../processors/type-generation-worker.d.mts | 39 +++++++++++++------ .../type-generation-worker.d.mts.map | 2 +- 4 files changed, 49 insertions(+), 18 deletions(-) diff --git a/docs/generated/API.md b/docs/generated/API.md index 06a802cae..a1ee10e6e 100644 --- a/docs/generated/API.md +++ b/docs/generated/API.md @@ -3267,7 +3267,7 @@ api.slothlet.permissions.control.readGating(true); // resume #### api.slothlet.permissions.control.seal() ⇒ void -Seal the permission control surface (one-way, no unseal). After sealing, enable/disable, addRule/removeRule, and readGating throw PERMISSION_SEALED. Enforcement continues to evaluate and shutdown() still works. Idempotent. +Seal the permission control surface (one-way, no unseal). After sealing, enable/disable, addRule/removeRule, readGating, and principal.register/principal.unregister throw PERMISSION_SEALED. Enforcement continues to evaluate, principal.invalidate and shutdown() still work. Idempotent. **Kind**: function property of [SlothletAPI](#typedef_module_at_cldmv_slash_slothlet_SlothletAPI) @@ -3713,7 +3713,7 @@ await api.slothlet.shutdown(); | slothlet.metadata | object | Module metadata accessor. | | slothlet.owner | object | Direct path ownership accessor (shorthand for `slothlet.ownership`). | | slothlet.ownership | object | Module ownership registry. | -| slothlet.permissions | object | Permission system surface — present whenever a `permissions` block is configured. Rule registration (`addRule`/`removeRule`), self/global introspection (`self.*`, `global.*`), and runtime control (`control.*`) live here. Only the `control` sub-namespace is typed in this typedef; the rule-management methods are documented in `docs/PERMISSIONS.md`. | +| slothlet.permissions | object | Permission system surface — present whenever a `permissions` block is configured. Rule registration (`addRule`/`removeRule`), principal resolvers (`principal.*`), self/global introspection (`self.*`, `global.*`), and runtime control (`control.*`) live here. Only the `control` sub-namespace is typed in this typedef; the rule-management methods are documented in `docs/PERMISSIONS.md`. | | slothlet.permissions.control | object | Runtime control over permission enforcement. | | slothlet.permissions.control.enabled | boolean | Current permission-enforcement state. Exposed as a getter so descriptor-based reads remain permission-gated. %%sig: boolean%% %%example: // ESM usage via slothlet API\|import slothlet from "@cldmv/slothlet";\|const api = await slothlet({ base: './api', permissions: { defaultPolicy: 'allow' } });\|const on = api.slothlet.permissions.control.enabled;%% | | slothlet.permissions.control.readGatingEnabled | boolean | Current read-level gating state. `true` when terminal data-value property reads are permission-gated. Exposed as a getter so descriptor-based reads remain permission-gated. %%sig: boolean%% %%example: // ESM usage via slothlet API\|import slothlet from "@cldmv/slothlet";\|const api = await slothlet({ base: './api', permissions: { defaultPolicy: 'allow' } });\|const gated = api.slothlet.permissions.control.readGatingEnabled;%% | diff --git a/src/lib/processors/type-generation-worker.mjs b/src/lib/processors/type-generation-worker.mjs index 21456d773..84ed65486 100644 --- a/src/lib/processors/type-generation-worker.mjs +++ b/src/lib/processors/type-generation-worker.mjs @@ -34,12 +34,26 @@ import slothlet from "@cldmv/slothlet"; import { generateTypes } from "@cldmv/slothlet/processors/type-generator"; import { SlothletError } from "@cldmv/slothlet/errors"; +/** + * IPC sender to the parent process (`process.send` in the fork). + * @callback WorkerMessageSender + * @param {{type: string, error: (string|undefined)}} message - `{ type: "success" }` or `{ type: "error", error }`. + * @returns {void} + */ + +/** + * Receives the worker's exit code (`process.exit` in the fork). + * @callback WorkerExit + * @param {number} code - `0` on success, `1` on failure. + * @returns {void} + */ + /** * Build the api described by `configJson` and write its declaration file. * @param {string|undefined} configJson - The serialized worker config (`SLOTHLET_CONFIG`): the * slothlet config for an eager, fast-mode instance plus `types` (`{ output, interfaceName, ... }`). - * @param {((message: {type: string, error?: string}) => void)|undefined} send - IPC sender to the - * parent (`process.send` in the fork); `undefined` when there is no IPC channel. + * @param {WorkerMessageSender|undefined} send - IPC sender to the parent (`process.send` in the + * fork); `undefined` when there is no IPC channel. * @returns {Promise} The process exit code: `0` on success, `1` on failure. * @example * const code = await runTypeGeneration(process.env.SLOTHLET_CONFIG, process.send?.bind(process)); @@ -69,8 +83,8 @@ export async function runTypeGeneration(configJson, send) { * no-op. * @param {string|undefined} argv1 - The entry script path (`process.argv[1]`). * @param {string|undefined} configJson - The serialized worker config (`SLOTHLET_CONFIG`). - * @param {((message: {type: string, error?: string}) => void)|undefined} send - IPC sender to the parent. - * @param {(code: number) => void} exit - Called with the exit code. + * @param {WorkerMessageSender|undefined} send - IPC sender to the parent. + * @param {WorkerExit} exit - Called with the exit code. * @returns {Promise|null} The pending run, or `null` when this module is not the entry script. * @example * runIfEntry(process.argv[1], process.env.SLOTHLET_CONFIG, process.send?.bind(process), process.exit.bind(process)); diff --git a/types/src/lib/processors/type-generation-worker.d.mts b/types/src/lib/processors/type-generation-worker.d.mts index 86e32337e..c0dcea8f2 100644 --- a/types/src/lib/processors/type-generation-worker.d.mts +++ b/types/src/lib/processors/type-generation-worker.d.mts @@ -1,31 +1,48 @@ +/** + * IPC sender to the parent process (`process.send` in the fork). + * @callback WorkerMessageSender + * @param {{type: string, error: (string|undefined)}} message - `{ type: "success" }` or `{ type: "error", error }`. + * @returns {void} + */ +/** + * Receives the worker's exit code (`process.exit` in the fork). + * @callback WorkerExit + * @param {number} code - `0` on success, `1` on failure. + * @returns {void} + */ /** * Build the api described by `configJson` and write its declaration file. * @param {string|undefined} configJson - The serialized worker config (`SLOTHLET_CONFIG`): the * slothlet config for an eager, fast-mode instance plus `types` (`{ output, interfaceName, ... }`). - * @param {((message: {type: string, error?: string}) => void)|undefined} send - IPC sender to the - * parent (`process.send` in the fork); `undefined` when there is no IPC channel. + * @param {WorkerMessageSender|undefined} send - IPC sender to the parent (`process.send` in the + * fork); `undefined` when there is no IPC channel. * @returns {Promise} The process exit code: `0` on success, `1` on failure. * @example * const code = await runTypeGeneration(process.env.SLOTHLET_CONFIG, process.send?.bind(process)); */ -export function runTypeGeneration(configJson: string | undefined, send: ((message: { - type: string; - error?: string; -}) => void) | undefined): Promise; +export function runTypeGeneration(configJson: string | undefined, send: WorkerMessageSender | undefined): Promise; /** * Run the type generation and hand its exit code to `exit`, but only when `argv1` is this file — * i.e. when the module is the process's entry script (the strict-mode fork). Any other import is a * no-op. * @param {string|undefined} argv1 - The entry script path (`process.argv[1]`). * @param {string|undefined} configJson - The serialized worker config (`SLOTHLET_CONFIG`). - * @param {((message: {type: string, error?: string}) => void)|undefined} send - IPC sender to the parent. - * @param {(code: number) => void} exit - Called with the exit code. + * @param {WorkerMessageSender|undefined} send - IPC sender to the parent. + * @param {WorkerExit} exit - Called with the exit code. * @returns {Promise|null} The pending run, or `null` when this module is not the entry script. * @example * runIfEntry(process.argv[1], process.env.SLOTHLET_CONFIG, process.send?.bind(process), process.exit.bind(process)); */ -export function runIfEntry(argv1: string | undefined, configJson: string | undefined, send: ((message: { +export function runIfEntry(argv1: string | undefined, configJson: string | undefined, send: WorkerMessageSender | undefined, exit: WorkerExit): Promise | null; +/** + * IPC sender to the parent process (`process.send` in the fork). + */ +export type WorkerMessageSender = (message: { type: string; - error?: string; -}) => void) | undefined, exit: (code: number) => void): Promise | null; + error: (string | undefined); +}) => void; +/** + * Receives the worker's exit code (`process.exit` in the fork). + */ +export type WorkerExit = (code: number) => void; //# sourceMappingURL=type-generation-worker.d.mts.map \ No newline at end of file diff --git a/types/src/lib/processors/type-generation-worker.d.mts.map b/types/src/lib/processors/type-generation-worker.d.mts.map index 74e95e5dc..0c8592893 100644 --- a/types/src/lib/processors/type-generation-worker.d.mts.map +++ b/types/src/lib/processors/type-generation-worker.d.mts.map @@ -1 +1 @@ -{"version":3,"file":"type-generation-worker.d.mts","sourceRoot":"","sources":["../../../../src/lib/processors/type-generation-worker.mjs"],"names":[],"mappings":"AAoCA;;;;;;;;;GASG;AACH,8CARW,MAAM,GAAC,SAAS,QAEhB,CAAC,CAAC,OAAO,EAAE;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAC,KAAK,IAAI,CAAC,GAAC,SAAS,GAE3D,OAAO,CAAC,MAAM,CAAC,CAqB3B;AAED;;;;;;;;;;;GAWG;AACH,kCARW,MAAM,GAAC,SAAS,cAChB,MAAM,GAAC,SAAS,QAChB,CAAC,CAAC,OAAO,EAAE;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAC,KAAK,IAAI,CAAC,GAAC,SAAS,QAC7D,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,GACpB,OAAO,CAAC,IAAI,CAAC,GAAC,IAAI,CAS9B"} \ No newline at end of file +{"version":3,"file":"type-generation-worker.d.mts","sourceRoot":"","sources":["../../../../src/lib/processors/type-generation-worker.mjs"],"names":[],"mappings":"AAoCA;;;;;GAKG;AAEH;;;;;GAKG;AAEH;;;;;;;;;GASG;AACH,8CARW,MAAM,GAAC,SAAS,QAEhB,mBAAmB,GAAC,SAAS,GAE3B,OAAO,CAAC,MAAM,CAAC,CAqB3B;AAED;;;;;;;;;;;GAWG;AACH,kCARW,MAAM,GAAC,SAAS,cAChB,MAAM,GAAC,SAAS,QAChB,mBAAmB,GAAC,SAAS,QAC7B,UAAU,GACR,OAAO,CAAC,IAAI,CAAC,GAAC,IAAI,CAS9B;;;;4CAzDU;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,CAAC,MAAM,GAAC,SAAS,CAAC,CAAA;CAAC,KACvC,IAAI;;;;gCAMN,MAAM,KACJ,IAAI"} \ No newline at end of file