@peers-app/peers-sdk 0.23.4 → 0.23.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. package/dist/context/data-context.d.ts +36 -5
  2. package/dist/context/data-context.js +44 -9
  3. package/dist/context/user-context.d.ts +44 -0
  4. package/dist/context/user-context.js +91 -12
  5. package/dist/contracts/__tests__/contract-provider-router.test.js +26 -0
  6. package/dist/contracts/__tests__/contract-resolvers.test.js +44 -0
  7. package/dist/contracts/__tests__/registry.test.js +104 -0
  8. package/dist/contracts/contract-provider-router.d.ts +21 -0
  9. package/dist/contracts/contract-provider-router.js +31 -5
  10. package/dist/contracts/contract-proxy.d.ts +3 -0
  11. package/dist/contracts/contract-proxy.js +3 -0
  12. package/dist/contracts/contract-resolvers.d.ts +21 -3
  13. package/dist/contracts/contract-resolvers.js +105 -32
  14. package/dist/contracts/index.d.ts +3 -3
  15. package/dist/contracts/registry.d.ts +59 -0
  16. package/dist/contracts/registry.js +155 -25
  17. package/dist/contracts/system-contracts.js +3 -1
  18. package/dist/data/index.d.ts +1 -0
  19. package/dist/data/index.js +1 -0
  20. package/dist/data/orm/types.d.ts +19 -0
  21. package/dist/data/orm/types.js +11 -0
  22. package/dist/data/package-table-identity.d.ts +46 -0
  23. package/dist/data/package-table-identity.js +71 -0
  24. package/dist/data/package-table-identity.test.d.ts +1 -0
  25. package/dist/data/package-table-identity.test.js +46 -0
  26. package/dist/data/package-version-resolver.d.ts +13 -0
  27. package/dist/data/package-version-resolver.js +142 -6
  28. package/dist/data/package-version-resolver.test.js +42 -0
  29. package/dist/data/persistent-var-identity.d.ts +37 -0
  30. package/dist/data/persistent-var-identity.js +69 -0
  31. package/dist/data/persistent-vars.d.ts +3 -5
  32. package/dist/data/persistent-vars.js +25 -13
  33. package/dist/data/persistent-vars.package-scope.test.d.ts +1 -0
  34. package/dist/data/persistent-vars.package-scope.test.js +27 -0
  35. package/dist/data/tools.d.ts +3 -3
  36. package/dist/package-loader/contract-package-loader.js +20 -8
  37. package/dist/package-loader/contract-package-loader.test.js +16 -0
  38. package/dist/package-loader/index.d.ts +4 -0
  39. package/dist/package-loader/index.js +4 -0
  40. package/dist/package-loader/isolated-artifact.d.ts +233 -0
  41. package/dist/package-loader/isolated-artifact.js +643 -0
  42. package/dist/package-loader/isolated-artifact.test.d.ts +1 -0
  43. package/dist/package-loader/isolated-artifact.test.js +270 -0
  44. package/dist/package-loader/isolated-persistent-var-gateway.d.ts +20 -0
  45. package/dist/package-loader/isolated-persistent-var-gateway.js +106 -0
  46. package/dist/package-loader/isolated-persistent-var-gateway.test.d.ts +1 -0
  47. package/dist/package-loader/isolated-persistent-var-gateway.test.js +56 -0
  48. package/dist/package-loader/isolated-runtime.d.ts +229 -0
  49. package/dist/package-loader/isolated-runtime.js +37 -0
  50. package/dist/package-loader/isolated-table-gateway.d.ts +16 -0
  51. package/dist/package-loader/isolated-table-gateway.js +375 -0
  52. package/dist/package-loader/isolated-table-gateway.test.d.ts +1 -0
  53. package/dist/package-loader/isolated-table-gateway.test.js +206 -0
  54. package/dist/package-loader/isolated-value.d.ts +14 -0
  55. package/dist/package-loader/isolated-value.js +63 -0
  56. package/dist/package-loader/package-loader.d.ts +123 -4
  57. package/dist/package-loader/package-loader.js +417 -46
  58. package/dist/package-loader/package-loader.test.js +478 -12
  59. package/dist/package-loader/resolve-consumed-contracts.d.ts +3 -3
  60. package/dist/package-loader/resolve-consumed-contracts.js +18 -10
  61. package/dist/utils.d.ts +21 -2
  62. package/dist/utils.js +46 -7
  63. package/package.json +1 -1
@@ -2,6 +2,10 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.PackageLoader = void 0;
4
4
  exports.setDefaultRequire = setDefaultRequire;
5
+ const contract_provider_router_1 = require("../contracts/contract-provider-router");
6
+ const contract_proxy_1 = require("../contracts/contract-proxy");
7
+ const contract_resolvers_1 = require("../contracts/contract-resolvers");
8
+ const contract_transport_1 = require("../contracts/contract-transport");
5
9
  const registry_1 = require("../contracts/registry");
6
10
  const system_contracts_1 = require("../contracts/system-contracts");
7
11
  const files_1 = require("../data/files");
@@ -11,51 +15,329 @@ const packages_1 = require("../data/packages");
11
15
  const tools_1 = require("../data/tools");
12
16
  const tools_2 = require("../tools");
13
17
  const contract_package_loader_1 = require("./contract-package-loader");
18
+ const isolated_artifact_1 = require("./isolated-artifact");
19
+ const isolated_persistent_var_gateway_1 = require("./isolated-persistent-var-gateway");
20
+ const isolated_runtime_1 = require("./isolated-runtime");
21
+ const isolated_table_gateway_1 = require("./isolated-table-gateway");
14
22
  const resolve_consumed_contracts_1 = require("./resolve-consumed-contracts");
15
23
  // Ensure built-in system contracts are cataloged before any PackageLoader installs them.
16
24
  require("../contracts/system");
25
+ function isolatedContractLifecycle(packageVersion) {
26
+ return {
27
+ contractLifecycle: packageVersion?.versionTag === "dev" || packageVersion?.versionTag === "beta"
28
+ ? "mutable"
29
+ : "stable",
30
+ packageVersionId: packageVersion?.packageVersionId,
31
+ packageVersion: packageVersion?.version,
32
+ versionTag: packageVersion?.versionTag,
33
+ };
34
+ }
35
+ /**
36
+ * Loads package versions for one {@link DataContext}.
37
+ *
38
+ * Isolated artifacts (magic-header JSON) are installed without host
39
+ * evaluation. Legacy `package.bundle.js` files still evaluate in-process.
40
+ */
17
41
  class PackageLoader {
18
42
  dataContext;
43
+ /** Host-injected SDK module used as the `peers-sdk` / `PeersSDK` require. */
19
44
  static PeersSDK;
45
+ /** Host-injected Zod module used as the `zod` require. */
20
46
  static Zod;
21
47
  packageInstances = {};
22
48
  /** Packages that were installed via the contract-based path (tools/assistants/tables already saved with packageId). */
23
49
  contractInstalledPackages = new Set();
50
+ /** Packages installed through the isolated runtime (no host eval). */
51
+ isolatedPackageIds = new Set();
24
52
  /**
25
53
  * In-memory contract providers for this data context. Seeded with system
26
54
  * contracts at construction; package providers are added via
27
- * {@link installContractPackage}.
55
+ * {@link installContractPackage} or the isolated runtime.
28
56
  */
29
57
  contractRegistry;
58
+ /** Optional isolated-provider supervisor created from the host factory. */
59
+ isolatedRuntime;
60
+ /** Optional host `require` used when reading local bundles or evaluating legacy packages. */
30
61
  require = undefined;
62
+ /**
63
+ * @param dataContext - Owning personal or group data context. Its
64
+ * `tableContainer` must already be assigned so isolated packages that
65
+ * declare tables receive a host gateway.
66
+ */
31
67
  constructor(dataContext) {
32
68
  this.dataContext = dataContext;
33
69
  this.contractRegistry = new registry_1.ContractRegistry();
34
70
  (0, system_contracts_1.installSystemContracts)(this.contractRegistry);
71
+ const factory = (0, isolated_runtime_1.getIsolatedPackageRuntimeFactory)();
72
+ if (factory) {
73
+ this.isolatedRuntime = factory({
74
+ registry: this.contractRegistry,
75
+ dataContextId: dataContext.dataContextId,
76
+ tableGateway: dataContext.tableContainer
77
+ ? (0, isolated_table_gateway_1.createIsolatedPackageTableGateway)(dataContext)
78
+ : undefined,
79
+ persistentVarGateway: dataContext.userContext
80
+ ? (0, isolated_persistent_var_gateway_1.createIsolatedPackagePersistentVarGateway)(dataContext)
81
+ : undefined,
82
+ providerGateway: {
83
+ resolveEndpoint: (request) => this.resolveContractEndpoint(request, {
84
+ providerPackageId: request.providerPackageId,
85
+ resolverOnly: true,
86
+ }),
87
+ },
88
+ });
89
+ }
35
90
  }
36
- /** Returns true if a package was installed via the contract-based definePackage() path. */
91
+ /**
92
+ * Returns true if a package was installed via the contract-based
93
+ * `definePackage()` path or as an isolated artifact.
94
+ *
95
+ * @param packageId - Package to query.
96
+ */
37
97
  isContractInstalled(packageId) {
38
98
  return this.contractInstalledPackages.has(packageId);
39
99
  }
100
+ /**
101
+ * Returns true if `packageId` was installed as an isolated artifact.
102
+ *
103
+ * @param packageId - Package to query.
104
+ */
105
+ isIsolatedInstalled(packageId) {
106
+ return this.isolatedPackageIds.has(packageId);
107
+ }
108
+ /** Return whether this host configured an isolated-package runtime. */
109
+ supportsIsolatedPackages() {
110
+ return this.isolatedRuntime !== undefined;
111
+ }
112
+ /**
113
+ * Return the active provider selected by this data context's trusted registry.
114
+ *
115
+ * @param contractId - Target contract identity.
116
+ * @param version - Target contract version.
117
+ */
118
+ getActiveContractProviderPackageId(contractId, version) {
119
+ return this.contractRegistry.getProviderPackageId(contractId, version);
120
+ }
121
+ /**
122
+ * Resolve one authorized endpoint through the registry-selected provider.
123
+ *
124
+ * Isolated providers are delegated to the host runtime; resolver-backed
125
+ * providers are looked up by provider package. Contracts that predate package
126
+ * registration may still use their legacy unscoped resolver.
127
+ * Provider identity comes only from trusted registry/options state, never
128
+ * from an {@link import("../contracts/contract-proxy").IContractCall}.
129
+ *
130
+ * @param request - Authorized contract route and notification sink.
131
+ * @param opts - Trusted provider override and host-only resolver capabilities.
132
+ * @throws If no provider or matching endpoint implementation is available.
133
+ */
134
+ async resolveContractEndpoint(request, opts = {}) {
135
+ const providerPackageId = opts.providerPackageId ??
136
+ request.providerPackageId ??
137
+ this.contractRegistry.getProviderPackageId(request.contractId, request.version);
138
+ if (!providerPackageId) {
139
+ const legacyResolver = (0, contract_resolvers_1.getContractResolver)(request.contractId, request.version);
140
+ if (!legacyResolver) {
141
+ throw new Error(`No contract provider registered for ${request.contractId} v${request.version}`);
142
+ }
143
+ const resolution = await legacyResolver({ dataContext: this.dataContext });
144
+ const localResolution = opts.resolveVariable
145
+ ? {
146
+ ...resolution,
147
+ toolContext: {
148
+ dataContextId: this.dataContext.dataContextId,
149
+ getVariable: opts.resolveVariable,
150
+ },
151
+ }
152
+ : resolution;
153
+ return (0, contract_proxy_1.createContractProviderEndpoint)(localResolution, {
154
+ context: request.context,
155
+ notify: request.notify,
156
+ });
157
+ }
158
+ if (!this.contractRegistry
159
+ .getRegisteredProviderIds(request.contractId, request.version)
160
+ .includes(providerPackageId)) {
161
+ throw new Error(`Package ${providerPackageId} is not a registered provider for ${request.contractId} v${request.version}`);
162
+ }
163
+ const trustedRequest = {
164
+ ...request,
165
+ providerPackageId,
166
+ };
167
+ if (!opts.resolverOnly && this.isolatedRuntime?.isInstalled(providerPackageId)) {
168
+ const isolatedEndpoint = await this.isolatedRuntime.resolveEndpoint(trustedRequest);
169
+ if (!isolatedEndpoint) {
170
+ throw new Error(`Isolated provider ${providerPackageId} could not resolve ${request.contractId} v${request.version}`);
171
+ }
172
+ return isolatedEndpoint;
173
+ }
174
+ const implementation = this.contractRegistry.getProviderImplementation(request.contractId, request.version, providerPackageId) ?? {
175
+ contractId: request.contractId,
176
+ version: request.version,
177
+ };
178
+ const resolver = (0, contract_resolvers_1.getContractResolver)(implementation.contractId, implementation.version, providerPackageId);
179
+ if (!resolver) {
180
+ throw new Error(`Provider ${providerPackageId} has no live resolver for ${request.contractId} v${request.version}`);
181
+ }
182
+ const resolution = await resolver({ dataContext: this.dataContext });
183
+ const localResolution = opts.resolveVariable
184
+ ? {
185
+ ...resolution,
186
+ toolContext: {
187
+ dataContextId: this.dataContext.dataContextId,
188
+ getVariable: opts.resolveVariable,
189
+ },
190
+ }
191
+ : resolution;
192
+ const endpoint = (0, contract_proxy_1.createContractProviderEndpoint)(localResolution, {
193
+ context: request.context,
194
+ notify: (notification) => request.notify({
195
+ ...notification,
196
+ contractId: request.contractId,
197
+ }),
198
+ });
199
+ if (implementation.contractId === request.contractId &&
200
+ implementation.version === request.version) {
201
+ return endpoint;
202
+ }
203
+ return {
204
+ handleCall: (call, context) => endpoint.handleCall({
205
+ ...call,
206
+ contractId: implementation.contractId,
207
+ version: implementation.version,
208
+ }, context),
209
+ dispose: () => endpoint.dispose(),
210
+ };
211
+ }
212
+ /**
213
+ * Bind a legacy package's declared consume to its selected provider.
214
+ *
215
+ * The returned proxy can target either a resolver-backed or isolated provider.
216
+ * Its provider choice is captured from {@link ContractRegistry.selectProvider}
217
+ * before any consumer call payload is processed.
218
+ *
219
+ * @param dependency - Validated package consume declaration.
220
+ * @returns A live consumer proxy, or `undefined` when no provider matches.
221
+ */
222
+ async resolveConsumedContract(dependency) {
223
+ const selected = this.contractRegistry.selectProvider(dependency);
224
+ if (!selected) {
225
+ return undefined;
226
+ }
227
+ const providerPackageId = selected.providerPackageId;
228
+ const [consumerTransport, providerTransport] = (0, contract_transport_1.inProcessTransportPair)();
229
+ const router = (0, contract_provider_router_1.createContractProviderRouter)(providerTransport, {
230
+ authorizeCall: () => ({
231
+ dataContextId: this.dataContext.dataContextId,
232
+ context: { dataContext: this.dataContext },
233
+ }),
234
+ resolveProviderPackageId: () => providerPackageId,
235
+ resolveEndpoint: (request) => this.resolveContractEndpoint(request, { providerPackageId }),
236
+ });
237
+ const consumer = (0, contract_proxy_1.createContractConsumer)(selected.definition, consumerTransport, {
238
+ dataContextId: this.dataContext.dataContextId,
239
+ });
240
+ const dispose = consumer.dispose.bind(consumer);
241
+ consumer.dispose = () => {
242
+ dispose();
243
+ router.dispose();
244
+ };
245
+ return consumer;
246
+ }
247
+ /**
248
+ * Resolve a sandbox-backed provider endpoint when this context owns an
249
+ * isolated provider for the requested contract.
250
+ *
251
+ * @param request - Authorized contract/version/data-context route.
252
+ */
253
+ async resolveIsolatedEndpoint(request) {
254
+ const providerPackageId = request.providerPackageId ??
255
+ this.contractRegistry.getProviderPackageId(request.contractId, request.version);
256
+ if (!this.isolatedRuntime ||
257
+ !providerPackageId ||
258
+ !this.isolatedRuntime.isInstalled(providerPackageId)) {
259
+ return undefined;
260
+ }
261
+ return this.isolatedRuntime.resolveEndpoint({ ...request, providerPackageId });
262
+ }
263
+ /**
264
+ * Stop an isolated worker (if any) and drop the in-memory package instance.
265
+ *
266
+ * @param packageId - Package to unload.
267
+ */
268
+ async unloadPackage(packageId) {
269
+ if (this.isolatedRuntime?.isInstalled(packageId)) {
270
+ await this.isolatedRuntime.uninstall(packageId);
271
+ }
272
+ this.contractRegistry.unregister(packageId);
273
+ this.isolatedPackageIds.delete(packageId);
274
+ this.contractInstalledPackages.delete(packageId);
275
+ delete this.packageInstances[packageId];
276
+ }
277
+ /**
278
+ * Dispose the isolated runtime for this data context.
279
+ */
280
+ async dispose() {
281
+ if (this.isolatedRuntime) {
282
+ await this.isolatedRuntime.dispose();
283
+ }
284
+ this.isolatedPackageIds.clear();
285
+ this.contractInstalledPackages.clear();
286
+ this.packageInstances = {};
287
+ }
288
+ /**
289
+ * Resolve and load every non-disabled package in this data context.
290
+ * Disabled packages that were previously loaded are unloaded.
291
+ *
292
+ * @param opts - Forwarded to {@link resolveDevicePackageVersion}.
293
+ */
40
294
  async loadAllPackages(opts) {
41
295
  const packages = await (0, packages_1.Packages)(this.dataContext).list();
42
- await Promise.all(packages
43
- .filter((pkg) => !pkg.disabled)
44
- .map((pkg) => (0, package_version_resolver_1.resolveDevicePackageVersion)(pkg, this.dataContext, opts)));
296
+ await Promise.all(packages.map(async (pkg) => {
297
+ if (pkg.disabled) {
298
+ await this.unloadPackage(pkg.packageId);
299
+ return;
300
+ }
301
+ try {
302
+ await (0, package_version_resolver_1.resolveDevicePackageVersion)(pkg, this.dataContext, opts);
303
+ }
304
+ catch (error) {
305
+ console.warn(`[PackageLoader] Failed to load package "${pkg.name}" (${pkg.packageId}):`, error instanceof Error ? error.message : String(error));
306
+ }
307
+ }));
45
308
  }
309
+ /**
310
+ * Load one package version into this data context.
311
+ *
312
+ * Isolated artifacts are parsed and installed without evaluating provider
313
+ * source. A recognized but malformed isolated envelope fails closed and
314
+ * never falls back to `new Function`. When `localPath` points at a rebuilt
315
+ * isolated `dist/package.bundle.js`, that disk artifact wins over a stale
316
+ * stored Files bundle so local-dev packages pick up new contract versions.
317
+ *
318
+ * @param pkg - Package row.
319
+ * @param opts - Optional force reload, local path fallback, and PV id.
320
+ */
46
321
  async loadPackage(pkg, opts) {
322
+ if (pkg.disabled) {
323
+ await this.unloadPackage(pkg.packageId);
324
+ return undefined;
325
+ }
47
326
  if (this.packageInstances[pkg.packageId] && !opts?.force) {
48
327
  return this.packageInstances[pkg.packageId];
49
328
  }
50
329
  const localPath = opts?.localPath;
330
+ let selectedPackageVersion;
51
331
  try {
52
332
  let bundleCode = "";
333
+ let usingLocalBundle = false;
53
334
  let bundleFileId;
54
335
  const pvId = opts?.packageVersionId ?? pkg.activePackageVersionId;
55
336
  if (pvId) {
56
337
  try {
57
338
  const pv = await (0, package_versions_1.PackageVersions)(this.dataContext).get(pvId);
58
339
  if (pv) {
340
+ selectedPackageVersion = pv;
59
341
  bundleFileId = pv.packageBundleFileId;
60
342
  }
61
343
  }
@@ -76,25 +358,33 @@ class PackageLoader {
76
358
  else {
77
359
  console.warn(`Package ${pkg.name} does not have a bundle file defined.`);
78
360
  }
79
- // Fallback: try loading from local path if stored bundle is not available
80
- if (!bundleCode && localPath) {
81
- bundleCode = this._readLocalBundle(localPath) ?? "";
361
+ if (localPath && selectedPackageVersion?.versionTag === "dev") {
362
+ const localBundleCode = this._readLocalBundle(localPath);
363
+ if (localBundleCode) {
364
+ bundleCode = localBundleCode;
365
+ usingLocalBundle = true;
366
+ }
82
367
  }
83
368
  if (!bundleCode)
84
369
  return;
85
- return this._evaluateBundle(pkg, bundleCode);
370
+ return await this._loadBundle(pkg, bundleCode, selectedPackageVersion, usingLocalBundle, opts?.force === true);
86
371
  }
87
372
  catch (err) {
88
- // If stored bundle failed to evaluate, try local path as fallback
89
- if (localPath) {
373
+ if (isIsolatedArtifactError(err)) {
374
+ throw err;
375
+ }
376
+ if (localPath && selectedPackageVersion?.versionTag === "dev") {
90
377
  console.warn(`[PackageLoader] Stored bundle failed for ${pkg.name}, trying local path fallback`, err.message);
91
378
  try {
92
379
  const localBundleCode = this._readLocalBundle(localPath);
93
380
  if (localBundleCode) {
94
- return this._evaluateBundle(pkg, localBundleCode);
381
+ return await this._loadBundle(pkg, localBundleCode, selectedPackageVersion, true, opts?.force === true);
95
382
  }
96
383
  }
97
384
  catch (localErr) {
385
+ if (isIsolatedArtifactError(localErr)) {
386
+ throw localErr;
387
+ }
98
388
  console.debug(`Could not load local bundle for ${pkg.name}`, localErr);
99
389
  }
100
390
  }
@@ -117,40 +407,100 @@ class PackageLoader {
117
407
  }
118
408
  /**
119
409
  * Post-correction: if the package definition carries a version string,
120
- * update the active IPackageVersion record to match — but only when that
121
- * active version is the mutable `dev` working copy. Never rewrite
410
+ * update the selected IPackageVersion record to match — but only when that
411
+ * selected version is the mutable `dev` working copy. Never rewrite
122
412
  * beta/stable version labels from a local package.json bump.
123
413
  */
124
- correctPackageVersion(pkg, def) {
125
- if (!pkg.activePackageVersionId)
126
- return;
127
- if (!def.version)
414
+ async correctPackageVersion(pkg, selectedPackageVersion, declaredVersion) {
415
+ if (!selectedPackageVersion ||
416
+ selectedPackageVersion.versionTag !== "dev" ||
417
+ !declaredVersion ||
418
+ selectedPackageVersion.version === declaredVersion) {
128
419
  return;
129
- const pvTable = (0, package_versions_1.PackageVersions)(this.dataContext);
130
- pvTable
131
- .get(pkg.activePackageVersionId)
132
- .then((pv) => {
133
- if (!pv)
134
- return;
135
- if (pv.versionTag !== "dev")
136
- return;
137
- if (def.version && pv.version !== def.version) {
138
- const packageVersionHash = (0, package_versions_1.computePackageVersionHash)(def.version, pv.versionTag || "dev", pv.packageBundleFileHash, pv.routesBundleFileHash, pv.uiBundleFileHash);
139
- // Version is part of both the content hash and the author-signed
140
- // payload — clear any prior author signature so it cannot go stale.
141
- const { packageAuthorSignature: _staleSig, ...rest } = pv;
142
- return pvTable.signAndSave({
143
- ...rest,
144
- version: def.version,
145
- packageVersionHash,
146
- });
420
+ }
421
+ try {
422
+ const packageVersionHash = (0, package_versions_1.computePackageVersionHash)(declaredVersion, selectedPackageVersion.versionTag, selectedPackageVersion.packageBundleFileHash, selectedPackageVersion.routesBundleFileHash, selectedPackageVersion.uiBundleFileHash);
423
+ // Version is part of both the content hash and the author-signed
424
+ // payload — clear any prior author signature so it cannot go stale.
425
+ const { packageAuthorSignature: _staleSig, ...rest } = selectedPackageVersion;
426
+ await (0, package_versions_1.PackageVersions)(this.dataContext).signAndSave({
427
+ ...rest,
428
+ version: declaredVersion,
429
+ packageVersionHash,
430
+ });
431
+ selectedPackageVersion.version = declaredVersion;
432
+ selectedPackageVersion.packageVersionHash = packageVersionHash;
433
+ }
434
+ catch (error) {
435
+ console.warn(`[PackageLoader] Failed to correct version for ${pkg.name}:`, error instanceof Error ? error.message : String(error));
436
+ }
437
+ }
438
+ async _loadBundle(pkg, bundleCode, selectedPackageVersion, usingLocalBundle, force) {
439
+ if ((0, isolated_artifact_1.looksLikeIsolatedPackageArtifact)(bundleCode)) {
440
+ try {
441
+ const artifact = (0, isolated_artifact_1.parseIsolatedPackageArtifact)(bundleCode);
442
+ return await this._installIsolated(pkg, artifact, selectedPackageVersion, usingLocalBundle, force);
147
443
  }
148
- })
149
- .catch((err) => {
150
- console.warn(`[PackageLoader] Failed to correct version for ${pkg.name}:`, err.message);
151
- });
444
+ catch (err) {
445
+ throw wrapIsolatedArtifactError(err);
446
+ }
447
+ }
448
+ const replacingIsolated = this.isolatedRuntime?.isInstalled(pkg.packageId) ?? false;
449
+ const instance = await this._evaluateBundle(pkg, bundleCode, selectedPackageVersion, force);
450
+ if (!instance) {
451
+ return undefined;
452
+ }
453
+ if (replacingIsolated && this.isolatedRuntime) {
454
+ await this.isolatedRuntime.uninstall(pkg.packageId, {
455
+ preserveRegistry: this.contractInstalledPackages.has(pkg.packageId),
456
+ });
457
+ this.isolatedPackageIds.delete(pkg.packageId);
458
+ }
459
+ return instance;
460
+ }
461
+ async _installIsolated(pkg, artifact, selectedPackageVersion, usingLocalBundle, force) {
462
+ if (artifact.manifest.packageId !== pkg.packageId) {
463
+ throw wrapIsolatedArtifactError(new Error(`Isolated packageId mismatch: artifact has "${artifact.manifest.packageId}" but DB record has "${pkg.packageId}"`));
464
+ }
465
+ if (!selectedPackageVersion) {
466
+ throw wrapIsolatedArtifactError(new Error(`No selected PackageVersion is available for isolated package ${pkg.packageId}`));
467
+ }
468
+ if (artifact.manifest.version !== selectedPackageVersion.version) {
469
+ if (usingLocalBundle && selectedPackageVersion.versionTag === "dev") {
470
+ await this.correctPackageVersion(pkg, selectedPackageVersion, artifact.manifest.version);
471
+ if (artifact.manifest.version !== selectedPackageVersion.version) {
472
+ throw wrapIsolatedArtifactError(new Error(`Could not correct selected dev PackageVersion to isolated artifact version "${artifact.manifest.version}"`));
473
+ }
474
+ }
475
+ else {
476
+ throw wrapIsolatedArtifactError(new Error(`Isolated package version mismatch: artifact has "${artifact.manifest.version}" but selected PackageVersion has "${selectedPackageVersion.version}"`));
477
+ }
478
+ }
479
+ if (!this.isolatedRuntime) {
480
+ await this.unloadPackage(pkg.packageId);
481
+ console.warn(`[PackageLoader] Skipping isolated package "${pkg.name}" because this host does not provide an isolated runtime.`);
482
+ return undefined;
483
+ }
484
+ const installOptions = isolatedContractLifecycle(selectedPackageVersion);
485
+ if (this.isolatedRuntime.isInstalled(pkg.packageId)) {
486
+ if (!force) {
487
+ return this.packageInstances[pkg.packageId];
488
+ }
489
+ await this.isolatedRuntime.replace(artifact, installOptions);
490
+ }
491
+ else {
492
+ await this.isolatedRuntime.install(artifact, installOptions);
493
+ }
494
+ this.isolatedPackageIds.add(pkg.packageId);
495
+ this.contractInstalledPackages.add(pkg.packageId);
496
+ const instance = {
497
+ packageId: artifact.manifest.packageId,
498
+ appNavs: artifact.manifest.appNavs,
499
+ };
500
+ this.packageInstances[pkg.packageId] = instance;
501
+ return instance;
152
502
  }
153
- async _evaluateBundle(pkg, bundleCode) {
503
+ async _evaluateBundle(pkg, bundleCode, selectedPackageVersion, force) {
154
504
  // Node.js built-in modules that do not exist in React Native (and would cause
155
505
  // a fatal native error if passed through to RN's require). We block them here
156
506
  // and throw a descriptive JS Error instead, which the surrounding try/catch
@@ -187,14 +537,16 @@ class PackageLoader {
187
537
  const moduleExports = {};
188
538
  const module = { exports: moduleExports };
189
539
  const customRequire = (moduleId) => {
190
- const _require = (this.require ?? defaultRequire ?? typeof require === "function") ? require : undefined;
540
+ const _require = this.require ??
541
+ defaultRequire ??
542
+ (typeof require === "function" ? require : undefined);
191
543
  if (!_require) {
192
544
  throw new Error("You must set the `require` function in package loader before loading a package");
193
545
  }
194
546
  switch (moduleId) {
195
547
  case "peers-sdk":
196
548
  case "PeersSDK":
197
- return PackageLoader.PeersSDK || _require("../index");
549
+ return PackageLoader.PeersSDK || _require("@peers-app/peers-sdk");
198
550
  case "zod":
199
551
  return PackageLoader.Zod || _require("zod");
200
552
  default:
@@ -221,12 +573,12 @@ class PackageLoader {
221
573
  if (packageDefinition.packageId !== pkg.packageId) {
222
574
  console.warn(`[PackageLoader] packageId mismatch: definition has "${packageDefinition.packageId}" but DB record has "${pkg.packageId}"`);
223
575
  }
576
+ await (0, resolve_consumed_contracts_1.resolveConsumedContracts)(this.dataContext, this.contractRegistry, packageDefinition);
224
577
  await (0, contract_package_loader_1.installContractPackage)(this.dataContext, packageDefinition, {
225
578
  registry: this.contractRegistry,
226
579
  });
227
- await (0, resolve_consumed_contracts_1.resolveConsumedContracts)(this.dataContext, this.contractRegistry, packageDefinition);
228
580
  this.contractInstalledPackages.add(packageDefinition.packageId);
229
- this.correctPackageVersion(pkg, packageDefinition);
581
+ await this.correctPackageVersion(pkg, selectedPackageVersion, packageDefinition.version);
230
582
  // Build a proper IPeersPackage from the contract definition so callers
231
583
  // (e.g. package-installer) can read appNavs, assistants, etc.
232
584
  const contractPackageInstance = {
@@ -241,7 +593,6 @@ class PackageLoader {
241
593
  }
242
594
  // Legacy path: extract IPeersPackage for backward compat
243
595
  const packageInstance = bundleExports?.exports || bundleExports?.package || bundleExports?.default || bundleExports;
244
- this.packageInstances[pkg.packageId] = packageInstance;
245
596
  if (packageInstance && typeof packageInstance === "object") {
246
597
  packageInstance.toolInstances?.forEach((toolInstance) => {
247
598
  (0, tools_2.registerTool)(toolInstance);
@@ -252,6 +603,11 @@ class PackageLoader {
252
603
  overwrite: true,
253
604
  });
254
605
  });
606
+ if (force && this.contractInstalledPackages.has(pkg.packageId)) {
607
+ this.contractRegistry.unregister(pkg.packageId);
608
+ this.contractInstalledPackages.delete(pkg.packageId);
609
+ }
610
+ this.packageInstances[pkg.packageId] = packageInstance;
255
611
  return packageInstance;
256
612
  }
257
613
  return;
@@ -263,7 +619,22 @@ class PackageLoader {
263
619
  }
264
620
  }
265
621
  exports.PackageLoader = PackageLoader;
622
+ const ISOLATED_ARTIFACT_ERROR = "IsolatedPackageArtifactError";
623
+ function wrapIsolatedArtifactError(err) {
624
+ const error = err instanceof Error ? err : new Error(String(err));
625
+ error.name = ISOLATED_ARTIFACT_ERROR;
626
+ return error;
627
+ }
628
+ function isIsolatedArtifactError(err) {
629
+ return err instanceof Error && err.name === ISOLATED_ARTIFACT_ERROR;
630
+ }
266
631
  let defaultRequire;
632
+ /**
633
+ * Set the process-wide `require` used when a {@link PackageLoader} instance
634
+ * does not supply its own.
635
+ *
636
+ * @param require - Host `require` function.
637
+ */
267
638
  function setDefaultRequire(require) {
268
639
  defaultRequire = require;
269
640
  }