@adcp/sdk 16.2.0 → 16.3.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 (97) hide show
  1. package/AGENTS.md +2 -0
  2. package/dist/lib/core/AgentClient.js +17 -1
  3. package/dist/lib/core/AgentClient.mjs +17 -1
  4. package/dist/lib/core/SingleAgentClient.js +13 -0
  5. package/dist/lib/core/SingleAgentClient.mjs +13 -0
  6. package/dist/lib/core/TaskExecutor.js +88 -13
  7. package/dist/lib/core/TaskExecutor.mjs +89 -14
  8. package/dist/lib/errors/index.d.mts +34 -0
  9. package/dist/lib/errors/index.d.ts +34 -0
  10. package/dist/lib/errors/index.js +11 -0
  11. package/dist/lib/errors/index.mjs +10 -0
  12. package/dist/lib/index.d.mts +1 -1
  13. package/dist/lib/index.d.ts +1 -1
  14. package/dist/lib/index.js +2 -0
  15. package/dist/lib/index.mjs +2 -0
  16. package/dist/lib/media-buy/compatibility.d.mts +1 -0
  17. package/dist/lib/media-buy/compatibility.d.ts +1 -0
  18. package/dist/lib/media-buy/compatibility.js +33 -10
  19. package/dist/lib/media-buy/compatibility.mjs +32 -14
  20. package/dist/lib/negotiation/buyer.js +11 -5
  21. package/dist/lib/negotiation/buyer.mjs +11 -5
  22. package/dist/lib/negotiation/types.d.mts +4 -2
  23. package/dist/lib/negotiation/types.d.ts +4 -2
  24. package/dist/lib/negotiation/verification.d.mts +15 -6
  25. package/dist/lib/negotiation/verification.d.ts +15 -6
  26. package/dist/lib/negotiation/verification.js +146 -27
  27. package/dist/lib/negotiation/verification.mjs +146 -28
  28. package/dist/lib/negotiation/version.d.mts +6 -0
  29. package/dist/lib/negotiation/version.d.ts +6 -0
  30. package/dist/lib/negotiation/version.js +60 -0
  31. package/dist/lib/negotiation/version.mjs +33 -0
  32. package/dist/lib/server/create-adcp-server.js +1 -1
  33. package/dist/lib/server/create-adcp-server.mjs +1 -1
  34. package/dist/lib/server/error-arm-tools.d.mts +10 -10
  35. package/dist/lib/server/error-arm-tools.d.ts +10 -10
  36. package/dist/lib/server/error-arm-tools.js +22 -58
  37. package/dist/lib/server/error-arm-tools.mjs +30 -65
  38. package/dist/lib/testing/compliance/comply.js +17 -0
  39. package/dist/lib/testing/compliance/comply.mjs +17 -0
  40. package/dist/lib/testing/index.d.mts +3 -0
  41. package/dist/lib/testing/index.d.ts +3 -0
  42. package/dist/lib/testing/index.js +44 -2
  43. package/dist/lib/testing/index.mjs +42 -0
  44. package/dist/lib/testing/storyboard/collection-selection.d.mts +259 -0
  45. package/dist/lib/testing/storyboard/collection-selection.d.ts +259 -0
  46. package/dist/lib/testing/storyboard/collection-selection.js +384 -0
  47. package/dist/lib/testing/storyboard/collection-selection.mjs +348 -0
  48. package/dist/lib/testing/storyboard/context.js +12 -6
  49. package/dist/lib/testing/storyboard/context.mjs +12 -6
  50. package/dist/lib/testing/storyboard/fixture-resolution.d.mts +39 -1
  51. package/dist/lib/testing/storyboard/fixture-resolution.d.ts +39 -1
  52. package/dist/lib/testing/storyboard/fixture-resolution.js +216 -12
  53. package/dist/lib/testing/storyboard/fixture-resolution.mjs +218 -12
  54. package/dist/lib/testing/storyboard/index.d.mts +9 -2
  55. package/dist/lib/testing/storyboard/index.d.ts +9 -2
  56. package/dist/lib/testing/storyboard/index.js +51 -2
  57. package/dist/lib/testing/storyboard/index.mjs +49 -1
  58. package/dist/lib/testing/storyboard/prepared-fixtures.d.mts +471 -0
  59. package/dist/lib/testing/storyboard/prepared-fixtures.d.ts +471 -0
  60. package/dist/lib/testing/storyboard/prepared-fixtures.js +434 -0
  61. package/dist/lib/testing/storyboard/prepared-fixtures.mjs +389 -0
  62. package/dist/lib/testing/storyboard/protected-context.d.mts +127 -0
  63. package/dist/lib/testing/storyboard/protected-context.d.ts +127 -0
  64. package/dist/lib/testing/storyboard/protected-context.js +301 -0
  65. package/dist/lib/testing/storyboard/protected-context.mjs +267 -0
  66. package/dist/lib/testing/storyboard/rejection-hints.js +13 -4
  67. package/dist/lib/testing/storyboard/rejection-hints.mjs +13 -4
  68. package/dist/lib/testing/storyboard/runner.d.mts +24 -0
  69. package/dist/lib/testing/storyboard/runner.d.ts +24 -0
  70. package/dist/lib/testing/storyboard/runner.js +1061 -105
  71. package/dist/lib/testing/storyboard/runner.mjs +1075 -106
  72. package/dist/lib/testing/storyboard/seeding.d.mts +15 -1
  73. package/dist/lib/testing/storyboard/seeding.d.ts +15 -1
  74. package/dist/lib/testing/storyboard/seeding.js +272 -12
  75. package/dist/lib/testing/storyboard/seeding.mjs +274 -13
  76. package/dist/lib/testing/storyboard/types.d.mts +207 -10
  77. package/dist/lib/testing/storyboard/types.d.ts +207 -10
  78. package/dist/lib/testing/storyboard/types.js +1 -1
  79. package/dist/lib/testing/storyboard/types.mjs +1 -1
  80. package/dist/lib/testing/storyboard/validations.d.mts +1 -1
  81. package/dist/lib/testing/storyboard/validations.d.ts +1 -1
  82. package/dist/lib/testing/storyboard/validations.js +95 -0
  83. package/dist/lib/testing/storyboard/validations.mjs +95 -0
  84. package/dist/lib/validation/bundled-schema-store.js +5 -0
  85. package/dist/lib/validation/bundled-schema-store.mjs +4 -0
  86. package/dist/lib/validation/schema-loader.js +19 -2
  87. package/dist/lib/validation/schema-loader.mjs +22 -3
  88. package/dist/lib/version.d.mts +3 -3
  89. package/dist/lib/version.d.ts +3 -3
  90. package/dist/lib/version.js +3 -3
  91. package/dist/lib/version.mjs +3 -3
  92. package/docs/TYPE-SUMMARY.md +1 -1
  93. package/docs/guides/COLLECTION-FIXTURE-FOUNDATION.md +850 -0
  94. package/docs/guides/PROPOSAL-TERMS-VERIFICATION.md +66 -0
  95. package/docs/llms.txt +3 -2
  96. package/docs/migration-14.x-rc-worksheet.md +4 -4
  97. package/package.json +1 -1
@@ -0,0 +1,434 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+ var prepared_fixtures_exports = {};
30
+ __export(prepared_fixtures_exports, {
31
+ PREPARED_FIXTURES_CONTRACT_VERSION: () => PREPARED_FIXTURES_CONTRACT_VERSION,
32
+ PreparedStoryboardFixtures: () => PreparedStoryboardFixtures,
33
+ buildPreparedBinding: () => buildPreparedBinding,
34
+ createPreparedStoryboardFixtures: () => createPreparedStoryboardFixtures,
35
+ diffPreparedBinding: () => diffPreparedBinding,
36
+ digestStoryboard: () => digestStoryboard,
37
+ fingerprintCredential: () => fingerprintCredential,
38
+ isTrustedPreparedFixtures: () => isTrustedPreparedFixtures,
39
+ preparedBindingRegistryFor: () => preparedBindingRegistryFor,
40
+ resolvePreparedFixtures: () => resolvePreparedFixtures,
41
+ resolveSchemaArtifact: () => resolveSchemaArtifact,
42
+ withPreparedFixtures: () => withPreparedFixtures
43
+ });
44
+ module.exports = __toCommonJS(prepared_fixtures_exports);
45
+ var import_node_crypto = require("node:crypto");
46
+ var import_node_fs = require("node:fs");
47
+ var import_node_path = __toESM(require("node:path"));
48
+ var import_schema_loader = require('../../validation/schema-loader.js');
49
+ var import_version = require('../../version.js');
50
+ const PREPARED_FIXTURES_CONTRACT_VERSION = "1.0.0";
51
+ const REFERENCE_REFUSAL_DETAIL = "prepared fixture state was refused by the configured authorizer";
52
+ const trustedStates = /* @__PURE__ */ new WeakSet();
53
+ const registries = /* @__PURE__ */ new WeakMap();
54
+ function freezeDeepLocal(value) {
55
+ if (value === null || typeof value !== "object") return value;
56
+ if (Object.isFrozen(value)) return value;
57
+ Object.freeze(value);
58
+ for (const nested of Object.values(value)) freezeDeepLocal(nested);
59
+ return value;
60
+ }
61
+ const CONSTRUCTION_TOKEN = /* @__PURE__ */ Symbol("adcp.preparedStoryboardFixtures");
62
+ class PreparedStoryboardFixtures {
63
+ #binding;
64
+ #resolutions;
65
+ #coverageGaps;
66
+ #captures;
67
+ #captureProvenance;
68
+ #witnesses;
69
+ #disposition;
70
+ #phase;
71
+ /**
72
+ * @internal Constructed only by `prepareStoryboardFixtures`. The token is
73
+ * module-private, so an external `new` call cannot satisfy it.
74
+ */
75
+ constructor(init, token) {
76
+ if (token !== CONSTRUCTION_TOKEN) {
77
+ throw new Error(
78
+ "PreparedStoryboardFixtures cannot be constructed directly; call prepareStoryboardFixtures() to produce prepared state"
79
+ );
80
+ }
81
+ this.#binding = freezeDeepLocal(structuredClone(init.binding));
82
+ if (init.bindings) registries.set(this, init.bindings);
83
+ this.#resolutions = Object.freeze(structuredClone(init.resolutions));
84
+ this.#coverageGaps = Object.freeze(structuredClone(init.coverageGaps));
85
+ this.#captures = Object.freeze(structuredClone(init.captures));
86
+ this.#captureProvenance = Object.freeze(structuredClone(init.captureProvenance));
87
+ this.#witnesses = Object.freeze(structuredClone(init.witnesses));
88
+ this.#disposition = freezeDeepLocal(structuredClone(init.disposition));
89
+ this.#phase = init.phase === void 0 ? void 0 : structuredClone(init.phase);
90
+ trustedStates.add(this);
91
+ Object.freeze(this);
92
+ }
93
+ /** Nonsecret binding identity. Detached copy. */
94
+ get binding() {
95
+ return structuredClone(this.#binding);
96
+ }
97
+ get disposition() {
98
+ return structuredClone(this.#disposition);
99
+ }
100
+ /** Machine-readable per-handle resolution evidence. Detached copy. */
101
+ get resolutions() {
102
+ return structuredClone(this.#resolutions);
103
+ }
104
+ /** Coverage gaps observed during preparation. Detached copy. */
105
+ get coverageGaps() {
106
+ return structuredClone(this.#coverageGaps);
107
+ }
108
+ /** Pinned seller identities per handle. Detached copy. */
109
+ get selections() {
110
+ return this.#resolutions.filter((record) => record.seller_ids !== void 0).map((record) => ({
111
+ handle: record.handle,
112
+ fixture_type: record.fixture_type,
113
+ ...record.product_handle && { product_handle: record.product_handle },
114
+ seller_ids: structuredClone(record.seller_ids)
115
+ }));
116
+ }
117
+ /** Reserved-namespace captures. Detached copy. */
118
+ get captures() {
119
+ return structuredClone(this.#captures);
120
+ }
121
+ /** Capture provenance. Detached copy. */
122
+ get captureProvenance() {
123
+ return structuredClone(this.#captureProvenance);
124
+ }
125
+ /** Retained collection witnesses per handle. Detached copy. */
126
+ get witnesses() {
127
+ return structuredClone(this.#witnesses);
128
+ }
129
+ /**
130
+ * Detached copy of the synthetic pre-flight phase, for attachment to a
131
+ * full run's report. A live reference would let a report consumer mutate
132
+ * the preparation's own evidence — including a failed or no-match
133
+ * disposition's raw failure detail.
134
+ */
135
+ get phase() {
136
+ return this.#phase === void 0 ? void 0 : structuredClone(this.#phase);
137
+ }
138
+ }
139
+ Object.freeze(PreparedStoryboardFixtures.prototype);
140
+ function readPreparedBindingRegistry(state) {
141
+ return registries.get(state);
142
+ }
143
+ function preparedBindingRegistryFor(state) {
144
+ return readPreparedBindingRegistry(state);
145
+ }
146
+ function createPreparedStoryboardFixtures(init) {
147
+ return new PreparedStoryboardFixtures(init, CONSTRUCTION_TOKEN);
148
+ }
149
+ function withPreparedFixtures(state, authorizer) {
150
+ return { prepared_fixtures: state, prepared_fixtures_authorizer: authorizer };
151
+ }
152
+ function isTrustedPreparedFixtures(value) {
153
+ return value instanceof PreparedStoryboardFixtures && trustedStates.has(value);
154
+ }
155
+ const CREDENTIAL_FINGERPRINT_KEY = (0, import_node_crypto.randomBytes)(32);
156
+ function fingerprintCredential(secret) {
157
+ return (0, import_node_crypto.createHmac)("sha256", CREDENTIAL_FINGERPRINT_KEY).update(secret).digest("hex").slice(0, 32);
158
+ }
159
+ function redactRouteCredentials(raw) {
160
+ let url;
161
+ try {
162
+ url = new URL(raw);
163
+ } catch {
164
+ return raw;
165
+ }
166
+ url.username = "";
167
+ url.password = "";
168
+ for (const key of [...url.searchParams.keys()]) {
169
+ if (/(token|secret|key|password|credential|signature|auth)/i.test(key)) {
170
+ url.searchParams.set(key, "[redacted]");
171
+ }
172
+ }
173
+ url.hash = "";
174
+ return url.toString();
175
+ }
176
+ function canonicalize(value) {
177
+ if (value === null || typeof value !== "object") return JSON.stringify(value) ?? "null";
178
+ if (Array.isArray(value)) return `[${value.map(canonicalize).join(",")}]`;
179
+ const entries = Object.entries(value).filter(([, nested]) => nested !== void 0).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0);
180
+ return `{${entries.map(([key, nested]) => `${JSON.stringify(key)}:${canonicalize(nested)}`).join(",")}}`;
181
+ }
182
+ function digestStoryboard(storyboard) {
183
+ return (0, import_node_crypto.createHash)("sha256").update(canonicalize(storyboard)).digest("hex");
184
+ }
185
+ const schemaDigestCache = /* @__PURE__ */ new Map();
186
+ function digestSchemaRoot(root) {
187
+ const cached = schemaDigestCache.get(root);
188
+ if (cached !== void 0) return cached;
189
+ const lines = [`root\0${root}`];
190
+ const walk = (dir, rel) => {
191
+ let entries;
192
+ try {
193
+ entries = (0, import_node_fs.readdirSync)(dir, { withFileTypes: true });
194
+ } catch {
195
+ lines.push(`${rel || "."}\0unreadable-dir`);
196
+ return;
197
+ }
198
+ for (const entry of entries.sort((a, b) => a.name < b.name ? -1 : a.name > b.name ? 1 : 0)) {
199
+ const absolute = import_node_path.default.join(dir, entry.name);
200
+ const relative = rel ? `${rel}/${entry.name}` : entry.name;
201
+ if (entry.isDirectory()) {
202
+ walk(absolute, relative);
203
+ continue;
204
+ }
205
+ try {
206
+ const stat = (0, import_node_fs.statSync)(absolute);
207
+ lines.push(`${relative}\0${stat.size}\0${stat.mtimeMs}`);
208
+ } catch {
209
+ lines.push(`${relative}\0unreadable`);
210
+ }
211
+ }
212
+ };
213
+ walk(root, "");
214
+ const digest = (0, import_node_crypto.createHash)("sha256").update(lines.join("\n")).digest("hex");
215
+ schemaDigestCache.set(root, digest);
216
+ return digest;
217
+ }
218
+ function resolveSchemaArtifact(adcpVersion = import_version.ADCP_VERSION) {
219
+ const location = (0, import_schema_loader.getActiveSchemaArtifactLocation)(adcpVersion);
220
+ return {
221
+ adcp_version: adcpVersion,
222
+ bundle_key: location.bundle_key,
223
+ external: location.external,
224
+ digest: digestSchemaRoot(location.root)
225
+ };
226
+ }
227
+ function buildPreparedBinding(input) {
228
+ return {
229
+ storyboard_id: input.storyboard.id,
230
+ storyboard_digest: digestStoryboard(input.storyboard),
231
+ agent_urls: [...input.agentUrls].sort().map(redactRouteCredentials),
232
+ // The route the GRAPH was pinned against, positionally — preparation
233
+ // contacts exactly one agent, `agentUrls[0]`, and nothing else in the
234
+ // binding records which one that was. `agent_urls` is sorted and
235
+ // `agent_route_fingerprint` sorts by key, both deliberately
236
+ // order-insensitive, so two runs whose `agents` maps differ only in
237
+ // insertion order produced byte-identical bindings while preparation
238
+ // had actually read seller B's catalog for one and seller A's for the
239
+ // other. Reuse across them sent B's pinned product_id to A.
240
+ fixture_agent_url: redactRouteCredentials(input.agentUrls[0] ?? ""),
241
+ // Covers the specialism→URL PAIRING, not just the URL set. Sorting the
242
+ // set alone meant swapping two specialisms' routes left the fingerprint
243
+ // unchanged while every per-specialism credential went to a different
244
+ // seller.
245
+ agent_route_fingerprint: fingerprintCredential(
246
+ JSON.stringify([
247
+ [...input.agentUrls].sort(),
248
+ input.agentRouteMap ? [...input.agentRouteMap].sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0) : null,
249
+ // The RAW positional first url — the one preparation contacts.
250
+ // `fixture_agent_url` beside it is redacted for the authorizer to
251
+ // read, so two routes differing only in userinfo or a credential
252
+ // query parameter render identically there and would have
253
+ // collapsed into one binding.
254
+ input.agentUrls[0] ?? null
255
+ ])
256
+ ),
257
+ schema_artifact: resolveSchemaArtifact(input.adcpVersion ?? import_version.ADCP_VERSION),
258
+ contract_version: PREPARED_FIXTURES_CONTRACT_VERSION,
259
+ account_scope: input.accountScope,
260
+ brand_scope: input.brandScope,
261
+ ...input.requestAccountScope !== void 0 && { request_account_scope: input.requestAccountScope },
262
+ sandbox: input.sandbox,
263
+ ...input.principal !== void 0 && { principal: input.principal },
264
+ credential_fingerprint: fingerprintCredential(input.credential),
265
+ ...input.stepCredential !== void 0 && {
266
+ step_credential_fingerprint: fingerprintCredential(input.stepCredential)
267
+ },
268
+ ...input.stepAuthKind !== void 0 && { step_auth_kind: input.stepAuthKind },
269
+ // Independent of the credential: a routed step whose credential happens
270
+ // to equal the run's still dispatches to one specific seller.
271
+ ...input.stepRoute !== void 0 && { step_route: input.stepRoute },
272
+ run_id: input.runId ?? (0, import_node_crypto.randomUUID)()
273
+ };
274
+ }
275
+ const BINDING_FIELDS = [
276
+ "storyboard_id",
277
+ "storyboard_digest",
278
+ "agent_urls",
279
+ "agent_route_fingerprint",
280
+ "fixture_agent_url",
281
+ "schema_artifact",
282
+ "contract_version",
283
+ "account_scope",
284
+ "brand_scope",
285
+ "sandbox",
286
+ "principal",
287
+ "credential_fingerprint"
288
+ ];
289
+ function diffPreparedBinding(prepared, current) {
290
+ const mismatches = [];
291
+ for (const field of BINDING_FIELDS) {
292
+ if (canonicalize(prepared[field]) !== canonicalize(current[field])) mismatches.push(field);
293
+ }
294
+ return mismatches;
295
+ }
296
+ function isReference(value) {
297
+ return typeof value === "object" && value !== null && typeof value.ref === "string" && typeof value.resolve === "function";
298
+ }
299
+ async function resolvePreparedFixtures(input) {
300
+ const supplied = input.supplied;
301
+ if (supplied === void 0) return void 0;
302
+ const authorizer = input.authorizer;
303
+ if (!authorizer) {
304
+ return {
305
+ ok: false,
306
+ refusal: {
307
+ code: "authorizer_required",
308
+ detail: isReference(supplied) ? "prepared_fixtures was supplied as an opaque reference, but no prepared_fixtures_authorizer was configured. An opaque reference is never authorization: supply an authorizer, because the SDK has no session and no grant store and cannot decide whether this reference is still yours." : "prepared_fixtures reuse requires prepared_fixtures_authorizer. Trusted object identity proves the state came from prepareStoryboardFixtures in this process; it does not establish a current grant, and binding coherence cannot observe revocation. Supply an authorizer that re-checks live grant state on every lookup. If this run genuinely has no grant model (single-tenant CLI, local dev), either drop prepared_fixtures and let the runner re-resolve, or pass prepared_fixtures_authorizer: () => ({ authorized: true }) as an explicit, reviewable decision. See docs/guides/COLLECTION-FIXTURE-FOUNDATION.md section 4."
309
+ }
310
+ };
311
+ }
312
+ let state = supplied;
313
+ let reference;
314
+ if (isReference(supplied)) {
315
+ reference = supplied.ref;
316
+ try {
317
+ state = await supplied.resolve(supplied.ref);
318
+ } catch {
319
+ state = void 0;
320
+ }
321
+ }
322
+ const refuseReference = async () => {
323
+ try {
324
+ await authorizer({
325
+ // Cloned, and cloned SEPARATELY, exactly as the authorized path
326
+ // does: handing the same object twice would let an authorizer
327
+ // detect `binding === current` by reference.
328
+ //
329
+ // That closes the object-IDENTITY distinction and nothing more.
330
+ // The VALUES still differ by path — a resolving reference carries
331
+ // the stored preparation binding, this one carries a second copy
332
+ // of `current` — so an authorizer that deep-compares them can
333
+ // still tell a missing reference from a present one. That is not
334
+ // the oracle this branch exists to close: the authorizer IS the
335
+ // host, it owns the store it would be learning about, and it
336
+ // cannot be given a fabricated stored binding without being lied
337
+ // to. What is closed is the oracle available to the CALLER, who
338
+ // sees only the refusal: identical text, identical machine code,
339
+ // identical validation shape, same authorizer call count.
340
+ binding: structuredClone(input.current),
341
+ current: structuredClone(input.current),
342
+ ...input.stepId !== void 0 && { step_id: input.stepId },
343
+ ...input.gate !== void 0 && { gate: input.gate },
344
+ ...reference !== void 0 && { reference }
345
+ });
346
+ } catch {
347
+ }
348
+ return { ok: false, refusal: { code: "authorizer_denied", detail: REFERENCE_REFUSAL_DETAIL } };
349
+ };
350
+ if (!isTrustedPreparedFixtures(state)) {
351
+ if (reference !== void 0) return refuseReference();
352
+ return {
353
+ ok: false,
354
+ refusal: {
355
+ code: "untrusted_state",
356
+ detail: "prepared fixture state was not produced by prepareStoryboardFixtures in this process; a rehydrated, cloned or caller-constructed look-alike carries no preparation authority"
357
+ }
358
+ };
359
+ }
360
+ const binding = state.binding;
361
+ if (binding.contract_version !== PREPARED_FIXTURES_CONTRACT_VERSION) {
362
+ if (reference !== void 0) return refuseReference();
363
+ return {
364
+ ok: false,
365
+ refusal: {
366
+ code: "contract_version_mismatch",
367
+ detail: `prepared state declares contract version ${binding.contract_version}; this runner implements ${PREPARED_FIXTURES_CONTRACT_VERSION}`
368
+ }
369
+ };
370
+ }
371
+ let authorized = false;
372
+ let decisionDetail;
373
+ try {
374
+ const decision = await authorizer({
375
+ binding: structuredClone(binding),
376
+ current: structuredClone(input.current),
377
+ ...input.stepId !== void 0 && { step_id: input.stepId },
378
+ ...input.gate !== void 0 && { gate: input.gate },
379
+ ...reference !== void 0 && { reference }
380
+ });
381
+ authorized = decision?.authorized === true;
382
+ const detail = decision?.detail;
383
+ decisionDetail = typeof detail === "string" ? detail : void 0;
384
+ } catch {
385
+ return { ok: false, refusal: { code: "authorizer_denied", detail: REFERENCE_REFUSAL_DETAIL } };
386
+ }
387
+ if (!authorized) {
388
+ if (reference !== void 0) {
389
+ return { ok: false, refusal: { code: "authorizer_denied", detail: REFERENCE_REFUSAL_DETAIL } };
390
+ }
391
+ return {
392
+ ok: false,
393
+ refusal: {
394
+ code: "authorizer_denied",
395
+ // `detail` is optional, so an absent one must not render as the
396
+ // string "undefined" — a difference in text is a difference a
397
+ // caller can measure.
398
+ detail: decisionDetail !== void 0 && decisionDetail.length > 0 ? `prepared fixture state was refused by the configured authorizer: ${decisionDetail}` : REFERENCE_REFUSAL_DETAIL
399
+ }
400
+ };
401
+ }
402
+ const mismatches = diffPreparedBinding(binding, input.current);
403
+ if (mismatches.length > 0) {
404
+ if (reference !== void 0) {
405
+ return { ok: false, refusal: { code: "authorizer_denied", detail: REFERENCE_REFUSAL_DETAIL } };
406
+ }
407
+ return {
408
+ ok: false,
409
+ refusal: {
410
+ code: "binding_mismatch",
411
+ // Field names only. The values are another principal's identity when
412
+ // the caller guessed a reference, and `credential_fingerprint` must
413
+ // never reach operator-visible text at all.
414
+ detail: `prepared fixture state does not match the current run scope; differing binding field(s): ${mismatches.join(", ")}`
415
+ }
416
+ };
417
+ }
418
+ return { ok: true, state };
419
+ }
420
+ // Annotate the CommonJS export names for ESM import in node:
421
+ 0 && (module.exports = {
422
+ PREPARED_FIXTURES_CONTRACT_VERSION,
423
+ PreparedStoryboardFixtures,
424
+ buildPreparedBinding,
425
+ createPreparedStoryboardFixtures,
426
+ diffPreparedBinding,
427
+ digestStoryboard,
428
+ fingerprintCredential,
429
+ isTrustedPreparedFixtures,
430
+ preparedBindingRegistryFor,
431
+ resolvePreparedFixtures,
432
+ resolveSchemaArtifact,
433
+ withPreparedFixtures
434
+ });