@volter/twin-standard 1.0.159 → 1.0.160
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/check-sources.js +2 -2
- package/dist/src/clients/resend.first-use.json +2 -2
- package/dist/src/conformance.d.ts +1 -4
- package/dist/src/conformance.js +4 -19
- package/dist/src/create-pack.d.ts +1 -1
- package/dist/src/create-pack.js +13 -12
- package/dist/src/derive.js +3 -3
- package/dist/src/index.d.ts +1 -1
- package/dist/src/index.js +1 -1
- package/dist/src/pack-facts.d.ts +3 -0
- package/dist/src/pack-facts.js +36 -1
- package/dist/src/protocol-3.js +5 -5
- package/dist/src/spec-ir-client.js +1 -1
- package/dist/src/spec-ir-commands.js +1 -1
- package/dist/src/spec-ir-lines.js +1 -1
- package/dist/src/spec-ir-proto.d.ts +1 -1
- package/dist/src/spec-patches.js +2 -2
- package/dist/src/tools/judge-brief.js +1 -2
- package/dist/src/tools/life-coverage.js +2 -2
- package/dist/src/tools/packs-root.js +3 -3
- package/dist/src/tools/score-pack.js +1 -2
- package/dist/src/tools/vendor-examples.js +4 -5
- package/dist/src/verdict.js +1 -1
- package/dist/src/walk/behavior-journey.js +18 -1
- package/dist/src/walk/journey-kit.d.ts +2 -2
- package/dist/src/walk/serve-pack.js +6 -0
- package/dist/src/walk/shape.d.ts +1 -1
- package/dist/src/walk/shape.js +1 -1
- package/package.json +6 -4
- package/src/check-sources.ts +2 -2
- package/src/clients/resend.first-use.json +2 -2
- package/src/conformance.ts +4 -19
- package/src/create-pack.ts +13 -12
- package/src/derive.ts +3 -3
- package/src/index.ts +1 -1
- package/src/pack-facts.ts +25 -2
- package/src/protocol-3.ts +5 -5
- package/src/spec-ir-client.ts +1 -1
- package/src/spec-ir-commands.ts +1 -1
- package/src/spec-ir-lines.ts +1 -1
- package/src/spec-ir-proto.ts +1 -1
- package/src/spec-patches.ts +2 -2
- package/src/tools/judge-brief.ts +1 -2
- package/src/tools/life-coverage.ts +2 -2
- package/src/tools/packs-root.ts +3 -3
- package/src/tools/score-pack.ts +1 -2
- package/src/tools/vendor-examples.ts +4 -5
- package/src/verdict.ts +1 -1
- package/src/walk/behavior-journey.ts +14 -1
- package/src/walk/journey-kit.ts +2 -2
- package/src/walk/serve-pack.ts +5 -0
- package/src/walk/shape.ts +1 -1
|
@@ -10,13 +10,13 @@ var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExte
|
|
|
10
10
|
// check-sources — the evidence a derived pack cites, fetched (docs/contributing/architecture.md,
|
|
11
11
|
// "Protocol 3", Correcting the spec and The semantics layer). Every URL a state transition's `source`,
|
|
12
12
|
// a handler's `// source:` line or a spec patch's `why` names is fetched once and its answer recorded in `spec/sources.json`; a patch's
|
|
13
|
-
// `quote` is looked for in the text of the page its `source` names.
|
|
13
|
+
// `quote` is looked for in the text of the page its `source` names. the derivation reads the record offline
|
|
14
14
|
// and refuses a dead source, one never checked, and a quote its page does not hold.
|
|
15
15
|
//
|
|
16
16
|
// Documentation pages, never a vendor's API: each is fetched at most once per run, sixteen at a time,
|
|
17
17
|
// and a page already recorded as found is not fetched again (--recheck fetches everything).
|
|
18
18
|
//
|
|
19
|
-
// Usage: twin-standard check-sources <pack dir> [--recheck]
|
|
19
|
+
// Usage: twin-standard check-sources <pack dir> [--recheck]
|
|
20
20
|
import { existsSync, readdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
21
21
|
import { dirname, join } from 'node:path';
|
|
22
22
|
import { unitAt } from "./unit.js";
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": 1,
|
|
3
3
|
"source": "https://www.npmjs.com/package/resend/v/6.6.0",
|
|
4
|
-
"about": "Send a synthetic sign-in email through the official SDK and inspect the retained message
|
|
4
|
+
"about": "Send a synthetic sign-in email through the official SDK and inspect the retained message after restart. Advance the frozen World clock to settle synthetic delivery before retained-state inspection.",
|
|
5
5
|
"files": {
|
|
6
6
|
"package.json": "{\n \"name\": \"resend-installed-customer\",\n \"private\": true,\n \"type\": \"module\",\n \"dependencies\": {\n \"resend\": \"6.6.0\"\n }\n}\n",
|
|
7
7
|
".env.example": "RESEND_API_KEY=\n",
|
|
8
|
-
"inspect-email.mjs": "import assert from 'node:assert/strict';\nimport { readFileSync } from 'node:fs';\nimport { Resend } from 'resend';\n\nassert.ok(process.env.VOLTER_WORLD, 'run this script through the World');\nconst { id } = JSON.parse(readFileSync('last-email.json', 'utf8'));\nconst resend = new Resend(process.env.RESEND_API_KEY);\nconst saved = await resend.emails.get(id);\nassert.equal(saved.error, null);\nassert.equal(saved.data.last_event, 'delivered');\
|
|
8
|
+
"inspect-email.mjs": "import assert from 'node:assert/strict';\nimport { readFileSync } from 'node:fs';\nimport { Resend } from 'resend';\n\nassert.ok(process.env.VOLTER_WORLD, 'run this script through the World');\nconst { id } = JSON.parse(readFileSync('last-email.json', 'utf8'));\nconst resend = new Resend(process.env.RESEND_API_KEY);\nconst saved = await resend.emails.get(id);\nassert.equal(saved.error, null);\nassert.equal(saved.data.last_event, 'delivered');\nassert.equal(saved.data.subject, 'Your local sign-in code');\nassert.match(saved.data.text, /123456/);\nassert.match(saved.data.html, /<strong>123456<\\/strong>/);\nconsole.log('the retained email reads back with its sign-in code');\n",
|
|
9
9
|
"send-email.mjs": "import assert from 'node:assert/strict';\nimport { writeFileSync } from 'node:fs';\nimport { Resend } from 'resend';\n\nassert.ok(process.env.VOLTER_WORLD, 'run this script through the World');\nconst resend = new Resend(process.env.RESEND_API_KEY);\nconst email = {\n from: 'Example app <onboarding@resend.dev>',\n to: ['owner@world.test'],\n subject: 'Your local sign-in code',\n text: 'Your synthetic sign-in code is 123456.',\n html: '<p>Your synthetic sign-in code is <strong>123456</strong>.</p>'\n};\nconst sent = await resend.emails.send(email);\nassert.equal(sent.error, null);\nassert.ok(sent.data.id);\nconst saved = await resend.emails.get(sent.data.id);\nassert.equal(saved.error, null);\nassert.equal(saved.data.id, sent.data.id);\nassert.equal(saved.data.subject, email.subject);\nassert.equal(saved.data.text, email.text);\nassert.deepEqual(saved.data.to, email.to);\nwriteFileSync('last-email.json', JSON.stringify({ id: sent.data.id }) + '\\n');\nconsole.log('email sent and read back locally');\n"
|
|
10
10
|
},
|
|
11
11
|
"run": [
|
|
@@ -6,13 +6,10 @@ export type ConformanceResult = {
|
|
|
6
6
|
note?: string;
|
|
7
7
|
};
|
|
8
8
|
/** The files conformance reads from a unit: its manifest, its spec's record, its vendor's life and its decision table
|
|
9
|
-
* (a lane's journeys are its own `journeys/` or else its vendor's). A release publishes each (published())
|
|
10
|
-
* pack's standing counts it in the form only when every unit holds them (twin-world's scripts/pack-standing.ts, by
|
|
11
|
-
* unitFilesMissing): the one list of both. */
|
|
9
|
+
* (a lane's journeys are its own `journeys/` or else its vendor's). A release publishes each (published()). */
|
|
12
10
|
export declare const UNIT_FILES: readonly ["src/manifest.ts", "spec/SOURCE.md", "journeys/customer-life.json", "journeys/decisions.json"];
|
|
13
11
|
/** The published package must carry every conformance input its units hold, including lanes. */
|
|
14
12
|
export declare function unpublishedUnitFiles(unit: string): string[];
|
|
15
|
-
export declare function unitFilesMissing(unit: string): string[];
|
|
16
13
|
type Owners = Record<string, string>;
|
|
17
14
|
type Decision = {
|
|
18
15
|
operation: string;
|
package/dist/src/conformance.js
CHANGED
|
@@ -64,9 +64,7 @@ async function credentialFields(server, door, host) {
|
|
|
64
64
|
return { values: fields, outputs };
|
|
65
65
|
}
|
|
66
66
|
/** The files conformance reads from a unit: its manifest, its spec's record, its vendor's life and its decision table
|
|
67
|
-
* (a lane's journeys are its own `journeys/` or else its vendor's). A release publishes each (published())
|
|
68
|
-
* pack's standing counts it in the form only when every unit holds them (twin-world's scripts/pack-standing.ts, by
|
|
69
|
-
* unitFilesMissing): the one list of both. */
|
|
67
|
+
* (a lane's journeys are its own `journeys/` or else its vendor's). A release publishes each (published()). */
|
|
70
68
|
export const UNIT_FILES = ['src/manifest.ts', 'spec/SOURCE.md', 'journeys/customer-life.json', 'journeys/decisions.json'];
|
|
71
69
|
/** The published package must carry every conformance input its units hold, including lanes. */
|
|
72
70
|
export function unpublishedUnitFiles(unit) {
|
|
@@ -80,8 +78,6 @@ export function unpublishedUnitFiles(unit) {
|
|
|
80
78
|
return !patterns.some((p) => !p.exclude && matches(p.pattern)) || patterns.some((p) => p.exclude && matches(p.pattern));
|
|
81
79
|
}).map((f) => `${f} is absent from package.json files (conformance reads it)`);
|
|
82
80
|
}
|
|
83
|
-
/** Each of UNIT_FILES a unit lacks where conformance reads it: the manifest of every unit, the life at its vendor's root
|
|
84
|
-
* (lifeServed), the spec's record and the decision table of a unit with a spec (decided()). */
|
|
85
81
|
const trackedJourneySources = new Map();
|
|
86
82
|
/** Table ownership follows one index snapshot per checkout/run. */
|
|
87
83
|
function hasJourneySource(unit) {
|
|
@@ -117,17 +113,6 @@ function hasJourneySource(unit) {
|
|
|
117
113
|
const prefix = relative(checkout, dir).replaceAll('\\', '/') + '/';
|
|
118
114
|
return source.some(path => path.startsWith(prefix));
|
|
119
115
|
}
|
|
120
|
-
export function unitFilesMissing(unit) {
|
|
121
|
-
const vendor = existsSync(join(unit, 'package.json')) ? unit : dirname(unit);
|
|
122
|
-
const spec = hasOwnSurface(unit);
|
|
123
|
-
const at = {
|
|
124
|
-
'src/manifest.ts': unit,
|
|
125
|
-
'spec/SOURCE.md': spec ? unit : undefined,
|
|
126
|
-
'journeys/customer-life.json': vendor,
|
|
127
|
-
'journeys/decisions.json': spec ? (hasJourneySource(unit) ? unit : vendor) : undefined,
|
|
128
|
-
};
|
|
129
|
-
return UNIT_FILES.filter((f) => at[f] !== undefined && !existsSync(join(at[f], f))).map((f) => `${relative(unit, join(at[f], f))} is missing (conformance reads it)`);
|
|
130
|
-
}
|
|
131
116
|
/** A unit's fetch export: a pack's `create<Vendor>Fetch`, a lane's `create<Lane>LaneFetch`. */
|
|
132
117
|
const fetchName = (dir) => (existsSync(join(dir, 'package.json')) ? `create${pascal(basename(dir))}Fetch` : `create${pascal(basename(dir))}LaneFetch`);
|
|
133
118
|
const manifestOf = async (unit) => (await import(__rewriteRelativeImportExtension(join(unit, 'src', 'manifest.ts')))).manifest;
|
|
@@ -273,7 +258,7 @@ async function cited(pack) {
|
|
|
273
258
|
for (const [url, quotes] of await citations(unit)) {
|
|
274
259
|
const r = record[url];
|
|
275
260
|
if (!r) {
|
|
276
|
-
out.push(`${nameOf(pack, unit)}: ${url} is cited and not recorded (
|
|
261
|
+
out.push(`${nameOf(pack, unit)}: ${url} is cited and not recorded (twin-standard check-sources ${nameOf(pack, unit)})`);
|
|
277
262
|
continue;
|
|
278
263
|
}
|
|
279
264
|
if (!sourceHeld(url, r))
|
|
@@ -897,8 +882,8 @@ async function clientCases(pack, scratch, consumer) {
|
|
|
897
882
|
Object.assign(issued, (await credentialFields(server, door, host)).outputs);
|
|
898
883
|
}
|
|
899
884
|
const job = join(scratch, 'client-job.json');
|
|
900
|
-
// a
|
|
901
|
-
//
|
|
885
|
+
// a pack carries its facts (generated/pack-facts.json, `twin-standard facts`), in source and release alike; one that
|
|
886
|
+
// has none yet has them compiled here, as that command compiles them, for the client the case runs
|
|
902
887
|
let packFacts = join(pack, 'generated', 'pack-facts.json');
|
|
903
888
|
if (!existsSync(packFacts)) {
|
|
904
889
|
const { compilePackFacts, renderPackFacts } = await import("./pack-facts.js");
|
|
@@ -25,7 +25,7 @@ export declare function brief(unit: string): Promise<string>;
|
|
|
25
25
|
export declare function packIndexFile(dir: string, vendor: string): string;
|
|
26
26
|
/** A pack's `src/server.ts` (a fixed file), the kernel's serve seam around its fetch, naming only what its unit has so
|
|
27
27
|
* it typechecks and a bundler follows every import: the plain seam (serveHttp, which takes the sockets a fetch carries
|
|
28
|
-
* itself: "Other wires"), or, for a unit whose spec is a proto, the gRPC seam over the schema
|
|
28
|
+
* itself: "Other wires"), or, for a unit whose spec is a proto, the gRPC seam over the schema the derivation generated
|
|
29
29
|
* (src/generated/proto.gen.json, imported by its literal path). `--init` writes the plain one, `--index` the gRPC one once
|
|
30
30
|
* the unit's proto is derived. */
|
|
31
31
|
export declare function serverFile(vendor: string, grpc: boolean, wire?: boolean, live?: boolean): string;
|
package/dist/src/create-pack.js
CHANGED
|
@@ -10,8 +10,9 @@ var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExte
|
|
|
10
10
|
// create-pack — the files of a Protocol 3 pack that nobody should write (the board's `create-pack` card;
|
|
11
11
|
// docs/contributing/architecture.md, "Creating a pack"), and the brief an author writes the rest from.
|
|
12
12
|
//
|
|
13
|
-
//
|
|
14
|
-
//
|
|
13
|
+
// twin-standard create <dir> --init <vendor> --package <@scope/name>
|
|
14
|
+
// write every fixed file of a new pack that does not exist yet
|
|
15
|
+
// twin-standard create <dir> --init <vendor> --package <@scope/name> --lanes a,b
|
|
15
16
|
// the same for a pack of lanes, one per API of the vendor, each
|
|
16
17
|
// derived from its own spec (<dir>/<lane>/spec): each lane's
|
|
17
18
|
// manifest, fetch, index and semantics, and the pack's server
|
|
@@ -20,11 +21,11 @@ var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExte
|
|
|
20
21
|
// index}.ts, src/semantics/{states,shared,doors}.ts,
|
|
21
22
|
// journeys/{demand,first-use}.json): the author fills the manifest's
|
|
22
23
|
// vendor facts, the descriptor's hosts and the families
|
|
23
|
-
//
|
|
24
|
+
// twin-standard create <unit> --index write src/semantics/index.ts, the handler map, and src/fetch.ts from the
|
|
24
25
|
// families' files (every exported function, by its name)
|
|
25
|
-
//
|
|
26
|
+
// twin-standard create <unit> --index --building the same during construction; unfinished operations stay gaps,
|
|
26
27
|
// and implemented operations must match their decisions
|
|
27
|
-
//
|
|
28
|
+
// twin-standard create <unit> --brief [--out f] the author's brief: every operation the derived core does not
|
|
28
29
|
// serve and no handler answers yet, by family, with what the
|
|
29
30
|
// vendored spec says of it (summary, description, parameters,
|
|
30
31
|
// the answers it documents and their examples), so a handler
|
|
@@ -75,7 +76,7 @@ export function handlerIndex(dir) {
|
|
|
75
76
|
// an index written before the derive has no operations to map, and every served call would fall to the gap (404): refuse
|
|
76
77
|
// it rather than write an empty map (2026-09-30: six packs committed so)
|
|
77
78
|
if (!existsSync(surfacePath) && fams.length)
|
|
78
|
-
throw new Error(`${dir}: no src/generated/surface.gen.json yet; derive the unit (
|
|
79
|
+
throw new Error(`${dir}: no src/generated/surface.gen.json yet; derive the unit (twin-standard derive) before --index`);
|
|
79
80
|
const where = new Map(fams.flatMap((f) => f.handlers.map((h) => [h, f.family])));
|
|
80
81
|
const named = new Set([...new Set(ids)].map(handlerName));
|
|
81
82
|
const stray = [...where.keys()].filter((h) => !named.has(h));
|
|
@@ -278,7 +279,7 @@ export async function brief(unit) {
|
|
|
278
279
|
const s = spec.get(o.id);
|
|
279
280
|
lines.push(`### ${o.id}${unmodeled.has(o.id) ? ' (unmodeled)' : ''}`, '', `\`${o.method.toUpperCase()} ${o.path}\` — ${o.class}${s?.summary ? `: ${oneLine(s.summary)}` : ''}`);
|
|
280
281
|
// the citation a handler carries, ready to paste: by operationId where the spec names one, else by its JSON pointer,
|
|
281
|
-
// quoting the spec's own words (
|
|
282
|
+
// quoting the spec's own words (the derivation refuses a quote the spec does not hold)
|
|
282
283
|
const words = oneLine(s?.summary ?? s?.description ?? '').split(/(?<=\.)\s/)[0].replace(/"/g, '\\"');
|
|
283
284
|
const cite = s && s.operationId === o.id ? `spec:${o.id}` : `spec:/paths/${o.path.replace(/~/g, '~0').replace(/\//g, '~1')}/${o.method.toLowerCase()}`;
|
|
284
285
|
if (words)
|
|
@@ -343,7 +344,7 @@ export function packIndexFile(dir, vendor) {
|
|
|
343
344
|
}
|
|
344
345
|
/** A pack's `src/server.ts` (a fixed file), the kernel's serve seam around its fetch, naming only what its unit has so
|
|
345
346
|
* it typechecks and a bundler follows every import: the plain seam (serveHttp, which takes the sockets a fetch carries
|
|
346
|
-
* itself: "Other wires"), or, for a unit whose spec is a proto, the gRPC seam over the schema
|
|
347
|
+
* itself: "Other wires"), or, for a unit whose spec is a proto, the gRPC seam over the schema the derivation generated
|
|
347
348
|
* (src/generated/proto.gen.json, imported by its literal path). `--init` writes the plain one, `--index` the gRPC one once
|
|
348
349
|
* the unit's proto is derived. */
|
|
349
350
|
export function serverFile(vendor, grpc, wire = false, live = false) {
|
|
@@ -443,7 +444,7 @@ export function initFiles(vendor) {
|
|
|
443
444
|
'src/semantics/states.ts': `// ${vendor}'s state machines and state rulings, each transition cited (architecture, "State").\nexport const states = {};\n`,
|
|
444
445
|
'src/semantics/shared.ts': `// What every family of ${vendor} shares: its stored shapes, who is calling, and the vendor's answers.\nimport type { HandlerContext } from '@volter/world-core';\n\nexport type Row = Record<string, unknown>;\nexport type { HandlerContext };\n`,
|
|
445
446
|
'src/semantics/doors.ts': `// The World's doors (architecture, "Doors, screens and the gap"): what the vendor's own pages do that its API does\n// not, each declared in the manifest's \`doors\` (its id, method and path under \`/_twin/\`) and answered here by the\n// export its id names, over the contract's context.\nimport type { HandlerContext } from '@volter/world-core';\n\nexport type { HandlerContext };\n`,
|
|
446
|
-
'src/manifest.ts': `// ${vendor}'s manifest: the vendor facts its spec does not carry (architecture, "Protocol 3"). The surface is generated\n// (./generated/surface.gen.json, from ../spec by derive
|
|
447
|
+
'src/manifest.ts': `// ${vendor}'s manifest: the vendor facts its spec does not carry (architecture, "Protocol 3"). The surface is generated\n// (./generated/surface.gen.json, from ../spec by twin-standard derive). Every line marked FILL is a vendor fact to state from\n// the vendor's pages, cited.\nimport type { DerivedManifest } from '@volter/world-core';\n\nexport const manifest: DerivedManifest = {\n vendor: '${vendor}',\n service: '${vendor}',\n body: { json: 'always' },\n ids: { template: '{uuid}' }, // FILL: the vendor's id form\n time: 'iso', // FILL: iso, unix or unix-ms\n error: { error: { message: '{message}', code: '{code}' } }, // FILL: the vendor's error body\n readOnly: { status: 403, message: 'This twin was started read-only; writes are refused.' }, // FILL: the vendor's refusal\n notFound: { status: 404, message: 'Not found' }, // FILL: the vendor's not-found\n list: { style: 'envelope', envelope: { data: '{data}' }, limit: { param: 'limit', default: 20, max: 100 } }, // FILL: its paging\n deleted: {},\n resources: {}, // FILL: the resources the core serves (plain create, read, update, delete, list)\n discovery: { twinOf: 'FILL: what the twin is of', stores: 'FILL: what it keeps' },\n // the descriptor, as data (twin-world's architecture, "The descriptor"): the pack registers packOf(manifest)\n descriptor: {\n protocol: '3',\n transport: 'rest',\n archetype: 'crud',\n bin: 'world-${vendor}',\n resources: [], // FILL: the stored types\n specSource: 'FILL: the vendored spec (spec/SOURCE.md)',\n description: 'FILL: one line',\n adoption: { sdks: [], envStems: [] }, // FILL: its npm clients, and each credential variable's stem (TRIPO for TRIPO_API_KEY: the suffix removed)\n hosts: [], // FILL: the vendor's hosts\n endpointEnvNone: 'FILL: the SDK\\'s base-URL variable (endpointEnv) or why none',\n },\n};\n`,
|
|
447
448
|
'src/server.ts': serverFile(vendor, false),
|
|
448
449
|
'src/cli.ts': cliFile(vendor),
|
|
449
450
|
'src/index.ts': `// @volter/twin-${vendor} — the ${vendor} twin (Protocol 3). A fixed file: the descriptor is the manifest's.\nimport { packOf, registerPack } from '@volter/world-core';\nimport { manifest } from './manifest.ts';\n\nexport { create${Name}Fetch } from './fetch.ts';\nexport { create${Name}TwinServer } from './server.ts';\nexport { manifest };\n\n// The descriptor: twin-world's architecture, "The descriptor"; declared in manifest.ts.\nexport const pack = registerPack(packOf(manifest));\n`,
|
|
@@ -522,7 +523,7 @@ export async function fixedFiles(dir) {
|
|
|
522
523
|
throw new Error('Pack manifest must declare its vendor identity independently of the npm package name');
|
|
523
524
|
files['src/index.ts'] = packIndexFile(dir, vendor);
|
|
524
525
|
if (!existsSync(join(dir, 'src', 'stream.ts'))) {
|
|
525
|
-
// a unit whose spec is a proto (
|
|
526
|
+
// a unit whose spec is a proto (the derivation generated its schema) is served over gRPC: its serve seam in that form
|
|
526
527
|
const lanesOnly = !hasOwnSurface(dir) && readdirSync(dir).some((n) => existsSync(join(dir, n, 'src', 'manifest.ts')));
|
|
527
528
|
const grpc = !lanesOnly && existsSync(join(dir, 'src', 'generated', 'proto.gen.json'));
|
|
528
529
|
// a unit that takes broadcasts over RTMP (its manifest's `live`) has the live forms --index writes, so they reproduce
|
|
@@ -535,7 +536,7 @@ export async function fixedFiles(dir) {
|
|
|
535
536
|
export async function createPack(args) {
|
|
536
537
|
const [unit, ...rest] = args;
|
|
537
538
|
if (!unit || (!rest.includes('--index') && !rest.includes('--brief') && !rest.includes('--init'))) {
|
|
538
|
-
console.error('usage:
|
|
539
|
+
console.error('usage: twin-standard create <dir> --init <vendor> --package <@scope/name> | <unit> --index [--building] | --brief [--out <file>]');
|
|
539
540
|
process.exit(2);
|
|
540
541
|
}
|
|
541
542
|
if (rest.includes('--building') && (!rest.includes('--index') || rest.includes('--init') || rest.includes('--brief'))) {
|
|
@@ -642,7 +643,7 @@ export async function createPack(args) {
|
|
|
642
643
|
writeFileSync(join(dir, 'tsconfig.json'), TSCONFIG);
|
|
643
644
|
console.log(`create-pack: wrote ${join(dir, 'tsconfig.json')}`);
|
|
644
645
|
}
|
|
645
|
-
// a unit whose spec is a proto (
|
|
646
|
+
// a unit whose spec is a proto (the derivation generated its schema) is served over gRPC: its serve seam in that form,
|
|
646
647
|
// the one form a unit takes after --init (a server.ts of a wire of the pack's own, SMTP's, is never written over)
|
|
647
648
|
if (!lanesOnly && existsSync(join(dir, 'src', 'generated', 'proto.gen.json'))) {
|
|
648
649
|
const wire = existsSync(join(dir, 'src', 'stream.ts'));
|
package/dist/src/derive.js
CHANGED
|
@@ -13,7 +13,7 @@ import { parseExactJson, exactInteger } from '@volter/world-core/exact-json';
|
|
|
13
13
|
// vendored spec (`<pack>/spec/openapi.json[.gz]`), builds the spec IR (./spec-ir.ts) and writes the pack's
|
|
14
14
|
// `src/generated/`. Deterministic: a second run over the same spec writes the same bytes. Never hand-edit what it writes.
|
|
15
15
|
//
|
|
16
|
-
// Usage: twin-standard derive <pack dir> [--out <dir>]
|
|
16
|
+
// Usage: twin-standard derive <pack dir> [--out <dir>]
|
|
17
17
|
import { parse as parseYaml } from 'yaml';
|
|
18
18
|
import { existsSync, mkdirSync, readdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
19
19
|
import { basename, join } from 'node:path';
|
|
@@ -47,7 +47,7 @@ const PKG = unitAt(vendor).dir;
|
|
|
47
47
|
const rfc = existsSync(join(PKG, 'spec')) ? readdirSync(join(PKG, 'spec')).find((f) => /^rfc\d+\.txt$/.test(f)) : undefined;
|
|
48
48
|
const specPath = ['openapi.json.gz', 'openapi.json', 'openapi.yaml.gz', 'openapi.yaml', 'smithy.json.gz', 'smithy.json', 'discovery.json.gz', 'discovery.json', ...(rfc ? [rfc] : []), 'openapi', 'proto', 'commands', 'command-builders.json', 'client-ops.json'].map((f) => join(PKG, 'spec', f)).find((p) => existsSync(p));
|
|
49
49
|
if (!specPath) {
|
|
50
|
-
console.error(`derive
|
|
50
|
+
console.error(`derive: no spec at ${join(PKG, 'spec')}/openapi.{json,yaml}[.gz], smithy.json[.gz], rfcNNNN.txt, proto/, commands/, command-builders.json or client-ops.json`);
|
|
51
51
|
process.exit(SOURCE_REFUSAL);
|
|
52
52
|
}
|
|
53
53
|
const rpcService = basename(specPath) === 'proto';
|
|
@@ -343,7 +343,7 @@ if (existsSync(manifestPath)) {
|
|
|
343
343
|
}
|
|
344
344
|
// a resource's refresh scope ("The real-system adapters", What a pack declares) names the list that enumerates it or
|
|
345
345
|
// its retrieve, or says why the vendor offers no read-back; whether every stored resource declares one is
|
|
346
|
-
//
|
|
346
|
+
// not this build's to decide
|
|
347
347
|
// (a resource the spec names is read by its own list or retrieve; one the manifest names apart from the spec's, an RPC
|
|
348
348
|
// vendor's, by the list or read the scope names, with `items` where the answer holds them when the spec does not say)
|
|
349
349
|
const specNamed = new Set(ir.resources.map((r) => r.name));
|
package/dist/src/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { grade, STANDARDS, type CriterionGrade, type GradeOptions, type StandardGrade } from './grade.js';
|
|
2
|
-
export { conformance, decisionTableProblems, type ConformanceResult, UNIT_FILES
|
|
2
|
+
export { conformance, decisionTableProblems, type ConformanceResult, UNIT_FILES } from './conformance.js';
|
|
3
3
|
export { laneRootFailures, p3Gate, unitsOf } from './gate.js';
|
|
4
4
|
export { conformPublished, gatePublished, vendorOfPackage, type PublishedGate } from './published.js';
|
|
5
5
|
export { handlerName, protocol3, PLANNED, withCheckoutFiles } from './protocol-3.js';
|
package/dist/src/index.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
// of a submission — judges by the same standard at the version it pins. Changing a criterion is a release of this
|
|
5
5
|
// package: a criterion's id never changes meaning, a changed criterion takes a new id and a new standard version.
|
|
6
6
|
export { grade, STANDARDS } from "./grade.js";
|
|
7
|
-
export { conformance, decisionTableProblems, UNIT_FILES
|
|
7
|
+
export { conformance, decisionTableProblems, UNIT_FILES } from "./conformance.js";
|
|
8
8
|
export { laneRootFailures, p3Gate, unitsOf } from "./gate.js";
|
|
9
9
|
export { conformPublished, gatePublished, vendorOfPackage } from "./published.js";
|
|
10
10
|
export { handlerName, protocol3, PLANNED, withCheckoutFiles } from "./protocol-3.js";
|
package/dist/src/pack-facts.d.ts
CHANGED
|
@@ -29,6 +29,7 @@ export type PackFacts = {
|
|
|
29
29
|
demandPathPattern?: string | false;
|
|
30
30
|
key?: string;
|
|
31
31
|
exclude?: true;
|
|
32
|
+
basic?: string;
|
|
32
33
|
}>;
|
|
33
34
|
hostsNone?: string;
|
|
34
35
|
hostsClaimed?: {
|
|
@@ -104,6 +105,8 @@ export type PackFacts = {
|
|
|
104
105
|
scheme?: string;
|
|
105
106
|
note: string;
|
|
106
107
|
}>;
|
|
108
|
+
/** The vendor's own requests a World's generated seed makes (see TwinPack.seed). */
|
|
109
|
+
seed?: TwinPack['seed'];
|
|
107
110
|
/** DERIVED, never declared: the pack's `create<Name>TwinServer` factory export — the
|
|
108
111
|
* `({port,root,readOnly}) => {port,stop}` contract the colocated host mounts
|
|
109
112
|
* (world-runtime/src/host.ts). Read off the module's own exports because the name is not
|
package/dist/src/pack-facts.js
CHANGED
|
@@ -42,6 +42,8 @@ function validateHostRules(vendor, rules, vendors, claimedKeys, worldIds) {
|
|
|
42
42
|
}
|
|
43
43
|
if (rule.exclude !== undefined && rule.exclude !== true)
|
|
44
44
|
throw new Error(`pack-facts: ${vendor} host rule ${JSON.stringify(rule)} — exclude is \`true\` or absent, never a falsy flag`);
|
|
45
|
+
if (rule.basic !== undefined && (typeof rule.basic !== 'string' || !/^[^:\s]+$/.test(rule.basic) || rule.exclude))
|
|
46
|
+
throw new Error(`pack-facts: ${vendor} host rule ${JSON.stringify(rule)} — basic names a user (no colon, no space) on an included host`);
|
|
45
47
|
const key = rule.key ?? vendor;
|
|
46
48
|
// A foreign key is a WORLD identity too: `volter-world init` emits <KEY>_TWIN_URL only for the
|
|
47
49
|
// ids in adoption.worldIds (covers.ts injectorVendorKeysFor), so a key not listed there would
|
|
@@ -230,6 +232,39 @@ export async function compilePackFacts(dirs) {
|
|
|
230
232
|
}
|
|
231
233
|
facts.sidePorts = pack.sidePorts;
|
|
232
234
|
}
|
|
235
|
+
if (pack.seed !== undefined) {
|
|
236
|
+
// ADR 0033 §3: one door per kind of fact, each the vendor's own request at its own host; only a domain's DNS record
|
|
237
|
+
// is a `/_twin/` stand-in
|
|
238
|
+
const seed = pack.seed;
|
|
239
|
+
const door = (value, what, standIn = false) => {
|
|
240
|
+
let url;
|
|
241
|
+
try {
|
|
242
|
+
url = new URL(String(value));
|
|
243
|
+
}
|
|
244
|
+
catch {
|
|
245
|
+
throw new Error(`pack-facts: ${vendor} seed ${what} "${String(value)}" is not a URL`);
|
|
246
|
+
}
|
|
247
|
+
if (url.protocol !== 'https:')
|
|
248
|
+
throw new Error(`pack-facts: ${vendor} seed ${what} is the vendor's own https request`);
|
|
249
|
+
if (!standIn && url.pathname.startsWith('/_twin/'))
|
|
250
|
+
throw new Error(`pack-facts: ${vendor} seed ${what} is the vendor's own request, never a /_twin/ door`);
|
|
251
|
+
};
|
|
252
|
+
if (!seed || typeof seed !== 'object' || Array.isArray(seed) || Object.keys(seed).some((k) => k !== 'signUp' && k !== 'domain'))
|
|
253
|
+
throw new Error(`pack-facts: ${vendor} seed is { signUp?, domain? }`);
|
|
254
|
+
if (seed.signUp !== undefined)
|
|
255
|
+
door(seed.signUp, 'signUp');
|
|
256
|
+
if (seed.domain !== undefined) {
|
|
257
|
+
const domain = seed.domain;
|
|
258
|
+
if (!domain || typeof domain !== 'object' || Array.isArray(domain) || Object.keys(domain).sort().join() !== 'add,record,settles,verify')
|
|
259
|
+
throw new Error(`pack-facts: ${vendor} seed.domain is { add, record, verify, settles }`);
|
|
260
|
+
door(domain.add, 'domain.add');
|
|
261
|
+
door(domain.verify, 'domain.verify');
|
|
262
|
+
door(domain.record, 'domain.record', true);
|
|
263
|
+
if (typeof domain.settles !== 'string' || !/^\d+[smhd]$/.test(domain.settles))
|
|
264
|
+
throw new Error(`pack-facts: ${vendor} seed.domain.settles is a World-clock time (10m)`);
|
|
265
|
+
}
|
|
266
|
+
facts.seed = pack.seed;
|
|
267
|
+
}
|
|
233
268
|
// The serve factory is DERIVED from the module's exports (see PackFacts.serveExport);
|
|
234
269
|
// a pack exporting SEVERAL factories must declare which one `cli.ts serve` boots — the
|
|
235
270
|
// exports alone cannot reveal that judgment (linear defaults to its DERIVED server).
|
|
@@ -250,7 +285,7 @@ export async function compilePackFacts(dirs) {
|
|
|
250
285
|
packs[vendor] = facts;
|
|
251
286
|
}
|
|
252
287
|
return {
|
|
253
|
-
'//': 'GENERATED by
|
|
288
|
+
'//': 'GENERATED by twin-standard facts from the pack descriptor; no prior-artifact entries are retained.',
|
|
254
289
|
packs: sortObject(packs),
|
|
255
290
|
undeclared,
|
|
256
291
|
};
|
package/dist/src/protocol-3.js
CHANGED
|
@@ -191,7 +191,7 @@ function noReport(unit) {
|
|
|
191
191
|
/** An operation's family: the first segment of its path after a version (`/v1/organizations/{id}` is `organizations`), a
|
|
192
192
|
* dotted RPC method's first part (`chat.postMessage` is `chat`), a line command's name (architecture, "Pack layout"). */
|
|
193
193
|
function familyOf(path) {
|
|
194
|
-
// the interim rule ("What an author writes"), until
|
|
194
|
+
// the interim rule ("What an author writes"), until the derivation writes the spec's own grouping on each operation
|
|
195
195
|
const segs = path.split('/').filter((x) => x && !/^v?\d+(\.\d+)?$|^\d{4}-\d{2}-\d{2}$/.test(x) && !x.startsWith('{'));
|
|
196
196
|
if (!segs.length)
|
|
197
197
|
return 'operations'; // named in a header: every path is `/`
|
|
@@ -290,7 +290,7 @@ const criteria = [
|
|
|
290
290
|
},
|
|
291
291
|
{
|
|
292
292
|
id: 'derived-reproducible', section: 'form',
|
|
293
|
-
asks: '
|
|
293
|
+
asks: 'twin-standard derive regenerates src/generated/ byte for byte',
|
|
294
294
|
check(unit) {
|
|
295
295
|
const gen = join(unit.dir, 'src', 'generated');
|
|
296
296
|
if (!existsSync(gen))
|
|
@@ -310,10 +310,10 @@ const criteria = [
|
|
|
310
310
|
// a standing written from one checkout reads the same from any other
|
|
311
311
|
if (run.status === null || run.signal !== null || run.error)
|
|
312
312
|
return { pass: false, cacheable: false,
|
|
313
|
-
reason: `derive
|
|
313
|
+
reason: `derive interrupted: status=${run.status} signal=${run.signal} error=${run.error?.message ?? ''}` };
|
|
314
314
|
if (run.status !== 0)
|
|
315
|
-
return { ...fail(`derive
|
|
316
|
-
return changed.length ? fail(`derive
|
|
315
|
+
return { ...fail(`derive refused: ${String(run.stderr || run.stdout).split(unit.dir).join('.').split(tmp).join('<out>').trim().split('\n').slice(-2).join(' ').slice(0, 200)}`), cacheable: run.status === 2 };
|
|
316
|
+
return changed.length ? fail(`derive writes differently: ${changed.slice(0, 5).join(', ')}${changed.length > 5 ? ` and ${changed.length - 5} more` : ''}`) : pass(`${filesUnder(gen).length} files reproduced`);
|
|
317
317
|
},
|
|
318
318
|
},
|
|
319
319
|
{
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
// `spec/`, Creating a pack, step 2): its method, its URL as the client
|
|
7
7
|
// builds it (a label is the Python expression the client interpolates), the query keys it sends (literal, or the keys
|
|
8
8
|
// of the dict it urlencodes) and its body's keys, each typed by the client's own annotation of the value (`null`
|
|
9
|
-
// where the client does not say). A spec patch corrects the table as it corrects a JSON spec (derive
|
|
9
|
+
// where the client does not say). A spec patch corrects the table as it corrects a JSON spec (twin-standard derive): what the
|
|
10
10
|
// client builds as a raw string (a token's scopes) comes from the vendor's reference page that way.
|
|
11
11
|
//
|
|
12
12
|
// The calls at one method and path are one operation, their query and body keys together. The client says nothing
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// its group, the version it arrived in, its arity, its arguments, its flags and its reply's schema); a pack whose wire
|
|
4
4
|
// carries Redis commands (Upstash's REST API: a command and its arguments in the path or the body) vendors the files of
|
|
5
5
|
// the commands its vendor serves and derives its command surface from them, as a line protocol's surface is its RFC's
|
|
6
|
-
// command table (spec-ir-lines.ts). A spec patch corrects the data as it corrects a JSON spec (derive
|
|
6
|
+
// command table (spec-ir-lines.ts). A spec patch corrects the data as it corrects a JSON spec (twin-standard derive).
|
|
7
7
|
function argumentOf(a) {
|
|
8
8
|
return {
|
|
9
9
|
name: String(a.name),
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// A line protocol's spec as data: the commands a client sends and the replies each may be answered,
|
|
2
2
|
// read from the RFC's own table of them. The IR a byte-stream pack derives its surface from, as an HTTP
|
|
3
|
-
// pack derives its from an OpenAPI document; a spec patch corrects it the same way (derive
|
|
3
|
+
// pack derives its from an OpenAPI document; a spec patch corrects it the same way (twin-standard derive).
|
|
4
4
|
//
|
|
5
5
|
// SMTP's table is RFC 5321 §4.3.2 "Command-Reply Sequences": each command, then its intermediate (I),
|
|
6
6
|
// success (S) and error (E) codes, where `I: 354 -> data -> S: 250` says what answers the lines that
|
|
@@ -40,7 +40,7 @@ export type ProtoFile = {
|
|
|
40
40
|
};
|
|
41
41
|
/** One `.proto` file's declarations. */
|
|
42
42
|
export declare function parseProto(path: string, text: string): ProtoFile;
|
|
43
|
-
/** The vendored tree (path → text) as data, so a spec patch corrects it as it corrects a JSON spec (derive
|
|
43
|
+
/** The vendored tree (path → text) as data, so a spec patch corrects it as it corrects a JSON spec (twin-standard derive). */
|
|
44
44
|
export type ProtoSpec = {
|
|
45
45
|
format: 'proto';
|
|
46
46
|
files: ProtoFile[];
|
package/dist/src/spec-patches.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// A pack's corrections to its vendor's spec (spec/patches.json): RFC 6902 operations (add, replace, remove, move, copy),
|
|
2
|
-
// applied to the parsed spec so the vendored file stays the vendor's own bytes.
|
|
3
|
-
// IR;
|
|
2
|
+
// applied to the parsed spec so the vendored file stays the vendor's own bytes. the derivation applies them before the
|
|
3
|
+
// IR; `twin-standard examples` reads the patched schemas, which are what the twin is derived from.
|
|
4
4
|
/** Apply one patch to `doc` in place. */
|
|
5
5
|
export function applySpecPatch(doc, p) {
|
|
6
6
|
const at = (pointer) => {
|
|
@@ -3,8 +3,7 @@
|
|
|
3
3
|
// runtime). The verdict names its judge and the authors; the grade refuses one whose judge is an author
|
|
4
4
|
// (packages/twin-standard/src/verdict.ts). One judging per pack: the verdict stands through later changes.
|
|
5
5
|
//
|
|
6
|
-
// twin-standard judge-brief <vendor> --authors <author name>[,<agent>...] (from the pack repository's root
|
|
7
|
-
// bun scripts/judge-brief.ts runs the same)
|
|
6
|
+
// twin-standard judge-brief <vendor> --authors <author name>[,<agent>...] (from the pack repository's root)
|
|
8
7
|
import { attributionName } from "../verdict.js";
|
|
9
8
|
import { createHash } from 'node:crypto';
|
|
10
9
|
import { existsSync, readFileSync } from 'node:fs';
|
|
@@ -32,8 +32,8 @@ var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExte
|
|
|
32
32
|
// A walk that fails, and a file in scope (the pack's src/ on disk, its sharing lanes' too) that declares code yet
|
|
33
33
|
// measures no line, stop the measurement: neither is a file with nothing to reach.
|
|
34
34
|
//
|
|
35
|
-
// Usage: twin-standard coverage <vendor> [--write | --out <file>] [--prune] (from the pack repository's root
|
|
36
|
-
//
|
|
35
|
+
// Usage: twin-standard coverage <vendor> [--write | --out <file>] [--prune] (from the pack repository's root)
|
|
36
|
+
// (--prune removes the stale records) (--write keeps journeys/coverage.json)
|
|
37
37
|
import { spawnSync } from 'node:child_process';
|
|
38
38
|
import ts from 'typescript';
|
|
39
39
|
import { existsSync, mkdtempSync, readdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
// Where the contributor's tools (walk, examples, coverage, judge-brief) find the pack they are given: a vendor's directory
|
|
2
2
|
// under the pack repository they run from. A contributor runs them from their own repository's root (`twin-standard walk
|
|
3
|
-
// <vendor>`), so that root is the working directory;
|
|
4
|
-
//
|
|
5
|
-
//
|
|
3
|
+
// <vendor>`), so that root is the working directory; a tool that runs another in a process of its own (run-tool.ts)
|
|
4
|
+
// names it before loading the tool (setPacksRoot), with the kernel checkout a walk must measure (setKernelCheckout).
|
|
5
|
+
// Each tool reads both once, when it loads.
|
|
6
6
|
import { join } from 'node:path';
|
|
7
7
|
let packs = process.cwd();
|
|
8
8
|
let kernel;
|
|
@@ -12,8 +12,7 @@
|
|
|
12
12
|
// This is source-journey evidence, not installed readiness or pack completion. pack-done owns completion and consumes
|
|
13
13
|
// this report alongside the exact artifact's installed customer assessment in the one final run.
|
|
14
14
|
//
|
|
15
|
-
// Usage: twin-standard walk <vendor> [--write] [--report <path>] (from the pack repository's root
|
|
16
|
-
// bun scripts/score-pack.ts <vendor> runs the same)
|
|
15
|
+
// Usage: twin-standard walk <vendor> [--write] [--report <path>] (from the pack repository's root)
|
|
17
16
|
// --write keeps journeys/score.json and journeys/coverage.json; --report writes the report and what it lacks for done
|
|
18
17
|
// to <path> as JSON and changes nothing in the checkout
|
|
19
18
|
import { parseExactJson } from '@volter/world-core/exact-json';
|
|
@@ -43,8 +43,7 @@ var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExte
|
|
|
43
43
|
// read for. `--sweep` prints, without changing done, every actor field (creator, user, *_by, ...) no `equals` pins and every
|
|
44
44
|
// list no expect closes: advice for fixing a ruling as a class (its hits include lists a pinned text already fixes).
|
|
45
45
|
//
|
|
46
|
-
// Usage: twin-standard examples <vendor> [--sweep] (from the pack repository's root
|
|
47
|
-
// bun scripts/vendor-examples.ts <vendor> runs the same)
|
|
46
|
+
// Usage: twin-standard examples <vendor> [--sweep] (from the pack repository's root)
|
|
48
47
|
import { parseExactJson, exactInteger, isExactIntegerValue } from '@volter/world-core/exact-json';
|
|
49
48
|
import { hasOwnSurface } from "../lanes.js";
|
|
50
49
|
import { archiveSource } from "../archive-source.js";
|
|
@@ -95,7 +94,7 @@ function answerKeys(output, published, sent, placeholder, rawBindings = false) {
|
|
|
95
94
|
: value;
|
|
96
95
|
return mapped(output);
|
|
97
96
|
}
|
|
98
|
-
/** The schemas the twin serves: the vendor's, with the pack's spec patches (spec/patches.json) applied as
|
|
97
|
+
/** The schemas the twin serves: the vendor's, with the pack's spec patches (spec/patches.json) applied as the derivation
|
|
99
98
|
* applies them. A sample is checked against what the twin is derived from; the published examples themselves stay the
|
|
100
99
|
* vendor's bytes. */
|
|
101
100
|
function served(pkg, doc) {
|
|
@@ -426,7 +425,7 @@ function fromSpec(pkg, lane, model) {
|
|
|
426
425
|
}
|
|
427
426
|
return out;
|
|
428
427
|
}
|
|
429
|
-
// a spec vendored as published or gzipped, as
|
|
428
|
+
// a spec vendored as published or gzipped, as the derivation reads it
|
|
430
429
|
const spec = ['openapi.json.gz', 'openapi.json', 'openapi.yaml.gz', 'openapi.yaml', 'discovery.json.gz', 'discovery.json'].map((f) => join(pkg, 'spec', f)).find((p) => existsSync(p));
|
|
431
430
|
// a Protocol Buffers service publishes no examples in its .proto files: its examples are its documentation's alone
|
|
432
431
|
if (!spec && existsSync(join(pkg, 'spec', 'proto')))
|
|
@@ -442,7 +441,7 @@ function fromSpec(pkg, lane, model) {
|
|
|
442
441
|
if (!spec && !documents)
|
|
443
442
|
throw new Error(`${pkg}: no published-example reader for this spec yet`);
|
|
444
443
|
const text = spec ? (spec.endsWith('.gz') ? gunzipSync(readFileSync(spec)) : readFileSync(spec)).toString('utf8') : '';
|
|
445
|
-
// a Google API Discovery document publishes no examples; it is read as the OpenAPI document
|
|
444
|
+
// a Google API Discovery document publishes no examples; it is read as the OpenAPI document the derivation reads it as,
|
|
446
445
|
// its patches applied to the Discovery document first (spec-ir-discovery.ts)
|
|
447
446
|
const discovery = spec !== undefined && /discovery\.json(\.gz)?$/.test(spec);
|
|
448
447
|
const docs = (documents ? Object.values(documents.documents) : [discovery ? discoveryToOpenAPI(parseExactJson(text)) : /\.yaml(\.gz)?$/.test(spec) ? parseExactJson(JSON.stringify(parseYaml(text, { intAsBigInt: true }), (_key, value) => typeof value === 'bigint' ? exactInteger(String(value)) : value)) : parseExactJson(text)]);
|
package/dist/src/verdict.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// A JUDGE'S INDEPENDENCE (the grade's outline-judged and life-judged, the published examples' verdict): a verdict rules
|
|
2
2
|
// only when it names its judge (`judge.agent`) and the authors of what it judges (`authors`), and the judge is none of
|
|
3
3
|
// them. The dispatcher stamps author names consistently; a judge started independently
|
|
4
|
-
// (
|
|
4
|
+
// (`twin-standard judge-brief` prints its brief) names its reviewer role (`judge.name`). One judging per pack: the
|
|
5
5
|
// verdict stands, and later changes to what it judged keep it (architecture, "Writing a life", Judge).
|
|
6
6
|
/** Committed attribution names people or author roles, never execution identities. */
|
|
7
7
|
export function attributionName(name) {
|
|
@@ -21,7 +21,7 @@ import { createRequire } from 'node:module';
|
|
|
21
21
|
import { gzipSync } from 'node:zlib';
|
|
22
22
|
import { existsSync, readdirSync, readFileSync } from 'node:fs';
|
|
23
23
|
import { join } from 'node:path';
|
|
24
|
-
import { databaseSession, getActivePackAssets, getActiveWorldStore, LocalPackAssets, MemoryBlobStore, MemoryWorldStore, setActiveBlobStore, setActivePackAssets, setActiveWorldStore, setDeliveryObserver, setInProcessTwins, settleEventDeliveries, worldDoorMatcher, worldNow } from '@volter/world-core/runtime';
|
|
24
|
+
import { databaseSession, getActivePackAssets, getActiveWorldStore, LocalPackAssets, MemoryBlobStore, MemoryWorldStore, setActiveBlobStore, setActivePackAssets, setActiveWorldStore, setDeliveryObserver, setInProcessMailServers, setInProcessTwins, settleEventDeliveries, worldDoorMatcher, worldNow } from '@volter/world-core/runtime';
|
|
25
25
|
import { createHostTable } from '@volter/world-core/host-routing';
|
|
26
26
|
import packFactsDocument from '@volter/world-core/generated/pack-facts.json' with { type: 'json' };
|
|
27
27
|
import { isMongo, isMysql, isResp, at, captureAt, echoFailures, expandFills, expectFailures, fill, fillJson, formBody, isGit, isLine, isSql, isSocket, isWait, jwtAuthorization, oauth1Authorization, presignedPath, sigv4Headers, waitMs } from "./journey-kit.js";
|
|
@@ -255,6 +255,21 @@ export async function walk(journey, root, collect, opts) {
|
|
|
255
255
|
}
|
|
256
256
|
companions.push({ vendor, keys, fetch: companion(root, { application }), databaseDoor: companion.managedDatabaseDoor });
|
|
257
257
|
}
|
|
258
|
+
// a companion that receives mail (an SMTP side port: gmail's, gmail.com's MX) is the mail server ctx.mail reaches for
|
|
259
|
+
// the domains its host rules claim, on its byte-stream door in process over this walk's World, as a World reaches its
|
|
260
|
+
// listener: the life declares the provider of every address it mails
|
|
261
|
+
const mailServers = [];
|
|
262
|
+
for (const c of companions) {
|
|
263
|
+
const sides = walkFacts[c.vendor.split('/')[0]]?.sidePorts ?? [];
|
|
264
|
+
if (!sides.some((side) => side.scheme === 'smtp'))
|
|
265
|
+
continue;
|
|
266
|
+
const stream = await vendorStream(packsDir(), c.vendor, dependenciesDir());
|
|
267
|
+
if (!stream) {
|
|
268
|
+
collect.push({ step: '(setup)', beat: 'ADAPTER', detail: `the companion ${c.vendor} receives mail on an smtp side port and exports no create<Name>TwinStream` });
|
|
269
|
+
return {};
|
|
270
|
+
}
|
|
271
|
+
mailServers.push({ vendor: c.vendor, receives: (domain) => c.keys.some((key) => walkHosts[key](domain, '/')), stream: stream(root, { application }) });
|
|
272
|
+
}
|
|
258
273
|
const eventFields = new Map();
|
|
259
274
|
for (const vendor of [journey.vendor, ...companions.map((companion) => companion.vendor)]) {
|
|
260
275
|
const directory = directoryFor(vendor);
|
|
@@ -1030,6 +1045,7 @@ export async function walk(journey, root, collect, opts) {
|
|
|
1030
1045
|
...companions.flatMap((c) => c.keys.map((key) => [key, c.fetch])),
|
|
1031
1046
|
...ownKeys.map((key) => [key, twin]),
|
|
1032
1047
|
]));
|
|
1048
|
+
const previousMailServers = setInProcessMailServers(mailServers);
|
|
1033
1049
|
try {
|
|
1034
1050
|
for (const step of journey.steps) {
|
|
1035
1051
|
await settleEventDeliveries(root);
|
|
@@ -1343,6 +1359,7 @@ export async function walk(journey, root, collect, opts) {
|
|
|
1343
1359
|
finally {
|
|
1344
1360
|
setDeliveryObserver(previousObserver);
|
|
1345
1361
|
setInProcessTwins(previousTwins);
|
|
1362
|
+
setInProcessMailServers(previousMailServers);
|
|
1346
1363
|
try {
|
|
1347
1364
|
if (sqlSession)
|
|
1348
1365
|
await sqlSession.then((session) => session.end(), () => { });
|
|
@@ -127,7 +127,7 @@ export type JourneyStep = {
|
|
|
127
127
|
* whose values the wire encodes (a psdb result's rows, base64 values sliced by per-column lengths) is asserted by
|
|
128
128
|
* what it holds. Captures read the answer as it came. */
|
|
129
129
|
view?: string;
|
|
130
|
-
/** This step sends a vendor's published example, `<operation>#<title>` (
|
|
130
|
+
/** This step sends a vendor's published example, `<operation>#<title>` (tools/vendor-examples.ts): its answer
|
|
131
131
|
* must hold every key path of the example's answer with the same JSON type. */
|
|
132
132
|
example?: string;
|
|
133
133
|
/** Caller-chosen values of the example's named placeholders, compared literally; captures may fill them.
|
|
@@ -138,7 +138,7 @@ export type JourneyStep = {
|
|
|
138
138
|
exampleDiffers?: Record<string, string>;
|
|
139
139
|
/** A published body field this step leaves out (a dot path; `a[].b` in each item) → why: a rendered value naming an
|
|
140
140
|
* object the World does not hold (a Git repository behind an integration). Only a field the request schema does not
|
|
141
|
-
* require may be left out (
|
|
141
|
+
* require may be left out (tools/vendor-examples.ts). */
|
|
142
142
|
unsent?: Record<string, string>;
|
|
143
143
|
/** answer paths whose value equals something the setup sent without the setup deciding it (a default the vendor applies
|
|
144
144
|
* that coincides), each with the reason: `vendor-examples.ts --sweep` does not ask them asserted, and the judge reads the reason */
|