@cldmv/slothlet 3.1.0 → 3.2.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/README.md +12 -7
- package/dist/lib/builders/api_builder.mjs +101 -4
- package/dist/lib/handlers/api-manager.mjs +179 -14
- package/dist/lib/handlers/metadata.mjs +15 -0
- package/dist/lib/handlers/unified-wrapper.mjs +22 -11
- package/dist/lib/handlers/version-manager.mjs +773 -0
- package/dist/lib/helpers/config.mjs +20 -5
- package/dist/lib/i18n/languages/de-de.json +15 -1
- package/dist/lib/i18n/languages/en-gb.json +15 -1
- package/dist/lib/i18n/languages/en-us.json +15 -1
- package/dist/lib/i18n/languages/es-mx.json +15 -1
- package/dist/lib/i18n/languages/fr-fr.json +15 -1
- package/dist/lib/i18n/languages/hi-in.json +15 -1
- package/dist/lib/i18n/languages/ja-jp.json +15 -1
- package/dist/lib/i18n/languages/ko-kr.json +15 -1
- package/dist/lib/i18n/languages/pt-br.json +15 -1
- package/dist/lib/i18n/languages/ru-ru.json +15 -1
- package/dist/lib/i18n/languages/zh-cn.json +15 -1
- package/dist/slothlet.mjs +70 -1
- package/package.json +5 -2
- package/types/dist/lib/builders/api_builder.d.mts.map +1 -1
- package/types/dist/lib/handlers/api-manager.d.mts +28 -0
- package/types/dist/lib/handlers/api-manager.d.mts.map +1 -1
- package/types/dist/lib/handlers/metadata.d.mts +15 -0
- package/types/dist/lib/handlers/metadata.d.mts.map +1 -1
- package/types/dist/lib/handlers/unified-wrapper.d.mts.map +1 -1
- package/types/dist/lib/handlers/version-manager.d.mts +234 -0
- package/types/dist/lib/handlers/version-manager.d.mts.map +1 -0
- package/types/dist/lib/helpers/config.d.mts.map +1 -1
- package/types/dist/slothlet.d.mts +15 -0
- package/types/dist/slothlet.d.mts.map +1 -1
|
@@ -0,0 +1,773 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright 2026 CLDMV/Shinrai
|
|
3
|
+
|
|
4
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
you may not use this file except in compliance with the License.
|
|
6
|
+
You may obtain a copy of the License at
|
|
7
|
+
|
|
8
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
|
|
10
|
+
Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
See the License for the specific language governing permissions and
|
|
14
|
+
limitations under the License.
|
|
15
|
+
*/
|
|
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
|
+
const existing = this.#versionMetadataByModule.get(ve.moduleID) ?? {};
|
|
216
|
+
|
|
217
|
+
this.#versionMetadataByModule.set(ve.moduleID, {
|
|
218
|
+
...existing,
|
|
219
|
+
...(patch && typeof patch === "object" ? patch : {}),
|
|
220
|
+
version: ve.versionTag,
|
|
221
|
+
logicalPath
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
list(logicalPath) {
|
|
227
|
+
const entry = this.#registry.get(logicalPath);
|
|
228
|
+
if (!entry) return undefined;
|
|
229
|
+
|
|
230
|
+
const versions = {};
|
|
231
|
+
for (const [tag, ve] of entry.versions) {
|
|
232
|
+
versions[tag] = { ...ve };
|
|
233
|
+
}
|
|
234
|
+
return { versions, default: this.getDefaultVersion(logicalPath) };
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
setDefault(logicalPath, versionTag) {
|
|
239
|
+
const entry = this.#registry.get(logicalPath);
|
|
240
|
+
if (!entry) {
|
|
241
|
+
throw new this.SlothletError("VERSION_NOT_FOUND", {
|
|
242
|
+
version: versionTag,
|
|
243
|
+
apiPath: logicalPath
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
if (!entry.versions.has(versionTag)) {
|
|
247
|
+
throw new this.SlothletError("VERSION_NOT_FOUND", {
|
|
248
|
+
version: versionTag,
|
|
249
|
+
apiPath: logicalPath
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
for (const ve of entry.versions.values()) {
|
|
254
|
+
ve.isDefault = false;
|
|
255
|
+
}
|
|
256
|
+
entry.versions.get(versionTag).isDefault = true;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
getDefaultVersion(logicalPath) {
|
|
263
|
+
const entry = this.#registry.get(logicalPath);
|
|
264
|
+
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
if (!entry || entry.versions.size === 0) return null;
|
|
268
|
+
|
|
269
|
+
|
|
270
|
+
for (const [tag, ve] of entry.versions) {
|
|
271
|
+
if (ve.isDefault) return tag;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
const tags = Array.from(entry.versions.keys());
|
|
276
|
+
if (tags.length === 1) return tags[0];
|
|
277
|
+
|
|
278
|
+
|
|
279
|
+
const sorted = tags
|
|
280
|
+
.map((tag) => ({ tag, tuple: normaliseVersionTag(tag) }))
|
|
281
|
+
.sort((a, b) => {
|
|
282
|
+
const cmp = compareTuples(a.tuple, b.tuple);
|
|
283
|
+
if (cmp !== 0) return cmp;
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
|
|
287
|
+
const aSuffix = a.tag.match(/[-+]/) ? 1 : 0;
|
|
288
|
+
|
|
289
|
+
const bSuffix = b.tag.match(/[-+]/) ? 1 : 0;
|
|
290
|
+
return aSuffix - bSuffix;
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
return sorted[0].tag;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
|
|
297
|
+
|
|
298
|
+
|
|
299
|
+
resolveForPath(logicalPath, allVersions, caller) {
|
|
300
|
+
const discriminator = this.slothlet.config?.versionDispatcher ?? "version";
|
|
301
|
+
|
|
302
|
+
let resolvedTag = null;
|
|
303
|
+
|
|
304
|
+
if (typeof discriminator === "string") {
|
|
305
|
+
|
|
306
|
+
resolvedTag = caller.versionMetadata?.[discriminator] ?? null;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
|
|
310
|
+
|
|
311
|
+
if (typeof discriminator === "function") {
|
|
312
|
+
try {
|
|
313
|
+
resolvedTag = discriminator(allVersions, caller);
|
|
314
|
+
} catch {
|
|
315
|
+
resolvedTag = null;
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
if (resolvedTag == null) return null;
|
|
320
|
+
|
|
321
|
+
|
|
322
|
+
const entry = this.#registry.get(logicalPath);
|
|
323
|
+
if (!entry || !entry.versions.has(resolvedTag)) {
|
|
324
|
+
this.slothlet.debug("versioning", {
|
|
325
|
+
key: "DEBUG_VERSION_RESOLVED",
|
|
326
|
+
version: null,
|
|
327
|
+
apiPath: logicalPath,
|
|
328
|
+
|
|
329
|
+
|
|
330
|
+
callerModule: caller?.metadata?.moduleID ?? null
|
|
331
|
+
});
|
|
332
|
+
return null;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
this.slothlet.debug("versioning", {
|
|
336
|
+
key: "DEBUG_VERSION_RESOLVED",
|
|
337
|
+
version: resolvedTag,
|
|
338
|
+
apiPath: logicalPath,
|
|
339
|
+
|
|
340
|
+
|
|
341
|
+
callerModule: caller?.metadata?.moduleID ?? null
|
|
342
|
+
});
|
|
343
|
+
|
|
344
|
+
return resolvedTag;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
|
|
348
|
+
|
|
349
|
+
|
|
350
|
+
buildAllVersionsArg(logicalPath) {
|
|
351
|
+
const entry = this.#registry.get(logicalPath);
|
|
352
|
+
|
|
353
|
+
|
|
354
|
+
|
|
355
|
+
if (!entry) return {};
|
|
356
|
+
|
|
357
|
+
const defaultTag = this.getDefaultVersion(logicalPath);
|
|
358
|
+
const result = {};
|
|
359
|
+
|
|
360
|
+
for (const [tag, ve] of entry.versions) {
|
|
361
|
+
|
|
362
|
+
|
|
363
|
+
|
|
364
|
+
const mountedWrapper = this.#walkApiPath(ve.versionedParts);
|
|
365
|
+
|
|
366
|
+
|
|
367
|
+
const regularMetadata = this.slothlet.handlers.metadata?.getMetadata?.(mountedWrapper) ?? {};
|
|
368
|
+
|
|
369
|
+
const versionMetadata = this.#versionMetadataByModule.get(ve.moduleID) ?? {};
|
|
370
|
+
|
|
371
|
+
result[tag] = {
|
|
372
|
+
version: tag,
|
|
373
|
+
default: tag === defaultTag,
|
|
374
|
+
metadata: regularMetadata,
|
|
375
|
+
versionMetadata
|
|
376
|
+
};
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
return result;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
|
|
383
|
+
buildCallerArg(callerWrapper) {
|
|
384
|
+
|
|
385
|
+
|
|
386
|
+
const callerModuleID = callerWrapper?.____slothletInternal?.moduleID ?? callerWrapper?.__moduleID;
|
|
387
|
+
const callerVersionEntry = callerModuleID ? this.#findVersionEntryForModule(callerModuleID) : null;
|
|
388
|
+
|
|
389
|
+
|
|
390
|
+
const regularMetadata = this.slothlet.handlers.metadata?.getMetadata?.(callerWrapper) ?? {};
|
|
391
|
+
|
|
392
|
+
if (!callerVersionEntry) {
|
|
393
|
+
return {
|
|
394
|
+
version: null,
|
|
395
|
+
default: null,
|
|
396
|
+
metadata: regularMetadata,
|
|
397
|
+
versionMetadata: null
|
|
398
|
+
};
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
const defaultTag = this.getDefaultVersion(callerVersionEntry.logicalPath);
|
|
402
|
+
|
|
403
|
+
|
|
404
|
+
const versionMetadata = this.#versionMetadataByModule.get(callerModuleID) ?? {};
|
|
405
|
+
|
|
406
|
+
return {
|
|
407
|
+
version: callerVersionEntry.versionTag,
|
|
408
|
+
default: callerVersionEntry.versionTag === defaultTag,
|
|
409
|
+
metadata: regularMetadata,
|
|
410
|
+
versionMetadata
|
|
411
|
+
};
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
|
|
415
|
+
#findVersionEntryForModule(moduleID) {
|
|
416
|
+
const key = this.#moduleToVersionKey.get(moduleID);
|
|
417
|
+
|
|
418
|
+
|
|
419
|
+
if (!key) return null;
|
|
420
|
+
const entry = this.#registry.get(key.logicalPath);
|
|
421
|
+
|
|
422
|
+
|
|
423
|
+
|
|
424
|
+
if (!entry) return null;
|
|
425
|
+
|
|
426
|
+
|
|
427
|
+
return entry.versions.get(key.versionTag) ?? null;
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
|
|
431
|
+
#walkApiPath(apiPath) {
|
|
432
|
+
|
|
433
|
+
|
|
434
|
+
if (!apiPath) return undefined;
|
|
435
|
+
let node = this.slothlet.api;
|
|
436
|
+
|
|
437
|
+
|
|
438
|
+
const segments = Array.isArray(apiPath) ? apiPath : apiPath.split(".");
|
|
439
|
+
for (const segment of segments) {
|
|
440
|
+
|
|
441
|
+
|
|
442
|
+
if (node == null) return undefined;
|
|
443
|
+
node = node[segment];
|
|
444
|
+
}
|
|
445
|
+
return node;
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
|
|
449
|
+
|
|
450
|
+
|
|
451
|
+
createDispatcher(logicalPath) {
|
|
452
|
+
const manager = this;
|
|
453
|
+
const target = { __isVersionDispatcher: true, __logicalPath: logicalPath };
|
|
454
|
+
const displayName = logicalPath.split(".").pop();
|
|
455
|
+
|
|
456
|
+
|
|
457
|
+
const resolveVersion = () => {
|
|
458
|
+
|
|
459
|
+
|
|
460
|
+
|
|
461
|
+
const ctx = manager.slothlet.contextManager?.tryGetContext?.();
|
|
462
|
+
const forcedVersion = ctx?.context?.[FORCE_VERSION_SYMBOL];
|
|
463
|
+
if (forcedVersion) {
|
|
464
|
+
const entry = manager.#registry.get(logicalPath);
|
|
465
|
+
if (entry?.versions.has(forcedVersion)) return forcedVersion;
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
|
|
469
|
+
|
|
470
|
+
const callerWrapper = ctx?.currentWrapper ?? null;
|
|
471
|
+
const allVersions = manager.buildAllVersionsArg(logicalPath);
|
|
472
|
+
const caller = manager.buildCallerArg(callerWrapper);
|
|
473
|
+
|
|
474
|
+
|
|
475
|
+
let tag = manager.resolveForPath(logicalPath, allVersions, caller);
|
|
476
|
+
|
|
477
|
+
if (tag == null) {
|
|
478
|
+
|
|
479
|
+
tag = manager.getDefaultVersion(logicalPath);
|
|
480
|
+
|
|
481
|
+
|
|
482
|
+
|
|
483
|
+
if (tag != null) {
|
|
484
|
+
manager.slothlet.debug("versioning", {
|
|
485
|
+
key: "DEBUG_VERSION_DEFAULT_USED",
|
|
486
|
+
apiPath: logicalPath,
|
|
487
|
+
version: tag
|
|
488
|
+
});
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
return tag;
|
|
493
|
+
};
|
|
494
|
+
|
|
495
|
+
|
|
496
|
+
const resolveVersionedWrapper = () => {
|
|
497
|
+
const versionTag = resolveVersion();
|
|
498
|
+
|
|
499
|
+
|
|
500
|
+
|
|
501
|
+
if (!versionTag) return null;
|
|
502
|
+
|
|
503
|
+
|
|
504
|
+
return manager.#walkApiPath([versionTag, ...logicalPath.split(".")]);
|
|
505
|
+
};
|
|
506
|
+
|
|
507
|
+
const handlers = {
|
|
508
|
+
|
|
509
|
+
get(t, prop) {
|
|
510
|
+
|
|
511
|
+
if (typeof prop === "string") {
|
|
512
|
+
if (prop === "____slothletInternal" || prop === "_impl" || prop === "__impl" || prop === "__state" || prop === "__invalid") {
|
|
513
|
+
return undefined;
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
|
|
518
|
+
if (prop === "__isVersionDispatcher") return true;
|
|
519
|
+
|
|
520
|
+
|
|
521
|
+
if (prop === "__mode") return "eager";
|
|
522
|
+
if (prop === "__apiPath") return logicalPath;
|
|
523
|
+
|
|
524
|
+
|
|
525
|
+
if (prop === "__slothletPath") return logicalPath;
|
|
526
|
+
if (prop === "__isCallable") return false;
|
|
527
|
+
|
|
528
|
+
|
|
529
|
+
|
|
530
|
+
|
|
531
|
+
if (prop === "__materializeOnCreate") return false;
|
|
532
|
+
if (prop === "__materialized") return true;
|
|
533
|
+
if (prop === "__inFlight") return false;
|
|
534
|
+
if (prop === "__displayName") return displayName;
|
|
535
|
+
if (prop === "__moduleID") return `versionDispatcher:${logicalPath}`;
|
|
536
|
+
if (prop === "_materialize") return () => {};
|
|
537
|
+
if (prop === "length") return 0;
|
|
538
|
+
if (prop === "name") return displayName;
|
|
539
|
+
|
|
540
|
+
|
|
541
|
+
|
|
542
|
+
|
|
543
|
+
|
|
544
|
+
if (prop === "then") return undefined;
|
|
545
|
+
|
|
546
|
+
|
|
547
|
+
|
|
548
|
+
|
|
549
|
+
if (prop === "constructor") return Object.prototype.constructor;
|
|
550
|
+
|
|
551
|
+
|
|
552
|
+
if (prop === Symbol.toStringTag) {
|
|
553
|
+
const vw = resolveVersionedWrapper();
|
|
554
|
+
|
|
555
|
+
|
|
556
|
+
if (!vw) return "Object";
|
|
557
|
+
return vw[Symbol.toStringTag];
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
|
|
561
|
+
|
|
562
|
+
|
|
563
|
+
|
|
564
|
+
|
|
565
|
+
if (typeof prop === "symbol" && prop.toString() === "Symbol(nodejs.util.inspect.custom)") {
|
|
566
|
+
return () => {
|
|
567
|
+
const vw = resolveVersionedWrapper();
|
|
568
|
+
if (vw) {
|
|
569
|
+
try {
|
|
570
|
+
return inspect(vw);
|
|
571
|
+
} catch {
|
|
572
|
+
|
|
573
|
+
void 0;
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
const entry = manager.#registry.get(logicalPath);
|
|
578
|
+
const versions = entry ? Array.from(entry.versions.keys()) : [];
|
|
579
|
+
return { __versionDispatcher: logicalPath, versions };
|
|
580
|
+
};
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
|
|
584
|
+
|
|
585
|
+
if (prop === "toString") return () => `[VersionDispatcher: ${logicalPath}]`;
|
|
586
|
+
|
|
587
|
+
|
|
588
|
+
if (prop === "valueOf") return () => dispatcherProxy;
|
|
589
|
+
|
|
590
|
+
|
|
591
|
+
if (prop === "toJSON") return () => undefined;
|
|
592
|
+
|
|
593
|
+
|
|
594
|
+
if (typeof prop === "symbol") return undefined;
|
|
595
|
+
|
|
596
|
+
|
|
597
|
+
if (prop === "__metadata" || prop === "__filePath" || prop === "__sourceFolder" || prop === "__type") {
|
|
598
|
+
const vw = resolveVersionedWrapper();
|
|
599
|
+
|
|
600
|
+
|
|
601
|
+
|
|
602
|
+
if (!vw) return undefined;
|
|
603
|
+
return vw[prop];
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
|
|
607
|
+
|
|
608
|
+
const versionTag = resolveVersion();
|
|
609
|
+
|
|
610
|
+
|
|
611
|
+
|
|
612
|
+
if (!versionTag) {
|
|
613
|
+
throw new manager.SlothletError("VERSION_NO_DEFAULT", {
|
|
614
|
+
apiPath: logicalPath
|
|
615
|
+
});
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
|
|
619
|
+
const versionedWrapper = manager.#walkApiPath([versionTag, ...logicalPath.split(".")]);
|
|
620
|
+
|
|
621
|
+
|
|
622
|
+
|
|
623
|
+
if (!versionedWrapper) return undefined;
|
|
624
|
+
|
|
625
|
+
return versionedWrapper[prop];
|
|
626
|
+
},
|
|
627
|
+
|
|
628
|
+
|
|
629
|
+
|
|
630
|
+
apply() {
|
|
631
|
+
throw new manager.SlothletError("VERSION_DISPATCH_NOT_CALLABLE", {
|
|
632
|
+
apiPath: logicalPath
|
|
633
|
+
});
|
|
634
|
+
},
|
|
635
|
+
|
|
636
|
+
|
|
637
|
+
has(t, key) {
|
|
638
|
+
|
|
639
|
+
|
|
640
|
+
|
|
641
|
+
if (Reflect.has(t, key)) return true;
|
|
642
|
+
const entry = manager.#registry.get(logicalPath);
|
|
643
|
+
|
|
644
|
+
|
|
645
|
+
if (!entry) return false;
|
|
646
|
+
for (const ve of entry.versions.values()) {
|
|
647
|
+
const vw = manager.#walkApiPath(ve.versionedParts);
|
|
648
|
+
|
|
649
|
+
|
|
650
|
+
|
|
651
|
+
|
|
652
|
+
|
|
653
|
+
if (vw && key in vw) return true;
|
|
654
|
+
}
|
|
655
|
+
return false;
|
|
656
|
+
},
|
|
657
|
+
|
|
658
|
+
|
|
659
|
+
ownKeys(t) {
|
|
660
|
+
const keySet = new Set(Reflect.ownKeys(t));
|
|
661
|
+
const entry = manager.#registry.get(logicalPath);
|
|
662
|
+
|
|
663
|
+
|
|
664
|
+
if (entry) {
|
|
665
|
+
for (const ve of entry.versions.values()) {
|
|
666
|
+
const vw = manager.#walkApiPath(ve.versionedParts);
|
|
667
|
+
|
|
668
|
+
|
|
669
|
+
if (vw) {
|
|
670
|
+
for (const k of Reflect.ownKeys(Object(vw))) {
|
|
671
|
+
keySet.add(k);
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
}
|
|
675
|
+
}
|
|
676
|
+
return Array.from(keySet);
|
|
677
|
+
},
|
|
678
|
+
|
|
679
|
+
|
|
680
|
+
getOwnPropertyDescriptor(t, prop) {
|
|
681
|
+
|
|
682
|
+
const targetDesc = Reflect.getOwnPropertyDescriptor(t, prop);
|
|
683
|
+
if (targetDesc) return { configurable: true, enumerable: true, writable: false, value: t[prop] };
|
|
684
|
+
|
|
685
|
+
|
|
686
|
+
|
|
687
|
+
|
|
688
|
+
return { configurable: true, enumerable: true, writable: false, value: undefined };
|
|
689
|
+
}
|
|
690
|
+
};
|
|
691
|
+
|
|
692
|
+
let dispatcherProxy;
|
|
693
|
+
dispatcherProxy = new Proxy(target, handlers);
|
|
694
|
+
return dispatcherProxy;
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
|
|
698
|
+
updateDispatcher(logicalPath) {
|
|
699
|
+
|
|
700
|
+
|
|
701
|
+
if (this.#dispatchers.has(logicalPath)) {
|
|
702
|
+
return;
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
const dispatcher = this.createDispatcher(logicalPath);
|
|
706
|
+
this.#dispatchers.set(logicalPath, dispatcher);
|
|
707
|
+
|
|
708
|
+
const parts = logicalPath.split(".");
|
|
709
|
+
const mountOptions = {
|
|
710
|
+
collisionMode: "replace",
|
|
711
|
+
moduleID: `versionDispatcher:${logicalPath}`,
|
|
712
|
+
allowOverwrite: true,
|
|
713
|
+
mutateExisting: false
|
|
714
|
+
};
|
|
715
|
+
|
|
716
|
+
|
|
717
|
+
|
|
718
|
+
|
|
719
|
+
if (this.slothlet.api) {
|
|
720
|
+
this.slothlet.handlers.apiManager.setValueAtPath(this.slothlet.api, parts, dispatcher, mountOptions);
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
|
|
724
|
+
if (this.slothlet.boundApi) {
|
|
725
|
+
this.slothlet.handlers.apiManager.setValueAtPath(this.slothlet.boundApi, parts, dispatcher, mountOptions);
|
|
726
|
+
}
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
|
|
730
|
+
teardownDispatcher(logicalPath) {
|
|
731
|
+
this.#dispatchers.delete(logicalPath);
|
|
732
|
+
|
|
733
|
+
const parts = logicalPath.split(".");
|
|
734
|
+
|
|
735
|
+
|
|
736
|
+
|
|
737
|
+
if (this.slothlet.api) {
|
|
738
|
+
this.slothlet.handlers.apiManager.deletePath(this.slothlet.api, parts).catch(() => {});
|
|
739
|
+
}
|
|
740
|
+
if (this.slothlet.boundApi) {
|
|
741
|
+
this.slothlet.handlers.apiManager.deletePath(this.slothlet.boundApi, parts).catch(() => {});
|
|
742
|
+
}
|
|
743
|
+
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
|
|
747
|
+
|
|
748
|
+
|
|
749
|
+
onVersionedModuleReload(moduleID) {
|
|
750
|
+
const key = this.#moduleToVersionKey.get(moduleID);
|
|
751
|
+
if (!key) return;
|
|
752
|
+
|
|
753
|
+
const { logicalPath } = key;
|
|
754
|
+
|
|
755
|
+
|
|
756
|
+
this.updateDispatcher(logicalPath);
|
|
757
|
+
|
|
758
|
+
this.slothlet.debug("versioning", {
|
|
759
|
+
key: "DEBUG_VERSION_REGISTERED",
|
|
760
|
+
version: key.versionTag,
|
|
761
|
+
logicalPath,
|
|
762
|
+
moduleID
|
|
763
|
+
});
|
|
764
|
+
}
|
|
765
|
+
|
|
766
|
+
|
|
767
|
+
shutdown() {
|
|
768
|
+
this.#registry.clear();
|
|
769
|
+
this.#versionMetadataByModule.clear();
|
|
770
|
+
this.#moduleToVersionKey.clear();
|
|
771
|
+
this.#dispatchers.clear();
|
|
772
|
+
}
|
|
773
|
+
}
|