@cldmv/slothlet 3.2.3 → 3.3.2

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.
Files changed (138) hide show
  1. package/README.md +22 -9
  2. package/REFERENCE.md +23 -0
  3. package/dist/lib/builders/api-assignment.mjs +1 -589
  4. package/dist/lib/builders/api_builder.mjs +1 -1155
  5. package/dist/lib/builders/builder.mjs +1 -78
  6. package/dist/lib/builders/modes-processor.mjs +1 -1800
  7. package/dist/lib/errors.mjs +9 -211
  8. package/dist/lib/factories/component-base.mjs +1 -80
  9. package/dist/lib/factories/context.mjs +1 -22
  10. package/dist/lib/handlers/api-cache-manager.mjs +1 -200
  11. package/dist/lib/handlers/api-manager.mjs +1 -2513
  12. package/dist/lib/handlers/context-async.mjs +1 -168
  13. package/dist/lib/handlers/context-live.mjs +1 -168
  14. package/dist/lib/handlers/hook-manager.mjs +1 -773
  15. package/dist/lib/handlers/lifecycle-token.mjs +1 -28
  16. package/dist/lib/handlers/lifecycle.mjs +1 -115
  17. package/dist/lib/handlers/materialize-manager.mjs +1 -48
  18. package/dist/lib/handlers/metadata.mjs +1 -501
  19. package/dist/lib/handlers/ownership.mjs +1 -322
  20. package/dist/lib/handlers/permission-manager.mjs +17 -0
  21. package/dist/lib/handlers/unified-wrapper.mjs +1 -3042
  22. package/dist/lib/handlers/version-manager.mjs +1 -885
  23. package/dist/lib/helpers/class-instance-wrapper.mjs +1 -109
  24. package/dist/lib/helpers/config.mjs +1 -355
  25. package/dist/lib/helpers/eventemitter-context.mjs +1 -349
  26. package/dist/lib/helpers/hint-detector.mjs +1 -47
  27. package/dist/lib/helpers/modes-utils.mjs +1 -37
  28. package/dist/lib/helpers/pattern-matcher.mjs +17 -0
  29. package/dist/lib/helpers/resolve-from-caller.mjs +1 -169
  30. package/dist/lib/helpers/sanitize.mjs +1 -340
  31. package/dist/lib/helpers/utilities.mjs +1 -70
  32. package/dist/lib/i18n/languages/de-de.json +21 -1
  33. package/dist/lib/i18n/languages/en-gb.json +21 -1
  34. package/dist/lib/i18n/languages/en-us.json +21 -1
  35. package/dist/lib/i18n/languages/es-mx.json +21 -1
  36. package/dist/lib/i18n/languages/fr-fr.json +21 -1
  37. package/dist/lib/i18n/languages/hi-in.json +21 -1
  38. package/dist/lib/i18n/languages/ja-jp.json +21 -1
  39. package/dist/lib/i18n/languages/ko-kr.json +21 -1
  40. package/dist/lib/i18n/languages/pt-br.json +21 -1
  41. package/dist/lib/i18n/languages/ru-ru.json +21 -1
  42. package/dist/lib/i18n/languages/zh-cn.json +21 -1
  43. package/dist/lib/i18n/translations.mjs +1 -126
  44. package/dist/lib/modes/eager.mjs +1 -59
  45. package/dist/lib/modes/lazy.mjs +1 -81
  46. package/dist/lib/processors/flatten.mjs +1 -437
  47. package/dist/lib/processors/loader.mjs +1 -339
  48. package/dist/lib/processors/type-generator.mjs +1 -275
  49. package/dist/lib/processors/typescript.mjs +1 -172
  50. package/dist/lib/runtime/runtime-asynclocalstorage.mjs +1 -113
  51. package/dist/lib/runtime/runtime-livebindings.mjs +1 -78
  52. package/dist/lib/runtime/runtime.mjs +1 -102
  53. package/dist/slothlet.mjs +1 -808
  54. package/package.json +37 -31
  55. package/types/dist/lib/builders/api-assignment.d.mts +3 -92
  56. package/types/dist/lib/builders/api-assignment.d.mts.map +1 -1
  57. package/types/dist/lib/builders/api_builder.d.mts +102 -91
  58. package/types/dist/lib/builders/api_builder.d.mts.map +1 -1
  59. package/types/dist/lib/builders/builder.d.mts +1 -55
  60. package/types/dist/lib/builders/builder.d.mts.map +1 -1
  61. package/types/dist/lib/builders/modes-processor.d.mts +3 -27
  62. package/types/dist/lib/builders/modes-processor.d.mts.map +1 -1
  63. package/types/dist/lib/errors.d.mts +19 -109
  64. package/types/dist/lib/errors.d.mts.map +1 -1
  65. package/types/dist/lib/factories/component-base.d.mts +7 -177
  66. package/types/dist/lib/factories/component-base.d.mts.map +1 -1
  67. package/types/dist/lib/factories/context.d.mts +4 -22
  68. package/types/dist/lib/factories/context.d.mts.map +1 -1
  69. package/types/dist/lib/handlers/api-cache-manager.d.mts +20 -203
  70. package/types/dist/lib/handlers/api-cache-manager.d.mts.map +1 -1
  71. package/types/dist/lib/handlers/api-manager.d.mts +33 -408
  72. package/types/dist/lib/handlers/api-manager.d.mts.map +1 -1
  73. package/types/dist/lib/handlers/context-async.d.mts +23 -61
  74. package/types/dist/lib/handlers/context-async.d.mts.map +1 -1
  75. package/types/dist/lib/handlers/context-live.d.mts +22 -59
  76. package/types/dist/lib/handlers/context-live.d.mts.map +1 -1
  77. package/types/dist/lib/handlers/hook-manager.d.mts +46 -185
  78. package/types/dist/lib/handlers/hook-manager.d.mts.map +1 -1
  79. package/types/dist/lib/handlers/lifecycle-token.d.mts +3 -48
  80. package/types/dist/lib/handlers/lifecycle-token.d.mts.map +1 -1
  81. package/types/dist/lib/handlers/lifecycle.d.mts +5 -82
  82. package/types/dist/lib/handlers/lifecycle.d.mts.map +1 -1
  83. package/types/dist/lib/handlers/materialize-manager.d.mts +8 -70
  84. package/types/dist/lib/handlers/materialize-manager.d.mts.map +1 -1
  85. package/types/dist/lib/handlers/metadata.d.mts +17 -221
  86. package/types/dist/lib/handlers/metadata.d.mts.map +1 -1
  87. package/types/dist/lib/handlers/ownership.d.mts +44 -160
  88. package/types/dist/lib/handlers/ownership.d.mts.map +1 -1
  89. package/types/dist/lib/handlers/permission-manager.d.mts +47 -0
  90. package/types/dist/lib/handlers/permission-manager.d.mts.map +1 -0
  91. package/types/dist/lib/handlers/unified-wrapper.d.mts +26 -239
  92. package/types/dist/lib/handlers/unified-wrapper.d.mts.map +1 -1
  93. package/types/dist/lib/handlers/version-manager.d.mts +28 -225
  94. package/types/dist/lib/handlers/version-manager.d.mts.map +1 -1
  95. package/types/dist/lib/helpers/class-instance-wrapper.d.mts +2 -52
  96. package/types/dist/lib/helpers/class-instance-wrapper.d.mts.map +1 -1
  97. package/types/dist/lib/helpers/config.d.mts +125 -123
  98. package/types/dist/lib/helpers/config.d.mts.map +1 -1
  99. package/types/dist/lib/helpers/eventemitter-context.d.mts +3 -29
  100. package/types/dist/lib/helpers/eventemitter-context.d.mts.map +1 -1
  101. package/types/dist/lib/helpers/hint-detector.d.mts +2 -15
  102. package/types/dist/lib/helpers/hint-detector.d.mts.map +1 -1
  103. package/types/dist/lib/helpers/modes-utils.d.mts +3 -30
  104. package/types/dist/lib/helpers/modes-utils.d.mts.map +1 -1
  105. package/types/dist/lib/helpers/pattern-matcher.d.mts +4 -0
  106. package/types/dist/lib/helpers/pattern-matcher.d.mts.map +1 -0
  107. package/types/dist/lib/helpers/resolve-from-caller.d.mts +3 -27
  108. package/types/dist/lib/helpers/resolve-from-caller.d.mts.map +1 -1
  109. package/types/dist/lib/helpers/sanitize.d.mts +4 -92
  110. package/types/dist/lib/helpers/sanitize.d.mts.map +1 -1
  111. package/types/dist/lib/helpers/utilities.d.mts +4 -52
  112. package/types/dist/lib/helpers/utilities.d.mts.map +1 -1
  113. package/types/dist/lib/i18n/translations.d.mts +4 -37
  114. package/types/dist/lib/i18n/translations.d.mts.map +1 -1
  115. package/types/dist/lib/modes/eager.d.mts +8 -30
  116. package/types/dist/lib/modes/eager.d.mts.map +1 -1
  117. package/types/dist/lib/modes/lazy.d.mts +10 -43
  118. package/types/dist/lib/modes/lazy.d.mts.map +1 -1
  119. package/types/dist/lib/processors/flatten.d.mts +56 -107
  120. package/types/dist/lib/processors/flatten.d.mts.map +1 -1
  121. package/types/dist/lib/processors/loader.d.mts +6 -41
  122. package/types/dist/lib/processors/loader.d.mts.map +1 -1
  123. package/types/dist/lib/processors/type-generator.d.mts +2 -16
  124. package/types/dist/lib/processors/type-generator.d.mts.map +1 -1
  125. package/types/dist/lib/processors/typescript.d.mts +6 -53
  126. package/types/dist/lib/processors/typescript.d.mts.map +1 -1
  127. package/types/dist/lib/runtime/runtime-asynclocalstorage.d.mts +3 -71
  128. package/types/dist/lib/runtime/runtime-asynclocalstorage.d.mts.map +1 -1
  129. package/types/dist/lib/runtime/runtime-livebindings.d.mts +2 -37
  130. package/types/dist/lib/runtime/runtime-livebindings.d.mts.map +1 -1
  131. package/types/dist/lib/runtime/runtime.d.mts +3 -39
  132. package/types/dist/lib/runtime/runtime.d.mts.map +1 -1
  133. package/types/dist/slothlet.d.mts +3 -249
  134. package/types/dist/slothlet.d.mts.map +1 -1
  135. package/types/index.d.mts +36 -16
  136. package/types/index.d.mts.map +1 -0
  137. package/AGENT-USAGE.md +0 -736
  138. package/docs/API-RULES.md +0 -712
package/dist/slothlet.mjs CHANGED
@@ -14,811 +14,4 @@
14
14
  limitations under the License.
15
15
  */
16
16
 
17
-
18
-
19
-
20
- import { readdirSync } from "node:fs";
21
- import { dirname, join } from "node:path";
22
- import { fileURLToPath, pathToFileURL } from "node:url";
23
- import { getContextManager } from "@cldmv/slothlet/factories/context";
24
- import { SlothletError, SlothletWarning, SlothletDebug } from "@cldmv/slothlet/errors";
25
- import { registerInstance } from "@cldmv/slothlet/handlers/lifecycle-token";
26
- import { resolveWrapper } from "@cldmv/slothlet/handlers/unified-wrapper";
27
- import { initI18n } from "@cldmv/slothlet/i18n";
28
- import {
29
- enableEventEmitterPatching,
30
- disableEventEmitterPatching,
31
- cleanupEventEmitterResources,
32
- setApiContextChecker
33
- } from "@cldmv/slothlet/helpers/eventemitter-context";
34
-
35
-
36
- class Slothlet {
37
-
38
-
39
- static RESERVED_ROOT_KEYS = ["slothlet", "shutdown", "destroy"];
40
-
41
-
42
- static SKIP_PROPS = ["__metadata", "__type", "_materialize", "_impl", "____slothletInternal"];
43
-
44
- constructor() {
45
-
46
- this.SlothletError = SlothletError;
47
- this.SlothletWarning = SlothletWarning;
48
-
49
- this.debugLogger = null;
50
-
51
-
52
- this.instanceID = null;
53
- this.config = null;
54
- this.api = null;
55
- this.boundApi = null;
56
- this.contextManager = null;
57
- this.isLoaded = false;
58
- this.reference = null;
59
- this.context = null;
60
-
61
-
62
- this.envSnapshot = null;
63
-
64
-
65
- this._totalLazyCount = 0;
66
- this._unmaterializedLazyCount = 0;
67
- this._materializationComplete = false;
68
- this._materializationWaiters = [];
69
- this._materializationCompleteEmitted = false;
70
-
71
-
72
- this.componentCategories = ["helpers", "handlers", "builders", "processors", "modes"];
73
-
74
-
75
- for (const category of this.componentCategories) {
76
- this[category] = {};
77
- }
78
- }
79
-
80
-
81
- async _initializeComponents() {
82
-
83
-
84
-
85
-
86
-
87
-
88
- const baseDir = join(dirname(fileURLToPath(import.meta.url)), "lib");
89
-
90
- for (const category of this.componentCategories) {
91
- const categoryDir = join(baseDir, category);
92
- const files = readdirSync(categoryDir).filter((f) => f.endsWith(".mjs"));
93
-
94
- for (const file of files) {
95
- const filePath = join(categoryDir, file);
96
-
97
- try {
98
- const module = await import(pathToFileURL(filePath).href);
99
-
100
-
101
- const classExports = Object.values(module).filter((exp) => typeof exp === "function" && exp.slothletProperty);
102
-
103
- for (const ClassExport of classExports) {
104
- const propName = ClassExport.slothletProperty;
105
-
106
- this[category][propName] = new ClassExport(this);
107
-
108
- if (this.config?.debug?.initialization) {
109
- this.debug("initialization", {
110
- key: "DEBUG_MODE_COMPONENT_INITIALIZED",
111
- component: ClassExport.name,
112
- category,
113
- propertyName: propName
114
- });
115
- }
116
- }
117
- } catch (error) {
118
-
119
-
120
-
121
-
122
- throw new this.SlothletError(
123
- "MODULE_IMPORT_FAILED",
124
- {
125
- modulePath: filePath
126
- },
127
- error
128
- );
129
- }
130
- }
131
- }
132
- }
133
-
134
-
135
- _setupLifecycleSubscribers() {
136
-
137
-
138
- if (!this.handlers.lifecycle) {
139
- return;
140
- }
141
-
142
-
143
-
144
-
145
-
146
- if (this.handlers.metadata) {
147
- this.handlers.lifecycle.subscribe("impl:created", (data, token) => {
148
- this.handlers.metadata.tagSystemMetadata(
149
- data.impl,
150
- {
151
- filePath: data.filePath,
152
- apiPath: data.apiPath,
153
- moduleID: data.moduleID,
154
- sourceFolder: data.sourceFolder
155
- },
156
- token
157
- );
158
- });
159
-
160
- this.handlers.lifecycle.subscribe("impl:changed", (data, token) => {
161
- this.handlers.metadata.tagSystemMetadata(
162
- data.impl,
163
- {
164
- filePath: data.filePath,
165
- apiPath: data.apiPath,
166
- moduleID: data.moduleID,
167
- sourceFolder: data.sourceFolder
168
- },
169
- token
170
- );
171
- });
172
-
173
- this.handlers.lifecycle.subscribe("impl:removed", (data) => {
174
-
175
-
176
-
177
- if (data.apiPath) {
178
- const rootSegment = data.apiPath.split(".")[0];
179
- this.handlers.metadata.removeUserMetadataByApiPath(rootSegment);
180
- }
181
- });
182
- }
183
-
184
-
185
-
186
-
187
- if (this.handlers.ownership) {
188
-
189
-
190
- this.handlers.lifecycle.subscribe("impl:created", (data) => {
191
-
192
-
193
-
194
- const collisionMode = this.config?.collision?.api || "merge";
195
-
196
- const implValue = data.wrapper?.__impl ?? data.impl;
197
- this.handlers.ownership.register({
198
- moduleID: data.moduleID,
199
- apiPath: data.apiPath,
200
- value: implValue,
201
- source: data.source,
202
- filePath: data.filePath,
203
- collisionMode: collisionMode
204
- });
205
- });
206
-
207
-
208
-
209
- this.handlers.lifecycle.subscribe("impl:changed", (data) => {
210
-
211
-
212
-
213
- const collisionMode = this.config?.collision?.api || "merge";
214
-
215
-
216
-
217
- const implValue = data.wrapper?.__impl ?? data.impl;
218
-
219
-
220
-
221
- const currentOwner = this.handlers.ownership.getCurrentOwner(data.apiPath);
222
- if (currentOwner?.moduleID !== data.moduleID) {
223
- this.handlers.ownership.register({
224
- moduleID: data.moduleID,
225
- apiPath: data.apiPath,
226
- value: implValue,
227
- source: data.source,
228
- filePath: data.filePath,
229
- collisionMode: collisionMode
230
- });
231
- }
232
- });
233
- }
234
- }
235
-
236
-
237
- _registerLazyWrapper() {
238
- this._totalLazyCount++;
239
- this._unmaterializedLazyCount++;
240
-
241
- if (this.config?.debug?.materialize) {
242
- this.debug("materialize", {
243
- key: "DEBUG_MODE_LAZY_WRAPPER_REGISTERED",
244
- total: this._totalLazyCount,
245
- unmaterialized: this._unmaterializedLazyCount
246
- });
247
- }
248
- }
249
-
250
-
251
- _onWrapperMaterialized() {
252
- this._unmaterializedLazyCount--;
253
-
254
- if (this.config?.debug?.materialize) {
255
- this.debug("materialize", {
256
- key: "DEBUG_MODE_LAZY_WRAPPER_MATERIALIZED",
257
- total: this._totalLazyCount,
258
- unmaterialized: this._unmaterializedLazyCount,
259
-
260
-
261
- percentage: this._totalLazyCount > 0 ? ((this._totalLazyCount - this._unmaterializedLazyCount) / this._totalLazyCount) * 100 : 100
262
- });
263
- }
264
-
265
-
266
- if (this._unmaterializedLazyCount === 0 && !this._materializationComplete) {
267
- this._materializationComplete = true;
268
-
269
- if (this.config?.debug?.materialize) {
270
- this.debug("materialize", {
271
- key: "DEBUG_MODE_ALL_LAZY_WRAPPERS_MATERIALIZED",
272
- total: this._totalLazyCount
273
- });
274
- }
275
-
276
-
277
- const waiters = this._materializationWaiters.splice(0);
278
- for (const resolve of waiters) {
279
- resolve();
280
- }
281
-
282
-
283
- if (this.config?.tracking?.materialization && !this._materializationCompleteEmitted) {
284
- this._materializationCompleteEmitted = true;
285
-
286
-
287
- if (this.handlers?.lifecycle) {
288
- this.handlers.lifecycle.emit("materialized:complete", {
289
- total: this._totalLazyCount,
290
- timestamp: Date.now()
291
- });
292
- }
293
- }
294
- }
295
- }
296
-
297
-
298
- _captureEnvSnapshot(envConfig) {
299
- const rawInclude = envConfig?.include;
300
- const include = Array.isArray(rawInclude) ? rawInclude.filter((key) => typeof key === "string") : null;
301
- const useAllowlist = include !== null && include.length > 0;
302
- const raw = useAllowlist
303
- ? include.reduce((acc, key) => {
304
- if (Object.prototype.hasOwnProperty.call(process.env, key)) {
305
- acc[key] = process.env[key];
306
- }
307
- return acc;
308
- }, Object.create(null))
309
- : { ...process.env };
310
- return Object.freeze(raw);
311
- }
312
-
313
-
314
- async load(config = {}, preservedInstanceID = null) {
315
-
316
- this.config = config;
317
-
318
-
319
-
320
-
321
-
322
-
323
- if (!this.envSnapshot) {
324
- this.envSnapshot = this._captureEnvSnapshot(config.env);
325
- }
326
-
327
-
328
-
329
-
330
- this.debugLogger = new SlothletDebug(config);
331
-
332
-
333
-
334
- await this._initializeComponents();
335
-
336
-
337
- registerInstance(this);
338
-
339
-
340
- this._setupLifecycleSubscribers();
341
-
342
-
343
- this.config = this.helpers.config.transformConfig(config);
344
-
345
-
346
- if (this.config?.i18n?.language) {
347
- initI18n({ language: this.config.i18n.language });
348
- }
349
-
350
-
351
-
352
- this.debugLogger = new SlothletDebug(this.config);
353
-
354
-
355
- this.instanceID = preservedInstanceID || this.helpers.utilities.generateId();
356
-
357
-
358
- this.reference = this.config.reference;
359
- this.context = this.config.context;
360
-
361
-
362
- this.contextManager = getContextManager(this.config.runtime);
363
-
364
-
365
-
366
-
367
-
368
-
369
-
370
-
371
-
372
- setApiContextChecker(() => {
373
-
374
- const ctx = this.contextManager.tryGetContext();
375
- return !!(ctx && ctx.self);
376
-
377
- });
378
-
379
-
380
-
381
- let store;
382
- if (preservedInstanceID && this.contextManager.instances.has(preservedInstanceID)) {
383
-
384
- this.contextManager.cleanup(preservedInstanceID);
385
- store = this.contextManager.initialize(this.instanceID, this.config);
386
- } else {
387
-
388
- store = this.contextManager.initialize(this.instanceID, this.config);
389
- }
390
-
391
-
392
-
393
- enableEventEmitterPatching();
394
-
395
-
396
-
397
-
398
- if (typeof this.contextManager.registerEventEmitterContextChecker === "function") {
399
- this.contextManager.registerEventEmitterContextChecker();
400
- }
401
-
402
-
403
-
404
-
405
- const baseModuleId = `base_${this.helpers.utilities.generateId().substring(0, 8)}`;
406
-
407
-
408
-
409
- const baseApi = await this.builders.builder.buildAPI({
410
- dir: this.config.dir,
411
- mode: this.config.mode,
412
- moduleID: baseModuleId
413
- });
414
-
415
-
416
-
417
- this.api = baseApi;
418
-
419
-
420
- const apiWithBuiltins = await this.buildFinalAPI(this.api);
421
-
422
-
423
-
424
-
425
-
426
- if (this.handlers.apiCacheManager) {
427
- this.handlers.apiCacheManager.set(baseModuleId, {
428
- endpoint: ".",
429
- moduleID: baseModuleId,
430
- api: this.api,
431
- folderPath: this.config.dir,
432
- mode: this.config.mode,
433
- sanitizeOptions: this.config.sanitize || {},
434
-
435
-
436
- collisionMode: this.config.collision?.api || "merge",
437
- config: { ...this.config },
438
- timestamp: Date.now()
439
- });
440
- }
441
-
442
-
443
- this.injectRuntimeMetadataFunctions(apiWithBuiltins);
444
-
445
-
446
- if (this.config.metadata && typeof this.config.metadata === "object") {
447
-
448
- for (const [key, value] of Object.entries(this.config.metadata)) {
449
- this.handlers.metadata.setGlobalMetadata(key, value);
450
- }
451
-
452
-
453
- this.handlers.metadata.registerUserMetadata(baseModuleId, this.config.metadata);
454
- }
455
-
456
-
457
-
458
-
459
-
460
- if (this.handlers.ownership) {
461
- this.handlers.ownership.registerSubtree(apiWithBuiltins, baseModuleId, "");
462
- }
463
-
464
-
465
-
466
-
467
-
468
- if (!this.boundApi) {
469
-
470
-
471
-
472
- const isCallable = typeof this.api === "function" || (this.api && typeof this.api.default === "function");
473
- const proxyTarget = isCallable ? function () {} : {};
474
-
475
- this.boundApi = new Proxy(proxyTarget, {
476
- get: (target, prop) => (this.api ? this.api[prop] : undefined),
477
- set: (target, prop, value) => {
478
- if (this.api) {
479
- this.api[prop] = value;
480
- }
481
- return true;
482
- },
483
- has: (target, prop) => (this.api ? prop in this.api : false),
484
- ownKeys: (____target) => (this.api ? Reflect.ownKeys(this.api) : []),
485
- deleteProperty: (target, prop) => (this.api ? delete this.api[prop] : true),
486
- apply: (target, thisArg, args) => (this.api ? Reflect.apply(this.api, thisArg, args) : undefined),
487
- construct: (target, args) => (this.api ? Reflect.construct(this.api, args) : {}),
488
- getOwnPropertyDescriptor: (target, prop) => {
489
-
490
- if (isCallable && prop === "prototype") {
491
- return Object.getOwnPropertyDescriptor(target, prop);
492
- }
493
- if (this.api && prop in this.api) {
494
- const desc = Object.getOwnPropertyDescriptor(this.api, prop);
495
- if (desc) {
496
- return { ...desc, configurable: true };
497
- }
498
- }
499
- return undefined;
500
- }
501
- });
502
-
503
- }
504
-
505
-
506
- store.self = this.boundApi;
507
- store.context = this.context || {};
508
-
509
-
510
-
511
- if (this.reference && typeof this.reference === "object") {
512
- Object.assign(this.boundApi, this.reference);
513
- }
514
-
515
- this.isLoaded = true;
516
-
517
- return this.boundApi;
518
- }
519
-
520
-
521
- async reload(options = {}) {
522
- const { keepInstanceID = false } = options;
523
-
524
-
525
-
526
- if (!this.config?.dir) {
527
- throw new SlothletError("INVALID_CONFIG_NOT_LOADED", {
528
- operation: "reload",
529
- validationError: true
530
- });
531
- }
532
-
533
-
534
-
535
-
536
- const operationHistory = this.handlers.apiManager?.state?.operationHistory ? [...this.handlers.apiManager.state.operationHistory] : [];
537
-
538
-
539
- await this._clearModuleCaches();
540
-
541
-
542
-
543
-
544
-
545
- const oldInstanceID = this.instanceID;
546
- if (!keepInstanceID) {
547
- this.instanceID = `${oldInstanceID}_reload_${Date.now()}`;
548
- }
549
-
550
-
551
-
552
- const savedMetadataState = this.handlers.metadata?.exportUserState?.();
553
-
554
-
555
-
556
- const savedHooks = this.handlers.hookManager?.exportHooks?.();
557
-
558
-
559
- await this.load(this.config, this.instanceID);
560
-
561
-
562
-
563
-
564
-
565
- if (savedMetadataState && this.handlers.metadata) {
566
- this.handlers.metadata.importUserState(savedMetadataState);
567
- }
568
-
569
-
570
-
571
-
572
- if (savedHooks?.length && this.handlers.hookManager) {
573
- this.handlers.hookManager.importHooks(savedHooks);
574
- }
575
-
576
-
577
-
578
-
579
- for (const [, store] of this.contextManager.instances) {
580
- if (store.parentInstanceID === oldInstanceID) {
581
- store.parentInstanceID = this.instanceID;
582
- }
583
- }
584
-
585
-
586
-
587
- if (oldInstanceID && oldInstanceID !== this.instanceID && this.contextManager.instances?.has(oldInstanceID)) {
588
- this.contextManager.cleanup(oldInstanceID);
589
- }
590
-
591
-
592
- for (const operation of operationHistory) {
593
- if (operation.type === "add") {
594
- await this.handlers.apiManager.addApiComponent({
595
- apiPath: operation.apiPath,
596
- folderPath: operation.folderPath,
597
-
598
-
599
- options: { ...(operation.options || {}), recordHistory: false },
600
- moduleID: `replay_${this.helpers.utilities.generateId().substring(0, 8)}`,
601
- versionConfig: operation.versionConfig || null
602
- });
603
-
604
-
605
- } else if (operation.type === "remove") {
606
-
607
-
608
- const { parts } = this.handlers.apiManager.normalizeApiPath(operation.apiPath);
609
- this.handlers.apiManager.deletePath(this.api, parts);
610
-
611
-
612
-
613
- if (this.handlers.metadata) {
614
- const rootSegment = operation.apiPath.split(".")[0];
615
- this.handlers.metadata.removeUserMetadataByApiPath(rootSegment);
616
- }
617
- }
618
- }
619
-
620
- return this.boundApi;
621
- }
622
-
623
-
624
- async _clearModuleCaches() {
625
-
626
-
627
- const targetDir = this.config.dir;
628
- const { resolve } = await import("node:path");
629
- const { createRequire } = await import("node:module");
630
- const require = createRequire(import.meta.url);
631
- const absoluteTargetDir = resolve(targetDir);
632
-
633
-
634
- for (const key of Object.keys(require.cache)) {
635
- if (key.startsWith(absoluteTargetDir)) {
636
- delete require.cache[key];
637
- }
638
- }
639
-
640
-
641
-
642
- }
643
-
644
-
645
- injectRuntimeMetadataFunctions(api) {
646
- if (!api.slothlet?.metadata) {
647
- return;
648
- }
649
-
650
- const metadataHandler = this.handlers.metadata;
651
-
652
-
653
- api.slothlet.metadata.get = async function slothlet_metadata_get_runtime(path) {
654
- return metadataHandler.get(path);
655
- };
656
-
657
- api.slothlet.metadata.self = function slothlet_metadata_self_runtime() {
658
- return metadataHandler.self();
659
- };
660
-
661
- api.slothlet.metadata.caller = function slothlet_metadata_caller_runtime() {
662
- return metadataHandler.caller();
663
- };
664
- }
665
-
666
-
667
- async _drainInFlightLoads() {
668
- if (!this.api) return;
669
-
670
- const pending = [];
671
- const seen = new Set();
672
-
673
-
674
- const collect = (obj, depth = 0) => {
675
-
676
-
677
-
678
-
679
- const objType = typeof obj;
680
- if (!obj || (objType !== "object" && objType !== "function") || depth > 15 || seen.has(obj)) return;
681
- seen.add(obj);
682
-
683
- try {
684
-
685
- if (obj.__isVersionDispatcher === true) return;
686
-
687
-
688
-
689
-
690
-
691
- const wrapper = resolveWrapper(obj);
692
- if (wrapper) {
693
- const mat = wrapper.____slothletInternal.materializationPromise;
694
- if (mat) pending.push(mat);
695
-
696
-
697
-
698
-
699
-
700
-
701
- for (const key of Object.keys(wrapper)) {
702
- if (!key.startsWith("____")) collect(wrapper[key], depth + 1);
703
- }
704
- return;
705
- }
706
-
707
- for (const key of Object.keys(obj)) {
708
- collect(obj[key], depth + 1);
709
- }
710
- } catch {
711
-
712
-
713
- }
714
- };
715
-
716
- for (const key of Object.keys(this.api)) {
717
- collect(this.api[key]);
718
- }
719
-
720
- if (pending.length > 0) {
721
- await Promise.allSettled(pending);
722
- }
723
- }
724
-
725
-
726
- async shutdown() {
727
- if (!this.isLoaded) {
728
- return;
729
- }
730
-
731
-
732
-
733
-
734
- await this._drainInFlightLoads();
735
-
736
-
737
- disableEventEmitterPatching();
738
- cleanupEventEmitterResources();
739
-
740
-
741
- if (this.instanceID && this.contextManager) {
742
- this.contextManager.cleanup(this.instanceID);
743
- }
744
-
745
-
746
-
747
-
748
- if (this.handlers.ownership) {
749
- this.handlers.ownership.clear();
750
- }
751
-
752
-
753
- this.handlers.versionManager?.shutdown();
754
-
755
-
756
-
757
- this.isLoaded = false;
758
- }
759
-
760
-
761
- debug(code, context = {}) {
762
- if (this.debugLogger) {
763
- this.debugLogger.log(code, context);
764
- }
765
- }
766
-
767
-
768
- getAPI() {
769
- if (!this.isLoaded) {
770
- throw new SlothletError(
771
- "INVALID_CONFIG_NOT_LOADED",
772
- {
773
- operation: "getAPI"
774
- },
775
- null,
776
- { validationError: true }
777
- );
778
- }
779
- return this.boundApi;
780
- }
781
-
782
-
783
- getDiagnostics() {
784
- return {
785
- instanceID: this.instanceID,
786
- isLoaded: this.isLoaded,
787
- config: this.config,
788
- context: this.contextManager?.getDiagnostics() || null,
789
- ownership: this.handlers.ownership?.getDiagnostics() || null
790
- };
791
- }
792
-
793
-
794
- getOwnership() {
795
- if (!this.handlers.ownership) {
796
- return null;
797
- }
798
- return this.handlers.ownership.getDiagnostics();
799
- }
800
-
801
-
802
- buildFinalAPI(userApi) {
803
- return this.builders.apiBuilder.buildFinalAPI(userApi);
804
- }
805
- }
806
-
807
-
808
- export async function slothlet(config) {
809
- const instance = new Slothlet();
810
- const api = await instance.load(config);
811
-
812
-
813
- return api;
814
- }
815
-
816
- export default slothlet;
817
-
818
-
819
-
820
-
821
-
822
-
823
-
824
-
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,"")}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||{};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()}`}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"){const{parts}=this.handlers.apiManager.normalizeApiPath(operation.apiPath);this.handlers.apiManager.deletePath(this.api,parts);if(this.handlers.metadata){const rootSegment=operation.apiPath.split(".")[0];this.handlers.metadata.removeUserMetadataByApiPath(rootSegment)}}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();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};