@odla-ai/chapter 0.47.8 → 0.47.10
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 +28 -0
- package/dist/index.cjs +24 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +24 -0
- package/dist/index.js.map +1 -1
- package/dist/testing.cjs +164 -46
- package/dist/testing.cjs.map +1 -1
- package/dist/testing.d.cts +10 -2
- package/dist/testing.d.ts +10 -2
- package/dist/testing.js +164 -46
- package/dist/testing.js.map +1 -1
- package/dist/worker/index.cjs +160 -33
- package/dist/worker/index.cjs.map +1 -1
- package/dist/worker/index.js +160 -33
- package/dist/worker/index.js.map +1 -1
- package/package.json +1 -1
package/dist/testing.cjs
CHANGED
|
@@ -29,6 +29,98 @@ __export(testing_exports, {
|
|
|
29
29
|
module.exports = __toCommonJS(testing_exports);
|
|
30
30
|
|
|
31
31
|
// src/testing-probe.ts
|
|
32
|
+
var MAX_BODY_BYTES = 64 * 1024;
|
|
33
|
+
var TIMEOUT_MS = 1e4;
|
|
34
|
+
function record(value) {
|
|
35
|
+
return value !== null && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
36
|
+
}
|
|
37
|
+
async function readBoundedJson(response, signal) {
|
|
38
|
+
const reader = response.body?.getReader();
|
|
39
|
+
if (!reader) throw new Error("invalid_json");
|
|
40
|
+
const chunks = [];
|
|
41
|
+
let size = 0;
|
|
42
|
+
const abort = () => {
|
|
43
|
+
void reader.cancel().catch(() => {
|
|
44
|
+
});
|
|
45
|
+
};
|
|
46
|
+
signal.addEventListener("abort", abort, { once: true });
|
|
47
|
+
try {
|
|
48
|
+
if (signal.aborted) throw new Error("response_failed");
|
|
49
|
+
for (; ; ) {
|
|
50
|
+
const { done, value } = await reader.read();
|
|
51
|
+
if (done) break;
|
|
52
|
+
size += value.byteLength;
|
|
53
|
+
if (size > MAX_BODY_BYTES) throw new Error("response_too_large");
|
|
54
|
+
chunks.push(value);
|
|
55
|
+
}
|
|
56
|
+
const bytes = new Uint8Array(size);
|
|
57
|
+
let offset = 0;
|
|
58
|
+
for (const chunk of chunks) {
|
|
59
|
+
bytes.set(chunk, offset);
|
|
60
|
+
offset += chunk.byteLength;
|
|
61
|
+
}
|
|
62
|
+
try {
|
|
63
|
+
return JSON.parse(new TextDecoder().decode(bytes));
|
|
64
|
+
} catch {
|
|
65
|
+
throw new Error("invalid_json");
|
|
66
|
+
}
|
|
67
|
+
} finally {
|
|
68
|
+
signal.removeEventListener("abort", abort);
|
|
69
|
+
await reader.cancel().catch(() => {
|
|
70
|
+
});
|
|
71
|
+
reader.releaseLock();
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
async function probe(url, fetcher, json) {
|
|
75
|
+
const controller = new AbortController();
|
|
76
|
+
let timer;
|
|
77
|
+
const timeout = new Promise((resolve) => {
|
|
78
|
+
timer = setTimeout(() => {
|
|
79
|
+
controller.abort();
|
|
80
|
+
resolve({ status: 0, error: "request_timeout" });
|
|
81
|
+
}, TIMEOUT_MS);
|
|
82
|
+
});
|
|
83
|
+
try {
|
|
84
|
+
return await Promise.race([timeout, (async () => {
|
|
85
|
+
let response;
|
|
86
|
+
try {
|
|
87
|
+
response = await fetcher(url, {
|
|
88
|
+
headers: { "cache-control": "no-cache" },
|
|
89
|
+
redirect: "manual",
|
|
90
|
+
signal: controller.signal
|
|
91
|
+
});
|
|
92
|
+
} catch {
|
|
93
|
+
return { status: 0, error: "request_failed" };
|
|
94
|
+
}
|
|
95
|
+
if (!json || !response.ok) {
|
|
96
|
+
await response.body?.cancel().catch(() => {
|
|
97
|
+
});
|
|
98
|
+
return { status: response.status };
|
|
99
|
+
}
|
|
100
|
+
try {
|
|
101
|
+
return {
|
|
102
|
+
status: response.status,
|
|
103
|
+
json: await readBoundedJson(response, controller.signal),
|
|
104
|
+
identity: Object.fromEntries(["app-id", "chapter-id", "data-environment", "runtime"].map((name) => [name, slug(response.headers.get(`x-odla-${name}`) ?? void 0)]))
|
|
105
|
+
};
|
|
106
|
+
} catch (error) {
|
|
107
|
+
const code = error instanceof Error && ["invalid_json", "response_too_large"].includes(error.message) ? error.message : "response_failed";
|
|
108
|
+
return { status: response.status, error: code };
|
|
109
|
+
}
|
|
110
|
+
})()]);
|
|
111
|
+
} finally {
|
|
112
|
+
clearTimeout(timer);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
function slug(value) {
|
|
116
|
+
return value === void 0 ? void 0 : typeof value === "string" && /^[a-z0-9][a-z0-9-]{1,62}$/.test(value) ? value : "invalid_value";
|
|
117
|
+
}
|
|
118
|
+
function amount(value) {
|
|
119
|
+
return value === void 0 ? void 0 : Number.isSafeInteger(value) && Number(value) >= 0 ? value : "invalid_value";
|
|
120
|
+
}
|
|
121
|
+
function boolean(value) {
|
|
122
|
+
return value === void 0 ? void 0 : typeof value === "boolean" ? value : "invalid_value";
|
|
123
|
+
}
|
|
32
124
|
function activeMemberships(site) {
|
|
33
125
|
return site.skus.filter((sku) => sku.active && sku.kind === "membership");
|
|
34
126
|
}
|
|
@@ -37,31 +129,35 @@ async function probeChapterJoinContracts(manifest, fetcher = fetch) {
|
|
|
37
129
|
if (!validation.ok) throw new TypeError(`invalid Chapter acceptance manifest: ${validation.errors.join("; ")}`);
|
|
38
130
|
const findings = [];
|
|
39
131
|
for (const site of manifest.targets) {
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
fetcher(`${site.origin}/api/health`, { headers: { "cache-control": "no-cache" } }),
|
|
45
|
-
fetcher(`${site.origin}/api/join-config`, { headers: { "cache-control": "no-cache" } })
|
|
46
|
-
]);
|
|
47
|
-
} catch (error) {
|
|
48
|
-
findings.push({ targetId: site.targetId, siteId: site.siteId, path: "fetch", expected: "reachable", actual: error instanceof Error ? error.message : String(error) });
|
|
49
|
-
continue;
|
|
50
|
-
}
|
|
132
|
+
const [health, configResponse] = await Promise.all([
|
|
133
|
+
probe(`${site.origin}/api/health`, fetcher, true),
|
|
134
|
+
probe(`${site.origin}/api/join-config`, fetcher, true)
|
|
135
|
+
]);
|
|
51
136
|
const targetNote = (path, expected, actual) => {
|
|
52
137
|
if (JSON.stringify(expected) !== JSON.stringify(actual)) findings.push({ targetId: site.targetId, siteId: site.siteId, path, expected, actual });
|
|
53
138
|
};
|
|
54
|
-
targetNote("/api/health status", 200, health.status);
|
|
55
|
-
targetNote("/api/
|
|
56
|
-
if (!
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
139
|
+
targetNote("/api/health status", 200, health.error ?? health.status);
|
|
140
|
+
if (health.status === 200 && !health.error) targetNote("/api/health ok", true, boolean(record(health.json)?.ok));
|
|
141
|
+
if (health.status === 200 && !health.error) {
|
|
142
|
+
for (const [name, expected] of Object.entries({
|
|
143
|
+
"app-id": site.target.appId,
|
|
144
|
+
"chapter-id": site.target.chapterId,
|
|
145
|
+
"data-environment": site.target.dataEnvironment,
|
|
146
|
+
"runtime": site.target.runtime
|
|
147
|
+
})) targetNote(`/api/health x-odla-${name}`, expected, health.identity?.[name]);
|
|
148
|
+
}
|
|
149
|
+
targetNote("/api/join-config status", 200, configResponse.status || configResponse.error);
|
|
150
|
+
if (configResponse.status !== 200) continue;
|
|
151
|
+
if (configResponse.error) {
|
|
152
|
+
targetNote("/api/join-config JSON", "object", configResponse.error === "invalid_json" ? "invalid JSON" : configResponse.error);
|
|
62
153
|
continue;
|
|
63
154
|
}
|
|
64
|
-
|
|
155
|
+
const config = record(configResponse.json);
|
|
156
|
+
if (!config) {
|
|
157
|
+
targetNote("/api/join-config JSON", "object", "invalid shape");
|
|
158
|
+
continue;
|
|
159
|
+
}
|
|
160
|
+
targetNote("id", site.target.chapterId, slug(config.id));
|
|
65
161
|
const actualTiers = Array.isArray(config.tiers) ? config.tiers : [];
|
|
66
162
|
const expectedTiers = activeMemberships(site).map((sku) => ({
|
|
67
163
|
id: sku.id,
|
|
@@ -71,17 +167,18 @@ async function probeChapterJoinContracts(manifest, fetcher = fetch) {
|
|
|
71
167
|
free: sku.priceCents === 0
|
|
72
168
|
}));
|
|
73
169
|
const projected = actualTiers.map((tier) => {
|
|
74
|
-
const value = tier;
|
|
170
|
+
const value = record(tier);
|
|
171
|
+
if (!value) return "invalid_tier";
|
|
75
172
|
return {
|
|
76
|
-
id: value.id,
|
|
77
|
-
priceCents: value.priceCents,
|
|
78
|
-
currency: value.currency,
|
|
79
|
-
interval: value.interval,
|
|
80
|
-
free: value.free
|
|
173
|
+
id: slug(value.id),
|
|
174
|
+
priceCents: amount(value.priceCents),
|
|
175
|
+
currency: value.currency === void 0 ? void 0 : typeof value.currency === "string" && /^[a-z]{3}$/.test(value.currency) ? value.currency : "invalid_value",
|
|
176
|
+
interval: value.interval === void 0 ? void 0 : ["month", "year", "once"].includes(String(value.interval)) ? value.interval : "invalid_value",
|
|
177
|
+
free: boolean(value.free)
|
|
81
178
|
};
|
|
82
179
|
});
|
|
83
180
|
targetNote("tiers", expectedTiers, projected);
|
|
84
|
-
targetNote("paymentsReady", expectedTiers.some((tier) => !tier.free), config.paymentsReady);
|
|
181
|
+
targetNote("paymentsReady", expectedTiers.some((tier) => !tier.free), boolean(config.paymentsReady));
|
|
85
182
|
}
|
|
86
183
|
return { ok: findings.length === 0, findings };
|
|
87
184
|
}
|
|
@@ -89,16 +186,21 @@ async function probeChapterJoinContracts(manifest, fetcher = fetch) {
|
|
|
89
186
|
// src/testing-runner.ts
|
|
90
187
|
var SAFE_LABEL = /^[a-z0-9][a-z0-9._:-]{0,159}$/;
|
|
91
188
|
var SAFE_REFERENCE = /^[A-Za-z0-9_:.=-]{3,240}$/;
|
|
189
|
+
var CREDENTIAL = /(?:^|[_.:-])(?:sk|rk|pk)_(?:test|live)_|whsec_|_secret_|eyJ[A-Za-z0-9_-]+\./i;
|
|
190
|
+
var ERROR_CODES = /* @__PURE__ */ new Set(["invalid_evidence", "assertion_failed", "preflight_failed", "unsupported_scenario", "execution_failed"]);
|
|
191
|
+
function safeValue(value, pattern) {
|
|
192
|
+
return pattern.test(value) && !CREDENTIAL.test(value);
|
|
193
|
+
}
|
|
92
194
|
function safeErrorCode(error) {
|
|
93
195
|
if (error && typeof error === "object") {
|
|
94
196
|
const code = error.code;
|
|
95
|
-
if (typeof code === "string" &&
|
|
197
|
+
if (typeof code === "string" && ERROR_CODES.has(code)) return code;
|
|
96
198
|
}
|
|
97
199
|
return "execution_failed";
|
|
98
200
|
}
|
|
99
201
|
function validateLabels(values, path) {
|
|
100
202
|
if (!Array.isArray(values) || values.length === 0) throw Object.assign(new TypeError(`${path} must be non-empty`), { code: "invalid_evidence" });
|
|
101
|
-
if (values.length > 64 || values.some((value) => typeof value !== "string" || !
|
|
203
|
+
if (values.length > 64 || values.some((value) => typeof value !== "string" || !safeValue(value, SAFE_LABEL))) {
|
|
102
204
|
throw Object.assign(new TypeError(`${path} contains an unsafe or unbounded label`), { code: "invalid_evidence" });
|
|
103
205
|
}
|
|
104
206
|
}
|
|
@@ -107,7 +209,7 @@ function validateReferences(values) {
|
|
|
107
209
|
if (!Array.isArray(values) || values.length > 64) throw Object.assign(new TypeError("references must be bounded"), { code: "invalid_evidence" });
|
|
108
210
|
const kinds = /* @__PURE__ */ new Set(["stripe_event", "stripe_object", "clerk_user", "calendar_event", "email_message", "event_digest"]);
|
|
109
211
|
for (const reference of values) {
|
|
110
|
-
if (!reference || !kinds.has(reference.kind) || typeof reference.value !== "string" || !
|
|
212
|
+
if (!reference || !kinds.has(reference.kind) || typeof reference.value !== "string" || !safeValue(reference.value, SAFE_REFERENCE) || reference.value.includes("@")) {
|
|
111
213
|
throw Object.assign(new TypeError("references may contain only opaque provider ids or digests"), { code: "invalid_evidence" });
|
|
112
214
|
}
|
|
113
215
|
}
|
|
@@ -116,15 +218,23 @@ function validateEvidence(evidence, provider) {
|
|
|
116
218
|
if (!evidence || typeof evidence !== "object") throw Object.assign(new TypeError("executor returned no evidence"), { code: "invalid_evidence" });
|
|
117
219
|
validateLabels(evidence.checks, "evidence.checks");
|
|
118
220
|
validateReferences(evidence.references);
|
|
119
|
-
if (provider && (typeof evidence.deploymentVersion !== "string" || !
|
|
221
|
+
if ((provider || evidence.deploymentVersion !== void 0) && (typeof evidence.deploymentVersion !== "string" || !safeValue(evidence.deploymentVersion, SAFE_REFERENCE))) {
|
|
120
222
|
throw Object.assign(new TypeError("provider evidence requires a safe deploymentVersion"), { code: "invalid_evidence" });
|
|
121
223
|
}
|
|
224
|
+
return {
|
|
225
|
+
checks: [...evidence.checks],
|
|
226
|
+
...evidence.deploymentVersion !== void 0 ? { deploymentVersion: evidence.deploymentVersion } : {},
|
|
227
|
+
...evidence.references !== void 0 ? {
|
|
228
|
+
references: evidence.references.map(({ kind, value }) => ({ kind, value }))
|
|
229
|
+
} : {}
|
|
230
|
+
};
|
|
122
231
|
}
|
|
123
232
|
function validateCleanup(cleanup) {
|
|
124
233
|
if (!cleanup || cleanup.status !== "complete" && cleanup.status !== "incomplete") {
|
|
125
234
|
throw Object.assign(new TypeError("executor returned invalid cleanup evidence"), { code: "cleanup_failed" });
|
|
126
235
|
}
|
|
127
236
|
validateLabels(cleanup.checks, "cleanup.checks");
|
|
237
|
+
return { status: cleanup.status, checks: [...cleanup.checks] };
|
|
128
238
|
}
|
|
129
239
|
function contextFor(manifest, id) {
|
|
130
240
|
const scenario2 = planChapterAcceptanceMatrix(manifest).find((item) => item.id === id);
|
|
@@ -140,28 +250,36 @@ function contextFor(manifest, id) {
|
|
|
140
250
|
};
|
|
141
251
|
}
|
|
142
252
|
async function runChapterAcceptanceSuite(manifest, suite, executor) {
|
|
253
|
+
if (suite !== "hermetic" && suite !== "provider_dev") throw new TypeError("unknown acceptance suite");
|
|
143
254
|
const scenarios = planChapterAcceptanceMatrix(manifest).filter((item) => item.suite === suite);
|
|
144
255
|
if (suite === "provider_dev" && typeof executor.cleanup !== "function") {
|
|
145
256
|
throw new TypeError("provider_dev acceptance requires a cleanup executor before any case can run");
|
|
146
257
|
}
|
|
147
258
|
const cases = [];
|
|
259
|
+
let cleanupFailed = false;
|
|
148
260
|
for (const scenario2 of scenarios) {
|
|
261
|
+
if (cleanupFailed) {
|
|
262
|
+
cases.push({ id: scenario2.id, suite, outcome: "failed", errorCode: "not_run_cleanup_failed" });
|
|
263
|
+
continue;
|
|
264
|
+
}
|
|
149
265
|
const context = contextFor(manifest, scenario2.id);
|
|
150
266
|
let evidence;
|
|
151
267
|
let errorCode;
|
|
152
268
|
let cleanup;
|
|
153
269
|
try {
|
|
154
|
-
evidence = await executor.execute(context);
|
|
155
|
-
validateEvidence(evidence, suite === "provider_dev");
|
|
270
|
+
evidence = validateEvidence(await executor.execute(context), suite === "provider_dev");
|
|
156
271
|
} catch (error) {
|
|
157
272
|
errorCode = safeErrorCode(error);
|
|
158
273
|
} finally {
|
|
159
274
|
if (suite === "provider_dev") {
|
|
160
275
|
try {
|
|
161
|
-
cleanup = await executor.cleanup(context);
|
|
162
|
-
|
|
163
|
-
|
|
276
|
+
cleanup = validateCleanup(await executor.cleanup(context));
|
|
277
|
+
if (cleanup.status !== "complete") {
|
|
278
|
+
cleanupFailed = true;
|
|
279
|
+
errorCode = "cleanup_incomplete";
|
|
280
|
+
}
|
|
164
281
|
} catch {
|
|
282
|
+
cleanupFailed = true;
|
|
165
283
|
errorCode = "cleanup_failed";
|
|
166
284
|
}
|
|
167
285
|
}
|
|
@@ -196,12 +314,12 @@ function activeMemberships2(site) {
|
|
|
196
314
|
function representativePaidMembership(site) {
|
|
197
315
|
return activeMemberships2(site).find((sku) => sku.priceCents > 0);
|
|
198
316
|
}
|
|
199
|
-
function
|
|
317
|
+
function record2(value) {
|
|
200
318
|
return value !== null && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
201
319
|
}
|
|
202
320
|
function validateChapterAcceptanceManifest(value) {
|
|
203
321
|
const errors = [];
|
|
204
|
-
const input =
|
|
322
|
+
const input = record2(value);
|
|
205
323
|
if (!input) return { ok: false, errors: ["manifest must be an object"] };
|
|
206
324
|
if (input.schemaVersion !== 1) errors.push("schemaVersion must be 1");
|
|
207
325
|
const inputTargets = Array.isArray(input.targets) ? input.targets : [];
|
|
@@ -212,7 +330,7 @@ function validateChapterAcceptanceManifest(value) {
|
|
|
212
330
|
const priceOwners = /* @__PURE__ */ new Map();
|
|
213
331
|
for (const [siteIndex, rawSite] of inputTargets.entries()) {
|
|
214
332
|
const path = `targets[${siteIndex}]`;
|
|
215
|
-
const site =
|
|
333
|
+
const site = record2(rawSite);
|
|
216
334
|
if (!site) {
|
|
217
335
|
errors.push(`${path} must be an object`);
|
|
218
336
|
continue;
|
|
@@ -227,13 +345,13 @@ function validateChapterAcceptanceManifest(value) {
|
|
|
227
345
|
try {
|
|
228
346
|
if (typeof site.origin !== "string") throw new TypeError("origin");
|
|
229
347
|
const origin = new URL(site.origin);
|
|
230
|
-
if (origin.protocol !== "https:" || origin.pathname !== "/" || origin.search || origin.hash) {
|
|
348
|
+
if (origin.protocol !== "https:" || origin.pathname !== "/" || origin.search || origin.hash || origin.username || origin.password) {
|
|
231
349
|
errors.push(`${path}.origin must be an HTTPS origin without a path, query, or fragment`);
|
|
232
350
|
}
|
|
233
351
|
} catch {
|
|
234
352
|
errors.push(`${path}.origin must be a valid HTTPS origin`);
|
|
235
353
|
}
|
|
236
|
-
const target =
|
|
354
|
+
const target = record2(site.target);
|
|
237
355
|
const targetValues = {};
|
|
238
356
|
for (const key of ["appId", "environment", "dataEnvironment", "runtime", "chapterId"]) {
|
|
239
357
|
targetValues[key] = typeof target?.[key] === "string" ? String(target[key]) : "";
|
|
@@ -254,7 +372,7 @@ function validateChapterAcceptanceManifest(value) {
|
|
|
254
372
|
const skus = [];
|
|
255
373
|
for (const [skuIndex, rawSku] of inputSkus.entries()) {
|
|
256
374
|
const skuPath = `${path}.skus[${skuIndex}]`;
|
|
257
|
-
const sku =
|
|
375
|
+
const sku = record2(rawSku);
|
|
258
376
|
if (!sku) {
|
|
259
377
|
errors.push(`${skuPath} must be an object`);
|
|
260
378
|
continue;
|
|
@@ -297,12 +415,12 @@ function validateChapterAcceptanceManifest(value) {
|
|
|
297
415
|
skus.push(sku);
|
|
298
416
|
}
|
|
299
417
|
if (!skus.some((sku) => sku.active && sku.kind === "membership")) errors.push(`${path}.skus must contain an active membership SKU`);
|
|
300
|
-
const options =
|
|
418
|
+
const options = record2(site.options);
|
|
301
419
|
if (!options) errors.push(`${path}.options must be an object`);
|
|
302
420
|
if (options?.ordinaryInterview !== true) errors.push(`${path}.options.ordinaryInterview must be true`);
|
|
303
421
|
if (typeof options?.invitationInterviewWaiver !== "boolean") errors.push(`${path}.options.invitationInterviewWaiver must be boolean`);
|
|
304
422
|
if (options?.autoRenew !== true) errors.push(`${path}.options.autoRenew must be true`);
|
|
305
|
-
const seat =
|
|
423
|
+
const seat = record2(options?.additionalSeat);
|
|
306
424
|
if (options?.additionalSeat !== void 0 && !seat) errors.push(`${path}.options.additionalSeat must be an object`);
|
|
307
425
|
if (seat) {
|
|
308
426
|
const seatSku = skus.find((sku) => sku.id === seat.skuId);
|
|
@@ -331,8 +449,8 @@ function scenario(input) {
|
|
|
331
449
|
function planChapterAcceptanceMatrix(manifest) {
|
|
332
450
|
const validation = validateChapterAcceptanceManifest(manifest);
|
|
333
451
|
if (!validation.ok) throw new TypeError(`invalid Chapter acceptance manifest: ${validation.errors.join("; ")}`);
|
|
334
|
-
if (manifest.targets.some((site) => site.stripeMode !== "test")) {
|
|
335
|
-
throw new TypeError("provider acceptance runs are development-only and require Stripe test mode on every target");
|
|
452
|
+
if (manifest.targets.some((site) => site.stripeMode !== "test" || site.target.environment !== "dev" || site.target.dataEnvironment !== "dev")) {
|
|
453
|
+
throw new TypeError("provider acceptance runs are development-only and require Stripe test mode and dev runtime/data environments on every target");
|
|
336
454
|
}
|
|
337
455
|
const scenarios = [];
|
|
338
456
|
for (const site of manifest.targets) {
|
package/dist/testing.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/testing.ts","../src/testing-probe.ts","../src/testing-runner.ts"],"sourcesContent":["import type {\n ChapterAcceptanceManifest, ChapterAcceptanceScenario, ChapterAcceptanceSku, ChapterAcceptanceTarget, ChapterAcceptanceValidation,\n} from \"./testing-contract\";\nexport type * from \"./testing-contract\";\nexport { probeChapterJoinContracts } from \"./testing-probe\";\nexport { formatChapterAcceptanceRun, runChapterAcceptanceSuite } from \"./testing-runner\";\n\nconst SLUG = /^[a-z0-9][a-z0-9-]{1,62}$/;\nconst CURRENCY = /^[a-z]{3}$/;\nconst STRIPE_PRICE = /^price_[A-Za-z0-9]{8,}$/;\n\nfunction activeMemberships(site: ChapterAcceptanceTarget): ChapterAcceptanceSku[] {\n return site.skus.filter((sku) => sku.active && sku.kind === \"membership\");\n}\n\nfunction representativePaidMembership(site: ChapterAcceptanceTarget): ChapterAcceptanceSku | undefined {\n return activeMemberships(site).find((sku) => sku.priceCents > 0);\n}\n\nfunction record(value: unknown): Record<string, unknown> | null {\n return value !== null && typeof value === \"object\" && !Array.isArray(value)\n ? value as Record<string, unknown>\n : null;\n}\n\n/** Validate the exact facts a multi-site acceptance run needs before it writes anywhere. */\nexport function validateChapterAcceptanceManifest(value: unknown): ChapterAcceptanceValidation {\n const errors: string[] = [];\n const input = record(value);\n if (!input) return { ok: false, errors: [\"manifest must be an object\"] };\n if (input.schemaVersion !== 1) errors.push(\"schemaVersion must be 1\");\n const inputTargets = Array.isArray(input.targets) ? input.targets : [];\n if (inputTargets.length < 2) errors.push(\"targets must contain at least two deployed Chapter targets\");\n\n const targetIds = new Set<string>();\n const siteIds = new Set<string>();\n const targets = new Set<string>();\n const priceOwners = new Map<string, string>();\n for (const [siteIndex, rawSite] of inputTargets.entries()) {\n const path = `targets[${siteIndex}]`;\n const site = record(rawSite);\n if (!site) {\n errors.push(`${path} must be an object`);\n continue;\n }\n const targetId = typeof site.targetId === \"string\" ? site.targetId : \"\";\n const siteId = typeof site.siteId === \"string\" ? site.siteId : \"\";\n if (!SLUG.test(targetId)) errors.push(`${path}.targetId must be a stable lowercase slug`);\n if (targetIds.has(targetId)) errors.push(`${path}.targetId duplicates ${JSON.stringify(targetId)}`);\n if (targetId) targetIds.add(targetId);\n if (!SLUG.test(siteId)) errors.push(`${path}.siteId must be a stable lowercase slug`);\n if (siteId) siteIds.add(siteId);\n try {\n if (typeof site.origin !== \"string\") throw new TypeError(\"origin\");\n const origin = new URL(site.origin);\n if (origin.protocol !== \"https:\" || origin.pathname !== \"/\" || origin.search || origin.hash) {\n errors.push(`${path}.origin must be an HTTPS origin without a path, query, or fragment`);\n }\n } catch {\n errors.push(`${path}.origin must be a valid HTTPS origin`);\n }\n const target = record(site.target);\n const targetValues = {} as Record<\"appId\" | \"environment\" | \"dataEnvironment\" | \"runtime\" | \"chapterId\", string>;\n for (const key of [\"appId\", \"environment\", \"dataEnvironment\", \"runtime\", \"chapterId\"] as const) {\n targetValues[key] = typeof target?.[key] === \"string\" ? String(target[key]) : \"\";\n if (!targetValues[key]) errors.push(`${path}.target.${key} is required`);\n }\n const partition = Object.values(targetValues).join(\"\\u0000\");\n if (targets.has(partition)) errors.push(`${path}.target duplicates another site partition`);\n if (Object.values(targetValues).every(Boolean)) targets.add(partition);\n if (site.stripeMode !== \"test\" && site.stripeMode !== \"live\") {\n errors.push(`${path}.stripeMode must be test or live`);\n }\n if (site.stripeMode === \"live\" && targetValues.environment !== \"prod\" && targetValues.environment !== \"production\") {\n errors.push(`${path}.stripeMode cannot be live outside a production environment`);\n }\n\n const skuIds = new Set<string>();\n const inputSkus = Array.isArray(site.skus) ? site.skus : [];\n if (!Array.isArray(site.skus) || inputSkus.length === 0) errors.push(`${path}.skus must be a non-empty array`);\n const skus: ChapterAcceptanceSku[] = [];\n for (const [skuIndex, rawSku] of inputSkus.entries()) {\n const skuPath = `${path}.skus[${skuIndex}]`;\n const sku = record(rawSku);\n if (!sku) {\n errors.push(`${skuPath} must be an object`);\n continue;\n }\n const id = typeof sku.id === \"string\" ? sku.id : \"\";\n if (!SLUG.test(id)) errors.push(`${skuPath}.id must be a stable lowercase slug`);\n if (skuIds.has(id)) errors.push(`${skuPath}.id duplicates ${JSON.stringify(id)}`);\n if (id) skuIds.add(id);\n if (sku.kind !== \"membership\" && sku.kind !== \"add_on\") errors.push(`${skuPath}.kind must be membership or add_on`);\n if (typeof sku.active !== \"boolean\") errors.push(`${skuPath}.active must be boolean`);\n const priceCents = typeof sku.priceCents === \"number\" ? sku.priceCents : Number.NaN;\n if (!Number.isSafeInteger(priceCents) || priceCents < 0) errors.push(`${skuPath}.priceCents must be a non-negative integer`);\n if (typeof sku.currency !== \"string\" || !CURRENCY.test(sku.currency)) errors.push(`${skuPath}.currency must be a lowercase ISO currency code`);\n if (sku.billing !== \"month\" && sku.billing !== \"year\" && sku.billing !== \"once\") {\n errors.push(`${skuPath}.billing must be month, year, or once`);\n }\n if (sku.kind === \"membership\" && sku.billing === \"once\") {\n errors.push(`${skuPath}.billing must be month or year for a membership SKU`);\n }\n if (sku.active === true && priceCents > 0 && (typeof sku.stripePriceId !== \"string\" || !STRIPE_PRICE.test(sku.stripePriceId))) {\n errors.push(`${skuPath}.stripePriceId is required for an active paid SKU`);\n }\n if (priceCents === 0 && sku.stripePriceId) errors.push(`${skuPath}.stripePriceId must be absent for a free SKU`);\n if (typeof sku.stripePriceId === \"string\" && sku.stripePriceId) {\n const owner = `${siteId}/${id}`;\n const previous = priceOwners.get(sku.stripePriceId);\n if (previous && previous !== owner) errors.push(`${skuPath}.stripePriceId is already owned by ${previous}`);\n else priceOwners.set(sku.stripePriceId, owner);\n }\n if (sku.compareAtPriceCents !== undefined) {\n if (!Number.isSafeInteger(sku.compareAtPriceCents) || Number(sku.compareAtPriceCents) <= priceCents) {\n errors.push(`${skuPath}.compareAtPriceCents must be an integer greater than priceCents`);\n }\n if (sku.discountDuration !== \"first_term\" && sku.discountDuration !== \"while_active\") {\n errors.push(`${skuPath}.discountDuration is required with compareAtPriceCents`);\n }\n } else if (sku.discountDuration) {\n errors.push(`${skuPath}.compareAtPriceCents is required with discountDuration`);\n }\n skus.push(sku as unknown as ChapterAcceptanceSku);\n }\n if (!skus.some((sku) => sku.active && sku.kind === \"membership\")) errors.push(`${path}.skus must contain an active membership SKU`);\n const options = record(site.options);\n if (!options) errors.push(`${path}.options must be an object`);\n if (options?.ordinaryInterview !== true) errors.push(`${path}.options.ordinaryInterview must be true`);\n if (typeof options?.invitationInterviewWaiver !== \"boolean\") errors.push(`${path}.options.invitationInterviewWaiver must be boolean`);\n if (options?.autoRenew !== true) errors.push(`${path}.options.autoRenew must be true`);\n const seat = record(options?.additionalSeat);\n if (options?.additionalSeat !== undefined && !seat) errors.push(`${path}.options.additionalSeat must be an object`);\n if (seat) {\n const seatSku = skus.find((sku) => sku.id === seat.skuId);\n if (!seatSku || !seatSku.active || seatSku.kind !== \"add_on\") {\n errors.push(`${path}.options.additionalSeat.skuId must name an active add_on SKU`);\n } else if (seatSku.billing === \"once\") {\n errors.push(`${path}.options.additionalSeat.skuId must name a recurring add_on SKU`);\n }\n if (typeof seat.purchaseWindowCalendarMonths !== \"number\" ||\n !Number.isInteger(seat.purchaseWindowCalendarMonths) || seat.purchaseWindowCalendarMonths < 1) {\n errors.push(`${path}.options.additionalSeat.purchaseWindowCalendarMonths must be a positive integer`);\n }\n if (seat.named !== true || seat.uniqueEmail !== true || seat.renewal !== \"membership_aligned\" || seat.chargeTiming !== \"start\") {\n errors.push(`${path}.options.additionalSeat must require a named unique-email seat, membership-aligned renewal, and payment at start`);\n }\n }\n }\n if (siteIds.size < 2) errors.push(\"targets must cover at least two distinct logical sites\");\n return errors.length ? { ok: false, errors } : { ok: true, value: value as ChapterAcceptanceManifest };\n}\n\nfunction scenario(input: Omit<ChapterAcceptanceScenario, \"id\">): ChapterAcceptanceScenario {\n return {\n ...input,\n id: [input.suite, input.targetId, input.skuId, input.siblingTargetId, input.kind].filter(Boolean).join(\":\"),\n };\n}\n\n/**\n * Build exhaustive pure cases plus a bounded provider-backed development matrix.\n * Throws before returning provider work when the manifest is invalid or names live Stripe.\n */\nexport function planChapterAcceptanceMatrix(manifest: ChapterAcceptanceManifest): ChapterAcceptanceScenario[] {\n const validation = validateChapterAcceptanceManifest(manifest);\n if (!validation.ok) throw new TypeError(`invalid Chapter acceptance manifest: ${validation.errors.join(\"; \")}`);\n if (manifest.targets.some((site) => site.stripeMode !== \"test\")) {\n throw new TypeError(\"provider acceptance runs are development-only and require Stripe test mode on every target\");\n }\n const scenarios: ChapterAcceptanceScenario[] = [];\n for (const site of manifest.targets) {\n for (const sku of site.skus.filter((candidate) => candidate.active)) {\n scenarios.push(scenario({ suite: \"hermetic\", kind: \"catalog_contract\", targetId: site.targetId, siteId: site.siteId, skuId: sku.id }));\n if (sku.priceCents > 0) {\n scenarios.push(scenario({ suite: \"hermetic\", kind: \"amount_tamper\", targetId: site.targetId, siteId: site.siteId, skuId: sku.id }));\n scenarios.push(scenario({ suite: \"hermetic\", kind: \"wrong_environment_event\", targetId: site.targetId, siteId: site.siteId, skuId: sku.id }));\n scenarios.push(scenario({ suite: \"hermetic\", kind: \"post_stripe_compensation\", targetId: site.targetId, siteId: site.siteId, skuId: sku.id }));\n for (const sibling of manifest.targets.filter((candidate) => candidate.targetId !== site.targetId)) {\n scenarios.push(scenario({\n suite: \"hermetic\", kind: \"sibling_event_isolation\", targetId: site.targetId, siteId: site.siteId,\n skuId: sku.id, siblingTargetId: sibling.targetId,\n }));\n }\n }\n }\n for (const sku of activeMemberships(site)) {\n scenarios.push(scenario({ suite: \"hermetic\", kind: \"submit_replay\", targetId: site.targetId, siteId: site.siteId, skuId: sku.id }));\n scenarios.push(scenario({\n suite: \"provider_dev\", kind: sku.priceCents > 0 ? \"paid_signup\" : \"free_signup\",\n targetId: site.targetId, siteId: site.siteId, skuId: sku.id,\n }));\n }\n const paid = representativePaidMembership(site);\n if (paid) {\n scenarios.push(scenario({ suite: \"provider_dev\", kind: \"ordinary_interview\", targetId: site.targetId, siteId: site.siteId, skuId: paid.id }));\n scenarios.push(scenario({ suite: \"provider_dev\", kind: \"renewal\", targetId: site.targetId, siteId: site.siteId, skuId: paid.id }));\n scenarios.push(scenario({ suite: \"provider_dev\", kind: \"refund_cancel\", targetId: site.targetId, siteId: site.siteId, skuId: paid.id }));\n if (site.options.invitationInterviewWaiver) {\n scenarios.push(scenario({ suite: \"provider_dev\", kind: \"invitation_interview_waiver\", targetId: site.targetId, siteId: site.siteId, skuId: paid.id }));\n scenarios.push(scenario({ suite: \"hermetic\", kind: \"expired_interview_waiver\", targetId: site.targetId, siteId: site.siteId, skuId: paid.id }));\n scenarios.push(scenario({ suite: \"hermetic\", kind: \"replayed_interview_waiver\", targetId: site.targetId, siteId: site.siteId, skuId: paid.id }));\n }\n if (site.options.additionalSeat) {\n scenarios.push(scenario({\n suite: \"provider_dev\", kind: \"named_seat\", targetId: site.targetId, siteId: site.siteId,\n skuId: site.options.additionalSeat.skuId,\n }));\n scenarios.push(scenario({\n suite: \"hermetic\", kind: \"duplicate_seat_email\", targetId: site.targetId, siteId: site.siteId,\n skuId: site.options.additionalSeat.skuId,\n }));\n scenarios.push(scenario({\n suite: \"hermetic\", kind: \"expired_seat_window\", targetId: site.targetId, siteId: site.siteId,\n skuId: site.options.additionalSeat.skuId,\n }));\n }\n scenarios.push(scenario({ suite: \"hermetic\", kind: \"canceled_membership\", targetId: site.targetId, siteId: site.siteId, skuId: paid.id }));\n }\n }\n return scenarios;\n}\n","import type {\n ChapterAcceptanceManifest,\n ChapterAcceptanceTarget,\n ChapterJoinConfigProbe,\n ChapterJoinContractFinding,\n ChapterJoinContractProbeResult,\n} from \"./testing-contract\";\nimport { validateChapterAcceptanceManifest } from \"./testing\";\n\nfunction activeMemberships(site: ChapterAcceptanceTarget) {\n return site.skus.filter((sku) => sku.active && sku.kind === \"membership\");\n}\n\n/** Probe public deployed join contracts without creating applications or provider objects. */\nexport async function probeChapterJoinContracts(\n manifest: ChapterAcceptanceManifest,\n fetcher: typeof fetch = fetch,\n): Promise<ChapterJoinContractProbeResult> {\n const validation = validateChapterAcceptanceManifest(manifest);\n if (!validation.ok) throw new TypeError(`invalid Chapter acceptance manifest: ${validation.errors.join(\"; \")}`);\n const findings: ChapterJoinContractFinding[] = [];\n for (const site of manifest.targets) {\n let health: Response;\n let configResponse: Response;\n try {\n [health, configResponse] = await Promise.all([\n fetcher(`${site.origin}/api/health`, { headers: { \"cache-control\": \"no-cache\" } }),\n fetcher(`${site.origin}/api/join-config`, { headers: { \"cache-control\": \"no-cache\" } }),\n ]);\n } catch (error) {\n findings.push({ targetId: site.targetId, siteId: site.siteId, path: \"fetch\", expected: \"reachable\", actual: error instanceof Error ? error.message : String(error) });\n continue;\n }\n const targetNote = (path: string, expected: unknown, actual: unknown): void => {\n if (JSON.stringify(expected) !== JSON.stringify(actual)) findings.push({ targetId: site.targetId, siteId: site.siteId, path, expected, actual });\n };\n targetNote(\"/api/health status\", 200, health.status);\n targetNote(\"/api/join-config status\", 200, configResponse.status);\n if (!configResponse.ok) continue;\n let config: ChapterJoinConfigProbe;\n try {\n config = await configResponse.json() as ChapterJoinConfigProbe;\n } catch {\n targetNote(\"/api/join-config JSON\", \"object\", \"invalid JSON\");\n continue;\n }\n targetNote(\"id\", site.target.chapterId, config.id);\n const actualTiers = Array.isArray(config.tiers) ? config.tiers : [];\n const expectedTiers = activeMemberships(site).map((sku) => ({\n id: sku.id, priceCents: sku.priceCents, currency: sku.currency,\n interval: sku.billing, free: sku.priceCents === 0,\n }));\n const projected = actualTiers.map((tier) => {\n const value = tier as Record<string, unknown>;\n return {\n id: value.id, priceCents: value.priceCents, currency: value.currency,\n interval: value.interval, free: value.free,\n };\n });\n targetNote(\"tiers\", expectedTiers, projected);\n targetNote(\"paymentsReady\", expectedTiers.some((tier) => !tier.free), config.paymentsReady);\n }\n return { ok: findings.length === 0, findings };\n}\n","import type {\n ChapterAcceptanceCaseResult,\n ChapterAcceptanceCleanupEvidence,\n ChapterAcceptanceEvidence,\n ChapterAcceptanceExecutionContext,\n ChapterAcceptanceExecutor,\n ChapterAcceptanceManifest,\n ChapterAcceptanceProviderReference,\n ChapterAcceptanceRunResult,\n} from \"./testing-contract\";\nimport { planChapterAcceptanceMatrix } from \"./testing\";\n\nconst SAFE_LABEL = /^[a-z0-9][a-z0-9._:-]{0,159}$/;\nconst SAFE_REFERENCE = /^[A-Za-z0-9_:.=-]{3,240}$/;\n\nfunction safeErrorCode(error: unknown): string {\n if (error && typeof error === \"object\") {\n const code = (error as { code?: unknown }).code;\n if (typeof code === \"string\" && SAFE_LABEL.test(code)) return code;\n }\n return \"execution_failed\";\n}\n\nfunction validateLabels(values: readonly string[], path: string): void {\n if (!Array.isArray(values) || values.length === 0) throw Object.assign(new TypeError(`${path} must be non-empty`), { code: \"invalid_evidence\" });\n if (values.length > 64 || values.some((value) => typeof value !== \"string\" || !SAFE_LABEL.test(value))) {\n throw Object.assign(new TypeError(`${path} contains an unsafe or unbounded label`), { code: \"invalid_evidence\" });\n }\n}\n\nfunction validateReferences(values: readonly ChapterAcceptanceProviderReference[] | undefined): void {\n if (values === undefined) return;\n if (!Array.isArray(values) || values.length > 64) throw Object.assign(new TypeError(\"references must be bounded\"), { code: \"invalid_evidence\" });\n const kinds = new Set([\"stripe_event\", \"stripe_object\", \"clerk_user\", \"calendar_event\", \"email_message\", \"event_digest\"]);\n for (const reference of values) {\n if (!reference || !kinds.has(reference.kind) || typeof reference.value !== \"string\" ||\n !SAFE_REFERENCE.test(reference.value) || reference.value.includes(\"@\")) {\n throw Object.assign(new TypeError(\"references may contain only opaque provider ids or digests\"), { code: \"invalid_evidence\" });\n }\n }\n}\n\nfunction validateEvidence(evidence: ChapterAcceptanceEvidence, provider: boolean): void {\n if (!evidence || typeof evidence !== \"object\") throw Object.assign(new TypeError(\"executor returned no evidence\"), { code: \"invalid_evidence\" });\n validateLabels(evidence.checks, \"evidence.checks\");\n validateReferences(evidence.references);\n if (provider && (typeof evidence.deploymentVersion !== \"string\" || !SAFE_REFERENCE.test(evidence.deploymentVersion))) {\n throw Object.assign(new TypeError(\"provider evidence requires a safe deploymentVersion\"), { code: \"invalid_evidence\" });\n }\n}\n\nfunction validateCleanup(cleanup: ChapterAcceptanceCleanupEvidence): void {\n if (!cleanup || (cleanup.status !== \"complete\" && cleanup.status !== \"incomplete\")) {\n throw Object.assign(new TypeError(\"executor returned invalid cleanup evidence\"), { code: \"cleanup_failed\" });\n }\n validateLabels(cleanup.checks, \"cleanup.checks\");\n}\n\nfunction contextFor(manifest: ChapterAcceptanceManifest, id: string): ChapterAcceptanceExecutionContext {\n const scenario = planChapterAcceptanceMatrix(manifest).find((item) => item.id === id);\n if (!scenario) throw new TypeError(`unknown acceptance scenario ${id}`);\n const target = manifest.targets.find((item) => item.targetId === scenario.targetId)!;\n return {\n scenario,\n target,\n ...(scenario.skuId ? { sku: target.skus.find((item) => item.id === scenario.skuId) } : {}),\n ...(scenario.siblingTargetId ? {\n siblingTarget: manifest.targets.find((item) => item.targetId === scenario.siblingTargetId),\n } : {}),\n };\n}\n\n/**\n * Execute one suite serially. Provider runs fail before their first mutation\n * unless the app supplies cleanup, then invoke cleanup after every attempted\n * case even when execution throws. Raw provider error text is never retained.\n */\nexport async function runChapterAcceptanceSuite(\n manifest: ChapterAcceptanceManifest,\n suite: \"hermetic\" | \"provider_dev\",\n executor: ChapterAcceptanceExecutor,\n): Promise<ChapterAcceptanceRunResult> {\n const scenarios = planChapterAcceptanceMatrix(manifest).filter((item) => item.suite === suite);\n if (suite === \"provider_dev\" && typeof executor.cleanup !== \"function\") {\n throw new TypeError(\"provider_dev acceptance requires a cleanup executor before any case can run\");\n }\n const cases: ChapterAcceptanceCaseResult[] = [];\n for (const scenario of scenarios) {\n const context = contextFor(manifest, scenario.id);\n let evidence: ChapterAcceptanceEvidence | undefined;\n let errorCode: string | undefined;\n let cleanup: ChapterAcceptanceCleanupEvidence | undefined;\n try {\n evidence = await executor.execute(context);\n validateEvidence(evidence, suite === \"provider_dev\");\n } catch (error) {\n errorCode = safeErrorCode(error);\n } finally {\n if (suite === \"provider_dev\") {\n try {\n cleanup = await executor.cleanup!(context);\n validateCleanup(cleanup);\n if (cleanup.status !== \"complete\") errorCode = \"cleanup_incomplete\";\n } catch {\n errorCode = \"cleanup_failed\";\n }\n }\n }\n cases.push({\n id: scenario.id,\n suite,\n outcome: errorCode ? \"failed\" : \"passed\",\n ...(evidence ? { evidence } : {}),\n ...(cleanup ? { cleanup } : {}),\n ...(errorCode ? { errorCode } : {}),\n });\n }\n return { ok: cases.every((item) => item.outcome === \"passed\"), suite, cases };\n}\n\n/** PII-free line report suitable for CI logs and PM evidence. */\nexport function formatChapterAcceptanceRun(result: ChapterAcceptanceRunResult): string {\n const passed = result.cases.filter((item) => item.outcome === \"passed\").length;\n const lines = [`${result.ok ? \"ok\" : \"failed\"}: ${result.suite} ${passed}/${result.cases.length}`];\n for (const item of result.cases) {\n lines.push(`${item.outcome === \"passed\" ? \"PASS\" : \"FAIL\"} ${item.id}${item.errorCode ? ` ${item.errorCode}` : \"\"}`);\n }\n return lines.join(\"\\n\");\n}\n"],"mappings":"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","names":["scenario","activeMemberships"]}
|
|
1
|
+
{"version":3,"sources":["../src/testing.ts","../src/testing-probe.ts","../src/testing-runner.ts"],"sourcesContent":["import type {\n ChapterAcceptanceManifest, ChapterAcceptanceScenario, ChapterAcceptanceSku, ChapterAcceptanceTarget, ChapterAcceptanceValidation,\n} from \"./testing-contract\";\nexport type * from \"./testing-contract\";\nexport { probeChapterJoinContracts } from \"./testing-probe\";\nexport { formatChapterAcceptanceRun, runChapterAcceptanceSuite } from \"./testing-runner\";\n\nconst SLUG = /^[a-z0-9][a-z0-9-]{1,62}$/;\nconst CURRENCY = /^[a-z]{3}$/;\nconst STRIPE_PRICE = /^price_[A-Za-z0-9]{8,}$/;\n\nfunction activeMemberships(site: ChapterAcceptanceTarget): ChapterAcceptanceSku[] {\n return site.skus.filter((sku) => sku.active && sku.kind === \"membership\");\n}\n\nfunction representativePaidMembership(site: ChapterAcceptanceTarget): ChapterAcceptanceSku | undefined {\n return activeMemberships(site).find((sku) => sku.priceCents > 0);\n}\n\nfunction record(value: unknown): Record<string, unknown> | null {\n return value !== null && typeof value === \"object\" && !Array.isArray(value)\n ? value as Record<string, unknown>\n : null;\n}\n\n/** Validate the exact facts a multi-site acceptance run needs before it writes anywhere. */\nexport function validateChapterAcceptanceManifest(value: unknown): ChapterAcceptanceValidation {\n const errors: string[] = [];\n const input = record(value);\n if (!input) return { ok: false, errors: [\"manifest must be an object\"] };\n if (input.schemaVersion !== 1) errors.push(\"schemaVersion must be 1\");\n const inputTargets = Array.isArray(input.targets) ? input.targets : [];\n if (inputTargets.length < 2) errors.push(\"targets must contain at least two deployed Chapter targets\");\n\n const targetIds = new Set<string>();\n const siteIds = new Set<string>();\n const targets = new Set<string>();\n const priceOwners = new Map<string, string>();\n for (const [siteIndex, rawSite] of inputTargets.entries()) {\n const path = `targets[${siteIndex}]`;\n const site = record(rawSite);\n if (!site) {\n errors.push(`${path} must be an object`);\n continue;\n }\n const targetId = typeof site.targetId === \"string\" ? site.targetId : \"\";\n const siteId = typeof site.siteId === \"string\" ? site.siteId : \"\";\n if (!SLUG.test(targetId)) errors.push(`${path}.targetId must be a stable lowercase slug`);\n if (targetIds.has(targetId)) errors.push(`${path}.targetId duplicates ${JSON.stringify(targetId)}`);\n if (targetId) targetIds.add(targetId);\n if (!SLUG.test(siteId)) errors.push(`${path}.siteId must be a stable lowercase slug`);\n if (siteId) siteIds.add(siteId);\n try {\n if (typeof site.origin !== \"string\") throw new TypeError(\"origin\");\n const origin = new URL(site.origin);\n if (origin.protocol !== \"https:\" || origin.pathname !== \"/\" || origin.search || origin.hash || origin.username || origin.password) {\n errors.push(`${path}.origin must be an HTTPS origin without a path, query, or fragment`);\n }\n } catch {\n errors.push(`${path}.origin must be a valid HTTPS origin`);\n }\n const target = record(site.target);\n const targetValues = {} as Record<\"appId\" | \"environment\" | \"dataEnvironment\" | \"runtime\" | \"chapterId\", string>;\n for (const key of [\"appId\", \"environment\", \"dataEnvironment\", \"runtime\", \"chapterId\"] as const) {\n targetValues[key] = typeof target?.[key] === \"string\" ? String(target[key]) : \"\";\n if (!targetValues[key]) errors.push(`${path}.target.${key} is required`);\n }\n const partition = Object.values(targetValues).join(\"\\u0000\");\n if (targets.has(partition)) errors.push(`${path}.target duplicates another site partition`);\n if (Object.values(targetValues).every(Boolean)) targets.add(partition);\n if (site.stripeMode !== \"test\" && site.stripeMode !== \"live\") {\n errors.push(`${path}.stripeMode must be test or live`);\n }\n if (site.stripeMode === \"live\" && targetValues.environment !== \"prod\" && targetValues.environment !== \"production\") {\n errors.push(`${path}.stripeMode cannot be live outside a production environment`);\n }\n\n const skuIds = new Set<string>();\n const inputSkus = Array.isArray(site.skus) ? site.skus : [];\n if (!Array.isArray(site.skus) || inputSkus.length === 0) errors.push(`${path}.skus must be a non-empty array`);\n const skus: ChapterAcceptanceSku[] = [];\n for (const [skuIndex, rawSku] of inputSkus.entries()) {\n const skuPath = `${path}.skus[${skuIndex}]`;\n const sku = record(rawSku);\n if (!sku) {\n errors.push(`${skuPath} must be an object`);\n continue;\n }\n const id = typeof sku.id === \"string\" ? sku.id : \"\";\n if (!SLUG.test(id)) errors.push(`${skuPath}.id must be a stable lowercase slug`);\n if (skuIds.has(id)) errors.push(`${skuPath}.id duplicates ${JSON.stringify(id)}`);\n if (id) skuIds.add(id);\n if (sku.kind !== \"membership\" && sku.kind !== \"add_on\") errors.push(`${skuPath}.kind must be membership or add_on`);\n if (typeof sku.active !== \"boolean\") errors.push(`${skuPath}.active must be boolean`);\n const priceCents = typeof sku.priceCents === \"number\" ? sku.priceCents : Number.NaN;\n if (!Number.isSafeInteger(priceCents) || priceCents < 0) errors.push(`${skuPath}.priceCents must be a non-negative integer`);\n if (typeof sku.currency !== \"string\" || !CURRENCY.test(sku.currency)) errors.push(`${skuPath}.currency must be a lowercase ISO currency code`);\n if (sku.billing !== \"month\" && sku.billing !== \"year\" && sku.billing !== \"once\") {\n errors.push(`${skuPath}.billing must be month, year, or once`);\n }\n if (sku.kind === \"membership\" && sku.billing === \"once\") {\n errors.push(`${skuPath}.billing must be month or year for a membership SKU`);\n }\n if (sku.active === true && priceCents > 0 && (typeof sku.stripePriceId !== \"string\" || !STRIPE_PRICE.test(sku.stripePriceId))) {\n errors.push(`${skuPath}.stripePriceId is required for an active paid SKU`);\n }\n if (priceCents === 0 && sku.stripePriceId) errors.push(`${skuPath}.stripePriceId must be absent for a free SKU`);\n if (typeof sku.stripePriceId === \"string\" && sku.stripePriceId) {\n const owner = `${siteId}/${id}`;\n const previous = priceOwners.get(sku.stripePriceId);\n if (previous && previous !== owner) errors.push(`${skuPath}.stripePriceId is already owned by ${previous}`);\n else priceOwners.set(sku.stripePriceId, owner);\n }\n if (sku.compareAtPriceCents !== undefined) {\n if (!Number.isSafeInteger(sku.compareAtPriceCents) || Number(sku.compareAtPriceCents) <= priceCents) {\n errors.push(`${skuPath}.compareAtPriceCents must be an integer greater than priceCents`);\n }\n if (sku.discountDuration !== \"first_term\" && sku.discountDuration !== \"while_active\") {\n errors.push(`${skuPath}.discountDuration is required with compareAtPriceCents`);\n }\n } else if (sku.discountDuration) {\n errors.push(`${skuPath}.compareAtPriceCents is required with discountDuration`);\n }\n skus.push(sku as unknown as ChapterAcceptanceSku);\n }\n if (!skus.some((sku) => sku.active && sku.kind === \"membership\")) errors.push(`${path}.skus must contain an active membership SKU`);\n const options = record(site.options);\n if (!options) errors.push(`${path}.options must be an object`);\n if (options?.ordinaryInterview !== true) errors.push(`${path}.options.ordinaryInterview must be true`);\n if (typeof options?.invitationInterviewWaiver !== \"boolean\") errors.push(`${path}.options.invitationInterviewWaiver must be boolean`);\n if (options?.autoRenew !== true) errors.push(`${path}.options.autoRenew must be true`);\n const seat = record(options?.additionalSeat);\n if (options?.additionalSeat !== undefined && !seat) errors.push(`${path}.options.additionalSeat must be an object`);\n if (seat) {\n const seatSku = skus.find((sku) => sku.id === seat.skuId);\n if (!seatSku || !seatSku.active || seatSku.kind !== \"add_on\") {\n errors.push(`${path}.options.additionalSeat.skuId must name an active add_on SKU`);\n } else if (seatSku.billing === \"once\") {\n errors.push(`${path}.options.additionalSeat.skuId must name a recurring add_on SKU`);\n }\n if (typeof seat.purchaseWindowCalendarMonths !== \"number\" ||\n !Number.isInteger(seat.purchaseWindowCalendarMonths) || seat.purchaseWindowCalendarMonths < 1) {\n errors.push(`${path}.options.additionalSeat.purchaseWindowCalendarMonths must be a positive integer`);\n }\n if (seat.named !== true || seat.uniqueEmail !== true || seat.renewal !== \"membership_aligned\" || seat.chargeTiming !== \"start\") {\n errors.push(`${path}.options.additionalSeat must require a named unique-email seat, membership-aligned renewal, and payment at start`);\n }\n }\n }\n if (siteIds.size < 2) errors.push(\"targets must cover at least two distinct logical sites\");\n return errors.length ? { ok: false, errors } : { ok: true, value: value as ChapterAcceptanceManifest };\n}\n\nfunction scenario(input: Omit<ChapterAcceptanceScenario, \"id\">): ChapterAcceptanceScenario {\n return {\n ...input,\n id: [input.suite, input.targetId, input.skuId, input.siblingTargetId, input.kind].filter(Boolean).join(\":\"),\n };\n}\n\n/**\n * Build exhaustive pure cases plus a bounded provider-backed development matrix.\n * Throws before returning provider work when the manifest is invalid or names live Stripe.\n */\nexport function planChapterAcceptanceMatrix(manifest: ChapterAcceptanceManifest): ChapterAcceptanceScenario[] {\n const validation = validateChapterAcceptanceManifest(manifest);\n if (!validation.ok) throw new TypeError(`invalid Chapter acceptance manifest: ${validation.errors.join(\"; \")}`);\n if (manifest.targets.some((site) => site.stripeMode !== \"test\" || site.target.environment !== \"dev\" || site.target.dataEnvironment !== \"dev\")) {\n throw new TypeError(\"provider acceptance runs are development-only and require Stripe test mode and dev runtime/data environments on every target\");\n }\n const scenarios: ChapterAcceptanceScenario[] = [];\n for (const site of manifest.targets) {\n for (const sku of site.skus.filter((candidate) => candidate.active)) {\n scenarios.push(scenario({ suite: \"hermetic\", kind: \"catalog_contract\", targetId: site.targetId, siteId: site.siteId, skuId: sku.id }));\n if (sku.priceCents > 0) {\n scenarios.push(scenario({ suite: \"hermetic\", kind: \"amount_tamper\", targetId: site.targetId, siteId: site.siteId, skuId: sku.id }));\n scenarios.push(scenario({ suite: \"hermetic\", kind: \"wrong_environment_event\", targetId: site.targetId, siteId: site.siteId, skuId: sku.id }));\n scenarios.push(scenario({ suite: \"hermetic\", kind: \"post_stripe_compensation\", targetId: site.targetId, siteId: site.siteId, skuId: sku.id }));\n for (const sibling of manifest.targets.filter((candidate) => candidate.targetId !== site.targetId)) {\n scenarios.push(scenario({\n suite: \"hermetic\", kind: \"sibling_event_isolation\", targetId: site.targetId, siteId: site.siteId,\n skuId: sku.id, siblingTargetId: sibling.targetId,\n }));\n }\n }\n }\n for (const sku of activeMemberships(site)) {\n scenarios.push(scenario({ suite: \"hermetic\", kind: \"submit_replay\", targetId: site.targetId, siteId: site.siteId, skuId: sku.id }));\n scenarios.push(scenario({\n suite: \"provider_dev\", kind: sku.priceCents > 0 ? \"paid_signup\" : \"free_signup\",\n targetId: site.targetId, siteId: site.siteId, skuId: sku.id,\n }));\n }\n const paid = representativePaidMembership(site);\n if (paid) {\n scenarios.push(scenario({ suite: \"provider_dev\", kind: \"ordinary_interview\", targetId: site.targetId, siteId: site.siteId, skuId: paid.id }));\n scenarios.push(scenario({ suite: \"provider_dev\", kind: \"renewal\", targetId: site.targetId, siteId: site.siteId, skuId: paid.id }));\n scenarios.push(scenario({ suite: \"provider_dev\", kind: \"refund_cancel\", targetId: site.targetId, siteId: site.siteId, skuId: paid.id }));\n if (site.options.invitationInterviewWaiver) {\n scenarios.push(scenario({ suite: \"provider_dev\", kind: \"invitation_interview_waiver\", targetId: site.targetId, siteId: site.siteId, skuId: paid.id }));\n scenarios.push(scenario({ suite: \"hermetic\", kind: \"expired_interview_waiver\", targetId: site.targetId, siteId: site.siteId, skuId: paid.id }));\n scenarios.push(scenario({ suite: \"hermetic\", kind: \"replayed_interview_waiver\", targetId: site.targetId, siteId: site.siteId, skuId: paid.id }));\n }\n if (site.options.additionalSeat) {\n scenarios.push(scenario({\n suite: \"provider_dev\", kind: \"named_seat\", targetId: site.targetId, siteId: site.siteId,\n skuId: site.options.additionalSeat.skuId,\n }));\n scenarios.push(scenario({\n suite: \"hermetic\", kind: \"duplicate_seat_email\", targetId: site.targetId, siteId: site.siteId,\n skuId: site.options.additionalSeat.skuId,\n }));\n scenarios.push(scenario({\n suite: \"hermetic\", kind: \"expired_seat_window\", targetId: site.targetId, siteId: site.siteId,\n skuId: site.options.additionalSeat.skuId,\n }));\n }\n scenarios.push(scenario({ suite: \"hermetic\", kind: \"canceled_membership\", targetId: site.targetId, siteId: site.siteId, skuId: paid.id }));\n }\n }\n return scenarios;\n}\n","import type {\n ChapterAcceptanceManifest,\n ChapterAcceptanceTarget,\n ChapterJoinContractFinding,\n ChapterJoinContractProbeResult,\n} from \"./testing-contract\";\nimport { validateChapterAcceptanceManifest } from \"./testing\";\n\nconst MAX_BODY_BYTES = 64 * 1024;\nconst TIMEOUT_MS = 10_000;\n\nfunction record(value: unknown): Record<string, unknown> | null {\n return value !== null && typeof value === \"object\" && !Array.isArray(value)\n ? value as Record<string, unknown> : null;\n}\n\nasync function readBoundedJson(response: Response, signal: AbortSignal): Promise<unknown> {\n const reader = response.body?.getReader();\n if (!reader) throw new Error(\"invalid_json\");\n const chunks: Uint8Array[] = [];\n let size = 0;\n const abort = () => { void reader.cancel().catch(() => {}); };\n signal.addEventListener(\"abort\", abort, { once: true });\n try {\n if (signal.aborted) throw new Error(\"response_failed\");\n for (;;) {\n const { done, value } = await reader.read();\n if (done) break;\n size += value.byteLength;\n if (size > MAX_BODY_BYTES) throw new Error(\"response_too_large\");\n chunks.push(value);\n }\n const bytes = new Uint8Array(size);\n let offset = 0;\n for (const chunk of chunks) { bytes.set(chunk, offset); offset += chunk.byteLength; }\n try { return JSON.parse(new TextDecoder().decode(bytes)); }\n catch { throw new Error(\"invalid_json\"); }\n } finally {\n signal.removeEventListener(\"abort\", abort);\n await reader.cancel().catch(() => {});\n reader.releaseLock();\n }\n}\n\ntype ProbeResponse = { status: number; json?: unknown; error?: string; identity?: Record<string, unknown> };\n\nasync function probe(url: string, fetcher: typeof fetch, json: boolean): Promise<ProbeResponse> {\n const controller = new AbortController();\n let timer: ReturnType<typeof setTimeout> | undefined;\n const timeout = new Promise<ProbeResponse>((resolve) => {\n timer = setTimeout(() => {\n controller.abort();\n resolve({ status: 0, error: \"request_timeout\" });\n }, TIMEOUT_MS);\n });\n try {\n return await Promise.race([timeout, (async (): Promise<ProbeResponse> => {\n let response: Response;\n try {\n response = await fetcher(url, {\n headers: { \"cache-control\": \"no-cache\" }, redirect: \"manual\", signal: controller.signal,\n });\n } catch { return { status: 0, error: \"request_failed\" }; }\n if (!json || !response.ok) {\n await response.body?.cancel().catch(() => {});\n return { status: response.status };\n }\n try {\n return {\n status: response.status, json: await readBoundedJson(response, controller.signal),\n identity: Object.fromEntries([\"app-id\", \"chapter-id\", \"data-environment\", \"runtime\"].map((name) =>\n [name, slug(response.headers.get(`x-odla-${name}`) ?? undefined)])),\n };\n }\n catch (error) {\n const code = error instanceof Error && [\"invalid_json\", \"response_too_large\"].includes(error.message)\n ? error.message : \"response_failed\";\n return { status: response.status, error: code };\n }\n })()]);\n } finally { clearTimeout(timer); }\n}\n\n// Findings are durable CI/PM data, not a mirror of an untrusted HTTP body.\nfunction slug(value: unknown): unknown {\n return value === undefined ? undefined : typeof value === \"string\" && /^[a-z0-9][a-z0-9-]{1,62}$/.test(value) ? value : \"invalid_value\";\n}\nfunction amount(value: unknown): unknown {\n return value === undefined ? undefined : Number.isSafeInteger(value) && Number(value) >= 0 ? value : \"invalid_value\";\n}\nfunction boolean(value: unknown): unknown {\n return value === undefined ? undefined : typeof value === \"boolean\" ? value : \"invalid_value\";\n}\n\nfunction activeMemberships(site: ChapterAcceptanceTarget) {\n return site.skus.filter((sku) => sku.active && sku.kind === \"membership\");\n}\n\n/**\n * Probe public deployed join contracts without creating applications or provider objects.\n * Each request, including its body, is limited to ten seconds and 64 KiB. Redirects\n * are refused. Malformed responses become per-target findings, never raw bodies\n * or exception text; one failed target does not suppress the remaining targets.\n */\nexport async function probeChapterJoinContracts(\n manifest: ChapterAcceptanceManifest,\n fetcher: typeof fetch = fetch,\n): Promise<ChapterJoinContractProbeResult> {\n const validation = validateChapterAcceptanceManifest(manifest);\n if (!validation.ok) throw new TypeError(`invalid Chapter acceptance manifest: ${validation.errors.join(\"; \")}`);\n const findings: ChapterJoinContractFinding[] = [];\n for (const site of manifest.targets) {\n const [health, configResponse] = await Promise.all([\n probe(`${site.origin}/api/health`, fetcher, true),\n probe(`${site.origin}/api/join-config`, fetcher, true),\n ]);\n const targetNote = (path: string, expected: unknown, actual: unknown): void => {\n if (JSON.stringify(expected) !== JSON.stringify(actual)) findings.push({ targetId: site.targetId, siteId: site.siteId, path, expected, actual });\n };\n targetNote(\"/api/health status\", 200, health.error ?? health.status);\n if (health.status === 200 && !health.error) targetNote(\"/api/health ok\", true, boolean(record(health.json)?.ok));\n if (health.status === 200 && !health.error) {\n for (const [name, expected] of Object.entries({\n \"app-id\": site.target.appId, \"chapter-id\": site.target.chapterId,\n \"data-environment\": site.target.dataEnvironment, \"runtime\": site.target.runtime,\n })) targetNote(`/api/health x-odla-${name}`, expected, health.identity?.[name]);\n }\n targetNote(\"/api/join-config status\", 200, configResponse.status || configResponse.error);\n if (configResponse.status !== 200) continue;\n if (configResponse.error) {\n targetNote(\"/api/join-config JSON\", \"object\", configResponse.error === \"invalid_json\" ? \"invalid JSON\" : configResponse.error);\n continue;\n }\n const config = record(configResponse.json);\n if (!config) {\n targetNote(\"/api/join-config JSON\", \"object\", \"invalid shape\");\n continue;\n }\n targetNote(\"id\", site.target.chapterId, slug(config.id));\n const actualTiers = Array.isArray(config.tiers) ? config.tiers : [];\n const expectedTiers = activeMemberships(site).map((sku) => ({\n id: sku.id, priceCents: sku.priceCents, currency: sku.currency,\n interval: sku.billing, free: sku.priceCents === 0,\n }));\n const projected = actualTiers.map((tier) => {\n const value = record(tier);\n if (!value) return \"invalid_tier\";\n return {\n id: slug(value.id), priceCents: amount(value.priceCents),\n currency: value.currency === undefined ? undefined : typeof value.currency === \"string\" && /^[a-z]{3}$/.test(value.currency) ? value.currency : \"invalid_value\",\n interval: value.interval === undefined ? undefined : [\"month\", \"year\", \"once\"].includes(String(value.interval)) ? value.interval : \"invalid_value\",\n free: boolean(value.free),\n };\n });\n targetNote(\"tiers\", expectedTiers, projected);\n targetNote(\"paymentsReady\", expectedTiers.some((tier) => !tier.free), boolean(config.paymentsReady));\n }\n return { ok: findings.length === 0, findings };\n}\n","import type {\n ChapterAcceptanceCaseResult,\n ChapterAcceptanceCleanupEvidence,\n ChapterAcceptanceEvidence,\n ChapterAcceptanceExecutionContext,\n ChapterAcceptanceExecutor,\n ChapterAcceptanceManifest,\n ChapterAcceptanceProviderReference,\n ChapterAcceptanceRunResult,\n} from \"./testing-contract\";\nimport { planChapterAcceptanceMatrix } from \"./testing\";\n\nconst SAFE_LABEL = /^[a-z0-9][a-z0-9._:-]{0,159}$/;\nconst SAFE_REFERENCE = /^[A-Za-z0-9_:.=-]{3,240}$/;\nconst CREDENTIAL = /(?:^|[_.:-])(?:sk|rk|pk)_(?:test|live)_|whsec_|_secret_|eyJ[A-Za-z0-9_-]+\\./i;\nconst ERROR_CODES = new Set([\"invalid_evidence\", \"assertion_failed\", \"preflight_failed\", \"unsupported_scenario\", \"execution_failed\"]);\n\nfunction safeValue(value: string, pattern: RegExp): boolean {\n return pattern.test(value) && !CREDENTIAL.test(value);\n}\n\nfunction safeErrorCode(error: unknown): string {\n if (error && typeof error === \"object\") {\n const code = (error as { code?: unknown }).code;\n if (typeof code === \"string\" && ERROR_CODES.has(code)) return code;\n }\n return \"execution_failed\";\n}\n\nfunction validateLabels(values: readonly string[], path: string): void {\n if (!Array.isArray(values) || values.length === 0) throw Object.assign(new TypeError(`${path} must be non-empty`), { code: \"invalid_evidence\" });\n if (values.length > 64 || values.some((value) => typeof value !== \"string\" || !safeValue(value, SAFE_LABEL))) {\n throw Object.assign(new TypeError(`${path} contains an unsafe or unbounded label`), { code: \"invalid_evidence\" });\n }\n}\n\nfunction validateReferences(values: readonly ChapterAcceptanceProviderReference[] | undefined): void {\n if (values === undefined) return;\n if (!Array.isArray(values) || values.length > 64) throw Object.assign(new TypeError(\"references must be bounded\"), { code: \"invalid_evidence\" });\n const kinds = new Set([\"stripe_event\", \"stripe_object\", \"clerk_user\", \"calendar_event\", \"email_message\", \"event_digest\"]);\n for (const reference of values) {\n if (!reference || !kinds.has(reference.kind) || typeof reference.value !== \"string\" ||\n !safeValue(reference.value, SAFE_REFERENCE) || reference.value.includes(\"@\")) {\n throw Object.assign(new TypeError(\"references may contain only opaque provider ids or digests\"), { code: \"invalid_evidence\" });\n }\n }\n}\n\nfunction validateEvidence(evidence: ChapterAcceptanceEvidence, provider: boolean): ChapterAcceptanceEvidence {\n if (!evidence || typeof evidence !== \"object\") throw Object.assign(new TypeError(\"executor returned no evidence\"), { code: \"invalid_evidence\" });\n validateLabels(evidence.checks, \"evidence.checks\");\n validateReferences(evidence.references);\n if ((provider || evidence.deploymentVersion !== undefined) &&\n (typeof evidence.deploymentVersion !== \"string\" || !safeValue(evidence.deploymentVersion, SAFE_REFERENCE))) {\n throw Object.assign(new TypeError(\"provider evidence requires a safe deploymentVersion\"), { code: \"invalid_evidence\" });\n }\n // Copy only the validated wire contract. Never retain provider response objects,\n // extra fields, custom toJSON methods, or arrays that an adapter can later mutate.\n return {\n checks: [...evidence.checks],\n ...(evidence.deploymentVersion !== undefined ? { deploymentVersion: evidence.deploymentVersion } : {}),\n ...(evidence.references !== undefined ? {\n references: evidence.references.map(({ kind, value }) => ({ kind, value })),\n } : {}),\n };\n}\n\nfunction validateCleanup(cleanup: ChapterAcceptanceCleanupEvidence): ChapterAcceptanceCleanupEvidence {\n if (!cleanup || (cleanup.status !== \"complete\" && cleanup.status !== \"incomplete\")) {\n throw Object.assign(new TypeError(\"executor returned invalid cleanup evidence\"), { code: \"cleanup_failed\" });\n }\n validateLabels(cleanup.checks, \"cleanup.checks\");\n return { status: cleanup.status, checks: [...cleanup.checks] };\n}\n\nfunction contextFor(manifest: ChapterAcceptanceManifest, id: string): ChapterAcceptanceExecutionContext {\n const scenario = planChapterAcceptanceMatrix(manifest).find((item) => item.id === id);\n if (!scenario) throw new TypeError(`unknown acceptance scenario ${id}`);\n const target = manifest.targets.find((item) => item.targetId === scenario.targetId)!;\n return {\n scenario,\n target,\n ...(scenario.skuId ? { sku: target.skus.find((item) => item.id === scenario.skuId) } : {}),\n ...(scenario.siblingTargetId ? {\n siblingTarget: manifest.targets.find((item) => item.targetId === scenario.siblingTargetId),\n } : {}),\n };\n}\n\n/**\n * Execute one suite serially. Provider runs fail before their first mutation\n * unless the app supplies cleanup, then invoke cleanup after every attempted\n * case even when execution throws. Unconfirmed cleanup stops subsequent cases;\n * those cases fail with `not_run_cleanup_failed` and are never executed.\n * Evidence is copied only after validation; raw provider errors and extra fields\n * are never retained. Adapter check labels must not contain personal data.\n */\nexport async function runChapterAcceptanceSuite(\n manifest: ChapterAcceptanceManifest,\n suite: \"hermetic\" | \"provider_dev\",\n executor: ChapterAcceptanceExecutor,\n): Promise<ChapterAcceptanceRunResult> {\n if (suite !== \"hermetic\" && suite !== \"provider_dev\") throw new TypeError(\"unknown acceptance suite\");\n const scenarios = planChapterAcceptanceMatrix(manifest).filter((item) => item.suite === suite);\n if (suite === \"provider_dev\" && typeof executor.cleanup !== \"function\") {\n throw new TypeError(\"provider_dev acceptance requires a cleanup executor before any case can run\");\n }\n const cases: ChapterAcceptanceCaseResult[] = [];\n let cleanupFailed = false;\n for (const scenario of scenarios) {\n if (cleanupFailed) {\n cases.push({ id: scenario.id, suite, outcome: \"failed\", errorCode: \"not_run_cleanup_failed\" });\n continue;\n }\n const context = contextFor(manifest, scenario.id);\n let evidence: ChapterAcceptanceEvidence | undefined;\n let errorCode: string | undefined;\n let cleanup: ChapterAcceptanceCleanupEvidence | undefined;\n try {\n evidence = validateEvidence(await executor.execute(context), suite === \"provider_dev\");\n } catch (error) {\n errorCode = safeErrorCode(error);\n } finally {\n if (suite === \"provider_dev\") {\n try {\n cleanup = validateCleanup(await executor.cleanup!(context));\n if (cleanup.status !== \"complete\") {\n cleanupFailed = true;\n errorCode = \"cleanup_incomplete\";\n }\n } catch {\n cleanupFailed = true;\n errorCode = \"cleanup_failed\";\n }\n }\n }\n cases.push({\n id: scenario.id,\n suite,\n outcome: errorCode ? \"failed\" : \"passed\",\n ...(evidence ? { evidence } : {}),\n ...(cleanup ? { cleanup } : {}),\n ...(errorCode ? { errorCode } : {}),\n });\n }\n return { ok: cases.every((item) => item.outcome === \"passed\"), suite, cases };\n}\n\n/** PII-free line report suitable for CI logs and PM evidence. */\nexport function formatChapterAcceptanceRun(result: ChapterAcceptanceRunResult): string {\n const passed = result.cases.filter((item) => item.outcome === \"passed\").length;\n const lines = [`${result.ok ? \"ok\" : \"failed\"}: ${result.suite} ${passed}/${result.cases.length}`];\n for (const item of result.cases) {\n lines.push(`${item.outcome === \"passed\" ? \"PASS\" : \"FAIL\"} ${item.id}${item.errorCode ? ` ${item.errorCode}` : \"\"}`);\n }\n return lines.join(\"\\n\");\n}\n"],"mappings":"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","names":["scenario","activeMemberships","record"]}
|
package/dist/testing.d.cts
CHANGED
|
@@ -137,13 +137,21 @@ interface ChapterAcceptanceRunResult {
|
|
|
137
137
|
cases: ChapterAcceptanceCaseResult[];
|
|
138
138
|
}
|
|
139
139
|
|
|
140
|
-
/**
|
|
140
|
+
/**
|
|
141
|
+
* Probe public deployed join contracts without creating applications or provider objects.
|
|
142
|
+
* Each request, including its body, is limited to ten seconds and 64 KiB. Redirects
|
|
143
|
+
* are refused. Malformed responses become per-target findings, never raw bodies
|
|
144
|
+
* or exception text; one failed target does not suppress the remaining targets.
|
|
145
|
+
*/
|
|
141
146
|
declare function probeChapterJoinContracts(manifest: ChapterAcceptanceManifest, fetcher?: typeof fetch): Promise<ChapterJoinContractProbeResult>;
|
|
142
147
|
|
|
143
148
|
/**
|
|
144
149
|
* Execute one suite serially. Provider runs fail before their first mutation
|
|
145
150
|
* unless the app supplies cleanup, then invoke cleanup after every attempted
|
|
146
|
-
* case even when execution throws.
|
|
151
|
+
* case even when execution throws. Unconfirmed cleanup stops subsequent cases;
|
|
152
|
+
* those cases fail with `not_run_cleanup_failed` and are never executed.
|
|
153
|
+
* Evidence is copied only after validation; raw provider errors and extra fields
|
|
154
|
+
* are never retained. Adapter check labels must not contain personal data.
|
|
147
155
|
*/
|
|
148
156
|
declare function runChapterAcceptanceSuite(manifest: ChapterAcceptanceManifest, suite: "hermetic" | "provider_dev", executor: ChapterAcceptanceExecutor): Promise<ChapterAcceptanceRunResult>;
|
|
149
157
|
/** PII-free line report suitable for CI logs and PM evidence. */
|
package/dist/testing.d.ts
CHANGED
|
@@ -137,13 +137,21 @@ interface ChapterAcceptanceRunResult {
|
|
|
137
137
|
cases: ChapterAcceptanceCaseResult[];
|
|
138
138
|
}
|
|
139
139
|
|
|
140
|
-
/**
|
|
140
|
+
/**
|
|
141
|
+
* Probe public deployed join contracts without creating applications or provider objects.
|
|
142
|
+
* Each request, including its body, is limited to ten seconds and 64 KiB. Redirects
|
|
143
|
+
* are refused. Malformed responses become per-target findings, never raw bodies
|
|
144
|
+
* or exception text; one failed target does not suppress the remaining targets.
|
|
145
|
+
*/
|
|
141
146
|
declare function probeChapterJoinContracts(manifest: ChapterAcceptanceManifest, fetcher?: typeof fetch): Promise<ChapterJoinContractProbeResult>;
|
|
142
147
|
|
|
143
148
|
/**
|
|
144
149
|
* Execute one suite serially. Provider runs fail before their first mutation
|
|
145
150
|
* unless the app supplies cleanup, then invoke cleanup after every attempted
|
|
146
|
-
* case even when execution throws.
|
|
151
|
+
* case even when execution throws. Unconfirmed cleanup stops subsequent cases;
|
|
152
|
+
* those cases fail with `not_run_cleanup_failed` and are never executed.
|
|
153
|
+
* Evidence is copied only after validation; raw provider errors and extra fields
|
|
154
|
+
* are never retained. Adapter check labels must not contain personal data.
|
|
147
155
|
*/
|
|
148
156
|
declare function runChapterAcceptanceSuite(manifest: ChapterAcceptanceManifest, suite: "hermetic" | "provider_dev", executor: ChapterAcceptanceExecutor): Promise<ChapterAcceptanceRunResult>;
|
|
149
157
|
/** PII-free line report suitable for CI logs and PM evidence. */
|