@volter/twin-catalog 0.2.28 → 0.2.29
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/catalog.json +1 -1
- package/docs/contributing.md +29 -6
- package/docs/process.md +21 -2
- package/lib/browse.d.mts +13 -0
- package/lib/browse.mjs +10 -0
- package/lib/runner.mjs +6 -2
- package/package.json +2 -2
- package/policy.json +5 -4
- package/runner/Dockerfile +7 -2
- package/runner/evaluate.mjs +3 -2
- package/runner/prepare.mjs +13 -1
package/catalog.json
CHANGED
package/docs/contributing.md
CHANGED
|
@@ -5,7 +5,7 @@ not scripts from a platform checkout. Read the [process](process.md) for identit
|
|
|
5
5
|
|
|
6
6
|
## Build a pack
|
|
7
7
|
|
|
8
|
-
Install Bun at the catalog policy's version and the public `@volter/world-core`, `@volter/world-runtime` and
|
|
8
|
+
Install Bun at the catalog policy's version and the public `@volter/world`, `@volter/world-core`, `@volter/world-runtime` and
|
|
9
9
|
`@volter/twin-standard` versions in `policy.json`. Install TypeScript and its Node/Bun types as development dependencies.
|
|
10
10
|
Keep the package directory named for the vendor; its npm package name is independent.
|
|
11
11
|
|
|
@@ -14,7 +14,9 @@ bunx --bun twin-standard create stripe --init stripe --package @example/payments
|
|
|
14
14
|
```
|
|
15
15
|
|
|
16
16
|
The generated files contain explicit fill markers. Author the vendored spec and its provenance, demand, manifest,
|
|
17
|
-
stored resource states, decision table, handlers and customer journey in the standard's required order.
|
|
17
|
+
stored resource states, decision table, handlers and customer journey in the standard's required order. Start the
|
|
18
|
+
outline from the vendor quickstart and pinned SDK's ordinary default requests, app environment and issued identity.
|
|
19
|
+
Walk that customer entry while building; reducing its requested fields to match the twin does not establish compatibility. The package
|
|
18
20
|
README describes its vendor surface, refusals and limits. Declare your own public GitHub repository and license in
|
|
19
21
|
package.json. Complete source requirements before derivation:
|
|
20
22
|
|
|
@@ -34,7 +36,27 @@ LICENSE. A build and `npm pack --dry-run` must show those paths; no platform-spe
|
|
|
34
36
|
|
|
35
37
|
## Assess before submitting
|
|
36
38
|
|
|
37
|
-
|
|
39
|
+
Package the customer entry in `journeys/first-use.json`. It has `schemaVersion: 1`, the upstream `source`, an
|
|
40
|
+
`about` describing its scope, and a `files` map of relative paths to text. Include `package.json` with exact registry
|
|
41
|
+
SDK dependencies, `.env.example` with the names the app reads, and its unchanged client script with result
|
|
42
|
+
assertions. `run` is an argument array such as `["node", "workflow.mjs"]`. For stored workflows,
|
|
43
|
+
`retention: { "readback": ["node", "readback.mjs"] }` inspects prior state without creating it again. A stateless
|
|
44
|
+
workflow instead declares `retention: { "none": "<why no stored vendor resource is created>" }`. Use SDKs to create
|
|
45
|
+
stored data and existing vendor doors for synthetic setup; do not use scenario success in place of a stored write.
|
|
46
|
+
The standard owns CLI lifecycle and teardown, so the scripts do not start or stop infrastructure themselves. For a timed workflow, `clock: { "at": "<ISO instant>", "advanceAfterRun": "10s" }` freezes time before the client runs, then advances and inspects the due result before retention is measured. The duration is the journey’s chosen synthetic wait, not a vendor timing guarantee.
|
|
47
|
+
|
|
48
|
+
Walk the life's journeys and this first-use journey while implementing. For final qualification, build and pack
|
|
49
|
+
first, install that tarball in a fresh directory without development links, and run the released standard against
|
|
50
|
+
the installed package. Keep the archive inventory and lockfile. Prepare a new customer app beside that installation:
|
|
51
|
+
|
|
52
|
+
```sh
|
|
53
|
+
twin-standard prepare-consumer <installed pack dir> --app <new app dir> --cli <absolute installed volter executable> --integrity <packed SHA-512 integrity> --out prepared-consumer.json
|
|
54
|
+
npm install --prefix <new app dir> --ignore-scripts --no-audit --no-fund
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
This preparation writes fixture files only; SDK dependency installation precedes offline execution. The product
|
|
58
|
+
CLI must resolve the installed candidate through that app's parent dependency directory. For the final assessment,
|
|
59
|
+
run the released standard through a World:
|
|
38
60
|
|
|
39
61
|
Create `assessment.world.config.json`:
|
|
40
62
|
|
|
@@ -43,15 +65,16 @@ Create `assessment.world.config.json`:
|
|
|
43
65
|
```
|
|
44
66
|
|
|
45
67
|
```sh
|
|
46
|
-
volter-world run assessment.world.config.json --root . --env-out /tmp/new-assessment.env --owner pack-assessment --
|
|
68
|
+
volter-world run assessment.world.config.json --root . --env-out /tmp/new-assessment.env --owner pack-assessment -- twin-standard assess <installed pack dir> --prepared-consumer prepared-consumer.json --browser --out assessment.json
|
|
47
69
|
```
|
|
48
70
|
|
|
49
|
-
The assessment config has no vendor services; the standard creates fresh in-memory
|
|
71
|
+
The outer assessment config has no vendor services; the standard creates fresh in-memory state for journey replay
|
|
72
|
+
and uses the product CLI to own a separate app World for the installed customer workflow.
|
|
50
73
|
For an HTTP browser target, use Linux, install Playwright at the catalog policy version and its Chromium binary,
|
|
51
74
|
and install `libfaketime` (Debian/Ubuntu: `sudo apt-get install libfaketime`). The browser process uses the World
|
|
52
75
|
clock for native cookie expiry, at one-second precision; JavaScript time retains millisecond precision. Authors on
|
|
53
76
|
other platforms can omit `--browser` for in-process assessment; catalog Actions supplies the browser target. The report
|
|
54
|
-
separates declared served/gap counts, operations actually exercised, deterministic replay, conformance and Chromium
|
|
77
|
+
separates declared served/gap counts, operations actually exercised, deterministic replay, conformance, installed customer workflow and Chromium
|
|
55
78
|
results. It does not measure DOM interactions, line coverage, real-vendor parity or every possible state transition.
|
|
56
79
|
|
|
57
80
|
Local assessment helps authors; it does not replace the independent catalog assessment of the published tarball.
|
package/docs/process.md
CHANGED
|
@@ -47,6 +47,8 @@ does not change that pin. Removing a recommendation is not uninstalling a runnin
|
|
|
47
47
|
Each assessed release exposes its artifact's description, license, Node requirements, kernel peer requirements,
|
|
48
48
|
source directory and issue tracker beside its integrity, provenance and measured behavior. README and license links
|
|
49
49
|
point to the source commit verified by npm provenance. They describe that version, not the publisher's current branch.
|
|
50
|
+
The JSON-only reader also projects installed customer scope, pinned SDK dependencies, actual CLI/kernel/runtime versions, clock conditions, retention and teardown when the bound report measured them. An older report exposes `consumer: null`; it cannot acquire a passing workflow from publisher trust or a later test.
|
|
51
|
+
|
|
50
52
|
Historical assessments without these retained fields show them as unavailable; current registry metadata does not
|
|
51
53
|
fill missing historical evidence. A provenance link establishes source and build identity, not vendor fidelity.
|
|
52
54
|
|
|
@@ -81,7 +83,8 @@ An already admitted package/version cannot acquire different bytes or a differen
|
|
|
81
83
|
|
|
82
84
|
The catalog evaluates only the tarball fetched at the declared integrity. Package source in the contributor's
|
|
83
85
|
repository and evidence supplied in the PR are context, never substitutes for running that artifact. A submission
|
|
84
|
-
cannot select its own evaluator, thresholds, registry
|
|
86
|
+
cannot select its own evaluator, thresholds, registry or privileges. A pack's customer fixture supplies its
|
|
87
|
+
unchanged client invocation; the pinned standard owns initialization, lifecycle, assertions and readiness.
|
|
85
88
|
|
|
86
89
|
## State and invalidation
|
|
87
90
|
|
|
@@ -143,7 +146,8 @@ routing, not the isolation boundary: the container enforces the evaluation netwo
|
|
|
143
146
|
The trusted controller owns success/failure, hashes and run identity. Candidate output is untrusted data; bounded
|
|
144
147
|
report parsing never executes it or interpolates it into a shell. It can explain results but cannot confer merge or
|
|
145
148
|
publication authority. The trusted summary renderer escapes contributor strings and neutralizes mentions. Raw logs
|
|
146
|
-
and JSON evidence are downloadable artifacts.
|
|
149
|
+
and JSON evidence are downloadable artifacts. A packaged first-use fixture executes only in the offline assessment
|
|
150
|
+
container under the same unprivileged boundary as the candidate pack; it has no controller or admission authority.
|
|
147
151
|
Conformance and journey checks are the versioned standard's own entrypoints. Like any in-process plugin check, they
|
|
148
152
|
measure cooperative pack behavior, not a proof that malicious executable code cannot deceive a test; moderator
|
|
149
153
|
source review remains part of admission.
|
|
@@ -161,6 +165,21 @@ The released standard owns the development SDK versions used by its client cases
|
|
|
161
165
|
The versioned `@volter/twin-standard` owns the checks and machine-readable assessment API. The catalog invokes it;
|
|
162
166
|
it does not implement another Protocol 3 grader or journey engine. Evaluation occurs inside a task-owned World.
|
|
163
167
|
|
|
168
|
+
Installed customer readiness is separate from the HTTP journey replay. The released standard materializes the
|
|
169
|
+
pack's `journeys/first-use.json` in a new application directory; versioned standard fixtures cover older immutable
|
|
170
|
+
artifacts that predate this format. Preparation installs the fixture's exact SDK versions with scripts disabled,
|
|
171
|
+
retains its resolved dependency lock and recipe digest, and pins the product CLI alongside the kernel and runtime.
|
|
172
|
+
Evaluation calls normal `volter world init` and checks that its service uses the exact candidate package/version,
|
|
173
|
+
then runs the unchanged client and its result assertions. A stored workflow must retain its state through `down`
|
|
174
|
+
and `up`; its readback may add no mutations. Timed workflows settle the declared result under an explicit frozen/advanced World clock before the retention baseline, so runtime scheduling delay is not a test input. A stateless workflow names why this does not apply. Final stopped
|
|
175
|
+
status is required. Missing or failing consumer evidence means changes needed, even when source form and replay pass.
|
|
176
|
+
These walks establish the advertised workflow at the recorded SDK and tool versions; they do not establish every
|
|
177
|
+
SDK method, complete vendor parity or production delivery. Historical reports keep their original scope.
|
|
178
|
+
|
|
179
|
+
The Linux runner installs native PostgreSQL tools without a distribution-owned cluster for customer workflows
|
|
180
|
+
that declare that backing. Each World starts and stops its own database through the runtime; the container image
|
|
181
|
+
and the tool/SDK locks identify the execution conditions.
|
|
182
|
+
|
|
164
183
|
Quick assessment walks the packaged customer journey twice from fresh state with the same clock and World draws.
|
|
165
184
|
It reports failures, answered steps, response replay equality, and served/gap counts over the entire declared surface. Actual operation coverage comes from kernel dispatch, with
|
|
166
185
|
every declared gap retained in the denominator; declared support and exercised coverage are separate fields.
|
package/lib/browse.d.mts
CHANGED
|
@@ -7,6 +7,19 @@ export type Coverage = {
|
|
|
7
7
|
};
|
|
8
8
|
export type Replay = { equal: boolean; runs: number; firstSha256: string; secondSha256: string };
|
|
9
9
|
export type Measurements = {
|
|
10
|
+
consumer: null | {
|
|
11
|
+
measured: boolean;
|
|
12
|
+
passed: boolean;
|
|
13
|
+
scope: string | null;
|
|
14
|
+
fixture: string | null;
|
|
15
|
+
sdkDependencies: Record<string, string> | null;
|
|
16
|
+
tools: Record<string, string> | null;
|
|
17
|
+
clock: Record<string, unknown> | null;
|
|
18
|
+
retainedReadback: boolean | null;
|
|
19
|
+
retentionNotApplicable: string | null;
|
|
20
|
+
teardownVerified: boolean | null;
|
|
21
|
+
failures: string[] | null;
|
|
22
|
+
};
|
|
10
23
|
scope: string | null;
|
|
11
24
|
surface: { total: number; served: number; gap: number } | null;
|
|
12
25
|
operationCoverage: Coverage | null;
|
package/lib/browse.mjs
CHANGED
|
@@ -130,6 +130,16 @@ export function browse(root, { vendor, package: packageName, integrity = null }
|
|
|
130
130
|
operationCoverage: quick?.operationCoverage ?? null, servedOperations: quick?.servedOperations ?? null,
|
|
131
131
|
steps: quick?.steps ?? null, answered: quick?.answered ?? null, failures: quick?.failures ?? null,
|
|
132
132
|
replay: quick?.replay ?? null, conformance: bound.report.conformance ?? null,
|
|
133
|
+
consumer: bound.report.consumer ? {
|
|
134
|
+
measured: bound.report.consumer.measured === true, passed: bound.report.consumer.passed === true,
|
|
135
|
+
scope: bound.report.consumer.scope ?? null, fixture: bound.report.consumer.fixture ?? null,
|
|
136
|
+
sdkDependencies: bound.envelope.prepared?.consumerFixture?.dependencies ?? null,
|
|
137
|
+
tools: bound.report.consumer.tools ?? null, clock: bound.report.consumer.clock ?? null,
|
|
138
|
+
retainedReadback: bound.report.consumer.retainedReadback ?? null,
|
|
139
|
+
retentionNotApplicable: bound.report.consumer.retentionNotApplicable ?? null,
|
|
140
|
+
teardownVerified: bound.report.consumer.teardownVerified ?? null,
|
|
141
|
+
failures: bound.report.consumer.failures ?? null,
|
|
142
|
+
} : null,
|
|
133
143
|
browser: browser?.measured === true ? browser : null,
|
|
134
144
|
codeCoverage: quick?.codeCoverage ?? null, stateTransitionCoverage: quick?.stateTransitionCoverage ?? null,
|
|
135
145
|
domCoverage: browser?.domCoverage ?? null, comparison: bound.report.comparison ?? null } : null };
|
package/lib/runner.mjs
CHANGED
|
@@ -25,7 +25,8 @@ export function withContainer(root, input, output, execute, { spawn = spawnSync
|
|
|
25
25
|
let volume = false;
|
|
26
26
|
try {
|
|
27
27
|
const tools = input.policy.tools;
|
|
28
|
-
|
|
28
|
+
requireThat(tools['@volter/world'], 'policy must pin the product CLI for installed customer workflows');
|
|
29
|
+
run(['build', '--iidfile', imageFile, '--build-arg', `NODE_IMAGE=${input.policy.nodeImage}`, '--build-arg', `BUN_VERSION=${input.policy.bun}`, '--build-arg', `STANDARD_VERSION=${tools['@volter/twin-standard']}`, '--build-arg', `CORE_VERSION=${tools['@volter/world-core']}`, '--build-arg', `RUNTIME_VERSION=${tools['@volter/world-runtime']}`, '--build-arg', `CLI_VERSION=${tools['@volter/world']}`, '--build-arg', `PLAYWRIGHT_VERSION=${tools.playwright}`, '-f', join(root, 'runner/Dockerfile'), root]);
|
|
29
30
|
const image = readFileSync(imageFile, 'utf8').trim();
|
|
30
31
|
requireThat(/^sha256:[a-f0-9]{64}$/.test(image), 'container image has no immutable identity');
|
|
31
32
|
run(['volume', 'create', '--label', `twin-catalog.owner=${id}`, '--label', `twin-catalog.run=${runOwner}`, id]); volume = true;
|
|
@@ -56,12 +57,15 @@ export function evaluate(root, input, output, options = {}) {
|
|
|
56
57
|
const lock = run([...common, '--network', 'none', image, '-e', 'process.stdout.write(require("fs").readFileSync("/work/package-lock.json","utf8"))']).stdout;
|
|
57
58
|
requireThat(digest(lock) === prepared.dependencyLockSha256, 'dependency lock identity changed');
|
|
58
59
|
write(`${output}.lock.json`, JSON.parse(lock));
|
|
60
|
+
const consumerLock = run([...common, '--network', 'none', image, '-e', 'process.stdout.write(require("fs").readFileSync("/work/customer-app/package-lock.json","utf8"))']).stdout;
|
|
61
|
+
requireThat(digest(consumerLock) === prepared.consumerDependencyLockSha256, 'customer SDK lock identity changed');
|
|
62
|
+
write(`${output}.consumer-lock.json`, JSON.parse(consumerLock));
|
|
59
63
|
phase = 'offline-assessment';
|
|
60
64
|
const result = run([...common, '--network', 'none', '--entrypoint', 'bun', image, '/opt/catalog/node_modules/@volter/world-runtime/src/cli.ts', 'run', '/work/world.json', '--root', '/work', '--env-out', '/work/world.env', '--owner', owner, '--', 'bun', '/opt/catalog/runner/evaluate.mjs'], true);
|
|
61
65
|
const reportRead = run([...common, '--network', 'none', image, '-e', 'process.stdout.write(require("fs").readFileSync("/work/report.json","utf8"))'], true);
|
|
62
66
|
const report = reportRead.status === 0 ? JSON.parse(reportRead.stdout) : null;
|
|
63
67
|
requireThat(!report || (report.schemaVersion === 1 && report.package === input.submission.package && report.version === input.submission.version && report.integrity === input.submission.integrity), 'evaluator returned a mismatched report');
|
|
64
|
-
const envelope = { schemaVersion: 1, inputSha256: digest(input), input, image, prepared, dependencyLock: JSON.parse(lock), status: result.status === 0 && report?.ready === true ? 'ready' : 'changes-needed', report, evaluatedAt: new Date().toISOString() };
|
|
68
|
+
const envelope = { schemaVersion: 1, inputSha256: digest(input), input, image, prepared, dependencyLock: JSON.parse(lock), consumerDependencyLock: JSON.parse(consumerLock), status: result.status === 0 && report?.ready === true ? 'ready' : 'changes-needed', report, evaluatedAt: new Date().toISOString() };
|
|
65
69
|
write(output, envelope);
|
|
66
70
|
return envelope;
|
|
67
71
|
}, options);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@volter/twin-catalog",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.29",
|
|
4
4
|
"description": "The moderated twin distribution: approved versions and submission tooling for independent publishers.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
"evidence"
|
|
39
39
|
],
|
|
40
40
|
"catalog": {
|
|
41
|
-
"sourceCommit": "
|
|
41
|
+
"sourceCommit": "251b4771b7e0f8f262041cc47f436d8b4f785537",
|
|
42
42
|
"digest": "3195c901d6068ef7be8e9e23676b9b6ec58fb4e50c2d5604bbea390fefec9fdb"
|
|
43
43
|
}
|
|
44
44
|
}
|
package/policy.json
CHANGED
|
@@ -2,10 +2,11 @@
|
|
|
2
2
|
"schemaVersion": 1,
|
|
3
3
|
"registry": "https://registry.npmjs.org",
|
|
4
4
|
"tools": {
|
|
5
|
-
"@volter/world-core": "3.0.
|
|
6
|
-
"@volter/world-runtime": "3.0.
|
|
7
|
-
"@volter/twin-standard": "1.0.
|
|
8
|
-
"playwright": "1.63.0"
|
|
5
|
+
"@volter/world-core": "3.0.100",
|
|
6
|
+
"@volter/world-runtime": "3.0.100",
|
|
7
|
+
"@volter/twin-standard": "1.0.92",
|
|
8
|
+
"playwright": "1.63.0",
|
|
9
|
+
"@volter/world": "3.0.100"
|
|
9
10
|
},
|
|
10
11
|
"bun": "1.4.2",
|
|
11
12
|
"nodeImage": "node:24-bookworm-slim",
|
package/runner/Dockerfile
CHANGED
|
@@ -4,13 +4,18 @@ ARG BUN_VERSION=1.3.11
|
|
|
4
4
|
ARG STANDARD_VERSION=1.0.37
|
|
5
5
|
ARG CORE_VERSION=3.0.45
|
|
6
6
|
ARG RUNTIME_VERSION=3.0.45
|
|
7
|
+
ARG CLI_VERSION
|
|
7
8
|
ARG PLAYWRIGHT_VERSION=1.63.0
|
|
8
9
|
ENV PLAYWRIGHT_BROWSERS_PATH=/opt/playwright
|
|
9
|
-
|
|
10
|
+
# Install native PostgreSQL tools without a distribution-owned cluster; each World owns its own data and lifetime.
|
|
11
|
+
# https://manpages.debian.org/bookworm/postgresql-common/pg_createcluster.1.en.html#DEFAULT_VALUES
|
|
12
|
+
RUN mkdir -p /etc/postgresql-common && printf 'create_main_cluster = false\n' > /etc/postgresql-common/createcluster.conf \
|
|
13
|
+
&& apt-get update && apt-get install -y --no-install-recommends git openssl ca-certificates libfaketime postgresql-15 && rm -rf /var/lib/apt/lists/*
|
|
14
|
+
ENV VOLTER_WORLD_POSTGRES_BIN=/usr/lib/postgresql/15/bin
|
|
10
15
|
WORKDIR /opt/catalog
|
|
11
16
|
RUN npm install --ignore-scripts --no-audit --no-fund --save-exact \
|
|
12
17
|
@oven/bun-linux-x64@${BUN_VERSION} @volter/twin-standard@${STANDARD_VERSION} \
|
|
13
|
-
@volter/world-core@${CORE_VERSION} @volter/world-runtime@${RUNTIME_VERSION} tar@7.5.7 semver@7.8.5 sigstore@4.1.1 playwright@${PLAYWRIGHT_VERSION}
|
|
18
|
+
@volter/world-core@${CORE_VERSION} @volter/world-runtime@${RUNTIME_VERSION} @volter/world@${CLI_VERSION} tar@7.5.7 semver@7.8.5 sigstore@4.1.1 playwright@${PLAYWRIGHT_VERSION}
|
|
14
19
|
ENV PATH="/opt/catalog/node_modules/@oven/bun-linux-x64/bin:/opt/catalog/node_modules/.bin:${PATH}"
|
|
15
20
|
RUN ./node_modules/.bin/playwright install --with-deps chromium
|
|
16
21
|
COPY lib /opt/catalog/lib
|
package/runner/evaluate.mjs
CHANGED
|
@@ -7,9 +7,10 @@ requireThat(Boolean(process.env.VOLTER_WORLD), 'evaluation must run inside a Wor
|
|
|
7
7
|
const { submission: s } = read('/input/input.json');
|
|
8
8
|
const requireFromPack = createRequire('/work/package.json');
|
|
9
9
|
const { assessPack } = await import(requireFromPack.resolve('@volter/twin-standard'));
|
|
10
|
-
const
|
|
10
|
+
const prepared = read('/work/prepared.json');
|
|
11
|
+
const assessment = await assessPack(join('/work/packs', s.vendor), { browser: true, consumer: prepared.consumer });
|
|
11
12
|
requireThat(assessment.package === s.package && assessment.version === s.version && assessment.vendor === s.vendor, 'assessment returned a different artifact identity');
|
|
12
|
-
const report = { ...assessment, integrity: s.integrity, prepared
|
|
13
|
+
const report = { ...assessment, integrity: s.integrity, prepared };
|
|
13
14
|
report.comparison = compareReports(report, existsSync('/input/baseline.json') ? read('/input/baseline.json') : null);
|
|
14
15
|
write('/work/report.json', report);
|
|
15
16
|
process.exit(report.ready ? 0 : 1);
|
package/runner/prepare.mjs
CHANGED
|
@@ -81,6 +81,18 @@ const provenance = await verifiedProvenance(await response.json(), source, bytes
|
|
|
81
81
|
// Keep a vendor-named package directory for the standard's public directory API; preserve the installed artifact.
|
|
82
82
|
mkdirSync('/work/packs', { recursive: true });
|
|
83
83
|
cpSync(join('/work/node_modules', s.package), join('/work/packs', s.vendor), { recursive: true });
|
|
84
|
+
const { prepareConsumerApp } = await import(evaluatorRequire.resolve('@volter/twin-standard'));
|
|
85
|
+
const appDir = '/work/customer-app';
|
|
86
|
+
const consumerFixture = prepareConsumerApp(join('/work/packs', s.vendor), appDir);
|
|
87
|
+
const appInstall = spawnSync('npm', ['install', '--ignore-scripts', '--no-audit', '--no-fund', '--registry', policy.registry], { cwd: appDir, encoding: 'utf8', maxBuffer: 16 * 1024 * 1024 });
|
|
88
|
+
requireThat(appInstall.status === 0, `customer SDK preparation failed: ${appInstall.stderr}`);
|
|
89
|
+
const consumerLock = read(join(appDir, 'package-lock.json'));
|
|
90
|
+
for (const [path, entry] of Object.entries(consumerLock.packages ?? {})) {
|
|
91
|
+
if (!path) continue;
|
|
92
|
+
requireThat(!entry.link && entry.resolved && entry.integrity, `${path}: customer dependency is not a locked registry artifact`);
|
|
93
|
+
registryURL(entry.resolved, policy.registry);
|
|
94
|
+
}
|
|
95
|
+
const consumer = { appDir, cli: '/work/node_modules/.bin/volter', artifactIntegrity: s.integrity };
|
|
84
96
|
// Retain listing facts from these exact bytes, never the registry's moving latest metadata.
|
|
85
97
|
const packageMetadata = {
|
|
86
98
|
description: typeof pack.description === 'string' ? pack.description : null,
|
|
@@ -90,5 +102,5 @@ const packageMetadata = {
|
|
|
90
102
|
repositoryDirectory: typeof pack.repository === 'object' ? pack.repository.directory ?? null : null,
|
|
91
103
|
bugs: typeof pack.bugs === 'string' ? pack.bugs : pack.bugs?.url ?? null,
|
|
92
104
|
};
|
|
93
|
-
write('/work/prepared.json', { schemaVersion: 1, submission: s, integrity: s.integrity, provenance, packageMetadata, dependencyLockSha256: digest(readFileSync('/work/package-lock.json', 'utf8')), clientSdkLocks, clientDependencyChoices, tools: policy.tools });
|
|
105
|
+
write('/work/prepared.json', { schemaVersion: 1, submission: s, integrity: s.integrity, provenance, packageMetadata, dependencyLockSha256: digest(readFileSync('/work/package-lock.json', 'utf8')), clientSdkLocks, clientDependencyChoices, tools: policy.tools, consumer, consumerFixture, consumerDependencyLockSha256: digest(readFileSync(join(appDir, 'package-lock.json'), 'utf8')) });
|
|
94
106
|
write('/work/world.json', { id: 'catalog-assessment', network: { egress: [] }, services: [] });
|