@volter/twin-standard 1.0.58 → 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.
@@ -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 context = clientContext(await worldProxy(job.port), job.host, job.env, job.scenarioPath);
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}`]),
@@ -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 = Date.now(); sink.write(bytes); });
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 = Date.now();
344
+ const start = performance.now();
343
345
  const since = heard;
344
- while (Date.now() - start < 10_000) {
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 ? Date.now() - heard >= 50 : Date.now() - start >= 500)
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
- if (!existsSync(join(import.meta.dir, 'clients', `${vendor}.ts`))) {
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(import.meta.dir, 'clients', 'run.ts'), job], { stdio: ['ignore', 'pipe', 'pipe'] });
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 = Date.now() + 10_000;
648
+ const deadline = performance.now() + 10_000;
649
649
  let seen = -1;
650
- while (Date.now() < deadline) {
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.58",
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.66",
78
+ "@volter/world-core": "3.0.68",
79
79
  "@volter/world-runtime": "*",
80
80
  "playwright": "^1.63.0"
81
81
  },
@@ -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 context = clientContext(await worldProxy(job.port), job.host, job.env, job.scenarioPath);
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}`]),
@@ -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 = Date.now(); sink.write(bytes); });
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 = Date.now(); const since = heard;
309
- while (Date.now() - start < 10_000) {
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 ? Date.now() - heard >= 50 : Date.now() - start >= 500) return;
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
- if (!existsSync(join(import.meta.dir, 'clients', `${vendor}.ts`))) {
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(import.meta.dir, 'clients', 'run.ts'), job], { stdio: ['ignore', 'pipe', 'pipe'] });
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 = Date.now() + 10_000;
486
+ const deadline = performance.now() + 10_000;
487
487
  let seen = -1;
488
- while (Date.now() < deadline) {
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 {