@cldmv/slothlet 3.2.3 → 3.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +22 -9
- package/REFERENCE.md +23 -0
- package/dist/lib/builders/api-assignment.mjs +1 -589
- package/dist/lib/builders/api_builder.mjs +1 -1155
- package/dist/lib/builders/builder.mjs +1 -78
- package/dist/lib/builders/modes-processor.mjs +1 -1800
- package/dist/lib/errors.mjs +9 -211
- package/dist/lib/factories/component-base.mjs +1 -80
- package/dist/lib/factories/context.mjs +1 -22
- package/dist/lib/handlers/api-cache-manager.mjs +1 -200
- package/dist/lib/handlers/api-manager.mjs +1 -2513
- package/dist/lib/handlers/context-async.mjs +1 -168
- package/dist/lib/handlers/context-live.mjs +1 -168
- package/dist/lib/handlers/hook-manager.mjs +1 -773
- package/dist/lib/handlers/lifecycle-token.mjs +1 -28
- package/dist/lib/handlers/lifecycle.mjs +1 -115
- package/dist/lib/handlers/materialize-manager.mjs +1 -48
- package/dist/lib/handlers/metadata.mjs +1 -501
- package/dist/lib/handlers/ownership.mjs +1 -322
- package/dist/lib/handlers/permission-manager.mjs +17 -0
- package/dist/lib/handlers/unified-wrapper.mjs +1 -3042
- package/dist/lib/handlers/version-manager.mjs +1 -885
- package/dist/lib/helpers/class-instance-wrapper.mjs +1 -109
- package/dist/lib/helpers/config.mjs +1 -355
- package/dist/lib/helpers/eventemitter-context.mjs +1 -349
- package/dist/lib/helpers/hint-detector.mjs +1 -47
- package/dist/lib/helpers/modes-utils.mjs +1 -37
- package/dist/lib/helpers/pattern-matcher.mjs +17 -0
- package/dist/lib/helpers/resolve-from-caller.mjs +1 -169
- package/dist/lib/helpers/sanitize.mjs +1 -340
- package/dist/lib/helpers/utilities.mjs +1 -70
- package/dist/lib/i18n/languages/de-de.json +21 -1
- package/dist/lib/i18n/languages/en-gb.json +21 -1
- package/dist/lib/i18n/languages/en-us.json +21 -1
- package/dist/lib/i18n/languages/es-mx.json +21 -1
- package/dist/lib/i18n/languages/fr-fr.json +21 -1
- package/dist/lib/i18n/languages/hi-in.json +21 -1
- package/dist/lib/i18n/languages/ja-jp.json +21 -1
- package/dist/lib/i18n/languages/ko-kr.json +21 -1
- package/dist/lib/i18n/languages/pt-br.json +21 -1
- package/dist/lib/i18n/languages/ru-ru.json +21 -1
- package/dist/lib/i18n/languages/zh-cn.json +21 -1
- package/dist/lib/i18n/translations.mjs +1 -126
- package/dist/lib/modes/eager.mjs +1 -59
- package/dist/lib/modes/lazy.mjs +1 -81
- package/dist/lib/processors/flatten.mjs +1 -437
- package/dist/lib/processors/loader.mjs +1 -339
- package/dist/lib/processors/type-generator.mjs +1 -275
- package/dist/lib/processors/typescript.mjs +1 -172
- package/dist/lib/runtime/runtime-asynclocalstorage.mjs +1 -113
- package/dist/lib/runtime/runtime-livebindings.mjs +1 -78
- package/dist/lib/runtime/runtime.mjs +1 -102
- package/dist/slothlet.mjs +1 -808
- package/package.json +37 -31
- package/types/dist/lib/builders/api-assignment.d.mts +3 -92
- package/types/dist/lib/builders/api-assignment.d.mts.map +1 -1
- package/types/dist/lib/builders/api_builder.d.mts +102 -91
- package/types/dist/lib/builders/api_builder.d.mts.map +1 -1
- package/types/dist/lib/builders/builder.d.mts +1 -55
- package/types/dist/lib/builders/builder.d.mts.map +1 -1
- package/types/dist/lib/builders/modes-processor.d.mts +3 -27
- package/types/dist/lib/builders/modes-processor.d.mts.map +1 -1
- package/types/dist/lib/errors.d.mts +19 -109
- package/types/dist/lib/errors.d.mts.map +1 -1
- package/types/dist/lib/factories/component-base.d.mts +7 -177
- package/types/dist/lib/factories/component-base.d.mts.map +1 -1
- package/types/dist/lib/factories/context.d.mts +4 -22
- package/types/dist/lib/factories/context.d.mts.map +1 -1
- package/types/dist/lib/handlers/api-cache-manager.d.mts +20 -203
- package/types/dist/lib/handlers/api-cache-manager.d.mts.map +1 -1
- package/types/dist/lib/handlers/api-manager.d.mts +33 -408
- package/types/dist/lib/handlers/api-manager.d.mts.map +1 -1
- package/types/dist/lib/handlers/context-async.d.mts +23 -61
- package/types/dist/lib/handlers/context-async.d.mts.map +1 -1
- package/types/dist/lib/handlers/context-live.d.mts +22 -59
- package/types/dist/lib/handlers/context-live.d.mts.map +1 -1
- package/types/dist/lib/handlers/hook-manager.d.mts +46 -185
- package/types/dist/lib/handlers/hook-manager.d.mts.map +1 -1
- package/types/dist/lib/handlers/lifecycle-token.d.mts +3 -48
- package/types/dist/lib/handlers/lifecycle-token.d.mts.map +1 -1
- package/types/dist/lib/handlers/lifecycle.d.mts +5 -82
- package/types/dist/lib/handlers/lifecycle.d.mts.map +1 -1
- package/types/dist/lib/handlers/materialize-manager.d.mts +8 -70
- package/types/dist/lib/handlers/materialize-manager.d.mts.map +1 -1
- package/types/dist/lib/handlers/metadata.d.mts +17 -221
- package/types/dist/lib/handlers/metadata.d.mts.map +1 -1
- package/types/dist/lib/handlers/ownership.d.mts +44 -160
- package/types/dist/lib/handlers/ownership.d.mts.map +1 -1
- package/types/dist/lib/handlers/permission-manager.d.mts +47 -0
- package/types/dist/lib/handlers/permission-manager.d.mts.map +1 -0
- package/types/dist/lib/handlers/unified-wrapper.d.mts +26 -239
- package/types/dist/lib/handlers/unified-wrapper.d.mts.map +1 -1
- package/types/dist/lib/handlers/version-manager.d.mts +28 -225
- package/types/dist/lib/handlers/version-manager.d.mts.map +1 -1
- package/types/dist/lib/helpers/class-instance-wrapper.d.mts +2 -52
- package/types/dist/lib/helpers/class-instance-wrapper.d.mts.map +1 -1
- package/types/dist/lib/helpers/config.d.mts +125 -123
- package/types/dist/lib/helpers/config.d.mts.map +1 -1
- package/types/dist/lib/helpers/eventemitter-context.d.mts +3 -29
- package/types/dist/lib/helpers/eventemitter-context.d.mts.map +1 -1
- package/types/dist/lib/helpers/hint-detector.d.mts +2 -15
- package/types/dist/lib/helpers/hint-detector.d.mts.map +1 -1
- package/types/dist/lib/helpers/modes-utils.d.mts +3 -30
- package/types/dist/lib/helpers/modes-utils.d.mts.map +1 -1
- package/types/dist/lib/helpers/pattern-matcher.d.mts +4 -0
- package/types/dist/lib/helpers/pattern-matcher.d.mts.map +1 -0
- package/types/dist/lib/helpers/resolve-from-caller.d.mts +3 -27
- package/types/dist/lib/helpers/resolve-from-caller.d.mts.map +1 -1
- package/types/dist/lib/helpers/sanitize.d.mts +4 -92
- package/types/dist/lib/helpers/sanitize.d.mts.map +1 -1
- package/types/dist/lib/helpers/utilities.d.mts +4 -52
- package/types/dist/lib/helpers/utilities.d.mts.map +1 -1
- package/types/dist/lib/i18n/translations.d.mts +4 -37
- package/types/dist/lib/i18n/translations.d.mts.map +1 -1
- package/types/dist/lib/modes/eager.d.mts +8 -30
- package/types/dist/lib/modes/eager.d.mts.map +1 -1
- package/types/dist/lib/modes/lazy.d.mts +10 -43
- package/types/dist/lib/modes/lazy.d.mts.map +1 -1
- package/types/dist/lib/processors/flatten.d.mts +56 -107
- package/types/dist/lib/processors/flatten.d.mts.map +1 -1
- package/types/dist/lib/processors/loader.d.mts +6 -41
- package/types/dist/lib/processors/loader.d.mts.map +1 -1
- package/types/dist/lib/processors/type-generator.d.mts +2 -16
- package/types/dist/lib/processors/type-generator.d.mts.map +1 -1
- package/types/dist/lib/processors/typescript.d.mts +6 -53
- package/types/dist/lib/processors/typescript.d.mts.map +1 -1
- package/types/dist/lib/runtime/runtime-asynclocalstorage.d.mts +3 -71
- package/types/dist/lib/runtime/runtime-asynclocalstorage.d.mts.map +1 -1
- package/types/dist/lib/runtime/runtime-livebindings.d.mts +2 -37
- package/types/dist/lib/runtime/runtime-livebindings.d.mts.map +1 -1
- package/types/dist/lib/runtime/runtime.d.mts +3 -39
- package/types/dist/lib/runtime/runtime.d.mts.map +1 -1
- package/types/dist/slothlet.d.mts +3 -249
- package/types/dist/slothlet.d.mts.map +1 -1
- package/types/index.d.mts +36 -16
- package/types/index.d.mts.map +1 -0
- package/AGENT-USAGE.md +0 -736
- package/docs/API-RULES.md +0 -712
|
@@ -14,888 +14,4 @@
|
|
|
14
14
|
limitations under the License.
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
import { inspect } from "node:util";
|
|
22
|
-
import { ComponentBase } from "@cldmv/slothlet/factories/component-base";
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
function stripPrefix(tag) {
|
|
28
|
-
|
|
29
|
-
return tag.replace(/^[^0-9]+/, "");
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
function stripSuffix(s) {
|
|
34
|
-
|
|
35
|
-
return s.replace(/[-+].*$/, "");
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
function normaliseVersionTag(tag) {
|
|
40
|
-
const bare = stripSuffix(stripPrefix(tag));
|
|
41
|
-
const parts = bare.split(".").map((p) => {
|
|
42
|
-
const n = parseInt(p, 10);
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
return isNaN(n) ? 0 : n;
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
return [parts[0] ?? 0, parts[1] ?? 0, parts[2] ?? 0];
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
function compareTuples(a, b) {
|
|
54
|
-
for (let i = 0; i < 3; i++) {
|
|
55
|
-
if (a[i] !== b[i]) return b[i] - a[i];
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
return 0;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
const FORCE_VERSION_SYMBOL = Symbol.for("slothlet.versioning.force");
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
export class VersionManager extends ComponentBase {
|
|
73
|
-
static slothletProperty = "versionManager";
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
#registry = new Map();
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
#versionMetadataByModule = new Map();
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
#moduleToVersionKey = new Map();
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
#dispatchers = new Map();
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
registerVersion(logicalPath, versionTag, moduleID, versionMeta, isDefault) {
|
|
91
|
-
if (!this.#registry.has(logicalPath)) {
|
|
92
|
-
this.#registry.set(logicalPath, { versions: new Map() });
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
const entry = this.#registry.get(logicalPath);
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
if (entry.versions.has(versionTag)) {
|
|
99
|
-
throw new this.SlothletError("VERSION_REGISTER_DUPLICATE", {
|
|
100
|
-
version: versionTag,
|
|
101
|
-
apiPath: logicalPath
|
|
102
|
-
});
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
const versionEntry = {
|
|
107
|
-
moduleID,
|
|
108
|
-
versionTag,
|
|
109
|
-
versionedPath: `${versionTag}.${logicalPath}`,
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
versionedParts: [versionTag, ...logicalPath.split(".")],
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
isDefault: isDefault ?? false,
|
|
117
|
-
|
|
118
|
-
versionMeta: versionMeta ?? {},
|
|
119
|
-
registeredAt: Date.now()
|
|
120
|
-
};
|
|
121
|
-
|
|
122
|
-
entry.versions.set(versionTag, versionEntry);
|
|
123
|
-
this.#moduleToVersionKey.set(moduleID, { logicalPath, versionTag });
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
this.#versionMetadataByModule.set(moduleID, {
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
...(versionMeta ?? {}),
|
|
130
|
-
version: versionTag,
|
|
131
|
-
logicalPath
|
|
132
|
-
});
|
|
133
|
-
|
|
134
|
-
this.slothlet.debug("versioning", {
|
|
135
|
-
key: "DEBUG_VERSION_REGISTERED",
|
|
136
|
-
version: versionTag,
|
|
137
|
-
logicalPath,
|
|
138
|
-
moduleID
|
|
139
|
-
});
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
this.updateDispatcher(logicalPath);
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
unregisterVersion(logicalPath, versionTag) {
|
|
147
|
-
const entry = this.#registry.get(logicalPath);
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
if (!entry) return false;
|
|
152
|
-
|
|
153
|
-
const versionEntry = entry.versions.get(versionTag);
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
if (!versionEntry) return false;
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
this.#moduleToVersionKey.delete(versionEntry.moduleID);
|
|
160
|
-
this.#versionMetadataByModule.delete(versionEntry.moduleID);
|
|
161
|
-
entry.versions.delete(versionTag);
|
|
162
|
-
|
|
163
|
-
this.slothlet.debug("versioning", {
|
|
164
|
-
key: "DEBUG_VERSION_UNREGISTERED",
|
|
165
|
-
version: versionTag,
|
|
166
|
-
logicalPath
|
|
167
|
-
});
|
|
168
|
-
|
|
169
|
-
if (entry.versions.size === 0) {
|
|
170
|
-
|
|
171
|
-
this.#registry.delete(logicalPath);
|
|
172
|
-
this.teardownDispatcher(logicalPath);
|
|
173
|
-
} else {
|
|
174
|
-
|
|
175
|
-
this.updateDispatcher(logicalPath);
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
return true;
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
getVersionKeyForModule(moduleID) {
|
|
183
|
-
return this.#moduleToVersionKey.get(moduleID);
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
hasDispatcher(logicalPath) {
|
|
188
|
-
return this.#dispatchers.has(logicalPath);
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
getVersionMetadata(moduleID) {
|
|
193
|
-
return this.#versionMetadataByModule.get(moduleID);
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
getVersionMetadataByPath(logicalPath, versionTag) {
|
|
198
|
-
const entry = this.#registry.get(logicalPath);
|
|
199
|
-
if (!entry) return undefined;
|
|
200
|
-
const ve = entry.versions.get(versionTag);
|
|
201
|
-
if (!ve) return undefined;
|
|
202
|
-
return this.#versionMetadataByModule.get(ve.moduleID);
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
setVersionMetadataByPath(logicalPath, versionTag, patch) {
|
|
207
|
-
const entry = this.#registry.get(logicalPath);
|
|
208
|
-
if (!entry || !entry.versions.has(versionTag)) {
|
|
209
|
-
throw new this.SlothletError("VERSION_NOT_FOUND", {
|
|
210
|
-
version: versionTag,
|
|
211
|
-
apiPath: logicalPath
|
|
212
|
-
});
|
|
213
|
-
}
|
|
214
|
-
const ve = entry.versions.get(versionTag);
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
const existing = this.#versionMetadataByModule.get(ve.moduleID) ?? {};
|
|
218
|
-
|
|
219
|
-
this.#versionMetadataByModule.set(ve.moduleID, {
|
|
220
|
-
...existing,
|
|
221
|
-
...(patch && typeof patch === "object" ? patch : {}),
|
|
222
|
-
version: ve.versionTag,
|
|
223
|
-
logicalPath
|
|
224
|
-
});
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
list(logicalPath) {
|
|
229
|
-
const entry = this.#registry.get(logicalPath);
|
|
230
|
-
if (!entry) return undefined;
|
|
231
|
-
|
|
232
|
-
const versions = {};
|
|
233
|
-
for (const [tag, ve] of entry.versions) {
|
|
234
|
-
versions[tag] = { ...ve };
|
|
235
|
-
}
|
|
236
|
-
return { versions, default: this.getDefaultVersion(logicalPath) };
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
setDefault(logicalPath, versionTag) {
|
|
241
|
-
const entry = this.#registry.get(logicalPath);
|
|
242
|
-
if (!entry) {
|
|
243
|
-
throw new this.SlothletError("VERSION_NOT_FOUND", {
|
|
244
|
-
version: versionTag,
|
|
245
|
-
apiPath: logicalPath
|
|
246
|
-
});
|
|
247
|
-
}
|
|
248
|
-
if (!entry.versions.has(versionTag)) {
|
|
249
|
-
throw new this.SlothletError("VERSION_NOT_FOUND", {
|
|
250
|
-
version: versionTag,
|
|
251
|
-
apiPath: logicalPath
|
|
252
|
-
});
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
for (const ve of entry.versions.values()) {
|
|
256
|
-
ve.isDefault = false;
|
|
257
|
-
}
|
|
258
|
-
entry.versions.get(versionTag).isDefault = true;
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
getDefaultVersion(logicalPath) {
|
|
265
|
-
const entry = this.#registry.get(logicalPath);
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
if (!entry || entry.versions.size === 0) return null;
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
for (const [tag, ve] of entry.versions) {
|
|
273
|
-
if (ve.isDefault) return tag;
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
const tags = Array.from(entry.versions.keys());
|
|
278
|
-
if (tags.length === 1) return tags[0];
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
const sorted = tags
|
|
282
|
-
.map((tag) => ({ tag, tuple: normaliseVersionTag(tag) }))
|
|
283
|
-
.sort((a, b) => {
|
|
284
|
-
const cmp = compareTuples(a.tuple, b.tuple);
|
|
285
|
-
if (cmp !== 0) return cmp;
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
const aSuffix = a.tag.match(/[-+]/) ? 1 : 0;
|
|
290
|
-
|
|
291
|
-
const bSuffix = b.tag.match(/[-+]/) ? 1 : 0;
|
|
292
|
-
return aSuffix - bSuffix;
|
|
293
|
-
});
|
|
294
|
-
|
|
295
|
-
return sorted[0].tag;
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
resolveForPath(logicalPath, allVersions, caller) {
|
|
302
|
-
const discriminator = this.slothlet.config?.versionDispatcher ?? "version";
|
|
303
|
-
|
|
304
|
-
let resolvedTag = null;
|
|
305
|
-
|
|
306
|
-
if (typeof discriminator === "string") {
|
|
307
|
-
|
|
308
|
-
resolvedTag = caller.versionMetadata?.[discriminator] ?? null;
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
if (typeof discriminator === "function") {
|
|
314
|
-
try {
|
|
315
|
-
resolvedTag = discriminator(allVersions, caller);
|
|
316
|
-
} catch {
|
|
317
|
-
resolvedTag = null;
|
|
318
|
-
}
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
if (resolvedTag == null) return null;
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
const entry = this.#registry.get(logicalPath);
|
|
325
|
-
if (!entry || !entry.versions.has(resolvedTag)) {
|
|
326
|
-
this.slothlet.debug("versioning", {
|
|
327
|
-
key: "DEBUG_VERSION_RESOLVED",
|
|
328
|
-
version: null,
|
|
329
|
-
apiPath: logicalPath,
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
callerModule: caller?.metadata?.moduleID ?? null
|
|
333
|
-
});
|
|
334
|
-
return null;
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
this.slothlet.debug("versioning", {
|
|
338
|
-
key: "DEBUG_VERSION_RESOLVED",
|
|
339
|
-
version: resolvedTag,
|
|
340
|
-
apiPath: logicalPath,
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
callerModule: caller?.metadata?.moduleID ?? null
|
|
344
|
-
});
|
|
345
|
-
|
|
346
|
-
return resolvedTag;
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
buildAllVersionsArg(logicalPath) {
|
|
353
|
-
const entry = this.#registry.get(logicalPath);
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
if (!entry) return {};
|
|
358
|
-
|
|
359
|
-
const defaultTag = this.getDefaultVersion(logicalPath);
|
|
360
|
-
const result = {};
|
|
361
|
-
|
|
362
|
-
for (const [tag, ve] of entry.versions) {
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
const mountedWrapper = this.#walkApiPath(ve.versionedParts);
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
const regularMetadata = this.slothlet.handlers.metadata?.getMetadata?.(mountedWrapper) ?? {};
|
|
370
|
-
|
|
371
|
-
const versionMetadata = this.#versionMetadataByModule.get(ve.moduleID) ?? {};
|
|
372
|
-
|
|
373
|
-
result[tag] = {
|
|
374
|
-
version: tag,
|
|
375
|
-
default: tag === defaultTag,
|
|
376
|
-
metadata: regularMetadata,
|
|
377
|
-
versionMetadata
|
|
378
|
-
};
|
|
379
|
-
}
|
|
380
|
-
|
|
381
|
-
return result;
|
|
382
|
-
}
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
buildCallerArg(callerWrapper) {
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
const callerModuleID = callerWrapper?.____slothletInternal?.moduleID ?? callerWrapper?.__moduleID;
|
|
389
|
-
const callerVersionEntry = callerModuleID ? this.#findVersionEntryForModule(callerModuleID) : null;
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
const regularMetadata = this.slothlet.handlers.metadata?.getMetadata?.(callerWrapper) ?? {};
|
|
393
|
-
|
|
394
|
-
if (!callerVersionEntry) {
|
|
395
|
-
return {
|
|
396
|
-
version: null,
|
|
397
|
-
default: null,
|
|
398
|
-
metadata: regularMetadata,
|
|
399
|
-
versionMetadata: null
|
|
400
|
-
};
|
|
401
|
-
}
|
|
402
|
-
|
|
403
|
-
const defaultTag = this.getDefaultVersion(callerVersionEntry.logicalPath);
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
const versionMetadata = this.#versionMetadataByModule.get(callerModuleID) ?? {};
|
|
407
|
-
|
|
408
|
-
return {
|
|
409
|
-
version: callerVersionEntry.versionTag,
|
|
410
|
-
default: callerVersionEntry.versionTag === defaultTag,
|
|
411
|
-
metadata: regularMetadata,
|
|
412
|
-
versionMetadata
|
|
413
|
-
};
|
|
414
|
-
}
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
#findVersionEntryForModule(moduleID) {
|
|
418
|
-
const key = this.#moduleToVersionKey.get(moduleID);
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
if (!key) return null;
|
|
422
|
-
const entry = this.#registry.get(key.logicalPath);
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
if (!entry) return null;
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
return entry.versions.get(key.versionTag) ?? null;
|
|
430
|
-
}
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
#walkApiPath(apiPath) {
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
if (!apiPath) return undefined;
|
|
437
|
-
let node = this.slothlet.api;
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
const segments = Array.isArray(apiPath) ? apiPath : apiPath.split(".");
|
|
443
|
-
for (const segment of segments) {
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
if (node == null) return undefined;
|
|
447
|
-
node = node[segment];
|
|
448
|
-
}
|
|
449
|
-
return node;
|
|
450
|
-
}
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
createDispatcher(logicalPath) {
|
|
456
|
-
const manager = this;
|
|
457
|
-
const target = { __isVersionDispatcher: true, __logicalPath: logicalPath };
|
|
458
|
-
const displayName = logicalPath.split(".").pop();
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
const resolveVersion = () => {
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
const ctx = manager.slothlet.contextManager?.tryGetContext?.();
|
|
466
|
-
const forcedVersion = ctx?.context?.[FORCE_VERSION_SYMBOL];
|
|
467
|
-
if (forcedVersion) {
|
|
468
|
-
const entry = manager.#registry.get(logicalPath);
|
|
469
|
-
if (entry?.versions.has(forcedVersion)) return forcedVersion;
|
|
470
|
-
}
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
const callerWrapper = ctx?.currentWrapper ?? null;
|
|
475
|
-
const allVersions = manager.buildAllVersionsArg(logicalPath);
|
|
476
|
-
const caller = manager.buildCallerArg(callerWrapper);
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
let tag = manager.resolveForPath(logicalPath, allVersions, caller);
|
|
480
|
-
|
|
481
|
-
if (tag == null) {
|
|
482
|
-
|
|
483
|
-
tag = manager.getDefaultVersion(logicalPath);
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
if (tag != null) {
|
|
488
|
-
manager.slothlet.debug("versioning", {
|
|
489
|
-
key: "DEBUG_VERSION_DEFAULT_USED",
|
|
490
|
-
apiPath: logicalPath,
|
|
491
|
-
version: tag
|
|
492
|
-
});
|
|
493
|
-
}
|
|
494
|
-
}
|
|
495
|
-
|
|
496
|
-
return tag;
|
|
497
|
-
};
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
const resolveVersionedWrapper = () => {
|
|
501
|
-
const versionTag = resolveVersion();
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
if (!versionTag) return null;
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
return manager.#walkApiPath([versionTag, ...logicalPath.split(".")]);
|
|
509
|
-
};
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
target[Symbol.for("nodejs.util.inspect.custom")] = function (_depth, options, inspectFn) {
|
|
517
|
-
const vw = resolveVersionedWrapper();
|
|
518
|
-
if (vw) {
|
|
519
|
-
try {
|
|
520
|
-
return typeof inspectFn === "function" ? inspectFn(vw, options) : inspect(vw, options);
|
|
521
|
-
} catch {
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
void 0;
|
|
525
|
-
}
|
|
526
|
-
}
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
const entry = manager.#registry.get(logicalPath);
|
|
531
|
-
const versions = entry ? Array.from(entry.versions.keys()) : [];
|
|
532
|
-
return { __versionDispatcher: logicalPath, versions };
|
|
533
|
-
};
|
|
534
|
-
|
|
535
|
-
const handlers = {
|
|
536
|
-
|
|
537
|
-
get(t, prop) {
|
|
538
|
-
|
|
539
|
-
if (typeof prop === "string") {
|
|
540
|
-
if (prop === "____slothletInternal" || prop === "_impl" || prop === "__impl" || prop === "__state" || prop === "__invalid") {
|
|
541
|
-
return undefined;
|
|
542
|
-
}
|
|
543
|
-
}
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
if (prop === "__isVersionDispatcher") return true;
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
if (prop === "__mode") return "eager";
|
|
550
|
-
if (prop === "__apiPath") return logicalPath;
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
if (prop === "__slothletPath") return logicalPath;
|
|
554
|
-
if (prop === "__isCallable") return false;
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
if (prop === "__materializeOnCreate") return false;
|
|
560
|
-
if (prop === "__materialized") return true;
|
|
561
|
-
if (prop === "__inFlight") return false;
|
|
562
|
-
if (prop === "__displayName") return displayName;
|
|
563
|
-
if (prop === "__moduleID") return `versionDispatcher:${logicalPath}`;
|
|
564
|
-
if (prop === "_materialize") return () => {};
|
|
565
|
-
if (prop === "length") return 0;
|
|
566
|
-
if (prop === "name") return displayName;
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
if (prop === "then") return undefined;
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
if (prop === "constructor") return Object.prototype.constructor;
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
if (prop === Symbol.toStringTag) {
|
|
581
|
-
const vw = resolveVersionedWrapper();
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
if (!vw) return "Object";
|
|
585
|
-
return vw[Symbol.toStringTag];
|
|
586
|
-
}
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
if (prop === inspect.custom) {
|
|
595
|
-
return (_depth, options, inspectFn) => {
|
|
596
|
-
const vw = resolveVersionedWrapper();
|
|
597
|
-
if (vw) {
|
|
598
|
-
try {
|
|
599
|
-
return typeof inspectFn === "function" ? inspectFn(vw, options) : inspect(vw, options);
|
|
600
|
-
} catch {
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
void 0;
|
|
604
|
-
}
|
|
605
|
-
}
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
const entry = manager.#registry.get(logicalPath);
|
|
609
|
-
const versions = entry ? Array.from(entry.versions.keys()) : [];
|
|
610
|
-
return { __versionDispatcher: logicalPath, versions };
|
|
611
|
-
};
|
|
612
|
-
}
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
if (prop === "toString") return () => `[VersionDispatcher: ${logicalPath}]`;
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
if (prop === "valueOf") return () => dispatcherProxy;
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
if (prop === "toJSON") return () => undefined;
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
if (typeof prop === "symbol") return undefined;
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
if (prop === "__metadata" || prop === "__filePath" || prop === "__sourceFolder" || prop === "__type") {
|
|
628
|
-
const vw = resolveVersionedWrapper();
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
if (!vw) return undefined;
|
|
633
|
-
return vw[prop];
|
|
634
|
-
}
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
const versionTag = resolveVersion();
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
if (!versionTag) {
|
|
643
|
-
throw new manager.SlothletError("VERSION_NO_DEFAULT", {
|
|
644
|
-
apiPath: logicalPath
|
|
645
|
-
});
|
|
646
|
-
}
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
const versionedWrapper = manager.#walkApiPath([versionTag, ...logicalPath.split(".")]);
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
if (!versionedWrapper) return undefined;
|
|
654
|
-
|
|
655
|
-
return versionedWrapper[prop];
|
|
656
|
-
},
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
apply() {
|
|
661
|
-
throw new manager.SlothletError("VERSION_DISPATCH_NOT_CALLABLE", {
|
|
662
|
-
apiPath: logicalPath
|
|
663
|
-
});
|
|
664
|
-
},
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
has(t, key) {
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
if (Reflect.has(t, key)) return true;
|
|
672
|
-
const entry = manager.#registry.get(logicalPath);
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
if (!entry) return false;
|
|
676
|
-
for (const ve of entry.versions.values()) {
|
|
677
|
-
const vw = manager.#walkApiPath(ve.versionedParts);
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
if (vw && key in vw) return true;
|
|
684
|
-
}
|
|
685
|
-
return false;
|
|
686
|
-
},
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
ownKeys(t) {
|
|
690
|
-
const keySet = new Set(Reflect.ownKeys(t));
|
|
691
|
-
const entry = manager.#registry.get(logicalPath);
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
if (entry) {
|
|
695
|
-
for (const ve of entry.versions.values()) {
|
|
696
|
-
const vw = manager.#walkApiPath(ve.versionedParts);
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
if (vw) {
|
|
700
|
-
for (const k of Reflect.ownKeys(Object(vw))) {
|
|
701
|
-
keySet.add(k);
|
|
702
|
-
}
|
|
703
|
-
}
|
|
704
|
-
}
|
|
705
|
-
}
|
|
706
|
-
return Array.from(keySet);
|
|
707
|
-
},
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
getOwnPropertyDescriptor(t, prop) {
|
|
711
|
-
const targetDesc = Reflect.getOwnPropertyDescriptor(t, prop);
|
|
712
|
-
if (targetDesc) {
|
|
713
|
-
if (!targetDesc.configurable) {
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
return targetDesc;
|
|
720
|
-
}
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
return { configurable: true, enumerable: true, writable: false, value: t[prop] };
|
|
724
|
-
}
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
return { configurable: true, enumerable: true, writable: false, value: undefined };
|
|
729
|
-
},
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
defineProperty(t, prop, descriptor) {
|
|
733
|
-
const vw = resolveVersionedWrapper();
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
if (!vw) return Reflect.defineProperty(t, prop, descriptor);
|
|
738
|
-
if (descriptor.configurable === false) {
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
const shadow = Object.create(Reflect.getPrototypeOf(t));
|
|
746
|
-
const currentDescriptor = Reflect.getOwnPropertyDescriptor(t, prop);
|
|
747
|
-
if (currentDescriptor) {
|
|
748
|
-
Reflect.defineProperty(shadow, prop, currentDescriptor);
|
|
749
|
-
}
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
if (!Reflect.isExtensible(t)) {
|
|
758
|
-
Reflect.preventExtensions(shadow);
|
|
759
|
-
}
|
|
760
|
-
if (!Reflect.defineProperty(shadow, prop, descriptor)) return false;
|
|
761
|
-
|
|
762
|
-
if (!Reflect.defineProperty(vw, prop, descriptor)) return false;
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
return Reflect.defineProperty(t, prop, descriptor);
|
|
767
|
-
}
|
|
768
|
-
|
|
769
|
-
return Reflect.defineProperty(vw, prop, descriptor);
|
|
770
|
-
},
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
set(t, prop, value) {
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
if (typeof prop === "symbol") return true;
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
if (prop === "____slothletInternal" || prop === "_impl" || prop === "__impl" || prop === "__state" || prop === "__invalid")
|
|
783
|
-
return true;
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
if (
|
|
787
|
-
prop === "__isVersionDispatcher" ||
|
|
788
|
-
prop === "__mode" ||
|
|
789
|
-
prop === "__apiPath" ||
|
|
790
|
-
prop === "__slothletPath" ||
|
|
791
|
-
prop === "__isCallable" ||
|
|
792
|
-
prop === "__materializeOnCreate" ||
|
|
793
|
-
prop === "__materialized" ||
|
|
794
|
-
prop === "__inFlight" ||
|
|
795
|
-
prop === "__displayName" ||
|
|
796
|
-
prop === "__moduleID" ||
|
|
797
|
-
prop === "_materialize" ||
|
|
798
|
-
prop === "length" ||
|
|
799
|
-
prop === "name"
|
|
800
|
-
)
|
|
801
|
-
return true;
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
if (prop === "then" || prop === "constructor" || prop === "toString" || prop === "valueOf" || prop === "toJSON") return true;
|
|
805
|
-
|
|
806
|
-
const vw = resolveVersionedWrapper();
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
if (!vw) return true;
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
return Reflect.set(vw, prop, value, vw);
|
|
817
|
-
}
|
|
818
|
-
};
|
|
819
|
-
|
|
820
|
-
let dispatcherProxy;
|
|
821
|
-
dispatcherProxy = new Proxy(target, handlers);
|
|
822
|
-
return dispatcherProxy;
|
|
823
|
-
}
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
updateDispatcher(logicalPath) {
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
if (this.#dispatchers.has(logicalPath)) {
|
|
830
|
-
return;
|
|
831
|
-
}
|
|
832
|
-
|
|
833
|
-
const dispatcher = this.createDispatcher(logicalPath);
|
|
834
|
-
this.#dispatchers.set(logicalPath, dispatcher);
|
|
835
|
-
|
|
836
|
-
const parts = logicalPath.split(".");
|
|
837
|
-
const mountOptions = {
|
|
838
|
-
collisionMode: "replace",
|
|
839
|
-
moduleID: `versionDispatcher:${logicalPath}`,
|
|
840
|
-
allowOverwrite: true,
|
|
841
|
-
mutateExisting: false
|
|
842
|
-
};
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
if (this.slothlet.api) {
|
|
848
|
-
this.slothlet.handlers.apiManager.setValueAtPath(this.slothlet.api, parts, dispatcher, mountOptions);
|
|
849
|
-
}
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
if (this.slothlet.boundApi) {
|
|
853
|
-
this.slothlet.handlers.apiManager.setValueAtPath(this.slothlet.boundApi, parts, dispatcher, mountOptions);
|
|
854
|
-
}
|
|
855
|
-
}
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
teardownDispatcher(logicalPath) {
|
|
859
|
-
this.#dispatchers.delete(logicalPath);
|
|
860
|
-
|
|
861
|
-
const parts = logicalPath.split(".");
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
if (this.slothlet.api) {
|
|
866
|
-
this.slothlet.handlers.apiManager.deletePath(this.slothlet.api, parts).catch(() => {});
|
|
867
|
-
}
|
|
868
|
-
if (this.slothlet.boundApi) {
|
|
869
|
-
this.slothlet.handlers.apiManager.deletePath(this.slothlet.boundApi, parts).catch(() => {});
|
|
870
|
-
}
|
|
871
|
-
|
|
872
|
-
}
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
onVersionedModuleReload(moduleID) {
|
|
878
|
-
const key = this.#moduleToVersionKey.get(moduleID);
|
|
879
|
-
if (!key) return;
|
|
880
|
-
|
|
881
|
-
const { logicalPath } = key;
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
this.updateDispatcher(logicalPath);
|
|
885
|
-
|
|
886
|
-
this.slothlet.debug("versioning", {
|
|
887
|
-
key: "DEBUG_VERSION_REGISTERED",
|
|
888
|
-
version: key.versionTag,
|
|
889
|
-
logicalPath,
|
|
890
|
-
moduleID
|
|
891
|
-
});
|
|
892
|
-
}
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
shutdown() {
|
|
896
|
-
this.#registry.clear();
|
|
897
|
-
this.#versionMetadataByModule.clear();
|
|
898
|
-
this.#moduleToVersionKey.clear();
|
|
899
|
-
this.#dispatchers.clear();
|
|
900
|
-
}
|
|
901
|
-
}
|
|
17
|
+
import{inspect}from"node:util";import{ComponentBase}from"@cldmv/slothlet/factories/component-base";function stripPrefix(tag){return tag.replace(/^[^0-9]+/,"")}function stripSuffix(s){return s.replace(/[-+].*$/,"")}function normaliseVersionTag(tag){const bare=stripSuffix(stripPrefix(tag));const parts=bare.split(".").map(p=>{const n=parseInt(p,10);return isNaN(n)?0:n});return[parts[0]??0,parts[1]??0,parts[2]??0]}function compareTuples(a,b){for(let i=0;i<3;i++){if(a[i]!==b[i])return b[i]-a[i]}return 0}const FORCE_VERSION_SYMBOL=Symbol.for("slothlet.versioning.force");class VersionManager extends ComponentBase{static slothletProperty="versionManager";#registry=new Map;#versionMetadataByModule=new Map;#moduleToVersionKey=new Map;#dispatchers=new Map;registerVersion(logicalPath,versionTag,moduleID,versionMeta,isDefault){if(!this.#registry.has(logicalPath)){this.#registry.set(logicalPath,{versions:new Map})}const entry=this.#registry.get(logicalPath);if(entry.versions.has(versionTag)){throw new this.SlothletError("VERSION_REGISTER_DUPLICATE",{version:versionTag,apiPath:logicalPath})}const versionEntry={moduleID,versionTag,versionedPath:`${versionTag}.${logicalPath}`,versionedParts:[versionTag,...logicalPath.split(".")],isDefault:isDefault??false,versionMeta:versionMeta??{},registeredAt:Date.now()};entry.versions.set(versionTag,versionEntry);this.#moduleToVersionKey.set(moduleID,{logicalPath,versionTag});this.#versionMetadataByModule.set(moduleID,{...versionMeta??{},version:versionTag,logicalPath});this.slothlet.debug("versioning",{key:"DEBUG_VERSION_REGISTERED",version:versionTag,logicalPath,moduleID});this.updateDispatcher(logicalPath)}unregisterVersion(logicalPath,versionTag){const entry=this.#registry.get(logicalPath);if(!entry)return false;const versionEntry=entry.versions.get(versionTag);if(!versionEntry)return false;this.#moduleToVersionKey.delete(versionEntry.moduleID);this.#versionMetadataByModule.delete(versionEntry.moduleID);entry.versions.delete(versionTag);this.slothlet.debug("versioning",{key:"DEBUG_VERSION_UNREGISTERED",version:versionTag,logicalPath});if(entry.versions.size===0){this.#registry.delete(logicalPath);this.teardownDispatcher(logicalPath)}else{this.updateDispatcher(logicalPath)}return true}getVersionKeyForModule(moduleID){return this.#moduleToVersionKey.get(moduleID)}hasDispatcher(logicalPath){return this.#dispatchers.has(logicalPath)}getVersionMetadata(moduleID){return this.#versionMetadataByModule.get(moduleID)}getVersionMetadataByPath(logicalPath,versionTag){const entry=this.#registry.get(logicalPath);if(!entry)return void 0;const ve=entry.versions.get(versionTag);if(!ve)return void 0;return this.#versionMetadataByModule.get(ve.moduleID)}setVersionMetadataByPath(logicalPath,versionTag,patch){const entry=this.#registry.get(logicalPath);if(!entry||!entry.versions.has(versionTag)){throw new this.SlothletError("VERSION_NOT_FOUND",{version:versionTag,apiPath:logicalPath})}const ve=entry.versions.get(versionTag);const existing=this.#versionMetadataByModule.get(ve.moduleID)??{};this.#versionMetadataByModule.set(ve.moduleID,{...existing,...patch&&typeof patch==="object"?patch:{},version:ve.versionTag,logicalPath})}list(logicalPath){const entry=this.#registry.get(logicalPath);if(!entry)return void 0;const versions={};for(const[tag,ve]of entry.versions){versions[tag]={...ve}}return{versions,default:this.getDefaultVersion(logicalPath)}}setDefault(logicalPath,versionTag){const entry=this.#registry.get(logicalPath);if(!entry){throw new this.SlothletError("VERSION_NOT_FOUND",{version:versionTag,apiPath:logicalPath})}if(!entry.versions.has(versionTag)){throw new this.SlothletError("VERSION_NOT_FOUND",{version:versionTag,apiPath:logicalPath})}for(const ve of entry.versions.values()){ve.isDefault=false}entry.versions.get(versionTag).isDefault=true}getDefaultVersion(logicalPath){const entry=this.#registry.get(logicalPath);if(!entry||entry.versions.size===0)return null;for(const[tag,ve]of entry.versions){if(ve.isDefault)return tag}const tags=Array.from(entry.versions.keys());if(tags.length===1)return tags[0];const sorted=tags.map(tag=>({tag,tuple:normaliseVersionTag(tag)})).sort((a,b)=>{const cmp=compareTuples(a.tuple,b.tuple);if(cmp!==0)return cmp;const aSuffix=a.tag.match(/[-+]/)?1:0;const bSuffix=b.tag.match(/[-+]/)?1:0;return aSuffix-bSuffix});return sorted[0].tag}resolveForPath(logicalPath,allVersions,caller){const discriminator=this.slothlet.config?.versionDispatcher??"version";let resolvedTag=null;if(typeof discriminator==="string"){resolvedTag=caller.versionMetadata?.[discriminator]??null}if(typeof discriminator==="function"){try{resolvedTag=discriminator(allVersions,caller)}catch{resolvedTag=null}}if(resolvedTag==null)return null;const entry=this.#registry.get(logicalPath);if(!entry||!entry.versions.has(resolvedTag)){this.slothlet.debug("versioning",{key:"DEBUG_VERSION_RESOLVED",version:null,apiPath:logicalPath,callerModule:caller?.metadata?.moduleID??null});return null}this.slothlet.debug("versioning",{key:"DEBUG_VERSION_RESOLVED",version:resolvedTag,apiPath:logicalPath,callerModule:caller?.metadata?.moduleID??null});return resolvedTag}buildAllVersionsArg(logicalPath){const entry=this.#registry.get(logicalPath);if(!entry)return{};const defaultTag=this.getDefaultVersion(logicalPath);const result={};for(const[tag,ve]of entry.versions){const mountedWrapper=this.#walkApiPath(ve.versionedParts);const regularMetadata=this.slothlet.handlers.metadata?.getMetadata?.(mountedWrapper)??{};const versionMetadata=this.#versionMetadataByModule.get(ve.moduleID)??{};result[tag]={version:tag,default:tag===defaultTag,metadata:regularMetadata,versionMetadata}}return result}buildCallerArg(callerWrapper){const callerModuleID=callerWrapper?.____slothletInternal?.moduleID??callerWrapper?.__moduleID;const callerVersionEntry=callerModuleID?this.#findVersionEntryForModule(callerModuleID):null;const regularMetadata=this.slothlet.handlers.metadata?.getMetadata?.(callerWrapper)??{};if(!callerVersionEntry){return{version:null,default:null,metadata:regularMetadata,versionMetadata:null}}const defaultTag=this.getDefaultVersion(callerVersionEntry.logicalPath);const versionMetadata=this.#versionMetadataByModule.get(callerModuleID)??{};return{version:callerVersionEntry.versionTag,default:callerVersionEntry.versionTag===defaultTag,metadata:regularMetadata,versionMetadata}}#findVersionEntryForModule(moduleID){const key=this.#moduleToVersionKey.get(moduleID);if(!key)return null;const entry=this.#registry.get(key.logicalPath);if(!entry)return null;return entry.versions.get(key.versionTag)??null}#walkApiPath(apiPath){if(!apiPath)return void 0;let node=this.slothlet.api;const segments=Array.isArray(apiPath)?apiPath:apiPath.split(".");for(const segment of segments){if(node==null)return void 0;node=node[segment]}return node}createDispatcher(logicalPath){const manager=this;const target={__isVersionDispatcher:true,__logicalPath:logicalPath};const displayName=logicalPath.split(".").pop();const resolveVersion=()=>{const ctx=manager.slothlet.contextManager?.tryGetContext?.();const forcedVersion=ctx?.context?.[FORCE_VERSION_SYMBOL];if(forcedVersion){const entry=manager.#registry.get(logicalPath);if(entry?.versions.has(forcedVersion))return forcedVersion}const callerWrapper=ctx?.currentWrapper??null;const allVersions=manager.buildAllVersionsArg(logicalPath);const caller=manager.buildCallerArg(callerWrapper);let tag=manager.resolveForPath(logicalPath,allVersions,caller);if(tag==null){tag=manager.getDefaultVersion(logicalPath);if(tag!=null){manager.slothlet.debug("versioning",{key:"DEBUG_VERSION_DEFAULT_USED",apiPath:logicalPath,version:tag})}}return tag};const resolveVersionedWrapper=()=>{const versionTag=resolveVersion();if(!versionTag)return null;return manager.#walkApiPath([versionTag,...logicalPath.split(".")])};target[Symbol.for("nodejs.util.inspect.custom")]=function(_depth,options,inspectFn){const vw=resolveVersionedWrapper();if(vw){try{return typeof inspectFn==="function"?inspectFn(vw,options):inspect(vw,options)}catch{}}const entry=manager.#registry.get(logicalPath);const versions=entry?Array.from(entry.versions.keys()):[];return{__versionDispatcher:logicalPath,versions}};const handlers={get(t,prop){if(typeof prop==="string"){if(prop==="____slothletInternal"||prop==="_impl"||prop==="__impl"||prop==="__state"||prop==="__invalid"){return void 0}}if(prop==="__isVersionDispatcher")return true;if(prop==="__mode")return"eager";if(prop==="__apiPath")return logicalPath;if(prop==="__slothletPath")return logicalPath;if(prop==="__isCallable")return false;if(prop==="__materializeOnCreate")return false;if(prop==="__materialized")return true;if(prop==="__inFlight")return false;if(prop==="__displayName")return displayName;if(prop==="__moduleID")return`versionDispatcher:${logicalPath}`;if(prop==="_materialize")return()=>{};if(prop==="length")return 0;if(prop==="name")return displayName;if(prop==="then")return void 0;if(prop==="constructor")return Object.prototype.constructor;if(prop===Symbol.toStringTag){const vw=resolveVersionedWrapper();if(!vw)return"Object";return vw[Symbol.toStringTag]}if(prop===inspect.custom){return(_depth,options,inspectFn)=>{const vw=resolveVersionedWrapper();if(vw){try{return typeof inspectFn==="function"?inspectFn(vw,options):inspect(vw,options)}catch{}}const entry=manager.#registry.get(logicalPath);const versions=entry?Array.from(entry.versions.keys()):[];return{__versionDispatcher:logicalPath,versions}}}if(prop==="toString")return()=>`[VersionDispatcher: ${logicalPath}]`;if(prop==="valueOf")return()=>dispatcherProxy;if(prop==="toJSON")return()=>void 0;if(typeof prop==="symbol")return void 0;if(prop==="__metadata"||prop==="__filePath"||prop==="__sourceFolder"||prop==="__type"){const vw=resolveVersionedWrapper();if(!vw)return void 0;return vw[prop]}const versionTag=resolveVersion();if(!versionTag){throw new manager.SlothletError("VERSION_NO_DEFAULT",{apiPath:logicalPath})}const versionedWrapper=manager.#walkApiPath([versionTag,...logicalPath.split(".")]);if(!versionedWrapper)return void 0;return versionedWrapper[prop]},apply(){throw new manager.SlothletError("VERSION_DISPATCH_NOT_CALLABLE",{apiPath:logicalPath})},has(t,key){if(Reflect.has(t,key))return true;const entry=manager.#registry.get(logicalPath);if(!entry)return false;for(const ve of entry.versions.values()){const vw=manager.#walkApiPath(ve.versionedParts);if(vw&&key in vw)return true}return false},ownKeys(t){const keySet=new Set(Reflect.ownKeys(t));const entry=manager.#registry.get(logicalPath);if(entry){for(const ve of entry.versions.values()){const vw=manager.#walkApiPath(ve.versionedParts);if(vw){for(const k of Reflect.ownKeys(Object(vw))){keySet.add(k)}}}}return Array.from(keySet)},getOwnPropertyDescriptor(t,prop){const targetDesc=Reflect.getOwnPropertyDescriptor(t,prop);if(targetDesc){if(!targetDesc.configurable){return targetDesc}return{configurable:true,enumerable:true,writable:false,value:t[prop]}}return{configurable:true,enumerable:true,writable:false,value:void 0}},defineProperty(t,prop,descriptor){const vw=resolveVersionedWrapper();if(!vw)return Reflect.defineProperty(t,prop,descriptor);if(descriptor.configurable===false){const shadow=Object.create(Reflect.getPrototypeOf(t));const currentDescriptor=Reflect.getOwnPropertyDescriptor(t,prop);if(currentDescriptor){Reflect.defineProperty(shadow,prop,currentDescriptor)}if(!Reflect.isExtensible(t)){Reflect.preventExtensions(shadow)}if(!Reflect.defineProperty(shadow,prop,descriptor))return false;if(!Reflect.defineProperty(vw,prop,descriptor))return false;return Reflect.defineProperty(t,prop,descriptor)}return Reflect.defineProperty(vw,prop,descriptor)},set(t,prop,value){if(typeof prop==="symbol")return true;if(prop==="____slothletInternal"||prop==="_impl"||prop==="__impl"||prop==="__state"||prop==="__invalid")return true;if(prop==="__isVersionDispatcher"||prop==="__mode"||prop==="__apiPath"||prop==="__slothletPath"||prop==="__isCallable"||prop==="__materializeOnCreate"||prop==="__materialized"||prop==="__inFlight"||prop==="__displayName"||prop==="__moduleID"||prop==="_materialize"||prop==="length"||prop==="name")return true;if(prop==="then"||prop==="constructor"||prop==="toString"||prop==="valueOf"||prop==="toJSON")return true;const vw=resolveVersionedWrapper();if(!vw)return true;return Reflect.set(vw,prop,value,vw)}};let dispatcherProxy;dispatcherProxy=new Proxy(target,handlers);return dispatcherProxy}updateDispatcher(logicalPath){if(this.#dispatchers.has(logicalPath)){return}const dispatcher=this.createDispatcher(logicalPath);this.#dispatchers.set(logicalPath,dispatcher);const parts=logicalPath.split(".");const mountOptions={collisionMode:"replace",moduleID:`versionDispatcher:${logicalPath}`,allowOverwrite:true,mutateExisting:false};if(this.slothlet.api){this.slothlet.handlers.apiManager.setValueAtPath(this.slothlet.api,parts,dispatcher,mountOptions)}if(this.slothlet.boundApi){this.slothlet.handlers.apiManager.setValueAtPath(this.slothlet.boundApi,parts,dispatcher,mountOptions)}}teardownDispatcher(logicalPath){this.#dispatchers.delete(logicalPath);const parts=logicalPath.split(".");if(this.slothlet.api){this.slothlet.handlers.apiManager.deletePath(this.slothlet.api,parts).catch(()=>{})}if(this.slothlet.boundApi){this.slothlet.handlers.apiManager.deletePath(this.slothlet.boundApi,parts).catch(()=>{})}}onVersionedModuleReload(moduleID){const key=this.#moduleToVersionKey.get(moduleID);if(!key)return;const{logicalPath}=key;this.updateDispatcher(logicalPath);this.slothlet.debug("versioning",{key:"DEBUG_VERSION_REGISTERED",version:key.versionTag,logicalPath,moduleID})}shutdown(){this.#registry.clear();this.#versionMetadataByModule.clear();this.#moduleToVersionKey.clear();this.#dispatchers.clear()}}export{VersionManager};
|