@peers-app/peers-sdk 0.28.6 → 0.28.8
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 +2 -2
- package/dist/contracts/builder.d.ts +0 -6
- package/dist/contracts/builder.js +0 -11
- package/dist/contracts/contract-proxy.d.ts +7 -6
- package/dist/contracts/extract.d.ts +1 -1
- package/dist/contracts/system/contract-catalog-contract.d.ts +6 -0
- package/dist/contracts/system/contract-catalog-contract.js +1 -0
- package/dist/contracts/system/packages-contract.d.ts +19 -6
- package/dist/contracts/system/packages-contract.js +15 -2
- package/dist/data/package-version-resolver.js +1 -19
- package/dist/data/package-versions.d.ts +4 -0
- package/dist/data/package-versions.js +8 -0
- package/dist/data/packages.utils.d.ts +4 -0
- package/dist/data/packages.utils.js +5 -1
- package/dist/data/tools.d.ts +0 -10
- package/dist/define/context.d.ts +5 -7
- package/dist/define/observables.js +1 -5
- package/dist/package-build/cli.js +4 -3
- package/dist/package-build/contract-add.d.ts +4 -2
- package/dist/package-build/contract-add.js +3 -11
- package/dist/package-build/contract-source.d.ts +7 -4
- package/dist/package-build/contract-source.js +26 -13
- package/dist/package-build/index.d.ts +3 -0
- package/dist/package-build/index.js +11 -0
- package/dist/package-installer/package-cloner.js +1 -0
- package/dist/package-installer/package-creator.js +2 -0
- package/dist/package-installer/package-installer.d.ts +3 -1
- package/dist/package-installer/package-installer.js +37 -8
- package/dist/package-installer/package-sdk-version.d.ts +5 -0
- package/dist/package-installer/package-sdk-version.js +50 -0
- package/dist/package-installer/types.d.ts +11 -4
- package/dist/package-loader/isolated-artifact.d.ts +16 -29
- package/dist/package-loader/isolated-artifact.js +20 -33
- package/dist/package-loader/package-loader.d.ts +5 -5
- package/dist/package-loader/package-loader.js +14 -43
- package/dist/package-loader/package-runtime-state.d.ts +1 -2
- package/dist/sdk-version.d.ts +2 -0
- package/dist/sdk-version.js +5 -0
- package/dist/types/peers-package.d.ts +1 -7
- package/package.json +1 -1
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
/** Node-only helpers for generating package contract declarations. */
|
|
2
|
+
export { addContract, type IContractAddOptions, type IContractAddResult } from "./contract-add";
|
|
3
|
+
export { contractSourceFromDefinition, contractSourceFromManifest, generateContractDeclaration, type IContractSource, type IGeneratedContractDeclaration, parseContractSelector, } from "./contract-source";
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.parseContractSelector = exports.generateContractDeclaration = exports.contractSourceFromManifest = exports.contractSourceFromDefinition = exports.addContract = void 0;
|
|
4
|
+
/** Node-only helpers for generating package contract declarations. */
|
|
5
|
+
var contract_add_1 = require("./contract-add");
|
|
6
|
+
Object.defineProperty(exports, "addContract", { enumerable: true, get: function () { return contract_add_1.addContract; } });
|
|
7
|
+
var contract_source_1 = require("./contract-source");
|
|
8
|
+
Object.defineProperty(exports, "contractSourceFromDefinition", { enumerable: true, get: function () { return contract_source_1.contractSourceFromDefinition; } });
|
|
9
|
+
Object.defineProperty(exports, "contractSourceFromManifest", { enumerable: true, get: function () { return contract_source_1.contractSourceFromManifest; } });
|
|
10
|
+
Object.defineProperty(exports, "generateContractDeclaration", { enumerable: true, get: function () { return contract_source_1.generateContractDeclaration; } });
|
|
11
|
+
Object.defineProperty(exports, "parseContractSelector", { enumerable: true, get: function () { return contract_source_1.parseContractSelector; } });
|
|
@@ -313,6 +313,7 @@ async function importExistingPackage(dataContext, deps, opts) {
|
|
|
313
313
|
packageId,
|
|
314
314
|
localPath: opts.location,
|
|
315
315
|
packageVersion: result.packageVersion,
|
|
316
|
+
warning: result.warning,
|
|
316
317
|
};
|
|
317
318
|
}
|
|
318
319
|
/**
|
|
@@ -413,6 +414,7 @@ async function createPackage(dataContext, deps, opts) {
|
|
|
413
414
|
packageId,
|
|
414
415
|
localPath: location,
|
|
415
416
|
packageVersion: result.packageVersion,
|
|
417
|
+
warning: result.warning,
|
|
416
418
|
templateWarning,
|
|
417
419
|
};
|
|
418
420
|
}
|
|
@@ -9,7 +9,9 @@ import type { IInstallOpts, IInstallResult, IPackageInfo, IPackageInstallerDeps
|
|
|
9
9
|
* only when content has changed, upserts the PackageVersion, and optionally
|
|
10
10
|
* triggers the package loader and device version resolution.
|
|
11
11
|
*
|
|
12
|
-
*
|
|
12
|
+
* A matching released version is returned without any writes or loading, even when
|
|
13
|
+
* the group has no selected version. Changed bundles at a released number are refused.
|
|
14
|
+
* When the bundle hash matches the existing dev PackageVersion, no files or PV are
|
|
13
15
|
* rewritten. An Admin's Packages row still gets a missing `activePackageVersionId`
|
|
14
16
|
* (and description), and when the id was missing the version is activated on
|
|
15
17
|
* this device unless `skipResolve`.
|
|
@@ -14,8 +14,10 @@ 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
16
|
const persistent_vars_1 = require("../data/persistent-vars");
|
|
17
|
+
const sdk_version_1 = require("../sdk-version");
|
|
17
18
|
const utils_1 = require("../utils");
|
|
18
19
|
const package_author_signer_1 = require("./package-author-signer");
|
|
20
|
+
const package_sdk_version_1 = require("./package-sdk-version");
|
|
19
21
|
/**
|
|
20
22
|
* Install or update a dev PackageVersion from locally-built bundle files.
|
|
21
23
|
*
|
|
@@ -24,7 +26,9 @@ const package_author_signer_1 = require("./package-author-signer");
|
|
|
24
26
|
* only when content has changed, upserts the PackageVersion, and optionally
|
|
25
27
|
* triggers the package loader and device version resolution.
|
|
26
28
|
*
|
|
27
|
-
*
|
|
29
|
+
* A matching released version is returned without any writes or loading, even when
|
|
30
|
+
* the group has no selected version. Changed bundles at a released number are refused.
|
|
31
|
+
* When the bundle hash matches the existing dev PackageVersion, no files or PV are
|
|
28
32
|
* rewritten. An Admin's Packages row still gets a missing `activePackageVersionId`
|
|
29
33
|
* (and description), and when the id was missing the version is activated on
|
|
30
34
|
* this device unless `skipResolve`.
|
|
@@ -39,6 +43,7 @@ async function installPackageFromBundles(dataContext, deps, packageId, opts) {
|
|
|
39
43
|
// 2. Read package info
|
|
40
44
|
const info = await getPackageInfo(deps, localPath);
|
|
41
45
|
const version = info.version ?? "0.0.1";
|
|
46
|
+
const warning = await installedSdkWarning(deps, localPath);
|
|
42
47
|
// 3. Read bundle files
|
|
43
48
|
const distDir = deps.resolvePath(localPath, "dist");
|
|
44
49
|
const packageBundlePath = deps.resolvePath(distDir, "package.bundle.js");
|
|
@@ -60,12 +65,26 @@ async function installPackageFromBundles(dataContext, deps, packageId, opts) {
|
|
|
60
65
|
const uiHash = uiBundle ? (0, file_types_1.computeFileHash)(uiBundle) : undefined;
|
|
61
66
|
// 5. Compute PV hash
|
|
62
67
|
const pvHash = (0, package_versions_1.computePackageVersionHash)(version, "", packageHash, routesHash, uiHash);
|
|
63
|
-
// 6.
|
|
64
|
-
// skipLoad), and still fill an empty activePackageVersionId (e.g. reimport after
|
|
65
|
-
// the Packages row was deleted). If it was empty, activate on the device unless
|
|
66
|
-
// skipResolve.
|
|
68
|
+
// 6. Check released numbers before looking for a mutable local build.
|
|
67
69
|
const pvTable = (0, package_versions_1.PackageVersions)(dataContext);
|
|
68
|
-
const
|
|
70
|
+
const released = (0, package_versions_1.releasedVersionWithNumber)(await pvTable.list({ packageId }), {
|
|
71
|
+
packageId,
|
|
72
|
+
version,
|
|
73
|
+
});
|
|
74
|
+
if (released) {
|
|
75
|
+
if (released.packageBundleFileHash !== packageHash ||
|
|
76
|
+
released.routesBundleFileHash !== routesHash ||
|
|
77
|
+
released.uiBundleFileHash !== uiHash) {
|
|
78
|
+
throw new Error(`v${version} is already released as ${released.versionTag}; bump the version in package.json, rebuild, and reload`);
|
|
79
|
+
}
|
|
80
|
+
return { packageVersion: released, loaded: undefined, unchanged: true, warning };
|
|
81
|
+
}
|
|
82
|
+
// Released rows are immutable builds: a new number gets a new row even when
|
|
83
|
+
// an SDK caller explicitly installs with a release tag.
|
|
84
|
+
const existingPv = versionTag === "beta" || versionTag === "stable"
|
|
85
|
+
? undefined
|
|
86
|
+
: await pvTable.findOne({ packageId, versionTag });
|
|
87
|
+
// An unchanged dev build can still repair appNavs and an empty group selection.
|
|
69
88
|
if (existingPv && existingPv.packageVersionHash === pvHash) {
|
|
70
89
|
let loaded;
|
|
71
90
|
let unchanged = true;
|
|
@@ -77,7 +96,7 @@ async function installPackageFromBundles(dataContext, deps, packageId, opts) {
|
|
|
77
96
|
skipResolve: opts?.skipResolve,
|
|
78
97
|
resolveOnlyWhenActivated: true,
|
|
79
98
|
});
|
|
80
|
-
return { packageVersion: existingPv, loaded, unchanged };
|
|
99
|
+
return { packageVersion: existingPv, loaded, unchanged, warning };
|
|
81
100
|
}
|
|
82
101
|
// 7. Save bundle files (deduped by hash)
|
|
83
102
|
const packageFile = await saveBundleFile(dataContext, packageBundle, `${packageId}-package.bundle.js`, packageId);
|
|
@@ -122,7 +141,17 @@ async function installPackageFromBundles(dataContext, deps, packageId, opts) {
|
|
|
122
141
|
await activateInstalledPackageVersion(dataContext, packageId, savedPv, info, localPath, {
|
|
123
142
|
skipResolve: opts?.skipResolve,
|
|
124
143
|
});
|
|
125
|
-
return { packageVersion: savedPv, loaded, unchanged: false };
|
|
144
|
+
return { packageVersion: savedPv, loaded, unchanged: false, warning };
|
|
145
|
+
}
|
|
146
|
+
async function installedSdkWarning(deps, localPath) {
|
|
147
|
+
try {
|
|
148
|
+
const metadata = await deps.fs.readJson(deps.resolvePath(localPath, "node_modules", "@peers-app", "peers-sdk", "package.json"));
|
|
149
|
+
return (0, package_sdk_version_1.packageSdkVersionWarning)(metadata?.version, sdk_version_1.SDK_VERSION);
|
|
150
|
+
}
|
|
151
|
+
catch {
|
|
152
|
+
// Packages may ship without node_modules, or the host may not be able to read it.
|
|
153
|
+
return undefined;
|
|
154
|
+
}
|
|
126
155
|
}
|
|
127
156
|
/**
|
|
128
157
|
* Sync the Packages row with an installed PackageVersion, then activate it on
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Warn when a package's installed SDK is newer than the host's, using SemVer precedence.
|
|
3
|
+
* Missing or invalid versions are ignored; build metadata does not affect precedence.
|
|
4
|
+
*/
|
|
5
|
+
export declare function packageSdkVersionWarning(packageVersion: unknown, hostVersion: unknown): string | undefined;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.packageSdkVersionWarning = packageSdkVersionWarning;
|
|
4
|
+
/**
|
|
5
|
+
* Warn when a package's installed SDK is newer than the host's, using SemVer precedence.
|
|
6
|
+
* Missing or invalid versions are ignored; build metadata does not affect precedence.
|
|
7
|
+
*/
|
|
8
|
+
function packageSdkVersionWarning(packageVersion, hostVersion) {
|
|
9
|
+
const incoming = parseVersion(packageVersion);
|
|
10
|
+
const host = parseVersion(hostVersion);
|
|
11
|
+
if (!incoming || !host || compareVersions(incoming, host) <= 0)
|
|
12
|
+
return undefined;
|
|
13
|
+
return `Built with @peers-app/peers-sdk ${packageVersion}, newer than this Peers (${hostVersion}); update Peers, or pin the SDK to ${hostVersion} in package.json`;
|
|
14
|
+
}
|
|
15
|
+
function parseVersion(value) {
|
|
16
|
+
if (typeof value !== "string")
|
|
17
|
+
return undefined;
|
|
18
|
+
const match = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?(?:\+[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?$/.exec(value);
|
|
19
|
+
if (!match || match[0] !== value)
|
|
20
|
+
return undefined;
|
|
21
|
+
const prerelease = match[4]?.split(".") ?? [];
|
|
22
|
+
if (prerelease.some((part) => /^0\d+$/.test(part)))
|
|
23
|
+
return undefined;
|
|
24
|
+
return { core: match.slice(1, 4), prerelease };
|
|
25
|
+
}
|
|
26
|
+
function compareIdentifiers(a, b) {
|
|
27
|
+
const aNumeric = /^\d+$/.test(a);
|
|
28
|
+
const bNumeric = /^\d+$/.test(b);
|
|
29
|
+
if (aNumeric && bNumeric && a.length !== b.length)
|
|
30
|
+
return a.length - b.length;
|
|
31
|
+
if (aNumeric !== bNumeric)
|
|
32
|
+
return aNumeric ? -1 : 1;
|
|
33
|
+
return a === b ? 0 : a > b ? 1 : -1;
|
|
34
|
+
}
|
|
35
|
+
function compareVersions(a, b) {
|
|
36
|
+
for (let index = 0; index < a.core.length; index++) {
|
|
37
|
+
const difference = compareIdentifiers(a.core[index], b.core[index]);
|
|
38
|
+
if (difference)
|
|
39
|
+
return difference;
|
|
40
|
+
}
|
|
41
|
+
if (!a.prerelease.length || !b.prerelease.length) {
|
|
42
|
+
return Number(!a.prerelease.length) - Number(!b.prerelease.length);
|
|
43
|
+
}
|
|
44
|
+
for (let index = 0; index < Math.min(a.prerelease.length, b.prerelease.length); index++) {
|
|
45
|
+
const difference = compareIdentifiers(a.prerelease[index], b.prerelease[index]);
|
|
46
|
+
if (difference)
|
|
47
|
+
return difference;
|
|
48
|
+
}
|
|
49
|
+
return a.prerelease.length - b.prerelease.length;
|
|
50
|
+
}
|
|
@@ -45,7 +45,7 @@ export interface IPackageInstallerDeps {
|
|
|
45
45
|
export interface IInstallOpts {
|
|
46
46
|
/** Override local path; otherwise read from the packageLocalPath device var. */
|
|
47
47
|
localPath?: string;
|
|
48
|
-
/**
|
|
48
|
+
/** Tag for a new build (default `"dev"`). An identical existing release is returned as-is. */
|
|
49
49
|
versionTag?: string;
|
|
50
50
|
/** Skip keeping the package in the loader cache for activation. AppNavs are still backfilled when missing. */
|
|
51
51
|
skipLoad?: boolean;
|
|
@@ -54,12 +54,15 @@ export interface IInstallOpts {
|
|
|
54
54
|
}
|
|
55
55
|
/** Result of {@link installPackageFromBundles}. */
|
|
56
56
|
export interface IInstallResult {
|
|
57
|
+
/** Non-fatal SDK compatibility warning for a folder install. */
|
|
58
|
+
warning?: string;
|
|
57
59
|
packageVersion: IPackageVersion;
|
|
58
60
|
loaded: IPeersPackage | undefined;
|
|
59
61
|
/**
|
|
60
62
|
* True when the on-disk bundles match the existing PackageVersion hash and this
|
|
61
63
|
* call persisted no new appNavs. Bundle files and the PackageVersion row are not
|
|
62
|
-
* rewritten; the Packages row and device activation may still
|
|
64
|
+
* rewritten; for a dev version the Packages row and device activation may still
|
|
65
|
+
* be updated. A matching beta/stable version causes no writes or loading.
|
|
63
66
|
*/
|
|
64
67
|
unchanged: boolean;
|
|
65
68
|
}
|
|
@@ -109,11 +112,13 @@ export interface ITemplateCacheResult {
|
|
|
109
112
|
}
|
|
110
113
|
/** Result of {@link createPackage}. */
|
|
111
114
|
export interface ICreatePackageResult {
|
|
115
|
+
/** Non-fatal SDK compatibility warning from installing the folder's bundles. */
|
|
116
|
+
warning?: string;
|
|
112
117
|
/** New or imported package ID. */
|
|
113
118
|
packageId: string;
|
|
114
119
|
/** On-disk package directory. */
|
|
115
120
|
localPath: string;
|
|
116
|
-
/** Installed
|
|
121
|
+
/** Installed build, or an identical already-released version. */
|
|
117
122
|
packageVersion: IPackageVersion;
|
|
118
123
|
/**
|
|
119
124
|
* Present when the default template cache could not be refreshed and a
|
|
@@ -136,12 +141,14 @@ export interface IClonePackageOpts {
|
|
|
136
141
|
}
|
|
137
142
|
/** Result of {@link clonePackage}. */
|
|
138
143
|
export interface IClonePackageResult {
|
|
144
|
+
/** Non-fatal SDK compatibility warning from installing the folder's bundles. */
|
|
145
|
+
warning?: string;
|
|
139
146
|
/** Imported package ID from the cloned repo. */
|
|
140
147
|
packageId: string;
|
|
141
148
|
/** On-disk clone directory. */
|
|
142
149
|
localPath: string;
|
|
143
150
|
/** Git URL that was cloned. */
|
|
144
151
|
remoteRepo: string;
|
|
145
|
-
/** Installed
|
|
152
|
+
/** Installed build, or an identical already-released version. */
|
|
146
153
|
packageVersion: IPackageVersion;
|
|
147
154
|
}
|
|
@@ -48,34 +48,17 @@ export interface IIsolatedContractTool extends Omit<IContractTool, "inputFields"
|
|
|
48
48
|
* {@link ISOLATED_TOOL_TIMEOUT_MAX_MS}. Host calls made during the invoke,
|
|
49
49
|
* including System HTTP, count against this deadline, as does time a handler spends
|
|
50
50
|
* waiting on the package's own locks. A call that times out is rejected; its handler keeps
|
|
51
|
-
* running unless the worker is stuck
|
|
51
|
+
* running unless the worker is stuck.
|
|
52
52
|
*/
|
|
53
53
|
timeoutMs?: number;
|
|
54
54
|
}
|
|
55
55
|
/**
|
|
56
|
-
* The
|
|
57
|
-
*
|
|
58
|
-
* - `3`: as `2`, and the package serves its own contract members. The host sends consumers'
|
|
59
|
-
* table calls, observable reads and writes, and subscriptions to the provider's worker
|
|
60
|
-
* (`contractMemberHandlerName`), and passes every table change, event, and observable
|
|
61
|
-
* value bound for a subscriber through it first (`contractDeliveryHandlerName`).
|
|
62
|
-
* - `2`: handlers receive `(args, ctx)` and calls run concurrently; the package orders any
|
|
63
|
-
* state it shares between calls itself. The host serves contract tables and stored
|
|
64
|
-
* observables from storage, without the worker.
|
|
65
|
-
* - Omitted: the legacy global helpers, with one call at a time.
|
|
66
|
-
*
|
|
67
|
-
* Hosts that predate a version reject it.
|
|
56
|
+
* The guest API version built and run by Peers. Consumers' contract member calls and
|
|
57
|
+
* notifications pass through the provider's worker under the caller's authority.
|
|
68
58
|
*/
|
|
69
59
|
export declare const ISOLATED_GUEST_API_VERSION = 3;
|
|
70
|
-
/** Guest API
|
|
71
|
-
export type IsolatedGuestApi =
|
|
72
|
-
/**
|
|
73
|
-
* True when the package serves its contract members itself (guest API 3): every consumer call
|
|
74
|
-
* and outgoing message goes through its worker.
|
|
75
|
-
*
|
|
76
|
-
* @param manifest - The package's manifest.
|
|
77
|
-
*/
|
|
78
|
-
export declare function servesContractMembers(manifest: Pick<IIsolatedPackageManifest, "guestApi">): boolean;
|
|
60
|
+
/** Guest API version for packages built with peers-package. */
|
|
61
|
+
export type IsolatedGuestApi = 3;
|
|
79
62
|
/** Longest per-tool invoke deadline an isolated manifest may declare. */
|
|
80
63
|
export declare const ISOLATED_TOOL_TIMEOUT_MAX_MS = 120000;
|
|
81
64
|
/**
|
|
@@ -152,9 +135,9 @@ export interface IIsolatedContractPersistentVarBinding {
|
|
|
152
135
|
}
|
|
153
136
|
/**
|
|
154
137
|
* Observable contract member. With `persistentVar`, a package-owned persistent variable holds
|
|
155
|
-
* the value. Without it
|
|
156
|
-
* publishes it with `publishOwnedObservable`.
|
|
157
|
-
*
|
|
138
|
+
* the value. Without it, the worker holds the value in memory and
|
|
139
|
+
* publishes it with `publishOwnedObservable`. All consumer calls and deliveries pass
|
|
140
|
+
* through the worker.
|
|
158
141
|
*/
|
|
159
142
|
export interface IIsolatedContractObservable extends IContractObservable {
|
|
160
143
|
persistentVar?: IIsolatedContractPersistentVarBinding;
|
|
@@ -224,9 +207,8 @@ export interface IIsolatedPackageManifest {
|
|
|
224
207
|
network?: IIsolatedPackageNetwork;
|
|
225
208
|
appNavs?: IAppNav[];
|
|
226
209
|
/**
|
|
227
|
-
* Guest API version ({@link ISOLATED_GUEST_API_VERSION}).
|
|
228
|
-
*
|
|
229
|
-
* legacy global helpers, which run one call at a time.
|
|
210
|
+
* Guest API version ({@link ISOLATED_GUEST_API_VERSION}). Required by the runtime;
|
|
211
|
+
* optional here so build tools can inspect historical bundles without running them.
|
|
230
212
|
*/
|
|
231
213
|
guestApi?: IsolatedGuestApi;
|
|
232
214
|
}
|
|
@@ -311,15 +293,20 @@ export declare function looksLikeIsolatedPackageArtifact(bundle: string): boolea
|
|
|
311
293
|
*/
|
|
312
294
|
export declare function validateIsolatedPackageArtifact(artifact: IIsolatedPackageArtifact, options?: {
|
|
313
295
|
strict?: boolean;
|
|
296
|
+
/** Refuse unsupported guest APIs before validating their contents for execution. */
|
|
297
|
+
requireCurrentGuestApi?: boolean;
|
|
298
|
+
/** Display name to include alongside the package ID in a runtime refusal. */
|
|
299
|
+
packageName?: string;
|
|
314
300
|
}): IIsolatedPackageManifest;
|
|
315
301
|
/**
|
|
316
302
|
* Parse a magic-header + JSON isolated package bundle.
|
|
317
303
|
*
|
|
318
304
|
* @param bundle - Full package-bundle text.
|
|
305
|
+
* @param options - Runtime loaders require the current guest API; build tools may inspect older bundles.
|
|
319
306
|
* @returns The validated artifact.
|
|
320
307
|
* @throws If the magic header is present but the envelope is malformed.
|
|
321
308
|
*/
|
|
322
|
-
export declare function parseIsolatedPackageArtifact(bundle: string): IIsolatedPackageArtifact;
|
|
309
|
+
export declare function parseIsolatedPackageArtifact(bundle: string, options?: Parameters<typeof validateIsolatedPackageArtifact>[1]): IIsolatedPackageArtifact;
|
|
323
310
|
/**
|
|
324
311
|
* Serialize an isolated artifact into the package-bundle slot format.
|
|
325
312
|
*
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.ISOLATED_MAX_FIELD_DEPTH = exports.ISOLATED_OBSERVABLE_HOST_MEMBER = exports.ISOLATED_EVENT_HOST_MEMBER = exports.ISOLATED_PERSISTENT_VAR_HOST_MEMBER = exports.ISOLATED_TABLE_HOST_MEMBER = exports.ISOLATED_CONSUME_HOST_MEMBER = exports.ISOLATED_TOOL_TIMEOUT_MAX_MS = exports.ISOLATED_GUEST_API_VERSION = exports.ISOLATED_PACKAGE_MAGIC = void 0;
|
|
4
|
-
exports.servesContractMembers = servesContractMembers;
|
|
5
4
|
exports.enumValueFits = enumValueFits;
|
|
6
5
|
exports.looksLikeIsolatedPackageArtifact = looksLikeIsolatedPackageArtifact;
|
|
7
6
|
exports.validateIsolatedPackageArtifact = validateIsolatedPackageArtifact;
|
|
@@ -30,29 +29,10 @@ const utils_1 = require("../utils");
|
|
|
30
29
|
*/
|
|
31
30
|
exports.ISOLATED_PACKAGE_MAGIC = "PEERS_ISOLATED_PACKAGE_V1";
|
|
32
31
|
/**
|
|
33
|
-
* The
|
|
34
|
-
*
|
|
35
|
-
* - `3`: as `2`, and the package serves its own contract members. The host sends consumers'
|
|
36
|
-
* table calls, observable reads and writes, and subscriptions to the provider's worker
|
|
37
|
-
* (`contractMemberHandlerName`), and passes every table change, event, and observable
|
|
38
|
-
* value bound for a subscriber through it first (`contractDeliveryHandlerName`).
|
|
39
|
-
* - `2`: handlers receive `(args, ctx)` and calls run concurrently; the package orders any
|
|
40
|
-
* state it shares between calls itself. The host serves contract tables and stored
|
|
41
|
-
* observables from storage, without the worker.
|
|
42
|
-
* - Omitted: the legacy global helpers, with one call at a time.
|
|
43
|
-
*
|
|
44
|
-
* Hosts that predate a version reject it.
|
|
32
|
+
* The guest API version built and run by Peers. Consumers' contract member calls and
|
|
33
|
+
* notifications pass through the provider's worker under the caller's authority.
|
|
45
34
|
*/
|
|
46
35
|
exports.ISOLATED_GUEST_API_VERSION = 3;
|
|
47
|
-
/**
|
|
48
|
-
* True when the package serves its contract members itself (guest API 3): every consumer call
|
|
49
|
-
* and outgoing message goes through its worker.
|
|
50
|
-
*
|
|
51
|
-
* @param manifest - The package's manifest.
|
|
52
|
-
*/
|
|
53
|
-
function servesContractMembers(manifest) {
|
|
54
|
-
return manifest.guestApi === 3;
|
|
55
|
-
}
|
|
56
36
|
/** Longest per-tool invoke deadline an isolated manifest may declare. */
|
|
57
37
|
exports.ISOLATED_TOOL_TIMEOUT_MAX_MS = 120_000;
|
|
58
38
|
/** Host-contract member guests use to request a consumed-contract tool call. */
|
|
@@ -340,7 +320,7 @@ function validatePackageTables(manifest, strict) {
|
|
|
340
320
|
(0, package_table_identity_1.validateTableIndexes)(table.indexes, new Set(table.fields.map((field) => field.name)), `${tableLabel}.indexes`, table.localOnly);
|
|
341
321
|
}
|
|
342
322
|
}
|
|
343
|
-
function validateProvidedContract(contract, index, packageTables,
|
|
323
|
+
function validateProvidedContract(contract, index, packageTables, strict) {
|
|
344
324
|
const label = `provides[${index}]`;
|
|
345
325
|
if (!contract || typeof contract !== "object" || Array.isArray(contract)) {
|
|
346
326
|
throw new Error(`${label} must be an object`);
|
|
@@ -488,13 +468,8 @@ function validateProvidedContract(contract, index, packageTables, guestApi, stri
|
|
|
488
468
|
throw new Error(`${observableLabel}.writable must be a boolean`);
|
|
489
469
|
}
|
|
490
470
|
const binding = observable.persistentVar;
|
|
491
|
-
if (binding === undefined)
|
|
492
|
-
// Held by the worker, which publishes it: that needs guest API 2 or later.
|
|
493
|
-
if (guestApi === undefined) {
|
|
494
|
-
throw new Error(`${observableLabel}.persistentVar is required unless the package uses guestApi 2 or later`);
|
|
495
|
-
}
|
|
471
|
+
if (binding === undefined)
|
|
496
472
|
continue;
|
|
497
|
-
}
|
|
498
473
|
if (!binding || typeof binding !== "object" || Array.isArray(binding)) {
|
|
499
474
|
throw new Error(`${observableLabel}.persistentVar must be an object`);
|
|
500
475
|
}
|
|
@@ -777,6 +752,15 @@ function validateIsolatedPackageArtifact(artifact, options = {}) {
|
|
|
777
752
|
if (!manifest || typeof manifest !== "object" || Array.isArray(manifest)) {
|
|
778
753
|
throw new Error("Package artifact.manifest is required");
|
|
779
754
|
}
|
|
755
|
+
if (options.requireCurrentGuestApi && manifest.guestApi !== exports.ISOLATED_GUEST_API_VERSION) {
|
|
756
|
+
const identity = options.packageName
|
|
757
|
+
? `'${options.packageName}' (${manifest.packageId})`
|
|
758
|
+
: manifest.packageId;
|
|
759
|
+
if (typeof manifest.guestApi === "number" && manifest.guestApi > exports.ISOLATED_GUEST_API_VERSION) {
|
|
760
|
+
throw new Error(`Package ${identity} needs a newer version of Peers; update Peers to use it.`);
|
|
761
|
+
}
|
|
762
|
+
throw new Error(`Package ${identity} has an unsupported guest API. Rebuild it with peers-package; guest API 2 and packages without guestApi are no longer supported.`);
|
|
763
|
+
}
|
|
780
764
|
assertOnlyKeys(manifest, [
|
|
781
765
|
"packageId",
|
|
782
766
|
"version",
|
|
@@ -802,7 +786,9 @@ function validateIsolatedPackageArtifact(artifact, options = {}) {
|
|
|
802
786
|
manifest.executionMode !== "privileged") {
|
|
803
787
|
throw new Error('manifest.executionMode must be "sandbox" or "privileged"');
|
|
804
788
|
}
|
|
805
|
-
|
|
789
|
+
const guestApi = manifest.guestApi;
|
|
790
|
+
// Contract import/freezing tools still inspect historical bundles without executing them.
|
|
791
|
+
if (guestApi !== undefined && guestApi !== 2 && guestApi !== 3) {
|
|
806
792
|
throw new Error("manifest.guestApi must be 2 or 3 when set");
|
|
807
793
|
}
|
|
808
794
|
assertPeerId(manifest.packageId, "packageId");
|
|
@@ -820,7 +806,7 @@ function validateIsolatedPackageArtifact(artifact, options = {}) {
|
|
|
820
806
|
const packageTables = manifest.tables ?? [];
|
|
821
807
|
const providedKeys = new Set();
|
|
822
808
|
for (const [index, contract] of manifest.provides.entries()) {
|
|
823
|
-
validateProvidedContract(contract, index, packageTables,
|
|
809
|
+
validateProvidedContract(contract, index, packageTables, strict);
|
|
824
810
|
const key = `${contract.contractId}@${contract.version}`;
|
|
825
811
|
if (providedKeys.has(key)) {
|
|
826
812
|
throw new Error(`Duplicate provided contract ${key}`);
|
|
@@ -857,10 +843,11 @@ function validateIsolatedPackageArtifact(artifact, options = {}) {
|
|
|
857
843
|
* Parse a magic-header + JSON isolated package bundle.
|
|
858
844
|
*
|
|
859
845
|
* @param bundle - Full package-bundle text.
|
|
846
|
+
* @param options - Runtime loaders require the current guest API; build tools may inspect older bundles.
|
|
860
847
|
* @returns The validated artifact.
|
|
861
848
|
* @throws If the magic header is present but the envelope is malformed.
|
|
862
849
|
*/
|
|
863
|
-
function parseIsolatedPackageArtifact(bundle) {
|
|
850
|
+
function parseIsolatedPackageArtifact(bundle, options = {}) {
|
|
864
851
|
if (!looksLikeIsolatedPackageArtifact(bundle)) {
|
|
865
852
|
throw new Error("Bundle is not an isolated package artifact");
|
|
866
853
|
}
|
|
@@ -878,7 +865,7 @@ function parseIsolatedPackageArtifact(bundle) {
|
|
|
878
865
|
throw new Error(`Isolated package artifact JSON is invalid: ${err instanceof Error ? err.message : String(err)}`);
|
|
879
866
|
}
|
|
880
867
|
const artifact = parsed;
|
|
881
|
-
validateIsolatedPackageArtifact(artifact);
|
|
868
|
+
validateIsolatedPackageArtifact(artifact, options);
|
|
882
869
|
return artifact;
|
|
883
870
|
}
|
|
884
871
|
/**
|
|
@@ -30,7 +30,7 @@ export interface IPackageContractEndpointOptions {
|
|
|
30
30
|
* Loads package versions for one {@link DataContext}.
|
|
31
31
|
*
|
|
32
32
|
* Isolated artifacts (magic-header JSON) are installed without host
|
|
33
|
-
* evaluation.
|
|
33
|
+
* evaluation. Host-backed `definePackage()` bundles evaluate in-process.
|
|
34
34
|
*/
|
|
35
35
|
export declare class PackageLoader {
|
|
36
36
|
readonly dataContext: DataContext;
|
|
@@ -75,7 +75,7 @@ export declare class PackageLoader {
|
|
|
75
75
|
get catalogReady(): Promise<void>;
|
|
76
76
|
/** Optional isolated-provider supervisor created from the host factory. */
|
|
77
77
|
readonly isolatedRuntime?: IIsolatedPackageRuntime;
|
|
78
|
-
/** Optional host `require` used when reading local bundles or evaluating
|
|
78
|
+
/** Optional host `require` used when reading local bundles or evaluating host-backed packages. */
|
|
79
79
|
require: (<T>(module: string) => T) | undefined;
|
|
80
80
|
/**
|
|
81
81
|
* @param dataContext - Owning personal or group data context. Its
|
|
@@ -116,7 +116,7 @@ export declare class PackageLoader {
|
|
|
116
116
|
*/
|
|
117
117
|
private recordLoadFailure;
|
|
118
118
|
private dispatchToolFromProvider;
|
|
119
|
-
/** Project a
|
|
119
|
+
/** Project a host caller's declared tool reach and authority for advisory catalog listings. */
|
|
120
120
|
hostToolPrincipal(principal: IIsolatedCallPrincipal, packageId: string, target: {
|
|
121
121
|
contractId: string;
|
|
122
122
|
version: number;
|
|
@@ -146,7 +146,7 @@ export declare class PackageLoader {
|
|
|
146
146
|
resolveContractEndpoint(request: IContractProviderEndpointRequest, opts?: IPackageContractEndpointOptions): Promise<IContractProviderEndpoint>;
|
|
147
147
|
private guardResolutionTools;
|
|
148
148
|
/**
|
|
149
|
-
* Bind a
|
|
149
|
+
* Bind a host-backed package's declared consume to its selected provider.
|
|
150
150
|
*
|
|
151
151
|
* The returned proxy can target either a resolver-backed or isolated provider.
|
|
152
152
|
* Its provider choice is captured from {@link ContractRegistry.selectProvider}
|
|
@@ -158,7 +158,7 @@ export declare class PackageLoader {
|
|
|
158
158
|
*/
|
|
159
159
|
resolveConsumedContract(dependency: IConsumedContract, callerPackageId: string): Promise<IContractConsumer | undefined>;
|
|
160
160
|
/**
|
|
161
|
-
* Bind a
|
|
161
|
+
* Bind a host-backed package's `remote()` declaration to a per-device consumer factory.
|
|
162
162
|
*
|
|
163
163
|
* No local provider is selected: every device consumer stamps `targetDeviceId` and
|
|
164
164
|
* this loader's own router forwards the call through
|
|
@@ -46,7 +46,7 @@ function isolatedContractLifecycle(packageVersion) {
|
|
|
46
46
|
* Loads package versions for one {@link DataContext}.
|
|
47
47
|
*
|
|
48
48
|
* Isolated artifacts (magic-header JSON) are installed without host
|
|
49
|
-
* evaluation.
|
|
49
|
+
* evaluation. Host-backed `definePackage()` bundles evaluate in-process.
|
|
50
50
|
*/
|
|
51
51
|
class PackageLoader {
|
|
52
52
|
dataContext;
|
|
@@ -93,7 +93,7 @@ class PackageLoader {
|
|
|
93
93
|
}
|
|
94
94
|
/** Optional isolated-provider supervisor created from the host factory. */
|
|
95
95
|
isolatedRuntime;
|
|
96
|
-
/** Optional host `require` used when reading local bundles or evaluating
|
|
96
|
+
/** Optional host `require` used when reading local bundles or evaluating host-backed packages. */
|
|
97
97
|
require = undefined;
|
|
98
98
|
/**
|
|
99
99
|
* @param dataContext - Owning personal or group data context. Its
|
|
@@ -264,7 +264,7 @@ class PackageLoader {
|
|
|
264
264
|
endpoint.dispose();
|
|
265
265
|
}
|
|
266
266
|
}
|
|
267
|
-
/** Project a
|
|
267
|
+
/** Project a host caller's declared tool reach and authority for advisory catalog listings. */
|
|
268
268
|
hostToolPrincipal(principal, packageId, target) {
|
|
269
269
|
if (!principal.userId || this.isolatedRuntime?.isInstalled(packageId))
|
|
270
270
|
return undefined;
|
|
@@ -473,7 +473,7 @@ class PackageLoader {
|
|
|
473
473
|
};
|
|
474
474
|
}
|
|
475
475
|
/**
|
|
476
|
-
* Bind a
|
|
476
|
+
* Bind a host-backed package's declared consume to its selected provider.
|
|
477
477
|
*
|
|
478
478
|
* The returned proxy can target either a resolver-backed or isolated provider.
|
|
479
479
|
* Its provider choice is captured from {@link ContractRegistry.selectProvider}
|
|
@@ -514,7 +514,7 @@ class PackageLoader {
|
|
|
514
514
|
return consumer;
|
|
515
515
|
}
|
|
516
516
|
/**
|
|
517
|
-
* Bind a
|
|
517
|
+
* Bind a host-backed package's `remote()` declaration to a per-device consumer factory.
|
|
518
518
|
*
|
|
519
519
|
* No local provider is selected: every device consumer stamps `targetDeviceId` and
|
|
520
520
|
* this loader's own router forwards the call through
|
|
@@ -910,7 +910,10 @@ class PackageLoader {
|
|
|
910
910
|
assertCurrent();
|
|
911
911
|
if ((0, isolated_artifact_1.looksLikeIsolatedPackageArtifact)(bundleCode)) {
|
|
912
912
|
try {
|
|
913
|
-
const artifact = (0, isolated_artifact_1.parseIsolatedPackageArtifact)(bundleCode
|
|
913
|
+
const artifact = (0, isolated_artifact_1.parseIsolatedPackageArtifact)(bundleCode, {
|
|
914
|
+
requireCurrentGuestApi: true,
|
|
915
|
+
packageName: pkg.name,
|
|
916
|
+
});
|
|
914
917
|
return await this._installIsolated(pkg, artifact, selectedPackageVersion, usingLocalBundle, force, assertCurrent);
|
|
915
918
|
}
|
|
916
919
|
catch (err) {
|
|
@@ -920,7 +923,7 @@ class PackageLoader {
|
|
|
920
923
|
await this.catalogReady;
|
|
921
924
|
assertCurrent();
|
|
922
925
|
const replacingIsolated = this.isolatedRuntime?.isInstalled(pkg.packageId) ?? false;
|
|
923
|
-
const instance = await this._evaluateBundle(pkg, bundleCode, selectedPackageVersion,
|
|
926
|
+
const instance = await this._evaluateBundle(pkg, bundleCode, selectedPackageVersion, assertCurrent);
|
|
924
927
|
if (!instance) {
|
|
925
928
|
return undefined;
|
|
926
929
|
}
|
|
@@ -995,7 +998,7 @@ class PackageLoader {
|
|
|
995
998
|
});
|
|
996
999
|
return instance;
|
|
997
1000
|
}
|
|
998
|
-
async _evaluateBundle(pkg, bundleCode, selectedPackageVersion,
|
|
1001
|
+
async _evaluateBundle(pkg, bundleCode, selectedPackageVersion, assertCurrent = this.guardLoad(pkg.packageId)) {
|
|
999
1002
|
// Node.js built-in modules that do not exist in React Native (and would cause
|
|
1000
1003
|
// a fatal native error if passed through to RN's require). We block them here
|
|
1001
1004
|
// and throw a descriptive JS Error instead, which the surrounding try/catch
|
|
@@ -1063,19 +1066,16 @@ class PackageLoader {
|
|
|
1063
1066
|
const bundleFunction = new Function("module", "exports", "require", bundleCode);
|
|
1064
1067
|
const activateResolvers = (0, contract_resolvers_1.stagePackageContractResolvers)(this.dataContext, pkg.packageId, () => bundleFunction(module, moduleExports, customRequire));
|
|
1065
1068
|
const bundleExports = module.exports;
|
|
1066
|
-
//
|
|
1069
|
+
// Host bundles must export a definePackage() definition.
|
|
1067
1070
|
const packageDefinition = (0, contract_package_loader_1.extractPackageDefinition)(bundleExports);
|
|
1068
1071
|
if (packageDefinition) {
|
|
1069
1072
|
if (packageDefinition.packageId !== pkg.packageId) {
|
|
1070
1073
|
console.warn(`[PackageLoader] packageId mismatch: definition has "${packageDefinition.packageId}" but DB record has "${pkg.packageId}"`);
|
|
1071
1074
|
}
|
|
1072
|
-
//
|
|
1073
|
-
// (e.g. package-installer) can read appNavs, assistants, etc.
|
|
1075
|
+
// Expose loaded identity and navigation to callers such as package-installer.
|
|
1074
1076
|
const contractPackageInstance = {
|
|
1075
1077
|
packageId: packageDefinition.packageId,
|
|
1076
1078
|
appNavs: packageDefinition.appNavs,
|
|
1077
|
-
assistants: packageDefinition.assistants,
|
|
1078
|
-
tableDefinitions: packageDefinition.tableDefinitions,
|
|
1079
1079
|
};
|
|
1080
1080
|
await (0, contract_package_loader_1.installContractPackage)(this.dataContext, packageDefinition, {
|
|
1081
1081
|
registry: this.contractRegistry,
|
|
@@ -1100,36 +1100,7 @@ class PackageLoader {
|
|
|
1100
1100
|
assertCurrent();
|
|
1101
1101
|
return contractPackageInstance;
|
|
1102
1102
|
}
|
|
1103
|
-
|
|
1104
|
-
const packageInstance = bundleExports?.exports || bundleExports?.package || bundleExports?.default || bundleExports;
|
|
1105
|
-
if (packageInstance &&
|
|
1106
|
-
typeof packageInstance === "object" &&
|
|
1107
|
-
typeof packageInstance.packageId === "string" &&
|
|
1108
|
-
packageInstance.packageId.length > 0) {
|
|
1109
|
-
const tools = packageInstance.toolInstances;
|
|
1110
|
-
if (Array.isArray(tools) && tools.length)
|
|
1111
|
-
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)`);
|
|
1112
|
-
await this.toolCatalog.publish(pkg.packageId, [], async () => assertCurrent(), () => {
|
|
1113
|
-
assertCurrent();
|
|
1114
|
-
// Final catalog writes must succeed before changing active host tables.
|
|
1115
|
-
if (packageInstance.tableDefinitions?.length) {
|
|
1116
|
-
this.dataContext.tableContainer.registerTableDefinitions(packageInstance.tableDefinitions, { overwrite: true });
|
|
1117
|
-
}
|
|
1118
|
-
activateResolvers();
|
|
1119
|
-
this.retireHostEndpoints(pkg.packageId);
|
|
1120
|
-
if (force && this.contractInstalledPackages.has(pkg.packageId)) {
|
|
1121
|
-
this.contractRegistry.unregister(pkg.packageId);
|
|
1122
|
-
this.contractInstalledPackages.delete(pkg.packageId);
|
|
1123
|
-
}
|
|
1124
|
-
this.packageInstances[pkg.packageId] = packageInstance;
|
|
1125
|
-
this.recordRunning(pkg.packageId, {
|
|
1126
|
-
...runningVersionFields(selectedPackageVersion),
|
|
1127
|
-
kind: "legacy",
|
|
1128
|
-
});
|
|
1129
|
-
});
|
|
1130
|
-
return packageInstance;
|
|
1131
|
-
}
|
|
1132
|
-
throw new Error("Package bundle exports nothing usable");
|
|
1103
|
+
throw new Error(`Package '${pkg.name}' (${pkg.packageId}) has an unsupported bundle format. Rebuild it with definePackage() and peers-package.`);
|
|
1133
1104
|
}
|
|
1134
1105
|
catch (err) {
|
|
1135
1106
|
this.recordLoadFailure(pkg.packageId, selectedPackageVersion, err, assertCurrent);
|