@aplaytest/core 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (112) hide show
  1. package/dist/.tsbuildinfo +1 -0
  2. package/dist/config/schema.d.ts +247 -0
  3. package/dist/config/schema.d.ts.map +1 -0
  4. package/dist/config/schema.js +279 -0
  5. package/dist/config/schema.js.map +1 -0
  6. package/dist/evidence/id.d.ts +17 -0
  7. package/dist/evidence/id.d.ts.map +1 -0
  8. package/dist/evidence/id.js +20 -0
  9. package/dist/evidence/id.js.map +1 -0
  10. package/dist/evidence/redact.d.ts +22 -0
  11. package/dist/evidence/redact.d.ts.map +1 -0
  12. package/dist/evidence/redact.js +97 -0
  13. package/dist/evidence/redact.js.map +1 -0
  14. package/dist/evidence/store.d.ts +51 -0
  15. package/dist/evidence/store.d.ts.map +1 -0
  16. package/dist/evidence/store.js +148 -0
  17. package/dist/evidence/store.js.map +1 -0
  18. package/dist/evidence/types.d.ts +183 -0
  19. package/dist/evidence/types.d.ts.map +1 -0
  20. package/dist/evidence/types.js +21 -0
  21. package/dist/evidence/types.js.map +1 -0
  22. package/dist/history/ingest-playwright.d.ts +26 -0
  23. package/dist/history/ingest-playwright.d.ts.map +1 -0
  24. package/dist/history/ingest-playwright.js +73 -0
  25. package/dist/history/ingest-playwright.js.map +1 -0
  26. package/dist/history/ingest.d.ts +29 -0
  27. package/dist/history/ingest.d.ts.map +1 -0
  28. package/dist/history/ingest.js +86 -0
  29. package/dist/history/ingest.js.map +1 -0
  30. package/dist/history/memory-index.d.ts +88 -0
  31. package/dist/history/memory-index.d.ts.map +1 -0
  32. package/dist/history/memory-index.js +236 -0
  33. package/dist/history/memory-index.js.map +1 -0
  34. package/dist/history/playwright-report.d.ts +102 -0
  35. package/dist/history/playwright-report.d.ts.map +1 -0
  36. package/dist/history/playwright-report.js +274 -0
  37. package/dist/history/playwright-report.js.map +1 -0
  38. package/dist/history/store.d.ts +73 -0
  39. package/dist/history/store.d.ts.map +1 -0
  40. package/dist/history/store.js +243 -0
  41. package/dist/history/store.js.map +1 -0
  42. package/dist/history/types.d.ts +69 -0
  43. package/dist/history/types.d.ts.map +1 -0
  44. package/dist/history/types.js +18 -0
  45. package/dist/history/types.js.map +1 -0
  46. package/dist/history/url.d.ts +79 -0
  47. package/dist/history/url.d.ts.map +1 -0
  48. package/dist/history/url.js +121 -0
  49. package/dist/history/url.js.map +1 -0
  50. package/dist/index.d.ts +39 -0
  51. package/dist/index.d.ts.map +1 -0
  52. package/dist/index.js +31 -0
  53. package/dist/index.js.map +1 -0
  54. package/dist/locator/stability.d.ts +63 -0
  55. package/dist/locator/stability.d.ts.map +1 -0
  56. package/dist/locator/stability.js +127 -0
  57. package/dist/locator/stability.js.map +1 -0
  58. package/dist/taxonomy/classify.d.ts +63 -0
  59. package/dist/taxonomy/classify.d.ts.map +1 -0
  60. package/dist/taxonomy/classify.js +344 -0
  61. package/dist/taxonomy/classify.js.map +1 -0
  62. package/dist/taxonomy/kinds.d.ts +49 -0
  63. package/dist/taxonomy/kinds.d.ts.map +1 -0
  64. package/dist/taxonomy/kinds.js +139 -0
  65. package/dist/taxonomy/kinds.js.map +1 -0
  66. package/dist/taxonomy/parse-error.d.ts +85 -0
  67. package/dist/taxonomy/parse-error.d.ts.map +1 -0
  68. package/dist/taxonomy/parse-error.js +179 -0
  69. package/dist/taxonomy/parse-error.js.map +1 -0
  70. package/dist/version.d.ts +10 -0
  71. package/dist/version.d.ts.map +1 -0
  72. package/dist/version.js +10 -0
  73. package/dist/version.js.map +1 -0
  74. package/dist-cjs/.tsbuildinfo +1 -0
  75. package/dist-cjs/config/schema.js +283 -0
  76. package/dist-cjs/config/schema.js.map +1 -0
  77. package/dist-cjs/evidence/id.js +24 -0
  78. package/dist-cjs/evidence/id.js.map +1 -0
  79. package/dist-cjs/evidence/redact.js +103 -0
  80. package/dist-cjs/evidence/redact.js.map +1 -0
  81. package/dist-cjs/evidence/store.js +156 -0
  82. package/dist-cjs/evidence/store.js.map +1 -0
  83. package/dist-cjs/evidence/types.js +25 -0
  84. package/dist-cjs/evidence/types.js.map +1 -0
  85. package/dist-cjs/history/ingest-playwright.js +76 -0
  86. package/dist-cjs/history/ingest-playwright.js.map +1 -0
  87. package/dist-cjs/history/ingest.js +89 -0
  88. package/dist-cjs/history/ingest.js.map +1 -0
  89. package/dist-cjs/history/memory-index.js +242 -0
  90. package/dist-cjs/history/memory-index.js.map +1 -0
  91. package/dist-cjs/history/playwright-report.js +277 -0
  92. package/dist-cjs/history/playwright-report.js.map +1 -0
  93. package/dist-cjs/history/store.js +247 -0
  94. package/dist-cjs/history/store.js.map +1 -0
  95. package/dist-cjs/history/types.js +23 -0
  96. package/dist-cjs/history/types.js.map +1 -0
  97. package/dist-cjs/history/url.js +127 -0
  98. package/dist-cjs/history/url.js.map +1 -0
  99. package/dist-cjs/index.js +88 -0
  100. package/dist-cjs/index.js.map +1 -0
  101. package/dist-cjs/locator/stability.js +134 -0
  102. package/dist-cjs/locator/stability.js.map +1 -0
  103. package/dist-cjs/package.json +3 -0
  104. package/dist-cjs/taxonomy/classify.js +348 -0
  105. package/dist-cjs/taxonomy/classify.js.map +1 -0
  106. package/dist-cjs/taxonomy/kinds.js +145 -0
  107. package/dist-cjs/taxonomy/kinds.js.map +1 -0
  108. package/dist-cjs/taxonomy/parse-error.js +186 -0
  109. package/dist-cjs/taxonomy/parse-error.js.map +1 -0
  110. package/dist-cjs/version.js +13 -0
  111. package/dist-cjs/version.js.map +1 -0
  112. package/package.json +39 -0
@@ -0,0 +1,283 @@
1
+ "use strict";
2
+ /**
3
+ * Configuration: one file, fully typed, Zod-validated at load, every field
4
+ * optional with a documented default.
5
+ *
6
+ * The defaults encode the project's core stance — `mode: 'strict'`,
7
+ * `heal.apply: 'propose'`, quarantine expiry ON — so that a consumer who
8
+ * writes `export default defineAtestConfig({})` gets the safe system rather
9
+ * than an unconfigured one.
10
+ */
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.AGGRESSIVENESS_PRESETS = exports.AtestConfigSchema = exports.AggressivenessSchema = exports.ExecutionModeSchema = void 0;
13
+ exports.defineAtestConfig = defineAtestConfig;
14
+ const zod_1 = require("zod");
15
+ const stability_js_1 = require("../locator/stability.js");
16
+ exports.ExecutionModeSchema = zod_1.z.enum(['strict', 'assisted', 'agentic']);
17
+ exports.AggressivenessSchema = zod_1.z.enum(['off', 'conservative', 'balanced', 'aggressive']);
18
+ const PlaywrightSchema = zod_1.z.object({
19
+ /** Named Playwright configs this project can drive. */
20
+ configs: zod_1.z.record(zod_1.z.string(), zod_1.z.string()).prefault({}),
21
+ defaultConfig: zod_1.z.string().default('playwright.config.ts'),
22
+ });
23
+ const HistorySchema = zod_1.z.object({
24
+ /**
25
+ * Chosen from `url`, not configured separately. Two fields naming one thing
26
+ * can disagree, and `driver: 'sqlite'` beside an `azblob://` url has no
27
+ * correct interpretation — so this is derived, and left here only because
28
+ * `aplaytest doctor` and the report header print it.
29
+ */
30
+ driver: zod_1.z.enum(['sqlite', 'azure-blob', 'memory']).default('sqlite'),
31
+ /**
32
+ * Where history lives. A path is a local SQLite file; `azblob://<account>/
33
+ * <container>[/<prefix>]` is Azure Blob Storage; `:memory:` is a throwaway.
34
+ *
35
+ * `:memory:` is a footgun in CI and is deliberately NOT the default here —
36
+ * with it, every run sees one attempt per test and flake scoring reports
37
+ * "insufficient data" forever, which reads as the engine working.
38
+ */
39
+ url: zod_1.z.string().default('.atest/history.sqlite'),
40
+ /** Attempts older than this are pruned by `aplaytest history prune`. */
41
+ retainDays: zod_1.z.number().int().positive().default(90),
42
+ /**
43
+ * Days the blob driver downloads before scoring. Bounded on purpose: an
44
+ * unbounded read gets slower every week until someone turns the feature off.
45
+ * Keep it at or below `retainDays` — reading further back than prune keeps
46
+ * only buys empty listings.
47
+ */
48
+ windowDays: zod_1.z.number().int().positive().default(90),
49
+ });
50
+ const EvidenceSchema = zod_1.z.object({
51
+ dir: zod_1.z.string().default('.atest/evidence'),
52
+ retainRuns: zod_1.z.number().int().positive().default(50),
53
+ /**
54
+ * Header/body/console keys scrubbed before anything is written to disk or
55
+ * sent to a model. A suite with MSAL auth WILL capture bearer tokens.
56
+ */
57
+ redact: zod_1.z
58
+ .array(zod_1.z.string())
59
+ .default(['password', 'token', 'authorization', 'cookie', 'secret', 'api-key']),
60
+ /** Token ceiling for the ARIA snapshot before truncation (marked explicitly). */
61
+ ariaMaxTokens: zod_1.z.number().int().positive().default(6_000),
62
+ });
63
+ const LlmSchema = zod_1.z.object({
64
+ /**
65
+ * Model provider. Only `anthropic` is implemented; `none` disables the
66
+ * model tier entirely. OpenAI and Ollama are not advertised here because
67
+ * a config that parses and then silently uses a different provider is
68
+ * worse than a config that fails at load.
69
+ */
70
+ provider: zod_1.z.enum(['anthropic', 'none']).default('anthropic'),
71
+ models: zod_1.z
72
+ .object({
73
+ classify: zod_1.z.string().default('claude-haiku-4-5'),
74
+ heal: zod_1.z.string().default('claude-sonnet-5'),
75
+ author: zod_1.z.string().default('claude-opus-5'),
76
+ vision: zod_1.z.string().default('claude-sonnet-5'),
77
+ })
78
+ .prefault({}),
79
+ /**
80
+ * Effort replaces temperature: sampling parameters are rejected outright by
81
+ * current Opus/Sonnet models. See docs/10-recommendations.md.
82
+ */
83
+ effort: zod_1.z
84
+ .object({
85
+ classify: zod_1.z.enum(['low', 'medium', 'high', 'xhigh', 'max']).default('low'),
86
+ heal: zod_1.z.enum(['low', 'medium', 'high', 'xhigh', 'max']).default('medium'),
87
+ author: zod_1.z.enum(['low', 'medium', 'high', 'xhigh', 'max']).default('xhigh'),
88
+ })
89
+ .prefault({}),
90
+ budget: zod_1.z
91
+ .object({
92
+ perFailureUsd: zod_1.z.number().positive().default(0.05),
93
+ perRunUsd: zod_1.z.number().positive().default(2.0),
94
+ })
95
+ .prefault({}),
96
+ });
97
+ const HealSchema = zod_1.z.object({
98
+ aggressiveness: exports.AggressivenessSchema.default('balanced'),
99
+ strategies: zod_1.z.array(zod_1.z.enum(['selector', 'assertion', 'flow'])).default(['selector']),
100
+ /** Re-runs required before a proposal is considered validated. */
101
+ validationRuns: zod_1.z.number().int().min(1).default(3),
102
+ /** Also re-run the rest of the spec file, to catch collateral damage. */
103
+ validateCollateral: zod_1.z.boolean().default(true),
104
+ /** Reject candidates weaker than this rank. 4 = text; excludes css/xpath. */
105
+ minStabilityRank: zod_1.z.number().int().min(0).max(stability_js_1.MAX_STABILITY_RANK).default(4),
106
+ allowedStrategies: zod_1.z.array(zod_1.z.enum(stability_js_1.LOCATOR_STRATEGIES)).default(['testid', 'role', 'label', 'text']),
107
+ apply: zod_1.z.enum(['propose', 'pr', 'local']).default('propose'),
108
+ /** Glob patterns a heal patch is permitted to touch. */
109
+ targets: zod_1.z
110
+ .array(zod_1.z.string())
111
+ .default([
112
+ 'src/**/*.constants.ts',
113
+ 'src/**/*.page.ts',
114
+ 'src/**/*.section.ts',
115
+ 'tests/**/*.spec.ts',
116
+ 'tests/**/*.test.ts',
117
+ ]),
118
+ });
119
+ const FlakySchema = zod_1.z.object({
120
+ window: zod_1.z
121
+ .object({
122
+ runs: zod_1.z.number().int().positive().default(50),
123
+ days: zod_1.z.number().int().positive().default(14),
124
+ })
125
+ .prefault({}),
126
+ /** Recency weighting: a failure this old counts half as much as one today. */
127
+ halfLifeDays: zod_1.z.number().positive().default(7),
128
+ threshold: zod_1.z.number().min(0).max(1).default(0.15),
129
+ minRuns: zod_1.z.number().int().positive().default(10),
130
+ quarantine: zod_1.z
131
+ .object({
132
+ policy: zod_1.z.enum(['off', 'propose', 'auto']).default('propose'),
133
+ expiryDays: zod_1.z.number().int().positive().default(14),
134
+ maxTests: zod_1.z.number().int().nonnegative().default(5),
135
+ maxRatio: zod_1.z.number().min(0).max(1).default(0.02),
136
+ tag: zod_1.z.string().default('@quarantine'),
137
+ })
138
+ .prefault({}),
139
+ });
140
+ const ImpactSchema = zod_1.z.object({
141
+ enabled: zod_1.z.boolean().default(true),
142
+ /** Changing any of these runs the whole suite, no questions asked. */
143
+ fullSuiteTriggers: zod_1.z
144
+ .array(zod_1.z.string())
145
+ .default([
146
+ 'package.json',
147
+ 'package-lock.json',
148
+ 'pnpm-lock.yaml',
149
+ 'playwright*.config.ts',
150
+ 'src/shared/**',
151
+ 'atest.config.ts',
152
+ '.github/workflows/**',
153
+ 'Dockerfile',
154
+ ]),
155
+ /** Tags that always run regardless of impact selection. */
156
+ alwaysRunTags: zod_1.z.array(zod_1.z.string()).default(['@smoke']),
157
+ /** Above this share of the suite, just run everything. */
158
+ fullSuiteThreshold: zod_1.z.number().min(0).max(1).default(0.6),
159
+ });
160
+ const ConventionsSchema = zod_1.z.object({
161
+ titlePattern: zod_1.z.string().default('^Given .+, when .+, then .+$'),
162
+ requiredTags: zod_1.z.array(zod_1.z.string()).default([]),
163
+ seededDataDir: zod_1.z.string().default('tests/testdata/seeded'),
164
+ /**
165
+ * Paths no agent may write to, ever. The seeded data is the oracle: an
166
+ * agent that can edit expected values can make any test pass.
167
+ */
168
+ forbidWriteTo: zod_1.z
169
+ .array(zod_1.z.string())
170
+ .default([
171
+ 'tests/testdata/seeded/**',
172
+ '**/__screenshots__/**',
173
+ '**/__aria__/**',
174
+ '.env*',
175
+ '.github/workflows/**',
176
+ 'atest.config.ts',
177
+ ]),
178
+ pageObjectDir: zod_1.z.string().default('src/ui/pages'),
179
+ /** Run against generated code before it is ever proposed. */
180
+ verifyCommands: zod_1.z.array(zod_1.z.string()).default(['npm run typecheck', 'npm run lint']),
181
+ });
182
+ const IntegrationsSchema = zod_1.z.object({
183
+ github: zod_1.z
184
+ .object({
185
+ autoComment: zod_1.z.boolean().default(true),
186
+ autoIssue: zod_1.z.boolean().default(false),
187
+ healBranchPrefix: zod_1.z.string().default('atest/heal/'),
188
+ })
189
+ .prefault({}),
190
+ otel: zod_1.z
191
+ .object({
192
+ endpoint: zod_1.z.string().nullable().default(null),
193
+ queryUrl: zod_1.z.string().nullable().default(null),
194
+ })
195
+ .prefault({}),
196
+ });
197
+ exports.AtestConfigSchema = zod_1.z.object({
198
+ mode: exports.ExecutionModeSchema.default('strict'),
199
+ playwright: PlaywrightSchema.prefault({}),
200
+ history: HistorySchema.prefault({}),
201
+ evidence: EvidenceSchema.prefault({}),
202
+ llm: LlmSchema.prefault({}),
203
+ heal: HealSchema.prefault({}),
204
+ flaky: FlakySchema.prefault({}),
205
+ impact: ImpactSchema.prefault({}),
206
+ conventions: ConventionsSchema.prefault({}),
207
+ integrations: IntegrationsSchema.prefault({}),
208
+ });
209
+ /**
210
+ * Validate user config and apply the safe defaults.
211
+ *
212
+ * An empty object yields `mode: 'strict'`, propose-only healing, and
213
+ * quarantine expiry — a consumer who configures nothing must not get an
214
+ * unguarded system. A misconfigured run fails here, at startup, not
215
+ * halfway through a suite.
216
+ *
217
+ * @param input - Partial config plus optional identity hooks for run/trace ids.
218
+ * @returns Every field filled, with `identity` attached (empty object if omitted).
219
+ * @throws {Error} When a field fails Zod validation. The message is prefixed
220
+ * with `Invalid atest.config.ts:` and lists every issue.
221
+ *
222
+ * @example
223
+ * ```ts
224
+ * // atest.config.ts
225
+ * import { defineAtestConfig } from '@aplaytest/core';
226
+ *
227
+ * export default defineAtestConfig({
228
+ * mode: 'strict',
229
+ * heal: { apply: 'propose' },
230
+ * });
231
+ * ```
232
+ */
233
+ function defineAtestConfig(input = {}) {
234
+ const { identity, ...rest } = input;
235
+ // Presets apply only when the user named an aggressiveness. Empty config
236
+ // must stay propose-only — the balanced preset's `apply: 'pr'` would
237
+ // otherwise override the safe default the rest of this function documents.
238
+ const named = rest.heal?.aggressiveness;
239
+ const preset = named === undefined ? undefined : exports.AGGRESSIVENESS_PRESETS[named];
240
+ const merged = preset === undefined
241
+ ? rest
242
+ : {
243
+ ...rest,
244
+ heal: { ...preset, ...rest.heal },
245
+ };
246
+ const parsed = exports.AtestConfigSchema.safeParse(merged);
247
+ if (!parsed.success) {
248
+ const issues = parsed.error.issues
249
+ .map(i => ` • ${i.path.join('.') || '(root)'}: ${i.message}`)
250
+ .join('\n');
251
+ throw new Error(`Invalid atest.config.ts:\n${issues}`);
252
+ }
253
+ return { ...parsed.data, identity: identity ?? {} };
254
+ }
255
+ /**
256
+ * Aggressiveness presets. Explicit fields in `heal` always win — the preset
257
+ * only fills what the user did not state.
258
+ */
259
+ exports.AGGRESSIVENESS_PRESETS = {
260
+ off: { strategies: [], apply: 'propose' },
261
+ conservative: {
262
+ strategies: ['selector'],
263
+ allowedStrategies: ['testid', 'role'],
264
+ minStabilityRank: 1,
265
+ validationRuns: 5,
266
+ apply: 'propose',
267
+ },
268
+ balanced: {
269
+ strategies: ['selector'],
270
+ allowedStrategies: ['testid', 'role', 'label', 'text'],
271
+ minStabilityRank: 4,
272
+ validationRuns: 3,
273
+ apply: 'pr',
274
+ },
275
+ aggressive: {
276
+ strategies: ['selector', 'flow'],
277
+ allowedStrategies: ['testid', 'role', 'label', 'text'],
278
+ minStabilityRank: 4,
279
+ validationRuns: 3,
280
+ apply: 'local',
281
+ },
282
+ };
283
+ //# sourceMappingURL=schema.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.js","sourceRoot":"","sources":["../../src/config/schema.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,24 @@
1
+ "use strict";
2
+ /**
3
+ * Evidence ids are DERIVED, never random.
4
+ *
5
+ * The same failure, re-analysed on another machine or in another shard, must
6
+ * produce the same id — otherwise the heal ledger, the history store, and CI
7
+ * artifacts cannot be joined after the fact.
8
+ */
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.evidenceId = evidenceId;
11
+ exports.isEvidenceId = isEvidenceId;
12
+ const node_crypto_1 = require("node:crypto");
13
+ const ID_PREFIX = 'ev_';
14
+ const ID_LENGTH = 12; // 48 bits — ample for per-run uniqueness, short enough to type
15
+ function evidenceId(parts) {
16
+ const digest = (0, node_crypto_1.createHash)('sha256')
17
+ .update([parts.runId, parts.testId, parts.project, String(parts.retry)].join('\x00'))
18
+ .digest('hex');
19
+ return `${ID_PREFIX}${digest.slice(0, ID_LENGTH)}`;
20
+ }
21
+ function isEvidenceId(value) {
22
+ return new RegExp(`^${ID_PREFIX}[0-9a-f]{${ID_LENGTH}}$`).test(value);
23
+ }
24
+ //# sourceMappingURL=id.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"id.js","sourceRoot":"","sources":["../../src/evidence/id.ts"],"names":[],"mappings":";AAAA;;;;;;GAMG;;AAgBH,gCAKC;AAED,oCAEC;AAvBD,6CAAyC;AAIzC,MAAM,SAAS,GAAG,KAAK,CAAC;AACxB,MAAM,SAAS,GAAG,EAAE,CAAC,CAAC,+DAA+D;AASrF,SAAgB,UAAU,CAAC,KAAsB;IAC/C,MAAM,MAAM,GAAG,IAAA,wBAAU,EAAC,QAAQ,CAAC;SAChC,MAAM,CAAC,CAAC,KAAK,CAAC,KAAK,EAAE,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,OAAO,EAAE,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;SACpF,MAAM,CAAC,KAAK,CAAC,CAAC;IACjB,OAAO,GAAG,SAAS,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,SAAS,CAAC,EAAgB,CAAC;AACnE,CAAC;AAED,SAAgB,YAAY,CAAC,KAAa;IACxC,OAAO,IAAI,MAAM,CAAC,IAAI,SAAS,YAAY,SAAS,IAAI,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;AACxE,CAAC"}
@@ -0,0 +1,103 @@
1
+ "use strict";
2
+ /**
3
+ * Redaction.
4
+ *
5
+ * Evidence bundles are written to disk, uploaded as CI artifacts, and sent to
6
+ * a model. A suite that authenticates — MSAL, OAuth, an API key header — WILL
7
+ * capture bearer tokens in its request ledger. Scrubbing happens once, at the
8
+ * boundary where a bundle is created, so that no downstream consumer has to
9
+ * remember to do it.
10
+ *
11
+ * The rule is fail-safe: an unrecognised structure is traversed, not skipped.
12
+ */
13
+ Object.defineProperty(exports, "__esModule", { value: true });
14
+ exports.REDACTED = void 0;
15
+ exports.redactString = redactString;
16
+ exports.redact = redact;
17
+ exports.redactUrl = redactUrl;
18
+ exports.REDACTED = '[redacted]';
19
+ /**
20
+ * Header and field names arrive in every casing and separator style —
21
+ * `api-key`, `API_KEY`, `apiKey`, `x-api-key-header`. Comparing them
22
+ * literally means a real credential slips through on a spelling difference,
23
+ * so both sides are normalised to letters and digits before matching.
24
+ */
25
+ function normaliseKey(key) {
26
+ return key.toLowerCase().replace(/[-_\s.]/g, '');
27
+ }
28
+ /** Matches `authorization: Bearer xyz`, `"token": "xyz"`, `API_KEY=xyz`. */
29
+ function keyValuePattern(key) {
30
+ // Allow any separator style between the key's own word parts.
31
+ const flexible = key
32
+ .replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
33
+ .replace(/[-_\s]+/g, '[-_\\s]?');
34
+ // The value group optionally swallows an auth scheme, so
35
+ // `Authorization: Bearer <token>` redacts the token rather than the word
36
+ // "Bearer".
37
+ return new RegExp(`(["']?${flexible}["']?\\s*[:=]\\s*["']?)((?:Bearer|Basic|Token)\\s+)?([^"'\\s,;&}]+)`, 'gi');
38
+ }
39
+ /** Matches a bearer token anywhere, even without a recognisable key. */
40
+ const BEARER_PATTERN = /\b(Bearer\s+)[A-Za-z0-9._~+/-]{12,}=*/gi;
41
+ /** Matches a JWT anywhere — three base64url segments. */
42
+ const JWT_PATTERN = /\beyJ[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}/g;
43
+ function redactString(input, keys) {
44
+ // ORDER IS LOAD-BEARING: value patterns run BEFORE key patterns.
45
+ //
46
+ // Running keys first leaks. On `Authorization: Bearer <token>` the key
47
+ // pattern matches `Authorization: ` and captures only `Bearer` — its value
48
+ // group stops at whitespace — so it rewrites the string to
49
+ // `Authorization: [redacted] <token>`. The bearer pattern then finds no
50
+ // `Bearer ` to anchor on, and the credential survives redaction entirely.
51
+ let out = input;
52
+ out = out.replace(BEARER_PATTERN, (_m, prefix) => `${prefix}${exports.REDACTED}`);
53
+ out = out.replace(JWT_PATTERN, exports.REDACTED);
54
+ for (const key of keys) {
55
+ out = out.replace(keyValuePattern(key), (_m, prefix, scheme) => `${prefix}${scheme ?? ''}${exports.REDACTED}`);
56
+ }
57
+ return out;
58
+ }
59
+ function keyMatches(key, keys) {
60
+ const normalised = normaliseKey(key);
61
+ return keys.some(k => normalised.includes(normaliseKey(k)));
62
+ }
63
+ /**
64
+ * Deep-redact any JSON-shaped value. Keys matching `keys` have their values
65
+ * replaced wholesale; every string is additionally scanned for embedded
66
+ * credentials, because secrets do not always arrive under an obvious key.
67
+ */
68
+ function redact(value, keys) {
69
+ return redactValue(value, keys, new WeakSet());
70
+ }
71
+ function redactValue(value, keys, seen) {
72
+ if (typeof value === 'string')
73
+ return redactString(value, keys);
74
+ if (value === null || typeof value !== 'object')
75
+ return value;
76
+ // Cycles are possible in captured objects; degrade rather than blow the stack.
77
+ if (seen.has(value))
78
+ return '[circular]';
79
+ seen.add(value);
80
+ if (Array.isArray(value))
81
+ return value.map(v => redactValue(v, keys, seen));
82
+ const out = {};
83
+ for (const [k, v] of Object.entries(value)) {
84
+ out[k] = keyMatches(k, keys) ? exports.REDACTED : redactValue(v, keys, seen);
85
+ }
86
+ return out;
87
+ }
88
+ /** Redact a URL's query string while keeping the path readable. */
89
+ function redactUrl(rawUrl, keys) {
90
+ try {
91
+ const url = new URL(rawUrl);
92
+ for (const key of [...url.searchParams.keys()]) {
93
+ if (keyMatches(key, keys))
94
+ url.searchParams.set(key, exports.REDACTED);
95
+ }
96
+ return url.toString();
97
+ }
98
+ catch {
99
+ // Not a parseable URL — fall back to string scrubbing rather than dropping it.
100
+ return redactString(rawUrl, keys);
101
+ }
102
+ }
103
+ //# sourceMappingURL=redact.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"redact.js","sourceRoot":"","sources":["../../src/evidence/redact.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;GAUG;;;AAmCH,oCAmBC;AAYD,wBAEC;AAoBD,8BAWC;AAjGY,QAAA,QAAQ,GAAG,YAAY,CAAC;AAErC;;;;;GAKG;AACH,SAAS,YAAY,CAAC,GAAW;IAC/B,OAAO,GAAG,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;AACnD,CAAC;AAED,4EAA4E;AAC5E,SAAS,eAAe,CAAC,GAAW;IAClC,8DAA8D;IAC9D,MAAM,QAAQ,GAAG,GAAG;SACjB,OAAO,CAAC,qBAAqB,EAAE,MAAM,CAAC;SACtC,OAAO,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC;IACnC,yDAAyD;IACzD,yEAAyE;IACzE,YAAY;IACZ,OAAO,IAAI,MAAM,CACf,SAAS,QAAQ,qEAAqE,EACtF,IAAI,CACL,CAAC;AACJ,CAAC;AAED,wEAAwE;AACxE,MAAM,cAAc,GAAG,yCAAyC,CAAC;AAEjE,yDAAyD;AACzD,MAAM,WAAW,GAAG,+DAA+D,CAAC;AAEpF,SAAgB,YAAY,CAAC,KAAa,EAAE,IAAuB;IACjE,iEAAiE;IACjE,EAAE;IACF,uEAAuE;IACvE,2EAA2E;IAC3E,2DAA2D;IAC3D,wEAAwE;IACxE,0EAA0E;IAC1E,IAAI,GAAG,GAAG,KAAK,CAAC;IAChB,GAAG,GAAG,GAAG,CAAC,OAAO,CAAC,cAAc,EAAE,CAAC,EAAE,EAAE,MAAc,EAAE,EAAE,CAAC,GAAG,MAAM,GAAG,gBAAQ,EAAE,CAAC,CAAC;IAClF,GAAG,GAAG,GAAG,CAAC,OAAO,CAAC,WAAW,EAAE,gBAAQ,CAAC,CAAC;IAEzC,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;QACvB,GAAG,GAAG,GAAG,CAAC,OAAO,CACf,eAAe,CAAC,GAAG,CAAC,EACpB,CAAC,EAAE,EAAE,MAAc,EAAE,MAA0B,EAAE,EAAE,CAAC,GAAG,MAAM,GAAG,MAAM,IAAI,EAAE,GAAG,gBAAQ,EAAE,CAC1F,CAAC;IACJ,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,SAAS,UAAU,CAAC,GAAW,EAAE,IAAuB;IACtD,MAAM,UAAU,GAAG,YAAY,CAAC,GAAG,CAAC,CAAC;IACrC,OAAO,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAC9D,CAAC;AAED;;;;GAIG;AACH,SAAgB,MAAM,CAAI,KAAQ,EAAE,IAAuB;IACzD,OAAO,WAAW,CAAC,KAAK,EAAE,IAAI,EAAE,IAAI,OAAO,EAAE,CAAM,CAAC;AACtD,CAAC;AAED,SAAS,WAAW,CAAC,KAAc,EAAE,IAAuB,EAAE,IAAqB;IACjF,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,YAAY,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;IAChE,IAAI,KAAK,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC;IAE9D,+EAA+E;IAC/E,IAAI,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC;QAAE,OAAO,YAAY,CAAC;IACzC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;IAEhB,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,WAAW,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC;IAE5E,MAAM,GAAG,GAA4B,EAAE,CAAC;IACxC,KAAK,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QAC3C,GAAG,CAAC,CAAC,CAAC,GAAG,UAAU,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,gBAAQ,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;IACvE,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,mEAAmE;AACnE,SAAgB,SAAS,CAAC,MAAc,EAAE,IAAuB;IAC/D,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,CAAC;QAC5B,KAAK,MAAM,GAAG,IAAI,CAAC,GAAG,GAAG,CAAC,YAAY,CAAC,IAAI,EAAE,CAAC,EAAE,CAAC;YAC/C,IAAI,UAAU,CAAC,GAAG,EAAE,IAAI,CAAC;gBAAE,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,GAAG,EAAE,gBAAQ,CAAC,CAAC;QACjE,CAAC;QACD,OAAO,GAAG,CAAC,QAAQ,EAAE,CAAC;IACxB,CAAC;IAAC,MAAM,CAAC;QACP,+EAA+E;QAC/E,OAAO,YAAY,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;IACpC,CAAC;AACH,CAAC"}
@@ -0,0 +1,156 @@
1
+ "use strict";
2
+ /**
3
+ * The evidence store: plain JSON on disk, one directory per run.
4
+ *
5
+ * <dir>/<runId>/<evidenceId>.json
6
+ *
7
+ * Deliberately not a binary format and not a database. A human with `jq` must
8
+ * be able to read a bundle, and a CI job must be able to upload the directory
9
+ * as an artifact with no export step. Every bundle carries `schemaVersion` so
10
+ * a reader can refuse politely rather than mis-parse.
11
+ */
12
+ Object.defineProperty(exports, "__esModule", { value: true });
13
+ exports.EvidenceStore = exports.SchemaVersionError = void 0;
14
+ exports.isEvidenceBundle = isEvidenceBundle;
15
+ exports.parseEvidenceBundle = parseEvidenceBundle;
16
+ exports.loadRunBundles = loadRunBundles;
17
+ const promises_1 = require("node:fs/promises");
18
+ const node_path_1 = require("node:path");
19
+ const redact_js_1 = require("./redact.js");
20
+ const types_js_1 = require("./types.js");
21
+ const JSON_EXTENSION = '.json';
22
+ function isRecord(value) {
23
+ return typeof value === 'object' && value !== null;
24
+ }
25
+ function isEvidenceBundle(value) {
26
+ if (!isRecord(value))
27
+ return false;
28
+ if (value['schemaVersion'] !== types_js_1.EVIDENCE_SCHEMA_VERSION)
29
+ return false;
30
+ if (typeof value['id'] !== 'string')
31
+ return false;
32
+ if (!isRecord(value['test']) || typeof value['test']['title'] !== 'string')
33
+ return false;
34
+ if (!isRecord(value['failure']) || typeof value['failure']['kind'] !== 'string')
35
+ return false;
36
+ return true;
37
+ }
38
+ function parseEvidenceBundle(value) {
39
+ return isEvidenceBundle(value) ? value : null;
40
+ }
41
+ function schemaVersionOf(value) {
42
+ return isRecord(value) ? value['schemaVersion'] : undefined;
43
+ }
44
+ async function latestRunDirectory(evidenceDir) {
45
+ const entries = await (0, promises_1.readdir)(evidenceDir, { withFileTypes: true }).catch(() => []);
46
+ const dirs = entries.filter(entry => entry.isDirectory()).map(entry => entry.name).sort();
47
+ return dirs[dirs.length - 1] ?? null;
48
+ }
49
+ async function loadRunBundles(evidenceDir, runId) {
50
+ const resolvedRunId = runId ?? (await latestRunDirectory(evidenceDir));
51
+ if (resolvedRunId === null)
52
+ return { runId: null, bundles: [], skipped: [] };
53
+ const dir = (0, node_path_1.join)(evidenceDir, resolvedRunId);
54
+ const files = (await (0, promises_1.readdir)(dir).catch(() => [])).filter(file => file.endsWith(JSON_EXTENSION));
55
+ const bundles = [];
56
+ const skipped = [];
57
+ for (const file of files) {
58
+ const raw = await (0, promises_1.readFile)((0, node_path_1.join)(dir, file), 'utf8').catch(() => null);
59
+ if (raw === null) {
60
+ skipped.push({ file, reason: 'unreadable' });
61
+ continue;
62
+ }
63
+ let parsed;
64
+ try {
65
+ parsed = JSON.parse(raw);
66
+ }
67
+ catch {
68
+ skipped.push({ file, reason: 'unreadable' });
69
+ continue;
70
+ }
71
+ const bundle = parseEvidenceBundle(parsed);
72
+ if (bundle === null) {
73
+ const version = schemaVersionOf(parsed);
74
+ skipped.push({
75
+ file,
76
+ reason: typeof version === 'number'
77
+ ? `schemaVersion ${version}, this build reads ${types_js_1.EVIDENCE_SCHEMA_VERSION}`
78
+ : 'malformed',
79
+ });
80
+ continue;
81
+ }
82
+ bundles.push(bundle);
83
+ }
84
+ return { runId: resolvedRunId, bundles, skipped };
85
+ }
86
+ class SchemaVersionError extends Error {
87
+ found;
88
+ expected;
89
+ path;
90
+ constructor(found, expected, path) {
91
+ super(`Evidence bundle at ${path} is schemaVersion ${found}, this build reads ${expected}. ` +
92
+ `Re-run the suite to regenerate, or use a matching atest version.`);
93
+ this.found = found;
94
+ this.expected = expected;
95
+ this.path = path;
96
+ this.name = 'SchemaVersionError';
97
+ }
98
+ }
99
+ exports.SchemaVersionError = SchemaVersionError;
100
+ class EvidenceStore {
101
+ options;
102
+ constructor(options) {
103
+ this.options = options;
104
+ }
105
+ bundlePath(runId, id) {
106
+ return (0, node_path_1.join)(this.options.dir, runId, `${id}.json`);
107
+ }
108
+ /**
109
+ * Redaction happens HERE, on the write path, so there is exactly one place
110
+ * that can forget to do it.
111
+ */
112
+ async write(bundle) {
113
+ const safe = (0, redact_js_1.redact)(bundle, this.options.redactKeys);
114
+ const path = this.bundlePath(bundle.runId, bundle.id);
115
+ await (0, promises_1.mkdir)((0, node_path_1.dirname)(path), { recursive: true });
116
+ await (0, promises_1.writeFile)(path, `${JSON.stringify(safe, null, 2)}\n`, 'utf8');
117
+ return path;
118
+ }
119
+ async read(runId, id) {
120
+ const path = this.bundlePath(runId, id);
121
+ const raw = await (0, promises_1.readFile)(path, 'utf8');
122
+ const parsed = JSON.parse(raw);
123
+ const bundle = parseEvidenceBundle(parsed);
124
+ if (bundle === null) {
125
+ throw new SchemaVersionError(Number(schemaVersionOf(parsed)), types_js_1.EVIDENCE_SCHEMA_VERSION, path);
126
+ }
127
+ return bundle;
128
+ }
129
+ /** Run ids, newest first by directory mtime. */
130
+ async listRuns() {
131
+ const entries = await (0, promises_1.readdir)(this.options.dir, { withFileTypes: true }).catch(() => []);
132
+ const dirs = entries.filter(e => e.isDirectory()).map(e => e.name);
133
+ const withTime = await Promise.all(dirs.map(async (name) => ({
134
+ name,
135
+ mtime: await (0, promises_1.stat)((0, node_path_1.join)(this.options.dir, name))
136
+ .then(s => s.mtimeMs)
137
+ .catch(() => 0),
138
+ })));
139
+ return withTime.sort((a, b) => b.mtime - a.mtime).map(e => e.name);
140
+ }
141
+ async listBundles(runId) {
142
+ const entries = await (0, promises_1.readdir)((0, node_path_1.join)(this.options.dir, runId)).catch(() => []);
143
+ return entries
144
+ .filter(file => file.endsWith(JSON_EXTENSION))
145
+ .map(file => file.slice(0, -JSON_EXTENSION.length));
146
+ }
147
+ /** Drop the oldest runs beyond the retention limit. Returns what it removed. */
148
+ async prune() {
149
+ const runs = await this.listRuns();
150
+ const doomed = runs.slice(this.options.retainRuns);
151
+ await Promise.all(doomed.map(r => (0, promises_1.rm)((0, node_path_1.join)(this.options.dir, r), { recursive: true, force: true })));
152
+ return doomed;
153
+ }
154
+ }
155
+ exports.EvidenceStore = EvidenceStore;
156
+ //# sourceMappingURL=store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store.js","sourceRoot":"","sources":["../../src/evidence/store.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ "use strict";
2
+ /**
3
+ * The Evidence Bundle — the central data structure of the system.
4
+ *
5
+ * Everything downstream (healing, flaky classification, MCP resources, report
6
+ * insights) consumes this and only this. Engines never re-open a browser to
7
+ * "go look"; if a field is missing, the fix is to capture it at source, not to
8
+ * re-run the test.
9
+ *
10
+ * DESIGN NOTE — nullable, not optional. Fields that may be absent are typed
11
+ * `T | null` rather than `field?: T`. Bundles are written to disk as JSON and
12
+ * read back by other processes (and by humans with `jq`); `undefined`
13
+ * disappears on serialisation, `null` round-trips. Every bundle therefore has
14
+ * the same key set regardless of what was captured.
15
+ */
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ exports.EVIDENCE_SCHEMA_VERSION = void 0;
18
+ exports.formatFailingStep = formatFailingStep;
19
+ exports.EVIDENCE_SCHEMA_VERSION = 1;
20
+ function formatFailingStep(step) {
21
+ if (step === null)
22
+ return null;
23
+ return `${step.pageObject}.${step.method}(${step.args.join(', ')})`;
24
+ }
25
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/evidence/types.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;GAaG;;;AA8BH,8CAGC;AAzBY,QAAA,uBAAuB,GAAG,CAAU,CAAC;AAsBlD,SAAgB,iBAAiB,CAAC,IAAuB;IACvD,IAAI,IAAI,KAAK,IAAI;QAAE,OAAO,IAAI,CAAC;IAC/B,OAAO,GAAG,IAAI,CAAC,UAAU,IAAI,IAAI,CAAC,MAAM,IAAI,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC;AACtE,CAAC"}