@adcp/sdk 13.0.0-rc.10 → 13.0.0-rc.13
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/dist/lib/core/AgentClient.d.mts +12 -3
- package/dist/lib/core/AgentClient.d.ts +12 -3
- package/dist/lib/core/AgentClient.d.ts.map +1 -1
- package/dist/lib/core/AgentClient.js +12 -3
- package/dist/lib/core/AgentClient.js.map +1 -1
- package/dist/lib/core/AgentClient.mjs +12 -3
- package/dist/lib/core/AgentClient.mjs.map +1 -1
- package/dist/lib/core/SingleAgentClient.d.mts +13 -3
- package/dist/lib/core/SingleAgentClient.d.ts +13 -3
- package/dist/lib/core/SingleAgentClient.d.ts.map +1 -1
- package/dist/lib/core/SingleAgentClient.js +52 -31
- package/dist/lib/core/SingleAgentClient.js.map +1 -1
- package/dist/lib/core/SingleAgentClient.mjs +52 -31
- package/dist/lib/core/SingleAgentClient.mjs.map +1 -1
- package/dist/lib/index.d.mts +1 -1
- package/dist/lib/index.d.ts +1 -1
- package/dist/lib/index.d.ts.map +1 -1
- package/dist/lib/index.js +8 -0
- package/dist/lib/index.js.map +1 -1
- package/dist/lib/index.mjs +8 -0
- package/dist/lib/index.mjs.map +1 -1
- package/dist/lib/registry/types.generated.d.mts +48 -1
- package/dist/lib/registry/types.generated.d.ts +48 -1
- package/dist/lib/registry/types.generated.d.ts.map +1 -1
- package/dist/lib/registry/types.generated.js.map +1 -1
- package/dist/lib/schemas-data/v2.5/_provenance.json +1 -1
- package/dist/lib/server/decisioning/runtime/from-platform.d.mts +1 -1
- package/dist/lib/server/decisioning/runtime/from-platform.d.ts +1 -1
- package/dist/lib/server/decisioning/runtime/from-platform.d.ts.map +1 -1
- package/dist/lib/server/decisioning/runtime/from-platform.js +389 -65
- package/dist/lib/server/decisioning/runtime/from-platform.js.map +1 -1
- package/dist/lib/server/decisioning/runtime/from-platform.mjs +389 -65
- package/dist/lib/server/decisioning/runtime/from-platform.mjs.map +1 -1
- package/dist/lib/testing/compliance/comply.d.ts.map +1 -1
- package/dist/lib/testing/compliance/comply.js +8 -2
- package/dist/lib/testing/compliance/comply.js.map +1 -1
- package/dist/lib/testing/compliance/comply.mjs +8 -2
- package/dist/lib/testing/compliance/comply.mjs.map +1 -1
- package/dist/lib/testing/compliance/spec-conformance.d.ts.map +1 -1
- package/dist/lib/testing/compliance/spec-conformance.js +2 -0
- package/dist/lib/testing/compliance/spec-conformance.js.map +1 -1
- package/dist/lib/testing/compliance/spec-conformance.mjs +2 -0
- package/dist/lib/testing/compliance/spec-conformance.mjs.map +1 -1
- package/dist/lib/testing/compliance/summary.d.mts +2 -0
- package/dist/lib/testing/compliance/summary.d.ts +2 -0
- package/dist/lib/testing/compliance/summary.d.ts.map +1 -1
- package/dist/lib/testing/compliance/summary.js +3 -2
- package/dist/lib/testing/compliance/summary.js.map +1 -1
- package/dist/lib/testing/compliance/summary.mjs +3 -2
- package/dist/lib/testing/compliance/summary.mjs.map +1 -1
- package/dist/lib/testing/compliance/types.d.mts +4 -0
- package/dist/lib/testing/compliance/types.d.ts +4 -0
- package/dist/lib/testing/compliance/types.d.ts.map +1 -1
- package/dist/lib/testing/compliance/types.js.map +1 -1
- package/dist/lib/testing/storyboard/junit.js +49 -32
- package/dist/lib/testing/storyboard/junit.js.map +1 -1
- package/dist/lib/testing/storyboard/junit.mjs +49 -32
- package/dist/lib/testing/storyboard/junit.mjs.map +1 -1
- package/dist/lib/testing/storyboard/loader.d.ts.map +1 -1
- package/dist/lib/testing/storyboard/loader.js +26 -0
- package/dist/lib/testing/storyboard/loader.js.map +1 -1
- package/dist/lib/testing/storyboard/loader.mjs +26 -0
- package/dist/lib/testing/storyboard/loader.mjs.map +1 -1
- package/dist/lib/testing/storyboard/runner.d.ts.map +1 -1
- package/dist/lib/testing/storyboard/runner.js +66 -15
- package/dist/lib/testing/storyboard/runner.js.map +1 -1
- package/dist/lib/testing/storyboard/runner.mjs +70 -16
- package/dist/lib/testing/storyboard/runner.mjs.map +1 -1
- package/dist/lib/testing/storyboard/types.d.mts +26 -1
- package/dist/lib/testing/storyboard/types.d.ts +26 -1
- package/dist/lib/testing/storyboard/types.d.ts.map +1 -1
- package/dist/lib/testing/storyboard/types.js.map +1 -1
- package/dist/lib/testing/storyboard/types.mjs.map +1 -1
- package/dist/lib/testing/storyboard/validations.d.mts +11 -0
- package/dist/lib/testing/storyboard/validations.d.ts +11 -0
- package/dist/lib/testing/storyboard/validations.d.ts.map +1 -1
- package/dist/lib/testing/storyboard/validations.js +30 -13
- package/dist/lib/testing/storyboard/validations.js.map +1 -1
- package/dist/lib/testing/storyboard/validations.mjs +27 -13
- package/dist/lib/testing/storyboard/validations.mjs.map +1 -1
- package/dist/lib/v2/projection/augment-response.d.mts +45 -0
- package/dist/lib/v2/projection/augment-response.d.ts +45 -0
- package/dist/lib/v2/projection/augment-response.d.ts.map +1 -1
- package/dist/lib/v2/projection/augment-response.js +54 -0
- package/dist/lib/v2/projection/augment-response.js.map +1 -1
- package/dist/lib/v2/projection/augment-response.mjs +52 -0
- package/dist/lib/v2/projection/augment-response.mjs.map +1 -1
- package/dist/lib/v2/projection/index.d.mts +7 -1
- package/dist/lib/v2/projection/index.d.ts +7 -1
- package/dist/lib/v2/projection/index.d.ts.map +1 -1
- package/dist/lib/v2/projection/index.js +9 -0
- package/dist/lib/v2/projection/index.js.map +1 -1
- package/dist/lib/v2/projection/index.mjs +10 -0
- package/dist/lib/v2/projection/index.mjs.map +1 -1
- package/dist/lib/v2/projection/legacy-routes.d.mts +24 -0
- package/dist/lib/v2/projection/legacy-routes.d.ts +25 -0
- package/dist/lib/v2/projection/legacy-routes.d.ts.map +1 -0
- package/dist/lib/v2/projection/legacy-routes.js +163 -0
- package/dist/lib/v2/projection/legacy-routes.js.map +1 -0
- package/dist/lib/v2/projection/legacy-routes.mjs +138 -0
- package/dist/lib/v2/projection/legacy-routes.mjs.map +1 -0
- package/dist/lib/v2/projection/v1-to-v2.d.mts +3 -0
- package/dist/lib/v2/projection/v1-to-v2.d.ts +3 -0
- package/dist/lib/v2/projection/v1-to-v2.d.ts.map +1 -1
- package/dist/lib/v2/projection/v1-to-v2.js +2 -1
- package/dist/lib/v2/projection/v1-to-v2.js.map +1 -1
- package/dist/lib/v2/projection/v1-to-v2.mjs +2 -1
- package/dist/lib/v2/projection/v1-to-v2.mjs.map +1 -1
- 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.js.map +1 -1
- package/dist/lib/version.mjs +3 -3
- package/dist/lib/version.mjs.map +1 -1
- package/docs/migration-12-to-13.md +56 -2
- package/package.json +3 -1
|
@@ -71,6 +71,7 @@ import {
|
|
|
71
71
|
projectMediaBuyCreativesForDelivery,
|
|
72
72
|
stripLegacyCreativeIdentity
|
|
73
73
|
} from "../../../v2/projection/creative-delivery.mjs";
|
|
74
|
+
import { projectV1ProductToV2 } from "../../../v2/projection/v1-to-v2.mjs";
|
|
74
75
|
import { toCanonicalOnlyResponse } from "../../../v2/projection/augment-response.mjs";
|
|
75
76
|
import {
|
|
76
77
|
CanonicalFormatLegacyResolutionError,
|
|
@@ -78,6 +79,7 @@ import {
|
|
|
78
79
|
resolveCanonicalFormatLegacyRefs
|
|
79
80
|
} from "../../../v2/projection/v2-to-v1.mjs";
|
|
80
81
|
import { legacyFormatRefsForDeclaration } from "../../../v2/projection/legacy-metadata.mjs";
|
|
82
|
+
import { canonicalizeAgentUrl } from "../../../discovery/resolve-agent-properties.mjs";
|
|
81
83
|
import { ADCP_VERSION } from "../../../version.mjs";
|
|
82
84
|
function supportsCanonicalCreativeCapability(version) {
|
|
83
85
|
if (typeof version !== "string") return false;
|
|
@@ -95,6 +97,21 @@ function requiresCanonicalCreativeWire(version) {
|
|
|
95
97
|
const minor = Number(match[2]);
|
|
96
98
|
return major > 3 || major === 3 && minor >= 2;
|
|
97
99
|
}
|
|
100
|
+
function supportsTransitionalDualCreativeWire(version) {
|
|
101
|
+
if (typeof version !== "string") return false;
|
|
102
|
+
const match = /^v?(\d+)\.(\d+)(?:\.|-|$)/.exec(version.trim());
|
|
103
|
+
if (!match) return false;
|
|
104
|
+
return Number(match[1]) === 3 && Number(match[2]) === 1;
|
|
105
|
+
}
|
|
106
|
+
function shouldEmitTransitionalDualCreativeWire(version, request) {
|
|
107
|
+
if (!supportsTransitionalDualCreativeWire(version)) return false;
|
|
108
|
+
if (request === null || typeof request !== "object") return true;
|
|
109
|
+
const fields = Object.getOwnPropertyDescriptor(request, "fields");
|
|
110
|
+
if (!fields || !("value" in fields) || !Array.isArray(fields.value)) return true;
|
|
111
|
+
return Object.values(Object.getOwnPropertyDescriptors(fields.value)).some(
|
|
112
|
+
(descriptor) => descriptor.enumerable && "value" in descriptor && descriptor.value === "format_ids"
|
|
113
|
+
);
|
|
114
|
+
}
|
|
98
115
|
function creativeWireEvidenceForRequest(request) {
|
|
99
116
|
let canonical = false;
|
|
100
117
|
let legacy = false;
|
|
@@ -366,7 +383,7 @@ function asCanonicalSemanticServerResponse(response, operation, legacyFormatConv
|
|
|
366
383
|
}
|
|
367
384
|
function asSemanticServerResponseForWire(response, operation, legacyFormatConverter, canonicalFormatLegacyResolver, wireMode) {
|
|
368
385
|
if (wireMode === "canonical") {
|
|
369
|
-
return
|
|
386
|
+
return attachCanonicalFormatWireRefs(
|
|
370
387
|
asCanonicalSemanticServerResponse(response, operation, legacyFormatConverter, canonicalFormatLegacyResolver),
|
|
371
388
|
canonicalFormatLegacyResolver,
|
|
372
389
|
operation
|
|
@@ -470,10 +487,10 @@ function asCanonicalGetProductsRequest(request) {
|
|
|
470
487
|
];
|
|
471
488
|
return { ...request, fields };
|
|
472
489
|
}
|
|
473
|
-
function asCanonicalListCreativesRequest(request, legacyFormatConverter) {
|
|
490
|
+
function asCanonicalListCreativesRequest(request, legacyFormatConverter, responseWireMode) {
|
|
474
491
|
const filters = request["filters"] !== null && typeof request["filters"] === "object" && !Array.isArray(request["filters"]) ? request["filters"] : void 0;
|
|
475
492
|
const legacyFormatIds = filters?.["format_ids"];
|
|
476
|
-
if (Array.isArray(legacyFormatIds) && legacyFormatIds.length > 0) {
|
|
493
|
+
if (Array.isArray(legacyFormatIds) && legacyFormatIds.length > 0 && responseWireMode !== "legacy") {
|
|
477
494
|
throw new AdcpError("INVALID_REQUEST", {
|
|
478
495
|
message: "list_creatives legacy format_ids filters are unavailable on the canonical platform surface.",
|
|
479
496
|
field: "filters.format_ids",
|
|
@@ -488,15 +505,22 @@ function asCanonicalListCreativesRequest(request, legacyFormatConverter) {
|
|
|
488
505
|
"list_creatives",
|
|
489
506
|
legacyFormatConverter
|
|
490
507
|
);
|
|
491
|
-
|
|
508
|
+
const withLegacyFilter = responseWireMode === "legacy" && Array.isArray(legacyFormatIds) && legacyFormatIds.length > 0 ? {
|
|
509
|
+
...projected,
|
|
510
|
+
filters: {
|
|
511
|
+
...projected["filters"] ?? {},
|
|
512
|
+
format_ids: legacyFormatIds
|
|
513
|
+
}
|
|
514
|
+
} : projected;
|
|
515
|
+
if (!Array.isArray(withLegacyFilter["fields"])) return withLegacyFilter;
|
|
492
516
|
const fields = [
|
|
493
517
|
...new Set(
|
|
494
|
-
|
|
518
|
+
withLegacyFilter["fields"].flatMap(
|
|
495
519
|
(field) => field === "format_id" ? ["format_kind"] : typeof field === "string" ? [field] : []
|
|
496
520
|
)
|
|
497
521
|
)
|
|
498
522
|
];
|
|
499
|
-
return { ...
|
|
523
|
+
return { ...withLegacyFilter, fields };
|
|
500
524
|
}
|
|
501
525
|
function asCanonicalProductResponse(response, legacyFormatConverter) {
|
|
502
526
|
try {
|
|
@@ -509,41 +533,324 @@ function asCanonicalProductResponse(response, legacyFormatConverter) {
|
|
|
509
533
|
suggestion: "Return an acyclic data-only product response without enumerable accessors."
|
|
510
534
|
});
|
|
511
535
|
}
|
|
536
|
+
validateDualProductFormatDeclarations(response, legacyFormatConverter);
|
|
512
537
|
const projected = toCanonicalOnlyResponse(response, {
|
|
513
538
|
legacyFormatConverter
|
|
514
539
|
});
|
|
515
|
-
|
|
516
|
-
|
|
540
|
+
const blockingDiagnostics = projected.diagnostics.filter(
|
|
541
|
+
(diagnostic) => diagnostic.code !== "LEGACY_FORMAT_ID_DROPPED_UNMAPPED"
|
|
542
|
+
);
|
|
543
|
+
if (blockingDiagnostics.length > 0) {
|
|
544
|
+
const first = blockingDiagnostics[0];
|
|
517
545
|
throw new AdcpError("INVALID_REQUEST", {
|
|
518
546
|
message: "get_products returned a legacy format that cannot be represented canonically.",
|
|
519
547
|
field: first.field,
|
|
520
548
|
suggestion: "Declare canonical format_options or configure legacyCreativeFormatConverter for the legacy format."
|
|
521
549
|
});
|
|
522
550
|
}
|
|
523
|
-
|
|
551
|
+
const canonicalResponse = asCanonicalServerResponse(projected.response);
|
|
552
|
+
const resolvedDropDiagnostics = projected.diagnostics.filter(
|
|
553
|
+
(diagnostic) => diagnostic.code === "LEGACY_FORMAT_ID_DROPPED_UNMAPPED"
|
|
554
|
+
);
|
|
555
|
+
if (resolvedDropDiagnostics.length > 0 && canonicalResponse !== null && typeof canonicalResponse === "object") {
|
|
556
|
+
resolvedLegacyDropDiagnosticsByResponse.set(canonicalResponse, resolvedDropDiagnostics);
|
|
557
|
+
}
|
|
558
|
+
preserveAuthoredFormatIds(response, canonicalResponse);
|
|
559
|
+
return canonicalResponse;
|
|
560
|
+
}
|
|
561
|
+
const authoredFormatIdsByOwner = /* @__PURE__ */ new WeakMap();
|
|
562
|
+
const resolvedLegacyDropDiagnosticsByResponse = /* @__PURE__ */ new WeakMap();
|
|
563
|
+
function ownArrayDataValues(value) {
|
|
564
|
+
const lengthDescriptor = Object.getOwnPropertyDescriptor(value, "length");
|
|
565
|
+
const length = lengthDescriptor && "value" in lengthDescriptor ? lengthDescriptor.value : 0;
|
|
566
|
+
if (typeof length !== "number" || !Number.isSafeInteger(length) || length < 0) return [];
|
|
567
|
+
const values = [];
|
|
568
|
+
for (let index = 0; index < length; index++) {
|
|
569
|
+
const descriptor = Object.getOwnPropertyDescriptor(value, String(index));
|
|
570
|
+
if (descriptor?.enumerable && "value" in descriptor) values.push(descriptor.value);
|
|
571
|
+
}
|
|
572
|
+
return values;
|
|
573
|
+
}
|
|
574
|
+
function cloneWireData(value) {
|
|
575
|
+
if (Array.isArray(value)) {
|
|
576
|
+
const cloned2 = [];
|
|
577
|
+
for (const entry of ownArrayDataValues(value)) cloned2.push(cloneWireData(entry));
|
|
578
|
+
return cloned2;
|
|
579
|
+
}
|
|
580
|
+
if (value === null || typeof value !== "object") return value;
|
|
581
|
+
const cloned = {};
|
|
582
|
+
for (const [key, descriptor] of Object.entries(Object.getOwnPropertyDescriptors(value))) {
|
|
583
|
+
if (descriptor.enumerable && "value" in descriptor) cloned[key] = cloneWireData(descriptor.value);
|
|
584
|
+
}
|
|
585
|
+
return cloned;
|
|
586
|
+
}
|
|
587
|
+
function preserveAuthoredFormatIds(source, target) {
|
|
588
|
+
if (source === null || target === null || typeof source !== "object" || typeof target !== "object") return;
|
|
589
|
+
const sourceProducts = Object.getOwnPropertyDescriptor(source, "products");
|
|
590
|
+
const targetProducts = Object.getOwnPropertyDescriptor(target, "products");
|
|
591
|
+
if (!sourceProducts || !sourceProducts.enumerable || !("value" in sourceProducts) || !Array.isArray(sourceProducts.value) || !targetProducts || !targetProducts.enumerable || !("value" in targetProducts) || !Array.isArray(targetProducts.value)) {
|
|
592
|
+
return;
|
|
593
|
+
}
|
|
594
|
+
const sourceValues = ownArrayDataValues(sourceProducts.value);
|
|
595
|
+
const targetValues = ownArrayDataValues(targetProducts.value);
|
|
596
|
+
const count = Math.min(sourceValues.length, targetValues.length);
|
|
597
|
+
for (let index = 0; index < count; index++)
|
|
598
|
+
preserveAuthoredFormatIdsForOwner(sourceValues[index], targetValues[index]);
|
|
599
|
+
}
|
|
600
|
+
function preserveAuthoredFormatIdsForOwner(source, target) {
|
|
601
|
+
if (source === null || target === null || typeof source !== "object" || typeof target !== "object") return;
|
|
602
|
+
const sourceProductId = Object.getOwnPropertyDescriptor(source, "product_id");
|
|
603
|
+
const sourcePlacementId = Object.getOwnPropertyDescriptor(source, "placement_id");
|
|
604
|
+
const recognizedOwner = sourceProductId?.enumerable && "value" in sourceProductId && typeof sourceProductId.value === "string" || sourcePlacementId?.enumerable && "value" in sourcePlacementId && typeof sourcePlacementId.value === "string";
|
|
605
|
+
if (!recognizedOwner) return;
|
|
606
|
+
const sourceFormatIds = Object.getOwnPropertyDescriptor(source, "format_ids");
|
|
607
|
+
const targetFormatOptions = Object.getOwnPropertyDescriptor(target, "format_options");
|
|
608
|
+
if (sourceFormatIds && sourceFormatIds.enumerable && "value" in sourceFormatIds && Array.isArray(sourceFormatIds.value) && targetFormatOptions && targetFormatOptions.enumerable && "value" in targetFormatOptions && Array.isArray(targetFormatOptions.value)) {
|
|
609
|
+
authoredFormatIdsByOwner.set(
|
|
610
|
+
target,
|
|
611
|
+
ownArrayDataValues(sourceFormatIds.value).map((ref) => cloneWireData(ref))
|
|
612
|
+
);
|
|
613
|
+
}
|
|
614
|
+
const sourcePlacements = Object.getOwnPropertyDescriptor(source, "placements");
|
|
615
|
+
const targetPlacements = Object.getOwnPropertyDescriptor(target, "placements");
|
|
616
|
+
if (sourcePlacements && sourcePlacements.enumerable && "value" in sourcePlacements && Array.isArray(sourcePlacements.value) && targetPlacements && targetPlacements.enumerable && "value" in targetPlacements && Array.isArray(targetPlacements.value)) {
|
|
617
|
+
const sourceValues = ownArrayDataValues(sourcePlacements.value);
|
|
618
|
+
const targetValues = ownArrayDataValues(targetPlacements.value);
|
|
619
|
+
const count = Math.min(sourceValues.length, targetValues.length);
|
|
620
|
+
for (let index = 0; index < count; index++) {
|
|
621
|
+
preserveAuthoredFormatIdsForOwner(sourceValues[index], targetValues[index]);
|
|
622
|
+
}
|
|
623
|
+
}
|
|
524
624
|
}
|
|
525
|
-
function
|
|
625
|
+
function removeResolvedLegacyDropErrors(response, diagnostics) {
|
|
626
|
+
if (response === null || typeof response !== "object") return response;
|
|
627
|
+
const resolved = diagnostics.filter((diagnostic) => diagnostic.code === "LEGACY_FORMAT_ID_DROPPED_UNMAPPED");
|
|
628
|
+
if (resolved.length === 0) return response;
|
|
629
|
+
const record = response;
|
|
630
|
+
if (!Array.isArray(record.errors)) return response;
|
|
631
|
+
const retained = [];
|
|
632
|
+
for (const value of ownArrayDataValues(record.errors)) {
|
|
633
|
+
let matchesResolvedDiagnostic = false;
|
|
634
|
+
if (value !== null && typeof value === "object" && !Array.isArray(value)) {
|
|
635
|
+
const error = value;
|
|
636
|
+
for (const diagnostic of resolved) {
|
|
637
|
+
if (error.code === diagnostic.code && error.field === diagnostic.field && error.source === diagnostic.source && error.sdk_id === diagnostic.sdk_id && jsonValuesEqual(error.details, diagnostic.error.details)) {
|
|
638
|
+
matchesResolvedDiagnostic = true;
|
|
639
|
+
break;
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
if (!matchesResolvedDiagnostic) retained.push(value);
|
|
644
|
+
}
|
|
645
|
+
if (retained.length > 0) return { ...record, errors: retained };
|
|
646
|
+
const { errors: _resolvedErrors, ...withoutErrors } = record;
|
|
647
|
+
void _resolvedErrors;
|
|
648
|
+
return withoutErrors;
|
|
649
|
+
}
|
|
650
|
+
function legacyFormatRefKey(value) {
|
|
651
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
652
|
+
const ref = value;
|
|
653
|
+
if (typeof ref.agent_url !== "string" || typeof ref.id !== "string") return void 0;
|
|
654
|
+
const agentUrl = canonicalizeAgentUrl(ref.agent_url) ?? ref.agent_url;
|
|
655
|
+
return `${agentUrl}::${ref.id}::${String(ref.width ?? "")}x${String(ref.height ?? "")}::${String(ref.duration_ms ?? "")}`;
|
|
656
|
+
}
|
|
657
|
+
function jsonValuesEqual(left, right) {
|
|
658
|
+
if (Object.is(left, right)) return true;
|
|
659
|
+
if (Array.isArray(left) || Array.isArray(right)) {
|
|
660
|
+
if (!Array.isArray(left) || !Array.isArray(right)) return false;
|
|
661
|
+
const leftValues = ownArrayDataValues(left);
|
|
662
|
+
const rightValues = ownArrayDataValues(right);
|
|
663
|
+
if (leftValues.length !== rightValues.length) return false;
|
|
664
|
+
for (let index = 0; index < leftValues.length; index++) {
|
|
665
|
+
if (!jsonValuesEqual(leftValues[index], rightValues[index])) return false;
|
|
666
|
+
}
|
|
667
|
+
return true;
|
|
668
|
+
}
|
|
669
|
+
if (left === null || right === null || typeof left !== "object" || typeof right !== "object") return false;
|
|
670
|
+
const leftRecord = left;
|
|
671
|
+
const rightRecord = right;
|
|
672
|
+
const leftKeys = Object.keys(leftRecord);
|
|
673
|
+
const rightKeys = Object.keys(rightRecord);
|
|
674
|
+
return leftKeys.length === rightKeys.length && leftKeys.every((key) => Object.hasOwn(rightRecord, key) && jsonValuesEqual(leftRecord[key], rightRecord[key]));
|
|
675
|
+
}
|
|
676
|
+
function matchLegacyRefToDeclaration(declaration, ref, ownerId, legacyFormatConverter) {
|
|
677
|
+
const projected = projectV1ProductToV2(
|
|
678
|
+
{
|
|
679
|
+
product_id: ownerId,
|
|
680
|
+
name: ownerId,
|
|
681
|
+
description: ownerId,
|
|
682
|
+
format_ids: [ref]
|
|
683
|
+
},
|
|
684
|
+
{ legacyFormatConverter }
|
|
685
|
+
);
|
|
686
|
+
if (projected.diagnostics.length > 0 || projected.v2.format_options.length !== 1) return "unresolved";
|
|
687
|
+
const expected = projected.v2.format_options[0];
|
|
688
|
+
if (declaration.format_kind !== expected.format_kind) return "kind_mismatch";
|
|
689
|
+
const params = declaration.params !== null && typeof declaration.params === "object" && !Array.isArray(declaration.params) ? declaration.params : {};
|
|
690
|
+
for (const [key, expectedValue] of Object.entries(expected.params)) {
|
|
691
|
+
if (!Object.hasOwn(params, key) || !jsonValuesEqual(params[key], expectedValue)) return "params_mismatch";
|
|
692
|
+
}
|
|
693
|
+
return "match";
|
|
694
|
+
}
|
|
695
|
+
function validateLegacyRefNarrowing(declaration, ref, ownerId, field, legacyFormatConverter) {
|
|
696
|
+
const match = matchLegacyRefToDeclaration(declaration, ref, ownerId, legacyFormatConverter);
|
|
697
|
+
if (match === "kind_mismatch") {
|
|
698
|
+
throw new AdcpError("INVALID_REQUEST", {
|
|
699
|
+
message: "get_products returned divergent legacy and canonical format declarations.",
|
|
700
|
+
field,
|
|
701
|
+
suggestion: "Make format_ids and format_options.v1_format_ref describe the same canonical format."
|
|
702
|
+
});
|
|
703
|
+
}
|
|
704
|
+
if (match === "params_mismatch") {
|
|
705
|
+
throw new AdcpError("INVALID_REQUEST", {
|
|
706
|
+
message: "get_products returned a canonical declaration that does not narrow its v1_format_ref.",
|
|
707
|
+
field: `${field}.params`,
|
|
708
|
+
suggestion: "Align canonical format parameters with the referenced legacy format requirements."
|
|
709
|
+
});
|
|
710
|
+
}
|
|
711
|
+
}
|
|
712
|
+
function validateDualProductFormatDeclarations(value, legacyFormatConverter) {
|
|
713
|
+
const seen = /* @__PURE__ */ new WeakSet();
|
|
714
|
+
const visit = (current, path) => {
|
|
715
|
+
if (current === null || typeof current !== "object" || seen.has(current)) return;
|
|
716
|
+
seen.add(current);
|
|
717
|
+
if (Array.isArray(current)) {
|
|
718
|
+
const entries = ownArrayDataValues(current);
|
|
719
|
+
for (let index = 0; index < entries.length; index++) visit(entries[index], `${path}[${index}]`);
|
|
720
|
+
return;
|
|
721
|
+
}
|
|
722
|
+
const record = current;
|
|
723
|
+
const formatOptions = Array.isArray(record.format_options) ? record.format_options : void 0;
|
|
724
|
+
const ownerId = typeof record.product_id === "string" ? record.product_id : typeof record.placement_id === "string" ? record.placement_id : void 0;
|
|
725
|
+
if (formatOptions && ownerId !== void 0) {
|
|
726
|
+
const declarationRefs = [];
|
|
727
|
+
const declarations = ownArrayDataValues(formatOptions);
|
|
728
|
+
for (let index = 0; index < declarations.length; index++) {
|
|
729
|
+
const value2 = declarations[index];
|
|
730
|
+
if (value2 === null || typeof value2 !== "object" || Array.isArray(value2)) continue;
|
|
731
|
+
const declaration = value2;
|
|
732
|
+
const refs = ownArrayDataValues(
|
|
733
|
+
legacyFormatRefsForDeclaration(declaration)
|
|
734
|
+
);
|
|
735
|
+
const field = `${path}.format_options[${index}]`;
|
|
736
|
+
if (declaration.canonical_formats_only === true && refs.length > 0) {
|
|
737
|
+
throw new AdcpError("INVALID_REQUEST", {
|
|
738
|
+
message: "get_products returned canonical_formats_only together with v1_format_ref.",
|
|
739
|
+
field,
|
|
740
|
+
suggestion: "Remove v1_format_ref or set canonical_formats_only to false."
|
|
741
|
+
});
|
|
742
|
+
}
|
|
743
|
+
declarationRefs.push(...refs);
|
|
744
|
+
for (const ref of refs) {
|
|
745
|
+
validateLegacyRefNarrowing(declaration, ref, ownerId, field, legacyFormatConverter);
|
|
746
|
+
}
|
|
747
|
+
}
|
|
748
|
+
if (Array.isArray(record.format_ids)) {
|
|
749
|
+
const formatIds = ownArrayDataValues(record.format_ids);
|
|
750
|
+
const formatIdKeys = /* @__PURE__ */ new Set();
|
|
751
|
+
for (const ref of formatIds) {
|
|
752
|
+
const key = legacyFormatRefKey(ref);
|
|
753
|
+
if (key !== void 0) formatIdKeys.add(key);
|
|
754
|
+
}
|
|
755
|
+
const declarationRefKeys = /* @__PURE__ */ new Set();
|
|
756
|
+
for (const ref of declarationRefs) {
|
|
757
|
+
const key = legacyFormatRefKey(ref);
|
|
758
|
+
if (key !== void 0) declarationRefKeys.add(key);
|
|
759
|
+
}
|
|
760
|
+
let divergent = false;
|
|
761
|
+
for (const key of declarationRefKeys) {
|
|
762
|
+
if (!formatIdKeys.has(key)) divergent = true;
|
|
763
|
+
}
|
|
764
|
+
for (const ref of formatIds) {
|
|
765
|
+
const key = legacyFormatRefKey(ref);
|
|
766
|
+
if (key !== void 0 && declarationRefKeys.has(key)) continue;
|
|
767
|
+
let matched = false;
|
|
768
|
+
for (const value2 of declarations) {
|
|
769
|
+
if (value2 !== null && typeof value2 === "object" && !Array.isArray(value2) && value2.canonical_formats_only !== true && matchLegacyRefToDeclaration(value2, ref, ownerId, legacyFormatConverter) === "match") {
|
|
770
|
+
matched = true;
|
|
771
|
+
break;
|
|
772
|
+
}
|
|
773
|
+
}
|
|
774
|
+
if (!matched) divergent = true;
|
|
775
|
+
}
|
|
776
|
+
if (divergent) {
|
|
777
|
+
throw new AdcpError("INVALID_REQUEST", {
|
|
778
|
+
message: "get_products returned divergent format_ids and format_options.v1_format_ref values.",
|
|
779
|
+
field: path,
|
|
780
|
+
suggestion: "Dual-emitted product declarations must carry the same legacy format references."
|
|
781
|
+
});
|
|
782
|
+
}
|
|
783
|
+
}
|
|
784
|
+
}
|
|
785
|
+
for (const [key, child] of Object.entries(record)) {
|
|
786
|
+
if (key === "format_options") continue;
|
|
787
|
+
visit(child, `${path}.${key}`);
|
|
788
|
+
}
|
|
789
|
+
};
|
|
790
|
+
visit(value, "$");
|
|
791
|
+
}
|
|
792
|
+
function attachCanonicalFormatWireRefs(value, canonicalFormatLegacyResolver, operation, emitTransitionalLegacyRefs = false) {
|
|
526
793
|
const visit = (current) => {
|
|
527
|
-
if (Array.isArray(current))
|
|
794
|
+
if (Array.isArray(current)) {
|
|
795
|
+
const mapped = [];
|
|
796
|
+
for (const entry of ownArrayDataValues(current)) mapped.push(visit(entry));
|
|
797
|
+
return mapped;
|
|
798
|
+
}
|
|
528
799
|
if (current === null || typeof current !== "object") return current;
|
|
529
800
|
const record = current;
|
|
530
801
|
const hasFormatDeclarations = Array.isArray(record.format_options);
|
|
531
802
|
const declarationOwner = typeof record.product_id === "string" ? record.product_id : typeof record.placement_id === "string" ? record.placement_id : "(response)";
|
|
532
803
|
let projected = record;
|
|
533
804
|
if (hasFormatDeclarations) {
|
|
534
|
-
const
|
|
535
|
-
|
|
805
|
+
const wireLegacyRefs = [];
|
|
806
|
+
const wireLegacyRefKeys = /* @__PURE__ */ new Set();
|
|
807
|
+
const appendWireLegacyRefs = (refs) => {
|
|
808
|
+
for (const ref of refs) {
|
|
809
|
+
const key = legacyFormatRefKey(ref);
|
|
810
|
+
if (key === void 0 || wireLegacyRefKeys.has(key)) continue;
|
|
811
|
+
wireLegacyRefKeys.add(key);
|
|
812
|
+
wireLegacyRefs.push(cloneWireData(ref));
|
|
813
|
+
}
|
|
814
|
+
};
|
|
815
|
+
if (emitTransitionalLegacyRefs) {
|
|
816
|
+
const authoredRefs = authoredFormatIdsByOwner.get(record);
|
|
817
|
+
if (authoredRefs) appendWireLegacyRefs(authoredRefs);
|
|
818
|
+
}
|
|
819
|
+
const formatOptions = [];
|
|
820
|
+
const declarations = ownArrayDataValues(record.format_options);
|
|
821
|
+
for (let index = 0; index < declarations.length; index++) {
|
|
822
|
+
const value2 = declarations[index];
|
|
823
|
+
if (value2 === null || typeof value2 !== "object" || Array.isArray(value2)) {
|
|
824
|
+
formatOptions.push(value2);
|
|
825
|
+
continue;
|
|
826
|
+
}
|
|
536
827
|
const declaration = value2;
|
|
537
|
-
if (declaration.
|
|
538
|
-
|
|
828
|
+
if (declaration.canonical_formats_only === true) {
|
|
829
|
+
formatOptions.push(declaration);
|
|
830
|
+
continue;
|
|
831
|
+
}
|
|
832
|
+
let refs = ownArrayDataValues(
|
|
833
|
+
legacyFormatRefsForDeclaration(declaration)
|
|
834
|
+
);
|
|
835
|
+
if (emitTransitionalLegacyRefs && refs.length > 0) {
|
|
836
|
+
const clonedRefs2 = refs.map((ref) => cloneWireData(ref));
|
|
837
|
+
appendWireLegacyRefs(clonedRefs2);
|
|
838
|
+
formatOptions.push({ ...declaration, v1_format_ref: clonedRefs2 });
|
|
839
|
+
continue;
|
|
840
|
+
}
|
|
841
|
+
if (declaration.format_kind !== "custom") {
|
|
842
|
+
formatOptions.push(declaration);
|
|
843
|
+
continue;
|
|
844
|
+
}
|
|
539
845
|
if (refs.length === 0) {
|
|
540
846
|
try {
|
|
541
|
-
|
|
847
|
+
const resolved = resolveCanonicalFormatLegacyRefs(canonicalFormatLegacyResolver, {
|
|
542
848
|
source: "product",
|
|
543
849
|
declaration,
|
|
544
850
|
productId: declarationOwner,
|
|
545
851
|
field: `${declarationOwner}.format_options[${index}]`
|
|
546
|
-
})
|
|
852
|
+
});
|
|
853
|
+
refs = resolved ? ownArrayDataValues(resolved) : [];
|
|
547
854
|
} catch (error) {
|
|
548
855
|
if (!(error instanceof CanonicalFormatLegacyResolutionError)) throw error;
|
|
549
856
|
throw new CreativeFormatProjectionError(
|
|
@@ -560,9 +867,15 @@ function attachCanonicalCustomFormatWireRefs(value, canonicalFormatLegacyResolve
|
|
|
560
867
|
"custom canonical declaration must set canonical_formats_only or provide an explicit legacy mapping"
|
|
561
868
|
);
|
|
562
869
|
}
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
870
|
+
const clonedRefs = refs.map((ref) => cloneWireData(ref));
|
|
871
|
+
if (emitTransitionalLegacyRefs) appendWireLegacyRefs(clonedRefs);
|
|
872
|
+
formatOptions.push({ ...declaration, v1_format_ref: clonedRefs });
|
|
873
|
+
}
|
|
874
|
+
projected = {
|
|
875
|
+
...record,
|
|
876
|
+
format_options: formatOptions,
|
|
877
|
+
...emitTransitionalLegacyRefs && wireLegacyRefs.length > 0 && { format_ids: wireLegacyRefs }
|
|
878
|
+
};
|
|
566
879
|
}
|
|
567
880
|
let changed = projected !== record;
|
|
568
881
|
const next = { ...projected };
|
|
@@ -578,9 +891,18 @@ function attachCanonicalCustomFormatWireRefs(value, canonicalFormatLegacyResolve
|
|
|
578
891
|
};
|
|
579
892
|
return visit(value);
|
|
580
893
|
}
|
|
581
|
-
function asProductResponseForWire(canonicalResponse, wireMode, canonicalFormatLegacyResolver) {
|
|
894
|
+
function asProductResponseForWire(canonicalResponse, wireMode, canonicalFormatLegacyResolver, emitTransitionalLegacyRefs = false) {
|
|
582
895
|
if (wireMode === "canonical") {
|
|
583
|
-
|
|
896
|
+
const responseForWire = emitTransitionalLegacyRefs && canonicalResponse !== null && typeof canonicalResponse === "object" ? removeResolvedLegacyDropErrors(
|
|
897
|
+
canonicalResponse,
|
|
898
|
+
resolvedLegacyDropDiagnosticsByResponse.get(canonicalResponse) ?? []
|
|
899
|
+
) : canonicalResponse;
|
|
900
|
+
return attachCanonicalFormatWireRefs(
|
|
901
|
+
responseForWire,
|
|
902
|
+
canonicalFormatLegacyResolver,
|
|
903
|
+
"get_products",
|
|
904
|
+
emitTransitionalLegacyRefs
|
|
905
|
+
);
|
|
584
906
|
}
|
|
585
907
|
if (!Array.isArray(canonicalResponse.products)) return canonicalResponse;
|
|
586
908
|
const products = canonicalResponse.products.map((product) => {
|
|
@@ -2492,7 +2814,8 @@ function buildMediaBuyHandlers(platform, taskRegistry, taskWebhookEmit, observab
|
|
|
2492
2814
|
return asProductResponseForWire(
|
|
2493
2815
|
canonicalResult,
|
|
2494
2816
|
responseWireMode,
|
|
2495
|
-
canonicalFormatLegacyResolver
|
|
2817
|
+
canonicalFormatLegacyResolver,
|
|
2818
|
+
shouldEmitTransitionalDualCreativeWire(ctx.servedAdcpVersion, params)
|
|
2496
2819
|
);
|
|
2497
2820
|
};
|
|
2498
2821
|
const isHandoff = isTaskHandoff(result);
|
|
@@ -2714,46 +3037,45 @@ function buildMediaBuyHandlers(platform, taskRegistry, taskWebhookEmit, observab
|
|
|
2714
3037
|
);
|
|
2715
3038
|
}
|
|
2716
3039
|
},
|
|
2717
|
-
syncCreatives
|
|
2718
|
-
|
|
2719
|
-
|
|
2720
|
-
|
|
2721
|
-
|
|
2722
|
-
|
|
2723
|
-
return
|
|
2724
|
-
|
|
2725
|
-
|
|
3040
|
+
...sales?.syncCreatives && {
|
|
3041
|
+
syncCreatives: async (params, ctx) => {
|
|
3042
|
+
const responseWireMode = creativeWireModeForRequest(ctx, creativeWireMode, params);
|
|
3043
|
+
params = asCanonicalSemanticServerRequest(params, "sync_creatives", legacyFormatConverter);
|
|
3044
|
+
const reqCtx = ctxFor(ctx, params);
|
|
3045
|
+
const creatives = params.creatives ?? [];
|
|
3046
|
+
return projectSync(
|
|
3047
|
+
async () => {
|
|
3048
|
+
const push = extractPushConfig(params, logger, {
|
|
3049
|
+
allowPrivateWebhookUrls: pushOpts.allowPrivateWebhookUrls
|
|
3050
|
+
});
|
|
3051
|
+
const result = await sales.syncCreatives(creatives, reqCtx);
|
|
3052
|
+
return routeIfHandoff(
|
|
3053
|
+
taskRegistry,
|
|
3054
|
+
{
|
|
3055
|
+
tool: "sync_creatives",
|
|
3056
|
+
accountId: reqCtx.account.id,
|
|
3057
|
+
ownerScope: taskOwnerScopeFor(ctx, reqCtx.account.id),
|
|
3058
|
+
pushNotificationUrl: push.url,
|
|
3059
|
+
pushNotificationToken: push.token,
|
|
3060
|
+
pushNotificationOperationId: push.operationId,
|
|
3061
|
+
emitWebhook: taskWebhookEmit ?? ctx.emitWebhook,
|
|
3062
|
+
autoEmitCompletion: pushOpts.autoEmitCompletionWebhooks,
|
|
3063
|
+
observability,
|
|
3064
|
+
logger
|
|
3065
|
+
},
|
|
3066
|
+
result,
|
|
3067
|
+
(rows) => asSemanticServerResponseForWire(
|
|
3068
|
+
{ creatives: rows.map(normalizeRowErrors) },
|
|
3069
|
+
"sync_creatives",
|
|
3070
|
+
legacyFormatConverter,
|
|
3071
|
+
canonicalFormatLegacyResolver,
|
|
3072
|
+
responseWireMode
|
|
3073
|
+
)
|
|
3074
|
+
);
|
|
3075
|
+
},
|
|
3076
|
+
(r) => r
|
|
3077
|
+
);
|
|
2726
3078
|
}
|
|
2727
|
-
return projectSync(
|
|
2728
|
-
async () => {
|
|
2729
|
-
const push = extractPushConfig(params, logger, { allowPrivateWebhookUrls: pushOpts.allowPrivateWebhookUrls });
|
|
2730
|
-
const result = await sales.syncCreatives(creatives, reqCtx);
|
|
2731
|
-
return routeIfHandoff(
|
|
2732
|
-
taskRegistry,
|
|
2733
|
-
{
|
|
2734
|
-
tool: "sync_creatives",
|
|
2735
|
-
accountId: reqCtx.account.id,
|
|
2736
|
-
ownerScope: taskOwnerScopeFor(ctx, reqCtx.account.id),
|
|
2737
|
-
pushNotificationUrl: push.url,
|
|
2738
|
-
pushNotificationToken: push.token,
|
|
2739
|
-
pushNotificationOperationId: push.operationId,
|
|
2740
|
-
emitWebhook: taskWebhookEmit ?? ctx.emitWebhook,
|
|
2741
|
-
autoEmitCompletion: pushOpts.autoEmitCompletionWebhooks,
|
|
2742
|
-
observability,
|
|
2743
|
-
logger
|
|
2744
|
-
},
|
|
2745
|
-
result,
|
|
2746
|
-
(rows) => asSemanticServerResponseForWire(
|
|
2747
|
-
{ creatives: rows.map(normalizeRowErrors) },
|
|
2748
|
-
"sync_creatives",
|
|
2749
|
-
legacyFormatConverter,
|
|
2750
|
-
canonicalFormatLegacyResolver,
|
|
2751
|
-
responseWireMode
|
|
2752
|
-
)
|
|
2753
|
-
);
|
|
2754
|
-
},
|
|
2755
|
-
(r) => r
|
|
2756
|
-
);
|
|
2757
3079
|
},
|
|
2758
3080
|
...sales?.getMediaBuyDelivery && {
|
|
2759
3081
|
getMediaBuyDelivery: async (params, ctx) => {
|
|
@@ -2836,7 +3158,7 @@ function buildMediaBuyHandlers(platform, taskRegistry, taskWebhookEmit, observab
|
|
|
2836
3158
|
"media_buy_id",
|
|
2837
3159
|
logger
|
|
2838
3160
|
);
|
|
2839
|
-
const wireResult = responseWireMode === "canonical" ?
|
|
3161
|
+
const wireResult = responseWireMode === "canonical" ? attachCanonicalFormatWireRefs(canonicalResult, canonicalFormatLegacyResolver, "get_media_buys") : asSemanticServerResponseForWire(
|
|
2840
3162
|
result,
|
|
2841
3163
|
"get_media_buys",
|
|
2842
3164
|
legacyFormatConverter,
|
|
@@ -2889,7 +3211,8 @@ function buildMediaBuyHandlers(platform, taskRegistry, taskWebhookEmit, observab
|
|
|
2889
3211
|
const responseWireMode = creativeWireModeForRequest(ctx, creativeWireMode, params);
|
|
2890
3212
|
const canonicalParams = asCanonicalListCreativesRequest(
|
|
2891
3213
|
params,
|
|
2892
|
-
legacyFormatConverter
|
|
3214
|
+
legacyFormatConverter,
|
|
3215
|
+
responseWireMode
|
|
2893
3216
|
);
|
|
2894
3217
|
const reqCtx = ctxFor(ctx, canonicalParams);
|
|
2895
3218
|
return projectSync(
|
|
@@ -3022,7 +3345,8 @@ function buildCreativeHandlers(platform, taskRegistry, taskWebhookEmit, observab
|
|
|
3022
3345
|
const responseWireMode = creativeWireModeForRequest(ctx, creativeWireMode, params);
|
|
3023
3346
|
const canonicalParams = asCanonicalListCreativesRequest(
|
|
3024
3347
|
params,
|
|
3025
|
-
legacyFormatConverter
|
|
3348
|
+
legacyFormatConverter,
|
|
3349
|
+
responseWireMode
|
|
3026
3350
|
);
|
|
3027
3351
|
const reqCtx = ctxFor(ctx, canonicalParams);
|
|
3028
3352
|
return projectSync(
|