@cldmv/slothlet-types 3.15.2 → 3.16.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.
Files changed (58) hide show
  1. package/lib/builders/api-assignment.d.mts +125 -4
  2. package/lib/builders/api_builder.d.mts +104 -7
  3. package/lib/builders/builder.d.mts +82 -1
  4. package/lib/builders/modes-processor.d.mts +66 -3
  5. package/lib/errors.d.mts +114 -19
  6. package/lib/factories/component-base.d.mts +171 -8
  7. package/lib/factories/context.d.mts +22 -4
  8. package/lib/handlers/api-cache-manager.d.mts +209 -20
  9. package/lib/handlers/api-manager.d.mts +539 -38
  10. package/lib/handlers/context-async.d.mts +92 -25
  11. package/lib/handlers/context-live.d.mts +117 -30
  12. package/lib/handlers/framework-internals.d.mts +33 -2
  13. package/lib/handlers/hook-manager.d.mts +306 -73
  14. package/lib/handlers/lifecycle-token.d.mts +48 -3
  15. package/lib/handlers/lifecycle.d.mts +86 -5
  16. package/lib/handlers/materialize-manager.d.mts +76 -8
  17. package/lib/handlers/metadata.d.mts +238 -18
  18. package/lib/handlers/module-manager.d.mts +169 -21
  19. package/lib/handlers/ownership.d.mts +376 -45
  20. package/lib/handlers/permission-manager.d.mts +283 -46
  21. package/lib/handlers/routine-manager.d.mts +425 -0
  22. package/lib/handlers/trusted-root.d.mts +45 -4
  23. package/lib/handlers/unified-wrapper.d.mts +287 -26
  24. package/lib/handlers/version-manager.d.mts +236 -29
  25. package/lib/helpers/caller-pinning.d.mts +21 -2
  26. package/lib/helpers/class-instance-wrapper.d.mts +56 -2
  27. package/lib/helpers/config.d.mts +311 -161
  28. package/lib/helpers/defaults.d.mts +40 -0
  29. package/lib/helpers/eventemitter-context.d.mts +29 -3
  30. package/lib/helpers/eventtarget-context.d.mts +19 -1
  31. package/lib/helpers/eventtarget-property-context.d.mts +21 -0
  32. package/lib/helpers/generate-manifest.d.mts +174 -7
  33. package/lib/helpers/hint-detector.d.mts +22 -2
  34. package/lib/helpers/manifest-resolver.d.mts +100 -1
  35. package/lib/helpers/modes-utils.d.mts +30 -3
  36. package/lib/helpers/module-discovery.d.mts +80 -7
  37. package/lib/helpers/module-manifest-validator.d.mts +36 -13
  38. package/lib/helpers/module-sort.d.mts +64 -1
  39. package/lib/helpers/observer-context.d.mts +21 -0
  40. package/lib/helpers/pattern-matcher.d.mts +43 -3
  41. package/lib/helpers/platform.d.mts +109 -10
  42. package/lib/helpers/resolve-from-caller.d.mts +27 -3
  43. package/lib/helpers/sanitize.d.mts +92 -4
  44. package/lib/helpers/scheduler-context.d.mts +21 -1
  45. package/lib/helpers/utilities.d.mts +52 -4
  46. package/lib/i18n/translations.d.mts +50 -5
  47. package/lib/modes/eager.d.mts +46 -8
  48. package/lib/modes/lazy.d.mts +57 -10
  49. package/lib/processors/flatten.d.mts +116 -56
  50. package/lib/processors/loader.d.mts +77 -10
  51. package/lib/processors/type-generator.d.mts +16 -2
  52. package/lib/processors/typescript.d.mts +169 -13
  53. package/lib/runtime/runtime-asynclocalstorage.d.mts +71 -3
  54. package/lib/runtime/runtime-livebindings.d.mts +37 -2
  55. package/lib/runtime/runtime.d.mts +39 -3
  56. package/lib/typegen/typegen.d.mts +34 -2
  57. package/package.json +5 -23
  58. package/slothlet.d.mts +428 -3
@@ -1,57 +1,388 @@
1
+ /**
2
+ * Summary result of an unregister operation.
3
+ * @typedef {Object} UnregisterResult
4
+ * @property {string[]} removed - API paths that were removed.
5
+ * @property {Object[]} rolledBack - Entries that were rolled back to a previous owner.
6
+ */
7
+ /**
8
+ * Tracks which modules own which API paths for hot reload and rollback
9
+ * @class OwnershipManager
10
+ * @extends ComponentBase
11
+ * @public
12
+ */
1
13
  export class OwnershipManager extends ComponentBase {
2
14
  static slothletProperty: string;
15
+ /**
16
+ * Create an OwnershipManager instance.
17
+ * @param {object} slothlet - Slothlet class instance.
18
+ */
19
+ constructor(slothlet: object);
3
20
  moduleToPath: Map<any, any>;
4
21
  pathToModule: Map<any, any>;
5
22
  _unregisteredModules: Set<any>;
6
23
  moduleEndpoints: Map<any, any>;
7
- setModuleEndpoint(moduleID: any, endpoint: any): void;
8
- getModuleEndpoint(moduleID: any): any;
9
- register({ moduleID, apiPath, value, source, collisionMode, config, filePath }: {
10
- moduleID: any;
11
- apiPath: any;
24
+ /**
25
+ * Record a module's mount endpoint (the apiPath it was loaded/added at).
26
+ * This is the module's ownership root — the subtree it is allowed to write
27
+ * to via `self.X = …`. Base modules use `"."`.
28
+ * @param {string} moduleID - Module identifier.
29
+ * @param {string} endpoint - Mount endpoint (`"."` for base modules).
30
+ * @returns {void}
31
+ * @public
32
+ */
33
+ public setModuleEndpoint(moduleID: string, endpoint: string): void;
34
+ /**
35
+ * Look up a module's mount endpoint.
36
+ * @param {string} moduleID - Module identifier.
37
+ * @returns {string|undefined} The mount endpoint, or undefined if unknown.
38
+ * @public
39
+ */
40
+ public getModuleEndpoint(moduleID: string): string | undefined;
41
+ /**
42
+ * Register module ownership of API path with its value
43
+ * @param {Object} options - Registration options
44
+ * @param {string} options.moduleID - Module identifier
45
+ * @param {string} options.apiPath - API path being registered
46
+ * @param {*} options.value - The actual function/object being registered
47
+ * @param {string} [options.source="core"] - Source of registration
48
+ * @param {string} [options.collisionMode="error"] - Collision mode: skip, warn, error, merge, replace
49
+ * @param {Object} [options.config] - Config object for silent mode check
50
+ * @param {string} [options.filePath=null] - File path of the module source (for metadata tracking)
51
+ * @returns {Object|null} Registration entry or null if skipped
52
+ * @public
53
+ */
54
+ public register({ moduleID, apiPath, value, source, collisionMode, config, filePath }: {
55
+ moduleID: string;
56
+ apiPath: string;
12
57
  value: any;
13
58
  source?: string | undefined;
14
59
  collisionMode?: string | undefined;
15
- config?: null | undefined;
16
- filePath?: null | undefined;
17
- }): any;
18
- unregister(moduleID: any): {
19
- removed: any[];
20
- rolledBack: {
21
- apiPath: any;
22
- restoredTo: any;
23
- }[];
60
+ config?: Object | undefined;
61
+ filePath?: string | undefined;
62
+ }): Object | null;
63
+ /**
64
+ * @param {string} moduleID - Module to unregister.
65
+ * @returns {UnregisterResult} Removal summary.
66
+ * @public
67
+ *
68
+ * @description
69
+ * Removes all paths owned by the provided moduleID and reports removals and rollbacks.
70
+ *
71
+ * @example
72
+ * const result = ownership.unregister("module-a");
73
+ */
74
+ public unregister(moduleID: string): UnregisterResult;
75
+ /**
76
+ * Block a moduleID from any further path registration without removing its current paths.
77
+ *
78
+ * @param {string} moduleID - Module to block from re-registration.
79
+ * @returns {void}
80
+ * @public
81
+ *
82
+ * @description
83
+ * Sets the same async-race guard {@link OwnershipManager#unregister} sets, but standalone: the scoped
84
+ * `remove(moduleID, apiPath)` path detaches nodes one at a time via {@link OwnershipManager#removePath}
85
+ * and never calls `unregister`. Tearing down a lazy node materializes it, and that materialization can
86
+ * register previously-unregistered descendants (a lazy submodule's leaves) AFTER the removal's target
87
+ * list was computed — which, on a reload replay, leak back and resurrect the removed subtree. When a
88
+ * scoped removal empties a module, call this BEFORE the walk so those late registrations are rejected
89
+ * (register() returns null for a module in this set). Cleared on the next {@link OwnershipManager#clear}
90
+ * (reload). Only for a full removal — a partial one keeps sibling paths that must still materialize.
91
+ *
92
+ * Because this is called only when the module is fully gone, it also drops its {@link OwnershipManager#moduleEndpoints}
93
+ * entry — `removePath()` deletes the emptied `moduleToPath` set but not the endpoint, and `unregister()`
94
+ * (the moduleID-only remove's analog) does delete it, so the scoped full-removal must too or a stale
95
+ * endpoint leaks until the next reload.
96
+ *
97
+ * @example
98
+ * ownership.markUnregistered("plugins-core");
99
+ */
100
+ public markUnregistered(moduleID: string): void;
101
+ /**
102
+ * Re-arm a moduleID for registration after a prior removal, for a deliberate new `api.add()`.
103
+ * @param {string} moduleID - Module identifier about to be (re-)registered.
104
+ * @returns {void}
105
+ * @public
106
+ *
107
+ * @description
108
+ * `register()`'s guard against `_unregisteredModules` (see its own doc comment) is meant to
109
+ * reject a STALE, late-arriving registration from a removed module's own in-flight lazy
110
+ * materialization — not to permanently block that moduleID from ever registering again. Without
111
+ * this call, a deliberate `api.add()` reusing a moduleID that was previously removed had every
112
+ * one of its registrations silently dropped (`register()` returns `null` unconditionally),
113
+ * losing ownership tracking entirely for content that WAS actually assigned onto the live tree —
114
+ * confirmed via a remove-then-re-add-same-moduleID repro (#372 review, suppressed finding on
115
+ * ownership.mjs's merge-loss correction). Called at the very start of `addApiComponent()`, before
116
+ * any registration for this build, so a genuinely new add's own registrations are never rejected;
117
+ * a stale materialization from the module's PREVIOUS lifetime that fires after this point is a
118
+ * separate, pre-existing race this call does not change the risk profile of.
119
+ *
120
+ * @example
121
+ * ownership.clearUnregistered("plugins-core");
122
+ */
123
+ public clearUnregistered(moduleID: string): void;
124
+ /**
125
+ * @param {string} apiPath - API path to modify.
126
+ * @param {string|null} [moduleID=null] - Module to remove (defaults to current owner).
127
+ * @returns {{ action: "delete"|"none"|"restore", removedModuleId: string|null,
128
+ * restoreModuleId: string|null }} Action taken for the path.
129
+ * @public
130
+ *
131
+ * @description
132
+ * Removes a module owner from a specific API path. If the current owner is removed and
133
+ * previous owners exist, the path is restored to the previous owner.
134
+ *
135
+ * @example
136
+ * const result = ownership.removePath("plugins.tools", "module-a");
137
+ */
138
+ public removePath(apiPath: string, moduleID?: string | null): {
139
+ action: "delete" | "none" | "restore";
140
+ removedModuleId: string | null;
141
+ restoreModuleId: string | null;
24
142
  };
25
- markUnregistered(moduleID: any): void;
26
- removePath(apiPath: any, moduleID?: null): {
27
- action: string;
28
- removedModuleId: any;
29
- restoreModuleId: any;
30
- };
31
- getCurrentOwner(apiPath: any): any;
32
- getCurrentValue(apiPath: any): any;
33
- getModulePaths(moduleID: any): any[];
34
- getPathHistory(apiPath: any): any;
35
- ownsPath(moduleID: any, apiPath: any): any;
36
- getDiagnostics(): {
37
- totalModules: number;
38
- totalPaths: number;
39
- modules: {
40
- moduleID: any;
41
- pathCount: any;
42
- }[];
43
- conflictedPaths: {
44
- apiPath: any;
45
- ownerStack: any;
46
- }[];
47
- };
48
- getPathOwnership(apiPath: any): Set<any> | null;
49
- registerSubtree(api: any, moduleID: any, path: any, visited?: WeakSet<object>): void;
50
- clear(): void;
51
- exportState(): {
52
- moduleToPath: any[][];
53
- pathToModule: [any, any][];
54
- };
55
- importState(state: any): void;
143
+ /**
144
+ * Get current owner of API path
145
+ * @param {string} apiPath - API path to check
146
+ * @returns {Object|null} Current owner entry or null
147
+ * @public
148
+ */
149
+ public getCurrentOwner(apiPath: string): Object | null;
150
+ /**
151
+ * Get current value for API path
152
+ * @param {string} apiPath - API path to check
153
+ * @returns {*} Current value or undefined
154
+ * @public
155
+ */
156
+ public getCurrentValue(apiPath: string): any;
157
+ /**
158
+ * Get all paths owned by module
159
+ * @param {string} moduleID - Module to query
160
+ * @returns {Array<string>} Array of API paths
161
+ * @public
162
+ */
163
+ public getModulePaths(moduleID: string): Array<string>;
164
+ /**
165
+ * Get ownership history for path
166
+ * @param {string} apiPath - API path to query
167
+ * @returns {Array<Object>} Ownership history stack
168
+ * @public
169
+ */
170
+ public getPathHistory(apiPath: string): Array<Object>;
171
+ /**
172
+ * Check if module owns path
173
+ * @param {string} moduleID - Module to check
174
+ * @param {string} apiPath - API path to check
175
+ * @returns {boolean} True if module owns path
176
+ * @public
177
+ */
178
+ public ownsPath(moduleID: string, apiPath: string): boolean;
179
+ /**
180
+ * Get diagnostic info about ownership
181
+ * @returns {Object} Diagnostic information
182
+ * @public
183
+ */
184
+ public getDiagnostics(): Object;
185
+ /**
186
+ * Get ownership info for a specific API path
187
+ * @param {string} apiPath - API path to check
188
+ * @returns {Set<string>|null} Set of moduleIDs that own this path, or null if path not found
189
+ * @public
190
+ */
191
+ public getPathOwnership(apiPath: string): Set<string> | null;
192
+ /**
193
+ * Recursively register API subtree with ownership
194
+ * @param {object} api - API object or subtree
195
+ * @param {string} moduleID - Module identifier (owner)
196
+ * @param {string} path - Current API path
197
+ * @param {WeakSet} [visited] - Visited objects (prevents circular refs)
198
+ * @returns {void}
199
+ * @public
200
+ *
201
+ * @description
202
+ * Registers entire API subtree structure with ownership manager.
203
+ * Used during load, reload, and api.add to establish ownership relationships.
204
+ *
205
+ * @example
206
+ * ownership.registerSubtree(api, "base_abc123", "");
207
+ */
208
+ public registerSubtree(api: object, moduleID: string, path: string, visited?: WeakSet<any>): void;
209
+ /**
210
+ * Snapshot the entries moduleID currently owns, keyed by apiPath, for later restoration
211
+ * @param {string} moduleID - Module identifier to snapshot.
212
+ * @returns {Map<string, {value: *, filePath: (string|null), source: string, isMergeLoss: boolean}>}
213
+ * One entry per apiPath the module currently owns, capturing exactly the fields a duplicate
214
+ * registration can overwrite.
215
+ * @public
216
+ *
217
+ * @description
218
+ * Call this BEFORE a candidate build's construction (buildAPI) runs, so a later revert can tell
219
+ * a path moduleID genuinely already owned (whose entry must be restored, not deleted) from one
220
+ * the candidate build's own speculative registration fabricated (which must be deleted outright).
221
+ *
222
+ * @example
223
+ * const snapshot = ownership.snapshotModuleEntries("same-mod");
224
+ */
225
+ public snapshotModuleEntries(moduleID: string): Map<string, {
226
+ value: any;
227
+ filePath: (string | null);
228
+ source: string;
229
+ isMergeLoss: boolean;
230
+ }>;
231
+ /**
232
+ * Restore a single entry's value/filePath/source/isMergeLoss, undoing a later registration's
233
+ * overwrite without changing its position in the ownership stack
234
+ * @param {string} moduleID - Module identifier.
235
+ * @param {string} apiPath - API path whose entry to restore.
236
+ * @param {{value: *, filePath: (string|null), source: string, isMergeLoss: boolean}} snapshot -
237
+ * Prior field values, from {@link OwnershipManager#snapshotModuleEntries}.
238
+ * @returns {void}
239
+ * @public
240
+ *
241
+ * @example
242
+ * ownership.restoreEntry("same-mod", "thing", snapshot.get("thing"));
243
+ */
244
+ public restoreEntry(moduleID: string, apiPath: string, snapshot: {
245
+ value: any;
246
+ filePath: (string | null);
247
+ source: string;
248
+ isMergeLoss: boolean;
249
+ }): void;
250
+ /**
251
+ * Snapshot exactly one (apiPath, moduleID) pair's current entry — the single-path analog of
252
+ * {@link OwnershipManager#snapshotModuleEntries}, for an internal candidate's own revert.
253
+ * @param {string} apiPath - Full api path the candidate is about to (re-)contribute to.
254
+ * @param {string} moduleID - Module identifier making the contribution.
255
+ * @returns {{value: *, filePath: (string|null), source: string, isMergeLoss: boolean}|undefined}
256
+ * The prior entry's snapshot, or `undefined` if none exists yet.
257
+ * @public
258
+ *
259
+ * @description
260
+ * `ModesProcessor`'s internal collision branches each construct a `UnifiedWrapper` (firing
261
+ * `impl:created` unconditionally) BEFORE `assignToApiPath()`'s real, per-call-aware collision
262
+ * decision is known. The generic `impl:created` subscriber (`src/slothlet.mjs`) reacts to that
263
+ * same construction and registers ownership using the INSTANCE's configured default mode,
264
+ * clamped to `"replace"`/`"merge-replace"` only (never `"skip"`/`"warn"`/`"error"`, exactly like
265
+ * `ModesProcessor#resolveOwnershipCollisionMode` clamps its own authoritative registration) —
266
+ * so that call always succeeds, regardless of what the real per-call mode later turns out to
267
+ * be. A `skip`/`warn`-rejected (or thrown) internal candidate therefore leaves a real,
268
+ * unrevertable ownership entry behind unless the caller snapshots-before/restores-or-drops-after
269
+ * around its own construction+assignment attempt (#372/#373 review, suppressed finding).
270
+ *
271
+ * @example
272
+ * const priorEntry = ownership.snapshotPathEntry("thing.initialize", moduleID);
273
+ * // ...wrapper construction + assignToApiPath() run...
274
+ * if (!assigned) {
275
+ * if (priorEntry) ownership.restoreEntry(moduleID, "thing.initialize", priorEntry);
276
+ * else ownership.removePath("thing.initialize", moduleID);
277
+ * }
278
+ */
279
+ public snapshotPathEntry(apiPath: string, moduleID: string): {
280
+ value: any;
281
+ filePath: (string | null);
282
+ source: string;
283
+ isMergeLoss: boolean;
284
+ } | undefined;
285
+ /**
286
+ * Revert a speculative API subtree's ownership registrations
287
+ * @param {object} api - API object or subtree (same shape registerSubtree() would have walked)
288
+ * @param {string} moduleID - Module identifier whose speculative registrations to revert
289
+ * @param {string} path - Current API path
290
+ * @param {Map<string, {value: *, filePath: (string|null), source: string}>} priorEntries -
291
+ * Snapshot from {@link OwnershipManager#snapshotModuleEntries}, taken before the candidate
292
+ * build ran, of what moduleID already legitimately owned.
293
+ * @param {WeakSet} [visited] - Visited objects (prevents circular refs)
294
+ * @returns {void}
295
+ * @public
296
+ *
297
+ * @description
298
+ * Mirrors registerSubtree()'s traversal. A candidate build's wrapper construction fires
299
+ * impl:created before the caller's own collision decision runs (buildAPI's apiPathPrefix
300
+ * already targets the final mount path), so the framework's generic impl:created subscriber
301
+ * (slothlet.mjs) auto-registers ownership for it — clamped to "merge" so it never throws —
302
+ * even when that build is a hot-reload api.add() candidate still pending its own
303
+ * setValueAtPath check. When that check then rejects the assignment under skip/warn, the live
304
+ * api tree is untouched but the speculative registration is not (#366 review). At each level:
305
+ * if `priorEntries` has this exact path, moduleID already owned it before this build — restore
306
+ * its value/filePath/source (register()'s duplicate-entry path overwrote them unconditionally,
307
+ * even for what turned out to be a rejected candidate), rather than deleting a genuine,
308
+ * pre-existing registration. Otherwise the path is purely speculative — remove it outright.
309
+ *
310
+ * @example
311
+ * const priorEntries = ownership.snapshotModuleEntries("same-mod");
312
+ * // ...buildAPI runs, candidate is rejected...
313
+ * ownership.revertSpeculativeSubtree(apiToMerge, "same-mod", "thing", priorEntries);
314
+ */
315
+ public revertSpeculativeSubtree(api: object, moduleID: string, path: string, priorEntries: Map<string, {
316
+ value: any;
317
+ filePath: (string | null);
318
+ source: string;
319
+ }>, visited?: WeakSet<any>): void;
320
+ /**
321
+ * Revert every speculative registration currently on record for a module, driven by the
322
+ * module's own current ownership state rather than a candidate api-tree reference
323
+ * @param {string} moduleID - Module identifier whose speculative state to revert.
324
+ * @param {Map<string, {value: *, filePath: (string|null), source: string, isMergeLoss: boolean}>} priorEntries -
325
+ * Snapshot from {@link OwnershipManager#snapshotModuleEntries}, taken before the candidate
326
+ * build ran.
327
+ * @returns {void}
328
+ * @public
329
+ *
330
+ * @description
331
+ * {@link OwnershipManager#revertSpeculativeSubtree} needs a concrete api-tree value to walk —
332
+ * fine when the caller has one (a rejected `skip`/`warn` candidate whose `apiToMerge` was still
333
+ * built successfully). It has nothing to walk when `buildAPI()` or `setValueAtPath()` itself
334
+ * THROWS (a genuine `collisionMode: "error"` collision, or any other failure) partway through —
335
+ * the candidate's speculative registrations still exist (whatever fired `impl:created` before
336
+ * the throw), but there may be no valid `newApi`/`apiToMerge` reference left to walk. This reads
337
+ * `moduleToPath.get(moduleID)` directly instead: whatever paths this moduleID currently owns,
338
+ * restore the ones already present in `priorEntries` and delete the rest — the same outcome as
339
+ * `revertSpeculativeSubtree`, without needing the tree shape at all (#372 review).
340
+ *
341
+ * @example
342
+ * const priorEntries = ownership.snapshotModuleEntries("same-mod");
343
+ * try {
344
+ * // ...buildAPI/setValueAtPath run and throw...
345
+ * } catch (err) {
346
+ * ownership.revertSpeculativeState("same-mod", priorEntries);
347
+ * throw err;
348
+ * }
349
+ */
350
+ public revertSpeculativeState(moduleID: string, priorEntries: Map<string, {
351
+ value: any;
352
+ filePath: (string | null);
353
+ source: string;
354
+ isMergeLoss: boolean;
355
+ }>): void;
356
+ /**
357
+ * Clear all ownership data
358
+ * @public
359
+ */
360
+ public clear(): void;
361
+ /**
362
+ * Export ownership state for preservation during reload
363
+ * @returns {Object} Serializable ownership state
364
+ * @public
365
+ */
366
+ public exportState(): Object;
367
+ /**
368
+ * Import ownership state from exported data
369
+ * @param {Object} state - Previously exported state
370
+ * @public
371
+ */
372
+ public importState(state: Object): void;
373
+ #private;
56
374
  }
375
+ /**
376
+ * Summary result of an unregister operation.
377
+ */
378
+ export type UnregisterResult = {
379
+ /**
380
+ * - API paths that were removed.
381
+ */
382
+ removed: string[];
383
+ /**
384
+ * - Entries that were rolled back to a previous owner.
385
+ */
386
+ rolledBack: Object[];
387
+ };
57
388
  import { ComponentBase } from "#factories/component-base";