@cldmv/slothlet 3.3.0 → 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 (127) hide show
  1. package/README.md +6 -8
  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 -1385
  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 -2536
  12. package/dist/lib/handlers/context-async.mjs +1 -172
  13. package/dist/lib/handlers/context-live.mjs +1 -173
  14. package/dist/lib/handlers/hook-manager.mjs +1 -667
  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 +1 -392
  21. package/dist/lib/handlers/unified-wrapper.mjs +1 -3110
  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 -439
  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 +1 -125
  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/translations.mjs +1 -126
  33. package/dist/lib/modes/eager.mjs +1 -59
  34. package/dist/lib/modes/lazy.mjs +1 -81
  35. package/dist/lib/processors/flatten.mjs +1 -437
  36. package/dist/lib/processors/loader.mjs +1 -339
  37. package/dist/lib/processors/type-generator.mjs +1 -275
  38. package/dist/lib/processors/typescript.mjs +1 -172
  39. package/dist/lib/runtime/runtime-asynclocalstorage.mjs +1 -113
  40. package/dist/lib/runtime/runtime-livebindings.mjs +1 -78
  41. package/dist/lib/runtime/runtime.mjs +1 -102
  42. package/dist/slothlet.mjs +1 -817
  43. package/package.json +34 -31
  44. package/types/dist/lib/builders/api-assignment.d.mts +3 -92
  45. package/types/dist/lib/builders/api-assignment.d.mts.map +1 -1
  46. package/types/dist/lib/builders/api_builder.d.mts +102 -91
  47. package/types/dist/lib/builders/api_builder.d.mts.map +1 -1
  48. package/types/dist/lib/builders/builder.d.mts +1 -55
  49. package/types/dist/lib/builders/builder.d.mts.map +1 -1
  50. package/types/dist/lib/builders/modes-processor.d.mts +3 -27
  51. package/types/dist/lib/builders/modes-processor.d.mts.map +1 -1
  52. package/types/dist/lib/errors.d.mts +19 -109
  53. package/types/dist/lib/errors.d.mts.map +1 -1
  54. package/types/dist/lib/factories/component-base.d.mts +7 -177
  55. package/types/dist/lib/factories/component-base.d.mts.map +1 -1
  56. package/types/dist/lib/factories/context.d.mts +4 -22
  57. package/types/dist/lib/factories/context.d.mts.map +1 -1
  58. package/types/dist/lib/handlers/api-cache-manager.d.mts +20 -203
  59. package/types/dist/lib/handlers/api-cache-manager.d.mts.map +1 -1
  60. package/types/dist/lib/handlers/api-manager.d.mts +33 -408
  61. package/types/dist/lib/handlers/api-manager.d.mts.map +1 -1
  62. package/types/dist/lib/handlers/context-async.d.mts +23 -61
  63. package/types/dist/lib/handlers/context-async.d.mts.map +1 -1
  64. package/types/dist/lib/handlers/context-live.d.mts +22 -59
  65. package/types/dist/lib/handlers/context-live.d.mts.map +1 -1
  66. package/types/dist/lib/handlers/hook-manager.d.mts +46 -185
  67. package/types/dist/lib/handlers/hook-manager.d.mts.map +1 -1
  68. package/types/dist/lib/handlers/lifecycle-token.d.mts +3 -48
  69. package/types/dist/lib/handlers/lifecycle-token.d.mts.map +1 -1
  70. package/types/dist/lib/handlers/lifecycle.d.mts +5 -82
  71. package/types/dist/lib/handlers/lifecycle.d.mts.map +1 -1
  72. package/types/dist/lib/handlers/materialize-manager.d.mts +8 -70
  73. package/types/dist/lib/handlers/materialize-manager.d.mts.map +1 -1
  74. package/types/dist/lib/handlers/metadata.d.mts +17 -221
  75. package/types/dist/lib/handlers/metadata.d.mts.map +1 -1
  76. package/types/dist/lib/handlers/ownership.d.mts +44 -160
  77. package/types/dist/lib/handlers/ownership.d.mts.map +1 -1
  78. package/types/dist/lib/handlers/permission-manager.d.mts +37 -141
  79. package/types/dist/lib/handlers/permission-manager.d.mts.map +1 -1
  80. package/types/dist/lib/handlers/unified-wrapper.d.mts +26 -239
  81. package/types/dist/lib/handlers/unified-wrapper.d.mts.map +1 -1
  82. package/types/dist/lib/handlers/version-manager.d.mts +28 -225
  83. package/types/dist/lib/handlers/version-manager.d.mts.map +1 -1
  84. package/types/dist/lib/helpers/class-instance-wrapper.d.mts +2 -52
  85. package/types/dist/lib/helpers/class-instance-wrapper.d.mts.map +1 -1
  86. package/types/dist/lib/helpers/config.d.mts +125 -139
  87. package/types/dist/lib/helpers/config.d.mts.map +1 -1
  88. package/types/dist/lib/helpers/eventemitter-context.d.mts +3 -29
  89. package/types/dist/lib/helpers/eventemitter-context.d.mts.map +1 -1
  90. package/types/dist/lib/helpers/hint-detector.d.mts +2 -15
  91. package/types/dist/lib/helpers/hint-detector.d.mts.map +1 -1
  92. package/types/dist/lib/helpers/modes-utils.d.mts +3 -30
  93. package/types/dist/lib/helpers/modes-utils.d.mts.map +1 -1
  94. package/types/dist/lib/helpers/pattern-matcher.d.mts +3 -43
  95. package/types/dist/lib/helpers/pattern-matcher.d.mts.map +1 -1
  96. package/types/dist/lib/helpers/resolve-from-caller.d.mts +3 -27
  97. package/types/dist/lib/helpers/resolve-from-caller.d.mts.map +1 -1
  98. package/types/dist/lib/helpers/sanitize.d.mts +4 -92
  99. package/types/dist/lib/helpers/sanitize.d.mts.map +1 -1
  100. package/types/dist/lib/helpers/utilities.d.mts +4 -52
  101. package/types/dist/lib/helpers/utilities.d.mts.map +1 -1
  102. package/types/dist/lib/i18n/translations.d.mts +4 -37
  103. package/types/dist/lib/i18n/translations.d.mts.map +1 -1
  104. package/types/dist/lib/modes/eager.d.mts +8 -30
  105. package/types/dist/lib/modes/eager.d.mts.map +1 -1
  106. package/types/dist/lib/modes/lazy.d.mts +10 -43
  107. package/types/dist/lib/modes/lazy.d.mts.map +1 -1
  108. package/types/dist/lib/processors/flatten.d.mts +56 -107
  109. package/types/dist/lib/processors/flatten.d.mts.map +1 -1
  110. package/types/dist/lib/processors/loader.d.mts +6 -41
  111. package/types/dist/lib/processors/loader.d.mts.map +1 -1
  112. package/types/dist/lib/processors/type-generator.d.mts +2 -16
  113. package/types/dist/lib/processors/type-generator.d.mts.map +1 -1
  114. package/types/dist/lib/processors/typescript.d.mts +6 -53
  115. package/types/dist/lib/processors/typescript.d.mts.map +1 -1
  116. package/types/dist/lib/runtime/runtime-asynclocalstorage.d.mts +3 -71
  117. package/types/dist/lib/runtime/runtime-asynclocalstorage.d.mts.map +1 -1
  118. package/types/dist/lib/runtime/runtime-livebindings.d.mts +2 -37
  119. package/types/dist/lib/runtime/runtime-livebindings.d.mts.map +1 -1
  120. package/types/dist/lib/runtime/runtime.d.mts +3 -39
  121. package/types/dist/lib/runtime/runtime.d.mts.map +1 -1
  122. package/types/dist/slothlet.d.mts +3 -249
  123. package/types/dist/slothlet.d.mts.map +1 -1
  124. package/types/index.d.mts +36 -16
  125. package/types/index.d.mts.map +1 -0
  126. package/AGENT-USAGE.md +0 -736
  127. package/docs/API-RULES.md +0 -712
package/dist/slothlet.mjs CHANGED
@@ -14,820 +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
- } else if (operation.type === "remove") {
604
-
605
-
606
- const { parts } = this.handlers.apiManager.normalizeApiPath(operation.apiPath);
607
- this.handlers.apiManager.deletePath(this.api, parts);
608
-
609
-
610
-
611
- if (this.handlers.metadata) {
612
- const rootSegment = operation.apiPath.split(".")[0];
613
- this.handlers.metadata.removeUserMetadataByApiPath(rootSegment);
614
- }
615
- } else if (operation.type === "addPermissionRule") {
616
- if (this.handlers.permissionManager) {
617
- this.handlers.permissionManager.addRule(operation.rule, operation.ownerModuleID, operation.ruleId);
618
- }
619
- } else if (operation.type === "removePermissionRule") {
620
- if (this.handlers.permissionManager) {
621
- this.handlers.permissionManager.removeRule(operation.ruleId, operation.callerModuleID);
622
- }
623
- }
624
- }
625
-
626
- return this.boundApi;
627
- }
628
-
629
-
630
- async _clearModuleCaches() {
631
-
632
-
633
- const targetDir = this.config.dir;
634
- const { resolve } = await import("node:path");
635
- const { createRequire } = await import("node:module");
636
- const require = createRequire(import.meta.url);
637
- const absoluteTargetDir = resolve(targetDir);
638
-
639
-
640
- for (const key of Object.keys(require.cache)) {
641
- if (key.startsWith(absoluteTargetDir)) {
642
- delete require.cache[key];
643
- }
644
- }
645
-
646
-
647
-
648
- }
649
-
650
-
651
- injectRuntimeMetadataFunctions(api) {
652
- if (!api.slothlet?.metadata) {
653
- return;
654
- }
655
-
656
- const metadataHandler = this.handlers.metadata;
657
-
658
-
659
- api.slothlet.metadata.get = async function slothlet_metadata_get_runtime(path) {
660
- return metadataHandler.get(path);
661
- };
662
-
663
- api.slothlet.metadata.self = function slothlet_metadata_self_runtime() {
664
- return metadataHandler.self();
665
- };
666
-
667
- api.slothlet.metadata.caller = function slothlet_metadata_caller_runtime() {
668
- return metadataHandler.caller();
669
- };
670
- }
671
-
672
-
673
- async _drainInFlightLoads() {
674
- if (!this.api) return;
675
-
676
- const pending = [];
677
- const seen = new Set();
678
-
679
-
680
- const collect = (obj, depth = 0) => {
681
-
682
-
683
-
684
-
685
- const objType = typeof obj;
686
- if (!obj || (objType !== "object" && objType !== "function") || depth > 15 || seen.has(obj)) return;
687
- seen.add(obj);
688
-
689
- try {
690
-
691
- if (obj.__isVersionDispatcher === true) return;
692
-
693
-
694
-
695
-
696
-
697
- const wrapper = resolveWrapper(obj);
698
- if (wrapper) {
699
- const mat = wrapper.____slothletInternal.materializationPromise;
700
- if (mat) pending.push(mat);
701
-
702
-
703
-
704
-
705
-
706
-
707
- for (const key of Object.keys(wrapper)) {
708
- if (!key.startsWith("____")) collect(wrapper[key], depth + 1);
709
- }
710
- return;
711
- }
712
-
713
- for (const key of Object.keys(obj)) {
714
- collect(obj[key], depth + 1);
715
- }
716
- } catch {
717
-
718
-
719
- }
720
- };
721
-
722
- for (const key of Object.keys(this.api)) {
723
- collect(this.api[key]);
724
- }
725
-
726
- if (pending.length > 0) {
727
- await Promise.allSettled(pending);
728
- }
729
- }
730
-
731
-
732
- async shutdown() {
733
- if (!this.isLoaded) {
734
- return;
735
- }
736
-
737
-
738
-
739
-
740
- await this._drainInFlightLoads();
741
-
742
-
743
- disableEventEmitterPatching();
744
- cleanupEventEmitterResources();
745
-
746
-
747
- if (this.instanceID && this.contextManager) {
748
- this.contextManager.cleanup(this.instanceID);
749
- }
750
-
751
-
752
-
753
-
754
- if (this.handlers.ownership) {
755
- this.handlers.ownership.clear();
756
- }
757
-
758
-
759
- this.handlers.versionManager?.shutdown();
760
-
761
-
762
- await this.handlers.permissionManager?.shutdown();
763
-
764
-
765
-
766
- this.isLoaded = false;
767
- }
768
-
769
-
770
- debug(code, context = {}) {
771
- if (this.debugLogger) {
772
- this.debugLogger.log(code, context);
773
- }
774
- }
775
-
776
-
777
- getAPI() {
778
- if (!this.isLoaded) {
779
- throw new SlothletError(
780
- "INVALID_CONFIG_NOT_LOADED",
781
- {
782
- operation: "getAPI"
783
- },
784
- null,
785
- { validationError: true }
786
- );
787
- }
788
- return this.boundApi;
789
- }
790
-
791
-
792
- getDiagnostics() {
793
- return {
794
- instanceID: this.instanceID,
795
- isLoaded: this.isLoaded,
796
- config: this.config,
797
- context: this.contextManager?.getDiagnostics() || null,
798
- ownership: this.handlers.ownership?.getDiagnostics() || null
799
- };
800
- }
801
-
802
-
803
- getOwnership() {
804
- if (!this.handlers.ownership) {
805
- return null;
806
- }
807
- return this.handlers.ownership.getDiagnostics();
808
- }
809
-
810
-
811
- buildFinalAPI(userApi) {
812
- return this.builders.apiBuilder.buildFinalAPI(userApi);
813
- }
814
- }
815
-
816
-
817
- export async function slothlet(config) {
818
- const instance = new Slothlet();
819
- const api = await instance.load(config);
820
-
821
-
822
- return api;
823
- }
824
-
825
- export default slothlet;
826
-
827
-
828
-
829
-
830
-
831
-
832
-
833
-
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};