@adcp/sdk 16.2.0 → 16.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +2 -0
- package/dist/lib/adapters/version/3.1/assignments.d.mts +12 -0
- package/dist/lib/adapters/version/3.1/assignments.d.ts +12 -0
- package/dist/lib/adapters/version/3.1/assignments.js +67 -0
- package/dist/lib/adapters/version/3.1/assignments.mjs +43 -0
- package/dist/lib/adapters/version/3.1/bidding.d.mts +3 -0
- package/dist/lib/adapters/version/3.1/bidding.d.ts +3 -0
- package/dist/lib/adapters/version/3.1/bidding.js +115 -0
- package/dist/lib/adapters/version/3.1/bidding.mjs +90 -0
- package/dist/lib/adapters/version/3.1/delivery.d.mts +7 -0
- package/dist/lib/adapters/version/3.1/delivery.d.ts +7 -0
- package/dist/lib/adapters/version/3.1/delivery.js +78 -0
- package/dist/lib/adapters/version/3.1/delivery.mjs +54 -0
- package/dist/lib/adapters/version/3.1/index.d.mts +3 -0
- package/dist/lib/adapters/version/3.1/index.d.ts +3 -0
- package/dist/lib/adapters/version/3.1/index.js +36 -0
- package/dist/lib/adapters/version/3.1/index.mjs +12 -0
- package/dist/lib/adapters/version/index.d.mts +59 -7
- package/dist/lib/adapters/version/index.d.ts +59 -7
- package/dist/lib/adapters/version/index.js +51 -2
- package/dist/lib/adapters/version/index.mjs +49 -1
- package/dist/lib/adapters/version/negotiate.d.mts +71 -0
- package/dist/lib/adapters/version/negotiate.d.ts +71 -0
- package/dist/lib/adapters/version/negotiate.js +101 -0
- package/dist/lib/adapters/version/negotiate.mjs +73 -0
- package/dist/lib/adapters/version/schema-projection.d.mts +33 -0
- package/dist/lib/adapters/version/schema-projection.d.ts +33 -0
- package/dist/lib/adapters/version/schema-projection.js +294 -0
- package/dist/lib/adapters/version/schema-projection.mjs +269 -0
- package/dist/lib/adapters/version/translate.d.mts +122 -0
- package/dist/lib/adapters/version/translate.d.ts +122 -0
- package/dist/lib/adapters/version/translate.js +169 -0
- package/dist/lib/adapters/version/translate.mjs +141 -0
- package/dist/lib/adapters/version/types.d.mts +51 -10
- package/dist/lib/adapters/version/types.d.ts +51 -10
- package/dist/lib/core/AgentClient.d.mts +1 -1
- package/dist/lib/core/AgentClient.d.ts +1 -1
- package/dist/lib/core/AgentClient.js +17 -1
- package/dist/lib/core/AgentClient.mjs +17 -1
- package/dist/lib/core/ConversationTypes.d.mts +7 -0
- package/dist/lib/core/ConversationTypes.d.ts +7 -0
- package/dist/lib/core/SingleAgentClient.d.mts +82 -0
- package/dist/lib/core/SingleAgentClient.d.ts +82 -0
- package/dist/lib/core/SingleAgentClient.js +336 -15
- package/dist/lib/core/SingleAgentClient.mjs +350 -17
- package/dist/lib/core/TaskExecutor.d.mts +1 -1
- package/dist/lib/core/TaskExecutor.d.ts +1 -1
- package/dist/lib/core/TaskExecutor.js +182 -31
- package/dist/lib/core/TaskExecutor.mjs +183 -32
- package/dist/lib/core/account-reduction.d.mts +36 -0
- package/dist/lib/core/account-reduction.d.ts +36 -0
- package/dist/lib/core/account-reduction.js +127 -0
- package/dist/lib/core/account-reduction.mjs +98 -0
- package/dist/lib/core/buyer-account-registry.d.mts +52 -0
- package/dist/lib/core/buyer-account-registry.d.ts +52 -0
- package/dist/lib/core/buyer-account-registry.js +142 -6
- package/dist/lib/core/buyer-account-registry.mjs +142 -6
- package/dist/lib/errors/index.d.mts +34 -0
- package/dist/lib/errors/index.d.ts +34 -0
- package/dist/lib/errors/index.js +11 -0
- package/dist/lib/errors/index.mjs +10 -0
- package/dist/lib/index.d.mts +1 -1
- package/dist/lib/index.d.ts +1 -1
- package/dist/lib/index.js +2 -0
- package/dist/lib/index.mjs +2 -0
- package/dist/lib/media-buy/action-contracts.js +4 -4
- package/dist/lib/media-buy/action-contracts.mjs +4 -4
- package/dist/lib/media-buy/action-types.d.mts +3 -2
- package/dist/lib/media-buy/action-types.d.ts +3 -2
- package/dist/lib/media-buy/compatibility.d.mts +1 -0
- package/dist/lib/media-buy/compatibility.d.ts +1 -0
- package/dist/lib/media-buy/compatibility.js +33 -10
- package/dist/lib/media-buy/compatibility.mjs +32 -14
- package/dist/lib/media-buy/update-fields.generated.d.mts +3 -2
- package/dist/lib/media-buy/update-fields.generated.d.ts +3 -2
- package/dist/lib/negotiation/buyer.js +11 -5
- package/dist/lib/negotiation/buyer.mjs +11 -5
- package/dist/lib/negotiation/types.d.mts +4 -2
- package/dist/lib/negotiation/types.d.ts +4 -2
- package/dist/lib/negotiation/verification.d.mts +15 -6
- package/dist/lib/negotiation/verification.d.ts +15 -6
- package/dist/lib/negotiation/verification.js +146 -27
- package/dist/lib/negotiation/verification.mjs +146 -28
- package/dist/lib/negotiation/version.d.mts +6 -0
- package/dist/lib/negotiation/version.d.ts +6 -0
- package/dist/lib/negotiation/version.js +60 -0
- package/dist/lib/negotiation/version.mjs +33 -0
- package/dist/lib/registry/types.generated.d.mts +1 -1
- package/dist/lib/registry/types.generated.d.ts +1 -1
- package/dist/lib/server/create-adcp-server.d.mts +21 -0
- package/dist/lib/server/create-adcp-server.d.ts +21 -0
- package/dist/lib/server/create-adcp-server.js +98 -16
- package/dist/lib/server/create-adcp-server.mjs +84 -2
- package/dist/lib/server/error-arm-tools.d.mts +10 -10
- package/dist/lib/server/error-arm-tools.d.ts +10 -10
- package/dist/lib/server/error-arm-tools.js +22 -58
- package/dist/lib/server/error-arm-tools.mjs +30 -65
- package/dist/lib/server/index.d.mts +1 -1
- package/dist/lib/server/index.d.ts +1 -1
- package/dist/lib/storage/interfaces.d.mts +2 -0
- package/dist/lib/storage/interfaces.d.ts +2 -0
- package/dist/lib/testing/compliance/comply.js +17 -0
- package/dist/lib/testing/compliance/comply.mjs +17 -0
- package/dist/lib/testing/index.d.mts +3 -0
- package/dist/lib/testing/index.d.ts +3 -0
- package/dist/lib/testing/index.js +44 -2
- package/dist/lib/testing/index.mjs +42 -0
- package/dist/lib/testing/storyboard/collection-selection.d.mts +259 -0
- package/dist/lib/testing/storyboard/collection-selection.d.ts +259 -0
- package/dist/lib/testing/storyboard/collection-selection.js +384 -0
- package/dist/lib/testing/storyboard/collection-selection.mjs +348 -0
- package/dist/lib/testing/storyboard/context.js +12 -6
- package/dist/lib/testing/storyboard/context.mjs +12 -6
- package/dist/lib/testing/storyboard/fixture-resolution.d.mts +39 -1
- package/dist/lib/testing/storyboard/fixture-resolution.d.ts +39 -1
- package/dist/lib/testing/storyboard/fixture-resolution.js +216 -12
- package/dist/lib/testing/storyboard/fixture-resolution.mjs +218 -12
- package/dist/lib/testing/storyboard/index.d.mts +9 -2
- package/dist/lib/testing/storyboard/index.d.ts +9 -2
- package/dist/lib/testing/storyboard/index.js +51 -2
- package/dist/lib/testing/storyboard/index.mjs +49 -1
- package/dist/lib/testing/storyboard/prepared-fixtures.d.mts +471 -0
- package/dist/lib/testing/storyboard/prepared-fixtures.d.ts +471 -0
- package/dist/lib/testing/storyboard/prepared-fixtures.js +434 -0
- package/dist/lib/testing/storyboard/prepared-fixtures.mjs +389 -0
- package/dist/lib/testing/storyboard/protected-context.d.mts +127 -0
- package/dist/lib/testing/storyboard/protected-context.d.ts +127 -0
- package/dist/lib/testing/storyboard/protected-context.js +301 -0
- package/dist/lib/testing/storyboard/protected-context.mjs +267 -0
- package/dist/lib/testing/storyboard/rejection-hints.js +13 -4
- package/dist/lib/testing/storyboard/rejection-hints.mjs +13 -4
- package/dist/lib/testing/storyboard/runner.d.mts +24 -0
- package/dist/lib/testing/storyboard/runner.d.ts +24 -0
- package/dist/lib/testing/storyboard/runner.js +1061 -105
- package/dist/lib/testing/storyboard/runner.mjs +1075 -106
- package/dist/lib/testing/storyboard/seeding.d.mts +15 -1
- package/dist/lib/testing/storyboard/seeding.d.ts +15 -1
- package/dist/lib/testing/storyboard/seeding.js +272 -12
- package/dist/lib/testing/storyboard/seeding.mjs +274 -13
- package/dist/lib/testing/storyboard/types.d.mts +207 -10
- package/dist/lib/testing/storyboard/types.d.ts +207 -10
- package/dist/lib/testing/storyboard/types.js +1 -1
- package/dist/lib/testing/storyboard/types.mjs +1 -1
- package/dist/lib/testing/storyboard/validations.d.mts +1 -1
- package/dist/lib/testing/storyboard/validations.d.ts +1 -1
- package/dist/lib/testing/storyboard/validations.js +95 -0
- package/dist/lib/testing/storyboard/validations.mjs +95 -0
- package/dist/lib/validation/bundled-schema-store.js +5 -0
- package/dist/lib/validation/bundled-schema-store.mjs +4 -0
- package/dist/lib/validation/schema-loader.js +19 -2
- package/dist/lib/validation/schema-loader.mjs +22 -3
- package/dist/lib/version.d.mts +3 -3
- package/dist/lib/version.d.ts +3 -3
- package/dist/lib/version.js +3 -3
- package/dist/lib/version.mjs +3 -3
- package/docs/TYPE-SUMMARY.md +2 -2
- package/docs/guides/COLLECTION-FIXTURE-FOUNDATION.md +850 -0
- package/docs/guides/PROPOSAL-TERMS-VERIFICATION.md +66 -0
- package/docs/llms.txt +4 -3
- package/docs/migration-14.x-rc-worksheet.md +4 -4
- package/package.json +1 -1
|
@@ -38,13 +38,22 @@ export declare function verifyProposalCommercialTerms(proposal: unknown, expecte
|
|
|
38
38
|
export declare function assertProposalCommercialTerms(proposal: unknown, expectedCommercialTerms: unknown, options?: VerifyProposalCommercialTermsOptions): void;
|
|
39
39
|
/** Strict RFC 3339 date-time accepted by the AdCP `format: date-time` contract. */
|
|
40
40
|
export declare function isStrictDateTime(value: unknown): value is string;
|
|
41
|
-
export declare function verifyRefineProposalsResponse(request: Pick<RefineProposalsRequest, 'refinements'
|
|
41
|
+
export declare function verifyRefineProposalsResponse(request: Pick<RefineProposalsRequest, 'refinements'> & Partial<Pick<RefineProposalsRequest, 'adcp_version'>>, response: unknown, options?: {
|
|
42
42
|
now?: Date;
|
|
43
|
+
adcpVersion?: string;
|
|
44
|
+
servedAdcpVersion?: string;
|
|
43
45
|
}): ProposalVerificationResult;
|
|
44
|
-
export declare function assertRefineProposalsResponse(request: Pick<RefineProposalsRequest, 'refinements'
|
|
46
|
+
export declare function assertRefineProposalsResponse(request: Pick<RefineProposalsRequest, 'refinements'> & Partial<Pick<RefineProposalsRequest, 'adcp_version'>>, response: unknown, options?: {
|
|
45
47
|
now?: Date;
|
|
48
|
+
adcpVersion?: string;
|
|
49
|
+
servedAdcpVersion?: string;
|
|
46
50
|
}): asserts response is RefineProposalsCompletedResponse;
|
|
47
|
-
/** Validate
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
+
/** Validate a completed or compact-submitted wire shape using the selected bundle.
|
|
52
|
+
* Pass adcpVersion when validating a negotiated contract other than the SDK pin. */
|
|
53
|
+
export declare function validateRefineProposalsResponseShape(response: unknown, options?: {
|
|
54
|
+
adcpVersion?: string;
|
|
55
|
+
}): ProposalVerificationResult;
|
|
56
|
+
/** Assert a selected completed or compact-submitted wire shape without source semantics. */
|
|
57
|
+
export declare function assertRefineProposalsResponseShape(response: unknown, options?: {
|
|
58
|
+
adcpVersion?: string;
|
|
59
|
+
}): asserts response is RefineProposalsResponse;
|
|
@@ -25,6 +25,7 @@ __export(verification_exports, {
|
|
|
25
25
|
assertRefineProposalsResponseShape: () => assertRefineProposalsResponseShape,
|
|
26
26
|
isStrictDateTime: () => isStrictDateTime,
|
|
27
27
|
proposalTermsDigest: () => proposalTermsDigest,
|
|
28
|
+
selectRefinementSchemaVersion: () => selectRefinementSchemaVersion,
|
|
28
29
|
validateRefineProposalsResponseShape: () => validateRefineProposalsResponseShape,
|
|
29
30
|
verifyProposalCommercialTerms: () => verifyProposalCommercialTerms,
|
|
30
31
|
verifyProposalTermsDigest: () => verifyProposalTermsDigest,
|
|
@@ -36,9 +37,10 @@ var import_json_depth = require('../utils/json-depth.js');
|
|
|
36
37
|
var import_jcs = require('../utils/jcs.js');
|
|
37
38
|
var import_schema_loader = require('../validation/schema-loader.js');
|
|
38
39
|
var import_version = require('../version.js');
|
|
40
|
+
var import_errors = require('../errors/index.js');
|
|
41
|
+
var import_version2 = require('./version.js');
|
|
39
42
|
var import_commercial_terms_schema = require('./commercial-terms-schema.js');
|
|
40
43
|
var import_commercial_terms_keywords = require('./commercial-terms-keywords.js');
|
|
41
|
-
const ADCP_32_RELEASE = /^3\.2(?:-[A-Za-z0-9]+(?:[.-][A-Za-z0-9]+)*)?$/;
|
|
42
44
|
const MAX_COMMERCIAL_TERMS_BYTES = 256 * 1024;
|
|
43
45
|
const MAX_COMMERCIAL_TERMS_NODES = 5e4;
|
|
44
46
|
const MAX_COMMERCIAL_TERMS_MISMATCHES = 100;
|
|
@@ -296,7 +298,8 @@ function isStrictDateTime(value) {
|
|
|
296
298
|
}
|
|
297
299
|
function verifyRefineProposalsResponse(request, response, options = {}) {
|
|
298
300
|
const issues = [];
|
|
299
|
-
|
|
301
|
+
const adcpVersion = selectRefinementSchemaVersion(request.adcp_version, response, options, issues);
|
|
302
|
+
if (adcpVersion === void 0 || !validateResponseShape(response, issues, adcpVersion)) return { ok: false, issues };
|
|
300
303
|
if (response.status === "submitted") {
|
|
301
304
|
push(issues, "shape", "status", "semantic proposal verification requires a completed response");
|
|
302
305
|
return { ok: false, issues };
|
|
@@ -338,30 +341,126 @@ function assertRefineProposalsResponse(request, response, options = {}) {
|
|
|
338
341
|
const result = verifyRefineProposalsResponse(request, response, options);
|
|
339
342
|
if (!result.ok) throw new ProposalResponseVerificationError(result.issues);
|
|
340
343
|
}
|
|
341
|
-
function validateRefineProposalsResponseShape(response) {
|
|
344
|
+
function validateRefineProposalsResponseShape(response, options = {}) {
|
|
342
345
|
const issues = [];
|
|
343
|
-
validateResponseShape(
|
|
346
|
+
validateResponseShape(
|
|
347
|
+
response,
|
|
348
|
+
issues,
|
|
349
|
+
options.adcpVersion ?? import_version.ADCP_VERSION,
|
|
350
|
+
options.adcpVersion && (0, import_version.toReleasePrecisionVersion)(options.adcpVersion)
|
|
351
|
+
);
|
|
344
352
|
return { ok: issues.length === 0, issues };
|
|
345
353
|
}
|
|
346
|
-
function assertRefineProposalsResponseShape(response) {
|
|
347
|
-
const result = validateRefineProposalsResponseShape(response);
|
|
354
|
+
function assertRefineProposalsResponseShape(response, options = {}) {
|
|
355
|
+
const result = validateRefineProposalsResponseShape(response, options);
|
|
348
356
|
if (!result.ok) throw new ProposalResponseVerificationError(result.issues);
|
|
349
357
|
}
|
|
350
|
-
function
|
|
358
|
+
function selectRefinementSchemaVersion(requested, response, options, issues) {
|
|
359
|
+
const configured = options.adcpVersion ?? import_version.ADCP_VERSION;
|
|
360
|
+
const configuredWire = (0, import_version.toReleasePrecisionVersion)(configured);
|
|
361
|
+
if (!(0, import_version2.isProposalRefinementVersion)(configuredWire)) {
|
|
362
|
+
shape(issues, "adcp_version", `selected schema ${(0, import_version2.describeRefinementVersion)(configured)} must be AdCP 3.2 or 3.3`);
|
|
363
|
+
return void 0;
|
|
364
|
+
}
|
|
365
|
+
const ceiling = requested ?? configuredWire;
|
|
366
|
+
if (!(0, import_version2.isProposalRefinementVersion)(ceiling)) {
|
|
367
|
+
shape(
|
|
368
|
+
issues,
|
|
369
|
+
"adcp_version",
|
|
370
|
+
`request ceiling must be release-precision AdCP 3.2 or 3.3; received ${(0, import_version2.describeRefinementVersion)(ceiling)}`
|
|
371
|
+
);
|
|
372
|
+
return void 0;
|
|
373
|
+
}
|
|
374
|
+
const echo = isRecord(response) ? response.adcp_version : void 0;
|
|
375
|
+
if (echo !== void 0 && !(0, import_version2.isProposalRefinementVersion)(echo)) {
|
|
376
|
+
shape(
|
|
377
|
+
issues,
|
|
378
|
+
"adcp_version",
|
|
379
|
+
`response must echo release-precision AdCP 3.2 or 3.3 at or below request ceiling ${ceiling}; received ${(0, import_version2.describeRefinementVersion)(echo)}`
|
|
380
|
+
);
|
|
381
|
+
return void 0;
|
|
382
|
+
}
|
|
383
|
+
const metadata = options.servedAdcpVersion === void 0 ? void 0 : (0, import_version.toReleasePrecisionVersion)(options.servedAdcpVersion);
|
|
384
|
+
if (metadata !== void 0 && !(0, import_version2.isProposalRefinementVersion)(metadata)) {
|
|
385
|
+
shape(
|
|
386
|
+
issues,
|
|
387
|
+
"adcp_version",
|
|
388
|
+
`served version metadata must identify AdCP 3.2 or 3.3; received ${(0, import_version2.describeRefinementVersion)(options.servedAdcpVersion)}`
|
|
389
|
+
);
|
|
390
|
+
return void 0;
|
|
391
|
+
}
|
|
392
|
+
if (metadata !== void 0 && echo !== void 0 && metadata !== echo) {
|
|
393
|
+
shape(issues, "adcp_version", `response echo ${echo} conflicts with served version metadata ${metadata}`);
|
|
394
|
+
return void 0;
|
|
395
|
+
}
|
|
396
|
+
const served = echo ?? metadata;
|
|
397
|
+
if (served === void 0) return configured;
|
|
398
|
+
if ((0, import_version2.compareProposalRefinementVersions)(served, ceiling) > 0) {
|
|
399
|
+
shape(
|
|
400
|
+
issues,
|
|
401
|
+
"adcp_version",
|
|
402
|
+
`response served AdCP ${served} above request ceiling ${ceiling} (configured schema ${configured})`
|
|
403
|
+
);
|
|
404
|
+
return void 0;
|
|
405
|
+
}
|
|
406
|
+
const sameMinor = served.split("-")[0] === configuredWire.split("-")[0];
|
|
407
|
+
const wireOnlyOverride = ceiling !== configuredWire && ceiling.split("-")[0] === configuredWire.split("-")[0];
|
|
408
|
+
if (served === configuredWire || wireOnlyOverride && served === ceiling && sameMinor) return configured;
|
|
409
|
+
if ((0, import_version2.compareProposalRefinementVersions)(served, configuredWire) > 0) {
|
|
410
|
+
shape(
|
|
411
|
+
issues,
|
|
412
|
+
"adcp_version",
|
|
413
|
+
`response served AdCP ${served}, newer than configured schema ${configured}; set adcpVersion to the served contract and register its matching schema bundle`
|
|
414
|
+
);
|
|
415
|
+
return void 0;
|
|
416
|
+
}
|
|
417
|
+
return metadata === served && options.servedAdcpVersion ? options.servedAdcpVersion : served;
|
|
418
|
+
}
|
|
419
|
+
function validateResponseShape(value, issues, adcpVersion, expectedVersion) {
|
|
351
420
|
if (!isRecord(value)) {
|
|
352
421
|
push(issues, "shape", "response", "response must be an object");
|
|
353
422
|
return false;
|
|
354
423
|
}
|
|
355
424
|
let valid = allowedKeys(value, RESPONSE_KEYS, "response", issues);
|
|
356
|
-
if (value.adcp_version !== void 0 && (
|
|
425
|
+
if (value.adcp_version !== void 0 && !(0, import_version2.isProposalRefinementVersion)(value.adcp_version)) {
|
|
357
426
|
valid = shape(
|
|
358
427
|
issues,
|
|
359
428
|
"adcp_version",
|
|
360
|
-
`refine_proposals response adcp_version must be
|
|
429
|
+
`refine_proposals response adcp_version must be release-precision AdCP 3.2 or 3.3; received ${(0, import_version2.describeRefinementVersion)(
|
|
361
430
|
value.adcp_version
|
|
362
431
|
)}`
|
|
363
432
|
);
|
|
364
433
|
}
|
|
434
|
+
const selectedWireVersion = (0, import_version.toReleasePrecisionVersion)(adcpVersion);
|
|
435
|
+
if (!(0, import_version2.isProposalRefinementVersion)(selectedWireVersion)) {
|
|
436
|
+
return shape(
|
|
437
|
+
issues,
|
|
438
|
+
"adcp_version",
|
|
439
|
+
`selected schema version ${(0, import_version2.describeRefinementVersion)(adcpVersion)} must be AdCP 3.2 or 3.3`
|
|
440
|
+
);
|
|
441
|
+
}
|
|
442
|
+
if ((0, import_version2.isProposalRefinementVersion)(value.adcp_version) && (expectedVersion ? value.adcp_version !== expectedVersion : value.adcp_version.split("-")[0] !== selectedWireVersion.split("-")[0])) {
|
|
443
|
+
return shape(
|
|
444
|
+
issues,
|
|
445
|
+
"adcp_version",
|
|
446
|
+
`response adcp_version ${(0, import_version2.describeRefinementVersion)(value.adcp_version)} must match selected served schema ${adcpVersion}`
|
|
447
|
+
);
|
|
448
|
+
}
|
|
449
|
+
try {
|
|
450
|
+
const selected = (0, import_schema_loader.getSchemaDocumentByRef)("core/canonical-proposal.json", adcpVersion);
|
|
451
|
+
if (!selected) {
|
|
452
|
+
return shape(issues, "adcp_version", unavailableRefinementSchema(adcpVersion));
|
|
453
|
+
}
|
|
454
|
+
if ((0, import_version.toReleasePrecisionVersion)(selected.resolvedVersion) !== selectedWireVersion) {
|
|
455
|
+
return shape(
|
|
456
|
+
issues,
|
|
457
|
+
"adcp_version",
|
|
458
|
+
`selected proposal schema for AdCP ${adcpVersion} resolved to ${(0, import_version2.describeRefinementVersion)(selected.resolvedVersion)}; install or register the actual matching schema bundle`
|
|
459
|
+
);
|
|
460
|
+
}
|
|
461
|
+
} catch (error) {
|
|
462
|
+
return shape(issues, "adcp_version", unavailableRefinementSchema(adcpVersion, error));
|
|
463
|
+
}
|
|
365
464
|
if (value.message !== void 0 && (typeof value.message !== "string" || value.message.length > 2e3)) {
|
|
366
465
|
valid = shape(issues, "message", "message must be a string of at most 2000 characters");
|
|
367
466
|
}
|
|
@@ -393,7 +492,7 @@ function validateResponseShape(value, issues) {
|
|
|
393
492
|
valid = shape(issues, "results", "results must be a non-empty array");
|
|
394
493
|
} else {
|
|
395
494
|
value.results.forEach((result, index) => {
|
|
396
|
-
if (!validateResultShape(result, `results[${index}]`, issues)) valid = false;
|
|
495
|
+
if (!validateResultShape(result, `results[${index}]`, issues, adcpVersion)) valid = false;
|
|
397
496
|
});
|
|
398
497
|
}
|
|
399
498
|
if (!Array.isArray(value.products)) {
|
|
@@ -420,7 +519,7 @@ function validateAdvisoryErrors(value, issues) {
|
|
|
420
519
|
});
|
|
421
520
|
return valid;
|
|
422
521
|
}
|
|
423
|
-
function validateResultShape(value, path, issues) {
|
|
522
|
+
function validateResultShape(value, path, issues, adcpVersion) {
|
|
424
523
|
if (!isRecord(value)) return shape(issues, path, "result must be an object");
|
|
425
524
|
let valid = allowedKeys(value, RESULT_KEYS, path, issues);
|
|
426
525
|
if (!nonempty(value.source_proposal_id))
|
|
@@ -437,14 +536,14 @@ function validateResultShape(value, path, issues) {
|
|
|
437
536
|
};
|
|
438
537
|
if (outcome === "revised") {
|
|
439
538
|
forbidden(["proposal", "reason_code", "reason", "unsatisfied_constraints", "unsatisfied_product_changes"]);
|
|
440
|
-
if (!validateProposalArray(value.proposals, `${path}.proposals`, issues)) valid = false;
|
|
539
|
+
if (!validateProposalArray(value.proposals, `${path}.proposals`, issues, adcpVersion)) valid = false;
|
|
441
540
|
} else if (outcome === "partial") {
|
|
442
541
|
forbidden(["proposal"]);
|
|
443
|
-
if (!validateProposalArray(value.proposals, `${path}.proposals`, issues)) valid = false;
|
|
542
|
+
if (!validateProposalArray(value.proposals, `${path}.proposals`, issues, adcpVersion)) valid = false;
|
|
444
543
|
if (!validateReason(value, path, issues)) valid = false;
|
|
445
544
|
} else if (outcome === "finalized") {
|
|
446
545
|
forbidden(["proposals", "reason_code", "reason", "unsatisfied_constraints", "unsatisfied_product_changes"]);
|
|
447
|
-
if (!validateCanonicalProposalShape(value.proposal, `${path}.proposal`, issues)) valid = false;
|
|
546
|
+
if (!validateCanonicalProposalShape(value.proposal, `${path}.proposal`, issues, adcpVersion)) valid = false;
|
|
448
547
|
} else {
|
|
449
548
|
forbidden(["proposal", "proposals"]);
|
|
450
549
|
if (!validateReason(value, path, issues)) valid = false;
|
|
@@ -486,24 +585,39 @@ function validateReason(value, path, issues) {
|
|
|
486
585
|
if (!nonempty(value.reason)) valid = shape(issues, `${path}.reason`, "reason must be a non-empty string");
|
|
487
586
|
return valid;
|
|
488
587
|
}
|
|
489
|
-
function validateProposalArray(value, path, issues) {
|
|
588
|
+
function validateProposalArray(value, path, issues, adcpVersion) {
|
|
490
589
|
if (!Array.isArray(value) || value.length === 0) return shape(issues, path, "proposals must be a non-empty array");
|
|
491
590
|
let valid = true;
|
|
492
591
|
value.forEach((proposal, index) => {
|
|
493
|
-
if (!validateCanonicalProposalShape(proposal, `${path}[${index}]`, issues)) valid = false;
|
|
592
|
+
if (!validateCanonicalProposalShape(proposal, `${path}[${index}]`, issues, adcpVersion)) valid = false;
|
|
494
593
|
});
|
|
495
594
|
return valid;
|
|
496
595
|
}
|
|
497
|
-
function
|
|
498
|
-
const
|
|
499
|
-
|
|
596
|
+
function unavailableRefinementSchema(adcpVersion, error) {
|
|
597
|
+
const cause = error instanceof import_errors.ConfigurationError ? error.message.includes("Schema validation requires unambiguous schema identities") ? " (ConfigurationError: schema validation requires unambiguous schema identities)" : " (ConfigurationError)" : error === void 0 ? "" : error instanceof Error && /^[A-Za-z][A-Za-z0-9_]{0,63}$/.test(error.name) ? ` (${error.name})` : " (schema loading error)";
|
|
598
|
+
return `selected proposal schema for AdCP ${adcpVersion} is unavailable${cause}; install its schema bundle or register a matching external schema root with @adcp/sdk/testing`;
|
|
599
|
+
}
|
|
600
|
+
function getRefinementSchemaValidator(schemaRef, adcpVersion) {
|
|
601
|
+
try {
|
|
602
|
+
const validator = (0, import_schema_loader.getSchemaValidatorByRef)(schemaRef, adcpVersion);
|
|
603
|
+
return { validator, error: validator ? void 0 : unavailableRefinementSchema(adcpVersion) };
|
|
604
|
+
} catch (error) {
|
|
605
|
+
return { validator: void 0, error: unavailableRefinementSchema(adcpVersion, error) };
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
function validateCanonicalProposalShape(value, path, issues, adcpVersion) {
|
|
609
|
+
const { validator: validateCanonicalProposal, error: schemaError } = getRefinementSchemaValidator(
|
|
610
|
+
"core/canonical-proposal.json",
|
|
611
|
+
adcpVersion
|
|
612
|
+
);
|
|
613
|
+
if (!validateCanonicalProposal) return shape(issues, path, schemaError);
|
|
500
614
|
if (!validateCanonicalProposal(value)) {
|
|
501
615
|
const error = validateCanonicalProposal.errors?.[0];
|
|
502
616
|
const errorPath = error?.instancePath ? `${path}${error.instancePath.replaceAll("/", ".")}` : path;
|
|
503
617
|
return shape(
|
|
504
618
|
issues,
|
|
505
619
|
errorPath,
|
|
506
|
-
`proposal must satisfy the AdCP
|
|
620
|
+
`proposal must satisfy the selected AdCP ${adcpVersion} schema${error?.message ? `: ${error.message}` : ""}`
|
|
507
621
|
);
|
|
508
622
|
}
|
|
509
623
|
const proposal = value;
|
|
@@ -547,10 +661,11 @@ function validateCanonicalProposalShape(value, path, issues) {
|
|
|
547
661
|
if (proposal.proposal_status === "committed" && !isStrictDateTime(proposal.expires_at)) {
|
|
548
662
|
valid = shape(issues, `${path}.expires_at`, "committed proposals require expires_at");
|
|
549
663
|
}
|
|
550
|
-
if (!validateCommercialTerms(proposal.commercial_terms, `${path}.commercial_terms`, issues))
|
|
664
|
+
if (!validateCommercialTerms(proposal.commercial_terms, `${path}.commercial_terms`, issues, adcpVersion))
|
|
665
|
+
valid = false;
|
|
551
666
|
return valid;
|
|
552
667
|
}
|
|
553
|
-
function validateCommercialTerms(value, path, issues) {
|
|
668
|
+
function validateCommercialTerms(value, path, issues, adcpVersion) {
|
|
554
669
|
if (!isRecord(value)) return shape(issues, path, "commercial_terms must be an object");
|
|
555
670
|
let valid = true;
|
|
556
671
|
if (!isRecord(value.brand)) valid = shape(issues, `${path}.brand`, "brand must be an object");
|
|
@@ -604,7 +719,7 @@ function validateCommercialTerms(value, path, issues) {
|
|
|
604
719
|
valid = shape(issues, `${path}.reporting_commitments`, "reporting_commitments must be a non-empty array");
|
|
605
720
|
if (value.cancellation_terms !== void 0 && !validateCancellationTerms(value.cancellation_terms, `${path}.cancellation_terms`, issues))
|
|
606
721
|
valid = false;
|
|
607
|
-
if (value.change_terms !== void 0 && !validateChangeTerms(value.change_terms, `${path}.change_terms`, issues, purchaseCurrency))
|
|
722
|
+
if (value.change_terms !== void 0 && !validateChangeTerms(value.change_terms, `${path}.change_terms`, issues, adcpVersion, purchaseCurrency))
|
|
608
723
|
valid = false;
|
|
609
724
|
return valid;
|
|
610
725
|
}
|
|
@@ -620,10 +735,13 @@ const CHANGE_TERM_ACTIONS_BY_CONSTRAINT = {
|
|
|
620
735
|
package_count: /* @__PURE__ */ new Set(["add_packages", "remove_packages"]),
|
|
621
736
|
effective_timing: /* @__PURE__ */ new Set(["pause", "resume", "cancel"])
|
|
622
737
|
};
|
|
623
|
-
function validateChangeTerms(value, path, issues, purchaseCurrency) {
|
|
738
|
+
function validateChangeTerms(value, path, issues, adcpVersion, purchaseCurrency) {
|
|
624
739
|
if (!Array.isArray(value) || value.length === 0) return shape(issues, path, "change_terms must be a non-empty array");
|
|
625
|
-
const validateChangeTerm = (
|
|
626
|
-
|
|
740
|
+
const { validator: validateChangeTerm, error: schemaError } = getRefinementSchemaValidator(
|
|
741
|
+
"media-buy/change-term.json",
|
|
742
|
+
adcpVersion
|
|
743
|
+
);
|
|
744
|
+
if (!validateChangeTerm) return shape(issues, path, schemaError);
|
|
627
745
|
let valid = true;
|
|
628
746
|
const actions = /* @__PURE__ */ new Set();
|
|
629
747
|
value.forEach((term, index) => {
|
|
@@ -634,7 +752,7 @@ function validateChangeTerms(value, path, issues, purchaseCurrency) {
|
|
|
634
752
|
valid = shape(
|
|
635
753
|
issues,
|
|
636
754
|
errorPath,
|
|
637
|
-
`change term must satisfy the AdCP
|
|
755
|
+
`change term must satisfy the selected AdCP ${adcpVersion} schema${error?.message ? `: ${error.message}` : ""}`
|
|
638
756
|
);
|
|
639
757
|
return;
|
|
640
758
|
}
|
|
@@ -1341,6 +1459,7 @@ function push(issues, code, path, message) {
|
|
|
1341
1459
|
assertRefineProposalsResponseShape,
|
|
1342
1460
|
isStrictDateTime,
|
|
1343
1461
|
proposalTermsDigest,
|
|
1462
|
+
selectRefinementSchemaVersion,
|
|
1344
1463
|
validateRefineProposalsResponseShape,
|
|
1345
1464
|
verifyProposalCommercialTerms,
|
|
1346
1465
|
verifyProposalTermsDigest,
|
|
@@ -2,10 +2,11 @@ import { createHash } from "node:crypto";
|
|
|
2
2
|
import { MAX_JSON_DEPTH } from "../utils/json-depth.mjs";
|
|
3
3
|
import { canonicalize } from "../utils/jcs.mjs";
|
|
4
4
|
import { getSchemaDocumentByRef, getSchemaValidatorByRef } from "../validation/schema-loader.mjs";
|
|
5
|
-
import { ADCP_VERSION } from "../version.mjs";
|
|
5
|
+
import { ADCP_VERSION, toReleasePrecisionVersion } from "../version.mjs";
|
|
6
|
+
import { ConfigurationError } from "../errors/index.mjs";
|
|
7
|
+
import { compareProposalRefinementVersions, describeRefinementVersion, isProposalRefinementVersion } from "./version.mjs";
|
|
6
8
|
import { commercialTermsSchemaSupportError } from "./commercial-terms-schema.mjs";
|
|
7
9
|
import { COMMERCIAL_TERMS_KEYWORDS } from "./commercial-terms-keywords.mjs";
|
|
8
|
-
const ADCP_32_RELEASE = /^3\.2(?:-[A-Za-z0-9]+(?:[.-][A-Za-z0-9]+)*)?$/;
|
|
9
10
|
const MAX_COMMERCIAL_TERMS_BYTES = 256 * 1024;
|
|
10
11
|
const MAX_COMMERCIAL_TERMS_NODES = 5e4;
|
|
11
12
|
const MAX_COMMERCIAL_TERMS_MISMATCHES = 100;
|
|
@@ -263,7 +264,8 @@ function isStrictDateTime(value) {
|
|
|
263
264
|
}
|
|
264
265
|
function verifyRefineProposalsResponse(request, response, options = {}) {
|
|
265
266
|
const issues = [];
|
|
266
|
-
|
|
267
|
+
const adcpVersion = selectRefinementSchemaVersion(request.adcp_version, response, options, issues);
|
|
268
|
+
if (adcpVersion === void 0 || !validateResponseShape(response, issues, adcpVersion)) return { ok: false, issues };
|
|
267
269
|
if (response.status === "submitted") {
|
|
268
270
|
push(issues, "shape", "status", "semantic proposal verification requires a completed response");
|
|
269
271
|
return { ok: false, issues };
|
|
@@ -305,30 +307,126 @@ function assertRefineProposalsResponse(request, response, options = {}) {
|
|
|
305
307
|
const result = verifyRefineProposalsResponse(request, response, options);
|
|
306
308
|
if (!result.ok) throw new ProposalResponseVerificationError(result.issues);
|
|
307
309
|
}
|
|
308
|
-
function validateRefineProposalsResponseShape(response) {
|
|
310
|
+
function validateRefineProposalsResponseShape(response, options = {}) {
|
|
309
311
|
const issues = [];
|
|
310
|
-
validateResponseShape(
|
|
312
|
+
validateResponseShape(
|
|
313
|
+
response,
|
|
314
|
+
issues,
|
|
315
|
+
options.adcpVersion ?? ADCP_VERSION,
|
|
316
|
+
options.adcpVersion && toReleasePrecisionVersion(options.adcpVersion)
|
|
317
|
+
);
|
|
311
318
|
return { ok: issues.length === 0, issues };
|
|
312
319
|
}
|
|
313
|
-
function assertRefineProposalsResponseShape(response) {
|
|
314
|
-
const result = validateRefineProposalsResponseShape(response);
|
|
320
|
+
function assertRefineProposalsResponseShape(response, options = {}) {
|
|
321
|
+
const result = validateRefineProposalsResponseShape(response, options);
|
|
315
322
|
if (!result.ok) throw new ProposalResponseVerificationError(result.issues);
|
|
316
323
|
}
|
|
317
|
-
function
|
|
324
|
+
function selectRefinementSchemaVersion(requested, response, options, issues) {
|
|
325
|
+
const configured = options.adcpVersion ?? ADCP_VERSION;
|
|
326
|
+
const configuredWire = toReleasePrecisionVersion(configured);
|
|
327
|
+
if (!isProposalRefinementVersion(configuredWire)) {
|
|
328
|
+
shape(issues, "adcp_version", `selected schema ${describeRefinementVersion(configured)} must be AdCP 3.2 or 3.3`);
|
|
329
|
+
return void 0;
|
|
330
|
+
}
|
|
331
|
+
const ceiling = requested ?? configuredWire;
|
|
332
|
+
if (!isProposalRefinementVersion(ceiling)) {
|
|
333
|
+
shape(
|
|
334
|
+
issues,
|
|
335
|
+
"adcp_version",
|
|
336
|
+
`request ceiling must be release-precision AdCP 3.2 or 3.3; received ${describeRefinementVersion(ceiling)}`
|
|
337
|
+
);
|
|
338
|
+
return void 0;
|
|
339
|
+
}
|
|
340
|
+
const echo = isRecord(response) ? response.adcp_version : void 0;
|
|
341
|
+
if (echo !== void 0 && !isProposalRefinementVersion(echo)) {
|
|
342
|
+
shape(
|
|
343
|
+
issues,
|
|
344
|
+
"adcp_version",
|
|
345
|
+
`response must echo release-precision AdCP 3.2 or 3.3 at or below request ceiling ${ceiling}; received ${describeRefinementVersion(echo)}`
|
|
346
|
+
);
|
|
347
|
+
return void 0;
|
|
348
|
+
}
|
|
349
|
+
const metadata = options.servedAdcpVersion === void 0 ? void 0 : toReleasePrecisionVersion(options.servedAdcpVersion);
|
|
350
|
+
if (metadata !== void 0 && !isProposalRefinementVersion(metadata)) {
|
|
351
|
+
shape(
|
|
352
|
+
issues,
|
|
353
|
+
"adcp_version",
|
|
354
|
+
`served version metadata must identify AdCP 3.2 or 3.3; received ${describeRefinementVersion(options.servedAdcpVersion)}`
|
|
355
|
+
);
|
|
356
|
+
return void 0;
|
|
357
|
+
}
|
|
358
|
+
if (metadata !== void 0 && echo !== void 0 && metadata !== echo) {
|
|
359
|
+
shape(issues, "adcp_version", `response echo ${echo} conflicts with served version metadata ${metadata}`);
|
|
360
|
+
return void 0;
|
|
361
|
+
}
|
|
362
|
+
const served = echo ?? metadata;
|
|
363
|
+
if (served === void 0) return configured;
|
|
364
|
+
if (compareProposalRefinementVersions(served, ceiling) > 0) {
|
|
365
|
+
shape(
|
|
366
|
+
issues,
|
|
367
|
+
"adcp_version",
|
|
368
|
+
`response served AdCP ${served} above request ceiling ${ceiling} (configured schema ${configured})`
|
|
369
|
+
);
|
|
370
|
+
return void 0;
|
|
371
|
+
}
|
|
372
|
+
const sameMinor = served.split("-")[0] === configuredWire.split("-")[0];
|
|
373
|
+
const wireOnlyOverride = ceiling !== configuredWire && ceiling.split("-")[0] === configuredWire.split("-")[0];
|
|
374
|
+
if (served === configuredWire || wireOnlyOverride && served === ceiling && sameMinor) return configured;
|
|
375
|
+
if (compareProposalRefinementVersions(served, configuredWire) > 0) {
|
|
376
|
+
shape(
|
|
377
|
+
issues,
|
|
378
|
+
"adcp_version",
|
|
379
|
+
`response served AdCP ${served}, newer than configured schema ${configured}; set adcpVersion to the served contract and register its matching schema bundle`
|
|
380
|
+
);
|
|
381
|
+
return void 0;
|
|
382
|
+
}
|
|
383
|
+
return metadata === served && options.servedAdcpVersion ? options.servedAdcpVersion : served;
|
|
384
|
+
}
|
|
385
|
+
function validateResponseShape(value, issues, adcpVersion, expectedVersion) {
|
|
318
386
|
if (!isRecord(value)) {
|
|
319
387
|
push(issues, "shape", "response", "response must be an object");
|
|
320
388
|
return false;
|
|
321
389
|
}
|
|
322
390
|
let valid = allowedKeys(value, RESPONSE_KEYS, "response", issues);
|
|
323
|
-
if (value.adcp_version !== void 0 &&
|
|
391
|
+
if (value.adcp_version !== void 0 && !isProposalRefinementVersion(value.adcp_version)) {
|
|
324
392
|
valid = shape(
|
|
325
393
|
issues,
|
|
326
394
|
"adcp_version",
|
|
327
|
-
`refine_proposals response adcp_version must be
|
|
395
|
+
`refine_proposals response adcp_version must be release-precision AdCP 3.2 or 3.3; received ${describeRefinementVersion(
|
|
328
396
|
value.adcp_version
|
|
329
397
|
)}`
|
|
330
398
|
);
|
|
331
399
|
}
|
|
400
|
+
const selectedWireVersion = toReleasePrecisionVersion(adcpVersion);
|
|
401
|
+
if (!isProposalRefinementVersion(selectedWireVersion)) {
|
|
402
|
+
return shape(
|
|
403
|
+
issues,
|
|
404
|
+
"adcp_version",
|
|
405
|
+
`selected schema version ${describeRefinementVersion(adcpVersion)} must be AdCP 3.2 or 3.3`
|
|
406
|
+
);
|
|
407
|
+
}
|
|
408
|
+
if (isProposalRefinementVersion(value.adcp_version) && (expectedVersion ? value.adcp_version !== expectedVersion : value.adcp_version.split("-")[0] !== selectedWireVersion.split("-")[0])) {
|
|
409
|
+
return shape(
|
|
410
|
+
issues,
|
|
411
|
+
"adcp_version",
|
|
412
|
+
`response adcp_version ${describeRefinementVersion(value.adcp_version)} must match selected served schema ${adcpVersion}`
|
|
413
|
+
);
|
|
414
|
+
}
|
|
415
|
+
try {
|
|
416
|
+
const selected = getSchemaDocumentByRef("core/canonical-proposal.json", adcpVersion);
|
|
417
|
+
if (!selected) {
|
|
418
|
+
return shape(issues, "adcp_version", unavailableRefinementSchema(adcpVersion));
|
|
419
|
+
}
|
|
420
|
+
if (toReleasePrecisionVersion(selected.resolvedVersion) !== selectedWireVersion) {
|
|
421
|
+
return shape(
|
|
422
|
+
issues,
|
|
423
|
+
"adcp_version",
|
|
424
|
+
`selected proposal schema for AdCP ${adcpVersion} resolved to ${describeRefinementVersion(selected.resolvedVersion)}; install or register the actual matching schema bundle`
|
|
425
|
+
);
|
|
426
|
+
}
|
|
427
|
+
} catch (error) {
|
|
428
|
+
return shape(issues, "adcp_version", unavailableRefinementSchema(adcpVersion, error));
|
|
429
|
+
}
|
|
332
430
|
if (value.message !== void 0 && (typeof value.message !== "string" || value.message.length > 2e3)) {
|
|
333
431
|
valid = shape(issues, "message", "message must be a string of at most 2000 characters");
|
|
334
432
|
}
|
|
@@ -360,7 +458,7 @@ function validateResponseShape(value, issues) {
|
|
|
360
458
|
valid = shape(issues, "results", "results must be a non-empty array");
|
|
361
459
|
} else {
|
|
362
460
|
value.results.forEach((result, index) => {
|
|
363
|
-
if (!validateResultShape(result, `results[${index}]`, issues)) valid = false;
|
|
461
|
+
if (!validateResultShape(result, `results[${index}]`, issues, adcpVersion)) valid = false;
|
|
364
462
|
});
|
|
365
463
|
}
|
|
366
464
|
if (!Array.isArray(value.products)) {
|
|
@@ -387,7 +485,7 @@ function validateAdvisoryErrors(value, issues) {
|
|
|
387
485
|
});
|
|
388
486
|
return valid;
|
|
389
487
|
}
|
|
390
|
-
function validateResultShape(value, path, issues) {
|
|
488
|
+
function validateResultShape(value, path, issues, adcpVersion) {
|
|
391
489
|
if (!isRecord(value)) return shape(issues, path, "result must be an object");
|
|
392
490
|
let valid = allowedKeys(value, RESULT_KEYS, path, issues);
|
|
393
491
|
if (!nonempty(value.source_proposal_id))
|
|
@@ -404,14 +502,14 @@ function validateResultShape(value, path, issues) {
|
|
|
404
502
|
};
|
|
405
503
|
if (outcome === "revised") {
|
|
406
504
|
forbidden(["proposal", "reason_code", "reason", "unsatisfied_constraints", "unsatisfied_product_changes"]);
|
|
407
|
-
if (!validateProposalArray(value.proposals, `${path}.proposals`, issues)) valid = false;
|
|
505
|
+
if (!validateProposalArray(value.proposals, `${path}.proposals`, issues, adcpVersion)) valid = false;
|
|
408
506
|
} else if (outcome === "partial") {
|
|
409
507
|
forbidden(["proposal"]);
|
|
410
|
-
if (!validateProposalArray(value.proposals, `${path}.proposals`, issues)) valid = false;
|
|
508
|
+
if (!validateProposalArray(value.proposals, `${path}.proposals`, issues, adcpVersion)) valid = false;
|
|
411
509
|
if (!validateReason(value, path, issues)) valid = false;
|
|
412
510
|
} else if (outcome === "finalized") {
|
|
413
511
|
forbidden(["proposals", "reason_code", "reason", "unsatisfied_constraints", "unsatisfied_product_changes"]);
|
|
414
|
-
if (!validateCanonicalProposalShape(value.proposal, `${path}.proposal`, issues)) valid = false;
|
|
512
|
+
if (!validateCanonicalProposalShape(value.proposal, `${path}.proposal`, issues, adcpVersion)) valid = false;
|
|
415
513
|
} else {
|
|
416
514
|
forbidden(["proposal", "proposals"]);
|
|
417
515
|
if (!validateReason(value, path, issues)) valid = false;
|
|
@@ -453,24 +551,39 @@ function validateReason(value, path, issues) {
|
|
|
453
551
|
if (!nonempty(value.reason)) valid = shape(issues, `${path}.reason`, "reason must be a non-empty string");
|
|
454
552
|
return valid;
|
|
455
553
|
}
|
|
456
|
-
function validateProposalArray(value, path, issues) {
|
|
554
|
+
function validateProposalArray(value, path, issues, adcpVersion) {
|
|
457
555
|
if (!Array.isArray(value) || value.length === 0) return shape(issues, path, "proposals must be a non-empty array");
|
|
458
556
|
let valid = true;
|
|
459
557
|
value.forEach((proposal, index) => {
|
|
460
|
-
if (!validateCanonicalProposalShape(proposal, `${path}[${index}]`, issues)) valid = false;
|
|
558
|
+
if (!validateCanonicalProposalShape(proposal, `${path}[${index}]`, issues, adcpVersion)) valid = false;
|
|
461
559
|
});
|
|
462
560
|
return valid;
|
|
463
561
|
}
|
|
464
|
-
function
|
|
465
|
-
const
|
|
466
|
-
|
|
562
|
+
function unavailableRefinementSchema(adcpVersion, error) {
|
|
563
|
+
const cause = error instanceof ConfigurationError ? error.message.includes("Schema validation requires unambiguous schema identities") ? " (ConfigurationError: schema validation requires unambiguous schema identities)" : " (ConfigurationError)" : error === void 0 ? "" : error instanceof Error && /^[A-Za-z][A-Za-z0-9_]{0,63}$/.test(error.name) ? ` (${error.name})` : " (schema loading error)";
|
|
564
|
+
return `selected proposal schema for AdCP ${adcpVersion} is unavailable${cause}; install its schema bundle or register a matching external schema root with @adcp/sdk/testing`;
|
|
565
|
+
}
|
|
566
|
+
function getRefinementSchemaValidator(schemaRef, adcpVersion) {
|
|
567
|
+
try {
|
|
568
|
+
const validator = getSchemaValidatorByRef(schemaRef, adcpVersion);
|
|
569
|
+
return { validator, error: validator ? void 0 : unavailableRefinementSchema(adcpVersion) };
|
|
570
|
+
} catch (error) {
|
|
571
|
+
return { validator: void 0, error: unavailableRefinementSchema(adcpVersion, error) };
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
function validateCanonicalProposalShape(value, path, issues, adcpVersion) {
|
|
575
|
+
const { validator: validateCanonicalProposal, error: schemaError } = getRefinementSchemaValidator(
|
|
576
|
+
"core/canonical-proposal.json",
|
|
577
|
+
adcpVersion
|
|
578
|
+
);
|
|
579
|
+
if (!validateCanonicalProposal) return shape(issues, path, schemaError);
|
|
467
580
|
if (!validateCanonicalProposal(value)) {
|
|
468
581
|
const error = validateCanonicalProposal.errors?.[0];
|
|
469
582
|
const errorPath = error?.instancePath ? `${path}${error.instancePath.replaceAll("/", ".")}` : path;
|
|
470
583
|
return shape(
|
|
471
584
|
issues,
|
|
472
585
|
errorPath,
|
|
473
|
-
`proposal must satisfy the AdCP
|
|
586
|
+
`proposal must satisfy the selected AdCP ${adcpVersion} schema${error?.message ? `: ${error.message}` : ""}`
|
|
474
587
|
);
|
|
475
588
|
}
|
|
476
589
|
const proposal = value;
|
|
@@ -514,10 +627,11 @@ function validateCanonicalProposalShape(value, path, issues) {
|
|
|
514
627
|
if (proposal.proposal_status === "committed" && !isStrictDateTime(proposal.expires_at)) {
|
|
515
628
|
valid = shape(issues, `${path}.expires_at`, "committed proposals require expires_at");
|
|
516
629
|
}
|
|
517
|
-
if (!validateCommercialTerms(proposal.commercial_terms, `${path}.commercial_terms`, issues))
|
|
630
|
+
if (!validateCommercialTerms(proposal.commercial_terms, `${path}.commercial_terms`, issues, adcpVersion))
|
|
631
|
+
valid = false;
|
|
518
632
|
return valid;
|
|
519
633
|
}
|
|
520
|
-
function validateCommercialTerms(value, path, issues) {
|
|
634
|
+
function validateCommercialTerms(value, path, issues, adcpVersion) {
|
|
521
635
|
if (!isRecord(value)) return shape(issues, path, "commercial_terms must be an object");
|
|
522
636
|
let valid = true;
|
|
523
637
|
if (!isRecord(value.brand)) valid = shape(issues, `${path}.brand`, "brand must be an object");
|
|
@@ -571,7 +685,7 @@ function validateCommercialTerms(value, path, issues) {
|
|
|
571
685
|
valid = shape(issues, `${path}.reporting_commitments`, "reporting_commitments must be a non-empty array");
|
|
572
686
|
if (value.cancellation_terms !== void 0 && !validateCancellationTerms(value.cancellation_terms, `${path}.cancellation_terms`, issues))
|
|
573
687
|
valid = false;
|
|
574
|
-
if (value.change_terms !== void 0 && !validateChangeTerms(value.change_terms, `${path}.change_terms`, issues, purchaseCurrency))
|
|
688
|
+
if (value.change_terms !== void 0 && !validateChangeTerms(value.change_terms, `${path}.change_terms`, issues, adcpVersion, purchaseCurrency))
|
|
575
689
|
valid = false;
|
|
576
690
|
return valid;
|
|
577
691
|
}
|
|
@@ -587,10 +701,13 @@ const CHANGE_TERM_ACTIONS_BY_CONSTRAINT = {
|
|
|
587
701
|
package_count: /* @__PURE__ */ new Set(["add_packages", "remove_packages"]),
|
|
588
702
|
effective_timing: /* @__PURE__ */ new Set(["pause", "resume", "cancel"])
|
|
589
703
|
};
|
|
590
|
-
function validateChangeTerms(value, path, issues, purchaseCurrency) {
|
|
704
|
+
function validateChangeTerms(value, path, issues, adcpVersion, purchaseCurrency) {
|
|
591
705
|
if (!Array.isArray(value) || value.length === 0) return shape(issues, path, "change_terms must be a non-empty array");
|
|
592
|
-
const validateChangeTerm =
|
|
593
|
-
|
|
706
|
+
const { validator: validateChangeTerm, error: schemaError } = getRefinementSchemaValidator(
|
|
707
|
+
"media-buy/change-term.json",
|
|
708
|
+
adcpVersion
|
|
709
|
+
);
|
|
710
|
+
if (!validateChangeTerm) return shape(issues, path, schemaError);
|
|
594
711
|
let valid = true;
|
|
595
712
|
const actions = /* @__PURE__ */ new Set();
|
|
596
713
|
value.forEach((term, index) => {
|
|
@@ -601,7 +718,7 @@ function validateChangeTerms(value, path, issues, purchaseCurrency) {
|
|
|
601
718
|
valid = shape(
|
|
602
719
|
issues,
|
|
603
720
|
errorPath,
|
|
604
|
-
`change term must satisfy the AdCP
|
|
721
|
+
`change term must satisfy the selected AdCP ${adcpVersion} schema${error?.message ? `: ${error.message}` : ""}`
|
|
605
722
|
);
|
|
606
723
|
return;
|
|
607
724
|
}
|
|
@@ -1307,6 +1424,7 @@ export {
|
|
|
1307
1424
|
assertRefineProposalsResponseShape,
|
|
1308
1425
|
isStrictDateTime,
|
|
1309
1426
|
proposalTermsDigest,
|
|
1427
|
+
selectRefinementSchemaVersion,
|
|
1310
1428
|
validateRefineProposalsResponseShape,
|
|
1311
1429
|
verifyProposalCommercialTerms,
|
|
1312
1430
|
verifyProposalTermsDigest,
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { ProposalRefinementVersion } from './types.mjs';
|
|
2
|
+
/** Supported wire releases. Keep this guard and the public type in sync. */
|
|
3
|
+
export declare function isProposalRefinementVersion(value: unknown): value is ProposalRefinementVersion;
|
|
4
|
+
/** Inputs have already passed the release-precision guard above. */
|
|
5
|
+
export declare function compareProposalRefinementVersions(left: ProposalRefinementVersion, right: ProposalRefinementVersion): number;
|
|
6
|
+
export declare function describeRefinementVersion(value: unknown): string;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { ProposalRefinementVersion } from './types';
|
|
2
|
+
/** Supported wire releases. Keep this guard and the public type in sync. */
|
|
3
|
+
export declare function isProposalRefinementVersion(value: unknown): value is ProposalRefinementVersion;
|
|
4
|
+
/** Inputs have already passed the release-precision guard above. */
|
|
5
|
+
export declare function compareProposalRefinementVersions(left: ProposalRefinementVersion, right: ProposalRefinementVersion): number;
|
|
6
|
+
export declare function describeRefinementVersion(value: unknown): string;
|