@peers-app/peers-sdk 0.28.7 → 0.28.9
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/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/contracts/tool-catalog.d.ts +9 -2
- package/dist/contracts/tool-catalog.js +10 -2
- package/dist/data/package-versions.d.ts +4 -0
- package/dist/data/package-versions.js +8 -0
- 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-runtime.d.ts +3 -1
- package/dist/package-loader/package-loader.js +42 -5
- package/dist/sdk-version.d.ts +2 -0
- package/dist/sdk-version.js +5 -0
- package/package.json +1 -1
|
@@ -71,6 +71,12 @@ export interface IContractDefinitionWithPolicy {
|
|
|
71
71
|
definition: IContractDefinition;
|
|
72
72
|
/** Each tool's policy, where the Runtime knows it. */
|
|
73
73
|
tools?: IContractToolPolicy[];
|
|
74
|
+
/** Active provider's table settings, when known; absent on older hosts. */
|
|
75
|
+
tables?: {
|
|
76
|
+
name: string;
|
|
77
|
+
versionNumber?: number;
|
|
78
|
+
localOnly?: boolean;
|
|
79
|
+
}[];
|
|
74
80
|
}
|
|
75
81
|
/**
|
|
76
82
|
* Consumer shape for the System Contract Catalog.
|
|
@@ -47,6 +47,7 @@ function getContractCatalogDefinition() {
|
|
|
47
47
|
outputFields: [
|
|
48
48
|
field("definition", field_type_1.FieldType.object, "The registered definition, in Peers IR."),
|
|
49
49
|
field("tools", field_type_1.FieldType.object, "Each tool's name, accessLevel, and authorityPropagation on the active provider.", { isArray: true, optional: true }),
|
|
50
|
+
field("tables", field_type_1.FieldType.object, "Each table's name, versionNumber, and localOnly setting on the active provider.", { isArray: true, optional: true }),
|
|
50
51
|
],
|
|
51
52
|
},
|
|
52
53
|
],
|
|
@@ -42,6 +42,8 @@ export interface IPackageObservedRuntime {
|
|
|
42
42
|
export interface IPackageRequestedVersion {
|
|
43
43
|
/** Group-wide active version (`Packages.activePackageVersionId`). */
|
|
44
44
|
groupActivePackageVersionId?: string;
|
|
45
|
+
/** The group selected a version whose row is not on this device yet. */
|
|
46
|
+
groupVersionMissing?: boolean;
|
|
45
47
|
/** This device's override, when set. */
|
|
46
48
|
deviceActivePackageVersionId?: string;
|
|
47
49
|
/** The version this device should run: the device override, else the group version. */
|
|
@@ -101,8 +103,8 @@ export interface IPackageOperationResult {
|
|
|
101
103
|
/** True when `createPackage` scaffolded a new package instead of importing an existing folder. */
|
|
102
104
|
created?: boolean;
|
|
103
105
|
/**
|
|
104
|
-
* Non-fatal problem the caller should show
|
|
105
|
-
*
|
|
106
|
+
* Non-fatal problem the caller should show, for example a missing package template.
|
|
107
|
+
* Older hosts also report released-number conflicts here instead of refusing reload.
|
|
106
108
|
*/
|
|
107
109
|
warning?: string;
|
|
108
110
|
/** The package after the operation. */
|
|
@@ -120,7 +122,7 @@ export interface IPackageReloadOutcome {
|
|
|
120
122
|
changed?: boolean;
|
|
121
123
|
/** Failure message when `ok` is false. */
|
|
122
124
|
error?: string;
|
|
123
|
-
/** Non-fatal problem
|
|
125
|
+
/** Non-fatal problem; older hosts may warn here about a reused released number. */
|
|
124
126
|
warning?: string;
|
|
125
127
|
}
|
|
126
128
|
/** Result of `reloadAllPackages`. */
|
|
@@ -141,11 +143,19 @@ export interface IPromotePackageVersionResult extends IPackageOperationResult {
|
|
|
141
143
|
/** True when this device holds the package signing key and signed the promoted version. */
|
|
142
144
|
signed: boolean;
|
|
143
145
|
/**
|
|
144
|
-
* True when
|
|
145
|
-
*
|
|
146
|
+
* True when activation ran, either explicitly with activateForGroup or automatically
|
|
147
|
+
* under the group follow policy. See groupRunsPromotedVersion for the group outcome.
|
|
146
148
|
*/
|
|
147
149
|
activated: boolean;
|
|
150
|
+
/** Group selection after promotion, when one is set. */
|
|
151
|
+
groupActivePackageVersionId?: string;
|
|
152
|
+
/** True even when the group already selected this id before it was re-tagged. */
|
|
153
|
+
groupRunsPromotedVersion?: boolean;
|
|
154
|
+
/** What the group now runs, why selection stayed unchanged, and any next command. */
|
|
155
|
+
activationNote?: string;
|
|
148
156
|
}
|
|
157
|
+
/** Explain a group version missing locally and how to select an available release. */
|
|
158
|
+
export declare function packageGroupVersionWarning(packageId: string, requested: Pick<IPackageRequestedVersion, "groupActivePackageVersionId" | "groupVersionMissing">): string | undefined;
|
|
149
159
|
/**
|
|
150
160
|
* Release channels a version may be promoted to from its current tag: `dev` to `beta` or
|
|
151
161
|
* `stable`, and `beta` to `stable`. Missing or empty tags on legacy versions mean `dev`,
|
|
@@ -207,7 +217,7 @@ export interface IPackagesConsumer extends IContractConsumerBase {
|
|
|
207
217
|
packageVersionId?: string;
|
|
208
218
|
scope?: "device" | "group";
|
|
209
219
|
}): Promise<IPackageOperationResult>;
|
|
210
|
-
/**
|
|
220
|
+
/** Read local bundles, then force-reload the selected version even when unchanged. */
|
|
211
221
|
reloadPackage(input: {
|
|
212
222
|
packageId: string;
|
|
213
223
|
}): Promise<IPackageOperationResult>;
|
|
@@ -228,10 +238,13 @@ export interface IPackagesConsumer extends IContractConsumerBase {
|
|
|
228
238
|
* Promote a stored version to `beta` or `stable`: re-tag, re-sign with the package author
|
|
229
239
|
* key when this device has it, record the promotion in the version's history, and
|
|
230
240
|
* activate it when this device was running it and the group follows the new tag.
|
|
241
|
+
* activateForGroup explicitly selects it for the group and follows that selection here.
|
|
231
242
|
*/
|
|
232
243
|
promotePackageVersion(input: {
|
|
233
244
|
packageVersionId: string;
|
|
234
245
|
versionTag: string;
|
|
246
|
+
/** Also select the promoted version for the group and follow it here (Admin). */
|
|
247
|
+
activateForGroup?: boolean;
|
|
235
248
|
}): Promise<IPromotePackageVersionResult>;
|
|
236
249
|
/** Install the default packages into the bound data context. */
|
|
237
250
|
installDefaultPackages(): Promise<{
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.PACKAGES_CONTRACT_VERSION = void 0;
|
|
4
|
+
exports.packageGroupVersionWarning = packageGroupVersionWarning;
|
|
4
5
|
exports.packageVersionPromotionTargets = packageVersionPromotionTargets;
|
|
5
6
|
exports.isPackageRepositoryUrl = isPackageRepositoryUrl;
|
|
6
7
|
exports.getPackagesContractDefinition = getPackagesContractDefinition;
|
|
@@ -9,6 +10,12 @@ const field_type_1 = require("../../types/field-type");
|
|
|
9
10
|
const system_contracts_1 = require("../system-contracts");
|
|
10
11
|
/** Version of the built-in System Packages contract. */
|
|
11
12
|
exports.PACKAGES_CONTRACT_VERSION = 1;
|
|
13
|
+
/** Explain a group version missing locally and how to select an available release. */
|
|
14
|
+
function packageGroupVersionWarning(packageId, requested) {
|
|
15
|
+
if (!requested.groupVersionMissing || !requested.groupActivePackageVersionId)
|
|
16
|
+
return undefined;
|
|
17
|
+
return `The group's selected version ${requested.groupActivePackageVersionId} isn't on this device; it may not have synced yet. To select an available beta or stable version for the group, run: peers packages activate ${packageId} <packageVersionId> --group`;
|
|
18
|
+
}
|
|
12
19
|
/**
|
|
13
20
|
* Release channels a version may be promoted to from its current tag: `dev` to `beta` or
|
|
14
21
|
* `stable`, and `beta` to `stable`. Missing or empty tags on legacy versions mean `dev`,
|
|
@@ -138,7 +145,7 @@ function getPackagesContractDefinition() {
|
|
|
138
145
|
},
|
|
139
146
|
{
|
|
140
147
|
name: "reloadPackage",
|
|
141
|
-
usageDescription: "
|
|
148
|
+
usageDescription: "Read local bundles, then force-reload the selected version even when unchanged. Changed builds at a released number are refused.",
|
|
142
149
|
inputFields: [packageIdField],
|
|
143
150
|
outputFields: operationResultFields,
|
|
144
151
|
},
|
|
@@ -170,10 +177,11 @@ function getPackagesContractDefinition() {
|
|
|
170
177
|
},
|
|
171
178
|
{
|
|
172
179
|
name: "promotePackageVersion",
|
|
173
|
-
usageDescription: "Promote a stored package version to beta or stable (dev to beta or stable, beta to stable). Re-signs it with the package author key when this device has it, records the promotion in its history, and activates it when this device was running it and the group follows the new tag.",
|
|
180
|
+
usageDescription: "Promote a stored package version to beta or stable (dev to beta or stable, beta to stable). Re-signs it with the package author key when this device has it, records the promotion in its history, and activates it when this device was running it and the group follows the new tag. Optionally selects it for the group with activateForGroup. Reports the group selection and any next command.",
|
|
174
181
|
inputFields: [
|
|
175
182
|
field("packageVersionId", field_type_1.FieldType.id, "Version to promote."),
|
|
176
183
|
field("versionTag", field_type_1.FieldType.string, "New release channel: beta or stable."),
|
|
184
|
+
field("activateForGroup", field_type_1.FieldType.boolean, "Also select this version for the group and follow it here (Admin).", { optional: true }),
|
|
177
185
|
],
|
|
178
186
|
outputFields: [
|
|
179
187
|
...operationResultFields,
|
|
@@ -181,6 +189,11 @@ function getPackagesContractDefinition() {
|
|
|
181
189
|
field("version", field_type_1.FieldType.string, "Its version number."),
|
|
182
190
|
field("versionTag", field_type_1.FieldType.string, "Its new release channel."),
|
|
183
191
|
field("signed", field_type_1.FieldType.boolean, "True when this device signed the promoted version."),
|
|
192
|
+
field("groupActivePackageVersionId", field_type_1.FieldType.id, "Group selection after promotion.", {
|
|
193
|
+
optional: true,
|
|
194
|
+
}),
|
|
195
|
+
field("groupRunsPromotedVersion", field_type_1.FieldType.boolean, "Whether the group now selects the promoted version, including an already-selected id.", { optional: true }),
|
|
196
|
+
field("activationNote", field_type_1.FieldType.string, "What the group now runs and any next command.", { optional: true }),
|
|
184
197
|
field("activated", field_type_1.FieldType.boolean, "True when the promotion also activated the version for the group or this device."),
|
|
185
198
|
],
|
|
186
199
|
},
|
|
@@ -91,8 +91,15 @@ export declare class ToolCatalog {
|
|
|
91
91
|
})[]): Promise<void>;
|
|
92
92
|
/** Reject quarantined or conflicting retained evidence before a contract tool call. */
|
|
93
93
|
assertTargetUsable(target: ToolCatalogTarget): Promise<void>;
|
|
94
|
-
/**
|
|
95
|
-
|
|
94
|
+
/**
|
|
95
|
+
* Serialize host removal with pending publications while retaining all catalog rows.
|
|
96
|
+
* An optional load-generation guard runs before any live registration is changed.
|
|
97
|
+
*
|
|
98
|
+
* @param provider - Package whose registrations are being removed.
|
|
99
|
+
* @param deactivate - Synchronously retire its host endpoints and bookkeeping.
|
|
100
|
+
* @param assertCurrent - Reject a superseded removal without changing the active provider.
|
|
101
|
+
*/
|
|
102
|
+
removeProvider(provider: string, deactivate: () => void, assertCurrent?: () => void): Promise<void>;
|
|
96
103
|
/** Stop subscriptions and queued lifecycle work; permanent rows are retained. */
|
|
97
104
|
dispose(): Promise<void>;
|
|
98
105
|
}
|
|
@@ -605,12 +605,20 @@ class ToolCatalog {
|
|
|
605
605
|
validateToolCatalogWrite({ toolId, ...resolvedFields(target, toolId) }, row);
|
|
606
606
|
}
|
|
607
607
|
}
|
|
608
|
-
/**
|
|
609
|
-
|
|
608
|
+
/**
|
|
609
|
+
* Serialize host removal with pending publications while retaining all catalog rows.
|
|
610
|
+
* An optional load-generation guard runs before any live registration is changed.
|
|
611
|
+
*
|
|
612
|
+
* @param provider - Package whose registrations are being removed.
|
|
613
|
+
* @param deactivate - Synchronously retire its host endpoints and bookkeeping.
|
|
614
|
+
* @param assertCurrent - Reject a superseded removal without changing the active provider.
|
|
615
|
+
*/
|
|
616
|
+
async removeProvider(provider, deactivate, assertCurrent) {
|
|
610
617
|
// Join the same readiness boundary as publish(), including on a cold context,
|
|
611
618
|
// so an earlier install cannot enqueue its activation behind this removal.
|
|
612
619
|
await this.ready();
|
|
613
620
|
await this.enqueue(async () => {
|
|
621
|
+
assertCurrent?.();
|
|
614
622
|
this.registry.unregister(provider);
|
|
615
623
|
deactivate();
|
|
616
624
|
});
|
|
@@ -115,6 +115,10 @@ declare const schema: z.ZodObject<{
|
|
|
115
115
|
}>;
|
|
116
116
|
/** A signed package build and its bundle hashes, tag, and lifecycle history. */
|
|
117
117
|
export type IPackageVersion = z.infer<typeof schema>;
|
|
118
|
+
/** Find a beta or stable build with the same package and version number, excluding a supplied id. */
|
|
119
|
+
export declare function releasedVersionWithNumber(versions: readonly IPackageVersion[], candidate: Pick<IPackageVersion, "packageId" | "version"> & {
|
|
120
|
+
packageVersionId?: string;
|
|
121
|
+
}): IPackageVersion | undefined;
|
|
118
122
|
/** Package version table with signature and group promotion permission checks. */
|
|
119
123
|
export declare class PackageVersionsTable extends Table<IPackageVersion> {
|
|
120
124
|
static isPassthrough: boolean;
|
|
@@ -35,6 +35,7 @@ var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn,
|
|
|
35
35
|
};
|
|
36
36
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
37
37
|
exports.PackageVersionsTable = void 0;
|
|
38
|
+
exports.releasedVersionWithNumber = releasedVersionWithNumber;
|
|
38
39
|
exports.PackageVersions = PackageVersions;
|
|
39
40
|
exports.computePackageVersionHash = computePackageVersionHash;
|
|
40
41
|
exports.isVersionInRange = isVersionInRange;
|
|
@@ -100,6 +101,13 @@ const metaData = {
|
|
|
100
101
|
fields: (0, types_1.schemaToFields)(schema),
|
|
101
102
|
indexes: [{ fields: ["packageId"] }],
|
|
102
103
|
};
|
|
104
|
+
/** Find a beta or stable build with the same package and version number, excluding a supplied id. */
|
|
105
|
+
function releasedVersionWithNumber(versions, candidate) {
|
|
106
|
+
return versions.find((other) => other.packageId === candidate.packageId &&
|
|
107
|
+
other.packageVersionId !== candidate.packageVersionId &&
|
|
108
|
+
other.version === candidate.version &&
|
|
109
|
+
(other.versionTag === "beta" || other.versionTag === "stable"));
|
|
110
|
+
}
|
|
103
111
|
// Symbol.for keeps signer registration shared by separately installed SDK copies.
|
|
104
112
|
const signingKey = Symbol.for("@peers-app/peers-sdk/package-version-signing");
|
|
105
113
|
const signingGlobals = globalThis;
|
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.main = main;
|
|
4
4
|
const node_fs_1 = require("node:fs");
|
|
5
5
|
const path = require("node:path");
|
|
6
|
+
const sdk_version_1 = require("../sdk-version");
|
|
6
7
|
const build_package_1 = require("./build-package");
|
|
7
8
|
const contract_add_1 = require("./contract-add");
|
|
8
9
|
const freeze_contracts_1 = require("./freeze-contracts");
|
|
@@ -11,7 +12,7 @@ const USAGE = `Usage: peers-package <command> [options]
|
|
|
11
12
|
Commands:
|
|
12
13
|
manifest Write dist/manifest.json from the package's declarations (src/package.ts).
|
|
13
14
|
build Write the manifest and dist/package.bundle.js, the artifact the Runtime
|
|
14
|
-
installs.
|
|
15
|
+
installs. Prints the SDK version used to build it.
|
|
15
16
|
contract add <source> Write src/contracts/<name>.v<version>.ts, a declaration of a contract this
|
|
16
17
|
package implements or calls, generated from <source>: a declaration module
|
|
17
18
|
and its export (path/to/file.ts#Name), a provider's manifest.json or
|
|
@@ -96,13 +97,13 @@ async function main(argv) {
|
|
|
96
97
|
console.error(`Build failed: ${result.message}`);
|
|
97
98
|
return;
|
|
98
99
|
}
|
|
99
|
-
console.log(`Built ${relative(result.artifactPath)} (${summarize(result)}; provider ${Math.round(result.providerChars / 1024)} KB)`);
|
|
100
|
+
console.log(`Built ${relative(result.artifactPath)} (${summarize(result)}; SDK ${sdk_version_1.SDK_VERSION}; provider ${Math.round(result.providerChars / 1024)} KB)`);
|
|
100
101
|
});
|
|
101
102
|
console.log("Watching for changes");
|
|
102
103
|
return new Promise(() => undefined);
|
|
103
104
|
}
|
|
104
105
|
const result = await (0, build_package_1.buildPackage)(options);
|
|
105
|
-
console.log(`Built ${relative(result.artifactPath)} (${summarize(result)}; provider ${Math.round(result.providerChars / 1024)} KB)`);
|
|
106
|
+
console.log(`Built ${relative(result.artifactPath)} (${summarize(result)}; SDK ${sdk_version_1.SDK_VERSION}; provider ${Math.round(result.providerChars / 1024)} KB)`);
|
|
106
107
|
return 0;
|
|
107
108
|
}
|
|
108
109
|
/** `peers-package contract add <source> [options]` or `peers-package contract freeze [options]`. */
|
|
@@ -1,11 +1,13 @@
|
|
|
1
|
+
import { type IContractSource } from "./contract-source";
|
|
1
2
|
/** What {@link addContract} takes. */
|
|
2
3
|
export interface IContractAddOptions {
|
|
3
4
|
/**
|
|
4
5
|
* Where the contract comes from: a declaration module and its export
|
|
5
6
|
* (`../voice-hub/src/contracts/voice-skill.ts#VoiceSkill`), a provider's `manifest.json` or
|
|
6
|
-
* `package.bundle.js`,
|
|
7
|
+
* `package.bundle.js`, the JSON `getContractDefinition` returned, or an in-memory
|
|
8
|
+
* contract source (for example from `contractSourceFromDefinition`).
|
|
7
9
|
*/
|
|
8
|
-
source: string;
|
|
10
|
+
source: string | IContractSource;
|
|
9
11
|
/** `contractId@version`, for a manifest that provides more than one contract. */
|
|
10
12
|
contract?: string;
|
|
11
13
|
/** Package root. Defaults to the current directory. */
|
|
@@ -19,9 +19,10 @@ const contract_source_1 = require("./contract-source");
|
|
|
19
19
|
*/
|
|
20
20
|
async function addContract(options) {
|
|
21
21
|
const root = path.resolve(options.root ?? process.cwd());
|
|
22
|
-
const contract =
|
|
22
|
+
const contract = typeof options.source === "string"
|
|
23
|
+
? await readContractSource(root, { ...options, source: options.source })
|
|
24
|
+
: options.source;
|
|
23
25
|
const generated = (0, contract_source_1.generateContractDeclaration)(contract, {
|
|
24
|
-
from: sourceLabel(root, options.source),
|
|
25
26
|
...(options.name ? { exportName: options.name } : {}),
|
|
26
27
|
});
|
|
27
28
|
const target = path.join(path.resolve(root, options.outDir ?? "src/contracts"), generated.fileName);
|
|
@@ -41,15 +42,6 @@ function splitSource(source) {
|
|
|
41
42
|
? { file: source.slice(0, hash), exportName: source.slice(hash + 1) }
|
|
42
43
|
: { file: source };
|
|
43
44
|
}
|
|
44
|
-
/**
|
|
45
|
-
* The source as the file's header names it: relative to the package root, with `/`, so the
|
|
46
|
-
* generated file (and `--check`) doesn't depend on where the command ran or on local paths.
|
|
47
|
-
*/
|
|
48
|
-
function sourceLabel(root, source) {
|
|
49
|
-
const { file, exportName } = splitSource(source);
|
|
50
|
-
const relative = path.relative(root, path.resolve(file)).split(path.sep).join("/");
|
|
51
|
-
return exportName === undefined ? relative : `${relative}#${exportName}`;
|
|
52
|
-
}
|
|
53
45
|
async function readContractSource(root, options) {
|
|
54
46
|
const { file, exportName } = splitSource(options.source);
|
|
55
47
|
if (exportName !== undefined) {
|
|
@@ -49,11 +49,14 @@ export declare function parseContractSelector(selector: string): {
|
|
|
49
49
|
* propagation. Refinements (a regex, a minimum) and defaults made by code aren't in the IR, so
|
|
50
50
|
* generated schemas check only what the IR describes. Objects keep keys their fields don't list.
|
|
51
51
|
*
|
|
52
|
+
* Table storage settings are preserved: implementing the declaration uses its tables.
|
|
53
|
+
* Default settings (schema version 1 and synced storage) are omitted.
|
|
54
|
+
*
|
|
52
55
|
* @param input - The contract and its tables.
|
|
53
|
-
* @param options -
|
|
54
|
-
* instead of the one made from the contract's name.
|
|
56
|
+
* @param options - An export name to use instead of the one made from the contract's name.
|
|
55
57
|
*/
|
|
56
|
-
export declare function generateContractDeclaration(input: IContractSource, options
|
|
57
|
-
|
|
58
|
+
export declare function generateContractDeclaration(input: IContractSource, options?: {
|
|
59
|
+
/** Ignored for compatibility with older callers; generated headers never name a source. */
|
|
60
|
+
from?: string;
|
|
58
61
|
exportName?: string;
|
|
59
62
|
}): IGeneratedContractDeclaration;
|
|
@@ -67,6 +67,7 @@ function contractSourceFromDefinition(input) {
|
|
|
67
67
|
throw new Error("Expected getContractDefinition's result: { definition, tools }");
|
|
68
68
|
}
|
|
69
69
|
const policy = new Map((input.tools ?? []).map((tool) => [tool.name, tool]));
|
|
70
|
+
const tablePolicy = new Map((input.tables ?? []).map((table) => [table.name, table]));
|
|
70
71
|
const tools = definition.tools.map((tool) => {
|
|
71
72
|
const toolPolicy = policy.get(tool.name);
|
|
72
73
|
if (toolPolicy?.accessLevel === undefined) {
|
|
@@ -101,12 +102,17 @@ function contractSourceFromDefinition(input) {
|
|
|
101
102
|
...(definition.observables?.length ? { observables: definition.observables } : {}),
|
|
102
103
|
...(definition.events?.length ? { events: definition.events } : {}),
|
|
103
104
|
},
|
|
104
|
-
tables: (definition.tables ?? []).map((table) =>
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
105
|
+
tables: (definition.tables ?? []).map((table) => {
|
|
106
|
+
const settings = tablePolicy.get(table.name);
|
|
107
|
+
return {
|
|
108
|
+
name: table.name,
|
|
109
|
+
description: table.description,
|
|
110
|
+
primaryKeyName: table.primaryKeyName,
|
|
111
|
+
fields: table.fields,
|
|
112
|
+
...(settings?.versionNumber === undefined ? {} : { versionNumber: settings.versionNumber }),
|
|
113
|
+
...(settings?.localOnly ? { localOnly: true } : {}),
|
|
114
|
+
};
|
|
115
|
+
}),
|
|
110
116
|
};
|
|
111
117
|
}
|
|
112
118
|
/**
|
|
@@ -131,11 +137,13 @@ function parseContractSelector(selector) {
|
|
|
131
137
|
* propagation. Refinements (a regex, a minimum) and defaults made by code aren't in the IR, so
|
|
132
138
|
* generated schemas check only what the IR describes. Objects keep keys their fields don't list.
|
|
133
139
|
*
|
|
140
|
+
* Table storage settings are preserved: implementing the declaration uses its tables.
|
|
141
|
+
* Default settings (schema version 1 and synced storage) are omitted.
|
|
142
|
+
*
|
|
134
143
|
* @param input - The contract and its tables.
|
|
135
|
-
* @param options -
|
|
136
|
-
* instead of the one made from the contract's name.
|
|
144
|
+
* @param options - An export name to use instead of the one made from the contract's name.
|
|
137
145
|
*/
|
|
138
|
-
function generateContractDeclaration(input, options) {
|
|
146
|
+
function generateContractDeclaration(input, options = {}) {
|
|
139
147
|
const { contract, tables } = input;
|
|
140
148
|
assertVersion(contract.version, "Contract version");
|
|
141
149
|
const exportName = options.exportName ?? pascalCase(contract.name);
|
|
@@ -145,7 +153,7 @@ function generateContractDeclaration(input, options) {
|
|
|
145
153
|
const imports = new Set(["declareContract"]);
|
|
146
154
|
const code = new Code(imports);
|
|
147
155
|
const lines = [];
|
|
148
|
-
lines.push(`// Generated
|
|
156
|
+
lines.push(`// Generated contract declaration. Regenerate with \`peers contracts add ${docText(contract.contractId)}@${contract.version}\``, "// or `peers-package contract add <source>`; do not edit by hand.", "");
|
|
149
157
|
const eventNames = new Map();
|
|
150
158
|
for (const event of contract.events ?? []) {
|
|
151
159
|
const name = uniqueName(`${camelCase(exportName)}${pascalCase(event.name)}`, new Set([...RESERVED_NAMES, exportName, ...eventNames.values()]));
|
|
@@ -223,7 +231,7 @@ class Code {
|
|
|
223
231
|
["description", JSON.stringify(table.description ?? "")],
|
|
224
232
|
["primaryKeyName", JSON.stringify(table.primaryKeyName)],
|
|
225
233
|
["schema", this.objectSchema(table.fields, depth + 1)],
|
|
226
|
-
...(table.versionNumber === undefined
|
|
234
|
+
...(table.versionNumber === undefined || table.versionNumber === 1
|
|
227
235
|
? []
|
|
228
236
|
: [["versionNumber", String(table.versionNumber)]]),
|
|
229
237
|
...(table.localOnly ? [["localOnly", "true"]] : []),
|
|
@@ -237,7 +245,7 @@ class Code {
|
|
|
237
245
|
["name", JSON.stringify(tool.name)],
|
|
238
246
|
["usageDescription", JSON.stringify(tool.usageDescription)],
|
|
239
247
|
["accessLevel", this.accessLevel(level)],
|
|
240
|
-
...(tool.authorityPropagation
|
|
248
|
+
...(tool.authorityPropagation && tool.authorityPropagation !== "cap"
|
|
241
249
|
? [["authorityPropagation", JSON.stringify(tool.authorityPropagation)]]
|
|
242
250
|
: []),
|
|
243
251
|
...(tool.remoteAccessLevel === undefined
|
|
@@ -323,7 +331,10 @@ class Code {
|
|
|
323
331
|
return `generatedField(${schema}, ${this.shapeSource(metadata)}${valueType === undefined ? "" : `, ${this.fieldType(valueType)}`})`;
|
|
324
332
|
}
|
|
325
333
|
shapeSource(shape) {
|
|
326
|
-
const entries = Object.entries(shape)
|
|
334
|
+
const entries = Object.entries(shape)
|
|
335
|
+
.filter(([, value]) => value !== undefined)
|
|
336
|
+
.sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0))
|
|
337
|
+
.map(([key, value]) => {
|
|
327
338
|
const source = key === "type"
|
|
328
339
|
? this.fieldType(value)
|
|
329
340
|
: key === "fields"
|
|
@@ -422,6 +433,8 @@ function dataSource(value) {
|
|
|
422
433
|
return `[${value.map(dataSource).join(", ")}]`;
|
|
423
434
|
if (value && typeof value === "object") {
|
|
424
435
|
return `{ ${Object.entries(value)
|
|
436
|
+
.filter(([, entry]) => entry !== undefined)
|
|
437
|
+
.sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0))
|
|
425
438
|
.map(([key, entry]) => `${propertyKey(key)}: ${dataSource(entry)}`)
|
|
426
439
|
.join(", ")} }`;
|
|
427
440
|
}
|
|
@@ -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
|
}
|
|
@@ -92,11 +92,13 @@ export interface IIsolatedPackageUninstallOptions {
|
|
|
92
92
|
/**
|
|
93
93
|
* Host-owned supervisor for isolated package providers.
|
|
94
94
|
*
|
|
95
|
-
* `peers-sdk` defines the interface only. Electron
|
|
95
|
+
* `peers-sdk` defines the interface only. Each Electron, headless or browser host
|
|
96
96
|
* registers a factory that constructs an implementation backed by
|
|
97
97
|
* `peers-isolate` workers. The SDK never depends on Node or SES.
|
|
98
98
|
*/
|
|
99
99
|
export interface IIsolatedPackageRuntime {
|
|
100
|
+
/** Supported provider execution modes. Omitted by older hosts that support both modes. */
|
|
101
|
+
readonly supportedExecutionModes?: readonly ("sandbox" | "privileged")[];
|
|
100
102
|
/**
|
|
101
103
|
* Register a validated artifact. Provider source is stored, not evaluated
|
|
102
104
|
* on the host.
|
|
@@ -955,13 +955,50 @@ class PackageLoader {
|
|
|
955
955
|
throw wrapIsolatedArtifactError(new Error(`Isolated package version mismatch: artifact has "${artifact.manifest.version}" but selected PackageVersion has "${selectedPackageVersion.version}"`));
|
|
956
956
|
}
|
|
957
957
|
}
|
|
958
|
+
const hasProvider = artifact.providerSource.trim() !== "";
|
|
959
|
+
const executionMode = artifact.manifest.executionMode ?? "sandbox";
|
|
960
|
+
const unsupportedReason = hasProvider
|
|
961
|
+
? !this.isolatedRuntime
|
|
962
|
+
? "This host has no isolated package runtime"
|
|
963
|
+
: this.isolatedRuntime.supportedExecutionModes &&
|
|
964
|
+
!this.isolatedRuntime.supportedExecutionModes.includes(executionMode)
|
|
965
|
+
? "This package runs only on desktop or headless"
|
|
966
|
+
: undefined
|
|
967
|
+
: undefined;
|
|
968
|
+
if (unsupportedReason) {
|
|
969
|
+
await this.unloadPackage(pkg.packageId, { status: "unsupported", reason: unsupportedReason });
|
|
970
|
+
console.warn(!this.isolatedRuntime
|
|
971
|
+
? `[PackageLoader] Skipping isolated package "${pkg.name}" because this host does not provide an isolated runtime.`
|
|
972
|
+
: `[PackageLoader] Skipping isolated package "${pkg.name}": ${unsupportedReason}`);
|
|
973
|
+
return undefined;
|
|
974
|
+
}
|
|
975
|
+
// Empty provider source is the validated screen-only artifact shape. A host with
|
|
976
|
+
// no runtime still publishes its navigation, just as a desktop runtime does.
|
|
958
977
|
if (!this.isolatedRuntime) {
|
|
959
|
-
await this.
|
|
960
|
-
|
|
961
|
-
|
|
978
|
+
await this.toolCatalog.removeProvider(pkg.packageId, () => {
|
|
979
|
+
this.retireHostEndpoints(pkg.packageId);
|
|
980
|
+
(0, contract_resolvers_1.unregisterPackageContractResolvers)(this.dataContext, pkg.packageId);
|
|
981
|
+
this.hostConsumes.delete(pkg.packageId);
|
|
982
|
+
this.contractInstalledPackages.delete(pkg.packageId);
|
|
983
|
+
}, assertCurrent);
|
|
984
|
+
assertCurrent();
|
|
985
|
+
const instance = {
|
|
986
|
+
packageId: artifact.manifest.packageId,
|
|
987
|
+
appNavs: artifact.manifest.appNavs,
|
|
988
|
+
};
|
|
989
|
+
this.packageInstances[pkg.packageId] = instance;
|
|
990
|
+
this.recordRunning(pkg.packageId, {
|
|
991
|
+
...runningVersionFields(selectedPackageVersion),
|
|
992
|
+
kind: "isolated",
|
|
993
|
+
executionMode,
|
|
994
|
+
provides: [],
|
|
995
|
+
consumes: artifact.manifest.consumes.map(({ contractId, version }) => ({
|
|
996
|
+
contractId,
|
|
997
|
+
version,
|
|
998
|
+
satisfied: !!this.contractRegistry.getProviderPackageId(contractId, version),
|
|
999
|
+
})),
|
|
962
1000
|
});
|
|
963
|
-
|
|
964
|
-
return undefined;
|
|
1001
|
+
return instance;
|
|
965
1002
|
}
|
|
966
1003
|
assertCurrent();
|
|
967
1004
|
if (this.isolatedPackageNames.get(pkg.packageId) !== pkg.name) {
|