autotel-schema 3.0.0 → 4.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/README.md +39 -15
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.d.cts.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js.map +1 -1
- package/dist/contract-Ymh_Q37N.d.cts.map +1 -1
- package/dist/contract-Ymh_Q37N.d.ts.map +1 -1
- package/dist/{diff-B1DoDhUn.d.ts → diff-CAIC04O4.d.ts} +4 -3
- package/dist/diff-CAIC04O4.d.ts.map +1 -0
- package/dist/{diff-Cjs6OPFN.d.cts → diff-CfvhW7ux.d.cts} +4 -3
- package/dist/diff-CfvhW7ux.d.cts.map +1 -0
- package/dist/diff.cjs.map +1 -1
- package/dist/diff.d.cts +1 -1
- package/dist/diff.d.ts +1 -1
- package/dist/diff.js.map +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -3
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.ts +2 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/processor-BPp_DewZ.js.map +1 -1
- package/dist/{processor-BsO4WD73.d.cts → processor-DVcsS6Rz.d.cts} +1 -2
- package/dist/processor-DVcsS6Rz.d.cts.map +1 -0
- package/dist/processor-Gu8Yl_dS.cjs.map +1 -1
- package/dist/{processor-CIEfOOYi.d.ts → processor-TpuacLr5.d.ts} +1 -2
- package/dist/processor-TpuacLr5.d.ts.map +1 -0
- package/dist/processor.d.cts +1 -1
- package/dist/processor.d.ts +1 -1
- package/dist/snapshot-CyWGJaJT.cjs.map +1 -1
- package/dist/snapshot-h8pb_Up_.js.map +1 -1
- package/package.json +4 -4
- package/dist/diff-B1DoDhUn.d.ts.map +0 -1
- package/dist/diff-Cjs6OPFN.d.cts.map +0 -1
- package/dist/processor-BsO4WD73.d.cts.map +0 -1
- package/dist/processor-CIEfOOYi.d.ts.map +0 -1
package/README.md
CHANGED
|
@@ -29,13 +29,21 @@ export const contract = defineContract({
|
|
|
29
29
|
service: 'checkout',
|
|
30
30
|
version: '1.2.0', // semver of the *contract*, not the app
|
|
31
31
|
commonAttributes: {
|
|
32
|
-
'user.id': {
|
|
32
|
+
'user.id': {
|
|
33
|
+
type: 'string',
|
|
34
|
+
highCardinality: true,
|
|
35
|
+
description: 'Authenticated user',
|
|
36
|
+
},
|
|
33
37
|
},
|
|
34
38
|
spans: {
|
|
35
39
|
'checkout.charge': {
|
|
36
40
|
description: 'Charge a payment method',
|
|
37
41
|
attributes: {
|
|
38
|
-
'payment.provider': {
|
|
42
|
+
'payment.provider': {
|
|
43
|
+
type: 'string',
|
|
44
|
+
required: true,
|
|
45
|
+
enum: ['stripe', 'paypal'],
|
|
46
|
+
},
|
|
39
47
|
'payment.amount_cents': { type: 'number', required: true },
|
|
40
48
|
},
|
|
41
49
|
},
|
|
@@ -71,7 +79,8 @@ const violations = validateSpan(
|
|
|
71
79
|
contract,
|
|
72
80
|
);
|
|
73
81
|
// → [missing_required payment.amount_cents, enum_violation payment.provider]
|
|
74
|
-
if (hasErrors(violations))
|
|
82
|
+
if (hasErrors(violations))
|
|
83
|
+
violations.forEach((v) => console.error(formatViolation(v)));
|
|
75
84
|
```
|
|
76
85
|
|
|
77
86
|
Violation codes: `missing_required`, `type_mismatch`, `enum_violation`, `unknown_attribute` (with a "did you mean?" suggestion via edit distance), and `unknown_span`.
|
|
@@ -85,7 +94,10 @@ import { contractToSnapshot, serializeSnapshot } from 'autotel-schema';
|
|
|
85
94
|
import { writeFileSync } from 'node:fs';
|
|
86
95
|
import { contract } from './telemetry.contract';
|
|
87
96
|
|
|
88
|
-
writeFileSync(
|
|
97
|
+
writeFileSync(
|
|
98
|
+
'telemetry.snapshot.json',
|
|
99
|
+
serializeSnapshot(contractToSnapshot(contract)),
|
|
100
|
+
);
|
|
89
101
|
```
|
|
90
102
|
|
|
91
103
|
Then in CI, with the bundled `autotel-schema` CLI:
|
|
@@ -100,7 +112,11 @@ autotel-schema diff telemetry.baseline.json telemetry.current.json --json
|
|
|
100
112
|
Programmatically:
|
|
101
113
|
|
|
102
114
|
```ts
|
|
103
|
-
import {
|
|
115
|
+
import {
|
|
116
|
+
diffSnapshots,
|
|
117
|
+
hasBreakingChanges,
|
|
118
|
+
formatDiff,
|
|
119
|
+
} from 'autotel-schema/diff';
|
|
104
120
|
|
|
105
121
|
const diff = diffSnapshots(baseline, current);
|
|
106
122
|
if (hasBreakingChanges(diff)) throw new Error(formatDiff(diff));
|
|
@@ -117,22 +133,29 @@ import { contract } from './telemetry.contract';
|
|
|
117
133
|
|
|
118
134
|
init({
|
|
119
135
|
service: 'checkout',
|
|
120
|
-
attributeRedactor: {
|
|
136
|
+
attributeRedactor: {
|
|
137
|
+
allowKeys: highCardinalityKeys(contract),
|
|
138
|
+
preset: 'strict',
|
|
139
|
+
},
|
|
121
140
|
});
|
|
122
141
|
```
|
|
123
142
|
|
|
124
143
|
## 5. Scenario conformance: contract one exercised flow
|
|
125
144
|
|
|
126
|
-
Spans and attributes contract your telemetry
|
|
145
|
+
Spans and attributes contract your telemetry _surface_. A **scenario**contracts the _behaviour_ of one exercised flow: which events must fire, how many times, in what topology, and, for async flows,**when the observation is complete**, because without a completion boundary a missing event and an event that hasn't fired _yet_ are indistinguishable.
|
|
127
146
|
|
|
128
147
|
```ts
|
|
129
148
|
export const contract = defineContract({
|
|
130
149
|
service: 'transfer',
|
|
131
150
|
version: '1.3.0',
|
|
132
|
-
spans: {
|
|
151
|
+
spans: {/* ... */},
|
|
133
152
|
scenarios: {
|
|
134
153
|
'transfer.accept': {
|
|
135
|
-
completion: {
|
|
154
|
+
completion: {
|
|
155
|
+
mode: 'terminal-event',
|
|
156
|
+
event: 'transfer.queued',
|
|
157
|
+
observationBudgetMs: 5000,
|
|
158
|
+
},
|
|
136
159
|
events: {
|
|
137
160
|
'transfer.request': { cardinality: 'exactly 1' },
|
|
138
161
|
'transfer.validate': { cardinality: 'exactly 1' },
|
|
@@ -145,7 +168,7 @@ export const contract = defineContract({
|
|
|
145
168
|
});
|
|
146
169
|
```
|
|
147
170
|
|
|
148
|
-
Checking yields **three** outcomes, not two
|
|
171
|
+
Checking yields **three** outcomes, not two. Infrastructure slowness is not reported as behavioural regression:
|
|
149
172
|
|
|
150
173
|
```ts
|
|
151
174
|
import { checkScenario, formatScenarioResult } from 'autotel-schema';
|
|
@@ -159,17 +182,18 @@ const result = await checkScenario(
|
|
|
159
182
|
// 'conformant' — boundary closed, signature satisfied
|
|
160
183
|
// 'non-conformant' — required behaviour missing or invalid (definitive)
|
|
161
184
|
// 'incomplete' — boundary didn't close within the observation budget
|
|
162
|
-
if (result.outcome !== 'conformant')
|
|
185
|
+
if (result.outcome !== 'conformant')
|
|
186
|
+
throw new Error(formatScenarioResult(result));
|
|
163
187
|
```
|
|
164
188
|
|
|
165
189
|
Semantics worth knowing:
|
|
166
190
|
|
|
167
191
|
- **Absence is definitive only after closure** (closed-world). Excess is definitive immediately: an unexpected error span or an exceeded `max` cardinality fails fast while the flow is still open.
|
|
168
|
-
- **Undeclared events are additive
|
|
169
|
-
- **The observation budget bounds how long the checker waits.** It is not a business SLO
|
|
192
|
+
- **Undeclared events are additive**: reported in `result.additions`, never a failure. Improving instrumentation must not break CI.
|
|
193
|
+
- **The observation budget bounds how long the checker waits.** It is not a business SLO: assert deadlines separately on `result.spans`.
|
|
170
194
|
- An `externally-reconciled` boundary never closes in-process: use it for phases verified by a deferred job keyed by a durable business ID.
|
|
171
195
|
|
|
172
|
-
Don't hand-write the first draft
|
|
196
|
+
Don't hand-write the first draft. Record it:
|
|
173
197
|
|
|
174
198
|
```ts
|
|
175
199
|
import { proposeScenario } from 'autotel-schema';
|
|
@@ -182,7 +206,7 @@ Stable events become required cardinality, variable ones get ranges flagged for
|
|
|
182
206
|
|
|
183
207
|
## What this is / isn't
|
|
184
208
|
|
|
185
|
-
- **Is**: a contract for your telemetry surface: span names, attribute keys, types, enums, stability, and breaking-vs-additive evolution
|
|
209
|
+
- **Is**: a contract for your telemetry surface: span names, attribute keys, types, enums, stability, and breaking-vs-additive evolution: and, per scenario, the behavioural signature of one exercised flow (events, cardinality, topology, completion).
|
|
186
210
|
- **Isn't**: a contract for application message payloads (use [`autotel-message-contract`](../autotel-message-contract)) or evidence that contracted interactions ran (use [`autotel-pact`](../autotel-pact)). It does not require the OpenTelemetry SDK. The processor works against structural span types.
|
|
187
211
|
|
|
188
212
|
## License
|
package/dist/cli.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.cjs","names":["diffSnapshots","parseSnapshot","formatDiff","hasBreakingChanges","path"],"sources":["../src/cli.ts"],"sourcesContent":["#!/usr/bin/env node\n/**\n * autotel-schema CLI — the CI gate for your telemetry's public API.\n *\n * autotel-schema diff <baseline.json> <current.json> # classify changes\n * autotel-schema check <baseline.json> <current.json> # exit 1 on breaking\n *\n * Both operate on snapshot JSON produced by `serializeSnapshot(contractToSnapshot(contract))`.\n * Commit the baseline; regenerate `current` in CI; gate the merge on `check`.\n */\n\nimport { readFileSync } from 'node:fs';\nimport path from 'node:path';\nimport { parseSnapshot } from './snapshot.js';\nimport {\n diffSnapshots,\n formatDiff,\n hasBreakingChanges,\n type SnapshotDiff,\n} from './diff.js';\n\ninterface Parsed {\n command: string | undefined;\n baseline: string | undefined;\n current: string | undefined;\n json: boolean;\n}\n\nfunction parseArgs(argv: string[]): Parsed {\n const positional: string[] = [];\n let json = false;\n for (const arg of argv) {\n if (arg === '--json') json = true;\n else if (arg === '-h' || arg === '--help') positional.unshift('help');\n else positional.push(arg);\n }\n return {\n command: positional[0],\n baseline: positional[1],\n current: positional[2],\n json,\n };\n}\n\nconst USAGE = `autotel-schema — treat your trace surface like a versioned public API\n\nUsage:\n autotel-schema diff <baseline.json> <current.json> [--json]\n autotel-schema check <baseline.json> <current.json> [--json]\n\nCommands:\n diff Print every change (breaking / additive / neutral). Always exits 0.\n check Like diff, but exits 1 if any breaking change is found (CI gate).\n\nSnapshots are produced with serializeSnapshot(contractToSnapshot(contract)).`;\n\nfunction loadDiff(baseline: string, current: string): SnapshotDiff {\n const prev = parseSnapshot(readFileSync(baseline, 'utf8'));\n const next = parseSnapshot(readFileSync(current, 'utf8'));\n return diffSnapshots(prev, next);\n}\n\nfunction emit(diff: SnapshotDiff, json: boolean): void {\n if (json) {\n console.log(JSON.stringify(diff, null, 2));\n } else {\n console.log(formatDiff(diff));\n }\n}\n\nexport function run(argv: string[]): number {\n const { command, baseline, current, json } = parseArgs(argv);\n\n if (!command || command === 'help') {\n console.log(USAGE);\n return command ? 0 : 1;\n }\n\n if (command !== 'diff' && command !== 'check') {\n console.error(`autotel-schema: unknown command \"${command}\"\\n\\n${USAGE}`);\n return 1;\n }\n\n if (!baseline || !current) {\n console.error('autotel-schema: both <baseline.json> and <current.json> are required\\n');\n console.error(USAGE);\n return 1;\n }\n\n let diff: SnapshotDiff;\n try {\n diff = loadDiff(baseline, current);\n } catch (error) {\n console.error(\n `autotel-schema: ${error instanceof Error ? error.message : String(error)}`,\n );\n return 1;\n }\n\n emit(diff, json);\n\n if (command === 'check' && hasBreakingChanges(diff)) {\n console.error(\n `\\nautotel-schema: ${diff.breaking.length} breaking change(s) to the telemetry contract. ` +\n `Bump the contract major version and update the committed snapshot.`,\n );\n return 1;\n }\n return 0;\n}\n\n// Only auto-run when invoked directly as the binary, not when imported in tests.\n// Match the basename so a repo path containing \"autotel-schema\" can't trigger it.\nconst entry = process.argv[1] ? path.basename(process.argv[1]) : '';\nif (entry === 'autotel-schema' || entry === 'cli.js' || entry === 'cli.cjs') {\n process.exit(run(process.argv.slice(2)));\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA4BA,SAAS,UAAU,MAAwB;CACzC,MAAM,aAAuB,CAAC;CAC9B,IAAI,OAAO;CACX,KAAK,MAAM,OAAO,MAChB,IAAI,QAAQ,UAAU,OAAO;MACxB,IAAI,QAAQ,QAAQ,QAAQ,UAAU,WAAW,QAAQ,MAAM;MAC/D,WAAW,KAAK,GAAG;CAE1B,OAAO;EACL,SAAS,WAAW;EACpB,UAAU,WAAW;EACrB,SAAS,WAAW;EACpB;CACF;AACF;AAEA,MAAM,QAAQ;;;;;;;;;;;AAYd,SAAS,SAAS,UAAkB,SAA+B;CAGjE,OAAOA,2BAFMC,yDAA2B,UAAU,MAAM,CAEhC,GADXA,yDAA2B,SAAS,MAAM,CACzB,CAAC;AACjC;AAEA,SAAS,KAAK,MAAoB,MAAqB;CACrD,IAAI,MACF,QAAQ,IAAI,KAAK,UAAU,MAAM,MAAM,CAAC,CAAC;MAEzC,QAAQ,IAAIC,wBAAW,IAAI,CAAC;AAEhC;AAEA,SAAgB,IAAI,MAAwB;CAC1C,MAAM,EAAE,SAAS,UAAU,SAAS,SAAS,UAAU,IAAI;CAE3D,IAAI,CAAC,WAAW,YAAY,QAAQ;EAClC,QAAQ,IAAI,KAAK;EACjB,OAAO,UAAU,IAAI;CACvB;CAEA,IAAI,YAAY,UAAU,YAAY,SAAS;EAC7C,QAAQ,MAAM,oCAAoC,QAAQ,OAAO,OAAO;EACxE,OAAO;CACT;CAEA,IAAI,CAAC,YAAY,CAAC,SAAS;EACzB,QAAQ,
|
|
1
|
+
{"version":3,"file":"cli.cjs","names":["diffSnapshots","parseSnapshot","formatDiff","hasBreakingChanges","path"],"sources":["../src/cli.ts"],"sourcesContent":["#!/usr/bin/env node\n/**\n * autotel-schema CLI — the CI gate for your telemetry's public API.\n *\n * autotel-schema diff <baseline.json> <current.json> # classify changes\n * autotel-schema check <baseline.json> <current.json> # exit 1 on breaking\n *\n * Both operate on snapshot JSON produced by `serializeSnapshot(contractToSnapshot(contract))`.\n * Commit the baseline; regenerate `current` in CI; gate the merge on `check`.\n */\n\nimport { readFileSync } from 'node:fs';\nimport path from 'node:path';\nimport { parseSnapshot } from './snapshot.js';\nimport {\n diffSnapshots,\n formatDiff,\n hasBreakingChanges,\n type SnapshotDiff,\n} from './diff.js';\n\ninterface Parsed {\n command: string | undefined;\n baseline: string | undefined;\n current: string | undefined;\n json: boolean;\n}\n\nfunction parseArgs(argv: string[]): Parsed {\n const positional: string[] = [];\n let json = false;\n for (const arg of argv) {\n if (arg === '--json') json = true;\n else if (arg === '-h' || arg === '--help') positional.unshift('help');\n else positional.push(arg);\n }\n return {\n command: positional[0],\n baseline: positional[1],\n current: positional[2],\n json,\n };\n}\n\nconst USAGE = `autotel-schema — treat your trace surface like a versioned public API\n\nUsage:\n autotel-schema diff <baseline.json> <current.json> [--json]\n autotel-schema check <baseline.json> <current.json> [--json]\n\nCommands:\n diff Print every change (breaking / additive / neutral). Always exits 0.\n check Like diff, but exits 1 if any breaking change is found (CI gate).\n\nSnapshots are produced with serializeSnapshot(contractToSnapshot(contract)).`;\n\nfunction loadDiff(baseline: string, current: string): SnapshotDiff {\n const prev = parseSnapshot(readFileSync(baseline, 'utf8'));\n const next = parseSnapshot(readFileSync(current, 'utf8'));\n return diffSnapshots(prev, next);\n}\n\nfunction emit(diff: SnapshotDiff, json: boolean): void {\n if (json) {\n console.log(JSON.stringify(diff, null, 2));\n } else {\n console.log(formatDiff(diff));\n }\n}\n\nexport function run(argv: string[]): number {\n const { command, baseline, current, json } = parseArgs(argv);\n\n if (!command || command === 'help') {\n console.log(USAGE);\n return command ? 0 : 1;\n }\n\n if (command !== 'diff' && command !== 'check') {\n console.error(`autotel-schema: unknown command \"${command}\"\\n\\n${USAGE}`);\n return 1;\n }\n\n if (!baseline || !current) {\n console.error(\n 'autotel-schema: both <baseline.json> and <current.json> are required\\n',\n );\n console.error(USAGE);\n return 1;\n }\n\n let diff: SnapshotDiff;\n try {\n diff = loadDiff(baseline, current);\n } catch (error) {\n console.error(\n `autotel-schema: ${error instanceof Error ? error.message : String(error)}`,\n );\n return 1;\n }\n\n emit(diff, json);\n\n if (command === 'check' && hasBreakingChanges(diff)) {\n console.error(\n `\\nautotel-schema: ${diff.breaking.length} breaking change(s) to the telemetry contract. ` +\n `Bump the contract major version and update the committed snapshot.`,\n );\n return 1;\n }\n return 0;\n}\n\n// Only auto-run when invoked directly as the binary, not when imported in tests.\n// Match the basename so a repo path containing \"autotel-schema\" can't trigger it.\nconst entry = process.argv[1] ? path.basename(process.argv[1]) : '';\nif (entry === 'autotel-schema' || entry === 'cli.js' || entry === 'cli.cjs') {\n process.exit(run(process.argv.slice(2)));\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA4BA,SAAS,UAAU,MAAwB;CACzC,MAAM,aAAuB,CAAC;CAC9B,IAAI,OAAO;CACX,KAAK,MAAM,OAAO,MAChB,IAAI,QAAQ,UAAU,OAAO;MACxB,IAAI,QAAQ,QAAQ,QAAQ,UAAU,WAAW,QAAQ,MAAM;MAC/D,WAAW,KAAK,GAAG;CAE1B,OAAO;EACL,SAAS,WAAW;EACpB,UAAU,WAAW;EACrB,SAAS,WAAW;EACpB;CACF;AACF;AAEA,MAAM,QAAQ;;;;;;;;;;;AAYd,SAAS,SAAS,UAAkB,SAA+B;CAGjE,OAAOA,2BAFMC,yDAA2B,UAAU,MAAM,CAEhC,GADXA,yDAA2B,SAAS,MAAM,CACzB,CAAC;AACjC;AAEA,SAAS,KAAK,MAAoB,MAAqB;CACrD,IAAI,MACF,QAAQ,IAAI,KAAK,UAAU,MAAM,MAAM,CAAC,CAAC;MAEzC,QAAQ,IAAIC,wBAAW,IAAI,CAAC;AAEhC;AAEA,SAAgB,IAAI,MAAwB;CAC1C,MAAM,EAAE,SAAS,UAAU,SAAS,SAAS,UAAU,IAAI;CAE3D,IAAI,CAAC,WAAW,YAAY,QAAQ;EAClC,QAAQ,IAAI,KAAK;EACjB,OAAO,UAAU,IAAI;CACvB;CAEA,IAAI,YAAY,UAAU,YAAY,SAAS;EAC7C,QAAQ,MAAM,oCAAoC,QAAQ,OAAO,OAAO;EACxE,OAAO;CACT;CAEA,IAAI,CAAC,YAAY,CAAC,SAAS;EACzB,QAAQ,MACN,wEACF;EACA,QAAQ,MAAM,KAAK;EACnB,OAAO;CACT;CAEA,IAAI;CACJ,IAAI;EACF,OAAO,SAAS,UAAU,OAAO;CACnC,SAAS,OAAO;EACd,QAAQ,MACN,mBAAmB,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,GAC1E;EACA,OAAO;CACT;CAEA,KAAK,MAAM,IAAI;CAEf,IAAI,YAAY,WAAWC,gCAAmB,IAAI,GAAG;EACnD,QAAQ,MACN,qBAAqB,KAAK,SAAS,OAAO,kHAE5C;EACA,OAAO;CACT;CACA,OAAO;AACT;AAIA,MAAM,QAAQ,QAAQ,KAAK,KAAKC,kBAAK,SAAS,QAAQ,KAAK,EAAE,IAAI;AACjE,IAAI,UAAU,oBAAoB,UAAU,YAAY,UAAU,WAChE,QAAQ,KAAK,IAAI,QAAQ,KAAK,MAAM,CAAC,CAAC,CAAC"}
|
package/dist/cli.d.cts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.d.cts","names":[],"sources":["../src/cli.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"cli.d.cts","names":[],"sources":["../src/cli.ts"],"mappings":";;;;;;;;;;iBAsEgB,IAAI"}
|
package/dist/cli.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.d.ts","names":[],"sources":["../src/cli.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"cli.d.ts","names":[],"sources":["../src/cli.ts"],"mappings":";;;;;;;;;;iBAsEgB,IAAI"}
|
package/dist/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.js","names":[],"sources":["../src/cli.ts"],"sourcesContent":["#!/usr/bin/env node\n/**\n * autotel-schema CLI — the CI gate for your telemetry's public API.\n *\n * autotel-schema diff <baseline.json> <current.json> # classify changes\n * autotel-schema check <baseline.json> <current.json> # exit 1 on breaking\n *\n * Both operate on snapshot JSON produced by `serializeSnapshot(contractToSnapshot(contract))`.\n * Commit the baseline; regenerate `current` in CI; gate the merge on `check`.\n */\n\nimport { readFileSync } from 'node:fs';\nimport path from 'node:path';\nimport { parseSnapshot } from './snapshot.js';\nimport {\n diffSnapshots,\n formatDiff,\n hasBreakingChanges,\n type SnapshotDiff,\n} from './diff.js';\n\ninterface Parsed {\n command: string | undefined;\n baseline: string | undefined;\n current: string | undefined;\n json: boolean;\n}\n\nfunction parseArgs(argv: string[]): Parsed {\n const positional: string[] = [];\n let json = false;\n for (const arg of argv) {\n if (arg === '--json') json = true;\n else if (arg === '-h' || arg === '--help') positional.unshift('help');\n else positional.push(arg);\n }\n return {\n command: positional[0],\n baseline: positional[1],\n current: positional[2],\n json,\n };\n}\n\nconst USAGE = `autotel-schema — treat your trace surface like a versioned public API\n\nUsage:\n autotel-schema diff <baseline.json> <current.json> [--json]\n autotel-schema check <baseline.json> <current.json> [--json]\n\nCommands:\n diff Print every change (breaking / additive / neutral). Always exits 0.\n check Like diff, but exits 1 if any breaking change is found (CI gate).\n\nSnapshots are produced with serializeSnapshot(contractToSnapshot(contract)).`;\n\nfunction loadDiff(baseline: string, current: string): SnapshotDiff {\n const prev = parseSnapshot(readFileSync(baseline, 'utf8'));\n const next = parseSnapshot(readFileSync(current, 'utf8'));\n return diffSnapshots(prev, next);\n}\n\nfunction emit(diff: SnapshotDiff, json: boolean): void {\n if (json) {\n console.log(JSON.stringify(diff, null, 2));\n } else {\n console.log(formatDiff(diff));\n }\n}\n\nexport function run(argv: string[]): number {\n const { command, baseline, current, json } = parseArgs(argv);\n\n if (!command || command === 'help') {\n console.log(USAGE);\n return command ? 0 : 1;\n }\n\n if (command !== 'diff' && command !== 'check') {\n console.error(`autotel-schema: unknown command \"${command}\"\\n\\n${USAGE}`);\n return 1;\n }\n\n if (!baseline || !current) {\n console.error('autotel-schema: both <baseline.json> and <current.json> are required\\n');\n console.error(USAGE);\n return 1;\n }\n\n let diff: SnapshotDiff;\n try {\n diff = loadDiff(baseline, current);\n } catch (error) {\n console.error(\n `autotel-schema: ${error instanceof Error ? error.message : String(error)}`,\n );\n return 1;\n }\n\n emit(diff, json);\n\n if (command === 'check' && hasBreakingChanges(diff)) {\n console.error(\n `\\nautotel-schema: ${diff.breaking.length} breaking change(s) to the telemetry contract. ` +\n `Bump the contract major version and update the committed snapshot.`,\n );\n return 1;\n }\n return 0;\n}\n\n// Only auto-run when invoked directly as the binary, not when imported in tests.\n// Match the basename so a repo path containing \"autotel-schema\" can't trigger it.\nconst entry = process.argv[1] ? path.basename(process.argv[1]) : '';\nif (entry === 'autotel-schema' || entry === 'cli.js' || entry === 'cli.cjs') {\n process.exit(run(process.argv.slice(2)));\n}\n"],"mappings":";;;;;;;;;;;;;;;;AA4BA,SAAS,UAAU,MAAwB;CACzC,MAAM,aAAuB,CAAC;CAC9B,IAAI,OAAO;CACX,KAAK,MAAM,OAAO,MAChB,IAAI,QAAQ,UAAU,OAAO;MACxB,IAAI,QAAQ,QAAQ,QAAQ,UAAU,WAAW,QAAQ,MAAM;MAC/D,WAAW,KAAK,GAAG;CAE1B,OAAO;EACL,SAAS,WAAW;EACpB,UAAU,WAAW;EACrB,SAAS,WAAW;EACpB;CACF;AACF;AAEA,MAAM,QAAQ;;;;;;;;;;;AAYd,SAAS,SAAS,UAAkB,SAA+B;CAGjE,OAAO,cAFM,cAAc,aAAa,UAAU,MAAM,CAEhC,GADX,cAAc,aAAa,SAAS,MAAM,CACzB,CAAC;AACjC;AAEA,SAAS,KAAK,MAAoB,MAAqB;CACrD,IAAI,MACF,QAAQ,IAAI,KAAK,UAAU,MAAM,MAAM,CAAC,CAAC;MAEzC,QAAQ,IAAI,WAAW,IAAI,CAAC;AAEhC;AAEA,SAAgB,IAAI,MAAwB;CAC1C,MAAM,EAAE,SAAS,UAAU,SAAS,SAAS,UAAU,IAAI;CAE3D,IAAI,CAAC,WAAW,YAAY,QAAQ;EAClC,QAAQ,IAAI,KAAK;EACjB,OAAO,UAAU,IAAI;CACvB;CAEA,IAAI,YAAY,UAAU,YAAY,SAAS;EAC7C,QAAQ,MAAM,oCAAoC,QAAQ,OAAO,OAAO;EACxE,OAAO;CACT;CAEA,IAAI,CAAC,YAAY,CAAC,SAAS;EACzB,QAAQ,
|
|
1
|
+
{"version":3,"file":"cli.js","names":[],"sources":["../src/cli.ts"],"sourcesContent":["#!/usr/bin/env node\n/**\n * autotel-schema CLI — the CI gate for your telemetry's public API.\n *\n * autotel-schema diff <baseline.json> <current.json> # classify changes\n * autotel-schema check <baseline.json> <current.json> # exit 1 on breaking\n *\n * Both operate on snapshot JSON produced by `serializeSnapshot(contractToSnapshot(contract))`.\n * Commit the baseline; regenerate `current` in CI; gate the merge on `check`.\n */\n\nimport { readFileSync } from 'node:fs';\nimport path from 'node:path';\nimport { parseSnapshot } from './snapshot.js';\nimport {\n diffSnapshots,\n formatDiff,\n hasBreakingChanges,\n type SnapshotDiff,\n} from './diff.js';\n\ninterface Parsed {\n command: string | undefined;\n baseline: string | undefined;\n current: string | undefined;\n json: boolean;\n}\n\nfunction parseArgs(argv: string[]): Parsed {\n const positional: string[] = [];\n let json = false;\n for (const arg of argv) {\n if (arg === '--json') json = true;\n else if (arg === '-h' || arg === '--help') positional.unshift('help');\n else positional.push(arg);\n }\n return {\n command: positional[0],\n baseline: positional[1],\n current: positional[2],\n json,\n };\n}\n\nconst USAGE = `autotel-schema — treat your trace surface like a versioned public API\n\nUsage:\n autotel-schema diff <baseline.json> <current.json> [--json]\n autotel-schema check <baseline.json> <current.json> [--json]\n\nCommands:\n diff Print every change (breaking / additive / neutral). Always exits 0.\n check Like diff, but exits 1 if any breaking change is found (CI gate).\n\nSnapshots are produced with serializeSnapshot(contractToSnapshot(contract)).`;\n\nfunction loadDiff(baseline: string, current: string): SnapshotDiff {\n const prev = parseSnapshot(readFileSync(baseline, 'utf8'));\n const next = parseSnapshot(readFileSync(current, 'utf8'));\n return diffSnapshots(prev, next);\n}\n\nfunction emit(diff: SnapshotDiff, json: boolean): void {\n if (json) {\n console.log(JSON.stringify(diff, null, 2));\n } else {\n console.log(formatDiff(diff));\n }\n}\n\nexport function run(argv: string[]): number {\n const { command, baseline, current, json } = parseArgs(argv);\n\n if (!command || command === 'help') {\n console.log(USAGE);\n return command ? 0 : 1;\n }\n\n if (command !== 'diff' && command !== 'check') {\n console.error(`autotel-schema: unknown command \"${command}\"\\n\\n${USAGE}`);\n return 1;\n }\n\n if (!baseline || !current) {\n console.error(\n 'autotel-schema: both <baseline.json> and <current.json> are required\\n',\n );\n console.error(USAGE);\n return 1;\n }\n\n let diff: SnapshotDiff;\n try {\n diff = loadDiff(baseline, current);\n } catch (error) {\n console.error(\n `autotel-schema: ${error instanceof Error ? error.message : String(error)}`,\n );\n return 1;\n }\n\n emit(diff, json);\n\n if (command === 'check' && hasBreakingChanges(diff)) {\n console.error(\n `\\nautotel-schema: ${diff.breaking.length} breaking change(s) to the telemetry contract. ` +\n `Bump the contract major version and update the committed snapshot.`,\n );\n return 1;\n }\n return 0;\n}\n\n// Only auto-run when invoked directly as the binary, not when imported in tests.\n// Match the basename so a repo path containing \"autotel-schema\" can't trigger it.\nconst entry = process.argv[1] ? path.basename(process.argv[1]) : '';\nif (entry === 'autotel-schema' || entry === 'cli.js' || entry === 'cli.cjs') {\n process.exit(run(process.argv.slice(2)));\n}\n"],"mappings":";;;;;;;;;;;;;;;;AA4BA,SAAS,UAAU,MAAwB;CACzC,MAAM,aAAuB,CAAC;CAC9B,IAAI,OAAO;CACX,KAAK,MAAM,OAAO,MAChB,IAAI,QAAQ,UAAU,OAAO;MACxB,IAAI,QAAQ,QAAQ,QAAQ,UAAU,WAAW,QAAQ,MAAM;MAC/D,WAAW,KAAK,GAAG;CAE1B,OAAO;EACL,SAAS,WAAW;EACpB,UAAU,WAAW;EACrB,SAAS,WAAW;EACpB;CACF;AACF;AAEA,MAAM,QAAQ;;;;;;;;;;;AAYd,SAAS,SAAS,UAAkB,SAA+B;CAGjE,OAAO,cAFM,cAAc,aAAa,UAAU,MAAM,CAEhC,GADX,cAAc,aAAa,SAAS,MAAM,CACzB,CAAC;AACjC;AAEA,SAAS,KAAK,MAAoB,MAAqB;CACrD,IAAI,MACF,QAAQ,IAAI,KAAK,UAAU,MAAM,MAAM,CAAC,CAAC;MAEzC,QAAQ,IAAI,WAAW,IAAI,CAAC;AAEhC;AAEA,SAAgB,IAAI,MAAwB;CAC1C,MAAM,EAAE,SAAS,UAAU,SAAS,SAAS,UAAU,IAAI;CAE3D,IAAI,CAAC,WAAW,YAAY,QAAQ;EAClC,QAAQ,IAAI,KAAK;EACjB,OAAO,UAAU,IAAI;CACvB;CAEA,IAAI,YAAY,UAAU,YAAY,SAAS;EAC7C,QAAQ,MAAM,oCAAoC,QAAQ,OAAO,OAAO;EACxE,OAAO;CACT;CAEA,IAAI,CAAC,YAAY,CAAC,SAAS;EACzB,QAAQ,MACN,wEACF;EACA,QAAQ,MAAM,KAAK;EACnB,OAAO;CACT;CAEA,IAAI;CACJ,IAAI;EACF,OAAO,SAAS,UAAU,OAAO;CACnC,SAAS,OAAO;EACd,QAAQ,MACN,mBAAmB,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,GAC1E;EACA,OAAO;CACT;CAEA,KAAK,MAAM,IAAI;CAEf,IAAI,YAAY,WAAW,mBAAmB,IAAI,GAAG;EACnD,QAAQ,MACN,qBAAqB,KAAK,SAAS,OAAO,kHAE5C;EACA,OAAO;CACT;CACA,OAAO;AACT;AAIA,MAAM,QAAQ,QAAQ,KAAK,KAAK,KAAK,SAAS,QAAQ,KAAK,EAAE,IAAI;AACjE,IAAI,UAAU,oBAAoB,UAAU,YAAY,UAAU,WAChE,QAAQ,KAAK,IAAI,QAAQ,KAAK,MAAM,CAAC,CAAC,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"contract-Ymh_Q37N.d.cts","names":[],"sources":["../src/scenario.ts","../src/contract.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"contract-Ymh_Q37N.d.cts","names":[],"sources":["../src/scenario.ts","../src/contract.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;UAyCiB;EACf;EACA;EACA;EACA;EACA,aAAa;;EAEb;EACA;;;UAIe;EACf;EACA;;;;;;iBAOc,iBAAiB,gBAAgB,cAAc;;UAmC9C;;EAEf,uBAAuB;;;;;EAKvB;EACA;;;;;;;;;;KAWU;EACN;EAA0B;;EAC1B;EAAwB;EAAe;;EACvC;EAA+B;;;UAGpB;EACf;;EAEA,YAAY;;EAEZ,QAAQ,eAAe;;;;;;EAMvB,QAAQ;;EAER,gBAAgB;;KAGN;KAEA;;UAOK;EACf,MAAM;EACN;EACA;EACA;;;UAIe;EACf;EACA;EACA;EACA;;UAGe;EACf;EACA,SAAS;;EAET;EACA,YAAY;EACZ,WAAW;;EAEX,OAAO;;;;;;iBAmBO,qBAAqB,cAAc,MAAM;;iBA8DzC,iBACd,MAAM,cACN,gBAAgB;UAgCD;;EAEf;;EAEA;;;;;;;;;iBAUc,iBACd,MAAM,cACN,gBAAgB,gBAChB,UAAU,0BACT;UA0Fc;;EAEf;;EAEA;;EAEA;;;;;;;;;;;;;;;iBAgBoB,cACpB,MAAM,cACN,yBAAyB,iBAAiB,iBAAiB,iBAC3D,UAAU,uBACT,QAAQ;;iBAkDK,qBAAqB,QAAQ;UAS5B;EACf,UAAU;;EAEV;;;;;;;;iBASc,gBACd,MAAM,cAAc,cAAc,gBAClC;EAAY;IACX;;;;KCxdS;cAGC,0BAA0B;;;;;;KAc3B;cAEC,sBAAsB;;UAOlB;;EAEf,MAAM;;EAEN,YAAY;;EAEZ;;EAEA;;;;;;;EAOA;;EAEA;;EAEA;;EAEA;;;UAIe;;EAEf;;EAEA,YAAY;;EAEZ,aAAa,eAAe;;;;;EAK5B;;;UAIe;;EAEf;;;;;;EAMA;;EAEA,OAAO,eAAe;;EAEtB,mBAAmB,eAAe;;;;;EAKlC;;;;;;EAMA,YAAY,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAiEb,eAAe,UAAU,oBAAoB;;;;;;iBAwC7C,qBACd,UAAU,mBACV,kBACA,cACC;;iBAQa,2BACd,UAAU,mBACV"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"contract-Ymh_Q37N.d.ts","names":[],"sources":["../src/scenario.ts","../src/contract.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"contract-Ymh_Q37N.d.ts","names":[],"sources":["../src/scenario.ts","../src/contract.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;UAyCiB;EACf;EACA;EACA;EACA;EACA,aAAa;;EAEb;EACA;;;UAIe;EACf;EACA;;;;;;iBAOc,iBAAiB,gBAAgB,cAAc;;UAmC9C;;EAEf,uBAAuB;;;;;EAKvB;EACA;;;;;;;;;;KAWU;EACN;EAA0B;;EAC1B;EAAwB;EAAe;;EACvC;EAA+B;;;UAGpB;EACf;;EAEA,YAAY;;EAEZ,QAAQ,eAAe;;;;;;EAMvB,QAAQ;;EAER,gBAAgB;;KAGN;KAEA;;UAOK;EACf,MAAM;EACN;EACA;EACA;;;UAIe;EACf;EACA;EACA;EACA;;UAGe;EACf;EACA,SAAS;;EAET;EACA,YAAY;EACZ,WAAW;;EAEX,OAAO;;;;;;iBAmBO,qBAAqB,cAAc,MAAM;;iBA8DzC,iBACd,MAAM,cACN,gBAAgB;UAgCD;;EAEf;;EAEA;;;;;;;;;iBAUc,iBACd,MAAM,cACN,gBAAgB,gBAChB,UAAU,0BACT;UA0Fc;;EAEf;;EAEA;;EAEA;;;;;;;;;;;;;;;iBAgBoB,cACpB,MAAM,cACN,yBAAyB,iBAAiB,iBAAiB,iBAC3D,UAAU,uBACT,QAAQ;;iBAkDK,qBAAqB,QAAQ;UAS5B;EACf,UAAU;;EAEV;;;;;;;;iBASc,gBACd,MAAM,cAAc,cAAc,gBAClC;EAAY;IACX;;;;KCxdS;cAGC,0BAA0B;;;;;;KAc3B;cAEC,sBAAsB;;UAOlB;;EAEf,MAAM;;EAEN,YAAY;;EAEZ;;EAEA;;;;;;;EAOA;;EAEA;;EAEA;;EAEA;;;UAIe;;EAEf;;EAEA,YAAY;;EAEZ,aAAa,eAAe;;;;;EAK5B;;;UAIe;;EAEf;;;;;;EAMA;;EAEA,OAAO,eAAe;;EAEtB,mBAAmB,eAAe;;;;;EAKlC;;;;;;EAMA,YAAY,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAiEb,eAAe,UAAU,oBAAoB;;;;;;iBAwC7C,qBACd,UAAU,mBACV,kBACA,cACC;;iBAQa,2BACd,UAAU,mBACV"}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { o as Stability, r as AttributeType, s as TelemetryContract } from "./contract-Ymh_Q37N.js";
|
|
2
|
-
|
|
3
2
|
//#region src/attrs.d.ts
|
|
4
3
|
/**
|
|
5
4
|
* Wire constants for the schema contract — the keys autotel-schema reads from
|
|
@@ -13,7 +12,9 @@ import { o as Stability, r as AttributeType, s as TelemetryContract } from "./co
|
|
|
13
12
|
* between "confidently correct" and "confidently wrong" after a rename.
|
|
14
13
|
*/
|
|
15
14
|
declare const SCHEMA_ATTRS: {
|
|
16
|
-
/** The service this contract describes (mirrors `service.name`). */
|
|
15
|
+
/** The service this contract describes (mirrors `service.name`). */
|
|
16
|
+
readonly SERVICE: "telemetry.schema.service";
|
|
17
|
+
/** Semver of the telemetry contract that produced this span. */
|
|
17
18
|
readonly VERSION: "telemetry.schema.version";
|
|
18
19
|
};
|
|
19
20
|
type SchemaAttributeKey = (typeof SCHEMA_ATTRS)[keyof typeof SCHEMA_ATTRS];
|
|
@@ -86,4 +87,4 @@ declare function hasBreakingChanges(diff: SnapshotDiff): boolean;
|
|
|
86
87
|
declare function formatDiff(diff: SnapshotDiff): string;
|
|
87
88
|
//#endregion
|
|
88
89
|
export { diffSnapshots as a, ContractSnapshot as c, contractToSnapshot as d, parseSnapshot as f, SchemaAttributeKey as g, SNAPSHOT_SPEC as h, SnapshotDiff as i, SnapshotAttribute as l, SCHEMA_ATTRS as m, ChangeType as n, formatDiff as o, serializeSnapshot as p, SnapshotChange as r, hasBreakingChanges as s, ChangeKind as t, SnapshotSpan as u };
|
|
89
|
-
//# sourceMappingURL=diff-
|
|
90
|
+
//# sourceMappingURL=diff-CAIC04O4.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"diff-CAIC04O4.d.ts","names":[],"sources":["../src/attrs.ts","../src/snapshot.ts","../src/diff.ts"],"mappings":";;;;;;;;;;;;;cAYa;;WAAA;;WAAA;;KAOD,6BACF,2BAA2B;;cAGxB;;;;UCPI;EACf,MAAM;EACN,WAAW;EACX;EACA;EACA;EACA;EACA;;UAGe;EACf,WAAW;EACX;EACA;EACA,YAAY,eAAe;;;UAIZ;EACf,aAAa;EACb;EACA;EACA,kBAAkB,eAAe;EACjC,OAAO,eAAe;;;;;;;iBA6BR,mBACd,UAAU,oBACT;;iBAgCa,kBAAkB,UAAU;;iBAK5B,cAAc,eAAe;;;KC/FjC;KAEA;UAeK;EACf,MAAM;EACN,MAAM;;EAEN;EACA;EACA;;UAGe;EACf;EACA;EACA;EACA,UAAU;EACV,UAAU;EACV,SAAS;;;;;;iBAuKK,cACd,UAAU,kBACV,MAAM,mBACL;;iBA6Ca,mBAAmB,MAAM;;iBAKzB,WAAW,MAAM"}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { o as Stability, r as AttributeType, s as TelemetryContract } from "./contract-Ymh_Q37N.cjs";
|
|
2
|
-
|
|
3
2
|
//#region src/attrs.d.ts
|
|
4
3
|
/**
|
|
5
4
|
* Wire constants for the schema contract — the keys autotel-schema reads from
|
|
@@ -13,7 +12,9 @@ import { o as Stability, r as AttributeType, s as TelemetryContract } from "./co
|
|
|
13
12
|
* between "confidently correct" and "confidently wrong" after a rename.
|
|
14
13
|
*/
|
|
15
14
|
declare const SCHEMA_ATTRS: {
|
|
16
|
-
/** The service this contract describes (mirrors `service.name`). */
|
|
15
|
+
/** The service this contract describes (mirrors `service.name`). */
|
|
16
|
+
readonly SERVICE: "telemetry.schema.service";
|
|
17
|
+
/** Semver of the telemetry contract that produced this span. */
|
|
17
18
|
readonly VERSION: "telemetry.schema.version";
|
|
18
19
|
};
|
|
19
20
|
type SchemaAttributeKey = (typeof SCHEMA_ATTRS)[keyof typeof SCHEMA_ATTRS];
|
|
@@ -86,4 +87,4 @@ declare function hasBreakingChanges(diff: SnapshotDiff): boolean;
|
|
|
86
87
|
declare function formatDiff(diff: SnapshotDiff): string;
|
|
87
88
|
//#endregion
|
|
88
89
|
export { diffSnapshots as a, ContractSnapshot as c, contractToSnapshot as d, parseSnapshot as f, SchemaAttributeKey as g, SNAPSHOT_SPEC as h, SnapshotDiff as i, SnapshotAttribute as l, SCHEMA_ATTRS as m, ChangeType as n, formatDiff as o, serializeSnapshot as p, SnapshotChange as r, hasBreakingChanges as s, ChangeKind as t, SnapshotSpan as u };
|
|
89
|
-
//# sourceMappingURL=diff-
|
|
90
|
+
//# sourceMappingURL=diff-CfvhW7ux.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"diff-CfvhW7ux.d.cts","names":[],"sources":["../src/attrs.ts","../src/snapshot.ts","../src/diff.ts"],"mappings":";;;;;;;;;;;;;cAYa;;WAAA;;WAAA;;KAOD,6BACF,2BAA2B;;cAGxB;;;;UCPI;EACf,MAAM;EACN,WAAW;EACX;EACA;EACA;EACA;EACA;;UAGe;EACf,WAAW;EACX;EACA;EACA,YAAY,eAAe;;;UAIZ;EACf,aAAa;EACb;EACA;EACA,kBAAkB,eAAe;EACjC,OAAO,eAAe;;;;;;;iBA6BR,mBACd,UAAU,oBACT;;iBAgCa,kBAAkB,UAAU;;iBAK5B,cAAc,eAAe;;;KC/FjC;KAEA;UAeK;EACf,MAAM;EACN,MAAM;;EAEN;EACA;EACA;;UAGe;EACf;EACA;EACA;EACA,UAAU;EACV,UAAU;EACV,SAAS;;;;;;iBAuKK,cACd,UAAU,kBACV,MAAM,mBACL;;iBA6Ca,mBAAmB,MAAM;;iBAKzB,WAAW,MAAM"}
|
package/dist/diff.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"diff.cjs","names":[],"sources":["../src/diff.ts"],"sourcesContent":["/**\n * Snapshot diffing — the CI gate that catches breaking changes to your trace\n * surface before they ship.\n *\n * \"If you wouldn't ship a rename to your public API without a changelog, don't\n * do it to your traces.\" This module is what makes that enforceable: classify\n * every change between two snapshots as breaking, additive, or neutral, and let\n * CI fail on the breaking ones.\n */\n\nimport type { ContractSnapshot, SnapshotAttribute } from './snapshot.js';\n\nexport type ChangeKind = 'breaking' | 'additive' | 'neutral';\n\nexport type ChangeType =\n | 'span_removed'\n | 'span_added'\n | 'attribute_removed'\n | 'attribute_added'\n | 'type_changed'\n | 'required_added'\n | 'required_removed'\n | 'enum_value_removed'\n | 'enum_value_added'\n | 'stability_downgraded'\n | 'stability_advanced'\n | 'deprecated'\n | 'replacement_documented';\n\nexport interface SnapshotChange {\n kind: ChangeKind;\n type: ChangeType;\n /** Span the change applies to (`*` = common attributes / contract-wide). */\n span: string;\n attribute?: string;\n message: string;\n}\n\nexport interface SnapshotDiff {\n service: string;\n previousVersion: string;\n nextVersion: string;\n breaking: SnapshotChange[];\n additive: SnapshotChange[];\n neutral: SnapshotChange[];\n}\n\n/**\n * How every cross-stability transition is classified. Keyed `prev->next` so all\n * six transitions are explicit and auditable — no silent fall-through. Same-\n * stability transitions are absent (no change to report).\n */\nconst STABILITY_TRANSITIONS: Record<\n string,\n { kind: ChangeKind; type: ChangeType }\n> = {\n 'stable->experimental': { kind: 'breaking', type: 'stability_downgraded' },\n 'stable->deprecated': { kind: 'additive', type: 'deprecated' },\n 'experimental->stable': { kind: 'neutral', type: 'stability_advanced' },\n 'experimental->deprecated': { kind: 'additive', type: 'deprecated' },\n 'deprecated->stable': { kind: 'neutral', type: 'stability_advanced' },\n 'deprecated->experimental': { kind: 'breaking', type: 'stability_downgraded' },\n};\n\nfunction stabilityMessage(\n type: ChangeType,\n attribute: string,\n prev: SnapshotAttribute,\n next: SnapshotAttribute,\n): string {\n if (type === 'deprecated') {\n return `attribute \"${attribute}\" was deprecated${next.replacedBy ? ` (use \"${next.replacedBy}\")` : ''}`;\n }\n if (type === 'stability_downgraded') {\n return `attribute \"${attribute}\" stability downgraded ${prev.stability} → ${next.stability}`;\n }\n return `attribute \"${attribute}\" promoted ${prev.stability} → ${next.stability}`;\n}\n\nfunction push(\n diff: SnapshotDiff,\n change: SnapshotChange,\n): void {\n if (change.kind === 'breaking') diff.breaking.push(change);\n else if (change.kind === 'additive') diff.additive.push(change);\n else diff.neutral.push(change);\n}\n\nfunction diffAttribute(\n diff: SnapshotDiff,\n span: string,\n attribute: string,\n prev: SnapshotAttribute,\n next: SnapshotAttribute,\n): void {\n if (prev.type !== next.type) {\n push(diff, {\n kind: 'breaking',\n type: 'type_changed',\n span,\n attribute,\n message: `attribute \"${attribute}\" changed type ${prev.type} → ${next.type}`,\n });\n }\n\n if (!prev.required && next.required) {\n push(diff, {\n kind: 'breaking',\n type: 'required_added',\n span,\n attribute,\n message: `attribute \"${attribute}\" became required`,\n });\n } else if (prev.required && !next.required) {\n push(diff, {\n kind: 'additive',\n type: 'required_removed',\n span,\n attribute,\n message: `attribute \"${attribute}\" is no longer required`,\n });\n }\n\n // Enum: removing a permitted value can break a producer that still emits it.\n if (prev.enum && next.enum) {\n const nextSet = new Set(next.enum);\n const removed = prev.enum.filter((v) => !nextSet.has(v));\n const prevSet = new Set(prev.enum);\n const added = next.enum.filter((v) => !prevSet.has(v));\n if (removed.length > 0) {\n push(diff, {\n kind: 'breaking',\n type: 'enum_value_removed',\n span,\n attribute,\n message: `attribute \"${attribute}\" dropped enum value(s) ${JSON.stringify(removed)}`,\n });\n }\n if (added.length > 0) {\n push(diff, {\n kind: 'additive',\n type: 'enum_value_added',\n span,\n attribute,\n message: `attribute \"${attribute}\" added enum value(s) ${JSON.stringify(added)}`,\n });\n }\n }\n\n if (prev.stability !== next.stability) {\n const transition =\n STABILITY_TRANSITIONS[`${prev.stability}->${next.stability}`];\n if (transition) {\n push(diff, {\n ...transition,\n span,\n attribute,\n message: stabilityMessage(transition.type, attribute, prev, next),\n });\n }\n }\n}\n\nfunction diffAttributeMaps(\n diff: SnapshotDiff,\n span: string,\n prev: Record<string, SnapshotAttribute>,\n next: Record<string, SnapshotAttribute>,\n): void {\n for (const [key, prevAttr] of Object.entries(prev)) {\n const nextAttr = next[key];\n if (!nextAttr) {\n // A removed attribute whose replacement is named is a documented\n // migration (still breaking — but reported as such with the pointer).\n push(diff, {\n kind: 'breaking',\n type: prevAttr.replacedBy ? 'replacement_documented' : 'attribute_removed',\n span,\n attribute: key,\n message: prevAttr.replacedBy\n ? `attribute \"${key}\" removed — replaced by \"${prevAttr.replacedBy}\"`\n : `attribute \"${key}\" was removed`,\n });\n continue;\n }\n diffAttribute(diff, span, key, prevAttr, nextAttr);\n }\n for (const key of Object.keys(next)) {\n if (!prev[key]) {\n const added = next[key];\n // Always `attribute_added` (the event is \"new attribute\"); severity rides\n // on `kind` — a new *required* attribute breaks existing producers.\n push(diff, {\n kind: added.required ? 'breaking' : 'additive',\n type: 'attribute_added',\n span,\n attribute: key,\n message: added.required\n ? `new required attribute \"${key}\" added`\n : `new attribute \"${key}\" added`,\n });\n }\n }\n}\n\n/**\n * Diff two snapshots, classifying every change. The `breaking` array is what a\n * CI gate keys off; `hasBreakingChanges()` is the convenience predicate.\n */\nexport function diffSnapshots(\n previous: ContractSnapshot,\n next: ContractSnapshot,\n): SnapshotDiff {\n const diff: SnapshotDiff = {\n service: next.service,\n previousVersion: previous.version,\n nextVersion: next.version,\n breaking: [],\n additive: [],\n neutral: [],\n };\n\n diffAttributeMaps(diff, '*', previous.commonAttributes, next.commonAttributes);\n\n for (const [name, prevSpan] of Object.entries(previous.spans)) {\n const nextSpan = next.spans[name];\n if (!nextSpan) {\n push(diff, {\n kind: 'breaking',\n type: 'span_removed',\n span: name,\n message: `span \"${name}\" was removed`,\n });\n continue;\n }\n diffAttributeMaps(diff, name, prevSpan.attributes, nextSpan.attributes);\n }\n for (const name of Object.keys(next.spans)) {\n if (!previous.spans[name]) {\n push(diff, {\n kind: 'additive',\n type: 'span_added',\n span: name,\n message: `new span \"${name}\" added`,\n });\n }\n }\n\n return diff;\n}\n\n/** `true` when the diff contains at least one breaking change. */\nexport function hasBreakingChanges(diff: SnapshotDiff): boolean {\n return diff.breaking.length > 0;\n}\n\n/** Markdown rendering of a diff — for CI logs and PR comments. */\nexport function formatDiff(diff: SnapshotDiff): string {\n const lines: string[] = [ \n `# Telemetry contract diff: ${diff.service} ${diff.previousVersion} → ${diff.nextVersion}`,\n ''];\n const section = (title: string, changes: SnapshotChange[]) => {\n if (changes.length === 0) return;\n lines.push(`## ${title} (${changes.length})`, '');\n for (const c of changes) {\n const where = c.attribute ? `\\`${c.span}.${c.attribute}\\`` : `\\`${c.span}\\``;\n lines.push(`- ${where}: ${c.message}`);\n }\n lines.push('');\n };\n section('💥 Breaking', diff.breaking);\n section('➕ Additive', diff.additive);\n section('• Neutral', diff.neutral);\n if (\n diff.breaking.length === 0 &&\n diff.additive.length === 0 &&\n diff.neutral.length === 0\n ) {\n lines.push('No changes to the telemetry contract.', '');\n }\n return lines.join('\\n');\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"diff.cjs","names":[],"sources":["../src/diff.ts"],"sourcesContent":["/**\n * Snapshot diffing — the CI gate that catches breaking changes to your trace\n * surface before they ship.\n *\n * \"If you wouldn't ship a rename to your public API without a changelog, don't\n * do it to your traces.\" This module is what makes that enforceable: classify\n * every change between two snapshots as breaking, additive, or neutral, and let\n * CI fail on the breaking ones.\n */\n\nimport type { ContractSnapshot, SnapshotAttribute } from './snapshot.js';\n\nexport type ChangeKind = 'breaking' | 'additive' | 'neutral';\n\nexport type ChangeType =\n | 'span_removed'\n | 'span_added'\n | 'attribute_removed'\n | 'attribute_added'\n | 'type_changed'\n | 'required_added'\n | 'required_removed'\n | 'enum_value_removed'\n | 'enum_value_added'\n | 'stability_downgraded'\n | 'stability_advanced'\n | 'deprecated'\n | 'replacement_documented';\n\nexport interface SnapshotChange {\n kind: ChangeKind;\n type: ChangeType;\n /** Span the change applies to (`*` = common attributes / contract-wide). */\n span: string;\n attribute?: string;\n message: string;\n}\n\nexport interface SnapshotDiff {\n service: string;\n previousVersion: string;\n nextVersion: string;\n breaking: SnapshotChange[];\n additive: SnapshotChange[];\n neutral: SnapshotChange[];\n}\n\n/**\n * How every cross-stability transition is classified. Keyed `prev->next` so all\n * six transitions are explicit and auditable — no silent fall-through. Same-\n * stability transitions are absent (no change to report).\n */\nconst STABILITY_TRANSITIONS: Record<\n string,\n { kind: ChangeKind; type: ChangeType }\n> = {\n 'stable->experimental': { kind: 'breaking', type: 'stability_downgraded' },\n 'stable->deprecated': { kind: 'additive', type: 'deprecated' },\n 'experimental->stable': { kind: 'neutral', type: 'stability_advanced' },\n 'experimental->deprecated': { kind: 'additive', type: 'deprecated' },\n 'deprecated->stable': { kind: 'neutral', type: 'stability_advanced' },\n 'deprecated->experimental': {\n kind: 'breaking',\n type: 'stability_downgraded',\n },\n};\n\nfunction stabilityMessage(\n type: ChangeType,\n attribute: string,\n prev: SnapshotAttribute,\n next: SnapshotAttribute,\n): string {\n if (type === 'deprecated') {\n return `attribute \"${attribute}\" was deprecated${next.replacedBy ? ` (use \"${next.replacedBy}\")` : ''}`;\n }\n if (type === 'stability_downgraded') {\n return `attribute \"${attribute}\" stability downgraded ${prev.stability} → ${next.stability}`;\n }\n return `attribute \"${attribute}\" promoted ${prev.stability} → ${next.stability}`;\n}\n\nfunction push(diff: SnapshotDiff, change: SnapshotChange): void {\n if (change.kind === 'breaking') diff.breaking.push(change);\n else if (change.kind === 'additive') diff.additive.push(change);\n else diff.neutral.push(change);\n}\n\nfunction diffAttribute(\n diff: SnapshotDiff,\n span: string,\n attribute: string,\n prev: SnapshotAttribute,\n next: SnapshotAttribute,\n): void {\n if (prev.type !== next.type) {\n push(diff, {\n kind: 'breaking',\n type: 'type_changed',\n span,\n attribute,\n message: `attribute \"${attribute}\" changed type ${prev.type} → ${next.type}`,\n });\n }\n\n if (!prev.required && next.required) {\n push(diff, {\n kind: 'breaking',\n type: 'required_added',\n span,\n attribute,\n message: `attribute \"${attribute}\" became required`,\n });\n } else if (prev.required && !next.required) {\n push(diff, {\n kind: 'additive',\n type: 'required_removed',\n span,\n attribute,\n message: `attribute \"${attribute}\" is no longer required`,\n });\n }\n\n // Enum: removing a permitted value can break a producer that still emits it.\n if (prev.enum && next.enum) {\n const nextSet = new Set(next.enum);\n const removed = prev.enum.filter((v) => !nextSet.has(v));\n const prevSet = new Set(prev.enum);\n const added = next.enum.filter((v) => !prevSet.has(v));\n if (removed.length > 0) {\n push(diff, {\n kind: 'breaking',\n type: 'enum_value_removed',\n span,\n attribute,\n message: `attribute \"${attribute}\" dropped enum value(s) ${JSON.stringify(removed)}`,\n });\n }\n if (added.length > 0) {\n push(diff, {\n kind: 'additive',\n type: 'enum_value_added',\n span,\n attribute,\n message: `attribute \"${attribute}\" added enum value(s) ${JSON.stringify(added)}`,\n });\n }\n }\n\n if (prev.stability !== next.stability) {\n const transition =\n STABILITY_TRANSITIONS[`${prev.stability}->${next.stability}`];\n if (transition) {\n push(diff, {\n ...transition,\n span,\n attribute,\n message: stabilityMessage(transition.type, attribute, prev, next),\n });\n }\n }\n}\n\nfunction diffAttributeMaps(\n diff: SnapshotDiff,\n span: string,\n prev: Record<string, SnapshotAttribute>,\n next: Record<string, SnapshotAttribute>,\n): void {\n for (const [key, prevAttr] of Object.entries(prev)) {\n const nextAttr = next[key];\n if (!nextAttr) {\n // A removed attribute whose replacement is named is a documented\n // migration (still breaking — but reported as such with the pointer).\n push(diff, {\n kind: 'breaking',\n type: prevAttr.replacedBy\n ? 'replacement_documented'\n : 'attribute_removed',\n span,\n attribute: key,\n message: prevAttr.replacedBy\n ? `attribute \"${key}\" removed — replaced by \"${prevAttr.replacedBy}\"`\n : `attribute \"${key}\" was removed`,\n });\n continue;\n }\n diffAttribute(diff, span, key, prevAttr, nextAttr);\n }\n for (const key of Object.keys(next)) {\n if (!prev[key]) {\n const added = next[key];\n // Always `attribute_added` (the event is \"new attribute\"); severity rides\n // on `kind` — a new *required* attribute breaks existing producers.\n push(diff, {\n kind: added.required ? 'breaking' : 'additive',\n type: 'attribute_added',\n span,\n attribute: key,\n message: added.required\n ? `new required attribute \"${key}\" added`\n : `new attribute \"${key}\" added`,\n });\n }\n }\n}\n\n/**\n * Diff two snapshots, classifying every change. The `breaking` array is what a\n * CI gate keys off; `hasBreakingChanges()` is the convenience predicate.\n */\nexport function diffSnapshots(\n previous: ContractSnapshot,\n next: ContractSnapshot,\n): SnapshotDiff {\n const diff: SnapshotDiff = {\n service: next.service,\n previousVersion: previous.version,\n nextVersion: next.version,\n breaking: [],\n additive: [],\n neutral: [],\n };\n\n diffAttributeMaps(\n diff,\n '*',\n previous.commonAttributes,\n next.commonAttributes,\n );\n\n for (const [name, prevSpan] of Object.entries(previous.spans)) {\n const nextSpan = next.spans[name];\n if (!nextSpan) {\n push(diff, {\n kind: 'breaking',\n type: 'span_removed',\n span: name,\n message: `span \"${name}\" was removed`,\n });\n continue;\n }\n diffAttributeMaps(diff, name, prevSpan.attributes, nextSpan.attributes);\n }\n for (const name of Object.keys(next.spans)) {\n if (!previous.spans[name]) {\n push(diff, {\n kind: 'additive',\n type: 'span_added',\n span: name,\n message: `new span \"${name}\" added`,\n });\n }\n }\n\n return diff;\n}\n\n/** `true` when the diff contains at least one breaking change. */\nexport function hasBreakingChanges(diff: SnapshotDiff): boolean {\n return diff.breaking.length > 0;\n}\n\n/** Markdown rendering of a diff — for CI logs and PR comments. */\nexport function formatDiff(diff: SnapshotDiff): string {\n const lines: string[] = [\n `# Telemetry contract diff: ${diff.service} ${diff.previousVersion} → ${diff.nextVersion}`,\n '',\n ];\n const section = (title: string, changes: SnapshotChange[]) => {\n if (changes.length === 0) return;\n lines.push(`## ${title} (${changes.length})`, '');\n for (const c of changes) {\n const where = c.attribute\n ? `\\`${c.span}.${c.attribute}\\``\n : `\\`${c.span}\\``;\n lines.push(`- ${where}: ${c.message}`);\n }\n lines.push('');\n };\n section('💥 Breaking', diff.breaking);\n section('➕ Additive', diff.additive);\n section('• Neutral', diff.neutral);\n if (\n diff.breaking.length === 0 &&\n diff.additive.length === 0 &&\n diff.neutral.length === 0\n ) {\n lines.push('No changes to the telemetry contract.', '');\n }\n return lines.join('\\n');\n}\n"],"mappings":"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"}
|
package/dist/diff.d.cts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as diffSnapshots, i as SnapshotDiff, n as ChangeType, o as formatDiff, r as SnapshotChange, s as hasBreakingChanges, t as ChangeKind } from "./diff-
|
|
1
|
+
import { a as diffSnapshots, i as SnapshotDiff, n as ChangeType, o as formatDiff, r as SnapshotChange, s as hasBreakingChanges, t as ChangeKind } from "./diff-CfvhW7ux.cjs";
|
|
2
2
|
export { ChangeKind, ChangeType, SnapshotChange, SnapshotDiff, diffSnapshots, formatDiff, hasBreakingChanges };
|
package/dist/diff.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as diffSnapshots, i as SnapshotDiff, n as ChangeType, o as formatDiff, r as SnapshotChange, s as hasBreakingChanges, t as ChangeKind } from "./diff-
|
|
1
|
+
import { a as diffSnapshots, i as SnapshotDiff, n as ChangeType, o as formatDiff, r as SnapshotChange, s as hasBreakingChanges, t as ChangeKind } from "./diff-CAIC04O4.js";
|
|
2
2
|
export { ChangeKind, ChangeType, SnapshotChange, SnapshotDiff, diffSnapshots, formatDiff, hasBreakingChanges };
|
package/dist/diff.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"diff.js","names":[],"sources":["../src/diff.ts"],"sourcesContent":["/**\n * Snapshot diffing — the CI gate that catches breaking changes to your trace\n * surface before they ship.\n *\n * \"If you wouldn't ship a rename to your public API without a changelog, don't\n * do it to your traces.\" This module is what makes that enforceable: classify\n * every change between two snapshots as breaking, additive, or neutral, and let\n * CI fail on the breaking ones.\n */\n\nimport type { ContractSnapshot, SnapshotAttribute } from './snapshot.js';\n\nexport type ChangeKind = 'breaking' | 'additive' | 'neutral';\n\nexport type ChangeType =\n | 'span_removed'\n | 'span_added'\n | 'attribute_removed'\n | 'attribute_added'\n | 'type_changed'\n | 'required_added'\n | 'required_removed'\n | 'enum_value_removed'\n | 'enum_value_added'\n | 'stability_downgraded'\n | 'stability_advanced'\n | 'deprecated'\n | 'replacement_documented';\n\nexport interface SnapshotChange {\n kind: ChangeKind;\n type: ChangeType;\n /** Span the change applies to (`*` = common attributes / contract-wide). */\n span: string;\n attribute?: string;\n message: string;\n}\n\nexport interface SnapshotDiff {\n service: string;\n previousVersion: string;\n nextVersion: string;\n breaking: SnapshotChange[];\n additive: SnapshotChange[];\n neutral: SnapshotChange[];\n}\n\n/**\n * How every cross-stability transition is classified. Keyed `prev->next` so all\n * six transitions are explicit and auditable — no silent fall-through. Same-\n * stability transitions are absent (no change to report).\n */\nconst STABILITY_TRANSITIONS: Record<\n string,\n { kind: ChangeKind; type: ChangeType }\n> = {\n 'stable->experimental': { kind: 'breaking', type: 'stability_downgraded' },\n 'stable->deprecated': { kind: 'additive', type: 'deprecated' },\n 'experimental->stable': { kind: 'neutral', type: 'stability_advanced' },\n 'experimental->deprecated': { kind: 'additive', type: 'deprecated' },\n 'deprecated->stable': { kind: 'neutral', type: 'stability_advanced' },\n 'deprecated->experimental': { kind: 'breaking', type: 'stability_downgraded' },\n};\n\nfunction stabilityMessage(\n type: ChangeType,\n attribute: string,\n prev: SnapshotAttribute,\n next: SnapshotAttribute,\n): string {\n if (type === 'deprecated') {\n return `attribute \"${attribute}\" was deprecated${next.replacedBy ? ` (use \"${next.replacedBy}\")` : ''}`;\n }\n if (type === 'stability_downgraded') {\n return `attribute \"${attribute}\" stability downgraded ${prev.stability} → ${next.stability}`;\n }\n return `attribute \"${attribute}\" promoted ${prev.stability} → ${next.stability}`;\n}\n\nfunction push(\n diff: SnapshotDiff,\n change: SnapshotChange,\n): void {\n if (change.kind === 'breaking') diff.breaking.push(change);\n else if (change.kind === 'additive') diff.additive.push(change);\n else diff.neutral.push(change);\n}\n\nfunction diffAttribute(\n diff: SnapshotDiff,\n span: string,\n attribute: string,\n prev: SnapshotAttribute,\n next: SnapshotAttribute,\n): void {\n if (prev.type !== next.type) {\n push(diff, {\n kind: 'breaking',\n type: 'type_changed',\n span,\n attribute,\n message: `attribute \"${attribute}\" changed type ${prev.type} → ${next.type}`,\n });\n }\n\n if (!prev.required && next.required) {\n push(diff, {\n kind: 'breaking',\n type: 'required_added',\n span,\n attribute,\n message: `attribute \"${attribute}\" became required`,\n });\n } else if (prev.required && !next.required) {\n push(diff, {\n kind: 'additive',\n type: 'required_removed',\n span,\n attribute,\n message: `attribute \"${attribute}\" is no longer required`,\n });\n }\n\n // Enum: removing a permitted value can break a producer that still emits it.\n if (prev.enum && next.enum) {\n const nextSet = new Set(next.enum);\n const removed = prev.enum.filter((v) => !nextSet.has(v));\n const prevSet = new Set(prev.enum);\n const added = next.enum.filter((v) => !prevSet.has(v));\n if (removed.length > 0) {\n push(diff, {\n kind: 'breaking',\n type: 'enum_value_removed',\n span,\n attribute,\n message: `attribute \"${attribute}\" dropped enum value(s) ${JSON.stringify(removed)}`,\n });\n }\n if (added.length > 0) {\n push(diff, {\n kind: 'additive',\n type: 'enum_value_added',\n span,\n attribute,\n message: `attribute \"${attribute}\" added enum value(s) ${JSON.stringify(added)}`,\n });\n }\n }\n\n if (prev.stability !== next.stability) {\n const transition =\n STABILITY_TRANSITIONS[`${prev.stability}->${next.stability}`];\n if (transition) {\n push(diff, {\n ...transition,\n span,\n attribute,\n message: stabilityMessage(transition.type, attribute, prev, next),\n });\n }\n }\n}\n\nfunction diffAttributeMaps(\n diff: SnapshotDiff,\n span: string,\n prev: Record<string, SnapshotAttribute>,\n next: Record<string, SnapshotAttribute>,\n): void {\n for (const [key, prevAttr] of Object.entries(prev)) {\n const nextAttr = next[key];\n if (!nextAttr) {\n // A removed attribute whose replacement is named is a documented\n // migration (still breaking — but reported as such with the pointer).\n push(diff, {\n kind: 'breaking',\n type: prevAttr.replacedBy ? 'replacement_documented' : 'attribute_removed',\n span,\n attribute: key,\n message: prevAttr.replacedBy\n ? `attribute \"${key}\" removed — replaced by \"${prevAttr.replacedBy}\"`\n : `attribute \"${key}\" was removed`,\n });\n continue;\n }\n diffAttribute(diff, span, key, prevAttr, nextAttr);\n }\n for (const key of Object.keys(next)) {\n if (!prev[key]) {\n const added = next[key];\n // Always `attribute_added` (the event is \"new attribute\"); severity rides\n // on `kind` — a new *required* attribute breaks existing producers.\n push(diff, {\n kind: added.required ? 'breaking' : 'additive',\n type: 'attribute_added',\n span,\n attribute: key,\n message: added.required\n ? `new required attribute \"${key}\" added`\n : `new attribute \"${key}\" added`,\n });\n }\n }\n}\n\n/**\n * Diff two snapshots, classifying every change. The `breaking` array is what a\n * CI gate keys off; `hasBreakingChanges()` is the convenience predicate.\n */\nexport function diffSnapshots(\n previous: ContractSnapshot,\n next: ContractSnapshot,\n): SnapshotDiff {\n const diff: SnapshotDiff = {\n service: next.service,\n previousVersion: previous.version,\n nextVersion: next.version,\n breaking: [],\n additive: [],\n neutral: [],\n };\n\n diffAttributeMaps(diff, '*', previous.commonAttributes, next.commonAttributes);\n\n for (const [name, prevSpan] of Object.entries(previous.spans)) {\n const nextSpan = next.spans[name];\n if (!nextSpan) {\n push(diff, {\n kind: 'breaking',\n type: 'span_removed',\n span: name,\n message: `span \"${name}\" was removed`,\n });\n continue;\n }\n diffAttributeMaps(diff, name, prevSpan.attributes, nextSpan.attributes);\n }\n for (const name of Object.keys(next.spans)) {\n if (!previous.spans[name]) {\n push(diff, {\n kind: 'additive',\n type: 'span_added',\n span: name,\n message: `new span \"${name}\" added`,\n });\n }\n }\n\n return diff;\n}\n\n/** `true` when the diff contains at least one breaking change. */\nexport function hasBreakingChanges(diff: SnapshotDiff): boolean {\n return diff.breaking.length > 0;\n}\n\n/** Markdown rendering of a diff — for CI logs and PR comments. */\nexport function formatDiff(diff: SnapshotDiff): string {\n const lines: string[] = [ \n `# Telemetry contract diff: ${diff.service} ${diff.previousVersion} → ${diff.nextVersion}`,\n ''];\n const section = (title: string, changes: SnapshotChange[]) => {\n if (changes.length === 0) return;\n lines.push(`## ${title} (${changes.length})`, '');\n for (const c of changes) {\n const where = c.attribute ? `\\`${c.span}.${c.attribute}\\`` : `\\`${c.span}\\``;\n lines.push(`- ${where}: ${c.message}`);\n }\n lines.push('');\n };\n section('💥 Breaking', diff.breaking);\n section('➕ Additive', diff.additive);\n section('• Neutral', diff.neutral);\n if (\n diff.breaking.length === 0 &&\n diff.additive.length === 0 &&\n diff.neutral.length === 0\n ) {\n lines.push('No changes to the telemetry contract.', '');\n }\n return lines.join('\\n');\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"diff.js","names":[],"sources":["../src/diff.ts"],"sourcesContent":["/**\n * Snapshot diffing — the CI gate that catches breaking changes to your trace\n * surface before they ship.\n *\n * \"If you wouldn't ship a rename to your public API without a changelog, don't\n * do it to your traces.\" This module is what makes that enforceable: classify\n * every change between two snapshots as breaking, additive, or neutral, and let\n * CI fail on the breaking ones.\n */\n\nimport type { ContractSnapshot, SnapshotAttribute } from './snapshot.js';\n\nexport type ChangeKind = 'breaking' | 'additive' | 'neutral';\n\nexport type ChangeType =\n | 'span_removed'\n | 'span_added'\n | 'attribute_removed'\n | 'attribute_added'\n | 'type_changed'\n | 'required_added'\n | 'required_removed'\n | 'enum_value_removed'\n | 'enum_value_added'\n | 'stability_downgraded'\n | 'stability_advanced'\n | 'deprecated'\n | 'replacement_documented';\n\nexport interface SnapshotChange {\n kind: ChangeKind;\n type: ChangeType;\n /** Span the change applies to (`*` = common attributes / contract-wide). */\n span: string;\n attribute?: string;\n message: string;\n}\n\nexport interface SnapshotDiff {\n service: string;\n previousVersion: string;\n nextVersion: string;\n breaking: SnapshotChange[];\n additive: SnapshotChange[];\n neutral: SnapshotChange[];\n}\n\n/**\n * How every cross-stability transition is classified. Keyed `prev->next` so all\n * six transitions are explicit and auditable — no silent fall-through. Same-\n * stability transitions are absent (no change to report).\n */\nconst STABILITY_TRANSITIONS: Record<\n string,\n { kind: ChangeKind; type: ChangeType }\n> = {\n 'stable->experimental': { kind: 'breaking', type: 'stability_downgraded' },\n 'stable->deprecated': { kind: 'additive', type: 'deprecated' },\n 'experimental->stable': { kind: 'neutral', type: 'stability_advanced' },\n 'experimental->deprecated': { kind: 'additive', type: 'deprecated' },\n 'deprecated->stable': { kind: 'neutral', type: 'stability_advanced' },\n 'deprecated->experimental': {\n kind: 'breaking',\n type: 'stability_downgraded',\n },\n};\n\nfunction stabilityMessage(\n type: ChangeType,\n attribute: string,\n prev: SnapshotAttribute,\n next: SnapshotAttribute,\n): string {\n if (type === 'deprecated') {\n return `attribute \"${attribute}\" was deprecated${next.replacedBy ? ` (use \"${next.replacedBy}\")` : ''}`;\n }\n if (type === 'stability_downgraded') {\n return `attribute \"${attribute}\" stability downgraded ${prev.stability} → ${next.stability}`;\n }\n return `attribute \"${attribute}\" promoted ${prev.stability} → ${next.stability}`;\n}\n\nfunction push(diff: SnapshotDiff, change: SnapshotChange): void {\n if (change.kind === 'breaking') diff.breaking.push(change);\n else if (change.kind === 'additive') diff.additive.push(change);\n else diff.neutral.push(change);\n}\n\nfunction diffAttribute(\n diff: SnapshotDiff,\n span: string,\n attribute: string,\n prev: SnapshotAttribute,\n next: SnapshotAttribute,\n): void {\n if (prev.type !== next.type) {\n push(diff, {\n kind: 'breaking',\n type: 'type_changed',\n span,\n attribute,\n message: `attribute \"${attribute}\" changed type ${prev.type} → ${next.type}`,\n });\n }\n\n if (!prev.required && next.required) {\n push(diff, {\n kind: 'breaking',\n type: 'required_added',\n span,\n attribute,\n message: `attribute \"${attribute}\" became required`,\n });\n } else if (prev.required && !next.required) {\n push(diff, {\n kind: 'additive',\n type: 'required_removed',\n span,\n attribute,\n message: `attribute \"${attribute}\" is no longer required`,\n });\n }\n\n // Enum: removing a permitted value can break a producer that still emits it.\n if (prev.enum && next.enum) {\n const nextSet = new Set(next.enum);\n const removed = prev.enum.filter((v) => !nextSet.has(v));\n const prevSet = new Set(prev.enum);\n const added = next.enum.filter((v) => !prevSet.has(v));\n if (removed.length > 0) {\n push(diff, {\n kind: 'breaking',\n type: 'enum_value_removed',\n span,\n attribute,\n message: `attribute \"${attribute}\" dropped enum value(s) ${JSON.stringify(removed)}`,\n });\n }\n if (added.length > 0) {\n push(diff, {\n kind: 'additive',\n type: 'enum_value_added',\n span,\n attribute,\n message: `attribute \"${attribute}\" added enum value(s) ${JSON.stringify(added)}`,\n });\n }\n }\n\n if (prev.stability !== next.stability) {\n const transition =\n STABILITY_TRANSITIONS[`${prev.stability}->${next.stability}`];\n if (transition) {\n push(diff, {\n ...transition,\n span,\n attribute,\n message: stabilityMessage(transition.type, attribute, prev, next),\n });\n }\n }\n}\n\nfunction diffAttributeMaps(\n diff: SnapshotDiff,\n span: string,\n prev: Record<string, SnapshotAttribute>,\n next: Record<string, SnapshotAttribute>,\n): void {\n for (const [key, prevAttr] of Object.entries(prev)) {\n const nextAttr = next[key];\n if (!nextAttr) {\n // A removed attribute whose replacement is named is a documented\n // migration (still breaking — but reported as such with the pointer).\n push(diff, {\n kind: 'breaking',\n type: prevAttr.replacedBy\n ? 'replacement_documented'\n : 'attribute_removed',\n span,\n attribute: key,\n message: prevAttr.replacedBy\n ? `attribute \"${key}\" removed — replaced by \"${prevAttr.replacedBy}\"`\n : `attribute \"${key}\" was removed`,\n });\n continue;\n }\n diffAttribute(diff, span, key, prevAttr, nextAttr);\n }\n for (const key of Object.keys(next)) {\n if (!prev[key]) {\n const added = next[key];\n // Always `attribute_added` (the event is \"new attribute\"); severity rides\n // on `kind` — a new *required* attribute breaks existing producers.\n push(diff, {\n kind: added.required ? 'breaking' : 'additive',\n type: 'attribute_added',\n span,\n attribute: key,\n message: added.required\n ? `new required attribute \"${key}\" added`\n : `new attribute \"${key}\" added`,\n });\n }\n }\n}\n\n/**\n * Diff two snapshots, classifying every change. The `breaking` array is what a\n * CI gate keys off; `hasBreakingChanges()` is the convenience predicate.\n */\nexport function diffSnapshots(\n previous: ContractSnapshot,\n next: ContractSnapshot,\n): SnapshotDiff {\n const diff: SnapshotDiff = {\n service: next.service,\n previousVersion: previous.version,\n nextVersion: next.version,\n breaking: [],\n additive: [],\n neutral: [],\n };\n\n diffAttributeMaps(\n diff,\n '*',\n previous.commonAttributes,\n next.commonAttributes,\n );\n\n for (const [name, prevSpan] of Object.entries(previous.spans)) {\n const nextSpan = next.spans[name];\n if (!nextSpan) {\n push(diff, {\n kind: 'breaking',\n type: 'span_removed',\n span: name,\n message: `span \"${name}\" was removed`,\n });\n continue;\n }\n diffAttributeMaps(diff, name, prevSpan.attributes, nextSpan.attributes);\n }\n for (const name of Object.keys(next.spans)) {\n if (!previous.spans[name]) {\n push(diff, {\n kind: 'additive',\n type: 'span_added',\n span: name,\n message: `new span \"${name}\" added`,\n });\n }\n }\n\n return diff;\n}\n\n/** `true` when the diff contains at least one breaking change. */\nexport function hasBreakingChanges(diff: SnapshotDiff): boolean {\n return diff.breaking.length > 0;\n}\n\n/** Markdown rendering of a diff — for CI logs and PR comments. */\nexport function formatDiff(diff: SnapshotDiff): string {\n const lines: string[] = [\n `# Telemetry contract diff: ${diff.service} ${diff.previousVersion} → ${diff.nextVersion}`,\n '',\n ];\n const section = (title: string, changes: SnapshotChange[]) => {\n if (changes.length === 0) return;\n lines.push(`## ${title} (${changes.length})`, '');\n for (const c of changes) {\n const where = c.attribute\n ? `\\`${c.span}.${c.attribute}\\``\n : `\\`${c.span}\\``;\n lines.push(`- ${where}: ${c.message}`);\n }\n lines.push('');\n };\n section('💥 Breaking', diff.breaking);\n section('➕ Additive', diff.additive);\n section('• Neutral', diff.neutral);\n if (\n diff.breaking.length === 0 &&\n diff.additive.length === 0 &&\n diff.neutral.length === 0\n ) {\n lines.push('No changes to the telemetry contract.', '');\n }\n return lines.join('\\n');\n}\n"],"mappings":"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"}
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","names":["defineContract"],"sources":["../src/redaction.ts","../src/contracts/agent-security.ts"],"sourcesContent":["/**\n * Cardinality posture helpers.\n *\n * The old cardinality rule — \"keep unique-value counts down\" — was a constraint\n * invented because dashboards have pixels and a graph with 10k series is\n * unreadable to a human. An agent does not look at the graph; it reads the\n * spans. A high-cardinality field (the user id, the sender domain, the request\n * id) is then the single most useful attribute on a trace when the agent is\n * chasing one specific failure.\n *\n * So the contract lets you mark attributes `highCardinality: true` as a\n * deliberate signal, and this module turns that into a *protect list*: the keys\n * a redactor or span-name normalizer must NOT strip, even when an aggressive\n * default would otherwise drop them.\n */\n\nimport type { TelemetryContract } from './contract.js';\n\n/**\n * Every attribute key in the contract flagged `highCardinality: true`, across\n * both common and per-span attributes. Feed this into a redaction/normalization\n * allow-list so the fields most useful to an agent reader survive.\n *\n * @example\n * ```ts\n * import { init } from 'autotel';\n * import { highCardinalityKeys } from 'autotel-schema';\n * import { contract } from './telemetry.contract';\n *\n * init({\n * service: 'checkout',\n * // keep user.id / request.id intact even under the strict redactor\n * attributeRedactor: { allowKeys: highCardinalityKeys(contract), preset: 'strict' },\n * });\n * ```\n */\nexport function highCardinalityKeys(contract: TelemetryContract): string[] {\n const keys = new Set<string>();\n for (const [key, spec] of Object.entries(contract.commonAttributes ?? {})) {\n if (spec.highCardinality) keys.add(key);\n }\n for (const spanSpec of Object.values(contract.spans)) {\n for (const [key, spec] of Object.entries(spanSpec.attributes ?? {})) {\n if (spec.highCardinality) keys.add(key);\n }\n }\n return [...keys].toSorted();\n}\n\n/**\n * Predicate form of {@link highCardinalityKeys} — `true` when `key` is declared\n * high-cardinality anywhere in the contract. Useful inside a custom\n * `spanNameNormalizer` or redactor callback.\n */\nexport function isHighCardinalityKey(\n contract: TelemetryContract,\n key: string,\n): boolean {\n if (contract.commonAttributes?.[key]?.highCardinality) return true;\n for (const spanSpec of Object.values(contract.spans)) {\n if (spanSpec.attributes?.[key]?.highCardinality) return true;\n }\n return false;\n}\n","import { defineContract } from '../contract.js';\n\nconst stringAttr = { type: 'string' as const };\nconst boolAttr = { type: 'boolean' as const };\nconst numberAttr = { type: 'number' as const };\nconst stringArrayAttr = { type: 'string[]' as const };\n\n/**\n * Published telemetry contract for Google SAIF-aligned agent security observability.\n * Span names are illustrative — attributes are the stable surface under validation.\n */\nexport const AGENT_SECURITY_TELEMETRY_CONTRACT = defineContract({\n service: 'autotel-agent-security',\n version: '1.0.0',\n commonAttributes: {\n 'autotel.agent': {
|
|
1
|
+
{"version":3,"file":"index.cjs","names":["defineContract"],"sources":["../src/redaction.ts","../src/contracts/agent-security.ts"],"sourcesContent":["/**\n * Cardinality posture helpers.\n *\n * The old cardinality rule — \"keep unique-value counts down\" — was a constraint\n * invented because dashboards have pixels and a graph with 10k series is\n * unreadable to a human. An agent does not look at the graph; it reads the\n * spans. A high-cardinality field (the user id, the sender domain, the request\n * id) is then the single most useful attribute on a trace when the agent is\n * chasing one specific failure.\n *\n * So the contract lets you mark attributes `highCardinality: true` as a\n * deliberate signal, and this module turns that into a *protect list*: the keys\n * a redactor or span-name normalizer must NOT strip, even when an aggressive\n * default would otherwise drop them.\n */\n\nimport type { TelemetryContract } from './contract.js';\n\n/**\n * Every attribute key in the contract flagged `highCardinality: true`, across\n * both common and per-span attributes. Feed this into a redaction/normalization\n * allow-list so the fields most useful to an agent reader survive.\n *\n * @example\n * ```ts\n * import { init } from 'autotel';\n * import { highCardinalityKeys } from 'autotel-schema';\n * import { contract } from './telemetry.contract';\n *\n * init({\n * service: 'checkout',\n * // keep user.id / request.id intact even under the strict redactor\n * attributeRedactor: { allowKeys: highCardinalityKeys(contract), preset: 'strict' },\n * });\n * ```\n */\nexport function highCardinalityKeys(contract: TelemetryContract): string[] {\n const keys = new Set<string>();\n for (const [key, spec] of Object.entries(contract.commonAttributes ?? {})) {\n if (spec.highCardinality) keys.add(key);\n }\n for (const spanSpec of Object.values(contract.spans)) {\n for (const [key, spec] of Object.entries(spanSpec.attributes ?? {})) {\n if (spec.highCardinality) keys.add(key);\n }\n }\n return [...keys].toSorted();\n}\n\n/**\n * Predicate form of {@link highCardinalityKeys} — `true` when `key` is declared\n * high-cardinality anywhere in the contract. Useful inside a custom\n * `spanNameNormalizer` or redactor callback.\n */\nexport function isHighCardinalityKey(\n contract: TelemetryContract,\n key: string,\n): boolean {\n if (contract.commonAttributes?.[key]?.highCardinality) return true;\n for (const spanSpec of Object.values(contract.spans)) {\n if (spanSpec.attributes?.[key]?.highCardinality) return true;\n }\n return false;\n}\n","import { defineContract } from '../contract.js';\n\nconst stringAttr = { type: 'string' as const };\nconst boolAttr = { type: 'boolean' as const };\nconst numberAttr = { type: 'number' as const };\nconst stringArrayAttr = { type: 'string[]' as const };\n\n/**\n * Published telemetry contract for Google SAIF-aligned agent security observability.\n * Span names are illustrative — attributes are the stable surface under validation.\n */\nexport const AGENT_SECURITY_TELEMETRY_CONTRACT = defineContract({\n service: 'autotel-agent-security',\n version: '1.0.0',\n commonAttributes: {\n 'autotel.agent': {\n ...boolAttr,\n required: false,\n description: 'Agent audit marker',\n },\n 'agent.controller.id': {\n ...stringAttr,\n highCardinality: true,\n description: 'Hashed controlling human user id',\n },\n 'agent.input.provenance': {\n ...stringAttr,\n enum: [\n 'user_direct',\n 'user_voice',\n 'rag',\n 'memory',\n 'tool_result',\n 'external_untrusted',\n ],\n },\n 'agent.action.risk_class': {\n ...stringAttr,\n enum: [\n 'read',\n 'write',\n 'destructive',\n 'financial',\n 'exfiltration_capable',\n ],\n },\n 'agent.consent.required': { ...boolAttr },\n 'agent.consent.outcome': {\n ...stringAttr,\n enum: ['approved', 'denied', 'timeout', 'revoked'],\n },\n 'agent.scope.active': { ...stringArrayAttr },\n 'agent.memory.operation': {\n ...stringAttr,\n enum: ['read', 'write', 'delete', 'search'],\n },\n 'agent.memory.isolation_key': { ...stringAttr, highCardinality: true },\n 'agent.plan.step_index': { ...numberAttr },\n 'agent.plan.tool_intents': { ...stringArrayAttr },\n 'agent.plan.risk.verdict': {\n ...stringAttr,\n enum: ['low', 'medium', 'high', 'critical'],\n },\n 'agent.plan.risk.score': { ...numberAttr },\n 'agent.plan.risk.categories': { ...stringArrayAttr },\n 'policy.decision': {\n ...stringAttr,\n enum: ['permit', 'deny', 'challenge', 'observe', 'error'],\n },\n 'tool.input_hash': { ...stringAttr },\n 'tool.output_hash': { ...stringAttr },\n 'mcp.tool.destructive': { ...boolAttr },\n 'mcp.tool.untrusted_content': { ...boolAttr },\n 'mcp.security.injection.verdict': {\n ...stringAttr,\n enum: ['clean', 'suspicious', 'malicious'],\n },\n 'security.event': { ...stringAttr },\n 'security.category': { ...stringAttr },\n 'security.outcome': { ...stringAttr },\n 'security.severity': {\n ...stringAttr,\n enum: ['info', 'warning', 'error', 'critical'],\n },\n },\n spans: {\n 'agent.action': {\n description: 'Scoped agent action or tool call with audit metadata',\n attributes: {\n 'agent.id': { ...stringAttr, required: true },\n 'tool.name': { ...stringAttr },\n },\n },\n 'tools/call': {\n description: 'MCP tool invocation with boundary security signals',\n attributes: {\n 'mcp.tool.name': { ...stringAttr, required: true },\n },\n },\n },\n});\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;AAoCA,SAAgB,oBAAoB,UAAuC;CACzE,MAAM,uBAAO,IAAI,IAAY;CAC7B,KAAK,MAAM,CAAC,KAAK,SAAS,OAAO,QAAQ,SAAS,oBAAoB,CAAC,CAAC,GACtE,IAAI,KAAK,iBAAiB,KAAK,IAAI,GAAG;CAExC,KAAK,MAAM,YAAY,OAAO,OAAO,SAAS,KAAK,GACjD,KAAK,MAAM,CAAC,KAAK,SAAS,OAAO,QAAQ,SAAS,cAAc,CAAC,CAAC,GAChE,IAAI,KAAK,iBAAiB,KAAK,IAAI,GAAG;CAG1C,OAAO,CAAC,GAAG,IAAI,CAAC,CAAC,SAAS;AAC5B;;;;;;AAOA,SAAgB,qBACd,UACA,KACS;CACT,IAAI,SAAS,mBAAmB,IAAI,EAAE,iBAAiB,OAAO;CAC9D,KAAK,MAAM,YAAY,OAAO,OAAO,SAAS,KAAK,GACjD,IAAI,SAAS,aAAa,IAAI,EAAE,iBAAiB,OAAO;CAE1D,OAAO;AACT;;;;AC7DA,MAAM,aAAa,EAAE,MAAM,SAAkB;AAC7C,MAAM,WAAW,EAAE,MAAM,UAAmB;AAC5C,MAAM,aAAa,EAAE,MAAM,SAAkB;AAC7C,MAAM,kBAAkB,EAAE,MAAM,WAAoB;;;;;AAMpD,MAAa,oCAAoCA,iCAAe;CAC9D,SAAS;CACT,SAAS;CACT,kBAAkB;EAChB,iBAAiB;GACf,GAAG;GACH,UAAU;GACV,aAAa;EACf;EACA,uBAAuB;GACrB,GAAG;GACH,iBAAiB;GACjB,aAAa;EACf;EACA,0BAA0B;GACxB,GAAG;GACH,MAAM;IACJ;IACA;IACA;IACA;IACA;IACA;GACF;EACF;EACA,2BAA2B;GACzB,GAAG;GACH,MAAM;IACJ;IACA;IACA;IACA;IACA;GACF;EACF;EACA,0BAA0B,EAAE,GAAG,SAAS;EACxC,yBAAyB;GACvB,GAAG;GACH,MAAM;IAAC;IAAY;IAAU;IAAW;GAAS;EACnD;EACA,sBAAsB,EAAE,GAAG,gBAAgB;EAC3C,0BAA0B;GACxB,GAAG;GACH,MAAM;IAAC;IAAQ;IAAS;IAAU;GAAQ;EAC5C;EACA,8BAA8B;GAAE,GAAG;GAAY,iBAAiB;EAAK;EACrE,yBAAyB,EAAE,GAAG,WAAW;EACzC,2BAA2B,EAAE,GAAG,gBAAgB;EAChD,2BAA2B;GACzB,GAAG;GACH,MAAM;IAAC;IAAO;IAAU;IAAQ;GAAU;EAC5C;EACA,yBAAyB,EAAE,GAAG,WAAW;EACzC,8BAA8B,EAAE,GAAG,gBAAgB;EACnD,mBAAmB;GACjB,GAAG;GACH,MAAM;IAAC;IAAU;IAAQ;IAAa;IAAW;GAAO;EAC1D;EACA,mBAAmB,EAAE,GAAG,WAAW;EACnC,oBAAoB,EAAE,GAAG,WAAW;EACpC,wBAAwB,EAAE,GAAG,SAAS;EACtC,8BAA8B,EAAE,GAAG,SAAS;EAC5C,kCAAkC;GAChC,GAAG;GACH,MAAM;IAAC;IAAS;IAAc;GAAW;EAC3C;EACA,kBAAkB,EAAE,GAAG,WAAW;EAClC,qBAAqB,EAAE,GAAG,WAAW;EACrC,oBAAoB,EAAE,GAAG,WAAW;EACpC,qBAAqB;GACnB,GAAG;GACH,MAAM;IAAC;IAAQ;IAAW;IAAS;GAAU;EAC/C;CACF;CACA,OAAO;EACL,gBAAgB;GACd,aAAa;GACb,YAAY;IACV,YAAY;KAAE,GAAG;KAAY,UAAU;IAAK;IAC5C,aAAa,EAAE,GAAG,WAAW;GAC/B;EACF;EACA,cAAc;GACZ,aAAa;GACb,YAAY,EACV,iBAAiB;IAAE,GAAG;IAAY,UAAU;GAAK,EACnD;EACF;CACF;AACF,CAAC"}
|
package/dist/index.d.cts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import { a as diffSnapshots, c as ContractSnapshot, d as contractToSnapshot, f as parseSnapshot, g as SchemaAttributeKey, h as SNAPSHOT_SPEC, i as SnapshotDiff, l as SnapshotAttribute, m as SCHEMA_ATTRS, n as ChangeType, o as formatDiff, p as serializeSnapshot, r as SnapshotChange, s as hasBreakingChanges, t as ChangeKind, u as SnapshotSpan } from "./diff-
|
|
1
|
+
import { a as diffSnapshots, c as ContractSnapshot, d as contractToSnapshot, f as parseSnapshot, g as SchemaAttributeKey, h as SNAPSHOT_SPEC, i as SnapshotDiff, l as SnapshotAttribute, m as SCHEMA_ATTRS, n as ChangeType, o as formatDiff, p as serializeSnapshot, r as SnapshotChange, s as hasBreakingChanges, t as ChangeKind, u as SnapshotSpan } from "./diff-CfvhW7ux.cjs";
|
|
2
2
|
import { A as validateScenarioSpec, C as ScenarioViolationCode, D as isScenarioClosed, E as formatScenarioResult, O as parseCardinality, S as ScenarioViolation, T as evaluateScenario, _ as ScenarioOutcome, a as SpanSpec, b as ScenarioSpan, c as allowsAdditionalAttributes, d as Cardinality, f as CheckScenarioOptions, g as ScenarioEventSpec, h as ScenarioAddition, i as STABILITIES, k as proposeScenario, l as defineContract, m as EvaluateScenarioOptions, n as AttributeSpec, o as Stability, p as CompletionBoundary, r as AttributeType, s as TelemetryContract, t as ATTRIBUTE_TYPES, u as resolveAttributeSpec, v as ScenarioProposal, w as checkScenario, x as ScenarioSpec, y as ScenarioResult } from "./contract-Ymh_Q37N.cjs";
|
|
3
|
-
import { a as SchemaValidationSpanProcessor, c as createSchemaValidationProcessor, d as ValidateOptions, f as ViolationCode, g as validateSpan, h as hasErrors, i as SchemaValidationProcessorOptions, l as SchemaViolation, m as formatViolation, n as ReadableSpanLike, o as SpanLike, p as ViolationSeverity, r as SchemaProcessorMode, s as SpanProcessorLike, t as OtelContext, u as SpanShape } from "./processor-
|
|
4
|
-
|
|
3
|
+
import { a as SchemaValidationSpanProcessor, c as createSchemaValidationProcessor, d as ValidateOptions, f as ViolationCode, g as validateSpan, h as hasErrors, i as SchemaValidationProcessorOptions, l as SchemaViolation, m as formatViolation, n as ReadableSpanLike, o as SpanLike, p as ViolationSeverity, r as SchemaProcessorMode, s as SpanProcessorLike, t as OtelContext, u as SpanShape } from "./processor-DVcsS6Rz.cjs";
|
|
5
4
|
//#region src/redaction.d.ts
|
|
6
5
|
/**
|
|
7
6
|
* Every attribute key in the contract flagged `highCardinality: true`, across
|
package/dist/index.d.cts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.cts","names":[],"sources":["../src/redaction.ts","../src/contracts/agent-security.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.cts","names":[],"sources":["../src/redaction.ts","../src/contracts/agent-security.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;iBAoCgB,oBAAoB,UAAU;;;;;;iBAkB9B,qBACd,UAAU,mBACV;;;;;;;cC7CW,mCAAA"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import { a as diffSnapshots, c as ContractSnapshot, d as contractToSnapshot, f as parseSnapshot, g as SchemaAttributeKey, h as SNAPSHOT_SPEC, i as SnapshotDiff, l as SnapshotAttribute, m as SCHEMA_ATTRS, n as ChangeType, o as formatDiff, p as serializeSnapshot, r as SnapshotChange, s as hasBreakingChanges, t as ChangeKind, u as SnapshotSpan } from "./diff-
|
|
1
|
+
import { a as diffSnapshots, c as ContractSnapshot, d as contractToSnapshot, f as parseSnapshot, g as SchemaAttributeKey, h as SNAPSHOT_SPEC, i as SnapshotDiff, l as SnapshotAttribute, m as SCHEMA_ATTRS, n as ChangeType, o as formatDiff, p as serializeSnapshot, r as SnapshotChange, s as hasBreakingChanges, t as ChangeKind, u as SnapshotSpan } from "./diff-CAIC04O4.js";
|
|
2
2
|
import { A as validateScenarioSpec, C as ScenarioViolationCode, D as isScenarioClosed, E as formatScenarioResult, O as parseCardinality, S as ScenarioViolation, T as evaluateScenario, _ as ScenarioOutcome, a as SpanSpec, b as ScenarioSpan, c as allowsAdditionalAttributes, d as Cardinality, f as CheckScenarioOptions, g as ScenarioEventSpec, h as ScenarioAddition, i as STABILITIES, k as proposeScenario, l as defineContract, m as EvaluateScenarioOptions, n as AttributeSpec, o as Stability, p as CompletionBoundary, r as AttributeType, s as TelemetryContract, t as ATTRIBUTE_TYPES, u as resolveAttributeSpec, v as ScenarioProposal, w as checkScenario, x as ScenarioSpec, y as ScenarioResult } from "./contract-Ymh_Q37N.js";
|
|
3
|
-
import { a as SchemaValidationSpanProcessor, c as createSchemaValidationProcessor, d as ValidateOptions, f as ViolationCode, g as validateSpan, h as hasErrors, i as SchemaValidationProcessorOptions, l as SchemaViolation, m as formatViolation, n as ReadableSpanLike, o as SpanLike, p as ViolationSeverity, r as SchemaProcessorMode, s as SpanProcessorLike, t as OtelContext, u as SpanShape } from "./processor-
|
|
4
|
-
|
|
3
|
+
import { a as SchemaValidationSpanProcessor, c as createSchemaValidationProcessor, d as ValidateOptions, f as ViolationCode, g as validateSpan, h as hasErrors, i as SchemaValidationProcessorOptions, l as SchemaViolation, m as formatViolation, n as ReadableSpanLike, o as SpanLike, p as ViolationSeverity, r as SchemaProcessorMode, s as SpanProcessorLike, t as OtelContext, u as SpanShape } from "./processor-TpuacLr5.js";
|
|
5
4
|
//#region src/redaction.d.ts
|
|
6
5
|
/**
|
|
7
6
|
* Every attribute key in the contract flagged `highCardinality: true`, across
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/redaction.ts","../src/contracts/agent-security.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/redaction.ts","../src/contracts/agent-security.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;iBAoCgB,oBAAoB,UAAU;;;;;;iBAkB9B,qBACd,UAAU,mBACV;;;;;;;cC7CW,mCAAA"}
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../src/redaction.ts","../src/contracts/agent-security.ts"],"sourcesContent":["/**\n * Cardinality posture helpers.\n *\n * The old cardinality rule — \"keep unique-value counts down\" — was a constraint\n * invented because dashboards have pixels and a graph with 10k series is\n * unreadable to a human. An agent does not look at the graph; it reads the\n * spans. A high-cardinality field (the user id, the sender domain, the request\n * id) is then the single most useful attribute on a trace when the agent is\n * chasing one specific failure.\n *\n * So the contract lets you mark attributes `highCardinality: true` as a\n * deliberate signal, and this module turns that into a *protect list*: the keys\n * a redactor or span-name normalizer must NOT strip, even when an aggressive\n * default would otherwise drop them.\n */\n\nimport type { TelemetryContract } from './contract.js';\n\n/**\n * Every attribute key in the contract flagged `highCardinality: true`, across\n * both common and per-span attributes. Feed this into a redaction/normalization\n * allow-list so the fields most useful to an agent reader survive.\n *\n * @example\n * ```ts\n * import { init } from 'autotel';\n * import { highCardinalityKeys } from 'autotel-schema';\n * import { contract } from './telemetry.contract';\n *\n * init({\n * service: 'checkout',\n * // keep user.id / request.id intact even under the strict redactor\n * attributeRedactor: { allowKeys: highCardinalityKeys(contract), preset: 'strict' },\n * });\n * ```\n */\nexport function highCardinalityKeys(contract: TelemetryContract): string[] {\n const keys = new Set<string>();\n for (const [key, spec] of Object.entries(contract.commonAttributes ?? {})) {\n if (spec.highCardinality) keys.add(key);\n }\n for (const spanSpec of Object.values(contract.spans)) {\n for (const [key, spec] of Object.entries(spanSpec.attributes ?? {})) {\n if (spec.highCardinality) keys.add(key);\n }\n }\n return [...keys].toSorted();\n}\n\n/**\n * Predicate form of {@link highCardinalityKeys} — `true` when `key` is declared\n * high-cardinality anywhere in the contract. Useful inside a custom\n * `spanNameNormalizer` or redactor callback.\n */\nexport function isHighCardinalityKey(\n contract: TelemetryContract,\n key: string,\n): boolean {\n if (contract.commonAttributes?.[key]?.highCardinality) return true;\n for (const spanSpec of Object.values(contract.spans)) {\n if (spanSpec.attributes?.[key]?.highCardinality) return true;\n }\n return false;\n}\n","import { defineContract } from '../contract.js';\n\nconst stringAttr = { type: 'string' as const };\nconst boolAttr = { type: 'boolean' as const };\nconst numberAttr = { type: 'number' as const };\nconst stringArrayAttr = { type: 'string[]' as const };\n\n/**\n * Published telemetry contract for Google SAIF-aligned agent security observability.\n * Span names are illustrative — attributes are the stable surface under validation.\n */\nexport const AGENT_SECURITY_TELEMETRY_CONTRACT = defineContract({\n service: 'autotel-agent-security',\n version: '1.0.0',\n commonAttributes: {\n 'autotel.agent': {
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../src/redaction.ts","../src/contracts/agent-security.ts"],"sourcesContent":["/**\n * Cardinality posture helpers.\n *\n * The old cardinality rule — \"keep unique-value counts down\" — was a constraint\n * invented because dashboards have pixels and a graph with 10k series is\n * unreadable to a human. An agent does not look at the graph; it reads the\n * spans. A high-cardinality field (the user id, the sender domain, the request\n * id) is then the single most useful attribute on a trace when the agent is\n * chasing one specific failure.\n *\n * So the contract lets you mark attributes `highCardinality: true` as a\n * deliberate signal, and this module turns that into a *protect list*: the keys\n * a redactor or span-name normalizer must NOT strip, even when an aggressive\n * default would otherwise drop them.\n */\n\nimport type { TelemetryContract } from './contract.js';\n\n/**\n * Every attribute key in the contract flagged `highCardinality: true`, across\n * both common and per-span attributes. Feed this into a redaction/normalization\n * allow-list so the fields most useful to an agent reader survive.\n *\n * @example\n * ```ts\n * import { init } from 'autotel';\n * import { highCardinalityKeys } from 'autotel-schema';\n * import { contract } from './telemetry.contract';\n *\n * init({\n * service: 'checkout',\n * // keep user.id / request.id intact even under the strict redactor\n * attributeRedactor: { allowKeys: highCardinalityKeys(contract), preset: 'strict' },\n * });\n * ```\n */\nexport function highCardinalityKeys(contract: TelemetryContract): string[] {\n const keys = new Set<string>();\n for (const [key, spec] of Object.entries(contract.commonAttributes ?? {})) {\n if (spec.highCardinality) keys.add(key);\n }\n for (const spanSpec of Object.values(contract.spans)) {\n for (const [key, spec] of Object.entries(spanSpec.attributes ?? {})) {\n if (spec.highCardinality) keys.add(key);\n }\n }\n return [...keys].toSorted();\n}\n\n/**\n * Predicate form of {@link highCardinalityKeys} — `true` when `key` is declared\n * high-cardinality anywhere in the contract. Useful inside a custom\n * `spanNameNormalizer` or redactor callback.\n */\nexport function isHighCardinalityKey(\n contract: TelemetryContract,\n key: string,\n): boolean {\n if (contract.commonAttributes?.[key]?.highCardinality) return true;\n for (const spanSpec of Object.values(contract.spans)) {\n if (spanSpec.attributes?.[key]?.highCardinality) return true;\n }\n return false;\n}\n","import { defineContract } from '../contract.js';\n\nconst stringAttr = { type: 'string' as const };\nconst boolAttr = { type: 'boolean' as const };\nconst numberAttr = { type: 'number' as const };\nconst stringArrayAttr = { type: 'string[]' as const };\n\n/**\n * Published telemetry contract for Google SAIF-aligned agent security observability.\n * Span names are illustrative — attributes are the stable surface under validation.\n */\nexport const AGENT_SECURITY_TELEMETRY_CONTRACT = defineContract({\n service: 'autotel-agent-security',\n version: '1.0.0',\n commonAttributes: {\n 'autotel.agent': {\n ...boolAttr,\n required: false,\n description: 'Agent audit marker',\n },\n 'agent.controller.id': {\n ...stringAttr,\n highCardinality: true,\n description: 'Hashed controlling human user id',\n },\n 'agent.input.provenance': {\n ...stringAttr,\n enum: [\n 'user_direct',\n 'user_voice',\n 'rag',\n 'memory',\n 'tool_result',\n 'external_untrusted',\n ],\n },\n 'agent.action.risk_class': {\n ...stringAttr,\n enum: [\n 'read',\n 'write',\n 'destructive',\n 'financial',\n 'exfiltration_capable',\n ],\n },\n 'agent.consent.required': { ...boolAttr },\n 'agent.consent.outcome': {\n ...stringAttr,\n enum: ['approved', 'denied', 'timeout', 'revoked'],\n },\n 'agent.scope.active': { ...stringArrayAttr },\n 'agent.memory.operation': {\n ...stringAttr,\n enum: ['read', 'write', 'delete', 'search'],\n },\n 'agent.memory.isolation_key': { ...stringAttr, highCardinality: true },\n 'agent.plan.step_index': { ...numberAttr },\n 'agent.plan.tool_intents': { ...stringArrayAttr },\n 'agent.plan.risk.verdict': {\n ...stringAttr,\n enum: ['low', 'medium', 'high', 'critical'],\n },\n 'agent.plan.risk.score': { ...numberAttr },\n 'agent.plan.risk.categories': { ...stringArrayAttr },\n 'policy.decision': {\n ...stringAttr,\n enum: ['permit', 'deny', 'challenge', 'observe', 'error'],\n },\n 'tool.input_hash': { ...stringAttr },\n 'tool.output_hash': { ...stringAttr },\n 'mcp.tool.destructive': { ...boolAttr },\n 'mcp.tool.untrusted_content': { ...boolAttr },\n 'mcp.security.injection.verdict': {\n ...stringAttr,\n enum: ['clean', 'suspicious', 'malicious'],\n },\n 'security.event': { ...stringAttr },\n 'security.category': { ...stringAttr },\n 'security.outcome': { ...stringAttr },\n 'security.severity': {\n ...stringAttr,\n enum: ['info', 'warning', 'error', 'critical'],\n },\n },\n spans: {\n 'agent.action': {\n description: 'Scoped agent action or tool call with audit metadata',\n attributes: {\n 'agent.id': { ...stringAttr, required: true },\n 'tool.name': { ...stringAttr },\n },\n },\n 'tools/call': {\n description: 'MCP tool invocation with boundary security signals',\n attributes: {\n 'mcp.tool.name': { ...stringAttr, required: true },\n },\n },\n },\n});\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AAoCA,SAAgB,oBAAoB,UAAuC;CACzE,MAAM,uBAAO,IAAI,IAAY;CAC7B,KAAK,MAAM,CAAC,KAAK,SAAS,OAAO,QAAQ,SAAS,oBAAoB,CAAC,CAAC,GACtE,IAAI,KAAK,iBAAiB,KAAK,IAAI,GAAG;CAExC,KAAK,MAAM,YAAY,OAAO,OAAO,SAAS,KAAK,GACjD,KAAK,MAAM,CAAC,KAAK,SAAS,OAAO,QAAQ,SAAS,cAAc,CAAC,CAAC,GAChE,IAAI,KAAK,iBAAiB,KAAK,IAAI,GAAG;CAG1C,OAAO,CAAC,GAAG,IAAI,CAAC,CAAC,SAAS;AAC5B;;;;;;AAOA,SAAgB,qBACd,UACA,KACS;CACT,IAAI,SAAS,mBAAmB,IAAI,EAAE,iBAAiB,OAAO;CAC9D,KAAK,MAAM,YAAY,OAAO,OAAO,SAAS,KAAK,GACjD,IAAI,SAAS,aAAa,IAAI,EAAE,iBAAiB,OAAO;CAE1D,OAAO;AACT;;;;AC7DA,MAAM,aAAa,EAAE,MAAM,SAAkB;AAC7C,MAAM,WAAW,EAAE,MAAM,UAAmB;AAC5C,MAAM,aAAa,EAAE,MAAM,SAAkB;AAC7C,MAAM,kBAAkB,EAAE,MAAM,WAAoB;;;;;AAMpD,MAAa,oCAAoC,eAAe;CAC9D,SAAS;CACT,SAAS;CACT,kBAAkB;EAChB,iBAAiB;GACf,GAAG;GACH,UAAU;GACV,aAAa;EACf;EACA,uBAAuB;GACrB,GAAG;GACH,iBAAiB;GACjB,aAAa;EACf;EACA,0BAA0B;GACxB,GAAG;GACH,MAAM;IACJ;IACA;IACA;IACA;IACA;IACA;GACF;EACF;EACA,2BAA2B;GACzB,GAAG;GACH,MAAM;IACJ;IACA;IACA;IACA;IACA;GACF;EACF;EACA,0BAA0B,EAAE,GAAG,SAAS;EACxC,yBAAyB;GACvB,GAAG;GACH,MAAM;IAAC;IAAY;IAAU;IAAW;GAAS;EACnD;EACA,sBAAsB,EAAE,GAAG,gBAAgB;EAC3C,0BAA0B;GACxB,GAAG;GACH,MAAM;IAAC;IAAQ;IAAS;IAAU;GAAQ;EAC5C;EACA,8BAA8B;GAAE,GAAG;GAAY,iBAAiB;EAAK;EACrE,yBAAyB,EAAE,GAAG,WAAW;EACzC,2BAA2B,EAAE,GAAG,gBAAgB;EAChD,2BAA2B;GACzB,GAAG;GACH,MAAM;IAAC;IAAO;IAAU;IAAQ;GAAU;EAC5C;EACA,yBAAyB,EAAE,GAAG,WAAW;EACzC,8BAA8B,EAAE,GAAG,gBAAgB;EACnD,mBAAmB;GACjB,GAAG;GACH,MAAM;IAAC;IAAU;IAAQ;IAAa;IAAW;GAAO;EAC1D;EACA,mBAAmB,EAAE,GAAG,WAAW;EACnC,oBAAoB,EAAE,GAAG,WAAW;EACpC,wBAAwB,EAAE,GAAG,SAAS;EACtC,8BAA8B,EAAE,GAAG,SAAS;EAC5C,kCAAkC;GAChC,GAAG;GACH,MAAM;IAAC;IAAS;IAAc;GAAW;EAC3C;EACA,kBAAkB,EAAE,GAAG,WAAW;EAClC,qBAAqB,EAAE,GAAG,WAAW;EACrC,oBAAoB,EAAE,GAAG,WAAW;EACpC,qBAAqB;GACnB,GAAG;GACH,MAAM;IAAC;IAAQ;IAAW;IAAS;GAAU;EAC/C;CACF;CACA,OAAO;EACL,gBAAgB;GACd,aAAa;GACb,YAAY;IACV,YAAY;KAAE,GAAG;KAAY,UAAU;IAAK;IAC5C,aAAa,EAAE,GAAG,WAAW;GAC/B;EACF;EACA,cAAc;GACZ,aAAa;GACb,YAAY,EACV,iBAAiB;IAAE,GAAG;IAAY,UAAU;GAAK,EACnD;EACF;CACF;AACF,CAAC"}
|