@descryy/ir 0.1.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.
Files changed (94) hide show
  1. package/LICENSE +21 -0
  2. package/dist/capabilities.d.ts +83 -0
  3. package/dist/capabilities.d.ts.map +1 -0
  4. package/dist/capabilities.js +91 -0
  5. package/dist/capabilities.js.map +1 -0
  6. package/dist/conformance/bind.d.ts +82 -0
  7. package/dist/conformance/bind.d.ts.map +1 -0
  8. package/dist/conformance/bind.js +157 -0
  9. package/dist/conformance/bind.js.map +1 -0
  10. package/dist/conformance/cli.d.ts +25 -0
  11. package/dist/conformance/cli.d.ts.map +1 -0
  12. package/dist/conformance/cli.js +137 -0
  13. package/dist/conformance/cli.js.map +1 -0
  14. package/dist/conformance/compare.d.ts +74 -0
  15. package/dist/conformance/compare.d.ts.map +1 -0
  16. package/dist/conformance/compare.js +140 -0
  17. package/dist/conformance/compare.js.map +1 -0
  18. package/dist/conformance/compose.d.ts +79 -0
  19. package/dist/conformance/compose.d.ts.map +1 -0
  20. package/dist/conformance/compose.js +167 -0
  21. package/dist/conformance/compose.js.map +1 -0
  22. package/dist/conformance/golden.d.ts +110 -0
  23. package/dist/conformance/golden.d.ts.map +1 -0
  24. package/dist/conformance/golden.js +252 -0
  25. package/dist/conformance/golden.js.map +1 -0
  26. package/dist/conformance/index.d.ts +26 -0
  27. package/dist/conformance/index.d.ts.map +1 -0
  28. package/dist/conformance/index.js +18 -0
  29. package/dist/conformance/index.js.map +1 -0
  30. package/dist/conformance/manifest-check.d.ts +78 -0
  31. package/dist/conformance/manifest-check.d.ts.map +1 -0
  32. package/dist/conformance/manifest-check.js +149 -0
  33. package/dist/conformance/manifest-check.js.map +1 -0
  34. package/dist/conformance/manifest.d.ts +83 -0
  35. package/dist/conformance/manifest.d.ts.map +1 -0
  36. package/dist/conformance/manifest.js +158 -0
  37. package/dist/conformance/manifest.js.map +1 -0
  38. package/dist/conformance/report.d.ts +14 -0
  39. package/dist/conformance/report.d.ts.map +1 -0
  40. package/dist/conformance/report.js +87 -0
  41. package/dist/conformance/report.js.map +1 -0
  42. package/dist/conformance/run.d.ts +172 -0
  43. package/dist/conformance/run.d.ts.map +1 -0
  44. package/dist/conformance/run.js +626 -0
  45. package/dist/conformance/run.js.map +1 -0
  46. package/dist/contracts.d.ts +198 -0
  47. package/dist/contracts.d.ts.map +1 -0
  48. package/dist/contracts.js +10 -0
  49. package/dist/contracts.js.map +1 -0
  50. package/dist/contradiction.d.ts +112 -0
  51. package/dist/contradiction.d.ts.map +1 -0
  52. package/dist/contradiction.js +97 -0
  53. package/dist/contradiction.js.map +1 -0
  54. package/dist/finding.d.ts +567 -0
  55. package/dist/finding.d.ts.map +1 -0
  56. package/dist/finding.js +491 -0
  57. package/dist/finding.js.map +1 -0
  58. package/dist/hypothesis.d.ts +130 -0
  59. package/dist/hypothesis.d.ts.map +1 -0
  60. package/dist/hypothesis.js +116 -0
  61. package/dist/hypothesis.js.map +1 -0
  62. package/dist/identity.d.ts +295 -0
  63. package/dist/identity.d.ts.map +1 -0
  64. package/dist/identity.js +404 -0
  65. package/dist/identity.js.map +1 -0
  66. package/dist/index.d.ts +34 -0
  67. package/dist/index.d.ts.map +1 -0
  68. package/dist/index.js +21 -0
  69. package/dist/index.js.map +1 -0
  70. package/dist/ir.d.ts +360 -0
  71. package/dist/ir.d.ts.map +1 -0
  72. package/dist/ir.js +76 -0
  73. package/dist/ir.js.map +1 -0
  74. package/dist/model-tables.d.ts +111 -0
  75. package/dist/model-tables.d.ts.map +1 -0
  76. package/dist/model-tables.js +103 -0
  77. package/dist/model-tables.js.map +1 -0
  78. package/dist/normalise.d.ts +114 -0
  79. package/dist/normalise.d.ts.map +1 -0
  80. package/dist/normalise.js +603 -0
  81. package/dist/normalise.js.map +1 -0
  82. package/dist/reliability.d.ts +89 -0
  83. package/dist/reliability.d.ts.map +1 -0
  84. package/dist/reliability.js +181 -0
  85. package/dist/reliability.js.map +1 -0
  86. package/dist/tool-surface.d.ts +131 -0
  87. package/dist/tool-surface.d.ts.map +1 -0
  88. package/dist/tool-surface.js +133 -0
  89. package/dist/tool-surface.js.map +1 -0
  90. package/dist/vocabulary.d.ts +73 -0
  91. package/dist/vocabulary.d.ts.map +1 -0
  92. package/dist/vocabulary.js +150 -0
  93. package/dist/vocabulary.js.map +1 -0
  94. package/package.json +27 -0
@@ -0,0 +1,74 @@
1
+ /**
2
+ * The comparison primitives: attribute matching and edge-type equivalence.
3
+ *
4
+ * Pure functions over plain values. Nothing here reads a batch, constructs a
5
+ * finding or knows what a pattern is — which is what makes the two rules below
6
+ * arguable on their own terms rather than buried inside the runner.
7
+ */
8
+ import type { EdgeType, NodeType } from "../vocabulary.ts";
9
+ /**
10
+ * Node types whose attributes are **not** passed through `fieldNameMap`.
11
+ *
12
+ * The map translates one identifier in source code into another corpus's
13
+ * spelling of the same idea — `total_amount` and `totalAmount` are the same
14
+ * field written the way each language writes fields. A database column is not
15
+ * that: `discount_code` is the same string in every language, because it is the
16
+ * name in the schema and not the name in the code. Mapping it would make golden
17
+ * pattern 10 demand `discountCode` from one adapter and `discount_code` from
18
+ * another for the identical migration, and pattern 10 exists precisely because
19
+ * that migration is language-independent (§14.6).
20
+ *
21
+ * Found by writing the map, not by reasoning about it: the corpus binds
22
+ * `discount_code -> discountCode` for one language and also asserts a column
23
+ * literally named `discount_code`. See DEC-040.
24
+ */
25
+ export declare const UNMAPPED_ATTR_NODE_TYPES: ReadonlySet<NodeType>;
26
+ /**
27
+ * Rewrite every string leaf of an expected value through the manifest's map.
28
+ *
29
+ * Applied to the *expectation*, never to what the adapter emitted. An adapter
30
+ * reports what the source says (`fixtures/languages/<name>/manifest.json`,
31
+ * `fieldNameMapNote`); rewriting its output would let the harness invent
32
+ * agreement that is not there.
33
+ */
34
+ export declare function mapFieldNames(value: unknown, map: Readonly<Record<string, string>>): unknown;
35
+ export interface AttrMismatch {
36
+ /** Dotted path into the attrs, for a message the adapter author can act on. */
37
+ readonly path: string;
38
+ readonly detail: string;
39
+ }
40
+ /**
41
+ * Does `actual` contain everything `expected` asserts?
42
+ *
43
+ * **Containment, not equality**, in three places, and each is deliberate:
44
+ *
45
+ * - extra keys in an actual object are fine — `attrs` is the pressure valve that
46
+ * keeps the vocabulary at 15 types, so a framework-specific key is expected;
47
+ * - extra elements in an actual array are fine, for the same reason;
48
+ * - array order is not compared, because declaration order of fields carries no
49
+ * meaning and forcing one would make the golden a style guide.
50
+ *
51
+ * Precision violations are not expressed by "the adapter emitted more than
52
+ * this"; they are expressed by `forbiddenEdges` and `mustNotResolveTo`, which
53
+ * say exactly what must be absent. Two mechanisms, not three.
54
+ */
55
+ export declare function attrsSubset(actual: unknown, expected: unknown, path?: string): AttrMismatch | null;
56
+ /**
57
+ * Edge types a golden expectation will also accept, and from whom.
58
+ *
59
+ * Only one pair, and it comes from the corpus rather than from convenience:
60
+ * golden pattern 11's note says a language that does not distinguish an
61
+ * interface from a base class may emit `INHERITS` where the golden says
62
+ * `IMPLEMENTS`, "and the adapter declares which via `capabilities()`".
63
+ *
64
+ * The declaration requirement is what keeps this from being a hole. Without it,
65
+ * any adapter could satisfy `IMPLEMENTS` by emitting `INHERITS` at random and
66
+ * the pattern would stop asserting anything.
67
+ */
68
+ export declare const EDGE_TYPE_ALTERNATIVES: Readonly<Partial<Record<EdgeType, readonly EdgeType[]>>>;
69
+ /**
70
+ * Which edge types satisfy an expectation for `expected`, given what the adapter
71
+ * declared it can produce. An adapter that declares nothing gets no substitutes.
72
+ */
73
+ export declare function acceptedEdgeTypes(expected: EdgeType, declared: readonly EdgeType[] | undefined): readonly EdgeType[];
74
+ //# sourceMappingURL=compare.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compare.d.ts","sourceRoot":"","sources":["../../src/conformance/compare.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAM3D;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,wBAAwB,EAAE,WAAW,CAAC,QAAQ,CAGzD,CAAC;AAEH;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,GAAG,OAAO,CAS5F;AAMD,MAAM,WAAW,YAAY;IAC3B,+EAA+E;IAC/E,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAMD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,WAAW,CACzB,MAAM,EAAE,OAAO,EACf,QAAQ,EAAE,OAAO,EACjB,IAAI,SAAU,GACb,YAAY,GAAG,IAAI,CAwCrB;AAMD;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,sBAAsB,EAAE,QAAQ,CAAC,OAAO,CAAC,MAAM,CAAC,QAAQ,EAAE,SAAS,QAAQ,EAAE,CAAC,CAAC,CAE3F,CAAC;AAEF;;;GAGG;AACH,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,QAAQ,EAClB,QAAQ,EAAE,SAAS,QAAQ,EAAE,GAAG,SAAS,GACxC,SAAS,QAAQ,EAAE,CAIrB"}
@@ -0,0 +1,140 @@
1
+ /**
2
+ * The comparison primitives: attribute matching and edge-type equivalence.
3
+ *
4
+ * Pure functions over plain values. Nothing here reads a batch, constructs a
5
+ * finding or knows what a pattern is — which is what makes the two rules below
6
+ * arguable on their own terms rather than buried inside the runner.
7
+ */
8
+ // ---------------------------------------------------------------------------
9
+ // Field-name mapping
10
+ // ---------------------------------------------------------------------------
11
+ /**
12
+ * Node types whose attributes are **not** passed through `fieldNameMap`.
13
+ *
14
+ * The map translates one identifier in source code into another corpus's
15
+ * spelling of the same idea — `total_amount` and `totalAmount` are the same
16
+ * field written the way each language writes fields. A database column is not
17
+ * that: `discount_code` is the same string in every language, because it is the
18
+ * name in the schema and not the name in the code. Mapping it would make golden
19
+ * pattern 10 demand `discountCode` from one adapter and `discount_code` from
20
+ * another for the identical migration, and pattern 10 exists precisely because
21
+ * that migration is language-independent (§14.6).
22
+ *
23
+ * Found by writing the map, not by reasoning about it: the corpus binds
24
+ * `discount_code -> discountCode` for one language and also asserts a column
25
+ * literally named `discount_code`. See DEC-040.
26
+ */
27
+ export const UNMAPPED_ATTR_NODE_TYPES = new Set([
28
+ "DATABASE_TABLE",
29
+ "DATABASE_COLUMN",
30
+ ]);
31
+ /**
32
+ * Rewrite every string leaf of an expected value through the manifest's map.
33
+ *
34
+ * Applied to the *expectation*, never to what the adapter emitted. An adapter
35
+ * reports what the source says (`fixtures/languages/<name>/manifest.json`,
36
+ * `fieldNameMapNote`); rewriting its output would let the harness invent
37
+ * agreement that is not there.
38
+ */
39
+ export function mapFieldNames(value, map) {
40
+ if (typeof value === "string")
41
+ return map[value] ?? value;
42
+ if (Array.isArray(value))
43
+ return value.map((item) => mapFieldNames(item, map));
44
+ if (typeof value === "object" && value !== null) {
45
+ const out = {};
46
+ for (const [key, entry] of Object.entries(value))
47
+ out[key] = mapFieldNames(entry, map);
48
+ return out;
49
+ }
50
+ return value;
51
+ }
52
+ function describe(value) {
53
+ return JSON.stringify(value) ?? String(value);
54
+ }
55
+ /**
56
+ * Does `actual` contain everything `expected` asserts?
57
+ *
58
+ * **Containment, not equality**, in three places, and each is deliberate:
59
+ *
60
+ * - extra keys in an actual object are fine — `attrs` is the pressure valve that
61
+ * keeps the vocabulary at 15 types, so a framework-specific key is expected;
62
+ * - extra elements in an actual array are fine, for the same reason;
63
+ * - array order is not compared, because declaration order of fields carries no
64
+ * meaning and forcing one would make the golden a style guide.
65
+ *
66
+ * Precision violations are not expressed by "the adapter emitted more than
67
+ * this"; they are expressed by `forbiddenEdges` and `mustNotResolveTo`, which
68
+ * say exactly what must be absent. Two mechanisms, not three.
69
+ */
70
+ export function attrsSubset(actual, expected, path = "attrs") {
71
+ if (Array.isArray(expected)) {
72
+ if (!Array.isArray(actual)) {
73
+ return { path, detail: `expected an array, got ${describe(actual)}` };
74
+ }
75
+ const taken = new Set();
76
+ for (const want of expected) {
77
+ let found = false;
78
+ for (let i = 0; i < actual.length; i += 1) {
79
+ if (taken.has(i))
80
+ continue;
81
+ if (attrsSubset(actual[i], want, `${path}[]`) === null) {
82
+ taken.add(i);
83
+ found = true;
84
+ break;
85
+ }
86
+ }
87
+ if (!found)
88
+ return { path, detail: `no entry matching ${describe(want)}` };
89
+ }
90
+ return null;
91
+ }
92
+ if (typeof expected === "object" && expected !== null) {
93
+ if (typeof actual !== "object" || actual === null || Array.isArray(actual)) {
94
+ return { path, detail: `expected an object, got ${describe(actual)}` };
95
+ }
96
+ const actualRecord = actual;
97
+ for (const [key, want] of Object.entries(expected)) {
98
+ if (!(key in actualRecord)) {
99
+ return { path: `${path}.${key}`, detail: `absent; expected ${describe(want)}` };
100
+ }
101
+ const mismatch = attrsSubset(actualRecord[key], want, `${path}.${key}`);
102
+ if (mismatch !== null)
103
+ return mismatch;
104
+ }
105
+ return null;
106
+ }
107
+ if (actual !== expected) {
108
+ return { path, detail: `expected ${describe(expected)}, got ${describe(actual)}` };
109
+ }
110
+ return null;
111
+ }
112
+ // ---------------------------------------------------------------------------
113
+ // Edge-type equivalence
114
+ // ---------------------------------------------------------------------------
115
+ /**
116
+ * Edge types a golden expectation will also accept, and from whom.
117
+ *
118
+ * Only one pair, and it comes from the corpus rather than from convenience:
119
+ * golden pattern 11's note says a language that does not distinguish an
120
+ * interface from a base class may emit `INHERITS` where the golden says
121
+ * `IMPLEMENTS`, "and the adapter declares which via `capabilities()`".
122
+ *
123
+ * The declaration requirement is what keeps this from being a hole. Without it,
124
+ * any adapter could satisfy `IMPLEMENTS` by emitting `INHERITS` at random and
125
+ * the pattern would stop asserting anything.
126
+ */
127
+ export const EDGE_TYPE_ALTERNATIVES = {
128
+ IMPLEMENTS: ["INHERITS"],
129
+ };
130
+ /**
131
+ * Which edge types satisfy an expectation for `expected`, given what the adapter
132
+ * declared it can produce. An adapter that declares nothing gets no substitutes.
133
+ */
134
+ export function acceptedEdgeTypes(expected, declared) {
135
+ const alternatives = EDGE_TYPE_ALTERNATIVES[expected] ?? [];
136
+ if (declared === undefined)
137
+ return [expected];
138
+ return [expected, ...alternatives.filter((type) => declared.includes(type))];
139
+ }
140
+ //# sourceMappingURL=compare.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compare.js","sourceRoot":"","sources":["../../src/conformance/compare.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,79 @@
1
+ /**
2
+ * Several producers, graded as the graph will actually see them.
3
+ *
4
+ * ## Why a per-adapter gate is not enough on its own
5
+ *
6
+ * `runConformance` grades **one** adapter, and that is right: a release gate
7
+ * certifies a thing that can be released. But four of the fourteen golden
8
+ * patterns assert nodes from **two different producers at once**, and no
9
+ * single-adapter run can ever satisfy one of those. Pattern 04 is the clearest
10
+ * case:
11
+ *
12
+ * API_ROUTE ──SERVES_API──▶ API_ENDPOINT and a FUNCTION handler
13
+ * (a contract, or a framework extractor) (the language adapter)
14
+ *
15
+ * `adapter-openapi` produces the route and the endpoint and genuinely cannot
16
+ * produce the handler; the language adapter produces the handler and genuinely
17
+ * cannot produce the route. Run apart, each correctly reports a skip for a type
18
+ * it does not declare — and the pattern is then **permanently unmeasured**,
19
+ * which reads exactly like a gap in the product when it is a gap in the harness.
20
+ *
21
+ * Composing them is not a loosening. It is the arrangement the product ships in:
22
+ * the Graph Builder merges every `IRSource` over one repository, and a question
23
+ * like *which handler serves this endpoint* is only ever answerable in the
24
+ * merged graph. What the composed run certifies is a **pair**, and it is
25
+ * reported separately from each adapter's own gate so neither number is diluted.
26
+ *
27
+ * ## What this is not
28
+ *
29
+ * It is not the Graph Builder. It concatenates batches and drops a node whose id
30
+ * a previous batch already emitted, which is the merge rule stated at its
31
+ * simplest (DEC-012); the builder additionally reconciles attributes, resolution
32
+ * and confidence across producers, and that reconciliation has its own tests.
33
+ * Using the real builder here would make a conformance failure ambiguous between
34
+ * the adapters and the merge — so this stays deliberately thin, and a
35
+ * disagreement between two producers over one id is **reported** rather than
36
+ * silently resolved.
37
+ *
38
+ * ## The one thing it gives up, and why that is allowed here
39
+ *
40
+ * The Normaliser requires every node in a batch to carry the batch own
41
+ * `producedBy`, because the writer invalidates by `(producedBy, sourceFiles)`
42
+ * and a node stamped differently would be inserted by a batch and then never
43
+ * deleted by it. That rule is load-bearing for storage and is not weakened: the
44
+ * composed batch **restamps** every node and edge with the composition id, which
45
+ * costs per-node provenance *inside this run*.
46
+ *
47
+ * That is acceptable only because a composed run stores nothing and certifies
48
+ * nothing shippable. The per-adapter gates above it keep full provenance and are
49
+ * what release depends on; this run answers one question — can the pair satisfy
50
+ * a pattern neither can satisfy alone — and is reported separately so no
51
+ * adapter number is diluted. The product itself never does this: the Graph
52
+ * Builder normalises each batch on its own and merges the results, which is why
53
+ * it keeps the provenance this does not.
54
+ */
55
+ import type { CapabilityMatrix, IRSource } from "../contracts.ts";
56
+ import type { NodeType } from "../vocabulary.ts";
57
+ /** A node two producers both claimed, kept for reporting rather than hidden. */
58
+ export interface CompositionConflict {
59
+ readonly nodeId: string;
60
+ readonly keptFrom: string;
61
+ readonly alsoFrom: string;
62
+ readonly type: NodeType;
63
+ readonly name: string;
64
+ }
65
+ export interface ComposedSource extends IRSource {
66
+ capabilities(): CapabilityMatrix;
67
+ /** Populated by `emit`. Empty when no two producers claimed the same id. */
68
+ readonly conflicts: readonly CompositionConflict[];
69
+ }
70
+ /**
71
+ * Compose several sources into one, in the given order.
72
+ *
73
+ * Order is significant and deliberate: the first producer to claim a node id
74
+ * keeps it. Passing the language adapter first therefore means a route it
75
+ * extracted from source outranks the same route read from a contract, which is
76
+ * the precedence a framework extractor should have — it saw the code.
77
+ */
78
+ export declare function composeSources(sources: readonly IRSource[], composedId?: string): ComposedSource;
79
+ //# sourceMappingURL=compose.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compose.d.ts","sourceRoot":"","sources":["../../src/conformance/compose.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqDG;AAIH,OAAO,KAAK,EAEV,gBAAgB,EAChB,QAAQ,EAET,MAAM,iBAAiB,CAAC;AACzB,OAAO,KAAK,EAAY,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAE3D,gFAAgF;AAChF,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;IACxB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,cAAe,SAAQ,QAAQ;IAC9C,YAAY,IAAI,gBAAgB,CAAC;IACjC,4EAA4E;IAC5E,QAAQ,CAAC,SAAS,EAAE,SAAS,mBAAmB,EAAE,CAAC;CACpD;AAKD;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,SAAS,QAAQ,EAAE,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,cAAc,CAyGhG"}
@@ -0,0 +1,167 @@
1
+ /**
2
+ * Several producers, graded as the graph will actually see them.
3
+ *
4
+ * ## Why a per-adapter gate is not enough on its own
5
+ *
6
+ * `runConformance` grades **one** adapter, and that is right: a release gate
7
+ * certifies a thing that can be released. But four of the fourteen golden
8
+ * patterns assert nodes from **two different producers at once**, and no
9
+ * single-adapter run can ever satisfy one of those. Pattern 04 is the clearest
10
+ * case:
11
+ *
12
+ * API_ROUTE ──SERVES_API──▶ API_ENDPOINT and a FUNCTION handler
13
+ * (a contract, or a framework extractor) (the language adapter)
14
+ *
15
+ * `adapter-openapi` produces the route and the endpoint and genuinely cannot
16
+ * produce the handler; the language adapter produces the handler and genuinely
17
+ * cannot produce the route. Run apart, each correctly reports a skip for a type
18
+ * it does not declare — and the pattern is then **permanently unmeasured**,
19
+ * which reads exactly like a gap in the product when it is a gap in the harness.
20
+ *
21
+ * Composing them is not a loosening. It is the arrangement the product ships in:
22
+ * the Graph Builder merges every `IRSource` over one repository, and a question
23
+ * like *which handler serves this endpoint* is only ever answerable in the
24
+ * merged graph. What the composed run certifies is a **pair**, and it is
25
+ * reported separately from each adapter's own gate so neither number is diluted.
26
+ *
27
+ * ## What this is not
28
+ *
29
+ * It is not the Graph Builder. It concatenates batches and drops a node whose id
30
+ * a previous batch already emitted, which is the merge rule stated at its
31
+ * simplest (DEC-012); the builder additionally reconciles attributes, resolution
32
+ * and confidence across producers, and that reconciliation has its own tests.
33
+ * Using the real builder here would make a conformance failure ambiguous between
34
+ * the adapters and the merge — so this stays deliberately thin, and a
35
+ * disagreement between two producers over one id is **reported** rather than
36
+ * silently resolved.
37
+ *
38
+ * ## The one thing it gives up, and why that is allowed here
39
+ *
40
+ * The Normaliser requires every node in a batch to carry the batch own
41
+ * `producedBy`, because the writer invalidates by `(producedBy, sourceFiles)`
42
+ * and a node stamped differently would be inserted by a batch and then never
43
+ * deleted by it. That rule is load-bearing for storage and is not weakened: the
44
+ * composed batch **restamps** every node and edge with the composition id, which
45
+ * costs per-node provenance *inside this run*.
46
+ *
47
+ * That is acceptable only because a composed run stores nothing and certifies
48
+ * nothing shippable. The per-adapter gates above it keep full provenance and are
49
+ * what release depends on; this run answers one question — can the pair satisfy
50
+ * a pattern neither can satisfy alone — and is reported separately so no
51
+ * adapter number is diluted. The product itself never does this: the Graph
52
+ * Builder normalises each batch on its own and merges the results, which is why
53
+ * it keeps the provenance this does not.
54
+ */
55
+ const hasCapabilities = (source) => typeof source.capabilities === "function";
56
+ /**
57
+ * Compose several sources into one, in the given order.
58
+ *
59
+ * Order is significant and deliberate: the first producer to claim a node id
60
+ * keeps it. Passing the language adapter first therefore means a route it
61
+ * extracted from source outranks the same route read from a contract, which is
62
+ * the precedence a framework extractor should have — it saw the code.
63
+ */
64
+ export function composeSources(sources, composedId) {
65
+ const conflicts = [];
66
+ // The id half of `producedBy` admits no `+`; the version half does. Both
67
+ // halves are checked by the Normaliser and a malformed one fails the whole
68
+ // batch, which is the boundary working rather than an obstacle.
69
+ const id = (composedId ?? sources.map((source) => source.id).join("-")).replace(/[^A-Za-z0-9._-]/g, "-");
70
+ const version = sources.map((source) => source.version).join("+");
71
+ const stamp = `${id}@${version}`;
72
+ return {
73
+ id,
74
+ version,
75
+ conflicts,
76
+ capabilities() {
77
+ const matrices = sources.filter(hasCapabilities).map((source) => source.capabilities());
78
+ const nodeTypes = new Set();
79
+ const edgeTypes = new Set();
80
+ const extractors = new Set();
81
+ const supplied = new Set();
82
+ let maxResolution = 0;
83
+ for (const matrix of matrices) {
84
+ for (const type of matrix.nodeTypes)
85
+ nodeTypes.add(type);
86
+ for (const type of matrix.edgeTypes)
87
+ edgeTypes.add(type);
88
+ for (const extractor of matrix.frameworkExtractors)
89
+ extractors.add(extractor);
90
+ for (const capability of matrix.frameworkCapabilities ?? [])
91
+ supplied.add(capability);
92
+ // The union's ceiling is the best any member reaches. A pattern is then
93
+ // graded against the producer that can actually satisfy it rather than
94
+ // against the weakest member of the pair.
95
+ if (matrix.maxResolution > maxResolution)
96
+ maxResolution = matrix.maxResolution;
97
+ }
98
+ return {
99
+ maxResolution,
100
+ nodeTypes: [...nodeTypes],
101
+ edgeTypes: [...edgeTypes],
102
+ hasCallHierarchy: matrices.some((matrix) => matrix.hasCallHierarchy),
103
+ hasTypeHierarchy: matrices.some((matrix) => matrix.hasTypeHierarchy),
104
+ frameworkExtractors: [...extractors],
105
+ // The union of what the members supply, and only declared when
106
+ // something is bound. A composed source binding extractors and
107
+ // declaring no capability is the same adapter failure as a single one:
108
+ // the check lives in manifest-check.ts and applies here unchanged.
109
+ ...(extractors.size === 0 ? {} : { frameworkCapabilities: [...supplied] }),
110
+ };
111
+ },
112
+ async emit(ctx) {
113
+ conflicts.length = 0;
114
+ const nodes = new Map();
115
+ const owner = new Map();
116
+ const edges = new Map();
117
+ const unresolved = [];
118
+ const sourceFiles = [];
119
+ const skippedFiles = [];
120
+ let reached = 0;
121
+ let first;
122
+ for (const source of sources) {
123
+ const batch = await source.emit(ctx);
124
+ first ??= batch;
125
+ sourceFiles.push(...batch.sourceFiles);
126
+ skippedFiles.push(...batch.skippedFiles);
127
+ if (batch.reachedResolution > reached)
128
+ reached = batch.reachedResolution;
129
+ for (const node of batch.nodes) {
130
+ const held = nodes.get(node.id);
131
+ if (held !== undefined) {
132
+ conflicts.push({
133
+ nodeId: node.id,
134
+ keptFrom: owner.get(node.id) ?? "?",
135
+ alsoFrom: batch.producedBy,
136
+ type: held.type,
137
+ name: held.name,
138
+ });
139
+ continue;
140
+ }
141
+ nodes.set(node.id, { ...node, producedBy: stamp });
142
+ owner.set(node.id, batch.producedBy);
143
+ }
144
+ for (const edge of batch.edges) {
145
+ edges.set(`${edge.from} ${edge.to} ${edge.type}`, { ...edge, producedBy: stamp });
146
+ }
147
+ unresolved.push(...batch.unresolved.map((ref) => ({ ...ref, producedBy: stamp })));
148
+ }
149
+ if (first === undefined) {
150
+ throw new TypeError("composeSources: at least one source is required");
151
+ }
152
+ return {
153
+ repo: first.repo,
154
+ ...(first.workspace === undefined ? {} : { workspace: first.workspace }),
155
+ commitSha: first.commitSha,
156
+ producedBy: stamp,
157
+ sourceFiles: [...new Set(sourceFiles)].sort(),
158
+ skippedFiles,
159
+ reachedResolution: reached,
160
+ nodes: [...nodes.values()],
161
+ edges: [...edges.values()],
162
+ unresolved,
163
+ };
164
+ },
165
+ };
166
+ }
167
+ //# sourceMappingURL=compose.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compose.js","sourceRoot":"","sources":["../../src/conformance/compose.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,110 @@
1
+ /**
2
+ * The golden corpus, parsed and validated — `fixtures/golden/patterns.json`.
3
+ *
4
+ * The golden is hand-written JSON that outlives every adapter, so it is parsed
5
+ * **strictly**: an unknown key is an error, not something to ignore. A typo in
6
+ * `requiredAtResolution` that silently parsed as "no requirement" would turn a
7
+ * recall gate into a no-op, and nothing downstream would ever notice — the suite
8
+ * would go green and stay green.
9
+ *
10
+ * One golden, N language bindings (`fixtures/README.md`). Nothing here is
11
+ * per-language, and nothing here may become per-language: the moment a pattern
12
+ * needs a different expectation per adapter it has stopped being a conformance
13
+ * pattern and become a language quirk, which belongs in `attrs`.
14
+ */
15
+ import { type FrameworkCapability } from "../capabilities.ts";
16
+ import type { ResolutionLevel } from "../ir.ts";
17
+ import { type EdgeType, type NodeType } from "../vocabulary.ts";
18
+ /** The wildcard target in a `forbiddenEdges` entry. See `compare.ts` for why it matters. */
19
+ export declare const ANY_TARGET = "*";
20
+ export interface GoldenNode {
21
+ readonly role: string;
22
+ readonly type: NodeType;
23
+ /** Asserts two roles resolve to nodes in the same file. Compares `node.file`. */
24
+ readonly sameFileAs?: string;
25
+ readonly attrs?: Readonly<Record<string, unknown>>;
26
+ }
27
+ export interface GoldenEdge {
28
+ readonly from: string;
29
+ readonly to: string;
30
+ readonly type: EdgeType;
31
+ /** Lowest level at which the edge MUST be present. Absence below it is correct. */
32
+ readonly requiredAtResolution?: ResolutionLevel;
33
+ readonly attrs?: Readonly<Record<string, unknown>>;
34
+ }
35
+ export interface GoldenForbiddenEdge {
36
+ readonly from: string;
37
+ /** A role, or `ANY_TARGET` — the licence to look outside the role-bound set. */
38
+ readonly to: string;
39
+ readonly type: EdgeType;
40
+ readonly atAnyResolution?: boolean;
41
+ }
42
+ /**
43
+ * A role the corpus declares and the adapter must NOT produce a node for.
44
+ *
45
+ * The mirror of every other role: a binding is normally an assertion that
46
+ * something exists, and this one asserts that nothing does. It is how the corpus
47
+ * states an OVER-ADMISSION trap — a call site spelled exactly like a
48
+ * registration whose receiver is not a router, which a name-based rule admits
49
+ * and a provenance-based rule refuses.
50
+ *
51
+ * Why a role rather than a literal in the golden: the trap's spelling is
52
+ * per-language (`mux.HandleFunc("/health", ...)`, `Preferences::get(...)`,
53
+ * `@mock.patch(...)`) and per-language detail belongs in the manifest. The
54
+ * golden names the rule; the manifest names the trap. `alternates` on the
55
+ * binding are every spelling a wrong adapter might give it, and any of them
56
+ * binding is the failure.
57
+ *
58
+ * **A corpus that declares no such role is SKIPPED, never passed.** A negative
59
+ * nothing can violate is decorative, and reporting it green is exactly the
60
+ * "green means covered" failure this corpus exists to prevent.
61
+ */
62
+ export interface GoldenMustNotBind {
63
+ readonly role: string;
64
+ readonly why: string;
65
+ }
66
+ export interface GoldenMustNotResolveTo {
67
+ /** `"from_role->to_role"`, naming one of the pattern's expected edges. */
68
+ readonly edge: string;
69
+ /** The role the edge must NOT terminate at. */
70
+ readonly target: string;
71
+ readonly why: string;
72
+ }
73
+ export interface GoldenPattern {
74
+ readonly description: string;
75
+ readonly nodes: readonly GoldenNode[];
76
+ readonly edges: readonly GoldenEdge[];
77
+ /** Applies to this pattern's node assertions, and to edges that state no level. */
78
+ readonly requiredAtResolution?: ResolutionLevel;
79
+ /**
80
+ * The framework capability a language adapter alone cannot supply (§11A.8).
81
+ *
82
+ * Named rather than boolean, so an adapter that reads routes and no ORM is
83
+ * held to the route pattern and excused the ORM ones. See `capabilities.ts`.
84
+ */
85
+ readonly requires?: FrameworkCapability;
86
+ readonly forbiddenEdges?: readonly GoldenForbiddenEdge[];
87
+ readonly mustNotResolveTo?: GoldenMustNotResolveTo;
88
+ /** Roles that must bind to nothing. See `GoldenMustNotBind`. */
89
+ readonly mustNotBind?: readonly GoldenMustNotBind[];
90
+ readonly note?: string;
91
+ }
92
+ export interface Golden {
93
+ readonly version: number;
94
+ readonly patterns: Readonly<Record<string, GoldenPattern>>;
95
+ }
96
+ export declare class GoldenError extends Error {
97
+ readonly name = "GoldenError";
98
+ }
99
+ export declare function parseGolden(raw: unknown): Golden;
100
+ export declare function loadGolden(path: string): Golden;
101
+ /** Every role any pattern names. Used to check a manifest binds the whole corpus. */
102
+ export declare function goldenRoles(golden: Golden): readonly string[];
103
+ /**
104
+ * The roles a single pattern needs bound before it can be checked.
105
+ *
106
+ * `forbiddenEdges[].to === ANY_TARGET` contributes nothing here, which is the
107
+ * point of the wildcard: it names no role, so it needs none bound.
108
+ */
109
+ export declare function patternRoles(pattern: GoldenPattern): readonly string[];
110
+ //# sourceMappingURL=golden.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"golden.d.ts","sourceRoot":"","sources":["../../src/conformance/golden.ts"],"names":[],"mappings":"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"}