@volter/twin-standard 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +202 -0
- package/README.md +20 -0
- package/dist/src/check-sources.d.ts +33 -0
- package/dist/src/check-sources.js +128 -0
- package/dist/src/cli.d.ts +2 -0
- package/dist/src/cli.js +105 -0
- package/dist/src/derive.d.ts +2 -0
- package/dist/src/derive.js +349 -0
- package/dist/src/gate.d.ts +9 -0
- package/dist/src/gate.js +109 -0
- package/dist/src/grade.d.ts +30 -0
- package/dist/src/grade.js +36 -0
- package/dist/src/index.d.ts +7 -0
- package/dist/src/index.js +11 -0
- package/dist/src/lanes.d.ts +2 -0
- package/dist/src/lanes.js +33 -0
- package/dist/src/p3-rules.d.ts +8 -0
- package/dist/src/p3-rules.js +43 -0
- package/dist/src/protocol-3.d.ts +5 -0
- package/dist/src/protocol-3.js +721 -0
- package/dist/src/published.d.ts +13 -0
- package/dist/src/published.js +36 -0
- package/dist/src/spec-documents.d.ts +12 -0
- package/dist/src/spec-documents.js +61 -0
- package/dist/src/spec-ir-client.d.ts +20 -0
- package/dist/src/spec-ir-client.js +77 -0
- package/dist/src/spec-ir-commands.d.ts +38 -0
- package/dist/src/spec-ir-commands.js +38 -0
- package/dist/src/spec-ir-discovery.d.ts +11 -0
- package/dist/src/spec-ir-discovery.js +106 -0
- package/dist/src/spec-ir-graphql.d.ts +25 -0
- package/dist/src/spec-ir-graphql.js +46 -0
- package/dist/src/spec-ir-lines.d.ts +19 -0
- package/dist/src/spec-ir-lines.js +76 -0
- package/dist/src/spec-ir-proto.d.ts +80 -0
- package/dist/src/spec-ir-proto.js +339 -0
- package/dist/src/spec-ir.d.ts +104 -0
- package/dist/src/spec-ir.js +691 -0
- package/dist/src/spec-patches.d.ts +8 -0
- package/dist/src/spec-patches.js +24 -0
- package/dist/src/spec.d.ts +5 -0
- package/dist/src/spec.js +7 -0
- package/dist/src/types.d.ts +75 -0
- package/dist/src/types.js +4 -0
- package/dist/src/unit.d.ts +5 -0
- package/dist/src/unit.js +14 -0
- package/package.json +71 -0
- package/src/check-sources.ts +109 -0
- package/src/cli.ts +75 -0
- package/src/derive.ts +316 -0
- package/src/gate.ts +95 -0
- package/src/grade.ts +47 -0
- package/src/index.ts +12 -0
- package/src/lanes.ts +29 -0
- package/src/p3-rules.ts +44 -0
- package/src/protocol-3.ts +617 -0
- package/src/published.ts +37 -0
- package/src/spec-documents.ts +58 -0
- package/src/spec-ir-client.ts +86 -0
- package/src/spec-ir-commands.ts +50 -0
- package/src/spec-ir-discovery.ts +104 -0
- package/src/spec-ir-graphql.ts +64 -0
- package/src/spec-ir-lines.ts +76 -0
- package/src/spec-ir-proto.ts +289 -0
- package/src/spec-ir.ts +689 -0
- package/src/spec-patches.ts +23 -0
- package/src/spec.ts +8 -0
- package/src/types.ts +53 -0
- package/src/unit.ts +15 -0
|
@@ -0,0 +1,23 @@
|
|
|
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. derive-pack.ts applies them before the
|
|
3
|
+
// IR; vendor-examples.ts reads the patched schemas, which are what the twin is derived from.
|
|
4
|
+
|
|
5
|
+
export type SpecPatch = { op: string; path: string; from?: string; value?: unknown };
|
|
6
|
+
|
|
7
|
+
/** Apply one patch to `doc` in place. */
|
|
8
|
+
export function applySpecPatch(doc: unknown, p: SpecPatch): void {
|
|
9
|
+
const at = (pointer: string): { parent: any; key: string } => {
|
|
10
|
+
const keys = pointer.split('/').slice(1).map((k) => k.replace(/~1/g, '/').replace(/~0/g, '~'));
|
|
11
|
+
const key = keys.pop()!;
|
|
12
|
+
return { parent: keys.reduce((node: any, k) => node[k], doc), key };
|
|
13
|
+
};
|
|
14
|
+
const read = (pointer: string): unknown => { const { parent, key } = at(pointer); return parent[key]; };
|
|
15
|
+
const value = p.op === 'move' || p.op === 'copy' ? structuredClone(read(p.from!)) : p.value;
|
|
16
|
+
const drop = ({ parent, key }: { parent: any; key: string }) => (Array.isArray(parent) ? parent.splice(Number(key), 1) : delete parent[key]);
|
|
17
|
+
if (p.op === 'move') drop(at(p.from!));
|
|
18
|
+
const { parent, key } = at(p.path);
|
|
19
|
+
if (p.op === 'remove') drop({ parent, key });
|
|
20
|
+
// into an array, `add` inserts at the index (`-` appends) and `replace` overwrites it
|
|
21
|
+
else if (Array.isArray(parent) && p.op !== 'replace') parent.splice(key === '-' ? parent.length : Number(key), 0, value);
|
|
22
|
+
else parent[key] = value;
|
|
23
|
+
}
|
package/src/spec.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
// The spec side of the standard, for a tool that reads a vendor's spec as the derivation does (a vendor's published
|
|
2
|
+
// examples replayed, a client's calls extracted): the spec IR, a Discovery document as OpenAPI, several documents as one
|
|
3
|
+
// API, and a recorded patch applied.
|
|
4
|
+
export { CRUD_CLASSES, fromOpenAPI, fromSmithy, fromSwagger2, slug, type SpecIR } from './spec-ir.ts';
|
|
5
|
+
export { discoveryToOpenAPI, fromDiscovery, isDiscovery } from './spec-ir-discovery.ts';
|
|
6
|
+
export type { ClientCall, ClientOpsSpec } from './spec-ir-client.ts';
|
|
7
|
+
export { mergeDocuments, readSpecDocuments, type SpecDocuments } from './spec-documents.ts';
|
|
8
|
+
export { applySpecPatch, type SpecPatch } from './spec-patches.ts';
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
// A standard the grader grades against (docs/contributing/architecture.md, "The grade"): its id and version, and
|
|
2
|
+
// its criteria, unweighted (a grade is criteria met over criteria). The grader is one engine (./grade.ts); a new
|
|
3
|
+
// protocol, or a revision of one, is another module beside this, released as a version of this package.
|
|
4
|
+
|
|
5
|
+
/** What a criterion is checked against: one unit (a pack, or one lane of a multi-API pack), read from the tree. */
|
|
6
|
+
export interface GradeUnit {
|
|
7
|
+
/** `stripe`, or a lane `aws/secretsmanager` */
|
|
8
|
+
name: string;
|
|
9
|
+
/** the vendor (the pack directory's name) */
|
|
10
|
+
vendor: string;
|
|
11
|
+
/** absolute path of the unit's directory */
|
|
12
|
+
dir: string;
|
|
13
|
+
/** score-pack's report for the unit, recomputed now (null when the unit has no customer life to walk) */
|
|
14
|
+
report: ScoreReport | null;
|
|
15
|
+
/** why the walk gave no report although the unit has a life (a crash, a timeout), else undefined */
|
|
16
|
+
walkError?: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface ScoreReport {
|
|
20
|
+
vendor: string;
|
|
21
|
+
life: { steps: number; verdict: string; judge: string | null; authors: string[] };
|
|
22
|
+
failures: Array<{ step: string; beat: string; detail: string }>;
|
|
23
|
+
flags: Array<{ pattern: string; step: string; detail: string }>;
|
|
24
|
+
coverage: { missing: number; code: { lines: number; run: number; recorded: number; missing: number }; state?: unknown } | null;
|
|
25
|
+
examples: { done: boolean; replayed?: boolean; verdict?: string; line: string };
|
|
26
|
+
breadth: { operations: number; [servedBy: string]: number };
|
|
27
|
+
missingForDone: string[];
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export interface CriterionResult {
|
|
31
|
+
pass: boolean;
|
|
32
|
+
/** why it failed, or what it measured when it passed */
|
|
33
|
+
reason: string;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export interface Criterion {
|
|
37
|
+
id: string;
|
|
38
|
+
section: 'form' | 'proof' | 'soundness';
|
|
39
|
+
/** it runs the pack (typecheck, tests), so its result can vary with the machine; the rest read the tree */
|
|
40
|
+
executed?: boolean;
|
|
41
|
+
/** what it asks, in one line, for the catalog */
|
|
42
|
+
asks: string;
|
|
43
|
+
check(unit: GradeUnit): CriterionResult | Promise<CriterionResult>;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export interface Standard {
|
|
47
|
+
id: string;
|
|
48
|
+
version: number;
|
|
49
|
+
title: string;
|
|
50
|
+
criteria: Criterion[];
|
|
51
|
+
/** criteria the standard names that no tool decides yet: shown, counted in no grade */
|
|
52
|
+
planned: string[];
|
|
53
|
+
}
|
package/src/unit.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// A unit the standard grades, named from its directory (docs/contributing/architecture.md, "The catalog: where twins come
|
|
2
|
+
// from": the standard takes a pack's directory, in any repository, or an unpacked published tarball): a pack is its
|
|
3
|
+
// directory's name, and a lane keeps its vendor (`cloudflare/r2`).
|
|
4
|
+
import { existsSync } from 'node:fs';
|
|
5
|
+
import { basename, join, resolve } from 'node:path';
|
|
6
|
+
|
|
7
|
+
/** The unit at `path`: its absolute directory and the name it is reported by. */
|
|
8
|
+
export function unitAt(path: string): { dir: string; name: string } {
|
|
9
|
+
const dir = resolve(path.replace(/^~/, process.env.HOME ?? '~'));
|
|
10
|
+
// a lane is a unit under its vendor's package: it has no package.json of its own, its vendor's directory does (a pack
|
|
11
|
+
// has its own, whatever its parent holds: a pack repository's root is a workspace)
|
|
12
|
+
const parent = resolve(dir, '..');
|
|
13
|
+
const lane = !existsSync(join(dir, 'package.json')) && existsSync(join(parent, 'package.json')) && existsSync(join(dir, 'spec'));
|
|
14
|
+
return { dir, name: lane ? `${basename(parent)}/${basename(dir)}` : basename(dir) };
|
|
15
|
+
}
|