@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.
- package/lib/builders/api-assignment.d.mts +125 -4
- package/lib/builders/api_builder.d.mts +104 -7
- package/lib/builders/builder.d.mts +82 -1
- package/lib/builders/modes-processor.d.mts +66 -3
- package/lib/errors.d.mts +114 -19
- package/lib/factories/component-base.d.mts +171 -8
- package/lib/factories/context.d.mts +22 -4
- package/lib/handlers/api-cache-manager.d.mts +209 -20
- package/lib/handlers/api-manager.d.mts +539 -38
- package/lib/handlers/context-async.d.mts +92 -25
- package/lib/handlers/context-live.d.mts +117 -30
- package/lib/handlers/framework-internals.d.mts +33 -2
- package/lib/handlers/hook-manager.d.mts +306 -73
- package/lib/handlers/lifecycle-token.d.mts +48 -3
- package/lib/handlers/lifecycle.d.mts +86 -5
- package/lib/handlers/materialize-manager.d.mts +76 -8
- package/lib/handlers/metadata.d.mts +238 -18
- package/lib/handlers/module-manager.d.mts +169 -21
- package/lib/handlers/ownership.d.mts +376 -45
- package/lib/handlers/permission-manager.d.mts +283 -46
- package/lib/handlers/routine-manager.d.mts +425 -0
- package/lib/handlers/trusted-root.d.mts +45 -4
- package/lib/handlers/unified-wrapper.d.mts +287 -26
- package/lib/handlers/version-manager.d.mts +236 -29
- package/lib/helpers/caller-pinning.d.mts +21 -2
- package/lib/helpers/class-instance-wrapper.d.mts +56 -2
- package/lib/helpers/config.d.mts +311 -161
- package/lib/helpers/defaults.d.mts +40 -0
- package/lib/helpers/eventemitter-context.d.mts +29 -3
- package/lib/helpers/eventtarget-context.d.mts +19 -1
- package/lib/helpers/eventtarget-property-context.d.mts +21 -0
- package/lib/helpers/generate-manifest.d.mts +174 -7
- package/lib/helpers/hint-detector.d.mts +22 -2
- package/lib/helpers/manifest-resolver.d.mts +100 -1
- package/lib/helpers/modes-utils.d.mts +30 -3
- package/lib/helpers/module-discovery.d.mts +80 -7
- package/lib/helpers/module-manifest-validator.d.mts +36 -13
- package/lib/helpers/module-sort.d.mts +64 -1
- package/lib/helpers/observer-context.d.mts +21 -0
- package/lib/helpers/pattern-matcher.d.mts +43 -3
- package/lib/helpers/platform.d.mts +109 -10
- package/lib/helpers/resolve-from-caller.d.mts +27 -3
- package/lib/helpers/sanitize.d.mts +92 -4
- package/lib/helpers/scheduler-context.d.mts +21 -1
- package/lib/helpers/utilities.d.mts +52 -4
- package/lib/i18n/translations.d.mts +50 -5
- package/lib/modes/eager.d.mts +46 -8
- package/lib/modes/lazy.d.mts +57 -10
- package/lib/processors/flatten.d.mts +116 -56
- package/lib/processors/loader.d.mts +77 -10
- package/lib/processors/type-generator.d.mts +16 -2
- package/lib/processors/typescript.d.mts +169 -13
- package/lib/runtime/runtime-asynclocalstorage.d.mts +71 -3
- package/lib/runtime/runtime-livebindings.d.mts +37 -2
- package/lib/runtime/runtime.d.mts +39 -3
- package/lib/typegen/typegen.d.mts +34 -2
- package/package.json +5 -23
- package/slothlet.d.mts +428 -3
|
@@ -0,0 +1,425 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tracks, per Slothlet instance, every mounted module's raw contribution of a real function at any
|
|
3
|
+
* api path, and resolves — on demand, at rebuild/cascade time, never inside the event handler
|
|
4
|
+
* itself — which configured routine(s) each one matches.
|
|
5
|
+
*
|
|
6
|
+
* @description
|
|
7
|
+
* Interpretation is deliberately deferred: a module's ownership *endpoint* (its own mount root,
|
|
8
|
+
* needed for mount-relative name matching) is not recorded until AFTER all of that module's own
|
|
9
|
+
* `impl:created` events have already fired (see `api-manager.mjs`'s `addApiComponent` —
|
|
10
|
+
* `setModuleEndpoint` runs once, after `buildAPI` has already built and emitted for every leaf).
|
|
11
|
+
* Matching inside the event handler would therefore see `undefined` for a brand-new module's own
|
|
12
|
+
* endpoint on its very first events. Capturing the raw `{apiPath, moduleID, fn}` unconditionally and
|
|
13
|
+
* resolving matches later — once every relevant `setModuleEndpoint` call has definitely run —
|
|
14
|
+
* sidesteps that ordering hazard entirely.
|
|
15
|
+
*
|
|
16
|
+
* @class RoutineManager
|
|
17
|
+
* @extends ComponentBase
|
|
18
|
+
* @public
|
|
19
|
+
*/
|
|
20
|
+
export class RoutineManager extends ComponentBase {
|
|
21
|
+
static slothletProperty: string;
|
|
22
|
+
/**
|
|
23
|
+
* Create a RoutineManager instance.
|
|
24
|
+
* @param {object} slothlet - Slothlet class instance.
|
|
25
|
+
*/
|
|
26
|
+
constructor(slothlet: object);
|
|
27
|
+
/**
|
|
28
|
+
* Raw capture, in arrival (registration) order, deduplicated by `(apiPath, moduleID)`. A
|
|
29
|
+
* re-registration of the same pair (hot-reload, or an incidental re-touch from an unrelated
|
|
30
|
+
* later mount at the same parent path) replaces the existing entry in place — preserving its
|
|
31
|
+
* original position — rather than appending a duplicate.
|
|
32
|
+
* @type {Array<{apiPath: string, moduleID: string, fn: Function}>}
|
|
33
|
+
*/
|
|
34
|
+
raw: Array<{
|
|
35
|
+
apiPath: string;
|
|
36
|
+
moduleID: string;
|
|
37
|
+
fn: Function;
|
|
38
|
+
}>;
|
|
39
|
+
/**
|
|
40
|
+
* Every `raw` entry's originating `UnifiedWrapper`, keyed `moduleID -> apiPath -> wrapper` —
|
|
41
|
+
* populated alongside `raw` in {@link RoutineManager#onImplCreated} purely so a
|
|
42
|
+
* build-attempt-wide revert ({@link RoutineManager#revertSpeculativeState}) can invalidate
|
|
43
|
+
* exactly the wrapper(s) IT speculatively created, without needing a concrete api-tree
|
|
44
|
+
* reference to walk. Never consulted by routine execution itself — `raw`'s own `fn` field
|
|
45
|
+
* stays the single source of truth there. Nested (not a single `` `${moduleID}:${apiPath}` ``
|
|
46
|
+
* string key) because `:` is a valid character in a user-supplied moduleID — a flat string
|
|
47
|
+
* key risked one module's prefix-scan (e.g. `pruneModule("a")`, matching `"a:"`) wrongly
|
|
48
|
+
* catching another module's entries (e.g. moduleID `"a:b"`'s own `"a:b:sub.path"` key)
|
|
49
|
+
* (#372/#373 review, suppressed finding).
|
|
50
|
+
* @type {Map<string, Map<string, object>>}
|
|
51
|
+
*/
|
|
52
|
+
rawWrappers: Map<string, Map<string, object>>;
|
|
53
|
+
/**
|
|
54
|
+
* Recording guard. `rebuildStacks()` (and its narrower reactive counterpart,
|
|
55
|
+
* `#reactivelyPatchStack()` — #362) overwrite live api properties, which re-enters
|
|
56
|
+
* `onImplCreated` via the same `impl:created` event every other write goes through — this
|
|
57
|
+
* flag is turned off for the duration of that overwrite so the stacked callable just
|
|
58
|
+
* installed is never captured as a phantom contributor to its own chain.
|
|
59
|
+
* @type {boolean}
|
|
60
|
+
*/
|
|
61
|
+
recording: boolean;
|
|
62
|
+
/**
|
|
63
|
+
* Compiled-glob-pattern cache, keyed by the raw pattern string (never includes a leading `^`
|
|
64
|
+
* — callers strip that themselves before compiling).
|
|
65
|
+
* @type {Map<string, function(string): boolean>}
|
|
66
|
+
*/
|
|
67
|
+
patternCache: Map<string, (arg0: string) => boolean>;
|
|
68
|
+
/**
|
|
69
|
+
* Discard all captured state. Called at the start of every `load()` (including `reload()`,
|
|
70
|
+
* which re-invokes `load()` on the same instance) so a previous cycle's contributors never
|
|
71
|
+
* bleed into a fresh compose.
|
|
72
|
+
* @returns {void}
|
|
73
|
+
* @public
|
|
74
|
+
*/
|
|
75
|
+
public reset(): void;
|
|
76
|
+
/**
|
|
77
|
+
* Lifecycle subscriber for BOTH `impl:created` and `impl:changed` (subscribed to both in
|
|
78
|
+
* src/slothlet.mjs's `_setupLifecycleSubscribers()`). Captures a contribution's raw
|
|
79
|
+
* `{apiPath, moduleID, fn}` unconditionally — routine-name interpretation happens later, at
|
|
80
|
+
* rebuild/cascade time (see the class-level description for why).
|
|
81
|
+
*
|
|
82
|
+
* @description
|
|
83
|
+
* Reads `data.wrapper.__impl` (present on every such event, in both eager and lazy mode) rather
|
|
84
|
+
* than `data.impl` — `impl:created` fires twice per leaf construction (once with `impl` set to
|
|
85
|
+
* the wrapper itself, once with the raw value for eager-known impls), and `wrapper.__impl` is
|
|
86
|
+
* the one consistent field across every variant. Fires BEFORE collision resolution decides
|
|
87
|
+
* which contributor's value survives onto the composed tree, so every contributor is captured —
|
|
88
|
+
* not just the merge winner.
|
|
89
|
+
*
|
|
90
|
+
* Also subscribed to `impl:changed` so a LATE, direct reassignment (`self.auth.shutdown = fn`,
|
|
91
|
+
* done after the module that owns `auth` finished loading) is captured too, not just the
|
|
92
|
+
* original module-load-time contribution — and, symmetrically, so a later reassignment AWAY
|
|
93
|
+
* from a function (to an object, `null`, or any other non-function value) removes the earlier
|
|
94
|
+
* capture rather than leaving a stale function contribution behind for a cascade to invoke.
|
|
95
|
+
* @param {object} data - `impl:created` / `impl:changed` event payload.
|
|
96
|
+
* @returns {void}
|
|
97
|
+
* @public
|
|
98
|
+
*/
|
|
99
|
+
public onImplCreated(data: object): void;
|
|
100
|
+
/**
|
|
101
|
+
* Lifecycle subscriber: `impl:removed`. Prunes a removed module's raw contribution.
|
|
102
|
+
* @param {object} data - `impl:removed` event payload.
|
|
103
|
+
* @returns {void}
|
|
104
|
+
* @public
|
|
105
|
+
*/
|
|
106
|
+
public onImplRemoved(data: object): void;
|
|
107
|
+
/**
|
|
108
|
+
* Prune every raw-captured contribution at or below a given api path for one moduleID — the
|
|
109
|
+
* scoped-removal analog of {@link RoutineManager#pruneModule}, for when a whole SUBTREE (not
|
|
110
|
+
* just its own top-level property) is deleted from the live tree.
|
|
111
|
+
* @param {string} apiPath - The removed subtree's own root path.
|
|
112
|
+
* @param {string} moduleID - Module identifier whose descendant contributions to prune.
|
|
113
|
+
* @returns {void}
|
|
114
|
+
* @public
|
|
115
|
+
*
|
|
116
|
+
* @description
|
|
117
|
+
* The scoped two-argument `api.remove(apiPath, moduleID)` deletes the ENTIRE live subtree
|
|
118
|
+
* rooted at `apiPath` (`ApiManager#deletePath`), but `impl:removed` only ever fires for the
|
|
119
|
+
* exact property that was deleted — never for descendants that were simply carried away with
|
|
120
|
+
* it. A nested routine capture like `auth.initialize` therefore survived indefinitely in `raw`
|
|
121
|
+
* after removing `auth`, still invoked by `stackRoutines: true`'s cascades even though its
|
|
122
|
+
* whole subtree is gone (#372/#373 review, suppressed finding).
|
|
123
|
+
*
|
|
124
|
+
* @example
|
|
125
|
+
* routineManager.pruneSubtree("auth", moduleID);
|
|
126
|
+
*/
|
|
127
|
+
public pruneSubtree(apiPath: string, moduleID: string): void;
|
|
128
|
+
/**
|
|
129
|
+
* Prune every raw-captured contribution belonging to a module, regardless of whether it was
|
|
130
|
+
* ever the live property at its own path.
|
|
131
|
+
* @param {string} moduleID - Module identifier being fully removed.
|
|
132
|
+
* @returns {void}
|
|
133
|
+
* @public
|
|
134
|
+
*
|
|
135
|
+
* @description
|
|
136
|
+
* `onImplRemoved()` alone is not enough for a whole-module removal: it prunes by (apiPath,
|
|
137
|
+
* moduleID) on the `impl:removed` lifecycle event, which fires only when a property is actually
|
|
138
|
+
* DELETED from the live composed tree. A module that lost a collision (a merge-loser, recorded
|
|
139
|
+
* in ownership but never installed as the live property at its path) is never the live property,
|
|
140
|
+
* so removing it resolves as an ownership "restore" (the current owner's value is re-applied,
|
|
141
|
+
* unchanged) rather than a "delete" — `impl:removed` never fires for the loser's own entry, and
|
|
142
|
+
* its raw contribution would otherwise survive `api.remove()` indefinitely, still invoked under
|
|
143
|
+
* `stackRoutines: true` (#372). Call this alongside `OwnershipManager#unregister()` for a
|
|
144
|
+
* whole-module removal, which already discards every path the module owned regardless of
|
|
145
|
+
* whether the live tree changed for each one.
|
|
146
|
+
*
|
|
147
|
+
* @example
|
|
148
|
+
* ownership.unregister(moduleID);
|
|
149
|
+
* routineManager.pruneModule(moduleID);
|
|
150
|
+
*/
|
|
151
|
+
public pruneModule(moduleID: string): void;
|
|
152
|
+
/**
|
|
153
|
+
* Snapshot the raw contributions moduleID currently has, keyed by apiPath, for later restoration
|
|
154
|
+
* @param {string} moduleID - Module identifier to snapshot.
|
|
155
|
+
* @returns {Map<string, {fn: Function, index: number, wrapper: object|undefined}>} One entry per
|
|
156
|
+
* apiPath the module currently contributes to.
|
|
157
|
+
* @public
|
|
158
|
+
*
|
|
159
|
+
* @description
|
|
160
|
+
* Call this BEFORE a candidate build's construction (buildAPI) runs, so a later revert can tell
|
|
161
|
+
* an apiPath moduleID already genuinely contributed to (whose entry must be restored, not
|
|
162
|
+
* dropped) from one the candidate build's own speculative `impl:created` capture fabricated
|
|
163
|
+
* (which must be discarded outright). Capturing `wrapper` alongside `fn`/`index` lets a restore
|
|
164
|
+
* put the ORIGINAL wrapper back into {@link RoutineManager#rawWrappers} tracking too — otherwise
|
|
165
|
+
* a candidate's own re-touch of the pair overwrites that tracking with its own (about to be
|
|
166
|
+
* invalidated) wrapper, and restoring only `raw` leaves nothing correctly tracked for the
|
|
167
|
+
* pre-candidate contribution (#372/#373 review, suppressed finding).
|
|
168
|
+
*
|
|
169
|
+
* @example
|
|
170
|
+
* const snapshot = routineManager.snapshotRawEntries("same-mod");
|
|
171
|
+
*/
|
|
172
|
+
public snapshotRawEntries(moduleID: string): Map<string, {
|
|
173
|
+
fn: Function;
|
|
174
|
+
index: number;
|
|
175
|
+
wrapper: object | undefined;
|
|
176
|
+
}>;
|
|
177
|
+
/**
|
|
178
|
+
* Snapshot exactly one (apiPath, moduleID) raw entry's function and array position, for a single
|
|
179
|
+
* internal candidate's own revert — the single-path analog of
|
|
180
|
+
* {@link RoutineManager#snapshotRawEntries}.
|
|
181
|
+
* @param {string} apiPath - Full api path the candidate is about to (re-)contribute to.
|
|
182
|
+
* @param {string} moduleID - Module identifier making the contribution.
|
|
183
|
+
* @returns {{fn: Function, index: number, wrapper: object|undefined}|undefined} The prior
|
|
184
|
+
* function, its position in `raw`, and its tracked wrapper (if any) — or `undefined` if none.
|
|
185
|
+
* @public
|
|
186
|
+
*
|
|
187
|
+
* @description
|
|
188
|
+
* `processFiles()`'s internal collision branches each construct a `UnifiedWrapper` (firing
|
|
189
|
+
* `impl:created`, unconditionally capturing into `raw`) BEFORE calling `assignToApiPath()` to
|
|
190
|
+
* learn whether that specific candidate is actually accepted. Call this immediately before
|
|
191
|
+
* constructing the wrapper for one such branch, then {@link RoutineManager#revertRawEntry} after
|
|
192
|
+
* a `false` assignment result, so a skip/warn-rejected internal candidate's raw capture is
|
|
193
|
+
* corrected without needing a whole-module snapshot (#372/#373 review). Capturing `index`
|
|
194
|
+
* alongside `fn` lets a restore re-insert at the original registration position instead of
|
|
195
|
+
* appending, preserving `stackRoutines: true`'s registration-order execution semantics (#372/#373
|
|
196
|
+
* review, suppressed finding).
|
|
197
|
+
*
|
|
198
|
+
* @example
|
|
199
|
+
* const priorEntry = routineManager.snapshotRawEntry("thing.initialize", moduleID);
|
|
200
|
+
* const wrapper = new UnifiedWrapper(...);
|
|
201
|
+
* const assigned = assignToApiPath(targetApi, "thing", wrapper.createProxy(), {...});
|
|
202
|
+
* if (!assigned) routineManager.revertRawEntry("thing.initialize", moduleID, priorEntry);
|
|
203
|
+
*/
|
|
204
|
+
public snapshotRawEntry(apiPath: string, moduleID: string): {
|
|
205
|
+
fn: Function;
|
|
206
|
+
index: number;
|
|
207
|
+
wrapper: object | undefined;
|
|
208
|
+
} | undefined;
|
|
209
|
+
/**
|
|
210
|
+
* Restore or drop exactly one (apiPath, moduleID) raw entry after an internal candidate at that
|
|
211
|
+
* path was rejected — the single-path analog of
|
|
212
|
+
* {@link RoutineManager#revertSpeculativeState}/{@link RoutineManager#revertSpeculativeSubtree}.
|
|
213
|
+
* @param {string} apiPath - Full api path the rejected candidate targeted.
|
|
214
|
+
* @param {string} moduleID - Module identifier the rejected candidate belongs to.
|
|
215
|
+
* @param {{fn: Function, index: number}|undefined} priorEntry - This pair's snapshot from BEFORE
|
|
216
|
+
* the candidate's own wrapper construction ran, from {@link RoutineManager#snapshotRawEntry} —
|
|
217
|
+
* `undefined` when there was no genuine prior contribution (the candidate's capture must be
|
|
218
|
+
* dropped outright).
|
|
219
|
+
* @returns {void}
|
|
220
|
+
* @public
|
|
221
|
+
*
|
|
222
|
+
* @example
|
|
223
|
+
* routineManager.revertRawEntry("thing.initialize", moduleID, priorEntry);
|
|
224
|
+
*/
|
|
225
|
+
public revertRawEntry(apiPath: string, moduleID: string, priorEntry: {
|
|
226
|
+
fn: Function;
|
|
227
|
+
index: number;
|
|
228
|
+
} | undefined): void;
|
|
229
|
+
/**
|
|
230
|
+
* Revert a speculative API subtree's raw routine contributions
|
|
231
|
+
* @param {object} api - API object or subtree (the same candidate value addApiComponent built).
|
|
232
|
+
* @param {string} moduleID - Module identifier whose speculative contributions to revert.
|
|
233
|
+
* @param {string} path - Current API path.
|
|
234
|
+
* @param {Map<string, Function>} priorEntries - Snapshot from
|
|
235
|
+
* {@link RoutineManager#snapshotRawEntries}, taken before the candidate build ran, of what
|
|
236
|
+
* moduleID already genuinely contributed.
|
|
237
|
+
* @param {WeakSet} [visited] - Visited objects (prevents circular refs).
|
|
238
|
+
* @returns {void}
|
|
239
|
+
* @public
|
|
240
|
+
*
|
|
241
|
+
* @description
|
|
242
|
+
* Mirrors OwnershipManager#revertSpeculativeSubtree()'s reasoning for the same underlying cause:
|
|
243
|
+
* `onImplCreated` fires from the SAME `impl:created`/`impl:changed` events during a candidate
|
|
244
|
+
* build's construction, before addApiComponent's own collision decision runs — capturing every
|
|
245
|
+
* constructed wrapper's function into `this.raw` regardless of whether the build is later
|
|
246
|
+
* accepted. Under `stackRoutines: true` (which bypasses ownership filtering entirely), a
|
|
247
|
+
* skip/warn-rejected candidate's raw entry would otherwise still be invoked by root-anchored
|
|
248
|
+
* routines and the exact-path stacked callable, even though its module was never actually
|
|
249
|
+
* mounted. At each level: if `priorEntries` has this exact apiPath, moduleID already
|
|
250
|
+
* contributed to it before this build — restore that function (a later re-registration for the
|
|
251
|
+
* same pair replaces in place, so a rejected candidate's fn would otherwise silently overwrite a
|
|
252
|
+
* genuine, pre-existing contribution). Otherwise the entry is purely speculative — drop it.
|
|
253
|
+
*
|
|
254
|
+
* @example
|
|
255
|
+
* const priorEntries = routineManager.snapshotRawEntries("same-mod");
|
|
256
|
+
* // ...buildAPI runs, candidate is rejected...
|
|
257
|
+
* routineManager.revertSpeculativeSubtree(apiToMerge, "same-mod", "thing", priorEntries);
|
|
258
|
+
*/
|
|
259
|
+
public revertSpeculativeSubtree(api: object, moduleID: string, path: string, priorEntries: Map<string, Function>, visited?: WeakSet<any>): void;
|
|
260
|
+
/**
|
|
261
|
+
* Revert every speculative raw contribution currently on record for a module, driven by the
|
|
262
|
+
* module's own current `raw` entries rather than a candidate api-tree reference
|
|
263
|
+
* @param {string} moduleID - Module identifier whose speculative raw entries to revert.
|
|
264
|
+
* @param {Map<string, Function>} priorEntries - Snapshot from
|
|
265
|
+
* {@link RoutineManager#snapshotRawEntries}, taken before the candidate build ran.
|
|
266
|
+
* @returns {void}
|
|
267
|
+
* @public
|
|
268
|
+
*
|
|
269
|
+
* @description
|
|
270
|
+
* Mirrors `OwnershipManager#revertSpeculativeState()`'s reasoning: `revertSpeculativeSubtree`
|
|
271
|
+
* needs a concrete api-tree value to walk, which may not exist when `buildAPI()` or
|
|
272
|
+
* `setValueAtPath()` throws partway through a candidate build. Reads `this.raw` directly for
|
|
273
|
+
* whatever paths this moduleID currently has an entry at, restoring the ones already present in
|
|
274
|
+
* `priorEntries` and dropping the rest (#372 review).
|
|
275
|
+
*
|
|
276
|
+
* Also invalidates ({@link module:@cldmv/slothlet/handlers/unified-wrapper~UnifiedWrapper#___invalidate})
|
|
277
|
+
* whatever wrapper this now-aborted build attempt itself constructed at each reverted path —
|
|
278
|
+
* `revertSpeculativeSubtree()`'s two callers already pair it with
|
|
279
|
+
* `ApiManager#invalidateSpeculativeWrappers()` on a concrete tree; this is the state-only
|
|
280
|
+
* variant's equivalent, since there is no tree here to walk. Restoring/dropping the raw `fn`
|
|
281
|
+
* alone is not enough when `materializeOnCreate` (`config.backgroundMaterialize`) is set: that
|
|
282
|
+
* wrapper can already be materializing in the background and would otherwise re-apply its
|
|
283
|
+
* result and re-fire `impl:changed` after this rollback (#372/#373 review, suppressed finding).
|
|
284
|
+
* A path this build attempt never touched (`raw`'s current `fn` still equals what `priorEntries`
|
|
285
|
+
* already had) keeps whatever wrapper it already had — only a CHANGED (apiPath, moduleID) pair
|
|
286
|
+
* had its wrapper constructed during this now-abandoned attempt.
|
|
287
|
+
*
|
|
288
|
+
* @example
|
|
289
|
+
* const priorEntries = routineManager.snapshotRawEntries("same-mod");
|
|
290
|
+
* try {
|
|
291
|
+
* // ...buildAPI/setValueAtPath run and throw...
|
|
292
|
+
* } catch (err) {
|
|
293
|
+
* routineManager.revertSpeculativeState("same-mod", priorEntries);
|
|
294
|
+
* throw err;
|
|
295
|
+
* }
|
|
296
|
+
*/
|
|
297
|
+
public revertSpeculativeState(moduleID: string, priorEntries: Map<string, Function>): void;
|
|
298
|
+
/**
|
|
299
|
+
* Run one exact api path's stacked contributors directly (the callable installed at that path
|
|
300
|
+
* by {@link rebuildStacks} delegates here). Every contributor runs regardless of an earlier
|
|
301
|
+
* one's failure; if any failed, one aggregate `ROUTINE_FAILED` error is thrown once all of them
|
|
302
|
+
* have run (see {@link #throwAggregate}).
|
|
303
|
+
* @param {string} apiPath - Exact composed api path.
|
|
304
|
+
* @param {Array} [args] - Arguments forwarded to every contributor.
|
|
305
|
+
* @param {object} [routine] - The specific routine config this callable was built for
|
|
306
|
+
* ({@link #buildStackedCallable}'s own caller, {@link rebuildStacks}, always supplies it).
|
|
307
|
+
* When present, entries are also filtered by {@link #matches} so a mount-relative name
|
|
308
|
+
* pattern belonging to a DIFFERENT routine that happens to resolve to the same exact apiPath
|
|
309
|
+
* (e.g. a root module's bare `"initialize"` and an `api.add()`-mounted module's own
|
|
310
|
+
* `"initialize"`, both composing to the same final path) doesn't invoke that other routine's
|
|
311
|
+
* raw functions too (#366 review).
|
|
312
|
+
* @returns {Promise<*>} The sole contributor's return value, an ordered array of every
|
|
313
|
+
* contributor's return value when there are two or more, or `[]` when there are none (e.g. a
|
|
314
|
+
* stacked callable left in place after its last contributor was removed without an
|
|
315
|
+
* intervening rebuild) — matching {@link runCascade}'s identical "no contributors" contract.
|
|
316
|
+
* @throws {SlothletError} `ROUTINE_FAILED` — see {@link #throwAggregate}.
|
|
317
|
+
* @public
|
|
318
|
+
*/
|
|
319
|
+
public runPath(apiPath: string, args?: any[], routine?: object): Promise<any>;
|
|
320
|
+
/**
|
|
321
|
+
* Run the root cascade for a routine: every matching contribution anywhere, grouped by exact
|
|
322
|
+
* api path — with `stackRoutines: true`, contributors colliding at the same path all run
|
|
323
|
+
* together, adjacently; with the default `stackRoutines: false`, only the current owner at
|
|
324
|
+
* that path runs, exactly like a direct call — the groups themselves ordered per the routine's
|
|
325
|
+
* configured `order` (see {@link #orderPaths}). No-op — and returns `undefined` — when the
|
|
326
|
+
* routine isn't configured.
|
|
327
|
+
*
|
|
328
|
+
* @description
|
|
329
|
+
* Force-materializes whatever the routine's pattern requires (see {@link #materializeFor})
|
|
330
|
+
* before reading `this.raw`, so a not-yet-touched lazy contribution is captured rather than
|
|
331
|
+
* missed. Best-effort across the whole cascade: every matching path's group runs regardless of
|
|
332
|
+
* an earlier group's failure; if any contributor anywhere failed, one aggregate `ROUTINE_FAILED`
|
|
333
|
+
* error is thrown once everything has run (see {@link #throwAggregate}).
|
|
334
|
+
* @param {string} name - Routine name.
|
|
335
|
+
* @param {Array} [args] - Arguments forwarded, unchanged, to EVERY contributor at EVERY matching
|
|
336
|
+
* path — the same broadcast `#runEntries` already gives a single path via {@link runPath}
|
|
337
|
+
* (#362 review: the cascade previously took no arguments at all).
|
|
338
|
+
* @param {boolean} [skipMaterialize=false] - Skip the {@link #materializeFor} call — internal
|
|
339
|
+
* use only, for a caller (`#runModeRoutines`) that already force-materialized this exact
|
|
340
|
+
* routine immediately beforehand and would otherwise re-walk the same tree for no new
|
|
341
|
+
* information. Always leave this `false` for any externally-triggered cascade (the installed
|
|
342
|
+
* `api[name]()` / `api.slothlet[name]()` callables never pass it), since those calls have no
|
|
343
|
+
* such prior guarantee.
|
|
344
|
+
* @returns {Promise<*>} The sole involved path's result, an ordered array of every involved
|
|
345
|
+
* path's result when there are two or more, `[]` when the routine has no contributors
|
|
346
|
+
* anywhere, or `undefined` when the routine isn't configured at all OR the instance has
|
|
347
|
+
* already been destroyed.
|
|
348
|
+
* @throws {SlothletError} `ROUTINE_FAILED` — see {@link #throwAggregate}.
|
|
349
|
+
* @public
|
|
350
|
+
*/
|
|
351
|
+
public runCascade(name: string, args?: any[], skipMaterialize?: boolean): Promise<any>;
|
|
352
|
+
/**
|
|
353
|
+
* Run every configured `mode: "shutdown"` routine's cascade. Called from the framework's
|
|
354
|
+
* existing dispose builtin (`createShutdownFunction()` in api_builder.mjs) — the
|
|
355
|
+
* `"shutdown"`-mode integration point the spec calls "the existing dispose path". Covers the
|
|
356
|
+
* default `shutdown` routine and any custom-named `mode: "shutdown"` routine alike.
|
|
357
|
+
*
|
|
358
|
+
* @description
|
|
359
|
+
* TEMPORARY v3-compat gate (#341): a no-op unless `config.autoRoutines` is `true` (its
|
|
360
|
+
* deprecated alias is `collectLifecycleHooks`, which also expands into an implicit
|
|
361
|
+
* `mode: "shutdown"` routine — see `Config.normalizeRoutines`). Every routine is always
|
|
362
|
+
* stacked/wrapped regardless of this flag — `self.<path>()` is always directly callable — only
|
|
363
|
+
* the AUTOMATIC firing at dispose is gated, so that a project upgrading to a slothlet version
|
|
364
|
+
* carrying #341 sees no behavior change by default. Planned for v4: default `autoRoutines` to
|
|
365
|
+
* `true` and remove `collectLifecycleHooks` — do not lose track of this before that release.
|
|
366
|
+
* @returns {Promise<void>}
|
|
367
|
+
* @public
|
|
368
|
+
*/
|
|
369
|
+
public runShutdownModeRoutines(): Promise<void>;
|
|
370
|
+
/**
|
|
371
|
+
* Run every configured `mode: "destroy"` routine's cascade. Called from the framework's
|
|
372
|
+
* existing dispose builtin (`createDestroyFunction()` in api_builder.mjs) — the "existing
|
|
373
|
+
* dispose path" for `destroy`, replacing what used to be a separate `_collectLifecycleHooks
|
|
374
|
+
* ("destroy")` walk. `destroy()` also calls the root `shutdown()` afterward, so
|
|
375
|
+
* `mode: "shutdown"` routines still run as part of `destroy()` too — this only covers routines
|
|
376
|
+
* a consumer wants to fire on `destroy()` specifically, not on a plain `shutdown()`.
|
|
377
|
+
*
|
|
378
|
+
* @description
|
|
379
|
+
* Same TEMPORARY v3-compat `autoRoutines` gate as {@link runShutdownModeRoutines} — see that
|
|
380
|
+
* method's description for the full rationale and the v4 migration plan.
|
|
381
|
+
* @returns {Promise<void>}
|
|
382
|
+
* @public
|
|
383
|
+
*/
|
|
384
|
+
public runDestroyModeRoutines(): Promise<void>;
|
|
385
|
+
/**
|
|
386
|
+
* Run every configured `mode: "startup"` routine's cascade. Called once, as the final awaited
|
|
387
|
+
* step of `load()` — `await slothlet(...)` resolves only after this completes.
|
|
388
|
+
*
|
|
389
|
+
* @description
|
|
390
|
+
* Same TEMPORARY v3-compat `autoRoutines` gate as {@link runShutdownModeRoutines} — see that
|
|
391
|
+
* method's description for the full rationale and the v4 migration plan. Wrapping still always
|
|
392
|
+
* happens; only automatic firing is gated.
|
|
393
|
+
* @returns {Promise<void>}
|
|
394
|
+
* @public
|
|
395
|
+
*/
|
|
396
|
+
public runStartupModeRoutines(): Promise<void>;
|
|
397
|
+
/**
|
|
398
|
+
* Overwrite every currently-known matching api path's slot on the live api tree with its
|
|
399
|
+
* stacked callable, and (re)attach every configured routine's root cascade at the api root and
|
|
400
|
+
* under `api.slothlet` (using the routine's `name` verbatim as the property key — a dotted or
|
|
401
|
+
* `^`-prefixed name is reachable via bracket notation, e.g. `api.slothlet["admin.initialize"]`,
|
|
402
|
+
* `api["^ext.*.initialize"]`; only a bare name gets clean dot-notation access).
|
|
403
|
+
*
|
|
404
|
+
* @description
|
|
405
|
+
* Safe to call repeatedly — at the end of initial `load()`, again after every
|
|
406
|
+
* `api.slothlet.api.add()`, and immediately before an auto-fired mode cascade — it re-derives
|
|
407
|
+
* every path from current state. A path whose container no longer resolves (its owning module
|
|
408
|
+
* was removed without ever un-registering) is silently skipped rather than throwing: teardown
|
|
409
|
+
* ordering across removal + rebuild is best-effort, not a correctness guarantee this feature
|
|
410
|
+
* makes. Between those trigger points, {@link onImplCreated}'s own narrower
|
|
411
|
+
* `#reactivelyPatchStack()` (#362) keeps a path that gains a second contributor from going
|
|
412
|
+
* stale — see its doc for why a full sweep here isn't needed for that case.
|
|
413
|
+
*
|
|
414
|
+
* Guards writes with `recording = false`: the property write below re-enters `onImplCreated`
|
|
415
|
+
* via the same `impl:created` event ordinary module writes go through, and without the guard
|
|
416
|
+
* the stacked callable being installed would be captured as a phantom contributor on the very
|
|
417
|
+
* next rebuild.
|
|
418
|
+
* @param {object} api - The fully composed, bound api object.
|
|
419
|
+
* @returns {Promise<void>}
|
|
420
|
+
* @public
|
|
421
|
+
*/
|
|
422
|
+
public rebuildStacks(api: object): Promise<void>;
|
|
423
|
+
#private;
|
|
424
|
+
}
|
|
425
|
+
import { ComponentBase } from "#factories/component-base";
|
|
@@ -1,5 +1,46 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Enforce owner-locking on a top-level runtime `context` set-trap write. If `prop` is an owner-locked
|
|
3
|
+
* key on the active store, the write is allowed only when the writing caller matches the declared
|
|
4
|
+
* owner: a `PROTECT_SENTINEL` owner is write-once/unowned (never writable via the set trap), and a
|
|
5
|
+
* named owner (a caller apiPath) permits only the caller whose current identity equals that name.
|
|
6
|
+
*
|
|
7
|
+
* @param {object} ctx - The active context store (carries `__contextOwners` and `currentWrapper`).
|
|
8
|
+
* @param {string|symbol} prop - The context key being written.
|
|
9
|
+
* @returns {void}
|
|
10
|
+
* @throws {SlothletError} CONTEXT_KEY_PROTECTED when the write is not permitted by the key's owner.
|
|
11
|
+
* @internal
|
|
12
|
+
*/
|
|
13
|
+
export function enforceContextKeyWrite(ctx: object, prop: string | symbol): void;
|
|
14
|
+
/**
|
|
15
|
+
* Resolve a runtime `context` get-trap read. For an owner-locked key (declared via
|
|
16
|
+
* `scope({ protect, owners })`) whose value is a plain object or array, returns a recursive protected
|
|
17
|
+
* view so nested writes stay enforced (#207); for every other key it returns the raw value, leaving
|
|
18
|
+
* unprotected context reads (and non-wrappable protected values) exactly as before.
|
|
19
|
+
*
|
|
20
|
+
* @param {object} ctx - The active context store.
|
|
21
|
+
* @param {string|symbol} prop - The context key being read.
|
|
22
|
+
* @param {function(): object|null} getContext - The runtime's active-store resolver, threaded into
|
|
23
|
+
* the view so nested writes resolve the writer at write time (see makeProtectedContextView).
|
|
24
|
+
* @returns {*} The raw value, or a protected view when the key is owner-locked and wrappable.
|
|
25
|
+
* @internal
|
|
26
|
+
*/
|
|
27
|
+
export function readProtectedContextValue(ctx: object, prop: string | symbol, getContext: () => object | null): any;
|
|
28
|
+
/**
|
|
29
|
+
* Marker set on a slothlet instance's base context store to identify host-initiated calls.
|
|
30
|
+
* @type {symbol}
|
|
31
|
+
* @internal
|
|
32
|
+
*/
|
|
33
|
+
export const TRUSTED_ROOT: symbol;
|
|
34
|
+
/**
|
|
35
|
+
* Registry of every genuine `UnifiedWrapper` instance. A caller identity absent from this set is a
|
|
36
|
+
* forged object and is denied by permission enforcement.
|
|
37
|
+
* @type {WeakSet<object>}
|
|
38
|
+
* @internal
|
|
39
|
+
*/
|
|
4
40
|
export const genuineWrappers: WeakSet<object>;
|
|
5
|
-
|
|
41
|
+
/**
|
|
42
|
+
* Sentinel owner for write-once/unowned ("protected") context keys.
|
|
43
|
+
* @type {symbol}
|
|
44
|
+
* @internal
|
|
45
|
+
*/
|
|
46
|
+
export const PROTECT_SENTINEL: symbol;
|