@cldmv/slothlet 3.7.0 → 3.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -5
- package/REFERENCE.md +2 -0
- package/dist/lib/builders/api_builder.mjs +1 -1
- package/dist/lib/builders/modes-processor.mjs +1 -1
- package/dist/lib/handlers/api-manager.mjs +1 -1
- package/dist/lib/handlers/module-manager.mjs +17 -0
- package/dist/lib/helpers/config.mjs +1 -1
- package/dist/lib/helpers/eventemitter-context.mjs +1 -1
- package/dist/lib/helpers/generate-manifest.mjs +17 -0
- package/dist/lib/helpers/manifest-resolver.mjs +17 -0
- package/dist/lib/helpers/module-discovery.mjs +17 -0
- package/dist/lib/helpers/module-manifest-validator.mjs +17 -0
- package/dist/lib/helpers/module-sort.mjs +17 -0
- package/dist/lib/helpers/resolve-from-caller.mjs +1 -1
- package/dist/lib/i18n/languages/de-de.json +76 -47
- package/dist/lib/i18n/languages/en-gb.json +76 -47
- package/dist/lib/i18n/languages/en-us.json +76 -47
- package/dist/lib/i18n/languages/es-es.json +76 -47
- package/dist/lib/i18n/languages/es-mx.json +76 -47
- package/dist/lib/i18n/languages/fr-fr.json +30 -1
- package/dist/lib/i18n/languages/hi-in.json +72 -43
- package/dist/lib/i18n/languages/ja-jp.json +32 -3
- package/dist/lib/i18n/languages/ko-kr.json +32 -3
- package/dist/lib/i18n/languages/pt-br.json +76 -47
- package/dist/lib/i18n/languages/ru-ru.json +68 -39
- package/dist/lib/i18n/languages/zh-cn.json +37 -8
- package/dist/lib/processors/loader.mjs +1 -1
- package/dist/lib/processors/type-generator.mjs +1 -1
- package/dist/lib/processors/typescript.mjs +1 -1
- package/dist/lib/typegen/typegen.mjs +1 -1
- package/dist/slothlet.mjs +1 -1
- package/index.cjs +4 -14
- package/index.mjs +2 -12
- package/package.json +5 -9
- package/schemas/slothlet.module.schema.json +98 -0
- package/types/dist/lib/builders/api_builder.d.mts.map +1 -1
- package/types/dist/lib/builders/modes-processor.d.mts.map +1 -1
- package/types/dist/lib/handlers/api-manager.d.mts.map +1 -1
- package/types/dist/lib/handlers/module-manager.d.mts +29 -0
- package/types/dist/lib/handlers/module-manager.d.mts.map +1 -0
- package/types/dist/lib/helpers/config.d.mts +7 -0
- package/types/dist/lib/helpers/config.d.mts.map +1 -1
- package/types/dist/lib/helpers/eventemitter-context.d.mts.map +1 -1
- package/types/dist/lib/helpers/generate-manifest.d.mts +2 -0
- package/types/dist/lib/helpers/generate-manifest.d.mts.map +1 -0
- package/types/dist/lib/helpers/manifest-resolver.d.mts +2 -0
- package/types/dist/lib/helpers/manifest-resolver.d.mts.map +1 -0
- package/types/dist/lib/helpers/module-discovery.d.mts +8 -0
- package/types/dist/lib/helpers/module-discovery.d.mts.map +1 -0
- package/types/dist/lib/helpers/module-manifest-validator.d.mts +14 -0
- package/types/dist/lib/helpers/module-manifest-validator.d.mts.map +1 -0
- package/types/dist/lib/helpers/module-sort.d.mts +2 -0
- package/types/dist/lib/helpers/module-sort.d.mts.map +1 -0
- package/types/dist/lib/helpers/resolve-from-caller.d.mts.map +1 -1
- package/types/dist/lib/processors/loader.d.mts.map +1 -1
- package/types/dist/lib/processors/type-generator.d.mts.map +1 -1
- package/types/dist/lib/processors/typescript.d.mts.map +1 -1
- package/types/dist/lib/typegen/typegen.d.mts.map +1 -1
- package/types/dist/slothlet.d.mts.map +1 -1
- package/types/index.d.mts +3 -24
- package/types/index.d.mts.map +1 -1
|
@@ -2,6 +2,9 @@
|
|
|
2
2
|
"translations": {
|
|
3
3
|
"INVALID_CONFIG": "Неверная конфигурация: {option} имеет значение {value}, ожидалось {expected}. {hint}",
|
|
4
4
|
"INVALID_CONFIG_DIR_MISSING": "Ошибка конфигурации: параметр 'dir' обязателен. Укажите путь к директории для загрузки API.",
|
|
5
|
+
"INVALID_CONFIG_BROWSER_REQUIRES_MANIFEST": "Ошибка конфигурации: 'manifest' обязателен, когда env равно 'browser'. Предоставьте заранее сгенерированный объект манифеста с массивами 'files' и 'directories'.",
|
|
6
|
+
"INVALID_CONFIG_BROWSER_MANIFEST_INVALID": "Ошибка конфигурации: 'manifest' должен быть простым объектом с массивами 'files' и 'directories'. Получено: {received}",
|
|
7
|
+
"INVALID_CONFIG_BROWSER_RESOLVE_SPECIFIER_INVALID": "Ошибка конфигурации: 'resolveModuleSpecifier' должен быть функцией, когда env равно 'browser'. Получено: {received}",
|
|
5
8
|
"INVALID_CONFIG_DIR_INVALID": "Ошибка конфигурации: 'dir' должен быть непустой строкой-путем. Получено: {dir}",
|
|
6
9
|
"INVALID_CONFIG_MODE_INVALID": "Ошибка конфигурации: 'mode' должен быть либо 'eager', либо 'lazy'. Получено: {value}",
|
|
7
10
|
"INVALID_CONFIG_NOT_LOADED": "Невозможно выполнить операцию '{operation}' - экземпляр не загружен. Сначала вызовите load().",
|
|
@@ -28,7 +31,7 @@
|
|
|
28
31
|
"CONTEXT_ALREADY_EXISTS": "Контекст для экземпляра '{instanceID}' уже существует. Невозможно инициализировать дважды.",
|
|
29
32
|
"CONTEXT_NOT_FOUND": "Контекст для экземпляра '{instanceID}' не найден. Экземпляр мог быть завершён. Доступные экземпляры: {availableInstances}",
|
|
30
33
|
"CONTEXT_EXECUTION_FAILED": "Не удалось выполнить в контексте для экземпляра '{instanceID}': {error}",
|
|
31
|
-
"NO_ACTIVE_CONTEXT_LIVE": "Нет активного контекста — в режиме
|
|
34
|
+
"NO_ACTIVE_CONTEXT_LIVE": "Нет активного контекста — в режиме живых привязок нет активного экземпляра.",
|
|
32
35
|
"NO_ACTIVE_CONTEXT_ASYNC": "Нет активного контекста — функцию нужно вызывать внутри slothlet API-контекста.",
|
|
33
36
|
"RUNTIME_NO_ACTIVE_CONTEXT_SELF": "Нет активного контекста — доступ к 'self' невозможен. Вызывайте внутри Slothlet API-функции.",
|
|
34
37
|
"RUNTIME_NO_ACTIVE_CONTEXT_CONTEXT": "Нет активного контекста — 'context' можно менять только внутри Slothlet API-вызовов.",
|
|
@@ -54,7 +57,10 @@
|
|
|
54
57
|
"HINT_WARNING_COLLISION_TRIGGER_MATERIALIZE_ERROR": "Это некритическая фоноваая ошибка при обработке конфликтов. Lazy-папка по этому пути не смогла материализоваться. Проверьте модуль по указанному пути.",
|
|
55
58
|
"WARNING_LIFECYCLE_HANDLER_ERROR": "Обработчик lifecycle-события выбросил ошибку для события '{event}'. Другие обработчики продолжили выполнение.",
|
|
56
59
|
"HINT_WARNING_LIFECYCLE_HANDLER_ERROR": "Один из обработчиков lifecycle для события '{event}' выдал ошибку. Проверьте lifecycle.on('{event}', ...) на наличие багов. Другие обработчики unaffected.",
|
|
57
|
-
"WARNING_MULTIPLE_ROOT_CONTRIBUTORS": "Обнаружено несколько
|
|
60
|
+
"WARNING_MULTIPLE_ROOT_CONTRIBUTORS": "Обнаружено несколько default экспортов корневого уровня: {rootContributors}. Каждый был помещён в namespace по имени файла (например api.{firstContributor}()). Рассмотрите вариант объединения.",
|
|
61
|
+
"WARN_SUPPRESS_FIX_ACTIVE": "Исправление правила '{rule}' подавлено через 'suppressFixes'. Подробности см. по адресу {url}. Это временное переопределение, которое будет удалено в v4. Исправленное поведение '{rule}' будет применено постоянно.",
|
|
62
|
+
"HINT_WARN_SUPPRESS_FIX_ACTIVE": "Удалите '{rule}' из 'suppressFixes' и обновите структуру директорий API для поддержки исправленного поведения перед обновлением до v4.",
|
|
63
|
+
"V3_CONFIG_DEPRECATED": "Параметр конфигурации '{option}' устарел и будет удалён в v4. Используйте вместо него '{replacement}'.",
|
|
58
64
|
"V2_CONFIG_UNSUPPORTED": "Опция конфигурации '{option}' из v2 не поддерживается в v3. {hint} Используйте '{replacement}'.",
|
|
59
65
|
"DEBUG_MODE_ROOT_CONTRIBUTOR": "[{mode}] Обнаружен root-contributor: {functionName}",
|
|
60
66
|
"DEBUG_MODE_ROOT_CONTRIBUTOR_APPLIED": "[{mode}] Применён шаблон root-contributor — API-функция с {properties} дополнительными свойствами",
|
|
@@ -78,12 +84,12 @@
|
|
|
78
84
|
"DEBUG_MODE_COLLISION_REPLACE_VERIFY": "СТОЛКНОВЕНИЕ-ЗАМЕНА: проверка",
|
|
79
85
|
"DEBUG_MODE_COLLISION_COPY_CHILD_KEYS": "СТОЛКНОВЕНИЕ-КОПИРОВАНИЕ: копирование дочерних ключей",
|
|
80
86
|
"DEBUG_MODE_COLLISION_COPY_INDIVIDUAL_KEY": "СТОЛКНОВЕНИЕ-КОПИРОВАНИЕ: копирование отдельного ключа",
|
|
81
|
-
"DEBUG_MODE_COLLISION_TRIGGER_EARLY_MAT": "СТОЛКНОВЕНИЕ-ЗАПУСК-ИНСТАНЦИРОВАНИЯ: ранняя материализация (
|
|
87
|
+
"DEBUG_MODE_COLLISION_TRIGGER_EARLY_MAT": "СТОЛКНОВЕНИЕ-ЗАПУСК-ИНСТАНЦИРОВАНИЯ: ранняя материализация (запустил-и-забыл)",
|
|
82
88
|
"DEBUG_MODE_MERGE_API_OBJECTS_ENTRY": "mergeApiObjects: вход",
|
|
83
89
|
"DEBUG_MODE_MERGE_API_OBJECTS_SOURCE_KEYS": "mergeApiObjects: ключи sourceApi",
|
|
84
90
|
"DEBUG_MODE_MERGE_API_OBJECTS_EXIT_INVALID_SOURCE": "mergeApiObjects: выход — sourceApi не объект/функция",
|
|
85
91
|
"DEBUG_MODE_MERGE_API_OBJECTS_PROCESSING_KEY": "mergeApiObjects: обработка ключа",
|
|
86
|
-
"DEBUG_MODE_MERGE_API_OBJECTS_RECURSING": "mergeApiObjects — оба
|
|
92
|
+
"DEBUG_MODE_MERGE_API_OBJECTS_RECURSING": "mergeApiObjects — оба простые object, рекурсивно",
|
|
87
93
|
"DEBUG_MODE_MERGE_API_OBJECTS_CALLING_ASSIGN": "mergeApiObjects: вызов assignToApiPath",
|
|
88
94
|
"DEBUG_MODE_BUILD_FINAL_API_CALLED": "Вызван buildFinalAPI",
|
|
89
95
|
"DEBUG_MODE_SLOTHLET_NAMESPACE_CREATED": "Создано пространство имён Slothlet",
|
|
@@ -139,7 +145,7 @@
|
|
|
139
145
|
"DEBUG_MODE_MUTATE_API_VALUE_SYNC_WRAPPERS": "mutateApiValue — оба являются обёртками, вызывается syncWrapper",
|
|
140
146
|
"DEBUG_MODE_MUTATE_API_VALUE_MERGE_INTO_WRAPPER": "mutateApiValue — слияние свойств в существующую обёртку",
|
|
141
147
|
"DEBUG_MODE_MUTATE_API_VALUE_MERGE_KEYS": "mutateApiValue: ключи для слияния",
|
|
142
|
-
"DEBUG_MODE_MUTATE_API_VALUE_SETIMPL_FALLBACK": "mutateApiValue — использование
|
|
148
|
+
"DEBUG_MODE_MUTATE_API_VALUE_SETIMPL_FALLBACK": "mutateApiValue — использование резервного ___setImpl",
|
|
143
149
|
"DEBUG_MODE_SET_VALUE_AT_PATH": "установить-значение-на-пути",
|
|
144
150
|
"DEBUG_MODE_SET_VALUE_AT_PATH_SKIP_COLLISION": "setValueAtPath — пропуск столкновения",
|
|
145
151
|
"DEBUG_MODE_SET_VALUE_AT_PATH_REPLACE_MERGE": "setValueAtPath — замена слиянием (сохраняет обёртку)",
|
|
@@ -168,7 +174,7 @@
|
|
|
168
174
|
"DEBUG_MODE_WRAPPER_CONSTRUCTOR_IMPL_KEYS": "Конструктор UnifiedWrapper — ключи реализации",
|
|
169
175
|
"DEBUG_MODE_WRAPPER_CONSTRUCTOR_AFTER_ADOPT": "Конструктор UnifiedWrapper — после adopt",
|
|
170
176
|
"DEBUG_MODE_BACKGROUND_MATERIALIZE_ERROR": "Фоновая ошибка материализации",
|
|
171
|
-
"DEBUG_MODE_APPLY_IMPL_UPDATE_PATH": "APPLY-IMPL-UPDATE-PATH: обновлён filePath из null",
|
|
177
|
+
"DEBUG_MODE_APPLY_IMPL_UPDATE_PATH": "[APPLY-IMPL-UPDATE-PATH]: обновлён filePath из null",
|
|
172
178
|
"DEBUG_MODE_SETIMPL_CALLED": "___setImpl вызван",
|
|
173
179
|
"DEBUG_MODE_RESETLAZY_CALLED": "___resetLazy вызван",
|
|
174
180
|
"DEBUG_MODE_RESETLAZY_COMPLETE": "___resetLazy завершён — обёртка теперь не материализована",
|
|
@@ -177,47 +183,47 @@
|
|
|
177
183
|
"DEBUG_MODE_MATERIALIZE_CALLING_FUNC": "_materialize: вызов materializeFunc",
|
|
178
184
|
"DEBUG_MODE_MATERIALIZE_COMPLETE": "_materialize: завершено",
|
|
179
185
|
"DEBUG_MODE_MATERIALIZE_ERROR": "_materialize: ошибка",
|
|
180
|
-
"DEBUG_MODE_ADOPT_START": "ADOPT: начало",
|
|
181
|
-
"DEBUG_MODE_ADOPT_REPLACE_CLEARING": "ADOPT: режим REPLACE — очистка существующих свойств",
|
|
182
|
-
"DEBUG_MODE_ADOPT_PROCESS": "ADOPT: процесс",
|
|
183
|
-
"DEBUG_MODE_ADOPT_CHECK": "ADOPT: проверка",
|
|
184
|
-
"DEBUG_MODE_ADOPT_SKIP_COLLISION_MERGED": "ADOPT: пропуск — столкновение объединено, сохраняется версия из файла",
|
|
185
|
-
"DEBUG_MODE_ADOPT_SKIP_NON_CONFIGURABLE": "ADOPT: пропуск — свойство неконфигурируемо (унаследовано)",
|
|
186
|
-
"DEBUG_MODE_ADOPT_SKIP_SAME_WRAPPER": "ADOPT: пропуск — свойство уже существует с той же обёрткой",
|
|
187
|
-
"DEBUG_MODE_ADOPT_ALLOW_NOT_COLLISION_MERGED": "ADOPT: разрешено — не было объединения",
|
|
188
|
-
"DEBUG_MODE_ADOPT_REUSE_CHILD_WRAPPER": "ADOPT: повторное использование дочерней обёртки",
|
|
189
|
-
"DEBUG_MODE_ADOPT_WRAP": "ADOPT: обёртывание",
|
|
190
|
-
"DEBUG_MODE_ADOPT_DEFINE": "ADOPT: определение на обёртке",
|
|
191
|
-
"DEBUG_MODE_ADOPT_DEFINED": "ADOPT: успешно определено на обёртке",
|
|
192
|
-
"DEBUG_MODE_WRAP_CHILD_PATH_CHECK": "WRAP-CHILD-PATH: проверка пути дочернего файла",
|
|
193
|
-
"DEBUG_MODE_WRAP_CHILD_PATH_AVAILABLE": "WRAP-CHILD-PATH: __childFilePaths доступны",
|
|
194
|
-
"DEBUG_MODE_WRAP_CHILD_PATH_USING": "WRAP-CHILD-PATH: использование __childFilePaths",
|
|
195
|
-
"DEBUG_MODE_WRAP_CHILD_PATH_PRE_MAT": "WRAP-CHILD-PATH: использование __childFilePathsPreMaterialize",
|
|
196
|
-
"DEBUG_MODE_WRAP_CHILD_PATH_FALLBACK": "WRAP-CHILD-PATH: использование запасного parentMetadata?.filePath",
|
|
186
|
+
"DEBUG_MODE_ADOPT_START": "[ADOPT]: начало",
|
|
187
|
+
"DEBUG_MODE_ADOPT_REPLACE_CLEARING": "[ADOPT]: режим REPLACE — очистка существующих свойств",
|
|
188
|
+
"DEBUG_MODE_ADOPT_PROCESS": "[ADOPT]: процесс",
|
|
189
|
+
"DEBUG_MODE_ADOPT_CHECK": "[ADOPT]: проверка",
|
|
190
|
+
"DEBUG_MODE_ADOPT_SKIP_COLLISION_MERGED": "[ADOPT]: пропуск — столкновение объединено, сохраняется версия из файла",
|
|
191
|
+
"DEBUG_MODE_ADOPT_SKIP_NON_CONFIGURABLE": "[ADOPT]: пропуск — свойство неконфигурируемо (унаследовано)",
|
|
192
|
+
"DEBUG_MODE_ADOPT_SKIP_SAME_WRAPPER": "[ADOPT]: пропуск — свойство уже существует с той же обёрткой",
|
|
193
|
+
"DEBUG_MODE_ADOPT_ALLOW_NOT_COLLISION_MERGED": "[ADOPT]: разрешено — не было объединения",
|
|
194
|
+
"DEBUG_MODE_ADOPT_REUSE_CHILD_WRAPPER": "[ADOPT]: повторное использование дочерней обёртки",
|
|
195
|
+
"DEBUG_MODE_ADOPT_WRAP": "[ADOPT]: обёртывание",
|
|
196
|
+
"DEBUG_MODE_ADOPT_DEFINE": "[ADOPT]: определение на обёртке",
|
|
197
|
+
"DEBUG_MODE_ADOPT_DEFINED": "[ADOPT]: успешно определено на обёртке",
|
|
198
|
+
"DEBUG_MODE_WRAP_CHILD_PATH_CHECK": "[WRAP-CHILD-PATH]: проверка пути дочернего файла",
|
|
199
|
+
"DEBUG_MODE_WRAP_CHILD_PATH_AVAILABLE": "[WRAP-CHILD-PATH]: __childFilePaths доступны",
|
|
200
|
+
"DEBUG_MODE_WRAP_CHILD_PATH_USING": "[WRAP-CHILD-PATH]: использование __childFilePaths",
|
|
201
|
+
"DEBUG_MODE_WRAP_CHILD_PATH_PRE_MAT": "[WRAP-CHILD-PATH]: использование __childFilePathsPreMaterialize",
|
|
202
|
+
"DEBUG_MODE_WRAP_CHILD_PATH_FALLBACK": "[WRAP-CHILD-PATH]: использование запасного parentMetadata?.filePath",
|
|
197
203
|
"DEBUG_MODE_WAITING_TYPE": "ОЖИДАЕТ-ТИП",
|
|
198
|
-
"DEBUG_MODE_WAITING_TYPE_WALK_WRAPPER": "WAITING-TYPE-WALK: найдено в обёртке",
|
|
199
|
-
"DEBUG_MODE_WAITING_TYPE_WALK_IMPL": "WAITING-TYPE-WALK: найдено в _impl",
|
|
200
|
-
"DEBUG_MODE_WAITING_TYPE_WALK_DIRECT": "WAITING-TYPE-WALK: доступ напрямую",
|
|
204
|
+
"DEBUG_MODE_WAITING_TYPE_WALK_WRAPPER": "[WAITING-TYPE-WALK]: найдено в обёртке",
|
|
205
|
+
"DEBUG_MODE_WAITING_TYPE_WALK_IMPL": "[WAITING-TYPE-WALK]: найдено в _impl",
|
|
206
|
+
"DEBUG_MODE_WAITING_TYPE_WALK_DIRECT": "[WAITING-TYPE-WALK]: доступ напрямую",
|
|
201
207
|
"DEBUG_MODE_WAITING_TYPE_RESOLVED": "ОЖИДАЕТ-ТИП: разрешено",
|
|
202
|
-
"DEBUG_MODE_WAITING_TYPE_INFLIGHT": "WAITING-TYPE-INFLIGHT: возвращает IN_FLIGHT",
|
|
203
|
-
"DEBUG_MODE_WAITING_GET_PREMATURE": "WAITING-GET-PREMATURE: найдено до материализации",
|
|
204
|
-
"DEBUG_MODE_WAITING_GET_IMMEDIATE_MAT": "WAITING-GET-IMMEDIATE-MAT: инициирована немедленная материализация для
|
|
205
|
-
"DEBUG_MODE_WAITING_GET_IMMEDIATE_MAT_ERROR": "WAITING-GET-IMMEDIATE-MAT-ERROR: материализация не удалась",
|
|
206
|
-
"DEBUG_MODE_WAITING_GET_IMMEDIATE_MAT_SUCCESS": "WAITING-GET-IMMEDIATE-MAT-SUCCESS: теперь доступно в обёртке",
|
|
208
|
+
"DEBUG_MODE_WAITING_TYPE_INFLIGHT": "[WAITING-TYPE-INFLIGHT]: возвращает IN_FLIGHT",
|
|
209
|
+
"DEBUG_MODE_WAITING_GET_PREMATURE": "[WAITING-GET-PREMATURE]: найдено до материализации",
|
|
210
|
+
"DEBUG_MODE_WAITING_GET_IMMEDIATE_MAT": "[WAITING-GET-IMMEDIATE-MAT]: инициирована немедленная материализация для объединённой при коллизии папки",
|
|
211
|
+
"DEBUG_MODE_WAITING_GET_IMMEDIATE_MAT_ERROR": "[WAITING-GET-IMMEDIATE-MAT-ERROR]: материализация не удалась",
|
|
212
|
+
"DEBUG_MODE_WAITING_GET_IMMEDIATE_MAT_SUCCESS": "[WAITING-GET-IMMEDIATE-MAT-SUCCESS]: теперь доступно в обёртке",
|
|
207
213
|
"DEBUG_MODE_WAITING_APPLY_ENTRY": "ОЖИДАЕТ-АНДЕР-ЗАПИСЬ",
|
|
208
|
-
"DEBUG_MODE_WAITING_APPLY_MATERIALIZE": "WAITING-APPLY-MATERIALIZE: запуск материализации",
|
|
209
|
-
"DEBUG_MODE_WAITING_APPLY_MATERIALIZED": "WAITING-APPLY-MATERIALIZED: материализация завершена",
|
|
210
|
-
"DEBUG_MODE_WAITING_APPLY_START_WALK": "WAITING-APPLY-START-WALK: начало обхода propChain",
|
|
214
|
+
"DEBUG_MODE_WAITING_APPLY_MATERIALIZE": "[WAITING-APPLY-MATERIALIZE]: запуск материализации",
|
|
215
|
+
"DEBUG_MODE_WAITING_APPLY_MATERIALIZED": "[WAITING-APPLY-MATERIALIZED]: материализация завершена",
|
|
216
|
+
"DEBUG_MODE_WAITING_APPLY_START_WALK": "[WAITING-APPLY-START-WALK]: начало обхода propChain",
|
|
211
217
|
"DEBUG_MODE_WAITING_APPLY_WALK": "ОЖИДАЕТ-ОБХОД-АНДЕР",
|
|
212
218
|
"DEBUG_MODE_WAITING_APPLY": "ОЖИДАЕТ-АНДЕР",
|
|
213
|
-
"DEBUG_MODE_GET_START": "GET: начало",
|
|
219
|
+
"DEBUG_MODE_GET_START": "[GET]: начало",
|
|
214
220
|
"DEBUG_MODE_INSPECT_LAZY_UNMATERIALIZED": "util.inspect.custom: lazy не материализовано",
|
|
215
221
|
"DEBUG_MODE_GET_CACHED_REPLACE": "ПОЛУЧИТЬ-ИЗ-КЭША-ЗАМЕНИТЬ",
|
|
216
222
|
"DEBUG_MODE_GET_CACHED": "ПОЛУЧЕНО-ИЗ-КЭША",
|
|
217
223
|
"DEBUG_MODE_ADOPT": "ПРИНЯТЬ",
|
|
218
|
-
"DEBUG_MODE_GET_PROXYGET_ACCESSING": "GET-PROXYGET: доступ",
|
|
219
|
-
"DEBUG_MODE_GET_PROXYGET_FOUND": "GET-PROXYGET: найдено в обёртке",
|
|
220
|
-
"DEBUG_MODE_LAZY_GET_CREATE_WAITING_PROXY": "LAZY-GET: будет создан ожид. прокси",
|
|
224
|
+
"DEBUG_MODE_GET_PROXYGET_ACCESSING": "[GET-PROXYGET]: доступ",
|
|
225
|
+
"DEBUG_MODE_GET_PROXYGET_FOUND": "[GET-PROXYGET]: найдено в обёртке",
|
|
226
|
+
"DEBUG_MODE_LAZY_GET_CREATE_WAITING_PROXY": "[LAZY-GET]: будет создан ожид. прокси",
|
|
221
227
|
"DEBUG_MODE_BUILD_LAZY_API_CALLED": "buildLazyAPI вызван",
|
|
222
228
|
"DEBUG_MODE_COMPONENT_INITIALIZED": "Компонент инициализирован: {component} → this.{category}.{propertyName}",
|
|
223
229
|
"DEBUG_MODE_LAZY_WRAPPER_REGISTERED": "Lazy-обёртка зарегистрирована",
|
|
@@ -261,6 +267,7 @@
|
|
|
261
267
|
"HINT_WARNING_HOT_RELOAD_MERGE_PRIMITIVES": "Нельзя объединить примитивы — используйте 'replace' для перезаписи или оставьте 'warn' для сохранения существующего.",
|
|
262
268
|
"HINT_WARNING_OWNERSHIP_CONFLICT": "Путь принадлежит другому модулю — используйте forceOverwrite: true или вызовите api.slothlet.api.remove().",
|
|
263
269
|
"HINT_V2_CONFIG_UNSUPPORTED": "Опция v2 не поддерживается в v3 — обновите конфигурацию.",
|
|
270
|
+
"HINT_V3_CONFIG_DEPRECATED": "Этот параметр конфигурации был переименован для ясности. Обновите код на новое имя параметра для совместимости с v4.",
|
|
264
271
|
"HINT_MODULE_LOAD_FAILED": "Проверьте файл модуля на ошибки синтаксиса и экспорты.",
|
|
265
272
|
"HINT_MODULE_IMPORT_FAILED": "Проверьте существование файла и корректность импорта. Проверьте зависимости.",
|
|
266
273
|
"HINT_MODULE_NO_EXPORTS": "Модуль должен экспортировать хотя бы одну функцию или объект.",
|
|
@@ -405,7 +412,29 @@
|
|
|
405
412
|
"PERM_RULE_CONDITION_INVALID": "rule.condition должно быть обычным объектом, функцией или массивом, в котором каждый элемент является обычным объектом или функцией",
|
|
406
413
|
"BRACE_EXPANSION_MAX_DEPTH": "Расширение фигурных скобок превысило максимальную глубину {maxDepth}",
|
|
407
414
|
"HINT_BRACE_EXPANSION_MAX_DEPTH": "Уменьшите вложенность в ваших шаблонах скобок или увеличьте параметр maxDepth.",
|
|
408
|
-
"PERMISSION_MANAGER_NOT_AVAILABLE": "Менеджер разрешений недоступен в этом экземпляре Slothlet"
|
|
415
|
+
"PERMISSION_MANAGER_NOT_AVAILABLE": "Менеджер разрешений недоступен в этом экземпляре Slothlet",
|
|
416
|
+
"MODULE_MANIFEST_NOT_FOUND": "Манифест модуля не найден для пакета '{packageName}': ожидался '{manifestPath}', но файл не существует.",
|
|
417
|
+
"HINT_MODULE_MANIFEST_NOT_FOUND": "Модули плагинов должны поставляться с 'slothlet.module.json' в корне пакета (или файлом/ключом, указанным через переопределение `manifest`). Проверьте, что файл существует в пакете и локатор переопределения корректен.",
|
|
418
|
+
"MODULE_MANIFEST_INVALID": "Манифест модуля недействителен для пакета '{packageName}' в '{manifestPath}': {reason}.",
|
|
419
|
+
"HINT_MODULE_MANIFEST_INVALID": "Откройте манифест и убедитесь, что это валидный JSON и он соответствует схеме модуля slothlet (см. schemas/slothlet.module.schema.json). Обязательные поля: schemaVersion, mountPath, apiDir.",
|
|
420
|
+
"MODULE_MANIFEST_UNKNOWN_FIELD": "Манифест модуля для пакета '{packageName}' содержит нераспознанное поле верхнего уровня '{field}'. Неизвестные поля отклоняются; используйте выделенный блок 'metadata' для определяемых разработчиком дополнений.",
|
|
421
|
+
"HINT_MODULE_MANIFEST_UNKNOWN_FIELD": "Если '{field}' — это опечатка, исправьте её. Если это намеренные метаданные разработчика, переместите их внутрь объекта 'metadata'.",
|
|
422
|
+
"MODULE_MANIFEST_NAME_MISMATCH": "Манифест модуля для пакета '{packageName}' объявляет имя '{manifestName}', которое не совпадает с именем в package.json '{packageName}'.",
|
|
423
|
+
"HINT_MODULE_MANIFEST_NAME_MISMATCH": "Поле 'name' в slothlet.module.json является необязательным, но должно совпадать с package.json при наличии. Либо удалите поле 'name' из манифеста, либо приведите его в соответствие с package.json.",
|
|
424
|
+
"MODULE_MANIFEST_VERSION_MISMATCH": "Манифест модуля для пакета '{packageName}' объявляет версию '{manifestVersion}', которая не совпадает с версией в package.json '{packageVersion}'.",
|
|
425
|
+
"HINT_MODULE_MANIFEST_VERSION_MISMATCH": "Поле 'version' в slothlet.module.json является необязательным, но должно совпадать с package.json при наличии. Либо удалите поле 'version' из манифеста, либо приведите его в соответствие с package.json.",
|
|
426
|
+
"MODULE_PATH_TRAVERSAL": "Пакет модуля '{packageName}' имеет apiDir '{apiDir}', который разрешается за пределами корня пакета ('{resolvedPath}' не находится под '{packageRoot}').",
|
|
427
|
+
"HINT_MODULE_PATH_TRAVERSAL": "apiDir должен быть относительным путём, остающимся внутри пакета. Удалите сегменты '..' и абсолютные пути.",
|
|
428
|
+
"MODULE_VERSION_UNSUPPORTED": "Манифест модуля для пакета '{packageName}' использует неподдерживаемую schemaVersion '{schemaVersion}'. Поддерживаемые: 1.",
|
|
429
|
+
"HINT_MODULE_VERSION_UNSUPPORTED": "Обновите slothlet до версии, поддерживающей schemaVersion {schemaVersion}, или обновите манифест для использования schemaVersion 1.",
|
|
430
|
+
"MODULE_PACKAGE_NOT_FOUND": "Пакет модуля '{packageName}' не найден в кэше обнаружения. {hint}",
|
|
431
|
+
"HINT_MODULE_PACKAGE_NOT_FOUND": "Либо пакет не установлен, либо scanRoot не охватывает его местоположение, либо вызов discover() ещё не был выполнен. addModule(name) лениво запустит discover() с scanRoot=process.cwd(), если кэш пуст.",
|
|
432
|
+
"MODULE_RESERVED_MOUNTPATH": "Пакет модуля '{packageName}' объявляет зарезервированный корень mountPath '{mountPathRoot}' (из mountPath '{mountPath}'). Зарезервированные корни: slothlet, shutdown, destroy.",
|
|
433
|
+
"HINT_MODULE_RESERVED_MOUNTPATH": "Зарезервированные корни mountPath используются встроенным интерфейсом slothlet и не могут быть востребованы модулями. Выберите другой корневой сегмент.",
|
|
434
|
+
"MODULE_DUPLICATE_NAME_VERSION_MISMATCH": "Пакет модуля '{packageName}' версии '{version}' был найден по нескольким различным реальным путям: {paths}.",
|
|
435
|
+
"HINT_MODULE_DUPLICATE_NAME_VERSION_MISMATCH": "Это почти наверняка ошибка конфигурации — один и тот же пакет той же версии установлен в двух несвязанных местах. Выясните, какая установка является целевой, и удалите другую.",
|
|
436
|
+
"MODULE_MOUNT_COLLISION": "Пакет модуля '{packageName}' не может быть смонтирован в '{mountPath}': путь уже занят модулем '{existingModuleID}' и collisionMode имеет значение '{collisionMode}'.",
|
|
437
|
+
"HINT_MODULE_MOUNT_COLLISION": "Измените mountPath в манифесте, передайте `collisionMode: \"merge\"` в `addModule()` / `addModules()` для разрешения сосуществования или сначала вызовите `removeModule()` для существующего модуля."
|
|
409
438
|
},
|
|
410
439
|
"metadata": {
|
|
411
440
|
"code": "ru-ru",
|
|
@@ -2,6 +2,9 @@
|
|
|
2
2
|
"translations": {
|
|
3
3
|
"INVALID_CONFIG": "配置错误:{option} 为 {value},预期为 {expected}。{hint}",
|
|
4
4
|
"INVALID_CONFIG_DIR_MISSING": "配置错误:必须提供 'dir' 选项。请指定加载 API 的目录路径。",
|
|
5
|
+
"INVALID_CONFIG_BROWSER_REQUIRES_MANIFEST": "配置错误:当 env 为 'browser' 时,必须提供 'manifest'。请提供包含 'files' 和 'directories' 数组的预生成 manifest 对象。",
|
|
6
|
+
"INVALID_CONFIG_BROWSER_MANIFEST_INVALID": "配置错误:'manifest' 必须是包含 'files' 和 'directories' 数组的普通对象。收到:{received}",
|
|
7
|
+
"INVALID_CONFIG_BROWSER_RESOLVE_SPECIFIER_INVALID": "配置错误:当 env 为 'browser' 时,'resolveModuleSpecifier' 必须是一个函数。收到:{received}",
|
|
5
8
|
"INVALID_CONFIG_DIR_INVALID": "配置错误:'dir' 必须是非空字符串路径。接收到:{dir}",
|
|
6
9
|
"INVALID_CONFIG_MODE_INVALID": "配置错误:'mode' 必须是 'eager' 或 'lazy'。接收到:{value}",
|
|
7
10
|
"INVALID_CONFIG_NOT_LOADED": "无法执行操作 '{operation}' - 实例未加载。请先调用 load()。",
|
|
@@ -14,7 +17,7 @@
|
|
|
14
17
|
"METADATA_LIFECYCLE_BYPASS": "tagSystemMetadata() 必须通过生命周期系统调用。请使用 lifecycle.emit('impl:created') 或 lifecycle.emit('impl:changed'),而不是直接调用。",
|
|
15
18
|
"INVALID_METADATA_KEY": "无效的元数据键:预期为 {expected},键 '{key}' 接收到类型 {type}。元数据键必须格式正确。",
|
|
16
19
|
"WARNING_METADATA_MISMATCH": "安全警告:'{apiPath}' 的元数据文件路径不匹配。堆栈跟踪显示为 '{stackFile}',但元数据记录为 '{metadataFile}'。这可能表明存在热重载或篡改。",
|
|
17
|
-
"COLLISION_ERROR": "
|
|
20
|
+
"COLLISION_ERROR": "冲突错误位于 '{key}' (context: {collisionContext}, mode: {collisionMode})。请将冲突模式设置为 'merge'、'replace'、'warn' 或 'skip' 以处理冲突。",
|
|
18
21
|
"COLLISION_DEFAULT_EXPORT_ERROR": "检测到冲突:属性 '{key}' 已存在于 '{apiPath}' 的默认导出中。当前冲突模式为 'error'。",
|
|
19
22
|
"INVALID_ARGUMENT": "无效参数 '{argument}':预期为 {expected},接收到 {received}。",
|
|
20
23
|
"RUNTIME_NO_ACTIVE_CONTEXT": "未找到活动上下文。此操作需要在 slothlet API 函数内部调用。",
|
|
@@ -39,7 +42,7 @@
|
|
|
39
42
|
"LOOSE_SET_RESERVED_KEY": "无法在 apiPath '{apiPath}' 中分配给保留键段 '{segment}'。__proto__、prototype 和 constructor 等名称已被阻止,以防止 API 树受到原型污染。",
|
|
40
43
|
"INVALID_DIRECTORY": "无效目录 '{dir}':{error}。请确保目录存在且可访问。",
|
|
41
44
|
"WARN_DIRECTORY_EMPTY": "目录 '{dir}'(解析路径:'{resolvedPath}')为空或不包含可加载模块(.mjs, .cjs, .js 文件)。这在 add-api 工作流程中是有效的,但可能表明路径配置有误。",
|
|
42
|
-
"INTERNAL_HOOK_STATE_CORRUPT": "[内部错误] HookManager 状态损坏:钩子 \"{hookId}\" ({type}/{subset}/{pattern}) — {detail}。这是一个 Slothlet
|
|
45
|
+
"INTERNAL_HOOK_STATE_CORRUPT": "[内部错误] HookManager 状态损坏:钩子 \"{hookId}\" ({type}/{subset}/{pattern}) — {detail}。这是一个 Slothlet 的缺陷 — 请报告它。",
|
|
43
46
|
"WARNING_RESERVED_PROPERTY_CONFLICT": "用户 API 与保留属性冲突:{properties}",
|
|
44
47
|
"WARNING_LANGUAGE_LOAD_FAILED": "加载语言 '{lang}' 失败,正在回退到英语。",
|
|
45
48
|
"WARNING_LANGUAGE_UNAVAILABLE": "语言 '{lang}' 不可用,正在使用英语。",
|
|
@@ -55,6 +58,9 @@
|
|
|
55
58
|
"WARNING_LIFECYCLE_HANDLER_ERROR": "生命周期事件处理程序为事件 '{event}' 抛出错误。该事件的其他处理程序继续执行。",
|
|
56
59
|
"HINT_WARNING_LIFECYCLE_HANDLER_ERROR": "为 '{event}' 事件注册的生命周期处理程序抛出错误。请检查您的 lifecycle.on('{event}', ...) 处理程序是否存在错误。同一事件中的其他处理程序不受影响。",
|
|
57
60
|
"WARNING_MULTIPLE_ROOT_CONTRIBUTORS": "检测到多个根级默认函数导出:{rootContributors}。每个导出已通过文件名(例如 api.{firstContributor}())进行命名空间划分。请考虑使用单个根级默认导出或将文件移动到子目录中。",
|
|
61
|
+
"WARN_SUPPRESS_FIX_ACTIVE": "规则修复 '{rule}' 已通过 'suppressFixes' 被禁用。详情请参阅 {url}。这是一个临时覆盖,将在 v4 中移除。'{rule}' 的修正行为将被永久执行。",
|
|
62
|
+
"HINT_WARN_SUPPRESS_FIX_ACTIVE": "在升级到 v4 之前,请从 'suppressFixes' 中删除 '{rule}',并更新您的 API 目录结构以适应修正后的行为。",
|
|
63
|
+
"V3_CONFIG_DEPRECATED": "配置选项 '{option}' 已弃用,将在 v4 中移除。请改用 '{replacement}'。",
|
|
58
64
|
"V2_CONFIG_UNSUPPORTED": "v2 中的配置选项 '{option}' 在 v3 中不受支持。{hint} 请改为使用 '{replacement}'。",
|
|
59
65
|
"DEBUG_MODE_ROOT_CONTRIBUTOR": "[{mode}] 检测到根贡献者:{functionName}",
|
|
60
66
|
"DEBUG_MODE_ROOT_CONTRIBUTOR_APPLIED": "[{mode}] 应用了根贡献者模式 - 具有 {properties} 个附加属性的函数 API",
|
|
@@ -168,7 +174,7 @@
|
|
|
168
174
|
"DEBUG_MODE_WRAPPER_CONSTRUCTOR_IMPL_KEYS": "UnifiedWrapper 构造函数 - 实现(impl)键",
|
|
169
175
|
"DEBUG_MODE_WRAPPER_CONSTRUCTOR_AFTER_ADOPT": "UnifiedWrapper 构造函数 - 采纳(adopt)后",
|
|
170
176
|
"DEBUG_MODE_BACKGROUND_MATERIALIZE_ERROR": "后台实例化错误",
|
|
171
|
-
"DEBUG_MODE_APPLY_IMPL_UPDATE_PATH": "APPLY-IMPL-UPDATE-PATH:从 null 更新了 filePath",
|
|
177
|
+
"DEBUG_MODE_APPLY_IMPL_UPDATE_PATH": "[APPLY-IMPL-UPDATE-PATH]:从 null 更新了 filePath",
|
|
172
178
|
"DEBUG_MODE_SETIMPL_CALLED": "___setImpl 已调用",
|
|
173
179
|
"DEBUG_MODE_RESETLAZY_CALLED": "___resetLazy 已调用",
|
|
174
180
|
"DEBUG_MODE_RESETLAZY_COMPLETE": "___resetLazy 完成 - 包装器现在处于未实例化(un-materialized)状态",
|
|
@@ -235,7 +241,7 @@
|
|
|
235
241
|
"HINT_OWNERSHIP_INVALID_MODULE_ID": "模块 ID 无效。请确保模块加载正确且 ID 准确。",
|
|
236
242
|
"HINT_OWNERSHIP_INVALID_API_PATH": "API 路径无效。请确保路径格式正确。",
|
|
237
243
|
"HINT_OWNERSHIP_CONFLICT": "路径已被另一个模块拥有。选项:(1) 在 api.add() 选项中使用 forceOverwrite: true;(2) 将冲突配置设置为 'merge' 或 'replace';(3) 先调用 api.slothlet.api.remove() 清除路径。",
|
|
238
|
-
"HINT_INVALID_CONFIG": "
|
|
244
|
+
"HINT_INVALID_CONFIG": "请检查配置值,并确保其符合预期的类型和格式。",
|
|
239
245
|
"HINT_INVALID_CONFIG_DIR_INVALID": "请确保目录路径是有效的字符串,并指向一个现有的目录。",
|
|
240
246
|
"HINT_INVALID_CONFIG_FORCE_OVERWRITE_REQUIRES_MODULE_ID": "使用 forceOverwrite 或 allowOverwrite 时,必须提供唯一的 moduleID 以跟踪被覆盖路径的所有权。",
|
|
241
247
|
"HINT_COLLISION_ERROR": "冲突模式设置为 'error',会阻止任何覆盖。请改用 'merge'(合并属性)、'replace'(完全覆盖)、'warn'(警告但保留现有)或 'skip'(默默忽略)。",
|
|
@@ -261,14 +267,15 @@
|
|
|
261
267
|
"HINT_WARNING_HOT_RELOAD_MERGE_PRIMITIVES": "无法合并原始值(字符串、数字、布尔值)。若要替换该值,请使用冲突模式 'replace';若要保留现有值,请继续使用 'warn' 模式。",
|
|
262
268
|
"HINT_WARNING_OWNERSHIP_CONFLICT": "路径已被另一个模块拥有。若要强制覆盖,请在 api.add() 选项中使用 forceOverwrite: true,或先调用 api.slothlet.api.remove() 清除路径。",
|
|
263
269
|
"HINT_V2_CONFIG_UNSUPPORTED": "v2 中的此配置选项在 v3 中不受支持。请更新您的配置以使用 v3 的对应项,从而获得更好的控制权和清晰度。",
|
|
270
|
+
"HINT_V3_CONFIG_DEPRECATED": "此配置选项已重命名以提高清晰度。请更新代码以使用新选项名称,以确保与 v4 的向前兼容性。",
|
|
264
271
|
"HINT_MODULE_LOAD_FAILED": "请检查模块文件是否存在错误。确保其具有有效的 JavaScript 语法和导出。",
|
|
265
272
|
"HINT_MODULE_IMPORT_FAILED": "请确保模块文件存在且可导入。检查语法错误或缺失的依赖项。",
|
|
266
273
|
"HINT_MODULE_NO_EXPORTS": "模块必须导出一个或多个函数或对象。",
|
|
267
274
|
"HINT_INVALID_DIRECTORY": "请确保目录路径正确,且目录存在且可访问。",
|
|
268
275
|
"HINT_WARN_DIRECTORY_EMPTY": "请检查目录路径是否正确。在模块动态加载的 add-api 工作流中,空目录是合法的。",
|
|
269
276
|
"HINT_INVALID_CONFIG_LAZY_NOT_READY": "在延迟(lazy)模式下,请在调用函数前通过访问属性来触发实例化。",
|
|
270
|
-
"HINT_INTERNAL_INVALID_STATE": "
|
|
271
|
-
"HINT_INTERNAL_HOOK_STATE_CORRUPT": "HookManager 内部状态不一致。这在正常使用中不应发生 —
|
|
277
|
+
"HINT_INTERNAL_INVALID_STATE": "发生内部状态错误。这很可能是一个缺陷 — 请提供详细信息以便报告。",
|
|
278
|
+
"HINT_INTERNAL_HOOK_STATE_CORRUPT": "HookManager 内部状态不一致。这在正常使用中不应发生 — 请提交缺陷报告,并说明您是如何调用 hook.on() 和 hook.remove() 的。",
|
|
272
279
|
"HINT_INVALID_CONFIG_LAZY_ASYNC_REQUIRED": "延迟模式需要异步操作。请使用立即(eager)模式或实现适当的异步模式。",
|
|
273
280
|
"HINT_RUNTIME_NO_ACTIVE_CONTEXT_SELF": "无法在 slothlet API 函数上下文之外访问 'self'。",
|
|
274
281
|
"HINT_RUNTIME_NO_ACTIVE_CONTEXT_CONTEXT": "无法在 slothlet API 函数外部访问 'context'。上下文只能在 slothlet API 调用内部修改。",
|
|
@@ -294,7 +301,7 @@
|
|
|
294
301
|
"HOOK_BRACE_EXPANSION_MAX_DEPTH": "钩子模式中的大括号扩展超过了最大嵌套深度 {maxDepth}。",
|
|
295
302
|
"HINT_HOOK_BRACE_EXPANSION_MAX_DEPTH": "请简化钩子路径模式以减少大括号嵌套。最大允许深度为 {maxDepth} 层。",
|
|
296
303
|
"SCOPE_DISABLED": "每请求上下文隔离已禁用。请在 slothlet 配置中设置 'scope: {}' 以启用该功能。",
|
|
297
|
-
"HINT_SCOPE_DISABLED": "请在您的 slothlet 选项中添加 'scope: {}'(或一个 scope 配置对象),以通过 api.slothlet.run()
|
|
304
|
+
"HINT_SCOPE_DISABLED": "请在您的 slothlet 选项中添加 'scope: {}'(或一个 scope 配置对象),以通过 api.slothlet.run() 和 api.slothlet.scope() 启用每请求上下文隔离。",
|
|
298
305
|
"SCOPE_INVALID_CONTEXT": "上下文 data 必须是一个普通对象。接收到:{received}。",
|
|
299
306
|
"HINT_SCOPE_INVALID_CONTEXT": "请将一个普通对象作为第一个参数传递给 run(),例如 api.slothlet.run({ userId: 1 }, callback)。",
|
|
300
307
|
"SCOPE_INVALID_CALLBACK": "回调必须是一个函数。接收到:{received}。",
|
|
@@ -405,7 +412,29 @@
|
|
|
405
412
|
"PERM_RULE_CONDITION_INVALID": "rule.condition 必须是普通对象、函数,或一个数组且其中每个条目都必须是普通对象或函数",
|
|
406
413
|
"BRACE_EXPANSION_MAX_DEPTH": "花括号展开超过了最大深度 {maxDepth}",
|
|
407
414
|
"HINT_BRACE_EXPANSION_MAX_DEPTH": "减少括号模式中的嵌套层数或增加 maxDepth 选项。",
|
|
408
|
-
"PERMISSION_MANAGER_NOT_AVAILABLE": "权限管理器在此 Slothlet 实例中不可用"
|
|
415
|
+
"PERMISSION_MANAGER_NOT_AVAILABLE": "权限管理器在此 Slothlet 实例中不可用",
|
|
416
|
+
"MODULE_MANIFEST_NOT_FOUND": "未找到包 '{packageName}' 的模块清单:预期为 '{manifestPath}' 但文件不存在。",
|
|
417
|
+
"HINT_MODULE_MANIFEST_NOT_FOUND": "插件模块必须在包根目录提供 'slothlet.module.json'(或通过 `manifest` 覆盖指定的文件/键)。请检查文件是否存在于包中以及覆盖定位符是否正确。",
|
|
418
|
+
"MODULE_MANIFEST_INVALID": "包 '{packageName}' 的模块清单在 '{manifestPath}' 处无效:{reason}。",
|
|
419
|
+
"HINT_MODULE_MANIFEST_INVALID": "打开清单并确保它是有效的 JSON 且符合 slothlet 模块架构(参见 schemas/slothlet.module.schema.json)。必需字段:schemaVersion、mountPath、apiDir。",
|
|
420
|
+
"MODULE_MANIFEST_UNKNOWN_FIELD": "包 '{packageName}' 的模块清单包含无法识别的顶层字段 '{field}'。未知字段将被拒绝;请使用专用的 'metadata' 块来存放开发者定义的附加项。",
|
|
421
|
+
"HINT_MODULE_MANIFEST_UNKNOWN_FIELD": "如果 '{field}' 是拼写错误,请修正。如果是有意的开发者元数据,请将其移到 'metadata' 对象内。",
|
|
422
|
+
"MODULE_MANIFEST_NAME_MISMATCH": "包 '{packageName}' 的模块清单声明名称 '{manifestName}',与 package.json 中的名称 '{packageName}' 不匹配。",
|
|
423
|
+
"HINT_MODULE_MANIFEST_NAME_MISMATCH": "slothlet.module.json 中的 'name' 字段是可选的,但存在时必须与 package.json 匹配。请删除清单中的 'name' 字段或使其与 package.json 一致。",
|
|
424
|
+
"MODULE_MANIFEST_VERSION_MISMATCH": "包 '{packageName}' 的模块清单声明版本 '{manifestVersion}',与 package.json 中的版本 '{packageVersion}' 不匹配。",
|
|
425
|
+
"HINT_MODULE_MANIFEST_VERSION_MISMATCH": "slothlet.module.json 中的 'version' 字段是可选的,但存在时必须与 package.json 匹配。请删除清单中的 'version' 字段或使其与 package.json 一致。",
|
|
426
|
+
"MODULE_PATH_TRAVERSAL": "模块包 '{packageName}' 的 apiDir '{apiDir}' 解析到包根目录之外('{resolvedPath}' 不在 '{packageRoot}' 之下)。",
|
|
427
|
+
"HINT_MODULE_PATH_TRAVERSAL": "apiDir 必须是保持在包内部的相对路径。请删除 '..' 段和绝对路径。",
|
|
428
|
+
"MODULE_VERSION_UNSUPPORTED": "包 '{packageName}' 的模块清单使用了不支持的 schemaVersion '{schemaVersion}'。支持的版本:1。",
|
|
429
|
+
"HINT_MODULE_VERSION_UNSUPPORTED": "将 slothlet 升级到支持 schemaVersion {schemaVersion} 的版本,或将清单更新为使用 schemaVersion 1。",
|
|
430
|
+
"MODULE_PACKAGE_NOT_FOUND": "在发现缓存中未找到模块包 '{packageName}'。{hint}",
|
|
431
|
+
"HINT_MODULE_PACKAGE_NOT_FOUND": "可能是包未安装、scanRoot 未覆盖其位置,或尚未进行 discover() 调用。如果缓存为空,addModule(name) 将使用 scanRoot=process.cwd() 延迟运行 discover()。",
|
|
432
|
+
"MODULE_RESERVED_MOUNTPATH": "模块包 '{packageName}' 声明了保留的 mountPath 根 '{mountPathRoot}'(来自 mountPath '{mountPath}')。保留的根:slothlet、shutdown、destroy。",
|
|
433
|
+
"HINT_MODULE_RESERVED_MOUNTPATH": "保留的 mountPath 根由 slothlet 的内置接口使用,模块不能占用。请选择不同的根段。",
|
|
434
|
+
"MODULE_DUPLICATE_NAME_VERSION_MISMATCH": "模块包 '{packageName}' 版本 '{version}' 在多个不同的真实路径中被发现:{paths}。",
|
|
435
|
+
"HINT_MODULE_DUPLICATE_NAME_VERSION_MISMATCH": "这几乎肯定是配置错误 — 同一版本的同一包安装在两个不相关的位置。请调查哪个安装是预期的并删除另一个。",
|
|
436
|
+
"MODULE_MOUNT_COLLISION": "模块包 '{packageName}' 无法挂载在 '{mountPath}':路径已被模块 '{existingModuleID}' 占用且 collisionMode 为 '{collisionMode}'。",
|
|
437
|
+
"HINT_MODULE_MOUNT_COLLISION": "更改清单的 mountPath,向 `addModule()` / `addModules()` 传递 `collisionMode: \"merge\"` 以允许共存,或先在现有模块上调用 `removeModule()`。"
|
|
409
438
|
},
|
|
410
439
|
"metadata": {
|
|
411
440
|
"code": "zh-cn",
|
|
@@ -14,4 +14,4 @@
|
|
|
14
14
|
limitations under the License.
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
|
-
import{readdir,stat}from"node:fs/promises";import{join,extname,basename,resolve}from"node:path";import{pathToFileURL}from"node:url";import{createRequire}from"node:module";import{ComponentBase}from"@cldmv/slothlet/factories/component-base";class Loader extends ComponentBase{static slothletProperty="loader";constructor(slothlet){super(slothlet)}async loadModule(filePath,instanceID,moduleID,cacheBust=null){try{if(filePath.endsWith(".cjs")){return this.#loadCJSIsolated(filePath)}const isTypeScript=filePath.endsWith(".ts")||filePath.endsWith(".mts");const typescriptConfig=this.slothlet.config?.typescript;let moduleUrl;if(isTypeScript&&typescriptConfig?.enabled){const mode=typescriptConfig.mode;if(mode==="strict"){if(!typescriptConfig.types?.output){throw new this.SlothletError("TS_STRICT_REQUIRES_OUTPUT",{},null,{validationError:true})}if(!typescriptConfig.types?.interfaceName){throw new this.SlothletError("TS_STRICT_REQUIRES_INTERFACE_NAME",{},null,{validationError:true})}if(!this.slothlet._typesGenerated){const{fork}=await import("child_process");const path=await import("path");const{fileURLToPath}=await import("url");const __dirname=path.dirname(fileURLToPath(import.meta.url));const scriptPath=path.resolve(__dirname,"../../../tools/build/generate-types-worker.mjs");const childConfig=JSON.stringify({dir:this.slothlet.config.root||this.slothlet.config.dir,mode:"eager",typescript:{enabled:true,mode:"fast"},types:typescriptConfig.types});await new Promise((resolve2,reject)=>{const child=fork(scriptPath,[],{stdio:["pipe","pipe","pipe","ipc"],env:{...process.env,SLOTHLET_CONFIG:childConfig}});let errorOutput="";child.stderr?.on("data",data=>{errorOutput+=data.toString()});child.on("message",msg=>{if(msg.type==="success"){this.slothlet._typesGenerated=true;resolve2()}else if(msg.type==="error"){reject(new this.SlothletError("TS_TYPE_GENERATION_FAILED",{},{message:msg.error}))}});child.on("error",error=>{reject(new this.SlothletError("TS_TYPE_GENERATION_FORK_FAILED",{},error))});child.on("exit",code=>{if(code!==0&&!this.slothlet._typesGenerated){reject(new this.SlothletError("TS_TYPE_GENERATION_PROCESS_EXITED",{code,output:errorOutput},null,{validationError:true}))}})})}const{transformTypeScriptStrict,writeTransformedToCache,formatDiagnostics}=await import("@cldmv/slothlet/processors/typescript");const strictTransform=async tsPath=>{const result=await transformTypeScriptStrict(tsPath,{target:typescriptConfig.target,module:typescriptConfig.module,strict:typescriptConfig.strict,typeDefinitionPath:typescriptConfig.types.output,compilerOptions:typescriptConfig.compilerOptions});if(result.diagnostics&&result.diagnostics.length>0){const ts=await import("typescript");const errors=formatDiagnostics(result.diagnostics,ts.default);const error=new this.SlothletError("TS_TYPE_CHECK_ERRORS",{filePath:tsPath,errors:errors.join("\n")},null,{validationError:true});error.diagnostics=result.diagnostics;throw error}return result.code};const entryCode=await strictTransform(filePath);moduleUrl=await this.#buildTypescriptModuleUrl(writeTransformedToCache,filePath,entryCode,instanceID,moduleID,cacheBust,strictTransform)}else{const{transformTypeScript,writeTransformedToCache}=await import("@cldmv/slothlet/processors/typescript");const transformOptions={target:typescriptConfig.target,sourcemap:typescriptConfig.sourcemap};const transformedCode=await transformTypeScript(filePath,transformOptions);const transform=tsPath=>transformTypeScript(tsPath,transformOptions);moduleUrl=await this.#buildTypescriptModuleUrl(writeTransformedToCache,filePath,transformedCode,instanceID,moduleID,cacheBust,transform)}}else{const fileUrl=pathToFileURL(filePath).href;moduleUrl=`${fileUrl}?slothlet_instance=${instanceID}`;if(moduleID){moduleUrl+=`&module=${moduleID}`}if(cacheBust){moduleUrl+=`&_reload=${cacheBust}`}}const module=await import(moduleUrl);return module}catch(error){throw new this.SlothletError("MODULE_IMPORT_FAILED",{modulePath:filePath},error)}}#loadCJSIsolated(filePath){const requireFn=createRequire(filePath);const resolved=requireFn.resolve(filePath);delete requireFn.cache[resolved];const exports=requireFn(resolved);delete requireFn.cache[resolved];const namespace={default:exports};if(exports!==null&&typeof exports==="object"){for(const key of Object.keys(exports)){if(key!=="default"){namespace[key]=exports[key]}}}return namespace}async#buildTypescriptModuleUrl(writeTransformedToCache,filePath,code,instanceID,moduleID,cacheBust,transform){const{url,cacheDir}=await writeTransformedToCache(filePath,code,instanceID,transform);(this.slothlet._typescriptCacheDirs??=new Set).add(cacheDir);let moduleUrl=`${url}?slothlet_instance=${instanceID}`;if(moduleID){moduleUrl+=`&module=${moduleID}`}if(cacheBust){moduleUrl+=`&_reload=${cacheBust}`}return moduleUrl}async scanDirectory(dir,options={}){const typescriptConfig=this.slothlet.config?.typescript;const defaultExtensions=[".mjs",".cjs",".js"];const typescriptExtensions=typescriptConfig?.enabled?[".ts",".mts"]:[];const allExtensions=[...defaultExtensions,...typescriptExtensions];const{recursive=true,extensions=allExtensions,isRootScan=true,currentDepth=0,maxDepth=Infinity,fileFilter=null}=options;try{await stat(dir)}catch(error){throw new this.SlothletError("INVALID_DIRECTORY",{dir},error)}const structure={files:[],directories:[]};const entries=await readdir(dir,{withFileTypes:true});for(const entry of entries){const fullPath=join(dir,entry.name);if(entry.isDirectory()){if(fileFilter){continue}if(recursive&¤tDepth<maxDepth){const subStructure=await this.scanDirectory(fullPath,{...options,isRootScan:false,currentDepth:currentDepth+1});structure.directories.push({path:fullPath,name:entry.name,children:subStructure})}}else if(entry.isFile()){const ext=extname(entry.name);if(extensions.includes(ext)){if(entry.name.startsWith("__")){continue}if(fileFilter&&!fileFilter(entry.name)){continue}const nameWithoutExt=basename(entry.name,ext);structure.files.push({path:fullPath,name:nameWithoutExt,fullName:entry.name,moduleID:this.slothlet.helpers.sanitize.getModuleId(fullPath,dir)})}}}if(isRootScan&&structure.files.length===0&&structure.directories.length===0){new this.SlothletWarning("WARN_DIRECTORY_EMPTY",{dir,resolvedPath:resolve(dir)})}return structure}extractExports(module){const exports={};if(module.default!==void 0){exports.default=module.default}for(const key of Object.keys(module)){if(key!=="default"&&key!=="module.exports"&&typeof key==="string"){exports[key]=module[key]}}if(exports.default&&typeof exports.default==="object"&&exports.default!==null&&"default"in exports.default){const rootNamedKeys=Object.keys(exports).filter(k=>k!=="default"&&k!=="module.exports");const defaultKeys=Object.keys(exports.default).filter(k=>k!=="default");const isCJSPattern=rootNamedKeys.every(k=>k in exports.default);if(isCJSPattern&&defaultKeys.length>0){exports.default=exports.default.default}}return exports}}export{Loader};
|
|
17
|
+
import{ComponentBase}from"@cldmv/slothlet/factories/component-base";const IS_NODE=typeof process!=="undefined"&&Boolean(process.versions?.node);const{readdir,stat,join,extname,basename,resolve,pathToFileURL,createRequire}=IS_NODE?await(async()=>{const[fsMod,pathMod,urlMod,moduleMod]=await Promise.all([import("node:fs/promises"),import("node:path"),import("node:url"),import("node:module")]);return{readdir:fsMod.readdir,stat:fsMod.stat,join:pathMod.join,extname:pathMod.extname,basename:pathMod.basename,resolve:pathMod.resolve,pathToFileURL:urlMod.pathToFileURL,createRequire:moduleMod.createRequire}})():{readdir:null,stat:null,join:null,extname:null,basename:null,resolve:null,pathToFileURL:null,createRequire:null};class Loader extends ComponentBase{static slothletProperty="loader";constructor(slothlet){super(slothlet)}async loadModule(filePath,instanceID,moduleID,cacheBust=null){try{if(this.slothlet.envTarget==="browser"){return this.#loadModuleBrowser(filePath)}if(filePath.endsWith(".cjs")){return this.#loadCJSIsolated(filePath)}const isTypeScript=filePath.endsWith(".ts")||filePath.endsWith(".mts");const typescriptConfig=this.slothlet.config?.typescript;let moduleUrl;if(isTypeScript&&typescriptConfig?.enabled){const mode=typescriptConfig.mode;if(mode==="strict"){if(!typescriptConfig.types?.output){throw new this.SlothletError("TS_STRICT_REQUIRES_OUTPUT",{},null,{validationError:true})}if(!typescriptConfig.types?.interfaceName){throw new this.SlothletError("TS_STRICT_REQUIRES_INTERFACE_NAME",{},null,{validationError:true})}if(!this.slothlet._typesGenerated){const{fork}=await import("child_process");const path=await import("path");const{fileURLToPath}=await import("url");const __dirname=path.dirname(fileURLToPath(import.meta.url));const scriptPath=path.resolve(__dirname,"../../../tools/build/generate-types-worker.mjs");const childConfig=JSON.stringify({dir:this.slothlet.config.root||this.slothlet.config.dir,mode:"eager",typescript:{enabled:true,mode:"fast"},types:typescriptConfig.types});await new Promise((resolve2,reject)=>{const child=fork(scriptPath,[],{stdio:["pipe","pipe","pipe","ipc"],env:{...process.env,SLOTHLET_CONFIG:childConfig}});let errorOutput="";child.stderr?.on("data",data=>{errorOutput+=data.toString()});child.on("message",msg=>{if(msg.type==="success"){this.slothlet._typesGenerated=true;resolve2()}else if(msg.type==="error"){reject(new this.SlothletError("TS_TYPE_GENERATION_FAILED",{},{message:msg.error}))}});child.on("error",error=>{reject(new this.SlothletError("TS_TYPE_GENERATION_FORK_FAILED",{},error))});child.on("exit",code=>{if(code!==0&&!this.slothlet._typesGenerated){reject(new this.SlothletError("TS_TYPE_GENERATION_PROCESS_EXITED",{code,output:errorOutput},null,{validationError:true}))}})})}const{transformTypeScriptStrict,writeTransformedToCache,formatDiagnostics}=await import("@cldmv/slothlet/processors/typescript");const strictTransform=async tsPath=>{const result=await transformTypeScriptStrict(tsPath,{target:typescriptConfig.target,module:typescriptConfig.module,strict:typescriptConfig.strict,typeDefinitionPath:typescriptConfig.types.output,compilerOptions:typescriptConfig.compilerOptions});if(result.diagnostics&&result.diagnostics.length>0){const ts=await import("typescript");const errors=formatDiagnostics(result.diagnostics,ts.default);const error=new this.SlothletError("TS_TYPE_CHECK_ERRORS",{filePath:tsPath,errors:errors.join("\n")},null,{validationError:true});error.diagnostics=result.diagnostics;throw error}return result.code};const entryCode=await strictTransform(filePath);moduleUrl=await this.#buildTypescriptModuleUrl(writeTransformedToCache,filePath,entryCode,instanceID,moduleID,cacheBust,strictTransform)}else{const{transformTypeScript,writeTransformedToCache}=await import("@cldmv/slothlet/processors/typescript");const transformOptions={target:typescriptConfig.target,sourcemap:typescriptConfig.sourcemap};const transformedCode=await transformTypeScript(filePath,transformOptions);const transform=tsPath=>transformTypeScript(tsPath,transformOptions);moduleUrl=await this.#buildTypescriptModuleUrl(writeTransformedToCache,filePath,transformedCode,instanceID,moduleID,cacheBust,transform)}}else{const fileUrl=pathToFileURL(filePath).href;moduleUrl=`${fileUrl}?slothlet_instance=${instanceID}`;if(moduleID){moduleUrl+=`&module=${moduleID}`}if(cacheBust){moduleUrl+=`&_reload=${cacheBust}`}}const module=await import(moduleUrl);return module}catch(error){throw new this.SlothletError("MODULE_IMPORT_FAILED",{modulePath:filePath},error)}}#loadCJSIsolated(filePath){const requireFn=createRequire(filePath);const resolved=requireFn.resolve(filePath);delete requireFn.cache[resolved];const exports=requireFn(resolved);delete requireFn.cache[resolved];const namespace={default:exports};if(exports!==null&&typeof exports==="object"){for(const key of Object.keys(exports)){if(key!=="default"){namespace[key]=exports[key]}}}return namespace}async#buildTypescriptModuleUrl(writeTransformedToCache,filePath,code,instanceID,moduleID,cacheBust,transform){const{url,cacheDir}=await writeTransformedToCache(filePath,code,instanceID,transform);(this.slothlet._typescriptCacheDirs??=new Set).add(cacheDir);let moduleUrl=`${url}?slothlet_instance=${instanceID}`;if(moduleID){moduleUrl+=`&module=${moduleID}`}if(cacheBust){moduleUrl+=`&_reload=${cacheBust}`}return moduleUrl}async scanDirectory(dir,options={}){if(this.slothlet.envTarget==="browser"){return this.#scanDirectoryBrowser(dir,options)}const typescriptConfig=this.slothlet.config?.typescript;const defaultExtensions=[".mjs",".cjs",".js"];const typescriptExtensions=typescriptConfig?.enabled?[".ts",".mts"]:[];const allExtensions=[...defaultExtensions,...typescriptExtensions];const{recursive=true,extensions=allExtensions,isRootScan=true,currentDepth=0,maxDepth=Infinity,fileFilter=null}=options;try{await stat(dir)}catch(error){throw new this.SlothletError("INVALID_DIRECTORY",{dir},error)}const structure={files:[],directories:[]};const entries=await readdir(dir,{withFileTypes:true});for(const entry of entries){const fullPath=join(dir,entry.name);if(entry.isDirectory()){if(fileFilter){continue}if(recursive&¤tDepth<maxDepth){const subStructure=await this.scanDirectory(fullPath,{...options,isRootScan:false,currentDepth:currentDepth+1});structure.directories.push({path:fullPath,name:entry.name,children:subStructure})}}else if(entry.isFile()){const ext=extname(entry.name);if(extensions.includes(ext)){if(entry.name.startsWith("__")){continue}if(fileFilter&&!fileFilter(entry.name)){continue}const nameWithoutExt=basename(entry.name,ext);structure.files.push({path:fullPath,name:nameWithoutExt,fullName:entry.name,moduleID:this.slothlet.helpers.sanitize.getModuleId(fullPath,dir)})}}}if(isRootScan&&structure.files.length===0&&structure.directories.length===0){new this.SlothletWarning("WARN_DIRECTORY_EMPTY",{dir,resolvedPath:resolve(dir)})}return structure}#scanDirectoryBrowser(dir,options={}){const manifest=this.slothlet.config?.manifest;if(!manifest){throw new this.SlothletError("INVALID_CONFIG_BROWSER_REQUIRES_MANIFEST",{},null,{validationError:true})}const configBase=(this.slothlet.config?.dir||"").replace(/\/$/,"");let relativePath=(dir||"").replace(/\/$/,"");if(configBase&&relativePath.startsWith(configBase)){relativePath=relativePath.slice(configBase.length).replace(/^\/|\/$/g,"")}const isRoot=!relativePath||relativePath==="/"||relativePath===".";const node=isRoot?manifest:this.#findManifestNode(manifest,relativePath);if(!node){throw new this.SlothletError("INVALID_DIRECTORY",{dir},null)}return this.#manifestNodeToStructure(node,dir,options)}#findManifestNode(node,targetPath){const normalised=targetPath.replace(/\\/g,"/").replace(/^\/|\/$/g,"");for(const dir of node.directories||[]){const dirPath=(dir.path||dir.name||"").replace(/\\/g,"/").replace(/^\/|\/$/g,"");if(dirPath===normalised)return dir.children||dir;const found=this.#findManifestNode(dir.children||dir,normalised);if(found)return found}return null}#manifestNodeToStructure(node,rootPath,options={}){const{fileFilter=null}=options;const ALLOWED_EXTS=[".mjs",".cjs",".js"];const structure={files:[],directories:[]};for(const file of node.files||[]){const filePath=file.path||file.relativePath||"";const fullName=file.fullName||filePath.split("/").pop();const lastDot=fullName.lastIndexOf(".");const ext=lastDot>=0?fullName.slice(lastDot):"";if(!ALLOWED_EXTS.includes(ext))continue;if(fullName.startsWith("__"))continue;if(fileFilter&&!fileFilter(fullName))continue;const name=file.name||(lastDot>=0?fullName.slice(0,lastDot):fullName);structure.files.push({path:filePath,name,fullName,moduleID:this.slothlet.helpers.sanitize.getModuleId(filePath,rootPath)})}if(!fileFilter){for(const dir of node.directories||[]){const dirPath=dir.path||dir.name||"";structure.directories.push({path:dirPath,name:dir.name||dirPath.split("/").pop(),children:this.#manifestNodeToStructure(dir.children||dir,dirPath,options)})}}return structure}async#loadModuleBrowser(filePath){const resolveModuleSpecifier=this.slothlet.config?.resolveModuleSpecifier??(({path:p})=>{const base=this.slothlet.config?.base??this.slothlet.config?.dir??"";const leadingSlash=base.startsWith("/")?"":"/";const baseUrl=/^[a-zA-Z][\w+\-.]*:\/\//.test(base)?base.endsWith("/")?base:base+"/":"file://"+leadingSlash+base.replace(/\/?$/,"/");return new URL(p,baseUrl).href});const fullName=filePath.split("/").pop();const lastDot=fullName.lastIndexOf(".");const name=lastDot>=0?fullName.slice(0,lastDot):fullName;const specifier=resolveModuleSpecifier({path:filePath,name,fullName});const module=await import(specifier);return module}extractExports(module){const exports={};if(module.default!==void 0){exports.default=module.default}for(const key of Object.keys(module)){if(key!=="default"&&key!=="module.exports"&&typeof key==="string"){exports[key]=module[key]}}if(exports.default&&typeof exports.default==="object"&&exports.default!==null&&"default"in exports.default){const rootNamedKeys=Object.keys(exports).filter(k=>k!=="default"&&k!=="module.exports");const defaultKeys=Object.keys(exports.default).filter(k=>k!=="default");const isCJSPattern=rootNamedKeys.every(k=>k in exports.default);if(isCJSPattern&&defaultKeys.length>0){exports.default=exports.default.default}}return exports}}export{Loader};
|
|
@@ -14,4 +14,4 @@
|
|
|
14
14
|
limitations under the License.
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
|
-
import fs from"fs";import path from"path";import{SlothletError}from"@cldmv/slothlet/errors";let typescriptInstance=null;async function getTypeScript(){if(!typescriptInstance){try{typescriptInstance=await import("typescript")}catch(error){throw new SlothletError("TYPESCRIPT_NOT_INSTALLED",{feature:"type-generation"},error)}}return typescriptInstance}async function generateTypes(api,options){const ts=await getTypeScript();if(!options.output){throw new SlothletError("INVALID_CONFIG",{option:"types.output",expected:"a string output path",value:options.output,hint:"Provide a string output path for the generated .d.ts file, e.g. './types/api.d.ts'.",validationError:true})}if(!options.interfaceName){throw new SlothletError("INVALID_CONFIG",{option:"types.interfaceName",expected:"a string interface name",value:options.interfaceName,hint:"Provide a string interface name for the generated TypeScript interface, e.g. 'SlothletAPI'.",validationError:true})}const nodes=traverseAPI(api);for(const node of nodes){if(node.metadata?.filePath){node.typeInfo=await extractTypesFromFile(node.metadata.filePath,ts)}}const declaration=generateDeclaration(nodes,options);const outputPath=path.resolve(options.output);const outputDir=path.dirname(outputPath);
|
|
17
|
+
import fs from"fs";import path from"path";import{SlothletError}from"@cldmv/slothlet/errors";let typescriptInstance=null;async function getTypeScript(){if(!typescriptInstance){try{typescriptInstance=await import("typescript")}catch(error){throw new SlothletError("TYPESCRIPT_NOT_INSTALLED",{feature:"type-generation"},error)}}return typescriptInstance}async function generateTypes(api,options){const ts=await getTypeScript();if(!options.output){throw new SlothletError("INVALID_CONFIG",{option:"types.output",expected:"a string output path",value:options.output,hint:"Provide a string output path for the generated .d.ts file, e.g. './types/api.d.ts'.",validationError:true})}if(!options.interfaceName){throw new SlothletError("INVALID_CONFIG",{option:"types.interfaceName",expected:"a string interface name",value:options.interfaceName,hint:"Provide a string interface name for the generated TypeScript interface, e.g. 'SlothletAPI'.",validationError:true})}const nodes=traverseAPI(api);for(const node of nodes){if(node.metadata?.filePath){node.typeInfo=await extractTypesFromFile(node.metadata.filePath,ts)}}const declaration=generateDeclaration(nodes,options);const outputPath=path.resolve(options.output);const outputDir=path.dirname(outputPath);fs.mkdirSync(outputDir,{recursive:true});fs.writeFileSync(outputPath,declaration,"utf8");return{output:declaration,filePath:outputPath}}function traverseAPI(api,currentPath=[],visited=new Set){const nodes=[];if(!api||typeof api!=="object"){return nodes}if(visited.has(api)){return nodes}visited.add(api);for(const[key,value]of Object.entries(api)){if(key.startsWith("_")||key.startsWith("__")||key==="slothlet"||key==="shutdown"||key==="destroy"){continue}const nodePath=[...currentPath,key];const metadata=value?.__metadata;if(typeof value==="function"){nodes.push({type:"function",path:nodePath,value,metadata})}else if(typeof value==="object"&&value!==null){nodes.push({type:"object",path:nodePath,value,metadata});const childNodes=traverseAPI(value,nodePath,visited);nodes.push(...childNodes)}}return nodes}async function extractTypesFromFile(filePath,ts){try{let visit=function(node){if(ts.isFunctionDeclaration(node)&&node.name){const hasExport=node.modifiers?.some(m=>m.kind===ts.SyntaxKind.ExportKeyword);if(hasExport){exports.push({name:node.name.text,type:"function",signature:extractFunctionSignature(node,source,ts)})}}if(ts.isVariableStatement(node)){const hasExport=node.modifiers?.some(m=>m.kind===ts.SyntaxKind.ExportKeyword);if(hasExport){for(const decl of node.declarationList.declarations){if(!decl.name||!ts.isIdentifier(decl.name))continue;const init=decl.initializer;if(!init)continue;if(ts.isArrowFunction(init)||ts.isFunctionExpression(init)){exports.push({name:decl.name.text,type:"function",signature:extractFunctionSignature(init,source,ts)})}}}}ts.forEachChild(node,visit)};const source=fs.readFileSync(filePath,"utf8");const sourceFile=ts.createSourceFile(filePath,source,ts.ScriptTarget.Latest,true);const exports=[];visit(sourceFile);return{exports,sourceFile}}catch(____error){return{exports:[]}}}function extractFunctionSignature(node,____source,____ts){const params=node.parameters.map(p=>{const name=p.name.getText(node.getSourceFile());const type=p.type?p.type.getText(node.getSourceFile()):"any";return`${name}: ${type}`}).join(", ");const returnType=node.type?node.type.getText(node.getSourceFile()):"any";return`(${params}): ${returnType}`}function generateDeclaration(nodes,options){const interfaceName=options.interfaceName;const lines=[];lines.push("/**");lines.push(` * Generated TypeScript declarations for Slothlet API`);lines.push(` * @generated ${new Date().toISOString()}`);lines.push(" */");lines.push("");const structure={};for(const node of nodes){if(node.type==="function"){const fnName=node.path[node.path.length-1];const exportInfo=node.typeInfo?.exports?.find(e=>e.name===fnName);const signature=exportInfo?.signature??"(...args: any[]): any";setNestedProperty(structure,node.path,{type:"function",signature})}}lines.push(`export interface ${interfaceName} {`);generateInterfaceContent(structure,lines,1);lines.push("}");lines.push("");lines.push(`declare const self: ${interfaceName};`);lines.push("");return lines.join("\n")}function setNestedProperty(obj,path2,value){let current=obj;for(let i=0;i<path2.length-1;i++){const key=path2[i];if(!current[key]){current[key]={}}current=current[key]}const lastKey=path2[path2.length-1];current[lastKey]=value}function generateInterfaceContent(structure,lines,indent){const indentation=" ".repeat(indent);for(const[key,value]of Object.entries(structure)){if(value.type==="function"){lines.push(`${indentation}${key}${value.signature};`)}else if(typeof value==="object"&&value!==null){lines.push(`${indentation}${key}: {`);generateInterfaceContent(value,lines,indent+1);lines.push(`${indentation}};`)}else{}}}export{generateTypes};
|
|
@@ -14,4 +14,4 @@
|
|
|
14
14
|
limitations under the License.
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
|
-
import fs from"fs";import path from"path";import{writeFile,mkdir,readdir,rm}from"node:fs/promises";import{createHash}from"node:crypto";import{tmpdir}from"node:os";import{pathToFileURL}from"node:url";import{SlothletError}from"@cldmv/slothlet/errors";let esbuildInstance=null;let typescriptInstance=null;async function getEsbuild(){if(!esbuildInstance){try{esbuildInstance=await import("esbuild")}catch(error){throw new SlothletError("TYPESCRIPT_ESBUILD_NOT_INSTALLED",{mode:"fast"},error)}}return esbuildInstance}async function getTypeScript(){if(!typescriptInstance){try{typescriptInstance=await import("typescript")}catch(error){throw new SlothletError("TYPESCRIPT_TSC_NOT_INSTALLED",{mode:"strict"},error)}}return typescriptInstance}async function transformTypeScript(filePath,options={}){const esbuild=await getEsbuild();const code=fs.readFileSync(filePath,"utf8");const result=await esbuild.transform(code,{loader:"ts",format:options.format||"esm",target:options.target||"es2020",sourcemap:options.sourcemap||false,...options});return result.code}function createDataUrl(code){const encoded=encodeURIComponent(code);const timestamp=Date.now();return`data:text/javascript;charset=utf-8,${encoded}#t=${timestamp}`}function findPackageRoot(startPath){let dir;try{const resolved=path.resolve(startPath);dir=fs.statSync(resolved).isFile()?path.dirname(resolved):resolved}catch{return null}while(true){if(fs.existsSync(path.join(dir,"package.json")))return dir;const parent=path.dirname(dir);if(parent===dir)return null;dir=parent}}function isProcessAlive(pid){try{process.kill(pid,0);return true}catch(err){return err.code!=="ESRCH"}}const sweepPromises=new Map;function sweepStaleSlothletCache(projectRoot){if(sweepPromises.has(projectRoot))return sweepPromises.get(projectRoot);const promise=(async()=>{const cacheRoot=path.join(projectRoot,".slothlet-cache");let entries;try{entries=await readdir(cacheRoot,{withFileTypes:true})}catch{return}const currentPid=process.pid;await Promise.allSettled(entries.map(async entry=>{if(!entry.isDirectory())return;const match=entry.name.match(/^(\d+)-/);if(!match)return;const pid=Number(match[1]);if(pid===currentPid||isProcessAlive(pid))return;await rm(path.join(cacheRoot,entry.name),{recursive:true,force:true})}))})();sweepPromises.set(projectRoot,promise);return promise}const TS_SOURCE_EXT=/\.m?ts$/;function resolveModuleFile(absoluteTarget){const isFile=candidate=>{try{return fs.statSync(candidate).isFile()}catch{return false}};if(isFile(absoluteTarget)){return{path:absoluteTarget,isTS:TS_SOURCE_EXT.test(absoluteTarget)}}const ext=path.extname(absoluteTarget);const base=ext?absoluteTarget.slice(0,-ext.length):absoluteTarget;const candidates=ext===".mjs"?[".mts"]:ext===".js"?[".ts"]:ext===""?[".mts",".ts"]:[];for(const candidate of candidates){if(isFile(base+candidate)){return{path:base+candidate,isTS:true}}}return{path:absoluteTarget,isTS:false}}const REGEX_PRECEDING_KEYWORDS=new Set(["return","typeof","instanceof","in","of","new","delete","void","throw","else","yield","await","case","do"]);function isIdentifierChar(ch){const cc=ch.charCodeAt(0);return cc>=97&&cc<=122||cc>=65&&cc<=90||cc>=48&&cc<=57||ch==="_"||ch==="$"||cc>127}function maskStringsAndComments(code){const n=code.length;const mask=new Uint8Array(n);let regexAllowed=true;let i=0;while(i<n){const c=code[i];const c2=code[i+1];if(c==="/"&&c2==="/"){while(i<n&&code[i]!=="\n")mask[i++]=1}else if(c==="/"&&c2==="*"){mask[i++]=1;mask[i++]=1;while(i<n&&!(code[i]==="*"&&code[i+1]==="/"))mask[i++]=1;if(i<n){mask[i++]=1;mask[i++]=1}}else if(c==="/"&®exAllowed){mask[i++]=1;let inClass=false;while(i<n&&code[i]!=="\n"){const ch=code[i];if(ch==="\\"&&i+1<n){mask[i++]=1;mask[i++]=1;continue}if(ch==="[")inClass=true;else if(ch==="]")inClass=false;mask[i++]=1;if(ch==="/"&&!inClass)break}regexAllowed=false}else if(c==='"'||c==="'"||c==="`"){mask[i++]=1;while(i<n&&code[i]!==c){if(code[i]==="\\"&&i+1<n)mask[i++]=1;mask[i++]=1}if(i<n)mask[i++]=1;regexAllowed=false}else if(c===" "||c===" "||c==="\n"||c==="\r"||c==="\f"||c==="\v"){i++}else if(isIdentifierChar(c)&&!(c>="0"&&c<="9")){const start=i;while(i<n&&isIdentifierChar(code[i]))i++;regexAllowed=REGEX_PRECEDING_KEYWORDS.has(code.slice(start,i))}else{regexAllowed=c!==")"&&c!=="]"&&!(c>="0"&&c<="9");i++}}return mask}const TOKEN_GAP="(?:\\s|/\\*[\\s\\S]*?\\*/|//[^\\n]*)*";const STATIC_FROM_RE=new RegExp(`^([ \\t]*(?:import|export)\\b[^"'\`;]*?\\bfrom${TOKEN_GAP})(["'])(\\.\\.?/[^"']*)\\2`,"gm");const BARE_IMPORT_RE=new RegExp(`^([ \\t]*import${TOKEN_GAP})(["'])(\\.\\.?/[^"']*)\\2`,"gm");const DYNAMIC_IMPORT_RE=new RegExp(`(?<![.\\w$])import(${TOKEN_GAP}\\(${TOKEN_GAP})(["'])(\\.\\.?/[^"']*)\\2(${TOKEN_GAP}\\))`,"g");function rewriteRelativeSpecifiers(code,sourcePath,resolve){const sourceDir=path.dirname(sourcePath);const resolveSpecifier=resolve??((absoluteTarget,suffix)=>pathToFileURL(absoluteTarget).href+suffix);const handle=specifier=>{const suffixIdx=specifier.search(/[?#]/);const pathPart=suffixIdx===-1?specifier:specifier.slice(0,suffixIdx);const suffix=suffixIdx===-1?"":specifier.slice(suffixIdx);return resolveSpecifier(path.resolve(sourceDir,pathPart),suffix,specifier)};const rewriteWith=(text,regex,replacer)=>{const mask=maskStringsAndComments(text);return text.replace(regex,(...args)=>{const offset=args[args.length-2];return mask[offset]?args[0]:replacer(...args)})};let out=code;out=rewriteWith(out,STATIC_FROM_RE,(_m,pre,q,spec)=>`${pre}${q}${handle(spec)}${q}`);out=rewriteWith(out,BARE_IMPORT_RE,(_m,pre,q,spec)=>`${pre}${q}${handle(spec)}${q}`);out=rewriteWith(out,DYNAMIC_IMPORT_RE,(_m,pre,q,spec,post)=>`import${pre}${q}${handle(spec)}${q}${post}`);return out}async function writeTransformedToCache(originalPath,code,instanceID,transform){const absolutePath=path.resolve(originalPath);const projectRoot=findPackageRoot(absolutePath)??tmpdir();await sweepStaleSlothletCache(projectRoot);const cacheDir=path.join(projectRoot,".slothlet-cache",`${process.pid}-${instanceID}`);const transformed=new Map([[absolutePath,code]]);const deps=new Map;if(transform){const pending=[absolutePath];while(pending.length>0){const file=pending.pop();const fileDeps=new Set;deps.set(file,fileDeps);rewriteRelativeSpecifiers(transformed.get(file),file,(absoluteTarget,_suffix,specifier)=>{const resolved=resolveModuleFile(absoluteTarget);if(resolved.isTS)fileDeps.add(resolved.path);return specifier});for(const dep of fileDeps){if(!transformed.has(dep)){transformed.set(dep,await transform(dep));pending.push(dep)}}}}const closureCache=new Map;const closureOf=file=>{if(closureCache.has(file))return closureCache.get(file);const seen=new Set;const walk=node=>{if(seen.has(node))return;seen.add(node);for(const next of deps.get(node)??[])walk(next)};walk(file);closureCache.set(file,seen);return seen};const cacheName=file=>{const hash=createHash("sha256");for(const member of[...closureOf(file)].sort()){hash.update(member).update("\0").update(transformed.get(member)).update("\0")}hash.update("entry\0").update(file);return`${hash.digest("hex").slice(0,16)}.mjs`};await mkdir(cacheDir,{recursive:true});for(const[file,fileCode]of transformed){const rewritten=rewriteRelativeSpecifiers(fileCode,file,(absoluteTarget,suffix)=>{const resolved=resolveModuleFile(absoluteTarget);if(resolved.isTS&&transformed.has(resolved.path)){return pathToFileURL(path.join(cacheDir,cacheName(resolved.path))).href+suffix}return pathToFileURL(resolved.path).href+suffix});const cachePath=path.join(cacheDir,cacheName(file));if(!fs.existsSync(cachePath)){await writeFile(cachePath,rewritten,"utf8")}}return{url:pathToFileURL(path.join(cacheDir,cacheName(absolutePath))).href,cacheDir}}async function transformTypeScriptStrict(filePath,options={}){const ts=await getTypeScript();const code=fs.readFileSync(filePath,"utf8");const targetMap={es3:ts.ScriptTarget.ES3,es5:ts.ScriptTarget.ES5,es6:ts.ScriptTarget.ES2015,es2015:ts.ScriptTarget.ES2015,es2016:ts.ScriptTarget.ES2016,es2017:ts.ScriptTarget.ES2017,es2018:ts.ScriptTarget.ES2018,es2019:ts.ScriptTarget.ES2019,es2020:ts.ScriptTarget.ES2020,es2021:ts.ScriptTarget.ES2021,es2022:ts.ScriptTarget.ES2022,esnext:ts.ScriptTarget.ESNext,latest:ts.ScriptTarget.Latest};const moduleMap={none:ts.ModuleKind.None,commonjs:ts.ModuleKind.CommonJS,amd:ts.ModuleKind.AMD,system:ts.ModuleKind.System,umd:ts.ModuleKind.UMD,es6:ts.ModuleKind.ES2015,es2015:ts.ModuleKind.ES2015,es2020:ts.ModuleKind.ES2020,es2022:ts.ModuleKind.ES2022,esnext:ts.ModuleKind.ESNext,node16:ts.ModuleKind.Node16,nodenext:ts.ModuleKind.NodeNext};const targetKey=(options.target||"es2020").toLowerCase();const moduleKey=(options.module||"esnext").toLowerCase();const compilerOptions={target:targetMap[targetKey]||ts.ScriptTarget.ES2020,module:moduleMap[moduleKey]||ts.ModuleKind.ESNext,strict:options.strict!==false,esModuleInterop:true,skipLibCheck:true,noEmit:false,...options.typeDefinitionPath&&{typeRoots:[path.dirname(options.typeDefinitionPath)],types:[path.basename(options.typeDefinitionPath,".d.ts")]},...options.compilerOptions};let diagnostics=[];if(!options.skipTypeCheck){const host=ts.createCompilerHost(compilerOptions);const originalReadFile=host.readFile;host.readFile=fileName=>{if(fileName===filePath){return code}return originalReadFile.call(host,fileName)};const program=ts.createProgram([filePath],compilerOptions,host);const allDiagnostics=[...program.getSemanticDiagnostics(),...program.getSyntacticDiagnostics()];diagnostics=allDiagnostics.filter(d=>d.file&&d.file.fileName===filePath)}const result=ts.transpileModule(code,{compilerOptions,fileName:filePath});return{code:result.outputText,diagnostics}}function formatDiagnostics(diagnostics,ts){return diagnostics.map(diagnostic=>{const message=ts.flattenDiagnosticMessageText(diagnostic.messageText,"\n");if(diagnostic.file&&diagnostic.start!==void 0){const{line,character}=diagnostic.file.getLineAndCharacterOfPosition(diagnostic.start);const fileName=diagnostic.file.fileName;return`${fileName}:${line+1}:${character+1} - ${message}`}return message})}export{createDataUrl,formatDiagnostics,maskStringsAndComments,resolveModuleFile,rewriteRelativeSpecifiers,transformTypeScript,transformTypeScriptStrict,writeTransformedToCache};
|
|
17
|
+
import fs from"fs";import path from"path";import{writeFile,mkdir,readdir,rm}from"node:fs/promises";import{createHash}from"node:crypto";import{tmpdir}from"node:os";import{pathToFileURL}from"node:url";import{SlothletError}from"@cldmv/slothlet/errors";let esbuildInstance=null;let typescriptInstance=null;async function getEsbuild(){if(!esbuildInstance){try{esbuildInstance=await import("esbuild")}catch(error){throw new SlothletError("TYPESCRIPT_ESBUILD_NOT_INSTALLED",{mode:"fast"},error)}}return esbuildInstance}async function getTypeScript(){if(!typescriptInstance){try{typescriptInstance=await import("typescript")}catch(error){throw new SlothletError("TYPESCRIPT_TSC_NOT_INSTALLED",{mode:"strict"},error)}}return typescriptInstance}async function transformTypeScript(filePath,options={}){const esbuild=await getEsbuild();const code=fs.readFileSync(filePath,"utf8");const result=await esbuild.transform(code,{loader:"ts",format:options.format||"esm",target:options.target||"es2020",sourcemap:options.sourcemap||false,...options});return result.code}function createDataUrl(code){const encoded=encodeURIComponent(code);const timestamp=Date.now();return`data:text/javascript;charset=utf-8,${encoded}#t=${timestamp}`}function findPackageRoot(startPath){let dir;try{const resolved=path.resolve(startPath);dir=fs.statSync(resolved).isFile()?path.dirname(resolved):resolved}catch{return null}while(true){if(fs.existsSync(path.join(dir,"package.json")))return dir;const parent=path.dirname(dir);if(parent===dir)return null;dir=parent}}function isProcessAlive(pid){try{process.kill(pid,0);return true}catch(err){return err.code!=="ESRCH"}}const sweepPromises=new Map;let secureFallbackRoot;function getSecureFallbackRoot(){return secureFallbackRoot??=fs.mkdtempSync(path.join(tmpdir(),"slothlet-"))}function sweepStaleSlothletCache(projectRoot){if(sweepPromises.has(projectRoot))return sweepPromises.get(projectRoot);const promise=(async()=>{const cacheRoot=path.join(projectRoot,".slothlet-cache");let entries;try{entries=await readdir(cacheRoot,{withFileTypes:true})}catch{return}const currentPid=process.pid;await Promise.allSettled(entries.map(async entry=>{if(!entry.isDirectory())return;const match=entry.name.match(/^(\d+)-/);if(!match)return;const pid=Number(match[1]);if(pid===currentPid||isProcessAlive(pid))return;await rm(path.join(cacheRoot,entry.name),{recursive:true,force:true})}))})();sweepPromises.set(projectRoot,promise);return promise}const TS_SOURCE_EXT=/\.m?ts$/;function resolveModuleFile(absoluteTarget){const isFile=candidate=>{try{return fs.statSync(candidate).isFile()}catch{return false}};if(isFile(absoluteTarget)){return{path:absoluteTarget,isTS:TS_SOURCE_EXT.test(absoluteTarget)}}const ext=path.extname(absoluteTarget);const base=ext?absoluteTarget.slice(0,-ext.length):absoluteTarget;const candidates=ext===".mjs"?[".mts"]:ext===".js"?[".ts"]:ext===""?[".mts",".ts"]:[];for(const candidate of candidates){if(isFile(base+candidate)){return{path:base+candidate,isTS:true}}}return{path:absoluteTarget,isTS:false}}const REGEX_PRECEDING_KEYWORDS=new Set(["return","typeof","instanceof","in","of","new","delete","void","throw","else","yield","await","case","do"]);function isIdentifierChar(ch){const cc=ch.charCodeAt(0);return cc>=97&&cc<=122||cc>=65&&cc<=90||cc>=48&&cc<=57||ch==="_"||ch==="$"||cc>127}function maskStringsAndComments(code){const n=code.length;const mask=new Uint8Array(n);let regexAllowed=true;let i=0;while(i<n){const c=code[i];const c2=code[i+1];if(c==="/"&&c2==="/"){while(i<n&&code[i]!=="\n")mask[i++]=1}else if(c==="/"&&c2==="*"){mask[i++]=1;mask[i++]=1;while(i<n&&!(code[i]==="*"&&code[i+1]==="/"))mask[i++]=1;if(i<n){mask[i++]=1;mask[i++]=1}}else if(c==="/"&®exAllowed){mask[i++]=1;let inClass=false;while(i<n&&code[i]!=="\n"){const ch=code[i];if(ch==="\\"&&i+1<n){mask[i++]=1;mask[i++]=1;continue}if(ch==="[")inClass=true;else if(ch==="]")inClass=false;mask[i++]=1;if(ch==="/"&&!inClass)break}regexAllowed=false}else if(c==='"'||c==="'"||c==="`"){mask[i++]=1;while(i<n&&code[i]!==c){if(code[i]==="\\"&&i+1<n)mask[i++]=1;mask[i++]=1}if(i<n)mask[i++]=1;regexAllowed=false}else if(c===" "||c===" "||c==="\n"||c==="\r"||c==="\f"||c==="\v"){i++}else if(isIdentifierChar(c)&&!(c>="0"&&c<="9")){const start=i;while(i<n&&isIdentifierChar(code[i]))i++;regexAllowed=REGEX_PRECEDING_KEYWORDS.has(code.slice(start,i))}else{regexAllowed=c!==")"&&c!=="]"&&!(c>="0"&&c<="9");i++}}return mask}const TOKEN_GAP="(?:\\s|/\\*[\\s\\S]*?\\*/|//[^\\n]*)*";const STATIC_FROM_RE=new RegExp(`^([ \\t]*(?:import|export)\\b[^"'\`;]*?\\bfrom${TOKEN_GAP})(["'])(\\.\\.?/[^"']*)\\2`,"gm");const BARE_IMPORT_RE=new RegExp(`^([ \\t]*import${TOKEN_GAP})(["'])(\\.\\.?/[^"']*)\\2`,"gm");const DYNAMIC_IMPORT_RE=new RegExp(`(?<![.\\w$])import(${TOKEN_GAP}\\(${TOKEN_GAP})(["'])(\\.\\.?/[^"']*)\\2(${TOKEN_GAP}\\))`,"g");function rewriteRelativeSpecifiers(code,sourcePath,resolve){const sourceDir=path.dirname(sourcePath);const resolveSpecifier=resolve??((absoluteTarget,suffix)=>pathToFileURL(absoluteTarget).href+suffix);const handle=specifier=>{const suffixIdx=specifier.search(/[?#]/);const pathPart=suffixIdx===-1?specifier:specifier.slice(0,suffixIdx);const suffix=suffixIdx===-1?"":specifier.slice(suffixIdx);return resolveSpecifier(path.resolve(sourceDir,pathPart),suffix,specifier)};const rewriteWith=(text,regex,replacer)=>{const mask=maskStringsAndComments(text);return text.replace(regex,(...args)=>{const offset=args[args.length-2];return mask[offset]?args[0]:replacer(...args)})};let out=code;out=rewriteWith(out,STATIC_FROM_RE,(_m,pre,q,spec)=>`${pre}${q}${handle(spec)}${q}`);out=rewriteWith(out,BARE_IMPORT_RE,(_m,pre,q,spec)=>`${pre}${q}${handle(spec)}${q}`);out=rewriteWith(out,DYNAMIC_IMPORT_RE,(_m,pre,q,spec,post)=>`import${pre}${q}${handle(spec)}${q}${post}`);return out}async function writeTransformedToCache(originalPath,code,instanceID,transform){const absolutePath=path.resolve(originalPath);const projectRoot=findPackageRoot(absolutePath)??getSecureFallbackRoot();await sweepStaleSlothletCache(projectRoot);const cacheDir=path.join(projectRoot,".slothlet-cache",`${process.pid}-${instanceID}`);const transformed=new Map([[absolutePath,code]]);const deps=new Map;if(transform){const pending=[absolutePath];while(pending.length>0){const file=pending.pop();const fileDeps=new Set;deps.set(file,fileDeps);rewriteRelativeSpecifiers(transformed.get(file),file,(absoluteTarget,_suffix,specifier)=>{const resolved=resolveModuleFile(absoluteTarget);if(resolved.isTS)fileDeps.add(resolved.path);return specifier});for(const dep of fileDeps){if(!transformed.has(dep)){transformed.set(dep,await transform(dep));pending.push(dep)}}}}const closureCache=new Map;const closureOf=file=>{if(closureCache.has(file))return closureCache.get(file);const seen=new Set;const walk=node=>{if(seen.has(node))return;seen.add(node);for(const next of deps.get(node)??[])walk(next)};walk(file);closureCache.set(file,seen);return seen};const cacheName=file=>{const hash=createHash("sha256");for(const member of[...closureOf(file)].sort()){hash.update(member).update("\0").update(transformed.get(member)).update("\0")}hash.update("entry\0").update(file);return`${hash.digest("hex").slice(0,16)}.mjs`};await mkdir(cacheDir,{recursive:true});for(const[file,fileCode]of transformed){const rewritten=rewriteRelativeSpecifiers(fileCode,file,(absoluteTarget,suffix)=>{const resolved=resolveModuleFile(absoluteTarget);if(resolved.isTS&&transformed.has(resolved.path)){return pathToFileURL(path.join(cacheDir,cacheName(resolved.path))).href+suffix}return pathToFileURL(resolved.path).href+suffix});const cachePath=path.join(cacheDir,cacheName(file));try{await writeFile(cachePath,rewritten,{encoding:"utf8",flag:"wx"})}catch(err){if(err.code!=="EEXIST")throw err}}return{url:pathToFileURL(path.join(cacheDir,cacheName(absolutePath))).href,cacheDir}}async function transformTypeScriptStrict(filePath,options={}){const ts=await getTypeScript();const code=fs.readFileSync(filePath,"utf8");const targetMap={es3:ts.ScriptTarget.ES3,es5:ts.ScriptTarget.ES5,es6:ts.ScriptTarget.ES2015,es2015:ts.ScriptTarget.ES2015,es2016:ts.ScriptTarget.ES2016,es2017:ts.ScriptTarget.ES2017,es2018:ts.ScriptTarget.ES2018,es2019:ts.ScriptTarget.ES2019,es2020:ts.ScriptTarget.ES2020,es2021:ts.ScriptTarget.ES2021,es2022:ts.ScriptTarget.ES2022,esnext:ts.ScriptTarget.ESNext,latest:ts.ScriptTarget.Latest};const moduleMap={none:ts.ModuleKind.None,commonjs:ts.ModuleKind.CommonJS,amd:ts.ModuleKind.AMD,system:ts.ModuleKind.System,umd:ts.ModuleKind.UMD,es6:ts.ModuleKind.ES2015,es2015:ts.ModuleKind.ES2015,es2020:ts.ModuleKind.ES2020,es2022:ts.ModuleKind.ES2022,esnext:ts.ModuleKind.ESNext,node16:ts.ModuleKind.Node16,nodenext:ts.ModuleKind.NodeNext};const targetKey=(options.target||"es2020").toLowerCase();const moduleKey=(options.module||"esnext").toLowerCase();const compilerOptions={target:targetMap[targetKey]||ts.ScriptTarget.ES2020,module:moduleMap[moduleKey]||ts.ModuleKind.ESNext,strict:options.strict!==false,esModuleInterop:true,skipLibCheck:true,noEmit:false,...options.typeDefinitionPath&&{typeRoots:[path.dirname(options.typeDefinitionPath)],types:[path.basename(options.typeDefinitionPath,".d.ts")]},...options.compilerOptions};let diagnostics=[];if(!options.skipTypeCheck){const host=ts.createCompilerHost(compilerOptions);const originalReadFile=host.readFile;host.readFile=fileName=>{if(fileName===filePath){return code}return originalReadFile.call(host,fileName)};const program=ts.createProgram([filePath],compilerOptions,host);const allDiagnostics=[...program.getSemanticDiagnostics(),...program.getSyntacticDiagnostics()];diagnostics=allDiagnostics.filter(d=>d.file&&d.file.fileName===filePath)}const result=ts.transpileModule(code,{compilerOptions,fileName:filePath});return{code:result.outputText,diagnostics}}function formatDiagnostics(diagnostics,ts){return diagnostics.map(diagnostic=>{const message=ts.flattenDiagnosticMessageText(diagnostic.messageText,"\n");if(diagnostic.file&&diagnostic.start!==void 0){const{line,character}=diagnostic.file.getLineAndCharacterOfPosition(diagnostic.start);const fileName=diagnostic.file.fileName;return`${fileName}:${line+1}:${character+1} - ${message}`}return message})}export{createDataUrl,formatDiagnostics,maskStringsAndComments,resolveModuleFile,rewriteRelativeSpecifiers,transformTypeScript,transformTypeScriptStrict,writeTransformedToCache};
|
|
@@ -14,4 +14,4 @@
|
|
|
14
14
|
limitations under the License.
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
|
-
import path from"node:path";import slothlet from"@cldmv/slothlet";import{generateTypes as writeDeclarationFile}from"@cldmv/slothlet/processors/type-generator";import{SlothletError}from"@cldmv/slothlet/errors";async function generateTypes(options={}){if(options===null||typeof options!=="object"||Array.isArray(options)){throw new SlothletError("INVALID_CONFIG",{option:"typegen.options",expected:"plain object",value:options,hint:"Call generateTypes({ dir, output, interfaceName }) with an options object.",validationError:true},null,{validationError:true})}assertOption(options.dir,"dir");assertOption(options.output,"output");assertOption(options.interfaceName,"interfaceName");const api=await slothlet({
|
|
17
|
+
import path from"node:path";import slothlet from"@cldmv/slothlet";import{generateTypes as writeDeclarationFile}from"@cldmv/slothlet/processors/type-generator";import{SlothletError}from"@cldmv/slothlet/errors";async function generateTypes(options={}){if(options===null||typeof options!=="object"||Array.isArray(options)){throw new SlothletError("INVALID_CONFIG",{option:"typegen.options",expected:"plain object",value:options,hint:"Call generateTypes({ dir, output, interfaceName }) with an options object.",validationError:true},null,{validationError:true})}assertOption(options.dir,"dir");assertOption(options.output,"output");assertOption(options.interfaceName,"interfaceName");const api=await slothlet({base:path.resolve(options.dir),mode:"eager",typescript:options.typescript??{mode:"fast"}});try{const result=await writeDeclarationFile(api,{output:path.resolve(options.output),interfaceName:options.interfaceName,includeDocumentation:options.includeDocumentation??true});return{filePath:result.filePath,content:result.output}}finally{await api.slothlet.shutdown()}}function assertOption(value,name){if(typeof value!=="string"||value.length===0){throw new SlothletError("INVALID_CONFIG",{option:`typegen.${name}`,expected:"non-empty string",value,hint:`Provide options.${name} as a non-empty string when calling generateTypes(). For the CLI, use --${camelToKebab(name)} or the matching positional argument.`,validationError:true},null,{validationError:true})}}function camelToKebab(s){return s.replace(/[A-Z]/g,m=>`-${m.toLowerCase()}`)}export{generateTypes};
|
package/dist/slothlet.mjs
CHANGED
|
@@ -14,4 +14,4 @@
|
|
|
14
14
|
limitations under the License.
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
|
-
import{readdirSync}from"node:fs";import{dirname,join}from"node:path";import{fileURLToPath,pathToFileURL}from"node:url";import{getContextManager}from"@cldmv/slothlet/factories/context";import{SlothletError,SlothletWarning,SlothletDebug}from"@cldmv/slothlet/errors";import{registerInstance}from"@cldmv/slothlet/handlers/lifecycle-token";import{resolveWrapper}from"@cldmv/slothlet/handlers/unified-wrapper";import{initI18n}from"@cldmv/slothlet/i18n";import{enableEventEmitterPatching,disableEventEmitterPatching,cleanupEventEmitterResources,setApiContextChecker}from"@cldmv/slothlet/helpers/eventemitter-context";class Slothlet{static RESERVED_ROOT_KEYS=["slothlet","shutdown","destroy"];static SKIP_PROPS=["__metadata","__type","_materialize","_impl","____slothletInternal"];constructor(){this.SlothletError=SlothletError;this.SlothletWarning=SlothletWarning;this.debugLogger=null;this.instanceID=null;this.config=null;this.api=null;this.boundApi=null;this.contextManager=null;this.isLoaded=false;this.reference=null;this.context=null;this.envSnapshot=null;this._totalLazyCount=0;this._unmaterializedLazyCount=0;this._materializationComplete=false;this._materializationWaiters=[];this._materializationCompleteEmitted=false;this.componentCategories=["helpers","handlers","builders","processors","modes"];for(const category of this.componentCategories){this[category]={}}}async _initializeComponents(){const baseDir=join(dirname(fileURLToPath(import.meta.url)),"lib");for(const category of this.componentCategories){const categoryDir=join(baseDir,category);const files=readdirSync(categoryDir).filter(f=>f.endsWith(".mjs"));for(const file of files){const filePath=join(categoryDir,file);try{const module=await import(pathToFileURL(filePath).href);const classExports=Object.values(module).filter(exp=>typeof exp==="function"&&exp.slothletProperty);for(const ClassExport of classExports){const propName=ClassExport.slothletProperty;this[category][propName]=new ClassExport(this);if(this.config?.debug?.initialization){this.debug("initialization",{key:"DEBUG_MODE_COMPONENT_INITIALIZED",component:ClassExport.name,category,propertyName:propName})}}}catch(error){throw new this.SlothletError("MODULE_IMPORT_FAILED",{modulePath:filePath},error)}}}}_setupLifecycleSubscribers(){if(!this.handlers.lifecycle){return}if(this.handlers.metadata){this.handlers.lifecycle.subscribe("impl:created",(data,token)=>{this.handlers.metadata.tagSystemMetadata(data.impl,{filePath:data.filePath,apiPath:data.apiPath,moduleID:data.moduleID,sourceFolder:data.sourceFolder},token)});this.handlers.lifecycle.subscribe("impl:changed",(data,token)=>{this.handlers.metadata.tagSystemMetadata(data.impl,{filePath:data.filePath,apiPath:data.apiPath,moduleID:data.moduleID,sourceFolder:data.sourceFolder},token)});this.handlers.lifecycle.subscribe("impl:removed",data=>{if(data.apiPath){const rootSegment=data.apiPath.split(".")[0];this.handlers.metadata.removeUserMetadataByApiPath(rootSegment)}})}if(this.handlers.ownership){this.handlers.lifecycle.subscribe("impl:created",data=>{const collisionMode=this.config?.collision?.api||"merge";const implValue=data.wrapper?.__impl??data.impl;this.handlers.ownership.register({moduleID:data.moduleID,apiPath:data.apiPath,value:implValue,source:data.source,filePath:data.filePath,collisionMode})});this.handlers.lifecycle.subscribe("impl:changed",data=>{const collisionMode=this.config?.collision?.api||"merge";const implValue=data.wrapper?.__impl??data.impl;const currentOwner=this.handlers.ownership.getCurrentOwner(data.apiPath);if(currentOwner?.moduleID!==data.moduleID){this.handlers.ownership.register({moduleID:data.moduleID,apiPath:data.apiPath,value:implValue,source:data.source,filePath:data.filePath,collisionMode})}})}}_registerLazyWrapper(){this._totalLazyCount++;this._unmaterializedLazyCount++;if(this.config?.debug?.materialize){this.debug("materialize",{key:"DEBUG_MODE_LAZY_WRAPPER_REGISTERED",total:this._totalLazyCount,unmaterialized:this._unmaterializedLazyCount})}}_onWrapperMaterialized(){this._unmaterializedLazyCount--;if(this.config?.debug?.materialize){this.debug("materialize",{key:"DEBUG_MODE_LAZY_WRAPPER_MATERIALIZED",total:this._totalLazyCount,unmaterialized:this._unmaterializedLazyCount,percentage:this._totalLazyCount>0?(this._totalLazyCount-this._unmaterializedLazyCount)/this._totalLazyCount*100:100})}if(this._unmaterializedLazyCount===0&&!this._materializationComplete){this._materializationComplete=true;if(this.config?.debug?.materialize){this.debug("materialize",{key:"DEBUG_MODE_ALL_LAZY_WRAPPERS_MATERIALIZED",total:this._totalLazyCount})}const waiters=this._materializationWaiters.splice(0);for(const resolve of waiters){resolve()}if(this.config?.tracking?.materialization&&!this._materializationCompleteEmitted){this._materializationCompleteEmitted=true;if(this.handlers?.lifecycle){this.handlers.lifecycle.emit("materialized:complete",{total:this._totalLazyCount,timestamp:Date.now()})}}}}_captureEnvSnapshot(envConfig){const rawInclude=envConfig?.include;const include=Array.isArray(rawInclude)?rawInclude.filter(key=>typeof key==="string"):null;const useAllowlist=include!==null&&include.length>0;const raw=useAllowlist?include.reduce((acc,key)=>{if(Object.prototype.hasOwnProperty.call(process.env,key)){acc[key]=process.env[key]}return acc},Object.create(null)):{...process.env};return Object.freeze(raw)}async load(config={},preservedInstanceID=null){this.config=config;if(!this.envSnapshot){this.envSnapshot=this._captureEnvSnapshot(config.env)}this.debugLogger=new SlothletDebug(config);await this._initializeComponents();registerInstance(this);this._setupLifecycleSubscribers();this.config=this.helpers.config.transformConfig(config);if(this.config?.i18n?.language){initI18n({language:this.config.i18n.language})}this.debugLogger=new SlothletDebug(this.config);this.instanceID=preservedInstanceID||this.helpers.utilities.generateId();this.reference=this.config.reference;this.context=this.config.context;this.contextManager=getContextManager(this.config.runtime);setApiContextChecker(()=>{const ctx=this.contextManager.tryGetContext();return!!(ctx&&ctx.self)});let store;if(preservedInstanceID&&this.contextManager.instances.has(preservedInstanceID)){this.contextManager.cleanup(preservedInstanceID);store=this.contextManager.initialize(this.instanceID,this.config)}else{store=this.contextManager.initialize(this.instanceID,this.config)}enableEventEmitterPatching();if(typeof this.contextManager.registerEventEmitterContextChecker==="function"){this.contextManager.registerEventEmitterContextChecker()}const baseModuleId=`base_${this.helpers.utilities.generateId().substring(0,8)}`;const baseApi=await this.builders.builder.buildAPI({dir:this.config.dir,mode:this.config.mode,moduleID:baseModuleId});this.api=baseApi;const apiWithBuiltins=await this.buildFinalAPI(this.api);if(this.handlers.apiCacheManager){this.handlers.apiCacheManager.set(baseModuleId,{endpoint:".",moduleID:baseModuleId,api:this.api,folderPath:this.config.dir,mode:this.config.mode,sanitizeOptions:this.config.sanitize||{},collisionMode:this.config.collision?.api||"merge",config:{...this.config},timestamp:Date.now()})}this.injectRuntimeMetadataFunctions(apiWithBuiltins);if(this.config.metadata&&typeof this.config.metadata==="object"){for(const[key,value]of Object.entries(this.config.metadata)){this.handlers.metadata.setGlobalMetadata(key,value)}this.handlers.metadata.registerUserMetadata(baseModuleId,this.config.metadata)}if(this.handlers.ownership){this.handlers.ownership.registerSubtree(apiWithBuiltins,baseModuleId,"");this.handlers.ownership.setModuleEndpoint(baseModuleId,".")}if(!this.boundApi){const isCallable=typeof this.api==="function"||this.api&&typeof this.api.default==="function";const proxyTarget=isCallable?function(){}:{};this.boundApi=new Proxy(proxyTarget,{get:(target,prop)=>this.api?this.api[prop]:void 0,set:(target,prop,value)=>{if(this.api){this.api[prop]=value}return true},has:(target,prop)=>this.api?prop in this.api:false,ownKeys:____target=>this.api?Reflect.ownKeys(this.api):[],deleteProperty:(target,prop)=>this.api?delete this.api[prop]:true,apply:(target,thisArg,args)=>this.api?Reflect.apply(this.api,thisArg,args):void 0,construct:(target,args)=>this.api?Reflect.construct(this.api,args):{},getOwnPropertyDescriptor:(target,prop)=>{if(isCallable&&prop==="prototype"){return Object.getOwnPropertyDescriptor(target,prop)}if(this.api&&prop in this.api){const desc=Object.getOwnPropertyDescriptor(this.api,prop);if(desc){return{...desc,configurable:true}}}return void 0}})}store.self=this.boundApi;store.context=this.context||{};store.slothlet=this;if(this.reference&&typeof this.reference==="object"){Object.assign(this.boundApi,this.reference)}this.isLoaded=true;return this.boundApi}async reload(options={}){const{keepInstanceID=false}=options;if(!this.config?.dir){throw new SlothletError("INVALID_CONFIG_NOT_LOADED",{operation:"reload",validationError:true})}const operationHistory=this.handlers.apiManager?.state?.operationHistory?[...this.handlers.apiManager.state.operationHistory]:[];await this._clearModuleCaches();const oldInstanceID=this.instanceID;if(!keepInstanceID){this.instanceID=`${oldInstanceID}_reload_${Date.now()}`;if(this._typescriptCacheDirs?.size){const{rm}=await import("node:fs/promises");await Promise.allSettled([...this._typescriptCacheDirs].map(dir=>rm(dir,{recursive:true,force:true})));this._typescriptCacheDirs.clear()}}const savedMetadataState=this.handlers.metadata?.exportUserState?.();const savedHooks=this.handlers.hookManager?.exportHooks?.();await this.load(this.config,this.instanceID);if(savedMetadataState&&this.handlers.metadata){this.handlers.metadata.importUserState(savedMetadataState)}if(savedHooks?.length&&this.handlers.hookManager){this.handlers.hookManager.importHooks(savedHooks)}for(const[,store]of this.contextManager.instances){if(store.parentInstanceID===oldInstanceID){store.parentInstanceID=this.instanceID}}if(oldInstanceID&&oldInstanceID!==this.instanceID&&this.contextManager.instances?.has(oldInstanceID)){this.contextManager.cleanup(oldInstanceID)}for(const operation of operationHistory){if(operation.type==="add"){await this.handlers.apiManager.addApiComponent({apiPath:operation.apiPath,folderPath:operation.folderPath,options:{...operation.options||{},recordHistory:false},moduleID:`replay_${this.helpers.utilities.generateId().substring(0,8)}`,versionConfig:operation.versionConfig||null})}else if(operation.type==="remove"){await this.handlers.apiManager.removeApiComponent(operation.apiPath,{recordHistory:false})}else if(operation.type==="addPermissionRule"){if(this.handlers.permissionManager){this.handlers.permissionManager.addRule(operation.rule,operation.ownerModuleID,operation.ruleId)}}else if(operation.type==="removePermissionRule"){if(this.handlers.permissionManager){this.handlers.permissionManager.removeRule(operation.ruleId,operation.callerModuleID)}}}return this.boundApi}async _clearModuleCaches(){const targetDir=this.config.dir;const{resolve}=await import("node:path");const{createRequire}=await import("node:module");const require2=createRequire(import.meta.url);const absoluteTargetDir=resolve(targetDir);for(const key of Object.keys(require2.cache)){if(key.startsWith(absoluteTargetDir)){delete require2.cache[key]}}}injectRuntimeMetadataFunctions(api){if(!api.slothlet?.metadata){return}const metadataHandler=this.handlers.metadata;api.slothlet.metadata.get=async function slothlet_metadata_get_runtime(path){return metadataHandler.get(path)};api.slothlet.metadata.self=function slothlet_metadata_self_runtime(){return metadataHandler.self()};api.slothlet.metadata.caller=function slothlet_metadata_caller_runtime(){return metadataHandler.caller()}}async _drainInFlightLoads(){if(!this.api)return;const pending=[];const seen=new Set;const collect=(obj,depth=0)=>{const objType=typeof obj;if(!obj||objType!=="object"&&objType!=="function"||depth>15||seen.has(obj))return;seen.add(obj);try{if(obj.__isVersionDispatcher===true)return;const wrapper=resolveWrapper(obj);if(wrapper){const mat=wrapper.____slothletInternal.materializationPromise;if(mat)pending.push(mat);for(const key of Object.keys(wrapper)){if(!key.startsWith("____"))collect(wrapper[key],depth+1)}return}for(const key of Object.keys(obj)){collect(obj[key],depth+1)}}catch{}};for(const key of Object.keys(this.api)){collect(this.api[key])}if(pending.length>0){await Promise.allSettled(pending)}}async shutdown(){if(!this.isLoaded){return}await this._drainInFlightLoads();disableEventEmitterPatching();cleanupEventEmitterResources();if(this.instanceID&&this.contextManager){this.contextManager.cleanup(this.instanceID)}if(this.handlers.ownership){this.handlers.ownership.clear()}this.handlers.versionManager?.shutdown();await this.handlers.permissionManager?.shutdown();if(this._typescriptCacheDirs?.size){const{rm}=await import("node:fs/promises");await Promise.allSettled([...this._typescriptCacheDirs].map(dir=>rm(dir,{recursive:true,force:true})));this._typescriptCacheDirs.clear()}this.isLoaded=false}debug(code,context={}){if(this.debugLogger){this.debugLogger.log(code,context)}}getAPI(){if(!this.isLoaded){throw new SlothletError("INVALID_CONFIG_NOT_LOADED",{operation:"getAPI"},null,{validationError:true})}return this.boundApi}getDiagnostics(){return{instanceID:this.instanceID,isLoaded:this.isLoaded,config:this.config,context:this.contextManager?.getDiagnostics()||null,ownership:this.handlers.ownership?.getDiagnostics()||null}}getOwnership(){if(!this.handlers.ownership){return null}return this.handlers.ownership.getDiagnostics()}buildFinalAPI(userApi){return this.builders.apiBuilder.buildFinalAPI(userApi)}}async function slothlet(config){const instance=new Slothlet;const api=await instance.load(config);return api}var stdin_default=slothlet;export{stdin_default as default,slothlet};
|
|
17
|
+
import{readdirSync}from"node:fs";import{dirname,join}from"node:path";import{fileURLToPath,pathToFileURL}from"node:url";import{getContextManager}from"@cldmv/slothlet/factories/context";import{SlothletError,SlothletWarning,SlothletDebug}from"@cldmv/slothlet/errors";import{registerInstance}from"@cldmv/slothlet/handlers/lifecycle-token";import{resolveWrapper}from"@cldmv/slothlet/handlers/unified-wrapper";import{initI18n}from"@cldmv/slothlet/i18n";import{enableEventEmitterPatching,disableEventEmitterPatching,cleanupEventEmitterResources,setApiContextChecker}from"@cldmv/slothlet/helpers/eventemitter-context";class Slothlet{static RESERVED_ROOT_KEYS=["slothlet","shutdown","destroy"];static SKIP_PROPS=["__metadata","__type","_materialize","_impl","____slothletInternal"];constructor(){this.SlothletError=SlothletError;this.SlothletWarning=SlothletWarning;this.debugLogger=null;this.instanceID=null;this.config=null;this.envTarget="node";this.api=null;this.boundApi=null;this.contextManager=null;this.isLoaded=false;this.reference=null;this.context=null;this.envSnapshot=null;this._totalLazyCount=0;this._unmaterializedLazyCount=0;this._materializationComplete=false;this._materializationWaiters=[];this._materializationCompleteEmitted=false;this.componentCategories=["helpers","handlers","builders","processors","modes"];for(const category of this.componentCategories){this[category]={}}}async _initializeComponents(){if(this.envTarget==="browser"){await this._initializeComponentsBrowser();return}const baseDir=join(dirname(fileURLToPath(import.meta.url)),"lib");for(const category of this.componentCategories){const categoryDir=join(baseDir,category);const files=readdirSync(categoryDir).filter(f=>f.endsWith(".mjs"));for(const file of files){const filePath=join(categoryDir,file);try{const module=await import(pathToFileURL(filePath).href);const classExports=Object.values(module).filter(exp=>typeof exp==="function"&&exp.slothletProperty);for(const ClassExport of classExports){const propName=ClassExport.slothletProperty;this[category][propName]=new ClassExport(this);if(this.config?.debug?.initialization){this.debug("initialization",{key:"DEBUG_MODE_COMPONENT_INITIALIZED",component:ClassExport.name,category,propertyName:propName})}}}catch(error){throw new this.SlothletError("MODULE_IMPORT_FAILED",{modulePath:filePath},error)}}}}async _initializeComponentsBrowser(){const BROWSER_COMPONENT_SPECIFIERS=["@cldmv/slothlet/builders/api-assignment","@cldmv/slothlet/builders/api_builder","@cldmv/slothlet/builders/builder","@cldmv/slothlet/builders/modes-processor","@cldmv/slothlet/handlers/api-cache-manager","@cldmv/slothlet/handlers/api-manager","@cldmv/slothlet/handlers/hook-manager","@cldmv/slothlet/handlers/lifecycle","@cldmv/slothlet/handlers/materialize-manager","@cldmv/slothlet/handlers/metadata","@cldmv/slothlet/handlers/module-manager","@cldmv/slothlet/handlers/ownership","@cldmv/slothlet/handlers/permission-manager","@cldmv/slothlet/handlers/version-manager","@cldmv/slothlet/helpers/config","@cldmv/slothlet/helpers/hint-detector","@cldmv/slothlet/helpers/modes-utils","@cldmv/slothlet/helpers/resolve-from-caller","@cldmv/slothlet/helpers/sanitize","@cldmv/slothlet/helpers/utilities","@cldmv/slothlet/modes/eager","@cldmv/slothlet/modes/lazy","@cldmv/slothlet/processors/flatten","@cldmv/slothlet/processors/loader"];for(const specifier of BROWSER_COMPONENT_SPECIFIERS){const parts=specifier.split("/");const category=parts[2];const module=await import(specifier);const classExports=Object.values(module).filter(exp=>typeof exp==="function"&&exp.slothletProperty);for(const ClassExport of classExports){const propName=ClassExport.slothletProperty;this[category][propName]=new ClassExport(this)}}}_setupLifecycleSubscribers(){if(!this.handlers.lifecycle){return}if(this.handlers.metadata){this.handlers.lifecycle.subscribe("impl:created",(data,token)=>{this.handlers.metadata.tagSystemMetadata(data.impl,{filePath:data.filePath,apiPath:data.apiPath,moduleID:data.moduleID,sourceFolder:data.sourceFolder},token)});this.handlers.lifecycle.subscribe("impl:changed",(data,token)=>{this.handlers.metadata.tagSystemMetadata(data.impl,{filePath:data.filePath,apiPath:data.apiPath,moduleID:data.moduleID,sourceFolder:data.sourceFolder},token)});this.handlers.lifecycle.subscribe("impl:removed",data=>{if(data.apiPath){const rootSegment=data.apiPath.split(".")[0];this.handlers.metadata.removeUserMetadataByApiPath(rootSegment)}})}if(this.handlers.ownership){this.handlers.lifecycle.subscribe("impl:created",data=>{const collisionMode=this.config?.collision?.api||"merge";const implValue=data.wrapper?.__impl??data.impl;this.handlers.ownership.register({moduleID:data.moduleID,apiPath:data.apiPath,value:implValue,source:data.source,filePath:data.filePath,collisionMode})});this.handlers.lifecycle.subscribe("impl:changed",data=>{const collisionMode=this.config?.collision?.api||"merge";const implValue=data.wrapper?.__impl??data.impl;const currentOwner=this.handlers.ownership.getCurrentOwner(data.apiPath);if(currentOwner?.moduleID!==data.moduleID){this.handlers.ownership.register({moduleID:data.moduleID,apiPath:data.apiPath,value:implValue,source:data.source,filePath:data.filePath,collisionMode})}})}}_registerLazyWrapper(){this._totalLazyCount++;this._unmaterializedLazyCount++;if(this.config?.debug?.materialize){this.debug("materialize",{key:"DEBUG_MODE_LAZY_WRAPPER_REGISTERED",total:this._totalLazyCount,unmaterialized:this._unmaterializedLazyCount})}}_onWrapperMaterialized(){this._unmaterializedLazyCount--;if(this.config?.debug?.materialize){this.debug("materialize",{key:"DEBUG_MODE_LAZY_WRAPPER_MATERIALIZED",total:this._totalLazyCount,unmaterialized:this._unmaterializedLazyCount,percentage:this._totalLazyCount>0?(this._totalLazyCount-this._unmaterializedLazyCount)/this._totalLazyCount*100:100})}if(this._unmaterializedLazyCount===0&&!this._materializationComplete){this._materializationComplete=true;if(this.config?.debug?.materialize){this.debug("materialize",{key:"DEBUG_MODE_ALL_LAZY_WRAPPERS_MATERIALIZED",total:this._totalLazyCount})}const waiters=this._materializationWaiters.splice(0);for(const resolve of waiters){resolve()}if(this.config?.tracking?.materialization&&!this._materializationCompleteEmitted){this._materializationCompleteEmitted=true;if(this.handlers?.lifecycle){this.handlers.lifecycle.emit("materialized:complete",{total:this._totalLazyCount,timestamp:Date.now()})}}}}_captureEnvSnapshot(envConfig){const rawInclude=envConfig?.include;const include=Array.isArray(rawInclude)?rawInclude.filter(key=>typeof key==="string"):null;const useAllowlist=include!==null&&include.length>0;const raw=useAllowlist?include.reduce((acc,key)=>{if(Object.prototype.hasOwnProperty.call(process.env,key)){acc[key]=process.env[key]}return acc},Object.create(null)):{...process.env};return Object.freeze(raw)}async load(config={},preservedInstanceID=null){this.config=config;this.envTarget=config.platform==="browser"||config.platform!=="node"&&config.manifest!=null?"browser":"node";if(!this.envSnapshot){this.envSnapshot=this.envTarget==="browser"?Object.freeze(Object.create(null)):this._captureEnvSnapshot(config.env)}this.debugLogger=new SlothletDebug(config);await this._initializeComponents();registerInstance(this);this._setupLifecycleSubscribers();this.config=this.helpers.config.transformConfig(config);if(this.config?.i18n?.language){initI18n({language:this.config.i18n.language})}this.debugLogger=new SlothletDebug(this.config);this.instanceID=preservedInstanceID||this.helpers.utilities.generateId();this.reference=this.config.reference;this.context=this.config.context;this.contextManager=getContextManager(this.config.runtime);setApiContextChecker(()=>{const ctx=this.contextManager.tryGetContext();return!!(ctx&&ctx.self)});let store;if(preservedInstanceID&&this.contextManager.instances.has(preservedInstanceID)){this.contextManager.cleanup(preservedInstanceID);store=this.contextManager.initialize(this.instanceID,this.config)}else{store=this.contextManager.initialize(this.instanceID,this.config)}enableEventEmitterPatching();if(typeof this.contextManager.registerEventEmitterContextChecker==="function"){this.contextManager.registerEventEmitterContextChecker()}const baseModuleId=`base_${this.helpers.utilities.generateId().substring(0,8)}`;const baseApi=await this.builders.builder.buildAPI({dir:this.config.dir,mode:this.config.mode,moduleID:baseModuleId});this.api=baseApi;const apiWithBuiltins=await this.buildFinalAPI(this.api);if(this.handlers.apiCacheManager){this.handlers.apiCacheManager.set(baseModuleId,{endpoint:".",moduleID:baseModuleId,api:this.api,folderPath:this.config.dir,mode:this.config.mode,sanitizeOptions:this.config.sanitize||{},collisionMode:this.config.collision?.api||"merge",config:{...this.config},timestamp:Date.now()})}this.injectRuntimeMetadataFunctions(apiWithBuiltins);if(this.config.metadata&&typeof this.config.metadata==="object"){for(const[key,value]of Object.entries(this.config.metadata)){this.handlers.metadata.setGlobalMetadata(key,value)}this.handlers.metadata.registerUserMetadata(baseModuleId,this.config.metadata)}if(this.handlers.ownership){this.handlers.ownership.registerSubtree(apiWithBuiltins,baseModuleId,"");this.handlers.ownership.setModuleEndpoint(baseModuleId,".")}if(!this.boundApi){const isCallable=typeof this.api==="function"||this.api&&typeof this.api.default==="function";const proxyTarget=isCallable?function(){}:{};this.boundApi=new Proxy(proxyTarget,{get:(target,prop)=>this.api?this.api[prop]:void 0,set:(target,prop,value)=>{if(this.api){this.api[prop]=value}return true},has:(target,prop)=>this.api?prop in this.api:false,ownKeys:____target=>this.api?Reflect.ownKeys(this.api):[],deleteProperty:(target,prop)=>this.api?delete this.api[prop]:true,apply:(target,thisArg,args)=>this.api?Reflect.apply(this.api,thisArg,args):void 0,construct:(target,args)=>this.api?Reflect.construct(this.api,args):{},getOwnPropertyDescriptor:(target,prop)=>{if(isCallable&&prop==="prototype"){return Object.getOwnPropertyDescriptor(target,prop)}if(this.api&&prop in this.api){const desc=Object.getOwnPropertyDescriptor(this.api,prop);if(desc){return{...desc,configurable:true}}}return void 0}})}store.self=this.boundApi;store.context=this.context||{};store.slothlet=this;if(this.reference&&typeof this.reference==="object"){Object.assign(this.boundApi,this.reference)}this.isLoaded=true;return this.boundApi}async reload(options={}){const{keepInstanceID=false}=options;if(!this.config?.dir){throw new SlothletError("INVALID_CONFIG_NOT_LOADED",{operation:"reload",validationError:true})}const operationHistory=this.handlers.apiManager?.state?.operationHistory?[...this.handlers.apiManager.state.operationHistory]:[];await this._clearModuleCaches();const oldInstanceID=this.instanceID;if(!keepInstanceID){this.instanceID=`${oldInstanceID}_reload_${Date.now()}`;if(this._typescriptCacheDirs?.size){const{rm}=await import("node:fs/promises");await Promise.allSettled([...this._typescriptCacheDirs].map(dir=>rm(dir,{recursive:true,force:true})));this._typescriptCacheDirs.clear()}}const savedMetadataState=this.handlers.metadata?.exportUserState?.();const savedHooks=this.handlers.hookManager?.exportHooks?.();await this.load(this.config,this.instanceID);if(savedMetadataState&&this.handlers.metadata){this.handlers.metadata.importUserState(savedMetadataState)}if(savedHooks?.length&&this.handlers.hookManager){this.handlers.hookManager.importHooks(savedHooks)}for(const[,store]of this.contextManager.instances){if(store.parentInstanceID===oldInstanceID){store.parentInstanceID=this.instanceID}}if(oldInstanceID&&oldInstanceID!==this.instanceID&&this.contextManager.instances?.has(oldInstanceID)){this.contextManager.cleanup(oldInstanceID)}for(const operation of operationHistory){if(operation.type==="add"){await this.handlers.apiManager.addApiComponent({apiPath:operation.apiPath,folderPath:operation.folderPath,options:{...operation.options||{},recordHistory:false},moduleID:`replay_${this.helpers.utilities.generateId().substring(0,8)}`,versionConfig:operation.versionConfig||null})}else if(operation.type==="remove"){await this.handlers.apiManager.removeApiComponent(operation.apiPath,{recordHistory:false})}else if(operation.type==="addPermissionRule"){if(this.handlers.permissionManager){this.handlers.permissionManager.addRule(operation.rule,operation.ownerModuleID,operation.ruleId)}}else if(operation.type==="removePermissionRule"){if(this.handlers.permissionManager){this.handlers.permissionManager.removeRule(operation.ruleId,operation.callerModuleID)}}}return this.boundApi}async _clearModuleCaches(){const targetDir=this.config.dir;const{resolve}=await import("node:path");const{createRequire}=await import("node:module");const require2=createRequire(import.meta.url);const absoluteTargetDir=resolve(targetDir);for(const key of Object.keys(require2.cache)){if(key.startsWith(absoluteTargetDir)){delete require2.cache[key]}}}injectRuntimeMetadataFunctions(api){if(!api.slothlet?.metadata){return}const metadataHandler=this.handlers.metadata;api.slothlet.metadata.get=async function slothlet_metadata_get_runtime(path){return metadataHandler.get(path)};api.slothlet.metadata.self=function slothlet_metadata_self_runtime(){return metadataHandler.self()};api.slothlet.metadata.caller=function slothlet_metadata_caller_runtime(){return metadataHandler.caller()}}async _drainInFlightLoads(){if(!this.api)return;const pending=[];const seen=new Set;const collect=(obj,depth=0)=>{const objType=typeof obj;if(!obj||objType!=="object"&&objType!=="function"||depth>15||seen.has(obj))return;seen.add(obj);try{if(obj.__isVersionDispatcher===true)return;const wrapper=resolveWrapper(obj);if(wrapper){const mat=wrapper.____slothletInternal.materializationPromise;if(mat)pending.push(mat);for(const key of Object.keys(wrapper)){if(!key.startsWith("____"))collect(wrapper[key],depth+1)}return}for(const key of Object.keys(obj)){collect(obj[key],depth+1)}}catch{}};for(const key of Object.keys(this.api)){collect(this.api[key])}if(pending.length>0){await Promise.allSettled(pending)}}async shutdown(){if(!this.isLoaded){return}await this._drainInFlightLoads();disableEventEmitterPatching();cleanupEventEmitterResources();if(this.instanceID&&this.contextManager){this.contextManager.cleanup(this.instanceID)}if(this.handlers.ownership){this.handlers.ownership.clear()}this.handlers.versionManager?.shutdown();await this.handlers.permissionManager?.shutdown();if(this._typescriptCacheDirs?.size){const{rm}=await import("node:fs/promises");await Promise.allSettled([...this._typescriptCacheDirs].map(dir=>rm(dir,{recursive:true,force:true})));this._typescriptCacheDirs.clear()}this.isLoaded=false}debug(code,context={}){if(this.debugLogger){this.debugLogger.log(code,context)}}getAPI(){if(!this.isLoaded){throw new SlothletError("INVALID_CONFIG_NOT_LOADED",{operation:"getAPI"},null,{validationError:true})}return this.boundApi}getDiagnostics(){return{instanceID:this.instanceID,isLoaded:this.isLoaded,config:this.config,context:this.contextManager?.getDiagnostics()||null,ownership:this.handlers.ownership?.getDiagnostics()||null}}getOwnership(){if(!this.handlers.ownership){return null}return this.handlers.ownership.getDiagnostics()}buildFinalAPI(userApi){return this.builders.apiBuilder.buildFinalAPI(userApi)}}async function slothlet(config){const instance=new Slothlet;const api=await instance.load(config);return api}var stdin_default=slothlet;export{stdin_default as default,slothlet};
|