@peers-app/peers-sdk 0.25.21 → 0.26.1
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/dist/contracts/__tests__/builder.test.js +0 -2
- package/dist/contracts/__tests__/contract-provider-router.test.js +51 -0
- package/dist/contracts/__tests__/contract-proxy.test.js +21 -0
- package/dist/contracts/__tests__/contract-resolvers.test.js +84 -0
- package/dist/contracts/__tests__/legacy-removal.test.d.ts +1 -0
- package/dist/contracts/__tests__/legacy-removal.test.js +90 -0
- package/dist/contracts/__tests__/system-contracts.test.js +88 -0
- package/dist/contracts/__tests__/tool-catalog-inventory.test.d.ts +1 -0
- package/dist/contracts/__tests__/tool-catalog-inventory.test.js +44 -0
- package/dist/contracts/__tests__/tool-catalog-service.test.d.ts +1 -0
- package/dist/contracts/__tests__/tool-catalog-service.test.js +446 -0
- package/dist/contracts/__tests__/tool-catalog.test.d.ts +1 -0
- package/dist/contracts/__tests__/tool-catalog.test.js +924 -0
- package/dist/contracts/__tests__/workflow-context.test.d.ts +1 -0
- package/dist/contracts/__tests__/workflow-context.test.js +61 -0
- package/dist/contracts/builder.d.ts +9 -8
- package/dist/contracts/builder.js +34 -16
- package/dist/contracts/contract-provider-router.d.ts +12 -1
- package/dist/contracts/contract-provider-router.js +40 -2
- package/dist/contracts/contract-proxy.d.ts +20 -6
- package/dist/contracts/contract-proxy.js +23 -12
- package/dist/contracts/contract-resolvers.d.ts +22 -3
- package/dist/contracts/contract-resolvers.js +86 -7
- package/dist/contracts/extract.d.ts +7 -0
- package/dist/contracts/extract.js +3 -0
- package/dist/contracts/index.d.ts +11 -4
- package/dist/contracts/index.js +14 -2
- package/dist/contracts/registry.d.ts +3 -0
- package/dist/contracts/registry.js +2 -0
- package/dist/contracts/system/desktop-tools-contract.d.ts +5 -0
- package/dist/contracts/system/desktop-tools-contract.js +88 -0
- package/dist/contracts/system/host-tools-contract.d.ts +5 -0
- package/dist/contracts/system/host-tools-contract.js +76 -0
- package/dist/contracts/system/index.d.ts +3 -0
- package/dist/contracts/system/index.js +3 -0
- package/dist/contracts/system/tool-catalog-contract.d.ts +73 -0
- package/dist/contracts/system/tool-catalog-contract.js +70 -0
- package/dist/contracts/system-contracts.d.ts +3 -1
- package/dist/contracts/system-contracts.js +20 -6
- package/dist/contracts/tool-catalog-identity.d.ts +35 -0
- package/dist/contracts/tool-catalog-identity.js +88 -0
- package/dist/contracts/tool-catalog-migrations.d.ts +12 -0
- package/dist/contracts/tool-catalog-migrations.js +179 -0
- package/dist/contracts/tool-catalog-service.d.ts +30 -0
- package/dist/contracts/tool-catalog-service.js +287 -0
- package/dist/contracts/tool-catalog.d.ts +96 -0
- package/dist/contracts/tool-catalog.js +542 -0
- package/dist/contracts/types.d.ts +7 -5
- package/dist/data/messages.d.ts +3 -3
- package/dist/data/orm/sql.data-source.d.ts +5 -0
- package/dist/data/orm/sql.data-source.js +28 -10
- package/dist/data/orm/sql.data-source.test.d.ts +1 -1
- package/dist/data/orm/sql.data-source.test.js +160 -0
- package/dist/data/orm/table-container.d.ts +14 -0
- package/dist/data/orm/table-container.js +20 -0
- package/dist/data/tool-access-approvals.d.ts +6 -6
- package/dist/data/tool-catalog-fields.d.ts +31 -0
- package/dist/data/tool-catalog-fields.js +67 -0
- package/dist/data/tools.d.ts +38 -10
- package/dist/data/tools.js +31 -8
- package/dist/data/workflow-logs.d.ts +6 -2
- package/dist/data/workflow-logs.js +7 -5
- package/dist/data/workflow-runs.d.ts +6 -6
- package/dist/index.d.ts +0 -2
- package/dist/index.js +1 -3
- package/dist/package-loader/contract-package-loader.d.ts +12 -3
- package/dist/package-loader/contract-package-loader.js +54 -50
- package/dist/package-loader/contract-package-loader.test.d.ts +1 -1
- package/dist/package-loader/contract-package-loader.test.js +172 -13
- package/dist/package-loader/host-authority-loading.test.js +16 -3
- package/dist/package-loader/isolated-runtime.d.ts +14 -2
- package/dist/package-loader/isolated-table-gateway.d.ts +3 -1
- package/dist/package-loader/isolated-table-gateway.js +54 -5
- package/dist/package-loader/isolated-table-gateway.test.js +123 -0
- package/dist/package-loader/package-loader.d.ts +14 -2
- package/dist/package-loader/package-loader.js +184 -62
- package/dist/package-loader/package-loader.test.js +674 -1
- package/dist/package-loader/resolve-consumed-contracts.js +1 -1
- package/dist/system-ids.d.ts +6 -0
- package/dist/system-ids.js +7 -1
- package/dist/types/peers-package.d.ts +3 -2
- package/dist/types/workflow-run-context.d.ts +12 -2
- package/package.json +1 -1
- package/dist/tools/index.d.ts +0 -1
- package/dist/tools/index.js +0 -17
- package/dist/tools/tools-factory.d.ts +0 -5
- package/dist/tools/tools-factory.js +0 -34
|
@@ -8,13 +8,11 @@ const contract_resolvers_1 = require("../contracts/contract-resolvers");
|
|
|
8
8
|
const contract_transport_1 = require("../contracts/contract-transport");
|
|
9
9
|
const registry_1 = require("../contracts/registry");
|
|
10
10
|
const remote_contract_transport_1 = require("../contracts/remote-contract-transport");
|
|
11
|
-
const
|
|
11
|
+
const tool_catalog_1 = require("../contracts/tool-catalog");
|
|
12
12
|
const files_1 = require("../data/files");
|
|
13
13
|
const package_version_resolver_1 = require("../data/package-version-resolver");
|
|
14
14
|
const package_versions_1 = require("../data/package-versions");
|
|
15
15
|
const packages_1 = require("../data/packages");
|
|
16
|
-
const tools_1 = require("../data/tools");
|
|
17
|
-
const tools_2 = require("../tools");
|
|
18
16
|
const contract_package_loader_1 = require("./contract-package-loader");
|
|
19
17
|
const isolated_artifact_1 = require("./isolated-artifact");
|
|
20
18
|
const isolated_persistent_var_gateway_1 = require("./isolated-persistent-var-gateway");
|
|
@@ -46,17 +44,27 @@ class PackageLoader {
|
|
|
46
44
|
/** Host-injected Zod module used as the `zod` require. */
|
|
47
45
|
static Zod;
|
|
48
46
|
hostConsumes = new Map();
|
|
47
|
+
loadGenerations = new Map();
|
|
48
|
+
hostActivationGenerations = new Map();
|
|
49
|
+
consumedContractRouters = new Set();
|
|
50
|
+
disposed = false;
|
|
49
51
|
packageInstances = {};
|
|
50
|
-
/** Packages
|
|
52
|
+
/** Packages installed through definePackage or isolation; providers own their executable members. */
|
|
51
53
|
contractInstalledPackages = new Set();
|
|
52
54
|
/** Packages installed through the isolated runtime (no host eval). */
|
|
53
55
|
isolatedPackageIds = new Set();
|
|
54
56
|
/**
|
|
55
57
|
* In-memory contract providers for this data context. Seeded with system
|
|
56
|
-
* contracts
|
|
58
|
+
* contracts after catalog readiness; package providers are added via
|
|
57
59
|
* {@link installContractPackage} or the isolated runtime.
|
|
58
60
|
*/
|
|
59
61
|
contractRegistry;
|
|
62
|
+
/** Permanent tool metadata owner for this context. */
|
|
63
|
+
toolCatalog;
|
|
64
|
+
/** Lazy, retryable readiness boundary; performs no constructor-time database work. */
|
|
65
|
+
get catalogReady() {
|
|
66
|
+
return this.toolCatalog.ready();
|
|
67
|
+
}
|
|
60
68
|
/** Optional isolated-provider supervisor created from the host factory. */
|
|
61
69
|
isolatedRuntime;
|
|
62
70
|
/** Optional host `require` used when reading local bundles or evaluating legacy packages. */
|
|
@@ -69,7 +77,7 @@ class PackageLoader {
|
|
|
69
77
|
constructor(dataContext) {
|
|
70
78
|
this.dataContext = dataContext;
|
|
71
79
|
this.contractRegistry = new registry_1.ContractRegistry();
|
|
72
|
-
(
|
|
80
|
+
this.toolCatalog = new tool_catalog_1.ToolCatalog(dataContext, this.contractRegistry);
|
|
73
81
|
const factory = (0, isolated_runtime_1.getIsolatedPackageRuntimeFactory)();
|
|
74
82
|
if (factory) {
|
|
75
83
|
this.isolatedRuntime = factory({
|
|
@@ -194,6 +202,7 @@ class PackageLoader {
|
|
|
194
202
|
* @throws If no provider or matching endpoint implementation is available.
|
|
195
203
|
*/
|
|
196
204
|
async resolveContractEndpoint(request, opts = {}) {
|
|
205
|
+
await this.catalogReady;
|
|
197
206
|
// A route that names another device is forwarded over the mesh instead of being
|
|
198
207
|
// resolved against a local provider. Only routers serving local principals set
|
|
199
208
|
// targetDeviceId; remote-caller routers reject it before reaching this point.
|
|
@@ -208,7 +217,7 @@ class PackageLoader {
|
|
|
208
217
|
request.providerPackageId ??
|
|
209
218
|
this.contractRegistry.getProviderPackageId(request.contractId, request.version);
|
|
210
219
|
if (!providerPackageId) {
|
|
211
|
-
const legacyResolver = (0, contract_resolvers_1.getContractResolver)(request.contractId, request.version);
|
|
220
|
+
const legacyResolver = (0, contract_resolvers_1.getContractResolver)(request.contractId, request.version, undefined, this.dataContext);
|
|
212
221
|
if (!legacyResolver) {
|
|
213
222
|
throw new Error(`No contract provider registered for ${request.contractId} v${request.version}`);
|
|
214
223
|
}
|
|
@@ -222,10 +231,28 @@ class PackageLoader {
|
|
|
222
231
|
},
|
|
223
232
|
}
|
|
224
233
|
: resolution;
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
234
|
+
const assertProviderCurrent = () => {
|
|
235
|
+
if (this.disposed ||
|
|
236
|
+
this.contractRegistry.getProviderPackageId(request.contractId, request.version) ||
|
|
237
|
+
(0, contract_resolvers_1.getContractResolver)(request.contractId, request.version, undefined, this.dataContext) !==
|
|
238
|
+
legacyResolver)
|
|
239
|
+
throw new contract_provider_router_1.RetiredContractProviderEndpointError(guardedEndpoint);
|
|
240
|
+
};
|
|
241
|
+
const endpoint = (0, contract_proxy_1.createContractProviderEndpoint)(this.guardResolutionTools(localResolution, request, assertProviderCurrent), { context: request.context, notify: request.notify });
|
|
242
|
+
const guardedEndpoint = {
|
|
243
|
+
handleCall: async (call, context) => {
|
|
244
|
+
if (call.member === "tool")
|
|
245
|
+
await this.toolCatalog.assertTargetUsable({
|
|
246
|
+
contractId: request.contractId,
|
|
247
|
+
version: request.version,
|
|
248
|
+
toolName: call.name,
|
|
249
|
+
});
|
|
250
|
+
assertProviderCurrent();
|
|
251
|
+
return endpoint.handleCall(call, context);
|
|
252
|
+
},
|
|
253
|
+
dispose: () => endpoint.dispose(),
|
|
254
|
+
};
|
|
255
|
+
return guardedEndpoint;
|
|
229
256
|
}
|
|
230
257
|
if (!this.contractRegistry
|
|
231
258
|
.getRegisteredProviderIds(request.contractId, request.version)
|
|
@@ -247,10 +274,11 @@ class PackageLoader {
|
|
|
247
274
|
contractId: request.contractId,
|
|
248
275
|
version: request.version,
|
|
249
276
|
};
|
|
250
|
-
const resolver = (0, contract_resolvers_1.getContractResolver)(implementation.contractId, implementation.version, providerPackageId);
|
|
277
|
+
const resolver = (0, contract_resolvers_1.getContractResolver)(implementation.contractId, implementation.version, providerPackageId, this.dataContext);
|
|
251
278
|
if (!resolver) {
|
|
252
279
|
throw new Error(`Provider ${providerPackageId} has no live resolver for ${request.contractId} v${request.version}`);
|
|
253
280
|
}
|
|
281
|
+
const generation = this.hostActivationGenerations.get(providerPackageId);
|
|
254
282
|
const resolution = await resolver({ dataContext: this.dataContext });
|
|
255
283
|
const localResolution = opts.resolveVariable
|
|
256
284
|
? {
|
|
@@ -261,7 +289,16 @@ class PackageLoader {
|
|
|
261
289
|
},
|
|
262
290
|
}
|
|
263
291
|
: resolution;
|
|
264
|
-
const
|
|
292
|
+
const assertProviderCurrent = () => {
|
|
293
|
+
const current = this.contractRegistry.getProviderImplementation(request.contractId, request.version, providerPackageId);
|
|
294
|
+
if (this.disposed ||
|
|
295
|
+
this.hostActivationGenerations.get(providerPackageId) !== generation ||
|
|
296
|
+
current?.contractId !== implementation.contractId ||
|
|
297
|
+
current?.version !== implementation.version ||
|
|
298
|
+
(0, contract_resolvers_1.getContractResolver)(implementation.contractId, implementation.version, providerPackageId, this.dataContext) !== resolver)
|
|
299
|
+
throw new contract_provider_router_1.RetiredContractProviderEndpointError(guardedEndpoint);
|
|
300
|
+
};
|
|
301
|
+
const endpoint = (0, contract_proxy_1.createContractProviderEndpoint)(this.guardResolutionTools(localResolution, request, assertProviderCurrent), {
|
|
265
302
|
dispatchTool: (target, args, context) => this.dispatchToolFromProvider(providerPackageId, target, args, context),
|
|
266
303
|
context: request.context,
|
|
267
304
|
notify: (notification) => request.notify({
|
|
@@ -269,18 +306,45 @@ class PackageLoader {
|
|
|
269
306
|
contractId: request.contractId,
|
|
270
307
|
}),
|
|
271
308
|
});
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
309
|
+
const guardedEndpoint = {
|
|
310
|
+
handleCall: async (call, context) => {
|
|
311
|
+
if (call.member === "tool")
|
|
312
|
+
await this.toolCatalog.assertTargetUsable({
|
|
313
|
+
contractId: request.contractId,
|
|
314
|
+
version: request.version,
|
|
315
|
+
toolName: call.name,
|
|
316
|
+
});
|
|
317
|
+
assertProviderCurrent();
|
|
318
|
+
return endpoint.handleCall({ ...call, contractId: implementation.contractId, version: implementation.version }, context);
|
|
319
|
+
},
|
|
282
320
|
dispose: () => endpoint.dispose(),
|
|
283
321
|
};
|
|
322
|
+
return guardedEndpoint;
|
|
323
|
+
}
|
|
324
|
+
guardResolutionTools(resolution, request, assertProviderCurrent) {
|
|
325
|
+
const guard = (instances) => instances &&
|
|
326
|
+
Object.fromEntries(Object.entries(instances).map(([name, instance]) => [
|
|
327
|
+
name,
|
|
328
|
+
{
|
|
329
|
+
...instance,
|
|
330
|
+
// The dispatcher awaits authorization before calling toolFn. Check again
|
|
331
|
+
// here so those awaits cannot retain old code. Once provider code has
|
|
332
|
+
// started, its nested action errors must not request a router replay.
|
|
333
|
+
toolFn: async (...args) => {
|
|
334
|
+
await this.toolCatalog.assertTargetUsable({
|
|
335
|
+
contractId: request.contractId,
|
|
336
|
+
version: request.version,
|
|
337
|
+
toolName: name,
|
|
338
|
+
});
|
|
339
|
+
assertProviderCurrent();
|
|
340
|
+
return instance.toolFn(...args);
|
|
341
|
+
},
|
|
342
|
+
},
|
|
343
|
+
]));
|
|
344
|
+
return {
|
|
345
|
+
...resolution,
|
|
346
|
+
tools: guard(resolution.tools),
|
|
347
|
+
};
|
|
284
348
|
}
|
|
285
349
|
/**
|
|
286
350
|
* Bind a legacy package's declared consume to its selected provider.
|
|
@@ -293,6 +357,7 @@ class PackageLoader {
|
|
|
293
357
|
* @returns A live consumer proxy, or `undefined` when no provider matches.
|
|
294
358
|
*/
|
|
295
359
|
async resolveConsumedContract(dependency) {
|
|
360
|
+
await this.catalogReady;
|
|
296
361
|
const selected = this.contractRegistry.selectProvider(dependency);
|
|
297
362
|
if (!selected) {
|
|
298
363
|
return undefined;
|
|
@@ -310,10 +375,12 @@ class PackageLoader {
|
|
|
310
375
|
const consumer = (0, contract_proxy_1.createContractConsumer)(selected.definition, consumerTransport, {
|
|
311
376
|
dataContextId: this.dataContext.dataContextId,
|
|
312
377
|
});
|
|
378
|
+
this.consumedContractRouters.add(router);
|
|
313
379
|
const dispose = consumer.dispose.bind(consumer);
|
|
314
380
|
consumer.dispose = () => {
|
|
315
381
|
dispose();
|
|
316
382
|
router.dispose();
|
|
383
|
+
this.consumedContractRouters.delete(router);
|
|
317
384
|
};
|
|
318
385
|
return consumer;
|
|
319
386
|
}
|
|
@@ -331,6 +398,7 @@ class PackageLoader {
|
|
|
331
398
|
* @returns A per-device consumer factory, or `undefined` when the contract is unknown.
|
|
332
399
|
*/
|
|
333
400
|
async resolveRemoteContract(declaration) {
|
|
401
|
+
await this.catalogReady;
|
|
334
402
|
const definition = this.contractRegistry.getDefinition(declaration.contractId, declaration.version);
|
|
335
403
|
if (!definition) {
|
|
336
404
|
return undefined;
|
|
@@ -376,26 +444,50 @@ class PackageLoader {
|
|
|
376
444
|
* @param packageId - Package to unload.
|
|
377
445
|
*/
|
|
378
446
|
async unloadPackage(packageId) {
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
this.
|
|
384
|
-
this.
|
|
385
|
-
|
|
386
|
-
|
|
447
|
+
// Invalidate loads before the first await, including those still resolving
|
|
448
|
+
// bundles or consumed contracts outside the catalog publication queue.
|
|
449
|
+
this.loadGenerations.set(packageId, (this.loadGenerations.get(packageId) ?? 0) + 1);
|
|
450
|
+
// Uninstall also queues behind an install that has not published yet.
|
|
451
|
+
await this.isolatedRuntime?.uninstall(packageId);
|
|
452
|
+
await this.toolCatalog.removeProvider(packageId, () => {
|
|
453
|
+
this.retireHostEndpoints(packageId);
|
|
454
|
+
(0, contract_resolvers_1.unregisterPackageContractResolvers)(this.dataContext, packageId);
|
|
455
|
+
this.hostConsumes.delete(packageId);
|
|
456
|
+
this.isolatedPackageIds.delete(packageId);
|
|
457
|
+
this.contractInstalledPackages.delete(packageId);
|
|
458
|
+
delete this.packageInstances[packageId];
|
|
459
|
+
});
|
|
387
460
|
}
|
|
388
461
|
/**
|
|
389
462
|
* Dispose the isolated runtime for this data context.
|
|
390
463
|
*/
|
|
391
464
|
async dispose() {
|
|
465
|
+
this.disposed = true;
|
|
466
|
+
for (const router of this.consumedContractRouters)
|
|
467
|
+
router.dispose();
|
|
468
|
+
this.consumedContractRouters.clear();
|
|
469
|
+
await this.toolCatalog.dispose();
|
|
392
470
|
if (this.isolatedRuntime) {
|
|
393
471
|
await this.isolatedRuntime.dispose();
|
|
394
472
|
}
|
|
473
|
+
for (const packageId of new Set([
|
|
474
|
+
...Object.keys(this.packageInstances),
|
|
475
|
+
...this.contractInstalledPackages,
|
|
476
|
+
...this.hostConsumes.keys(),
|
|
477
|
+
])) {
|
|
478
|
+
this.contractRegistry.unregister(packageId);
|
|
479
|
+
(0, contract_resolvers_1.unregisterPackageContractResolvers)(this.dataContext, packageId);
|
|
480
|
+
}
|
|
481
|
+
this.hostConsumes.clear();
|
|
395
482
|
this.isolatedPackageIds.clear();
|
|
396
483
|
this.contractInstalledPackages.clear();
|
|
397
484
|
this.packageInstances = {};
|
|
398
485
|
}
|
|
486
|
+
retireHostEndpoints(packageId) {
|
|
487
|
+
this.hostActivationGenerations.set(packageId, (this.hostActivationGenerations.get(packageId) ?? 0) + 1);
|
|
488
|
+
for (const router of this.consumedContractRouters)
|
|
489
|
+
router.invalidateProvider(packageId);
|
|
490
|
+
}
|
|
399
491
|
/**
|
|
400
492
|
* Resolve and load every non-disabled package in this data context.
|
|
401
493
|
* Disabled packages that were previously loaded are unloaded.
|
|
@@ -403,6 +495,7 @@ class PackageLoader {
|
|
|
403
495
|
* @param opts - Forwarded to {@link resolveDevicePackageVersion}.
|
|
404
496
|
*/
|
|
405
497
|
async loadAllPackages(opts) {
|
|
498
|
+
await this.catalogReady;
|
|
406
499
|
const packages = await (0, packages_1.Packages)(this.dataContext).list();
|
|
407
500
|
await Promise.all(packages.map(async (pkg) => {
|
|
408
501
|
if (pkg.disabled) {
|
|
@@ -434,6 +527,7 @@ class PackageLoader {
|
|
|
434
527
|
await this.unloadPackage(pkg.packageId);
|
|
435
528
|
return undefined;
|
|
436
529
|
}
|
|
530
|
+
const assertCurrent = this.guardLoad(pkg.packageId);
|
|
437
531
|
if (this.packageInstances[pkg.packageId] && !opts?.force) {
|
|
438
532
|
return this.packageInstances[pkg.packageId];
|
|
439
533
|
}
|
|
@@ -478,7 +572,7 @@ class PackageLoader {
|
|
|
478
572
|
}
|
|
479
573
|
if (!bundleCode)
|
|
480
574
|
return;
|
|
481
|
-
return await this._loadBundle(pkg, bundleCode, selectedPackageVersion, usingLocalBundle, opts?.force === true);
|
|
575
|
+
return await this._loadBundle(pkg, bundleCode, selectedPackageVersion, usingLocalBundle, opts?.force === true, assertCurrent);
|
|
482
576
|
}
|
|
483
577
|
catch (err) {
|
|
484
578
|
if (isIsolatedArtifactError(err)) {
|
|
@@ -489,7 +583,7 @@ class PackageLoader {
|
|
|
489
583
|
try {
|
|
490
584
|
const localBundleCode = this._readLocalBundle(localPath);
|
|
491
585
|
if (localBundleCode) {
|
|
492
|
-
return await this._loadBundle(pkg, localBundleCode, selectedPackageVersion, true, opts?.force === true);
|
|
586
|
+
return await this._loadBundle(pkg, localBundleCode, selectedPackageVersion, true, opts?.force === true, assertCurrent);
|
|
493
587
|
}
|
|
494
588
|
}
|
|
495
589
|
catch (localErr) {
|
|
@@ -516,6 +610,13 @@ class PackageLoader {
|
|
|
516
610
|
return;
|
|
517
611
|
}
|
|
518
612
|
}
|
|
613
|
+
guardLoad(packageId) {
|
|
614
|
+
const generation = this.loadGenerations.get(packageId) ?? 0;
|
|
615
|
+
return () => {
|
|
616
|
+
if (this.disposed || generation !== (this.loadGenerations.get(packageId) ?? 0))
|
|
617
|
+
throw new Error(`Package load invalidated: ${packageId}`);
|
|
618
|
+
};
|
|
619
|
+
}
|
|
519
620
|
/**
|
|
520
621
|
* Post-correction: if the package definition carries a version string,
|
|
521
622
|
* update the selected IPackageVersion record to match — but only when that
|
|
@@ -546,18 +647,21 @@ class PackageLoader {
|
|
|
546
647
|
console.warn(`[PackageLoader] Failed to correct version for ${pkg.name}:`, error instanceof Error ? error.message : String(error));
|
|
547
648
|
}
|
|
548
649
|
}
|
|
549
|
-
async _loadBundle(pkg, bundleCode, selectedPackageVersion, usingLocalBundle, force) {
|
|
650
|
+
async _loadBundle(pkg, bundleCode, selectedPackageVersion, usingLocalBundle, force, assertCurrent = this.guardLoad(pkg.packageId)) {
|
|
651
|
+
assertCurrent();
|
|
550
652
|
if ((0, isolated_artifact_1.looksLikeIsolatedPackageArtifact)(bundleCode)) {
|
|
551
653
|
try {
|
|
552
654
|
const artifact = (0, isolated_artifact_1.parseIsolatedPackageArtifact)(bundleCode);
|
|
553
|
-
return await this._installIsolated(pkg, artifact, selectedPackageVersion, usingLocalBundle, force);
|
|
655
|
+
return await this._installIsolated(pkg, artifact, selectedPackageVersion, usingLocalBundle, force, assertCurrent);
|
|
554
656
|
}
|
|
555
657
|
catch (err) {
|
|
556
658
|
throw wrapIsolatedArtifactError(err);
|
|
557
659
|
}
|
|
558
660
|
}
|
|
661
|
+
await this.catalogReady;
|
|
662
|
+
assertCurrent();
|
|
559
663
|
const replacingIsolated = this.isolatedRuntime?.isInstalled(pkg.packageId) ?? false;
|
|
560
|
-
const instance = await this._evaluateBundle(pkg, bundleCode, selectedPackageVersion, force);
|
|
664
|
+
const instance = await this._evaluateBundle(pkg, bundleCode, selectedPackageVersion, force, assertCurrent);
|
|
561
665
|
if (!instance) {
|
|
562
666
|
return undefined;
|
|
563
667
|
}
|
|
@@ -569,7 +673,8 @@ class PackageLoader {
|
|
|
569
673
|
}
|
|
570
674
|
return instance;
|
|
571
675
|
}
|
|
572
|
-
async _installIsolated(pkg, artifact, selectedPackageVersion, usingLocalBundle, force) {
|
|
676
|
+
async _installIsolated(pkg, artifact, selectedPackageVersion, usingLocalBundle, force, assertCurrent) {
|
|
677
|
+
assertCurrent();
|
|
573
678
|
if (artifact.manifest.packageId !== pkg.packageId) {
|
|
574
679
|
throw wrapIsolatedArtifactError(new Error(`Isolated packageId mismatch: artifact has "${artifact.manifest.packageId}" but DB record has "${pkg.packageId}"`));
|
|
575
680
|
}
|
|
@@ -579,6 +684,7 @@ class PackageLoader {
|
|
|
579
684
|
if (artifact.manifest.version !== selectedPackageVersion.version) {
|
|
580
685
|
if (usingLocalBundle && selectedPackageVersion.versionTag === "dev") {
|
|
581
686
|
await this.correctPackageVersion(pkg, selectedPackageVersion, artifact.manifest.version);
|
|
687
|
+
assertCurrent();
|
|
582
688
|
if (artifact.manifest.version !== selectedPackageVersion.version) {
|
|
583
689
|
throw wrapIsolatedArtifactError(new Error(`Could not correct selected dev PackageVersion to isolated artifact version "${artifact.manifest.version}"`));
|
|
584
690
|
}
|
|
@@ -592,7 +698,8 @@ class PackageLoader {
|
|
|
592
698
|
console.warn(`[PackageLoader] Skipping isolated package "${pkg.name}" because this host does not provide an isolated runtime.`);
|
|
593
699
|
return undefined;
|
|
594
700
|
}
|
|
595
|
-
|
|
701
|
+
assertCurrent();
|
|
702
|
+
const installOptions = { ...isolatedContractLifecycle(selectedPackageVersion), assertCurrent };
|
|
596
703
|
if (this.isolatedRuntime.isInstalled(pkg.packageId)) {
|
|
597
704
|
if (!force) {
|
|
598
705
|
return this.packageInstances[pkg.packageId];
|
|
@@ -602,6 +709,9 @@ class PackageLoader {
|
|
|
602
709
|
else {
|
|
603
710
|
await this.isolatedRuntime.install(artifact, installOptions);
|
|
604
711
|
}
|
|
712
|
+
assertCurrent();
|
|
713
|
+
this.retireHostEndpoints(pkg.packageId);
|
|
714
|
+
(0, contract_resolvers_1.unregisterPackageContractResolvers)(this.dataContext, pkg.packageId);
|
|
605
715
|
this.isolatedPackageIds.add(pkg.packageId);
|
|
606
716
|
this.contractInstalledPackages.add(pkg.packageId);
|
|
607
717
|
const instance = {
|
|
@@ -611,7 +721,7 @@ class PackageLoader {
|
|
|
611
721
|
this.packageInstances[pkg.packageId] = instance;
|
|
612
722
|
return instance;
|
|
613
723
|
}
|
|
614
|
-
async _evaluateBundle(pkg, bundleCode, selectedPackageVersion, force) {
|
|
724
|
+
async _evaluateBundle(pkg, bundleCode, selectedPackageVersion, force, assertCurrent = this.guardLoad(pkg.packageId)) {
|
|
615
725
|
// Node.js built-in modules that do not exist in React Native (and would cause
|
|
616
726
|
// a fatal native error if passed through to RN's require). We block them here
|
|
617
727
|
// and throw a descriptive JS Error instead, which the surrounding try/catch
|
|
@@ -675,8 +785,9 @@ class PackageLoader {
|
|
|
675
785
|
}
|
|
676
786
|
};
|
|
677
787
|
try {
|
|
788
|
+
assertCurrent();
|
|
678
789
|
const bundleFunction = new Function("module", "exports", "require", bundleCode);
|
|
679
|
-
bundleFunction(module, moduleExports, customRequire);
|
|
790
|
+
const activateResolvers = (0, contract_resolvers_1.stagePackageContractResolvers)(this.dataContext, pkg.packageId, () => bundleFunction(module, moduleExports, customRequire));
|
|
680
791
|
const bundleExports = module.exports;
|
|
681
792
|
// Check for contract-based package definition (new path)
|
|
682
793
|
const packageDefinition = (0, contract_package_loader_1.extractPackageDefinition)(bundleExports);
|
|
@@ -684,42 +795,53 @@ class PackageLoader {
|
|
|
684
795
|
if (packageDefinition.packageId !== pkg.packageId) {
|
|
685
796
|
console.warn(`[PackageLoader] packageId mismatch: definition has "${packageDefinition.packageId}" but DB record has "${pkg.packageId}"`);
|
|
686
797
|
}
|
|
687
|
-
await (0, resolve_consumed_contracts_1.resolveConsumedContracts)(this.dataContext, this.contractRegistry, packageDefinition);
|
|
688
|
-
await (0, contract_package_loader_1.installContractPackage)(this.dataContext, packageDefinition, {
|
|
689
|
-
registry: this.contractRegistry,
|
|
690
|
-
});
|
|
691
|
-
this.hostConsumes.set(packageDefinition.packageId, packageDefinition.consumes);
|
|
692
|
-
this.contractInstalledPackages.add(packageDefinition.packageId);
|
|
693
|
-
await this.correctPackageVersion(pkg, selectedPackageVersion, packageDefinition.version);
|
|
694
798
|
// Build a proper IPeersPackage from the contract definition so callers
|
|
695
799
|
// (e.g. package-installer) can read appNavs, assistants, etc.
|
|
696
800
|
const contractPackageInstance = {
|
|
697
801
|
packageId: packageDefinition.packageId,
|
|
698
802
|
appNavs: packageDefinition.appNavs,
|
|
699
803
|
assistants: packageDefinition.assistants,
|
|
700
|
-
toolInstances: packageDefinition.toolInstances,
|
|
701
804
|
tableDefinitions: packageDefinition.tableDefinitions,
|
|
702
805
|
};
|
|
703
|
-
this.
|
|
806
|
+
await (0, contract_package_loader_1.installContractPackage)(this.dataContext, packageDefinition, {
|
|
807
|
+
registry: this.contractRegistry,
|
|
808
|
+
assertCurrent,
|
|
809
|
+
prepare: () => this.correctPackageVersion(pkg, selectedPackageVersion, packageDefinition.version),
|
|
810
|
+
onActivated: () => {
|
|
811
|
+
activateResolvers();
|
|
812
|
+
this.retireHostEndpoints(pkg.packageId);
|
|
813
|
+
this.hostConsumes.set(packageDefinition.packageId, packageDefinition.consumes);
|
|
814
|
+
this.contractInstalledPackages.add(packageDefinition.packageId);
|
|
815
|
+
this.packageInstances[pkg.packageId] = contractPackageInstance;
|
|
816
|
+
},
|
|
817
|
+
});
|
|
818
|
+
// A package may declare remote/consume handles for its own contracts.
|
|
819
|
+
// Settle only after guarded activation makes those definitions and
|
|
820
|
+
// resolvers visible, outside the catalog's serialized publication turn.
|
|
821
|
+
await (0, resolve_consumed_contracts_1.resolveConsumedContracts)(this.dataContext, this.contractRegistry, packageDefinition);
|
|
822
|
+
assertCurrent();
|
|
704
823
|
return contractPackageInstance;
|
|
705
824
|
}
|
|
706
825
|
// Legacy path: extract IPeersPackage for backward compat
|
|
707
826
|
const packageInstance = bundleExports?.exports || bundleExports?.package || bundleExports?.default || bundleExports;
|
|
708
827
|
if (packageInstance && typeof packageInstance === "object") {
|
|
709
|
-
packageInstance.toolInstances
|
|
710
|
-
|
|
711
|
-
(
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
828
|
+
const tools = packageInstance.toolInstances;
|
|
829
|
+
if (Array.isArray(tools) && tools.length)
|
|
830
|
+
throw new Error(`Package '${pkg.name}' (${pkg.packageId}) ships legacy executable tools, which this runtime no longer runs; upgrade it to definePackage() contract tools (docs/Packages/contracts.md#migrating-from-toolinstances)`);
|
|
831
|
+
await this.toolCatalog.publish(pkg.packageId, [], async () => assertCurrent(), () => {
|
|
832
|
+
assertCurrent();
|
|
833
|
+
// Final catalog writes must succeed before changing active host tables.
|
|
834
|
+
if (packageInstance.tableDefinitions?.length) {
|
|
835
|
+
this.dataContext.tableContainer.registerTableDefinitions(packageInstance.tableDefinitions, { overwrite: true });
|
|
836
|
+
}
|
|
837
|
+
activateResolvers();
|
|
838
|
+
this.retireHostEndpoints(pkg.packageId);
|
|
839
|
+
if (force && this.contractInstalledPackages.has(pkg.packageId)) {
|
|
840
|
+
this.contractRegistry.unregister(pkg.packageId);
|
|
841
|
+
this.contractInstalledPackages.delete(pkg.packageId);
|
|
842
|
+
}
|
|
843
|
+
this.packageInstances[pkg.packageId] = packageInstance;
|
|
717
844
|
});
|
|
718
|
-
if (force && this.contractInstalledPackages.has(pkg.packageId)) {
|
|
719
|
-
this.contractRegistry.unregister(pkg.packageId);
|
|
720
|
-
this.contractInstalledPackages.delete(pkg.packageId);
|
|
721
|
-
}
|
|
722
|
-
this.packageInstances[pkg.packageId] = packageInstance;
|
|
723
845
|
return packageInstance;
|
|
724
846
|
}
|
|
725
847
|
return;
|