@mnemom/mnemom 0.17.36 → 0.17.38-next.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (121) hide show
  1. package/dist/commands/agent-run.d.ts +11 -0
  2. package/dist/commands/agent-run.js +37 -0
  3. package/dist/commands/agent-share-render.d.ts +5 -5
  4. package/dist/commands/agent-share-render.js +9 -7
  5. package/dist/commands/agent-share-test-issuer.d.ts +1 -1
  6. package/dist/commands/agent-share-test-issuer.js +1 -1
  7. package/dist/commands/agent.d.ts +8 -1
  8. package/dist/commands/agent.js +14 -3
  9. package/dist/commands/scan.d.ts +15 -2
  10. package/dist/commands/scan.js +48 -3
  11. package/dist/host/commands.js +4 -1
  12. package/dist/host/owner.d.ts +2 -0
  13. package/dist/host/verbs.d.ts +16 -1
  14. package/dist/host/verbs.js +45 -3
  15. package/dist/index.js +1 -1
  16. package/dist/lib/commit-report.d.ts +7 -13
  17. package/dist/lib/commit-report.js +12 -5
  18. package/dist/lib/conduct-receipt.d.ts +8 -3
  19. package/dist/lib/conduct-receipt.js +29 -7
  20. package/dist/lib/experimental.d.ts +1 -1
  21. package/dist/lib/experimental.js +1 -12
  22. package/dist/lib/runner/executor-client.d.ts +5 -0
  23. package/dist/lib/runner/executor-client.js +8 -0
  24. package/dist/lib/runner/job.d.ts +37 -0
  25. package/dist/lib/runner/job.js +74 -1
  26. package/dist/lib/runner/phases.d.ts +7 -2
  27. package/dist/lib/runner/phases.js +7 -2
  28. package/dist/lib/runner/proxy.d.ts +26 -2
  29. package/dist/lib/runner/proxy.js +33 -1
  30. package/dist/lib/runner/record-commit.d.ts +16 -0
  31. package/dist/lib/runner/record-commit.js +7 -0
  32. package/dist/lib/scan/spend/anthropic-usage.d.ts +20 -1
  33. package/dist/lib/scan/spend/anthropic-usage.js +32 -7
  34. package/dist/lib/scan/spend/anthropic-wif.d.ts +42 -0
  35. package/dist/lib/scan/spend/anthropic-wif.js +153 -0
  36. package/dist/vendor/durable-contract/index.js +6 -3
  37. package/dist/vendor/shared/durable-contract/cli-surface.js +2 -2
  38. package/dist/vendor/shared/durable-contract/codes.js +4 -1
  39. package/package.json +2 -5
  40. package/node_modules/@mnemom/aip-verifier/LICENSE +0 -190
  41. package/node_modules/@mnemom/aip-verifier/dist/claim-lint-CVITIRcU.d.cts +0 -478
  42. package/node_modules/@mnemom/aip-verifier/dist/claim-lint-CVITIRcU.d.ts +0 -478
  43. package/node_modules/@mnemom/aip-verifier/dist/index.cjs +0 -971
  44. package/node_modules/@mnemom/aip-verifier/dist/index.cjs.map +0 -1
  45. package/node_modules/@mnemom/aip-verifier/dist/index.d.cts +0 -330
  46. package/node_modules/@mnemom/aip-verifier/dist/index.d.ts +0 -330
  47. package/node_modules/@mnemom/aip-verifier/dist/index.js +0 -933
  48. package/node_modules/@mnemom/aip-verifier/dist/index.js.map +0 -1
  49. package/node_modules/@mnemom/aip-verifier/dist/render-lines.cjs +0 -879
  50. package/node_modules/@mnemom/aip-verifier/dist/render-lines.cjs.map +0 -1
  51. package/node_modules/@mnemom/aip-verifier/dist/render-lines.d.cts +0 -96
  52. package/node_modules/@mnemom/aip-verifier/dist/render-lines.d.ts +0 -96
  53. package/node_modules/@mnemom/aip-verifier/dist/render-lines.js +0 -875
  54. package/node_modules/@mnemom/aip-verifier/dist/render-lines.js.map +0 -1
  55. package/node_modules/@mnemom/aip-verifier/package.json +0 -86
  56. package/node_modules/@noble/ed25519/LICENSE +0 -21
  57. package/node_modules/@noble/ed25519/README.md +0 -371
  58. package/node_modules/@noble/ed25519/index.d.ts +0 -391
  59. package/node_modules/@noble/ed25519/index.js +0 -933
  60. package/node_modules/@noble/ed25519/index.ts +0 -1134
  61. package/node_modules/@noble/ed25519/package.json +0 -54
  62. package/node_modules/@noble/hashes/LICENSE +0 -21
  63. package/node_modules/@noble/hashes/README.md +0 -478
  64. package/node_modules/@noble/hashes/_blake.d.ts +0 -20
  65. package/node_modules/@noble/hashes/_blake.js +0 -59
  66. package/node_modules/@noble/hashes/_md.d.ts +0 -95
  67. package/node_modules/@noble/hashes/_md.js +0 -209
  68. package/node_modules/@noble/hashes/_u64.d.ts +0 -36
  69. package/node_modules/@noble/hashes/_u64.js +0 -77
  70. package/node_modules/@noble/hashes/argon2.d.ts +0 -148
  71. package/node_modules/@noble/hashes/argon2.js +0 -577
  72. package/node_modules/@noble/hashes/blake1.d.ts +0 -162
  73. package/node_modules/@noble/hashes/blake1.js +0 -630
  74. package/node_modules/@noble/hashes/blake2.d.ts +0 -203
  75. package/node_modules/@noble/hashes/blake2.js +0 -573
  76. package/node_modules/@noble/hashes/blake3.d.ts +0 -68
  77. package/node_modules/@noble/hashes/blake3.js +0 -310
  78. package/node_modules/@noble/hashes/eskdf.d.ts +0 -91
  79. package/node_modules/@noble/hashes/eskdf.js +0 -225
  80. package/node_modules/@noble/hashes/hkdf.d.ts +0 -70
  81. package/node_modules/@noble/hashes/hkdf.js +0 -152
  82. package/node_modules/@noble/hashes/hmac.d.ts +0 -50
  83. package/node_modules/@noble/hashes/hmac.js +0 -93
  84. package/node_modules/@noble/hashes/index.d.ts +0 -1
  85. package/node_modules/@noble/hashes/index.js +0 -34
  86. package/node_modules/@noble/hashes/legacy.d.ts +0 -107
  87. package/node_modules/@noble/hashes/legacy.js +0 -348
  88. package/node_modules/@noble/hashes/package.json +0 -91
  89. package/node_modules/@noble/hashes/pbkdf2.d.ts +0 -66
  90. package/node_modules/@noble/hashes/pbkdf2.js +0 -176
  91. package/node_modules/@noble/hashes/scrypt.d.ts +0 -98
  92. package/node_modules/@noble/hashes/scrypt.js +0 -304
  93. package/node_modules/@noble/hashes/sha2.d.ts +0 -176
  94. package/node_modules/@noble/hashes/sha2.js +0 -446
  95. package/node_modules/@noble/hashes/sha3-addons.d.ts +0 -533
  96. package/node_modules/@noble/hashes/sha3-addons.js +0 -973
  97. package/node_modules/@noble/hashes/sha3.d.ts +0 -208
  98. package/node_modules/@noble/hashes/sha3.js +0 -475
  99. package/node_modules/@noble/hashes/src/_blake.ts +0 -67
  100. package/node_modules/@noble/hashes/src/_md.ts +0 -227
  101. package/node_modules/@noble/hashes/src/_u64.ts +0 -111
  102. package/node_modules/@noble/hashes/src/argon2.ts +0 -693
  103. package/node_modules/@noble/hashes/src/blake1.ts +0 -677
  104. package/node_modules/@noble/hashes/src/blake2.ts +0 -648
  105. package/node_modules/@noble/hashes/src/blake3.ts +0 -344
  106. package/node_modules/@noble/hashes/src/eskdf.ts +0 -265
  107. package/node_modules/@noble/hashes/src/hkdf.ts +0 -161
  108. package/node_modules/@noble/hashes/src/hmac.ts +0 -130
  109. package/node_modules/@noble/hashes/src/index.ts +0 -33
  110. package/node_modules/@noble/hashes/src/legacy.ts +0 -361
  111. package/node_modules/@noble/hashes/src/pbkdf2.ts +0 -219
  112. package/node_modules/@noble/hashes/src/scrypt.ts +0 -375
  113. package/node_modules/@noble/hashes/src/sha2.ts +0 -486
  114. package/node_modules/@noble/hashes/src/sha3-addons.ts +0 -1157
  115. package/node_modules/@noble/hashes/src/sha3.ts +0 -525
  116. package/node_modules/@noble/hashes/src/utils.ts +0 -978
  117. package/node_modules/@noble/hashes/src/webcrypto.ts +0 -283
  118. package/node_modules/@noble/hashes/utils.d.ts +0 -560
  119. package/node_modules/@noble/hashes/utils.js +0 -687
  120. package/node_modules/@noble/hashes/webcrypto.d.ts +0 -121
  121. package/node_modules/@noble/hashes/webcrypto.js +0 -228
@@ -1,875 +0,0 @@
1
- import { verifyAsync } from '@noble/ed25519';
2
- import { utf8ToBytes, hexToBytes, bytesToHex } from '@noble/hashes/utils.js';
3
- import { sha256 } from '@noble/hashes/sha2.js';
4
-
5
- // src/claim-table.ts
6
- var CLAIM_CLASSES = ["measured", "verdict", "estimate", "benchmark", "outcome"];
7
- var SURFACES = ["record_page", "check_summary", "pr_comment", "share_card", "period_card"];
8
- var CARD_SURFACES = ["share_card", "period_card"];
9
- var ESTIMATE_FORBIDDEN_SURFACES = ["pr_comment", "share_card", "period_card"];
10
- var MODES = ["off", "observe", "nudge", "enforce"];
11
- var MODE_VERBS = {
12
- off: "not checked",
13
- observe: "flagged",
14
- nudge: "flagged, agent nudged",
15
- enforce: "stopped"
16
- };
17
- var PROTECTION_VERDICTS = ["pass", "warn", "nudge", "quarantine", "block"];
18
- function protectionVerb(mode, verdict) {
19
- if (mode === "off" || verdict === "pass") return null;
20
- if (mode === "observe") return MODE_VERBS.observe;
21
- if (mode === "nudge") return verdict === "nudge" ? MODE_VERBS.nudge : MODE_VERBS.observe;
22
- return verdict === "block" || verdict === "quarantine" ? MODE_VERBS.enforce : MODE_VERBS.observe;
23
- }
24
- var CLAIM_TABLE = {
25
- commits_recorded: { class: "measured", verb_by_mode: false, money_allowed: false },
26
- messages_from_person: { class: "measured", verb_by_mode: false, money_allowed: false },
27
- compactions: { class: "measured", verb_by_mode: false, money_allowed: false },
28
- tokens_sent_vs_reaching: { class: "measured", verb_by_mode: false, money_allowed: false },
29
- peak_context: { class: "measured", verb_by_mode: false, money_allowed: false },
30
- model_cost: { class: "measured", verb_by_mode: false, money_allowed: true },
31
- mu_cost: { class: "measured", verb_by_mode: false, money_allowed: true },
32
- approval: { class: "measured", verb_by_mode: false, money_allowed: false },
33
- /** How the goal was set: "sealed contract", "implicit goal" or "not checked" (M7: its own measured line). */
34
- goal_mode: { class: "measured", verb_by_mode: false, money_allowed: false },
35
- goal: { class: "verdict", verb_by_mode: true, money_allowed: false },
36
- /** The protection area's mode when no act is shown: "not checked" under off. */
37
- protection_mode: { class: "measured", verb_by_mode: false, money_allowed: false },
38
- protection: { class: "verdict", verb_by_mode: true, money_allowed: false },
39
- autonomy: { class: "measured", verb_by_mode: false, money_allowed: false },
40
- savings_estimate: {
41
- class: "estimate",
42
- verb_by_mode: false,
43
- money_allowed: true,
44
- surfaces: ["record_page", "check_summary"]
45
- },
46
- benchmark: { class: "benchmark", verb_by_mode: false, money_allowed: false },
47
- outcome: { class: "outcome", verb_by_mode: false, money_allowed: false }
48
- };
49
- function isClaimField(field) {
50
- return typeof field === "string" && Object.prototype.hasOwnProperty.call(CLAIM_TABLE, field);
51
- }
52
- var LINE_KEY_FIELDS = {
53
- "cost.model": "model_cost",
54
- "cost.mu": "mu_cost",
55
- "goal.mode": "goal_mode",
56
- "goal.verdict": "goal",
57
- "protection.mode": "protection_mode",
58
- "people.messages": "messages_from_person",
59
- "context.compactions": "compactions"
60
- };
61
- var PROTECTION_ACT_KEY = /^protection\.act\.\d+$/;
62
- function fieldForLineKey(key) {
63
- if (typeof key !== "string") return void 0;
64
- if (PROTECTION_ACT_KEY.test(key)) return "protection";
65
- return Object.prototype.hasOwnProperty.call(LINE_KEY_FIELDS, key) ? LINE_KEY_FIELDS[key] : void 0;
66
- }
67
-
68
- // src/claim-lint.ts
69
- var LINT_RULES = {
70
- one_class_per_line: "one_class_per_line",
71
- forbidden_word: "forbidden_word",
72
- money_on_goal_or_protection: "money_on_goal_or_protection",
73
- implicit_goal_sealed: "implicit_goal_sealed",
74
- goal_off_not_checked: "goal_off_not_checked",
75
- estimate_on_forbidden_surface: "estimate_on_forbidden_surface",
76
- verb_mismatch: "verb_mismatch",
77
- enforce_without_tool_name: "enforce_without_tool_name",
78
- tool_name_on_card: "tool_name_on_card",
79
- tool_arguments_shown: "tool_arguments_shown",
80
- trust_claim: "trust_claim",
81
- savings_percentage: "savings_percentage",
82
- benchmark_not_published: "benchmark_not_published",
83
- verdict_without_coverage: "verdict_without_coverage",
84
- card_class_not_allowed: "card_class_not_allowed",
85
- period_card_without_revert: "period_card_without_revert",
86
- field_not_on_surface: "field_not_on_surface",
87
- provisional_mu_on_card: "provisional_mu_on_card",
88
- provisional_mu_off_cost_line: "provisional_mu_off_cost_line",
89
- unknown_surface: "unknown_surface"
90
- };
91
- function clauseHas(text, rule) {
92
- const seen = /* @__PURE__ */ new Set();
93
- for (const m of text.matchAll(rule.tokens)) {
94
- const kind = Object.entries(m.groups ?? {}).find(([, v]) => v !== void 0)?.[0];
95
- if (kind === void 0) continue;
96
- if (kind === "brk") {
97
- seen.clear();
98
- continue;
99
- }
100
- if (rule.alone?.includes(kind)) return true;
101
- if (rule.pairs.some(([first, then]) => then === kind && seen.has(first))) return true;
102
- seen.add(kind);
103
- }
104
- return false;
105
- }
106
- var FORBIDDEN_WORD = /\b(safe|safely|unsafe|correct|correctly|incorrect|prevent|prevents|prevented|preventing|prevention)\b/i;
107
- var MONEY = /(?:\$|€|£|¥)\s?\d|\b(?<!\d,+)\d[\d,]*(?:\.\d+)?\s?(?:USD|EUR|GBP|dollars?|cents?)\b/i;
108
- var SEALED = /\bsealed\b/i;
109
- var ESTIMATE_WORDS = /\b(estimate[ds]?|estimation|saving|savings|saved|would have cost|counterfactual)\b/i;
110
- var TRUST_CLAIM = /\b(minutes?|seconds?|hours?)\s+to\s+trust\b|\btrust(ed)?\s+in\s+(under\s+|about\s+)?\d+\s*(minutes?|seconds?|hours?|min|s|h)\b|\breview[- ]time\b|\btime\s+to\s+review\b|\bwithout\s+re-?check(ing)?\b|\bno\s+need\s+to\s+(re-?check|review)\b|\btrust(ed)?\s+without\b/i;
111
- var SAVINGS_PERCENTAGE = {
112
- tokens: /\d+(?<percent>(?:\.\d+)?\s?%)?|\b(?<less>less)\b|\b(?<saving>sav(?:e[sd]?|ings?)|cheaper|reduction|reduced)\b|(?<brk>[.\n])/gi,
113
- pairs: [
114
- ["percent", "saving"],
115
- ["percent", "less"],
116
- ["saving", "percent"]
117
- ]
118
- };
119
- var REVERT = /\brevert(s|ed)?\b/i;
120
- var PROVISIONAL_MU = {
121
- tokens: /(?<approx>(?:≈|~|\bapprox(?:\.|imately)?\b|\babout\b)\s*\d[\d,]*(?:\.\d+)?\s*(?:mmu\b|mu\b|μ|µ))|\d[\d,]*(?<amount>(?:\.\d+)?\s*(?:mmu\b|mu\b|μ|µ))?|\b(?<pending>pending)\b|(?<brk>[.\n])/gi,
122
- alone: ["approx"],
123
- pairs: [
124
- ["amount", "pending"],
125
- ["pending", "amount"]
126
- ]
127
- };
128
- var NOT_CHECKED = /^not checked$/i;
129
- var COVERAGE_IN_TEXT = /\b(\d+)\s+of\s+(\d+)\s+turns\s+judged\b/i;
130
- var MODE_VERB_REGEX = {
131
- off: NOT_CHECKED,
132
- observe: /\bflagged\b/i,
133
- nudge: /\bflagged, agent nudged\b/i,
134
- enforce: /\bstopped\b/i
135
- };
136
- var OTHER_MODE_VERBS = {
137
- off: [/\bflagged\b/i, /\bnudged\b/i, /\bstopped\b/i],
138
- observe: [/\bnudged\b/i, /\bstopped\b/i],
139
- nudge: [/\bstopped\b/i],
140
- enforce: [/\bflagged\b/i, /\bnudged\b/i]
141
- };
142
- function isObject(v) {
143
- return typeof v === "object" && v !== null && !Array.isArray(v);
144
- }
145
- function coverageObjectValid(cov) {
146
- return isObject(cov) && Number.isInteger(cov.judged) && Number.isInteger(cov.total) && cov.judged >= 0 && cov.total >= cov.judged;
147
- }
148
- function coverageShown(line) {
149
- const cov = line.coverage;
150
- if (cov !== void 0) return coverageObjectValid(cov);
151
- const m = COVERAGE_IN_TEXT.exec(line.text);
152
- return m !== null && Number(m[2]) >= Number(m[1]);
153
- }
154
- function expectedVerb(mode, verdict) {
155
- if (mode === "off") return "off";
156
- if (verdict === void 0) return mode === "observe" ? "observe" : void 0;
157
- if (!PROTECTION_VERDICTS.includes(verdict)) return void 0;
158
- const verb = protectionVerb(mode, verdict);
159
- return MODES.find((m) => MODE_VERBS[m] === verb);
160
- }
161
- function escapeRegExp(s) {
162
- return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
163
- }
164
- function argumentsAppear(text, args) {
165
- if (args === void 0 || args === null) return false;
166
- const needles = [];
167
- const walk = (v) => {
168
- if (typeof v === "string") {
169
- if (v.length >= 3) needles.push(v);
170
- } else if (typeof v === "number" || typeof v === "boolean") {
171
- needles.push(String(v));
172
- } else if (Array.isArray(v)) {
173
- v.forEach(walk);
174
- } else if (isObject(v)) {
175
- Object.values(v).forEach(walk);
176
- }
177
- };
178
- walk(args);
179
- if (needles.length === 0) return false;
180
- try {
181
- needles.push(JSON.stringify(args));
182
- } catch {
183
- }
184
- return needles.some((n) => n.length > 0 && text.includes(n));
185
- }
186
- function lintLine(surface, line, index, out) {
187
- const push = (rule) => {
188
- out.push({ rule, line: index, text: isObject(line) && typeof line.text === "string" ? line.text : "" });
189
- };
190
- if (!isObject(line) || typeof line.text !== "string" || typeof line.class !== "string") {
191
- push(LINT_RULES.one_class_per_line);
192
- return;
193
- }
194
- const text = line.text;
195
- const onCard = CARD_SURFACES.includes(surface);
196
- const field = line.field !== void 0 ? line.field : fieldForLineKey(line.key);
197
- const cls = line.class;
198
- if (!CLAIM_CLASSES.includes(cls)) {
199
- push(LINT_RULES.one_class_per_line);
200
- } else if (field !== void 0) {
201
- if (!isClaimField(field)) {
202
- push(LINT_RULES.one_class_per_line);
203
- } else {
204
- const entry = CLAIM_TABLE[field];
205
- if (entry.class !== cls) push(LINT_RULES.one_class_per_line);
206
- if (entry.surfaces && !entry.surfaces.includes(surface)) {
207
- push(LINT_RULES.field_not_on_surface);
208
- }
209
- }
210
- }
211
- if (cls !== "estimate" && ESTIMATE_WORDS.test(text)) push(LINT_RULES.one_class_per_line);
212
- if (FORBIDDEN_WORD.test(text)) push(LINT_RULES.forbidden_word);
213
- const isGoal = field === "goal" || field === "goal_mode";
214
- const isProtection = field === "protection" || field === "protection_mode";
215
- if ((isGoal || isProtection) && MONEY.test(text)) push(LINT_RULES.money_on_goal_or_protection);
216
- if (field !== void 0 && isClaimField(field) && !CLAIM_TABLE[field].money_allowed) {
217
- if (!isGoal && !isProtection && MONEY.test(text)) push(LINT_RULES.one_class_per_line);
218
- }
219
- if (isGoal) {
220
- if (line.goal_mode === "implicit" && SEALED.test(text)) push(LINT_RULES.implicit_goal_sealed);
221
- if (line.goal_mode === "off" && !NOT_CHECKED.test(text.trim())) push(LINT_RULES.goal_off_not_checked);
222
- }
223
- if (cls === "estimate" || ESTIMATE_WORDS.test(text)) {
224
- if (ESTIMATE_FORBIDDEN_SURFACES.includes(surface)) push(LINT_RULES.estimate_on_forbidden_surface);
225
- }
226
- if (line.mode !== void 0) {
227
- const verb = MODES.includes(line.mode) ? expectedVerb(line.mode, line.verdict) : void 0;
228
- const name = typeof line.tool_name === "string" ? line.tool_name.trim() : "";
229
- const nameShown = name.length > 0 && new RegExp(`(^|[^A-Za-z0-9_])${escapeRegExp(name)}([^A-Za-z0-9_]|$)`).test(text);
230
- if (verb === void 0) {
231
- push(LINT_RULES.verb_mismatch);
232
- } else if (!MODE_VERB_REGEX[verb].test(verb === "off" ? text.trim() : text)) {
233
- push(LINT_RULES.verb_mismatch);
234
- } else if (OTHER_MODE_VERBS[verb].some((re) => re.test(text))) {
235
- push(LINT_RULES.verb_mismatch);
236
- } else if (verb !== "enforce" && nameShown && !onCard) {
237
- push(LINT_RULES.verb_mismatch);
238
- }
239
- if (onCard) {
240
- if (nameShown) push(LINT_RULES.tool_name_on_card);
241
- } else if (verb === "enforce" && !nameShown) {
242
- push(LINT_RULES.enforce_without_tool_name);
243
- }
244
- }
245
- if (argumentsAppear(text, line.tool_arguments)) push(LINT_RULES.tool_arguments_shown);
246
- if (clauseHas(text, PROVISIONAL_MU)) {
247
- if (onCard) push(LINT_RULES.provisional_mu_on_card);
248
- if (field !== "mu_cost") push(LINT_RULES.provisional_mu_off_cost_line);
249
- }
250
- if (TRUST_CLAIM.test(text)) push(LINT_RULES.trust_claim);
251
- if (clauseHas(text, SAVINGS_PERCENTAGE)) push(LINT_RULES.savings_percentage);
252
- if (cls === "benchmark") push(LINT_RULES.benchmark_not_published);
253
- if (cls === "verdict") {
254
- const covered = onCard ? coverageObjectValid(line.coverage) : field === "protection" || coverageShown(line);
255
- if (!covered) push(LINT_RULES.verdict_without_coverage);
256
- }
257
- if (onCard && cls !== "measured" && cls !== "verdict" && cls !== "outcome") push(LINT_RULES.card_class_not_allowed);
258
- }
259
- function lintRendering(rendering) {
260
- const violations = [];
261
- if (!isObject(rendering) || !Array.isArray(rendering.lines)) {
262
- return { ok: false, violations: [{ rule: LINT_RULES.unknown_surface, line: -1, text: "" }] };
263
- }
264
- const surface = rendering.surface;
265
- if (!SURFACES.includes(surface)) {
266
- return { ok: false, violations: [{ rule: LINT_RULES.unknown_surface, line: -1, text: String(surface) }] };
267
- }
268
- const lines = rendering.lines;
269
- lines.forEach((line, i) => lintLine(surface, line, i, violations));
270
- if (surface === "period_card") {
271
- const hasRevert = lines.some(
272
- (l) => isObject(l) && l.class === "outcome" && typeof l.text === "string" && REVERT.test(l.text)
273
- );
274
- if (!hasRevert) violations.push({ rule: LINT_RULES.period_card_without_revert, line: -1, text: "" });
275
- }
276
- return { ok: violations.length === 0, violations };
277
- }
278
-
279
- // src/render-estimate.ts
280
- var ESTIMATE_COUNTING_FLOOR_TOKENS = 2e5;
281
- var ESTIMATE_DISQUALIFIERS = {
282
- not_an_object: "not_an_object",
283
- method_not_lower_bound: "method_not_lower_bound",
284
- counting_floor_below_200k: "counting_floor_below_200k",
285
- not_net_of_mu: "not_net_of_mu",
286
- never_shows_loss: "never_shows_loss",
287
- value_not_finite: "value_not_finite"
288
- };
289
- function isObject2(v) {
290
- return typeof v === "object" && v !== null && !Array.isArray(v);
291
- }
292
- function estimateQualifies(estimate) {
293
- if (!isObject2(estimate)) return { qualifies: false, reasons: [ESTIMATE_DISQUALIFIERS.not_an_object] };
294
- const reasons = [];
295
- if (estimate.method !== "lower_bound") reasons.push(ESTIMATE_DISQUALIFIERS.method_not_lower_bound);
296
- const floor = estimate.counting_floor_tokens;
297
- if (typeof floor !== "number" || !Number.isFinite(floor) || floor < ESTIMATE_COUNTING_FLOOR_TOKENS) {
298
- reasons.push(ESTIMATE_DISQUALIFIERS.counting_floor_below_200k);
299
- }
300
- if (estimate.net_of_mu !== true) reasons.push(ESTIMATE_DISQUALIFIERS.not_net_of_mu);
301
- if (estimate.reports_loss !== true) reasons.push(ESTIMATE_DISQUALIFIERS.never_shows_loss);
302
- if (typeof estimate.net_lower_bound !== "number" || !Number.isFinite(estimate.net_lower_bound)) {
303
- reasons.push(ESTIMATE_DISQUALIFIERS.value_not_finite);
304
- }
305
- return { qualifies: reasons.length === 0, reasons };
306
- }
307
- var RENDER_ESTIMATE_REASONS = {
308
- unknown_surface: "unknown_surface",
309
- surface_never_shows_estimate: "surface_never_shows_estimate",
310
- estimate_does_not_qualify: "estimate_does_not_qualify"
311
- };
312
- function shouldRenderEstimate(estimate, surface) {
313
- if (!SURFACES.includes(surface)) {
314
- return { render: false, reasons: [RENDER_ESTIMATE_REASONS.unknown_surface] };
315
- }
316
- if (ESTIMATE_FORBIDDEN_SURFACES.includes(surface)) {
317
- return { render: false, reasons: [RENDER_ESTIMATE_REASONS.surface_never_shows_estimate] };
318
- }
319
- const q = estimateQualifies(estimate);
320
- if (!q.qualifies) {
321
- return { render: false, reasons: [RENDER_ESTIMATE_REASONS.estimate_does_not_qualify, ...q.reasons] };
322
- }
323
- return { render: true, reasons: [] };
324
- }
325
-
326
- // src/canonical.ts
327
- function canonicalJson(value) {
328
- if (value === null || typeof value !== "object") {
329
- return JSON.stringify(value);
330
- }
331
- if (Array.isArray(value)) {
332
- return `[${value.map(canonicalJson).join(",")}]`;
333
- }
334
- const record = value;
335
- const entries = Object.keys(record).sort().map((k) => `${JSON.stringify(k)}:${canonicalJson(record[k])}`);
336
- return `{${entries.join(",")}}`;
337
- }
338
- function base64urlToUint8(input) {
339
- if (typeof input !== "string" || !/^[A-Za-z0-9_-]*={0,2}$/.test(input)) {
340
- throw new Error("not base64url");
341
- }
342
- const b64 = input.replace(/-/g, "+").replace(/_/g, "/").replace(/=+$/, "");
343
- const padded = b64 + "===".slice(0, (4 - b64.length % 4) % 4);
344
- const binary = atob(padded);
345
- const out = new Uint8Array(binary.length);
346
- for (let i = 0; i < binary.length; i++) out[i] = binary.charCodeAt(i);
347
- return out;
348
- }
349
- function hexToUint8(hex) {
350
- return hexToBytes(hex);
351
- }
352
-
353
- // src/sharecard.ts
354
- var SHARE_CARD_TYP = "AAP-ShareCard/v1";
355
- var IDENTIFYING_KEYS = /* @__PURE__ */ new Set([
356
- "repo",
357
- "repository",
358
- "repo_name",
359
- "branch",
360
- "branch_name",
361
- "ref",
362
- "pr",
363
- "pr_title",
364
- "pull_request",
365
- "title",
366
- "commit",
367
- "commit_message",
368
- "commit_sha",
369
- "message",
370
- "author",
371
- "email",
372
- "url",
373
- "path"
374
- ]);
375
- var CARD_KEYS = ["typ", "card_id", "period", "issued_at", "signing_key_id", "statements", "lines", "signature"];
376
- var PERIOD_KEYS = ["start", "end"];
377
- var STATEMENT_KEYS = ["typ", "hash"];
378
- var LINE_KEYS = ["id", "kind", "label", "value", "statement_hash"];
379
- var HEX64 = /^[0-9a-f]{64}$/;
380
- var DATE = /^\d{4}-\d{2}-\d{2}$/;
381
- var OPAQUE_ID = /^[a-z0-9_-]{1,64}$/;
382
- var LABEL = /^[A-Za-z0-9 .,()+-]{1,40}$/;
383
- var LABEL_MAX = 40;
384
- var SHARE_CARD_STATEMENT_TYPES = [
385
- "AAP-ConductRecord/v1",
386
- "AAP-PRRecord/v1",
387
- "AAP-Outcome/v1"
388
- ];
389
- var SIGNATURE_LEN = 86;
390
- var RFC3339 = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(?:\.\d+)?(?:Z|([+-])(\d{2}):(\d{2}))$/;
391
- function isCalendarDate(y, m, d) {
392
- if (m < 1 || m > 12 || d < 1) return false;
393
- const dim = [31, y % 4 === 0 && (y % 100 !== 0 || y % 400 === 0) ? 29 : 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31][m - 1];
394
- return d <= dim;
395
- }
396
- function isRfc3339(v) {
397
- const m = RFC3339.exec(v);
398
- if (!m) return false;
399
- const [y, mo, d, h, mi, se] = m.slice(1, 7).map(Number);
400
- if (!isCalendarDate(y, mo, d) || h > 23 || mi > 59 || se > 59) return false;
401
- if (m[7] !== void 0 && (Number(m[8]) > 23 || Number(m[9]) > 59)) return false;
402
- return true;
403
- }
404
- function shareCardSigningInput(card) {
405
- const { signature: _signature, ...rest } = card;
406
- return canonicalJson(rest);
407
- }
408
- function bytesToBase64url(b) {
409
- let bin = "";
410
- for (const x of b) bin += String.fromCharCode(x);
411
- return btoa(bin).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
412
- }
413
- function base64urlToBytes(s) {
414
- if (s.length !== SIGNATURE_LEN || !/^[A-Za-z0-9_-]+$/.test(s)) throw new Error("bad base64url");
415
- const padded = s.replace(/-/g, "+").replace(/_/g, "/") + "===".slice(0, (4 - s.length % 4) % 4);
416
- const bin = atob(padded);
417
- const out = new Uint8Array(bin.length);
418
- for (let i = 0; i < bin.length; i++) out[i] = bin.charCodeAt(i);
419
- if (out.length !== 64 || bytesToBase64url(out) !== s) throw new Error("non-canonical base64url");
420
- return out;
421
- }
422
- function isObj(v) {
423
- return typeof v === "object" && v !== null && !Array.isArray(v);
424
- }
425
- function exactKeys(obj, allowed, where, errors) {
426
- for (const k of Object.keys(obj)) {
427
- if (IDENTIFYING_KEYS.has(k.toLowerCase())) errors.push(`${where}: identifying field "${k}" is not allowed`);
428
- else if (!allowed.includes(k)) errors.push(`${where}: unknown field "${k}"`);
429
- }
430
- for (const k of allowed) if (!(k in obj)) errors.push(`${where}: missing field "${k}"`);
431
- }
432
- function validateShareCardShape(card) {
433
- const errors = [];
434
- if (!isObj(card)) return ["card: not an object"];
435
- exactKeys(card, CARD_KEYS, "card", errors);
436
- if (card.typ !== SHARE_CARD_TYP) errors.push(`card: typ must be ${SHARE_CARD_TYP}`);
437
- if (typeof card.card_id !== "string" || !OPAQUE_ID.test(card.card_id)) errors.push("card: card_id must be an opaque id matching ^[a-z0-9_-]{1,64}$");
438
- if (typeof card.issued_at !== "string" || !isRfc3339(card.issued_at)) errors.push("card: issued_at must be an RFC 3339 date-time");
439
- if (typeof card.signing_key_id !== "string" || !card.signing_key_id) errors.push("card: signing_key_id must be a non-empty string");
440
- if (typeof card.signature !== "string") errors.push("card: signature must be a string");
441
- else {
442
- try {
443
- base64urlToBytes(card.signature);
444
- } catch {
445
- errors.push("card: signature must be a canonical 86-char unpadded base64url Ed25519 signature");
446
- }
447
- }
448
- if (!isObj(card.period)) errors.push("period: not an object");
449
- else {
450
- exactKeys(card.period, PERIOD_KEYS, "period", errors);
451
- for (const k of PERIOD_KEYS) {
452
- const v = card.period[k];
453
- if (typeof v !== "string" || !DATE.test(v) || !isCalendarDate(Number(v.slice(0, 4)), Number(v.slice(5, 7)), Number(v.slice(8, 10)))) {
454
- errors.push(`period.${k}: must be a real calendar date YYYY-MM-DD`);
455
- }
456
- }
457
- const { start, end } = card.period;
458
- if (typeof start === "string" && typeof end === "string" && DATE.test(start) && DATE.test(end) && start > end) {
459
- errors.push("period: start must be <= end");
460
- }
461
- }
462
- const hashes = /* @__PURE__ */ new Set();
463
- const referenced = /* @__PURE__ */ new Set();
464
- const lineIds = /* @__PURE__ */ new Set();
465
- if (!Array.isArray(card.statements) || card.statements.length === 0) errors.push("statements: must be a non-empty array");
466
- else
467
- card.statements.forEach((s, i) => {
468
- if (!isObj(s)) return void errors.push(`statements[${i}]: not an object`);
469
- exactKeys(s, STATEMENT_KEYS, `statements[${i}]`, errors);
470
- if (typeof s.typ !== "string" || !SHARE_CARD_STATEMENT_TYPES.includes(s.typ)) {
471
- errors.push(`statements[${i}].typ: must be one of ${SHARE_CARD_STATEMENT_TYPES.join(", ")}`);
472
- }
473
- if (typeof s.hash !== "string" || !HEX64.test(s.hash)) errors.push(`statements[${i}].hash: must be 64 lowercase hex`);
474
- else hashes.add(s.hash);
475
- });
476
- if (!Array.isArray(card.lines) || card.lines.length === 0) errors.push("lines: must be a non-empty array");
477
- else
478
- card.lines.forEach((l, i) => {
479
- if (!isObj(l)) return void errors.push(`lines[${i}]: not an object`);
480
- exactKeys(l, LINE_KEYS, `lines[${i}]`, errors);
481
- if (typeof l.id !== "string" || !OPAQUE_ID.test(l.id)) errors.push(`lines[${i}].id: must be an opaque id matching ^[a-z0-9_-]{1,64}$`);
482
- else if (lineIds.has(l.id)) errors.push(`lines[${i}].id: duplicate line id "${l.id}"`);
483
- else lineIds.add(l.id);
484
- if (l.kind !== "count" && l.kind !== "revert") errors.push(`lines[${i}].kind: must be "count" or "revert"`);
485
- if (typeof l.label !== "string" || !l.label || l.label.length > LABEL_MAX || /[/#]|:\/\/|www\./i.test(l.label) || !LABEL.test(l.label)) {
486
- errors.push(`lines[${i}].label: must be short plain text (max ${LABEL_MAX}), no "/", "#" or URLs`);
487
- }
488
- if (typeof l.value !== "number" || !Number.isSafeInteger(l.value) || l.value < 0) errors.push(`lines[${i}].value: must be a non-negative integer`);
489
- if (typeof l.statement_hash !== "string" || !HEX64.test(l.statement_hash)) errors.push(`lines[${i}].statement_hash: must be 64 lowercase hex`);
490
- else if (!hashes.has(l.statement_hash)) errors.push(`lines[${i}].statement_hash: not committed in statements`);
491
- else referenced.add(l.statement_hash);
492
- });
493
- for (const h of hashes) if (!referenced.has(h)) errors.push(`statements: ${h} is not referenced by any line`);
494
- return errors;
495
- }
496
- async function verifyShareCard(card, options) {
497
- const fail2 = (errors) => ({ valid: false, lines: [], errors });
498
- const shapeErrors = validateShareCardShape(card);
499
- if (shapeErrors.length > 0) return fail2(shapeErrors);
500
- const c = card;
501
- let ok = false;
502
- try {
503
- const pk = typeof options.publicKey === "string" ? hexToUint8(options.publicKey) : options.publicKey;
504
- ok = await verifyAsync(base64urlToBytes(c.signature), utf8ToBytes(shareCardSigningInput(c)), pk, { zip215: false });
505
- } catch {
506
- ok = false;
507
- }
508
- if (!ok) return fail2(["signature: verification failed"]);
509
- return { valid: true, lines: c.lines.map((l) => ({ ...l })), errors: [] };
510
- }
511
-
512
- // src/statements.ts
513
- var STATEMENT_TYPES = [
514
- "AAP-ConductRecord/v1",
515
- "AAP-PRRecord/v1",
516
- "AAP-Outcome/v1",
517
- "AAP-ShareCard/v1"
518
- ];
519
- var BUNDLE_TYP = "AAP-RecordBundle/v1";
520
- var CARD_LINE_CLASSES = ["measured", "verdict", "outcome"];
521
- var REASONS = {
522
- not_an_object: "not_an_object",
523
- unknown_typ: "unknown_typ",
524
- unsupported_alg: "unsupported_alg",
525
- signing_key_id_missing: "signing_key_id_missing",
526
- signature_missing: "signature_missing",
527
- signature_malformed: "signature_malformed",
528
- key_not_found: "key_not_found",
529
- key_not_ed25519: "key_not_ed25519",
530
- signature_invalid: "signature_invalid",
531
- bundle_not_an_object: "bundle_not_an_object",
532
- bundle_unknown_typ: "bundle_unknown_typ",
533
- bundle_statements_missing: "bundle_statements_missing",
534
- digest_malformed: "digest_malformed",
535
- linked_statement_wrong_typ: "linked_statement_wrong_typ",
536
- card_line_class_not_allowed: "card_line_class_not_allowed",
537
- card_verdict_without_coverage: "card_verdict_without_coverage"
538
- };
539
- var FLAGS = {
540
- card_hash_not_recorded: "card hash not recorded",
541
- card_not_bound: "card not bound",
542
- linked_statement_not_in_bundle: "linked statement not in bundle"
543
- };
544
-
545
- // src/verify-record.ts
546
- var DIGEST_RE = /^[0-9a-f]{64}$/;
547
- function isObject3(v) {
548
- return typeof v === "object" && v !== null && !Array.isArray(v);
549
- }
550
- function statementCommitment(statement) {
551
- const { signature: _signature, ...rest } = statement;
552
- return canonicalJson(rest);
553
- }
554
- function statementDigest(statement) {
555
- return bytesToHex(sha256(utf8ToBytes(statementCommitment(statement))));
556
- }
557
- function selectKey(jwks, kid) {
558
- const keys = isObject3(jwks) && Array.isArray(jwks.keys) ? jwks.keys : [];
559
- const jwk = keys.find((k) => isObject3(k) && k.kid === kid);
560
- if (!jwk) return { reason: REASONS.key_not_found };
561
- if (jwk.alg !== "EdDSA" || jwk.kty !== "OKP" || jwk.crv !== "Ed25519" || typeof jwk.x !== "string") {
562
- return { reason: REASONS.key_not_ed25519 };
563
- }
564
- let key;
565
- try {
566
- key = base64urlToUint8(jwk.x);
567
- } catch {
568
- return { reason: REASONS.key_not_ed25519 };
569
- }
570
- if (key.length !== 32) return { reason: REASONS.key_not_ed25519 };
571
- return { key };
572
- }
573
- function checkpointFlags(checkpoints) {
574
- if (!Array.isArray(checkpoints)) return [];
575
- const flags = [];
576
- for (const c of checkpoints) {
577
- if (!isObject3(c)) continue;
578
- const subject = typeof c.checkpoint_id === "string" ? c.checkpoint_id : "<no checkpoint_id>";
579
- if (typeof c.card_hash !== "string" || c.card_hash.length === 0) {
580
- flags.push({ flag: FLAGS.card_hash_not_recorded, subject });
581
- }
582
- const bound = typeof c.combined_commitment === "string" && c.combined_commitment.length > 0 && typeof c.commitment_signature === "string" && c.commitment_signature.length > 0;
583
- if (!bound) flags.push({ flag: FLAGS.card_not_bound, subject });
584
- }
585
- return flags;
586
- }
587
- function shareCardReasons(lines) {
588
- if (!Array.isArray(lines)) return [];
589
- const reasons = [];
590
- for (const line of lines) {
591
- if (!isObject3(line) || !CARD_LINE_CLASSES.includes(line.class)) {
592
- reasons.push(REASONS.card_line_class_not_allowed);
593
- continue;
594
- }
595
- if (line.class === "verdict") {
596
- const cov = line.coverage;
597
- const ok = isObject3(cov) && Number.isInteger(cov.judged) && Number.isInteger(cov.total) && cov.judged >= 0 && cov.total >= cov.judged;
598
- if (!ok) reasons.push(REASONS.card_verdict_without_coverage);
599
- }
600
- }
601
- return [...new Set(reasons)];
602
- }
603
- function digestFieldsOf(statement) {
604
- const out = [];
605
- if (statement.previous_digest !== void 0 && statement.previous_digest !== null) {
606
- out.push(statement.previous_digest);
607
- }
608
- if (statement.pr_record_digest !== void 0) out.push(statement.pr_record_digest);
609
- if (statement.source_digest !== void 0) out.push(statement.source_digest);
610
- if (statement.conduct_digests !== void 0) {
611
- if (Array.isArray(statement.conduct_digests)) out.push(...statement.conduct_digests);
612
- else out.push(null);
613
- }
614
- return out;
615
- }
616
- async function verifyStatement(statement, jwks) {
617
- const fail2 = (reasons2, partial = {}) => ({
618
- valid: false,
619
- typ: null,
620
- digest: null,
621
- signing_key_id: null,
622
- flags: [],
623
- ...partial,
624
- reasons: reasons2
625
- });
626
- if (!isObject3(statement)) return fail2([REASONS.not_an_object]);
627
- const typ = typeof statement.typ === "string" ? statement.typ : null;
628
- const digest = statementDigest(statement);
629
- const kid = typeof statement.signing_key_id === "string" ? statement.signing_key_id : null;
630
- const base = { typ, digest, signing_key_id: kid };
631
- if (!typ || !STATEMENT_TYPES.includes(typ)) {
632
- return fail2([REASONS.unknown_typ], base);
633
- }
634
- if (statement.alg !== void 0 && statement.alg !== "EdDSA") {
635
- return fail2([REASONS.unsupported_alg], base);
636
- }
637
- if (!kid) return fail2([REASONS.signing_key_id_missing], base);
638
- if (typeof statement.signature !== "string" || statement.signature.length === 0) {
639
- return fail2([REASONS.signature_missing], base);
640
- }
641
- let sig;
642
- try {
643
- sig = base64urlToUint8(statement.signature);
644
- } catch {
645
- return fail2([REASONS.signature_malformed], base);
646
- }
647
- if (sig.length !== 64) return fail2([REASONS.signature_malformed], base);
648
- const selected = selectKey(jwks, kid);
649
- if (!selected.key) return fail2([selected.reason], base);
650
- let sigOk = false;
651
- try {
652
- sigOk = await verifyAsync(sig, utf8ToBytes(statementCommitment(statement)), selected.key);
653
- } catch {
654
- sigOk = false;
655
- }
656
- if (!sigOk) return fail2([REASONS.signature_invalid], base);
657
- const reasons = [];
658
- for (const d of digestFieldsOf(statement)) {
659
- if (typeof d !== "string" || !DIGEST_RE.test(d)) {
660
- reasons.push(REASONS.digest_malformed);
661
- break;
662
- }
663
- }
664
- if (typ === "AAP-ShareCard/v1") reasons.push(...shareCardReasons(statement.lines));
665
- const flags = typ === "AAP-ConductRecord/v1" ? checkpointFlags(statement.checkpoints) : [];
666
- return {
667
- valid: reasons.length === 0,
668
- typ,
669
- digest,
670
- signing_key_id: kid,
671
- reasons,
672
- flags
673
- };
674
- }
675
- var LINK_TARGETS = {
676
- previous_digest: "same",
677
- conduct_digests: ["AAP-ConductRecord/v1"],
678
- pr_record_digest: ["AAP-PRRecord/v1"],
679
- source_digest: ["AAP-ConductRecord/v1", "AAP-PRRecord/v1", "AAP-Outcome/v1"]
680
- };
681
- async function verify(input, jwks) {
682
- const verified_at = (/* @__PURE__ */ new Date()).toISOString();
683
- const result = (valid, reasons2, flags2, statements2) => ({ valid, reasons: reasons2, flags: flags2, statements: statements2, verified_at });
684
- if (!isObject3(input)) return result(false, [REASONS.bundle_not_an_object], [], []);
685
- if (input.typ !== BUNDLE_TYP && Array.isArray(input.statements) && !STATEMENT_TYPES.includes(input.typ)) {
686
- return result(false, [REASONS.bundle_unknown_typ], [], []);
687
- }
688
- if (input.typ !== BUNDLE_TYP) {
689
- const one = await verifyStatement(input, jwks);
690
- return result(one.valid, one.reasons.map((r) => `0:${r}`), one.flags, [one]);
691
- }
692
- const rawStatements = Array.isArray(input.statements) ? input.statements : null;
693
- if (!rawStatements) {
694
- return result(false, [REASONS.bundle_statements_missing], [], []);
695
- }
696
- const statements = [];
697
- for (const s of rawStatements) statements.push(await verifyStatement(s, jwks));
698
- const byDigest = /* @__PURE__ */ new Map();
699
- statements.forEach((s) => {
700
- if (s.digest) byDigest.set(s.digest, s);
701
- });
702
- const reasons = [];
703
- const flags = [];
704
- statements.forEach((s, i) => {
705
- s.reasons.forEach((r) => reasons.push(`${i}:${r}`));
706
- flags.push(...s.flags);
707
- const raw = rawStatements[i];
708
- if (!s.valid || !isObject3(raw)) return;
709
- for (const [field, targets] of Object.entries(LINK_TARGETS)) {
710
- const value = raw[field];
711
- const digests = Array.isArray(value) ? value : [value];
712
- for (const d of digests) {
713
- if (typeof d !== "string") continue;
714
- const linked = byDigest.get(d);
715
- if (!linked) {
716
- flags.push({ flag: FLAGS.linked_statement_not_in_bundle, subject: d });
717
- continue;
718
- }
719
- const allowed = targets === "same" ? [s.typ] : targets;
720
- if (!linked.typ || !allowed.includes(linked.typ)) {
721
- reasons.push(`${i}:${REASONS.linked_statement_wrong_typ}`);
722
- }
723
- }
724
- }
725
- });
726
- return result(reasons.length === 0, reasons, flags, statements);
727
- }
728
-
729
- // src/render-lines.ts
730
- var RENDER_LINES_REASONS = {
731
- render_lines_disabled: "render_lines_disabled",
732
- unknown_surface: "unknown_surface",
733
- not_verifiable: "not_verifiable",
734
- missing_key: "missing_key",
735
- no_share_card_statement: "no_share_card_statement",
736
- identifying_field: "identifying_field",
737
- line_class_not_allowed: "line_class_not_allowed",
738
- line_malformed: "line_malformed",
739
- no_lines: "no_lines",
740
- period_card_without_outcomes: "period_card_without_outcomes",
741
- period_card_without_revert_line: "period_card_without_revert_line",
742
- estimate_not_renderable: "estimate_not_renderable",
743
- lint_failed: "lint_failed"
744
- };
745
- var R = RENDER_LINES_REASONS;
746
- function renderLinesEnabled(opts) {
747
- if (opts?.enabled === true) return true;
748
- const env = globalThis.process?.env;
749
- return env?.AIP_RENDER_LINES === "1";
750
- }
751
- var fail = (reasons, violations) => ({
752
- ok: false,
753
- lines: [],
754
- reasons,
755
- ...violations ? { violations } : {}
756
- });
757
- function isObj2(v) {
758
- return typeof v === "object" && v !== null && !Array.isArray(v);
759
- }
760
- var STATEMENT_KEYS2 = /* @__PURE__ */ new Set([
761
- "typ",
762
- "signing_key_id",
763
- "signature",
764
- "alg",
765
- "built_at",
766
- "previous_digest",
767
- "commitment_key_id",
768
- "unit_kind",
769
- "source_digest",
770
- "lines"
771
- ]);
772
- var STATEMENT_LINE_KEYS = /* @__PURE__ */ new Set(["class", "text", "coverage"]);
773
- var COVERAGE_KEYS = /* @__PURE__ */ new Set(["judged", "total"]);
774
- function unknownKeys(o, allowed, where) {
775
- return Object.keys(o).filter((k) => !allowed.has(k)).map((k) => `${R.identifying_field}:${where}.${k}`);
776
- }
777
- function outcomeKind(s) {
778
- if (typeof s.outcome !== "string") return null;
779
- if (s.outcome === "accepted_no_revert") return "accepted";
780
- if (s.outcome === "reverted") return "reverted";
781
- return null;
782
- }
783
- function finish(surface, lines) {
784
- if (lines.length === 0) return fail([R.no_lines]);
785
- if (surface === "period_card" && !lines.some((l) => l.revert === true)) {
786
- return fail([R.period_card_without_revert_line]);
787
- }
788
- const lintLines = lines.map(({ revert: _revert, ...l }) => l);
789
- const lint = lintRendering({ surface, lines: lintLines });
790
- if (!lint.ok) return fail([R.lint_failed], lint.violations);
791
- return { ok: true, lines, reasons: [] };
792
- }
793
- async function renderFromStatements(input, surface, jwks) {
794
- const v = await verify(input, jwks);
795
- if (!v.valid) return fail([R.not_verifiable, ...v.reasons]);
796
- const statements = Array.isArray(input.statements) ? input.statements : [input];
797
- const cards = statements.filter((s) => isObj2(s) && s.typ === "AAP-ShareCard/v1");
798
- if (cards.length === 0) return fail([R.no_share_card_statement]);
799
- const errors = [];
800
- const lines = [];
801
- cards.forEach((card, ci) => {
802
- errors.push(...unknownKeys(card, STATEMENT_KEYS2, `card${ci}`));
803
- const raw = Array.isArray(card.lines) ? card.lines : [];
804
- raw.forEach((l, i) => {
805
- const where = `card${ci}.lines[${i}]`;
806
- if (!isObj2(l)) return void errors.push(`${R.line_malformed}:${where}`);
807
- errors.push(...unknownKeys(l, STATEMENT_LINE_KEYS, where));
808
- if (l.class !== "measured" && l.class !== "outcome" && l.class !== "verdict") {
809
- return void errors.push(`${R.line_class_not_allowed}:${where}`);
810
- }
811
- if (typeof l.text !== "string" || l.text.length === 0) return void errors.push(`${R.line_malformed}:${where}`);
812
- const out = { class: l.class, text: l.text };
813
- if (l.class === "verdict") {
814
- const c = l.coverage;
815
- if (!isObj2(c)) return void errors.push(`${R.line_malformed}:${where}.coverage`);
816
- errors.push(...unknownKeys(c, COVERAGE_KEYS, `${where}.coverage`));
817
- out.coverage = { judged: c.judged, total: c.total };
818
- } else if (l.coverage !== void 0 && l.coverage !== null) {
819
- errors.push(`${R.identifying_field}:${where}.coverage`);
820
- }
821
- lines.push(out);
822
- });
823
- });
824
- if (surface === "period_card") {
825
- const outcomes = statements.filter((s) => isObj2(s) && s.typ === "AAP-Outcome/v1");
826
- if (outcomes.length === 0) errors.push(R.period_card_without_outcomes);
827
- else {
828
- const kinds = outcomes.map(outcomeKind);
829
- if (kinds.includes(null)) errors.push(`${R.line_malformed}:outcome`);
830
- else {
831
- const reverted = kinds.filter((k) => k === "reverted").length;
832
- lines.push({ class: "outcome", text: `${reverted} of ${kinds.length} changes reverted`, revert: true });
833
- }
834
- }
835
- }
836
- if (errors.length > 0) return fail(errors);
837
- return finish(surface, lines);
838
- }
839
- async function renderFromNumericCard(card, surface, publicKey) {
840
- const shape = validateShareCardShape(card);
841
- if (shape.length > 0) {
842
- const ident = shape.some((e) => /identifying field/.test(e));
843
- return fail([ident ? R.identifying_field : R.not_verifiable, ...shape]);
844
- }
845
- const v = await verifyShareCard(card, { publicKey });
846
- if (!v.valid) return fail([R.not_verifiable, ...v.errors]);
847
- const lines = v.lines.map(
848
- (l) => l.kind === "revert" ? { class: "outcome", text: `${l.label}: ${l.value}`, revert: true } : { class: "measured", text: `${l.label}: ${l.value}` }
849
- );
850
- return finish(surface, lines);
851
- }
852
- async function renderLines(input, opts = {}) {
853
- try {
854
- if (!renderLinesEnabled(opts)) return fail([R.render_lines_disabled]);
855
- const surface = opts.surface ?? "share_card";
856
- if (surface !== "share_card" && surface !== "period_card") return fail([R.unknown_surface]);
857
- if (!isObj2(input)) return fail([R.not_verifiable]);
858
- if ("estimate" in input || "savings" in input) {
859
- const d = shouldRenderEstimate(input.estimate ?? input.savings, surface);
860
- return fail([R.estimate_not_renderable, ...d.reasons]);
861
- }
862
- if ("card_id" in input) {
863
- if (opts.publicKey === void 0) return fail([R.missing_key]);
864
- return await renderFromNumericCard(input, surface, opts.publicKey);
865
- }
866
- if (!opts.jwks) return fail([R.missing_key]);
867
- return await renderFromStatements(input, surface, opts.jwks);
868
- } catch {
869
- return fail([R.not_verifiable]);
870
- }
871
- }
872
-
873
- export { RENDER_LINES_REASONS, renderLines, renderLinesEnabled };
874
- //# sourceMappingURL=render-lines.js.map
875
- //# sourceMappingURL=render-lines.js.map