@volter/twin-standard 1.0.59 → 1.0.60
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/src/clients/run.js
CHANGED
|
@@ -10,11 +10,20 @@ var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExte
|
|
|
10
10
|
// vendor, the served port, the host, the issued credentials and the scenario file; each case's failures are printed as
|
|
11
11
|
// one JSON line, `{ id, asks, failures }`.
|
|
12
12
|
import { readFileSync } from 'node:fs';
|
|
13
|
+
import { createRequire } from 'node:module';
|
|
13
14
|
import { clientContext, worldProxy } from "./harness.js";
|
|
14
15
|
const job = JSON.parse(readFileSync(process.argv[2], 'utf8'));
|
|
15
16
|
const { cases } = (await import(__rewriteRelativeImportExtension(`./${job.vendor}.ts`)));
|
|
16
17
|
// every connection a case makes goes through the World's proxy, as an application's does in a World
|
|
17
|
-
const
|
|
18
|
+
const proxy = await worldProxy(job.port);
|
|
19
|
+
// Bind only this artifact's declared vendor hosts to the assessed server. The enclosing World's
|
|
20
|
+
// injector stays active, and untwinned destinations keep its refusal policy.
|
|
21
|
+
const require = createRequire(import.meta.url);
|
|
22
|
+
require('@volter/world-core/pack-facts').extendPackFacts([job.packFacts]);
|
|
23
|
+
const facts = JSON.parse(readFileSync(job.packFacts, 'utf8')).packs[job.vendor];
|
|
24
|
+
const keys = [...new Set((facts.hosts ?? []).map((rule) => rule.key ?? job.vendor))];
|
|
25
|
+
require('@volter/world-core/inject').install(Object.fromEntries(keys.map((key) => [key, `http://127.0.0.1:${proxy}`])));
|
|
26
|
+
const context = clientContext(proxy, job.host, job.env, job.scenarioPath);
|
|
18
27
|
for (const c of cases) {
|
|
19
28
|
const failures = await Promise.race([
|
|
20
29
|
c.run(context).catch((e) => [`the case threw: ${e.message}`]),
|
package/dist/src/conformance.js
CHANGED
|
@@ -25,6 +25,8 @@ import { connect } from 'node:net';
|
|
|
25
25
|
import { tmpdir } from 'node:os';
|
|
26
26
|
import { basename, dirname, join, matchesGlob, relative, resolve } from 'node:path';
|
|
27
27
|
import { citations, sourceHeld } from "./check-sources.js";
|
|
28
|
+
import { standardPackageFile } from "./package-file.js";
|
|
29
|
+
import { fileURLToPath } from 'node:url';
|
|
28
30
|
const pascal = (s) => s.split(/[^a-zA-Z0-9]+/).filter(Boolean).map((w) => w[0].toUpperCase() + w.slice(1)).join('');
|
|
29
31
|
const readJson = (p) => (existsSync(p) ? JSON.parse(readFileSync(p, 'utf8')) : undefined);
|
|
30
32
|
/** The pack's units: the pack and each lane in it (a directory with its own src/manifest.ts and no package.json). */
|
|
@@ -331,7 +333,7 @@ function lineConnection(port) {
|
|
|
331
333
|
return (sink) => {
|
|
332
334
|
const socket = connect(port, '127.0.0.1');
|
|
333
335
|
let heard = 0;
|
|
334
|
-
socket.on('data', (bytes) => { heard =
|
|
336
|
+
socket.on('data', (bytes) => { heard = performance.now(); sink.write(bytes); });
|
|
335
337
|
socket.on('close', () => sink.end());
|
|
336
338
|
socket.on('error', () => socket.destroy());
|
|
337
339
|
const opened = new Promise((resolveOpen) => { socket.once('connect', () => resolveOpen()); socket.once('error', () => resolveOpen()); });
|
|
@@ -339,13 +341,13 @@ function lineConnection(port) {
|
|
|
339
341
|
data: (chunk) => { void opened.then(() => socket.write(chunk)); },
|
|
340
342
|
settled: async () => {
|
|
341
343
|
await opened;
|
|
342
|
-
const start =
|
|
344
|
+
const start = performance.now();
|
|
343
345
|
const since = heard;
|
|
344
|
-
while (
|
|
346
|
+
while (performance.now() - start < 10_000) {
|
|
345
347
|
await new Promise((r) => setTimeout(r, 10));
|
|
346
348
|
if (socket.destroyed)
|
|
347
349
|
return;
|
|
348
|
-
if (heard > since ?
|
|
350
|
+
if (heard > since ? performance.now() - heard >= 50 : performance.now() - start >= 500)
|
|
349
351
|
return;
|
|
350
352
|
}
|
|
351
353
|
},
|
|
@@ -411,7 +413,8 @@ async function lifeServed(pack) {
|
|
|
411
413
|
* descriptor's clientCasesNone says why none is owed. */
|
|
412
414
|
async function clientCases(pack, scratch) {
|
|
413
415
|
const vendor = (await manifestOf(pack)).vendor ?? basename(pack);
|
|
414
|
-
|
|
416
|
+
const clients = join(dirname(fileURLToPath(standardPackageFile('package.json'))), 'src', 'clients');
|
|
417
|
+
if (!existsSync(join(clients, `${vendor}.ts`))) {
|
|
415
418
|
// a vendor whose descriptor names npm clients (adoption.sdks) is held to them: no case is no proof the SDK works,
|
|
416
419
|
// unless the descriptor says why none is owed (clientCasesNone: no official SDK, or none the demand's applications
|
|
417
420
|
// use); one an application calls with no SDK of the vendor's (a raw HTTP catalog) has no client to drive
|
|
@@ -455,10 +458,10 @@ async function clientCases(pack, scratch) {
|
|
|
455
458
|
issued[name] = String(answer[field]);
|
|
456
459
|
}
|
|
457
460
|
const job = join(scratch, 'client-job.json');
|
|
458
|
-
writeFileSync(job, JSON.stringify({ vendor, port: server.port, host, env: issued, scenarioPath }));
|
|
461
|
+
writeFileSync(job, JSON.stringify({ vendor, port: server.port, host, env: issued, scenarioPath, packFacts: join(pack, 'generated', 'pack-facts.json') }));
|
|
459
462
|
// Node, as an application runs its SDK (asynchronous: this process serves the pack meanwhile)
|
|
460
463
|
const out = await new Promise((resolve) => {
|
|
461
|
-
const child = spawn('node', ['--experimental-strip-types', '--no-warnings', join(
|
|
464
|
+
const child = spawn('node', ['--experimental-strip-types', '--no-warnings', join(clients, 'run.ts'), job], { stdio: ['ignore', 'pipe', 'pipe'] });
|
|
462
465
|
let text = '';
|
|
463
466
|
let errors = '';
|
|
464
467
|
child.stdout.on('data', (d) => { text += d; });
|
|
@@ -645,9 +645,9 @@ export async function walk(journey, root, collect, opts) {
|
|
|
645
645
|
// they arrive (the steps' settle waits until none has come for a moment)
|
|
646
646
|
const wire = new Map();
|
|
647
647
|
const settleWire = async (c, step) => {
|
|
648
|
-
const deadline =
|
|
648
|
+
const deadline = performance.now() + 10_000;
|
|
649
649
|
let seen = -1;
|
|
650
|
-
while (
|
|
650
|
+
while (performance.now() < deadline) {
|
|
651
651
|
const body = { frames: c.frames, last: c.frames.at(-1) ?? null, closed: c.closed };
|
|
652
652
|
let expected = true;
|
|
653
653
|
try {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@volter/twin-standard",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.60",
|
|
4
4
|
"description": "The Protocol 3 standard for Volter twin packs: the criteria a pack is graded by, the grader, the gate a pack repository and the twin catalog run on every change and submission, and the spec derivation the grade checks against. Pin a version: every place a pack is judged judges by that standard.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"twin",
|
|
@@ -75,7 +75,7 @@
|
|
|
75
75
|
"yaml": "2.9.0"
|
|
76
76
|
},
|
|
77
77
|
"peerDependencies": {
|
|
78
|
-
"@volter/world-core": "3.0.
|
|
78
|
+
"@volter/world-core": "3.0.68",
|
|
79
79
|
"@volter/world-runtime": "*",
|
|
80
80
|
"playwright": "^1.63.0"
|
|
81
81
|
},
|
package/src/clients/run.ts
CHANGED
|
@@ -2,12 +2,21 @@
|
|
|
2
2
|
// vendor, the served port, the host, the issued credentials and the scenario file; each case's failures are printed as
|
|
3
3
|
// one JSON line, `{ id, asks, failures }`.
|
|
4
4
|
import { readFileSync } from 'node:fs';
|
|
5
|
+
import { createRequire } from 'node:module';
|
|
5
6
|
import { clientContext, worldProxy, type ClientCase } from './harness.ts';
|
|
6
7
|
|
|
7
|
-
const job = JSON.parse(readFileSync(process.argv[2]!, 'utf8')) as { vendor: string; port: number; host: string; env: Record<string, string>; scenarioPath: string };
|
|
8
|
+
const job = JSON.parse(readFileSync(process.argv[2]!, 'utf8')) as { vendor: string; port: number; host: string; env: Record<string, string>; scenarioPath: string; packFacts: string };
|
|
8
9
|
const { cases } = (await import(`./${job.vendor}.ts`)) as { cases: ClientCase[] };
|
|
9
10
|
// every connection a case makes goes through the World's proxy, as an application's does in a World
|
|
10
|
-
const
|
|
11
|
+
const proxy = await worldProxy(job.port);
|
|
12
|
+
// Bind only this artifact's declared vendor hosts to the assessed server. The enclosing World's
|
|
13
|
+
// injector stays active, and untwinned destinations keep its refusal policy.
|
|
14
|
+
const require = createRequire(import.meta.url);
|
|
15
|
+
require('@volter/world-core/pack-facts').extendPackFacts([job.packFacts]);
|
|
16
|
+
const facts = JSON.parse(readFileSync(job.packFacts, 'utf8')).packs[job.vendor];
|
|
17
|
+
const keys: string[] = [...new Set<string>((facts.hosts ?? []).map((rule: { key?: string }) => rule.key ?? job.vendor))];
|
|
18
|
+
require('@volter/world-core/inject').install(Object.fromEntries(keys.map((key) => [key, `http://127.0.0.1:${proxy}`])));
|
|
19
|
+
const context = clientContext(proxy, job.host, job.env, job.scenarioPath);
|
|
11
20
|
for (const c of cases) {
|
|
12
21
|
const failures = await Promise.race([
|
|
13
22
|
c.run(context).catch((e: Error) => [`the case threw: ${e.message}`]),
|
package/src/conformance.ts
CHANGED
|
@@ -18,6 +18,8 @@ import { tmpdir } from 'node:os';
|
|
|
18
18
|
import { basename, dirname, join, matchesGlob, relative, resolve } from 'node:path';
|
|
19
19
|
import type { TwinStream } from '@volter/world-core';
|
|
20
20
|
import { citations, sourceHeld, type SourceRecord } from './check-sources.ts';
|
|
21
|
+
import { standardPackageFile } from './package-file.ts';
|
|
22
|
+
import { fileURLToPath } from 'node:url';
|
|
21
23
|
|
|
22
24
|
export type ConformanceResult = { id: string; asks: string; failures: string[]; note?: string };
|
|
23
25
|
|
|
@@ -297,7 +299,7 @@ function lineConnection(port: number): TwinStream {
|
|
|
297
299
|
return (sink) => {
|
|
298
300
|
const socket = connect(port, '127.0.0.1');
|
|
299
301
|
let heard = 0;
|
|
300
|
-
socket.on('data', (bytes: Uint8Array) => { heard =
|
|
302
|
+
socket.on('data', (bytes: Uint8Array) => { heard = performance.now(); sink.write(bytes); });
|
|
301
303
|
socket.on('close', () => sink.end());
|
|
302
304
|
socket.on('error', () => socket.destroy());
|
|
303
305
|
const opened = new Promise<void>((resolveOpen) => { socket.once('connect', () => resolveOpen()); socket.once('error', () => resolveOpen()); });
|
|
@@ -305,11 +307,11 @@ function lineConnection(port: number): TwinStream {
|
|
|
305
307
|
data: (chunk) => { void opened.then(() => socket.write(chunk)); },
|
|
306
308
|
settled: async () => {
|
|
307
309
|
await opened;
|
|
308
|
-
const start =
|
|
309
|
-
while (
|
|
310
|
+
const start = performance.now(); const since = heard;
|
|
311
|
+
while (performance.now() - start < 10_000) {
|
|
310
312
|
await new Promise((r) => setTimeout(r, 10));
|
|
311
313
|
if (socket.destroyed) return;
|
|
312
|
-
if (heard > since ?
|
|
314
|
+
if (heard > since ? performance.now() - heard >= 50 : performance.now() - start >= 500) return;
|
|
313
315
|
}
|
|
314
316
|
},
|
|
315
317
|
close: () => socket.destroy(),
|
|
@@ -364,7 +366,8 @@ async function lifeServed(pack: string): Promise<string[]> {
|
|
|
364
366
|
* descriptor's clientCasesNone says why none is owed. */
|
|
365
367
|
async function clientCases(pack: string, scratch: string): Promise<{ failures: string[]; note?: string }> {
|
|
366
368
|
const vendor = (await manifestOf(pack)).vendor ?? basename(pack);
|
|
367
|
-
|
|
369
|
+
const clients = join(dirname(fileURLToPath(standardPackageFile('package.json'))), 'src', 'clients');
|
|
370
|
+
if (!existsSync(join(clients, `${vendor}.ts`))) {
|
|
368
371
|
// a vendor whose descriptor names npm clients (adoption.sdks) is held to them: no case is no proof the SDK works,
|
|
369
372
|
// unless the descriptor says why none is owed (clientCasesNone: no official SDK, or none the demand's applications
|
|
370
373
|
// use); one an application calls with no SDK of the vendor's (a raw HTTP catalog) has no client to drive
|
|
@@ -396,10 +399,10 @@ async function clientCases(pack: string, scratch: string): Promise<{ failures: s
|
|
|
396
399
|
for (const [name, field] of Object.entries(door.fill)) if (answer[field] !== undefined) issued[name] = String(answer[field]);
|
|
397
400
|
}
|
|
398
401
|
const job = join(scratch, 'client-job.json');
|
|
399
|
-
writeFileSync(job, JSON.stringify({ vendor, port: server.port, host, env: issued, scenarioPath }));
|
|
402
|
+
writeFileSync(job, JSON.stringify({ vendor, port: server.port, host, env: issued, scenarioPath, packFacts: join(pack, 'generated', 'pack-facts.json') }));
|
|
400
403
|
// Node, as an application runs its SDK (asynchronous: this process serves the pack meanwhile)
|
|
401
404
|
const out = await new Promise<string>((resolve) => {
|
|
402
|
-
const child = spawn('node', ['--experimental-strip-types', '--no-warnings', join(
|
|
405
|
+
const child = spawn('node', ['--experimental-strip-types', '--no-warnings', join(clients, 'run.ts'), job], { stdio: ['ignore', 'pipe', 'pipe'] });
|
|
403
406
|
let text = '';
|
|
404
407
|
let errors = '';
|
|
405
408
|
child.stdout.on('data', (d: Buffer) => { text += d; });
|
|
@@ -483,9 +483,9 @@ export async function walk(journey: Journey, root: string, collect: Failure[], o
|
|
|
483
483
|
// they arrive (the steps' settle waits until none has come for a moment)
|
|
484
484
|
const wire = new Map<string, WebSocket>();
|
|
485
485
|
const settleWire = async (c: Socket, step?: SocketStep): Promise<void> => {
|
|
486
|
-
const deadline =
|
|
486
|
+
const deadline = performance.now() + 10_000;
|
|
487
487
|
let seen = -1;
|
|
488
|
-
while (
|
|
488
|
+
while (performance.now() < deadline) {
|
|
489
489
|
const body = { frames: c.frames, last: c.frames.at(-1) ?? null, closed: c.closed };
|
|
490
490
|
let expected = true;
|
|
491
491
|
try {
|