@peers-app/peers-sdk 0.26.5 → 0.27.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -1
- package/bin/peers-package.js +13 -0
- package/define/package.json +4 -0
- package/dist/contracts/__tests__/action-dispatch.test.js +56 -76
- package/dist/contracts/__tests__/also-implements.test.d.ts +1 -0
- package/dist/contracts/__tests__/also-implements.test.js +110 -0
- package/dist/contracts/__tests__/builder.test.js +45 -0
- package/dist/contracts/__tests__/contract-resolvers.test.js +134 -4
- package/dist/contracts/__tests__/frozen-contracts.test.d.ts +1 -0
- package/dist/contracts/__tests__/frozen-contracts.test.js +191 -0
- package/dist/contracts/__tests__/generic-tool-dispatch.test.js +13 -56
- package/dist/contracts/__tests__/host-contract-client.test.d.ts +1 -0
- package/dist/contracts/__tests__/host-contract-client.test.js +793 -0
- package/dist/contracts/__tests__/host-contract-transport.test.js +15 -0
- package/dist/contracts/__tests__/registry.test.js +68 -0
- package/dist/contracts/__tests__/target-dispatch.d.ts +11 -0
- package/dist/contracts/__tests__/target-dispatch.js +24 -0
- package/dist/contracts/__tests__/tool-catalog-service.test.js +37 -1
- package/dist/contracts/__tests__/tool-catalog.test.js +12 -3
- package/dist/contracts/__tests__/validate.test.js +177 -7
- package/dist/contracts/__tests__/workflow-context.test.js +0 -11
- package/dist/contracts/builder.d.ts +16 -2
- package/dist/contracts/builder.js +44 -3
- package/dist/contracts/contract-definition-schema.d.ts +251 -0
- package/dist/contracts/contract-definition-schema.js +48 -0
- package/dist/contracts/contract-provider-router.d.ts +2 -0
- package/dist/contracts/contract-provider-router.js +5 -2
- package/dist/contracts/contract-proxy.d.ts +52 -18
- package/dist/contracts/contract-proxy.js +144 -203
- package/dist/contracts/contract-resolvers.d.ts +6 -4
- package/dist/contracts/contract-resolvers.js +32 -9
- package/dist/contracts/frozen-contracts.d.ts +59 -0
- package/dist/contracts/frozen-contracts.js +86 -0
- package/dist/contracts/host-contract-client.d.ts +134 -0
- package/dist/contracts/host-contract-client.js +396 -0
- package/dist/contracts/host-contract-transport.d.ts +2 -0
- package/dist/contracts/host-contract-transport.js +8 -0
- package/dist/contracts/index.d.ts +6 -4
- package/dist/contracts/index.js +11 -5
- package/dist/contracts/registry.d.ts +31 -1
- package/dist/contracts/registry.js +100 -6
- package/dist/contracts/system/contract-catalog-contract.d.ts +61 -6
- package/dist/contracts/system/contract-catalog-contract.js +17 -5
- package/dist/contracts/tool-catalog-service.js +26 -6
- package/dist/contracts/tool-catalog.d.ts +1 -1
- package/dist/contracts/tool-catalog.js +4 -1
- package/dist/contracts/types.d.ts +32 -2
- package/dist/contracts/validate.d.ts +16 -1
- package/dist/contracts/validate.js +139 -25
- package/dist/data/change-tracking.d.ts +17 -5
- package/dist/data/change-tracking.js +22 -6
- package/dist/data/change-tracking.test.d.ts +1 -0
- package/dist/data/change-tracking.test.js +62 -0
- package/dist/data/group-members.d.ts +6 -1
- package/dist/data/group-members.js +12 -4
- package/dist/data/groups.d.ts +6 -1
- package/dist/data/groups.js +12 -4
- package/dist/data/orm/sql.data-source.d.ts +9 -0
- package/dist/data/orm/sql.data-source.js +47 -2
- package/dist/data/orm/sql.data-source.test.js +142 -0
- package/dist/data/orm/table-container.js +9 -9
- package/dist/data/orm/table-container.test.js +44 -0
- package/dist/data/orm/types.d.ts +41 -1
- package/dist/data/orm/types.js +159 -28
- package/dist/data/orm/types.test.js +129 -0
- package/dist/data/package-versions.d.ts +5 -1
- package/dist/data/package-versions.js +11 -4
- package/dist/data/packages.d.ts +7 -1
- package/dist/data/packages.js +13 -4
- package/dist/data/peer-types.d.ts +8 -62
- package/dist/data/persistent-vars.d.ts +4 -1
- package/dist/data/persistent-vars.js +7 -2
- package/dist/data/table-signing.test.d.ts +1 -0
- package/dist/data/table-signing.test.js +118 -0
- package/dist/data/tools.d.ts +29 -221
- package/dist/data/users.d.ts +1 -2
- package/dist/data/users.js +8 -5
- package/dist/define/also-implements.test.d.ts +1 -0
- package/dist/define/also-implements.test.js +123 -0
- package/dist/define/context.d.ts +77 -0
- package/dist/define/context.js +2 -0
- package/dist/define/contract-ref.d.ts +177 -0
- package/dist/define/contract-ref.js +67 -0
- package/dist/define/declare-contract.d.ts +167 -0
- package/dist/define/declare-contract.js +220 -0
- package/dist/define/declare-contract.test.d.ts +1 -0
- package/dist/define/declare-contract.test.js +573 -0
- package/dist/define/define-package.d.ts +203 -0
- package/dist/define/define-package.js +289 -0
- package/dist/define/define.test.d.ts +1 -0
- package/dist/define/define.test.js +1010 -0
- package/dist/define/empty-fields.d.ts +15 -0
- package/dist/define/empty-fields.js +134 -0
- package/dist/define/empty-fields.test.d.ts +1 -0
- package/dist/define/empty-fields.test.js +55 -0
- package/dist/define/events-observables.test.d.ts +1 -0
- package/dist/define/events-observables.test.js +1013 -0
- package/dist/define/events.d.ts +81 -0
- package/dist/define/events.js +81 -0
- package/dist/define/generated-field.d.ts +20 -0
- package/dist/define/generated-field.js +25 -0
- package/dist/define/index.d.ts +28 -0
- package/dist/define/index.js +62 -0
- package/dist/define/lazy-members.d.ts +8 -0
- package/dist/define/lazy-members.js +38 -0
- package/dist/define/manifest.d.ts +63 -0
- package/dist/define/manifest.js +653 -0
- package/dist/define/observables.d.ts +146 -0
- package/dist/define/observables.js +187 -0
- package/dist/define/serve-members.d.ts +21 -0
- package/dist/define/serve-members.js +152 -0
- package/dist/define/serve-members.test.d.ts +1 -0
- package/dist/define/serve-members.test.js +630 -0
- package/dist/define/serve.d.ts +22 -0
- package/dist/define/serve.js +58 -0
- package/dist/define/system-contracts.d.ts +41 -0
- package/dist/define/system-contracts.js +21 -0
- package/dist/define/table-handle.d.ts +13 -0
- package/dist/define/table-handle.js +36 -0
- package/dist/define/table.d.ts +128 -0
- package/dist/define/table.js +122 -0
- package/dist/define/tool.d.ts +129 -0
- package/dist/define/tool.js +91 -0
- package/dist/define/vars.d.ts +82 -0
- package/dist/define/vars.js +256 -0
- package/dist/define/vars.test.d.ts +1 -0
- package/dist/define/vars.test.js +39 -0
- package/dist/define/wire-value.d.ts +12 -0
- package/dist/define/wire-value.js +76 -0
- package/dist/define/worker-state.d.ts +15 -0
- package/dist/define/worker-state.js +45 -0
- package/dist/device/tx-encoding.test.js +10 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +5 -1
- package/dist/observable.d.ts +42 -2
- package/dist/observable.js +25 -7
- package/dist/package-build/build-package.d.ts +60 -0
- package/dist/package-build/build-package.js +312 -0
- package/dist/package-build/build-package.test.d.ts +1 -0
- package/dist/package-build/build-package.test.js +204 -0
- package/dist/package-build/cli.d.ts +7 -0
- package/dist/package-build/cli.js +218 -0
- package/dist/package-build/contract-add.d.ts +35 -0
- package/dist/package-build/contract-add.js +97 -0
- package/dist/package-build/contract-add.test.d.ts +1 -0
- package/dist/package-build/contract-add.test.js +467 -0
- package/dist/package-build/contract-source.d.ts +59 -0
- package/dist/package-build/contract-source.js +471 -0
- package/dist/package-build/freeze-contracts.d.ts +56 -0
- package/dist/package-build/freeze-contracts.js +198 -0
- package/dist/package-build/freeze-contracts.test.d.ts +1 -0
- package/dist/package-build/freeze-contracts.test.js +355 -0
- package/dist/package-installer/package-creator.d.ts +4 -0
- package/dist/package-installer/package-creator.js +57 -16
- package/dist/package-installer/package-creator.test.js +86 -0
- package/dist/package-loader/contract-members.d.ts +53 -0
- package/dist/package-loader/contract-members.js +31 -0
- package/dist/package-loader/contract-package-loader.js +5 -1
- package/dist/package-loader/contract-package-loader.test.js +11 -0
- package/dist/package-loader/host-authority-loading.test.js +9 -5
- package/dist/package-loader/index.d.ts +1 -0
- package/dist/package-loader/index.js +1 -0
- package/dist/package-loader/isolated-artifact.d.ts +80 -17
- package/dist/package-loader/isolated-artifact.js +257 -51
- package/dist/package-loader/isolated-artifact.test.js +294 -21
- package/dist/package-loader/isolated-runtime.d.ts +22 -7
- package/dist/package-loader/isolated-table-gateway.js +30 -11
- package/dist/package-loader/isolated-table-gateway.test.js +243 -0
- package/dist/package-loader/package-loader.d.ts +8 -1
- package/dist/package-loader/package-loader.js +39 -20
- package/dist/package-loader/package-loader.test.js +47 -0
- package/dist/peer-id.d.ts +25 -0
- package/dist/peer-id.js +99 -0
- package/dist/serial-json.compat.test.d.ts +1 -0
- package/dist/serial-json.compat.test.js +369 -0
- package/dist/serial-json.d.ts +41 -5
- package/dist/serial-json.js +216 -157
- package/dist/serial-json.oracle.d.ts +21 -0
- package/dist/serial-json.oracle.js +222 -0
- package/dist/serial-json.test.js +315 -0
- package/dist/testing/index.d.ts +4 -0
- package/dist/testing/index.js +8 -0
- package/dist/testing/mock-host-contract.d.ts +123 -0
- package/dist/testing/mock-host-contract.js +434 -0
- package/dist/testing/mock-host-contract.test.d.ts +1 -0
- package/dist/testing/mock-host-contract.test.js +782 -0
- package/dist/types/field-type.d.ts +35 -18
- package/dist/types/field-type.js +4 -1
- package/dist/types/zod-types.d.ts +9 -0
- package/dist/types/zod-types.js +8 -2
- package/dist/utils.d.ts +1 -25
- package/dist/utils.js +10 -90
- package/package.json +9 -2
- package/testing/package.json +4 -0
package/dist/observable.js
CHANGED
|
@@ -5,10 +5,19 @@ exports.computed = computed;
|
|
|
5
5
|
exports.isSubscribable = isSubscribable;
|
|
6
6
|
exports.unwrapObservable = unwrapObservable;
|
|
7
7
|
exports.linkObservables = linkObservables;
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
/**
|
|
9
|
+
* Observables: a value you call to read (`obs()`) or write (`obs(value)`), with subscribers
|
|
10
|
+
* notified on each change. Persistent variables and computed values build on them.
|
|
11
|
+
*
|
|
12
|
+
* This module has no dependency beyond peer ids, so isolated package bundles can use it.
|
|
13
|
+
*/
|
|
14
|
+
const peer_id_1 = require("./peer-id");
|
|
11
15
|
const runningComputes = {};
|
|
16
|
+
/**
|
|
17
|
+
* Create an observable, starting at `initialValue`.
|
|
18
|
+
*
|
|
19
|
+
* @param initialValue - The starting value.
|
|
20
|
+
*/
|
|
12
21
|
function observable(initialValue) {
|
|
13
22
|
let value = initialValue;
|
|
14
23
|
const subscribers = [];
|
|
@@ -46,12 +55,18 @@ function observable(initialValue) {
|
|
|
46
55
|
obs.subscriberCount = () => subscribers.length;
|
|
47
56
|
return obs;
|
|
48
57
|
}
|
|
58
|
+
/**
|
|
59
|
+
* An observable derived from other observables: `read` runs again when any observable it read
|
|
60
|
+
* changes. Pass `{ read, write }` to make it writable.
|
|
61
|
+
*
|
|
62
|
+
* @param fn - The read function, or read and write functions.
|
|
63
|
+
*/
|
|
49
64
|
function computed(fn) {
|
|
50
|
-
const read =
|
|
51
|
-
const write =
|
|
65
|
+
const read = typeof fn === "function" ? fn : fn.read;
|
|
66
|
+
const write = typeof fn === "function" ? undefined : fn.write;
|
|
52
67
|
// compute dependencies and initial value;
|
|
53
68
|
const dependencies = [];
|
|
54
|
-
const runId = (0,
|
|
69
|
+
const runId = (0, peer_id_1.newid)();
|
|
55
70
|
runningComputes[runId] = dependencies;
|
|
56
71
|
const _obs = observable(read());
|
|
57
72
|
delete runningComputes[runId];
|
|
@@ -84,18 +99,21 @@ function computed(fn) {
|
|
|
84
99
|
}
|
|
85
100
|
};
|
|
86
101
|
// subscribe to dependencies
|
|
87
|
-
|
|
102
|
+
[...new Set(dependencies)].forEach((dep) => {
|
|
88
103
|
dep.subscribe(onDepChange);
|
|
89
104
|
});
|
|
90
105
|
Object.assign(obs, _obs);
|
|
91
106
|
return obs;
|
|
92
107
|
}
|
|
108
|
+
/** True when `obj` is an observable. */
|
|
93
109
|
function isSubscribable(obj) {
|
|
94
110
|
return obj && typeof obj === "function" && obj.subscribe && obj.notifySubscribers;
|
|
95
111
|
}
|
|
112
|
+
/** The observable's current value, or `obj` itself when it isn't an observable. */
|
|
96
113
|
function unwrapObservable(obj) {
|
|
97
114
|
return isSubscribable(obj) ? obj() : obj;
|
|
98
115
|
}
|
|
116
|
+
/** Keep two observables equal: a change to either is written to the other. */
|
|
99
117
|
function linkObservables(obs1, obs2) {
|
|
100
118
|
const sub1 = obs1.subscribe((value) => {
|
|
101
119
|
if (value !== obs2()) {
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { type IIsolatedPackageManifest } from "../package-loader/isolated-artifact";
|
|
2
|
+
/** Where the package is and where its build goes. */
|
|
3
|
+
export interface IPackageBuildOptions {
|
|
4
|
+
/** The package's root, holding its `package.json`. Defaults to the current directory. */
|
|
5
|
+
root?: string;
|
|
6
|
+
/** The declaration module, relative to `root`. Defaults to `src/package.ts`. */
|
|
7
|
+
entry?: string;
|
|
8
|
+
/** The output directory, relative to `root`. Defaults to `dist`. */
|
|
9
|
+
outDir?: string;
|
|
10
|
+
}
|
|
11
|
+
/** What {@link buildPackage} wrote. */
|
|
12
|
+
export interface IPackageBuildResult {
|
|
13
|
+
manifest: IIsolatedPackageManifest;
|
|
14
|
+
manifestPath: string;
|
|
15
|
+
artifactPath: string;
|
|
16
|
+
/** Size of the bundled provider source, in characters. */
|
|
17
|
+
providerChars: number;
|
|
18
|
+
}
|
|
19
|
+
/** A running package watcher. Dispose it to stop watching and release esbuild. */
|
|
20
|
+
export interface IPackageWatcher {
|
|
21
|
+
dispose(): Promise<void>;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Bundle a TypeScript or JavaScript module for Node, the way the build loads `src/package.ts`,
|
|
25
|
+
* and return its exports. Its installed imports resolve from the package at `root`.
|
|
26
|
+
*
|
|
27
|
+
* @param root - The package root, whose esbuild compiles the module.
|
|
28
|
+
* @param entry - The module's path.
|
|
29
|
+
*/
|
|
30
|
+
export declare function loadPackageModule(root: string, entry: string): Promise<unknown>;
|
|
31
|
+
/**
|
|
32
|
+
* The generate-manifest step: write `dist/manifest.json` from the package's declarations.
|
|
33
|
+
*
|
|
34
|
+
* @param buildOptions - Package root, declaration entry, and output directory.
|
|
35
|
+
*/
|
|
36
|
+
export declare function generatePackageManifest(buildOptions?: IPackageBuildOptions): Promise<{
|
|
37
|
+
manifest: IIsolatedPackageManifest;
|
|
38
|
+
manifestPath: string;
|
|
39
|
+
}>;
|
|
40
|
+
/**
|
|
41
|
+
* Build the package's manifest from its declarations, validated, without writing it or checking
|
|
42
|
+
* it against `frozen-contracts.json` (`peers-package contract freeze` does that itself).
|
|
43
|
+
*
|
|
44
|
+
* @param buildOptions - Package root and declaration entry.
|
|
45
|
+
*/
|
|
46
|
+
export declare function packageManifest(buildOptions?: IPackageBuildOptions): Promise<IIsolatedPackageManifest>;
|
|
47
|
+
/**
|
|
48
|
+
* Generate the manifest, bundle the provider for the sandbox, and write `dist/package.bundle.js`.
|
|
49
|
+
*
|
|
50
|
+
* @param buildOptions - Package root, declaration entry, and output directory.
|
|
51
|
+
*/
|
|
52
|
+
export declare function buildPackage(buildOptions?: IPackageBuildOptions): Promise<IPackageBuildResult>;
|
|
53
|
+
/**
|
|
54
|
+
* Build once, then rebuild whenever a source file changes. Resolves once watching starts.
|
|
55
|
+
*
|
|
56
|
+
* @param buildOptions - Package root, declaration entry, and output directory.
|
|
57
|
+
* @param onBuild - Called after each build, with its result or error.
|
|
58
|
+
* @returns A handle that stops watching when disposed.
|
|
59
|
+
*/
|
|
60
|
+
export declare function watchPackage(buildOptions?: IPackageBuildOptions, onBuild?: (result: IPackageBuildResult | Error) => void): Promise<IPackageWatcher>;
|
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.loadPackageModule = loadPackageModule;
|
|
4
|
+
exports.generatePackageManifest = generatePackageManifest;
|
|
5
|
+
exports.packageManifest = packageManifest;
|
|
6
|
+
exports.buildPackage = buildPackage;
|
|
7
|
+
exports.watchPackage = watchPackage;
|
|
8
|
+
/**
|
|
9
|
+
* `peers-package`: build an isolated package from its declarations (Node only).
|
|
10
|
+
*
|
|
11
|
+
* `manifest` is the generate-manifest step: it loads `src/package.ts` in Node and writes the
|
|
12
|
+
* manifest, plain JSON in Peers IR, to `dist/manifest.json`. `build` also bundles the same
|
|
13
|
+
* `src/package.ts` for the sandbox and writes `dist/package.bundle.js`, the artifact the
|
|
14
|
+
* Runtime installs. esbuild comes from the package's own dependencies.
|
|
15
|
+
*/
|
|
16
|
+
const promises_1 = require("node:fs/promises");
|
|
17
|
+
const node_module_1 = require("node:module");
|
|
18
|
+
const path = require("node:path");
|
|
19
|
+
const manifest_1 = require("../define/manifest");
|
|
20
|
+
const isolated_artifact_1 = require("../package-loader/isolated-artifact");
|
|
21
|
+
const freeze_contracts_1 = require("./freeze-contracts");
|
|
22
|
+
/** Source text SES rejects before running it, with what to tell the author. */
|
|
23
|
+
const SES_REJECTED_PATTERNS = [
|
|
24
|
+
{ pattern: /(?:<!--|-->)/, what: "an HTML comment token (<!-- or -->)" },
|
|
25
|
+
{
|
|
26
|
+
pattern: /(^|[^.]|\.\.\.)\bimport(\s*(?:\(|\/[/*]))/,
|
|
27
|
+
what: "import( or import followed by a comment",
|
|
28
|
+
},
|
|
29
|
+
{ pattern: /(^|[^.])\beval(\s*\()/, what: "a direct eval(...) call" },
|
|
30
|
+
];
|
|
31
|
+
const NODE_BUILTINS = [...node_module_1.builtinModules, ...node_module_1.builtinModules.map((name) => `node:${name}`)];
|
|
32
|
+
function resolveOptions(options) {
|
|
33
|
+
const root = path.resolve(options.root ?? process.cwd());
|
|
34
|
+
const packageRequire = (0, node_module_1.createRequire)(path.join(root, "package.json"));
|
|
35
|
+
let esbuild;
|
|
36
|
+
try {
|
|
37
|
+
esbuild = packageRequire("esbuild");
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
throw new Error(`peers-package needs esbuild in ${root}. Install it: npm install --save-dev esbuild`);
|
|
41
|
+
}
|
|
42
|
+
return {
|
|
43
|
+
root,
|
|
44
|
+
entry: path.resolve(root, options.entry ?? "src/package.ts"),
|
|
45
|
+
outDir: path.resolve(root, options.outDir ?? "dist"),
|
|
46
|
+
esbuild,
|
|
47
|
+
require: packageRequire,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
/** Node compilation used to load the declaration without bundling its installed libraries. */
|
|
51
|
+
function definitionBuildOptions(entry, outfile) {
|
|
52
|
+
return {
|
|
53
|
+
entryPoints: [entry],
|
|
54
|
+
outfile,
|
|
55
|
+
bundle: true,
|
|
56
|
+
platform: "node",
|
|
57
|
+
format: "cjs",
|
|
58
|
+
target: "node20",
|
|
59
|
+
packages: "external",
|
|
60
|
+
logLevel: "silent",
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
/** Bundle the declaration module for Node and load its default export. */
|
|
64
|
+
async function loadDefinition(options) {
|
|
65
|
+
const loaded = (await loadModule(options, options.entry));
|
|
66
|
+
const definition = loaded.default;
|
|
67
|
+
if (definition?.kind !== "package") {
|
|
68
|
+
throw new Error(`${path.relative(options.root, options.entry)} must export default definePackage(...)`);
|
|
69
|
+
}
|
|
70
|
+
return definition;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Bundle a TypeScript or JavaScript module for Node, the way the build loads `src/package.ts`,
|
|
74
|
+
* and return its exports. Its installed imports resolve from the package at `root`.
|
|
75
|
+
*
|
|
76
|
+
* @param root - The package root, whose esbuild compiles the module.
|
|
77
|
+
* @param entry - The module's path.
|
|
78
|
+
*/
|
|
79
|
+
async function loadPackageModule(root, entry) {
|
|
80
|
+
const options = resolveOptions({ root });
|
|
81
|
+
return loadModule(options, path.resolve(entry));
|
|
82
|
+
}
|
|
83
|
+
/** Bundle one module for Node and load it, then forget it and its installed imports. */
|
|
84
|
+
async function loadModule(options, entry) {
|
|
85
|
+
// External imports must resolve from the package, even when --out-dir is elsewhere, so the
|
|
86
|
+
// compiled declaration goes under the package's node_modules/.cache: git and npm ignore it,
|
|
87
|
+
// so a build killed before cleanup (Ctrl-C while watching) leaves nothing in the source tree.
|
|
88
|
+
// Use a unique directory so concurrent builds cannot overwrite or remove each other's code.
|
|
89
|
+
const cacheDir = path.join(options.root, "node_modules", ".cache", "peers-package");
|
|
90
|
+
await (0, promises_1.mkdir)(cacheDir, { recursive: true });
|
|
91
|
+
const temporaryDir = await (0, promises_1.mkdtemp)(path.join(cacheDir, "definition-"));
|
|
92
|
+
const outfile = path.join(temporaryDir, "definition.cjs");
|
|
93
|
+
const definitionRequire = (0, node_module_1.createRequire)(outfile);
|
|
94
|
+
let cacheKey;
|
|
95
|
+
let previousModules;
|
|
96
|
+
try {
|
|
97
|
+
await options.esbuild.build(definitionBuildOptions(entry, outfile));
|
|
98
|
+
cacheKey = definitionRequire.resolve(outfile);
|
|
99
|
+
previousModules = new Set(Object.keys(definitionRequire.cache));
|
|
100
|
+
return definitionRequire(outfile);
|
|
101
|
+
}
|
|
102
|
+
finally {
|
|
103
|
+
const previous = previousModules;
|
|
104
|
+
if (previous) {
|
|
105
|
+
// The entry is bundled, but its installed dependencies are external. A watch rebuild
|
|
106
|
+
// must reload those too, including dependencies loaded before an evaluation failed.
|
|
107
|
+
const cache = definitionRequire.cache;
|
|
108
|
+
const pending = Object.keys(cache).filter((key) => !previous.has(key));
|
|
109
|
+
if (cacheKey)
|
|
110
|
+
pending.push(cacheKey);
|
|
111
|
+
const seen = new Set();
|
|
112
|
+
while (pending.length) {
|
|
113
|
+
const key = pending.pop();
|
|
114
|
+
if (seen.has(key))
|
|
115
|
+
continue;
|
|
116
|
+
seen.add(key);
|
|
117
|
+
for (const child of cache[key]?.children ?? [])
|
|
118
|
+
pending.push(child.id);
|
|
119
|
+
delete cache[key];
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
await (0, promises_1.rm)(temporaryDir, { recursive: true, force: true });
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Check a manifest without changing it: validation fills in defaults on what it's given. Strict,
|
|
127
|
+
* so a field key this SDK doesn't know (a typo in hand-written IR) fails the build.
|
|
128
|
+
*/
|
|
129
|
+
function validateManifest(manifest, providerSource = "void 0;") {
|
|
130
|
+
(0, isolated_artifact_1.validateIsolatedPackageArtifact)({ manifest: structuredClone(manifest), providerSource }, { strict: true });
|
|
131
|
+
}
|
|
132
|
+
/** The package's manifest from its declarations, validated. */
|
|
133
|
+
async function validManifest(options) {
|
|
134
|
+
// frozen-contracts.json gives the shapes of older versions a contract also implements.
|
|
135
|
+
const manifest = (0, manifest_1.buildPackageManifest)(await loadDefinition(options), {
|
|
136
|
+
frozen: await (0, freeze_contracts_1.readFrozenContracts)(options.root),
|
|
137
|
+
});
|
|
138
|
+
validateManifest(manifest);
|
|
139
|
+
return manifest;
|
|
140
|
+
}
|
|
141
|
+
/** The package's manifest, validated and checked against the contracts it froze. */
|
|
142
|
+
async function checkedManifest(options) {
|
|
143
|
+
const manifest = await validManifest(options);
|
|
144
|
+
await (0, freeze_contracts_1.assertFrozenContracts)(options.root, manifest);
|
|
145
|
+
return manifest;
|
|
146
|
+
}
|
|
147
|
+
async function writeManifest(options) {
|
|
148
|
+
return saveManifest(options, await checkedManifest(options));
|
|
149
|
+
}
|
|
150
|
+
async function saveManifest(options, manifest) {
|
|
151
|
+
const manifestPath = path.join(options.outDir, "manifest.json");
|
|
152
|
+
await (0, promises_1.mkdir)(options.outDir, { recursive: true });
|
|
153
|
+
await (0, promises_1.writeFile)(manifestPath, `${JSON.stringify(manifest, null, 2)}\n`, "utf8");
|
|
154
|
+
return { manifest, manifestPath };
|
|
155
|
+
}
|
|
156
|
+
/** esbuild options for the provider bundle: the declaration plus `servePackage`. */
|
|
157
|
+
function providerBuildOptions(options, manifest, plugins = []) {
|
|
158
|
+
const sandbox = manifest.executionMode !== "privileged";
|
|
159
|
+
const sdkRoot = path.dirname(options.require.resolve("@peers-app/peers-sdk/package.json"));
|
|
160
|
+
const zodPath = (0, node_module_1.createRequire)(path.join(sdkRoot, "package.json")).resolve("zod");
|
|
161
|
+
const singleZod = {
|
|
162
|
+
name: "peers-package-single-zod",
|
|
163
|
+
setup(build) {
|
|
164
|
+
// One zod for the package and the SDK, so schemas from both mix.
|
|
165
|
+
build.onResolve({ filter: /^zod$/ }, () => ({ path: zodPath }));
|
|
166
|
+
},
|
|
167
|
+
};
|
|
168
|
+
return {
|
|
169
|
+
stdin: {
|
|
170
|
+
contents: [
|
|
171
|
+
`import definition from ${JSON.stringify(options.entry)};`,
|
|
172
|
+
`import { servePackage } from "@peers-app/peers-sdk/define";`,
|
|
173
|
+
"servePackage(definition);",
|
|
174
|
+
].join("\n"),
|
|
175
|
+
resolveDir: options.root,
|
|
176
|
+
sourcefile: "peers-package-provider.js",
|
|
177
|
+
loader: "js",
|
|
178
|
+
},
|
|
179
|
+
bundle: true,
|
|
180
|
+
write: false,
|
|
181
|
+
format: "iife",
|
|
182
|
+
target: "es2022",
|
|
183
|
+
// The sandbox is a worker with web globals and no Node builtins. A privileged provider runs
|
|
184
|
+
// in Node with `require`.
|
|
185
|
+
platform: sandbox ? "browser" : "node",
|
|
186
|
+
// Leave Node builtins to libraries' own guarded `require`s; the sandbox has none.
|
|
187
|
+
external: sandbox ? NODE_BUILTINS : [],
|
|
188
|
+
define: { "process.env.NODE_ENV": '"production"' },
|
|
189
|
+
// Also strips comments: SES rejects some text even inside a comment.
|
|
190
|
+
minifyWhitespace: true,
|
|
191
|
+
legalComments: "none",
|
|
192
|
+
logLevel: "silent",
|
|
193
|
+
plugins: [singleZod, ...plugins],
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
function assertSesCompatible(source, manifest) {
|
|
197
|
+
if (manifest.executionMode === "privileged") {
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
for (const { pattern, what } of SES_REJECTED_PATTERNS) {
|
|
201
|
+
const match = pattern.exec(source);
|
|
202
|
+
if (match) {
|
|
203
|
+
const at = match.index + match[0].length;
|
|
204
|
+
const snippet = source.slice(Math.max(0, at - 60), at + 20).replace(/\s+/g, " ");
|
|
205
|
+
throw new Error(`The provider bundle contains ${what}, which the sandbox rejects even inside a string. Near: …${snippet}…`);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
async function writeArtifact(options, manifest, providerSource) {
|
|
210
|
+
assertSesCompatible(providerSource, manifest);
|
|
211
|
+
validateManifest(manifest, providerSource);
|
|
212
|
+
const artifactPath = path.join(options.outDir, "package.bundle.js");
|
|
213
|
+
await (0, promises_1.mkdir)(options.outDir, { recursive: true });
|
|
214
|
+
await (0, promises_1.writeFile)(artifactPath, `${isolated_artifact_1.ISOLATED_PACKAGE_MAGIC}\n${JSON.stringify({ manifest, providerSource })}\n`, "utf8");
|
|
215
|
+
return artifactPath;
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* The generate-manifest step: write `dist/manifest.json` from the package's declarations.
|
|
219
|
+
*
|
|
220
|
+
* @param buildOptions - Package root, declaration entry, and output directory.
|
|
221
|
+
*/
|
|
222
|
+
async function generatePackageManifest(buildOptions = {}) {
|
|
223
|
+
return writeManifest(resolveOptions(buildOptions));
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* Build the package's manifest from its declarations, validated, without writing it or checking
|
|
227
|
+
* it against `frozen-contracts.json` (`peers-package contract freeze` does that itself).
|
|
228
|
+
*
|
|
229
|
+
* @param buildOptions - Package root and declaration entry.
|
|
230
|
+
*/
|
|
231
|
+
async function packageManifest(buildOptions = {}) {
|
|
232
|
+
return validManifest(resolveOptions(buildOptions));
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* Generate the manifest, bundle the provider for the sandbox, and write `dist/package.bundle.js`.
|
|
236
|
+
*
|
|
237
|
+
* @param buildOptions - Package root, declaration entry, and output directory.
|
|
238
|
+
*/
|
|
239
|
+
async function buildPackage(buildOptions = {}) {
|
|
240
|
+
const options = resolveOptions(buildOptions);
|
|
241
|
+
const manifest = await checkedManifest(options);
|
|
242
|
+
const output = await options.esbuild.build(providerBuildOptions(options, manifest));
|
|
243
|
+
const providerSource = output.outputFiles?.[0]?.text ?? "";
|
|
244
|
+
const artifactPath = await writeArtifact(options, manifest, providerSource);
|
|
245
|
+
const { manifestPath } = await saveManifest(options, manifest);
|
|
246
|
+
return { manifest, manifestPath, artifactPath, providerChars: providerSource.length };
|
|
247
|
+
}
|
|
248
|
+
/**
|
|
249
|
+
* Build once, then rebuild whenever a source file changes. Resolves once watching starts.
|
|
250
|
+
*
|
|
251
|
+
* @param buildOptions - Package root, declaration entry, and output directory.
|
|
252
|
+
* @param onBuild - Called after each build, with its result or error.
|
|
253
|
+
* @returns A handle that stops watching when disposed.
|
|
254
|
+
*/
|
|
255
|
+
async function watchPackage(buildOptions = {}, onBuild = () => undefined) {
|
|
256
|
+
const options = resolveOptions(buildOptions);
|
|
257
|
+
const manifest = await validManifest(options);
|
|
258
|
+
let mode = manifest.executionMode ?? "sandbox";
|
|
259
|
+
let disposed = false;
|
|
260
|
+
let restart = Promise.resolve();
|
|
261
|
+
let context;
|
|
262
|
+
const rebuild = {
|
|
263
|
+
name: "peers-package-write",
|
|
264
|
+
setup(build) {
|
|
265
|
+
build.onEnd(async () => {
|
|
266
|
+
if (disposed)
|
|
267
|
+
return;
|
|
268
|
+
try {
|
|
269
|
+
// Recreate the provider's options from the current declaration on every rebuild:
|
|
270
|
+
// executionMode can change which platform and dependency exports must be bundled.
|
|
271
|
+
const latest = await buildPackage(buildOptions);
|
|
272
|
+
onBuild(latest);
|
|
273
|
+
const nextMode = latest.manifest.executionMode ?? "sandbox";
|
|
274
|
+
if (nextMode !== mode) {
|
|
275
|
+
mode = nextMode;
|
|
276
|
+
// Dispose after this onEnd returns: awaiting disposal here would deadlock it.
|
|
277
|
+
// Recreate the watch graph too, so browser/node conditional dependencies are watched.
|
|
278
|
+
restart = restart
|
|
279
|
+
.then(async () => {
|
|
280
|
+
await context.dispose();
|
|
281
|
+
if (disposed)
|
|
282
|
+
return;
|
|
283
|
+
context = await options.esbuild.context(providerBuildOptions(options, latest.manifest, [rebuild]));
|
|
284
|
+
await context.watch();
|
|
285
|
+
})
|
|
286
|
+
.catch((error) => {
|
|
287
|
+
onBuild(error instanceof Error ? error : new Error(String(error)));
|
|
288
|
+
});
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
catch (error) {
|
|
292
|
+
onBuild(error instanceof Error ? error : new Error(String(error)));
|
|
293
|
+
}
|
|
294
|
+
});
|
|
295
|
+
},
|
|
296
|
+
};
|
|
297
|
+
context = await options.esbuild.context(providerBuildOptions(options, manifest, [rebuild]));
|
|
298
|
+
try {
|
|
299
|
+
await context.watch();
|
|
300
|
+
return {
|
|
301
|
+
async dispose() {
|
|
302
|
+
disposed = true;
|
|
303
|
+
await restart;
|
|
304
|
+
await context.dispose();
|
|
305
|
+
},
|
|
306
|
+
};
|
|
307
|
+
}
|
|
308
|
+
catch (error) {
|
|
309
|
+
await context.dispose();
|
|
310
|
+
throw error;
|
|
311
|
+
}
|
|
312
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const node_child_process_1 = require("node:child_process");
|
|
4
|
+
const node_crypto_1 = require("node:crypto");
|
|
5
|
+
const promises_1 = require("node:fs/promises");
|
|
6
|
+
const node_os_1 = require("node:os");
|
|
7
|
+
const path = require("node:path");
|
|
8
|
+
const node_vm_1 = require("node:vm");
|
|
9
|
+
const isolated_artifact_1 = require("../package-loader/isolated-artifact");
|
|
10
|
+
const peer_id_1 = require("../peer-id");
|
|
11
|
+
const build_package_1 = require("./build-package");
|
|
12
|
+
const sdkRoot = path.resolve(__dirname, "../..");
|
|
13
|
+
const packageId = (0, peer_id_1.newid)();
|
|
14
|
+
const contractId = (0, peer_id_1.newid)();
|
|
15
|
+
let root;
|
|
16
|
+
let watcher;
|
|
17
|
+
function declaration(mode = "sandbox", version = "1.0.0", bad = false) {
|
|
18
|
+
return `
|
|
19
|
+
import { defineContract, definePackage, defineTool, z } from "@peers-app/peers-sdk/define";
|
|
20
|
+
import { side } from "platform-probe";
|
|
21
|
+
// <!-- Comments that SES rejects must be stripped from the provider.
|
|
22
|
+
export default definePackage((pkg) => {
|
|
23
|
+
pkg.packageId = ${JSON.stringify(packageId)};
|
|
24
|
+
pkg.version = ${JSON.stringify(version)};
|
|
25
|
+
pkg.executionMode = ${JSON.stringify(mode)};
|
|
26
|
+
pkg.provides(defineContract({
|
|
27
|
+
contractId: ${JSON.stringify(contractId)}, version: 1, name: "Probe",
|
|
28
|
+
tools: [defineTool({
|
|
29
|
+
name: "probe", usageDescription: "Probe the bundler platform.", accessLevel: 0,
|
|
30
|
+
inputSchema: z.object({}), outputSchema: z.object({ side: z.string() }),
|
|
31
|
+
handler: () => ({ side: ${bad ? '"<!--"' : "side"} }),
|
|
32
|
+
})],
|
|
33
|
+
}));
|
|
34
|
+
});
|
|
35
|
+
`;
|
|
36
|
+
}
|
|
37
|
+
beforeEach(async () => {
|
|
38
|
+
root = await (0, promises_1.mkdtemp)(path.join((0, node_os_1.tmpdir)(), "peers-package-test-"));
|
|
39
|
+
await (0, promises_1.mkdir)(path.join(root, "src"));
|
|
40
|
+
await (0, promises_1.mkdir)(path.join(root, "node_modules/@peers-app"), { recursive: true });
|
|
41
|
+
await (0, promises_1.symlink)(sdkRoot, path.join(root, "node_modules/@peers-app/peers-sdk"), "dir");
|
|
42
|
+
await (0, promises_1.symlink)(path.dirname(require.resolve("esbuild/package.json")), path.join(root, "node_modules/esbuild"), "dir");
|
|
43
|
+
const probe = path.join(root, "node_modules/platform-probe");
|
|
44
|
+
await (0, promises_1.mkdir)(probe);
|
|
45
|
+
await (0, promises_1.writeFile)(path.join(root, "package.json"), '{"name":"build-probe","version":"1.0.0"}');
|
|
46
|
+
await (0, promises_1.writeFile)(path.join(probe, "package.json"), JSON.stringify({
|
|
47
|
+
name: "platform-probe",
|
|
48
|
+
exports: { browser: "./browser.js", default: "./node.js" },
|
|
49
|
+
}));
|
|
50
|
+
await (0, promises_1.writeFile)(path.join(probe, "browser.js"), 'exports.side = "browser";');
|
|
51
|
+
await (0, promises_1.writeFile)(path.join(probe, "node.js"), 'exports.side = "node";');
|
|
52
|
+
await (0, promises_1.writeFile)(path.join(root, "src/package.ts"), declaration());
|
|
53
|
+
});
|
|
54
|
+
afterEach(async () => {
|
|
55
|
+
await watcher?.dispose();
|
|
56
|
+
watcher = undefined;
|
|
57
|
+
await (0, promises_1.rm)(root, { recursive: true, force: true });
|
|
58
|
+
});
|
|
59
|
+
async function probeArtifact() {
|
|
60
|
+
const artifact = (0, isolated_artifact_1.parseIsolatedPackageArtifact)(await (0, promises_1.readFile)(path.join(root, "dist/package.bundle.js"), "utf8"));
|
|
61
|
+
const handlers = [];
|
|
62
|
+
const sandbox = {
|
|
63
|
+
registerContractTool: (_id, _version, _name, handler) => handlers.push(handler),
|
|
64
|
+
registerHandler: () => undefined,
|
|
65
|
+
onWorkerStart: () => undefined,
|
|
66
|
+
};
|
|
67
|
+
try {
|
|
68
|
+
(0, node_vm_1.runInNewContext)(artifact.providerSource, { ...sandbox, self: { crypto: node_crypto_1.webcrypto }, require });
|
|
69
|
+
}
|
|
70
|
+
catch (error) {
|
|
71
|
+
throw new Error(String(error));
|
|
72
|
+
}
|
|
73
|
+
return { artifact, result: await handlers[0]({}, {}) };
|
|
74
|
+
}
|
|
75
|
+
test("the CLI generates a validated manifest with custom paths and no provider artifact", async () => {
|
|
76
|
+
(0, node_child_process_1.execFileSync)(process.execPath, [
|
|
77
|
+
path.join(sdkRoot, "bin/peers-package.js"),
|
|
78
|
+
"manifest",
|
|
79
|
+
"--root",
|
|
80
|
+
root,
|
|
81
|
+
"--out-dir",
|
|
82
|
+
"generated",
|
|
83
|
+
], { encoding: "utf8" });
|
|
84
|
+
const manifest = JSON.parse(await (0, promises_1.readFile)(path.join(root, "generated/manifest.json"), "utf8"));
|
|
85
|
+
expect(manifest).toMatchObject({ packageId, guestApi: 3, version: "1.0.0" });
|
|
86
|
+
await expect((0, promises_1.readFile)(path.join(root, "generated/package.bundle.js"))).rejects.toThrow();
|
|
87
|
+
await (0, promises_1.writeFile)(path.join(root, "src/package.ts"), declaration("sandbox", ""));
|
|
88
|
+
await expect((0, build_package_1.generatePackageManifest)({ root })).rejects.toThrow(/version/);
|
|
89
|
+
});
|
|
90
|
+
test.each([
|
|
91
|
+
"sandbox",
|
|
92
|
+
"privileged",
|
|
93
|
+
])("builds %s with its platform's dependency exports", async (mode) => {
|
|
94
|
+
await (0, promises_1.writeFile)(path.join(root, "src/package.ts"), declaration(mode));
|
|
95
|
+
const built = await (0, build_package_1.buildPackage)({ root });
|
|
96
|
+
const { artifact, result } = await probeArtifact();
|
|
97
|
+
expect(result).toEqual({ side: mode === "sandbox" ? "browser" : "node" });
|
|
98
|
+
expect(artifact.providerSource).not.toContain("<!--");
|
|
99
|
+
const raw = JSON.parse((await (0, promises_1.readFile)(built.artifactPath, "utf8")).split("\n")[1]);
|
|
100
|
+
expect(raw.manifest).toEqual(JSON.parse(await (0, promises_1.readFile)(built.manifestPath, "utf8")));
|
|
101
|
+
});
|
|
102
|
+
test("manifest and provider builds resolve dependencies with an output directory outside the package", async () => {
|
|
103
|
+
const outDir = await (0, promises_1.mkdtemp)(path.join((0, node_os_1.tmpdir)(), "peers-package-output-test-"));
|
|
104
|
+
try {
|
|
105
|
+
const generated = await (0, build_package_1.generatePackageManifest)({ root, outDir });
|
|
106
|
+
expect(generated.manifest.packageId).toBe(packageId);
|
|
107
|
+
expect(generated.manifestPath).toBe(path.join(outDir, "manifest.json"));
|
|
108
|
+
const built = await (0, build_package_1.buildPackage)({ root, outDir });
|
|
109
|
+
const raw = JSON.parse((await (0, promises_1.readFile)(built.artifactPath, "utf8")).split("\n")[1]);
|
|
110
|
+
expect(raw.manifest).toEqual(JSON.parse(await (0, promises_1.readFile)(built.manifestPath, "utf8")));
|
|
111
|
+
}
|
|
112
|
+
finally {
|
|
113
|
+
await (0, promises_1.rm)(outDir, { recursive: true, force: true });
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
test("an interrupted build leaves no scratch files in the package's source tree", async () => {
|
|
117
|
+
// Killed mid-evaluation, as Ctrl-C during a watch rebuild would: no cleanup code runs.
|
|
118
|
+
await (0, promises_1.writeFile)(path.join(root, "src/package.ts"), `process.kill(process.pid, "SIGKILL");\n${declaration()}`);
|
|
119
|
+
expect(() => (0, node_child_process_1.execFileSync)(process.execPath, [path.join(sdkRoot, "bin/peers-package.js"), "manifest", "--root", root], { stdio: "pipe" })).toThrow();
|
|
120
|
+
expect((await (0, promises_1.readdir)(root)).sort()).toEqual(["node_modules", "package.json", "src"]);
|
|
121
|
+
});
|
|
122
|
+
test("a failed provider build preserves both previously successful outputs", async () => {
|
|
123
|
+
const first = await (0, build_package_1.buildPackage)({ root });
|
|
124
|
+
const before = await Promise.all([(0, promises_1.readFile)(first.manifestPath), (0, promises_1.readFile)(first.artifactPath)]);
|
|
125
|
+
jest.resetModules();
|
|
126
|
+
await (0, promises_1.writeFile)(path.join(root, "src/package.ts"), declaration("sandbox", "2.0.0", true));
|
|
127
|
+
await expect((0, build_package_1.buildPackage)({ root })).rejects.toThrow(/HTML comment token/);
|
|
128
|
+
expect(await Promise.all([(0, promises_1.readFile)(first.manifestPath), (0, promises_1.readFile)(first.artifactPath)])).toEqual(before);
|
|
129
|
+
});
|
|
130
|
+
test("repeated builds reload external declaration dependencies, including after an evaluation failure", async () => {
|
|
131
|
+
const source = declaration().replace('pkg.version = "1.0.0";', "pkg.version = side;");
|
|
132
|
+
await (0, promises_1.writeFile)(path.join(root, "src/package.ts"), source);
|
|
133
|
+
const stdout = (0, node_child_process_1.execFileSync)(process.execPath, [
|
|
134
|
+
"-e",
|
|
135
|
+
`
|
|
136
|
+
const { writeFile } = require("node:fs/promises");
|
|
137
|
+
const { buildPackage } = require(${JSON.stringify(path.join(sdkRoot, "dist/package-build/build-package.js"))});
|
|
138
|
+
const root = ${JSON.stringify(root)};
|
|
139
|
+
const dependency = ${JSON.stringify(path.join(root, "node_modules/platform-probe/node.js"))};
|
|
140
|
+
(async () => {
|
|
141
|
+
const versions = [];
|
|
142
|
+
await writeFile(dependency, 'exports.side = "1.0.0";');
|
|
143
|
+
versions.push((await buildPackage({ root })).manifest.version);
|
|
144
|
+
await writeFile(dependency, 'exports.side = "";');
|
|
145
|
+
try { await buildPackage({ root }); throw new Error("Invalid declaration accepted"); }
|
|
146
|
+
catch (error) { if (!error.message.includes("must set version")) throw error; }
|
|
147
|
+
await writeFile(dependency, 'exports.side = "2.0.0";');
|
|
148
|
+
versions.push((await buildPackage({ root })).manifest.version);
|
|
149
|
+
process.stdout.write(JSON.stringify(versions));
|
|
150
|
+
})().catch((error) => { console.error(error); process.exitCode = 1; });
|
|
151
|
+
`,
|
|
152
|
+
], { encoding: "utf8", timeout: 10_000 });
|
|
153
|
+
expect(JSON.parse(stdout)).toEqual(["1.0.0", "2.0.0"]);
|
|
154
|
+
});
|
|
155
|
+
test("watch rebuilds use the current execution mode and recover after invalid declarations", async () => {
|
|
156
|
+
const queued = [];
|
|
157
|
+
let deliver;
|
|
158
|
+
const next = () => new Promise((resolve, reject) => {
|
|
159
|
+
const pending = queued.shift();
|
|
160
|
+
if (pending)
|
|
161
|
+
return resolve(pending);
|
|
162
|
+
const timeout = setTimeout(() => reject(new Error("No watch build completed")), 10_000);
|
|
163
|
+
deliver = (value) => {
|
|
164
|
+
clearTimeout(timeout);
|
|
165
|
+
deliver = undefined;
|
|
166
|
+
resolve(value);
|
|
167
|
+
};
|
|
168
|
+
});
|
|
169
|
+
watcher = await (0, build_package_1.watchPackage)({ root }, (value) => {
|
|
170
|
+
if (deliver)
|
|
171
|
+
deliver(value);
|
|
172
|
+
else
|
|
173
|
+
queued.push(value);
|
|
174
|
+
});
|
|
175
|
+
const until = async (matches) => {
|
|
176
|
+
let value = await next();
|
|
177
|
+
while (!matches(value))
|
|
178
|
+
value = await next();
|
|
179
|
+
return value;
|
|
180
|
+
};
|
|
181
|
+
expect(await next()).not.toBeInstanceOf(Error);
|
|
182
|
+
for (const mode of ["privileged", "sandbox"]) {
|
|
183
|
+
// Jest's require cache is separate from the native cache invalidated by loadDefinition.
|
|
184
|
+
jest.resetModules();
|
|
185
|
+
await (0, promises_1.writeFile)(path.join(root, "src/package.ts"), declaration(mode));
|
|
186
|
+
expect(await until((value) => !(value instanceof Error) && value.manifest.executionMode === mode)).toMatchObject({ manifest: { executionMode: mode } });
|
|
187
|
+
expect((await probeArtifact()).result).toEqual({
|
|
188
|
+
side: mode === "sandbox" ? "browser" : "node",
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
// After changing modes, watch the active conditional dependency as well as src/package.ts.
|
|
192
|
+
await (0, promises_1.writeFile)(path.join(root, "node_modules/platform-probe/browser.js"), 'exports.side = "browser-v2";');
|
|
193
|
+
let side;
|
|
194
|
+
do {
|
|
195
|
+
expect(await next()).not.toBeInstanceOf(Error);
|
|
196
|
+
side = (await probeArtifact()).result.side;
|
|
197
|
+
} while (side !== "browser-v2");
|
|
198
|
+
jest.resetModules();
|
|
199
|
+
await (0, promises_1.writeFile)(path.join(root, "src/package.ts"), declaration("sandbox", ""));
|
|
200
|
+
expect(await until((value) => value instanceof Error)).toBeInstanceOf(Error);
|
|
201
|
+
jest.resetModules();
|
|
202
|
+
await (0, promises_1.writeFile)(path.join(root, "src/package.ts"), declaration("sandbox", "2.0.0"));
|
|
203
|
+
expect(await until((value) => !(value instanceof Error) && value.manifest.version === "2.0.0")).toMatchObject({ manifest: { version: "2.0.0" } });
|
|
204
|
+
}, 30_000);
|