@adcp/sdk 12.1.1 → 13.0.0-rc.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/README.md +19 -3
- package/bin/adcp-fuzz.js +3 -1
- package/dist/lib/adapters/content-standards-adapter.d.mts +9 -9
- package/dist/lib/adapters/content-standards-adapter.d.ts +9 -9
- package/dist/lib/adapters/content-standards-adapter.d.ts.map +1 -1
- package/dist/lib/adapters/content-standards-adapter.js +22 -22
- package/dist/lib/adapters/content-standards-adapter.js.map +1 -1
- package/dist/lib/adapters/content-standards-adapter.mjs +18 -18
- package/dist/lib/adapters/content-standards-adapter.mjs.map +1 -1
- package/dist/lib/adapters/index.d.mts +2 -2
- package/dist/lib/adapters/index.d.ts +2 -2
- package/dist/lib/adapters/index.d.ts.map +1 -1
- package/dist/lib/adapters/index.js +10 -10
- package/dist/lib/adapters/index.js.map +1 -1
- package/dist/lib/adapters/index.mjs +9 -9
- package/dist/lib/adapters/index.mjs.map +1 -1
- package/dist/lib/auth/oauth/provider-cache.d.mts +7 -0
- package/dist/lib/auth/oauth/provider-cache.d.ts +8 -0
- package/dist/lib/auth/oauth/provider-cache.d.ts.map +1 -0
- package/dist/lib/auth/oauth/provider-cache.js +44 -0
- package/dist/lib/auth/oauth/provider-cache.js.map +1 -0
- package/dist/lib/auth/oauth/provider-cache.mjs +19 -0
- package/dist/lib/auth/oauth/provider-cache.mjs.map +1 -0
- package/dist/lib/conformance/invariants/uniformError.js +1 -1
- package/dist/lib/conformance/invariants/uniformError.js.map +1 -1
- package/dist/lib/conformance/invariants/uniformError.mjs +1 -1
- package/dist/lib/conformance/invariants/uniformError.mjs.map +1 -1
- package/dist/lib/conformance/runners.js +1 -1
- package/dist/lib/conformance/runners.js.map +1 -1
- package/dist/lib/conformance/runners.mjs +1 -1
- package/dist/lib/conformance/runners.mjs.map +1 -1
- package/dist/lib/conformance/seeder.js +5 -5
- package/dist/lib/conformance/seeder.js.map +1 -1
- package/dist/lib/conformance/seeder.mjs +5 -5
- package/dist/lib/conformance/seeder.mjs.map +1 -1
- package/dist/lib/core/ADCPMultiAgentClient.d.mts +26 -29
- package/dist/lib/core/ADCPMultiAgentClient.d.ts +26 -29
- package/dist/lib/core/ADCPMultiAgentClient.d.ts.map +1 -1
- package/dist/lib/core/ADCPMultiAgentClient.js +27 -19
- package/dist/lib/core/ADCPMultiAgentClient.js.map +1 -1
- package/dist/lib/core/ADCPMultiAgentClient.mjs +27 -19
- package/dist/lib/core/ADCPMultiAgentClient.mjs.map +1 -1
- package/dist/lib/core/AgentClient.d.mts +148 -138
- package/dist/lib/core/AgentClient.d.ts +148 -138
- package/dist/lib/core/AgentClient.d.ts.map +1 -1
- package/dist/lib/core/AgentClient.js +174 -85
- package/dist/lib/core/AgentClient.js.map +1 -1
- package/dist/lib/core/AgentClient.mjs +174 -85
- package/dist/lib/core/AgentClient.mjs.map +1 -1
- package/dist/lib/core/AsyncHandler.d.mts +18 -16
- package/dist/lib/core/AsyncHandler.d.ts +18 -16
- package/dist/lib/core/AsyncHandler.d.ts.map +1 -1
- package/dist/lib/core/AsyncHandler.js +22 -1
- package/dist/lib/core/AsyncHandler.js.map +1 -1
- package/dist/lib/core/AsyncHandler.mjs +22 -1
- package/dist/lib/core/AsyncHandler.mjs.map +1 -1
- package/dist/lib/core/ConversationTypes.d.mts +6 -2
- package/dist/lib/core/ConversationTypes.d.ts +6 -2
- package/dist/lib/core/ConversationTypes.d.ts.map +1 -1
- package/dist/lib/core/ConversationTypes.js.map +1 -1
- package/dist/lib/core/CreativeAgentClient.d.mts +41 -17
- package/dist/lib/core/CreativeAgentClient.d.ts +41 -17
- package/dist/lib/core/CreativeAgentClient.d.ts.map +1 -1
- package/dist/lib/core/CreativeAgentClient.js +39 -12
- package/dist/lib/core/CreativeAgentClient.js.map +1 -1
- package/dist/lib/core/CreativeAgentClient.mjs +39 -12
- package/dist/lib/core/CreativeAgentClient.mjs.map +1 -1
- package/dist/lib/core/SingleAgentClient.d.mts +127 -50
- package/dist/lib/core/SingleAgentClient.d.ts +127 -50
- package/dist/lib/core/SingleAgentClient.d.ts.map +1 -1
- package/dist/lib/core/SingleAgentClient.js +1617 -177
- package/dist/lib/core/SingleAgentClient.js.map +1 -1
- package/dist/lib/core/SingleAgentClient.mjs +1627 -177
- package/dist/lib/core/SingleAgentClient.mjs.map +1 -1
- package/dist/lib/core/TaskExecutor.d.mts +9 -0
- package/dist/lib/core/TaskExecutor.d.ts +9 -0
- package/dist/lib/core/TaskExecutor.d.ts.map +1 -1
- package/dist/lib/core/TaskExecutor.js +136 -43
- package/dist/lib/core/TaskExecutor.js.map +1 -1
- package/dist/lib/core/TaskExecutor.mjs +136 -43
- package/dist/lib/core/TaskExecutor.mjs.map +1 -1
- package/dist/lib/index.d.mts +111 -17
- package/dist/lib/index.d.ts +111 -17
- package/dist/lib/index.d.ts.map +1 -1
- package/dist/lib/index.js +94 -94
- package/dist/lib/index.js.map +1 -1
- package/dist/lib/index.mjs +93 -94
- package/dist/lib/index.mjs.map +1 -1
- package/dist/lib/protocols/index.d.ts.map +1 -1
- package/dist/lib/protocols/index.js +6 -15
- package/dist/lib/protocols/index.js.map +1 -1
- package/dist/lib/protocols/index.mjs +6 -16
- package/dist/lib/protocols/index.mjs.map +1 -1
- package/dist/lib/protocols/mcp-modern.d.ts.map +1 -1
- package/dist/lib/protocols/mcp-modern.js +87 -12
- package/dist/lib/protocols/mcp-modern.js.map +1 -1
- package/dist/lib/protocols/mcp-modern.mjs +89 -12
- package/dist/lib/protocols/mcp-modern.mjs.map +1 -1
- package/dist/lib/schemas-data/v2.5/_provenance.json +1 -1
- package/dist/lib/server/create-adcp-server.d.mts +6 -0
- package/dist/lib/server/create-adcp-server.d.ts +6 -0
- package/dist/lib/server/create-adcp-server.d.ts.map +1 -1
- package/dist/lib/server/create-adcp-server.js +295 -48
- package/dist/lib/server/create-adcp-server.js.map +1 -1
- package/dist/lib/server/create-adcp-server.mjs +301 -50
- package/dist/lib/server/create-adcp-server.mjs.map +1 -1
- package/dist/lib/server/decisioning/assembly-helpers.d.mts +27 -13
- package/dist/lib/server/decisioning/assembly-helpers.d.ts +27 -13
- package/dist/lib/server/decisioning/assembly-helpers.d.ts.map +1 -1
- package/dist/lib/server/decisioning/assembly-helpers.js +136 -14
- package/dist/lib/server/decisioning/assembly-helpers.js.map +1 -1
- package/dist/lib/server/decisioning/assembly-helpers.mjs +134 -13
- package/dist/lib/server/decisioning/assembly-helpers.mjs.map +1 -1
- package/dist/lib/server/decisioning/index.d.mts +13 -12
- package/dist/lib/server/decisioning/index.d.ts +13 -12
- package/dist/lib/server/decisioning/index.d.ts.map +1 -1
- package/dist/lib/server/decisioning/index.js +6 -0
- package/dist/lib/server/decisioning/index.js.map +1 -1
- package/dist/lib/server/decisioning/index.mjs +9 -3
- package/dist/lib/server/decisioning/index.mjs.map +1 -1
- package/dist/lib/server/decisioning/list-helpers.d.mts +13 -3
- package/dist/lib/server/decisioning/list-helpers.d.ts +13 -3
- package/dist/lib/server/decisioning/list-helpers.d.ts.map +1 -1
- package/dist/lib/server/decisioning/list-helpers.js +13 -3
- package/dist/lib/server/decisioning/list-helpers.js.map +1 -1
- package/dist/lib/server/decisioning/list-helpers.mjs +11 -2
- package/dist/lib/server/decisioning/list-helpers.mjs.map +1 -1
- package/dist/lib/server/decisioning/manifest-helpers.d.mts +5 -3
- package/dist/lib/server/decisioning/manifest-helpers.d.ts +5 -3
- package/dist/lib/server/decisioning/manifest-helpers.d.ts.map +1 -1
- package/dist/lib/server/decisioning/manifest-helpers.js.map +1 -1
- package/dist/lib/server/decisioning/manifest-helpers.mjs.map +1 -1
- package/dist/lib/server/decisioning/proposal/dispatch.d.mts +2 -2
- package/dist/lib/server/decisioning/proposal/dispatch.d.ts +2 -2
- package/dist/lib/server/decisioning/proposal/dispatch.d.ts.map +1 -1
- package/dist/lib/server/decisioning/proposal/dispatch.js.map +1 -1
- package/dist/lib/server/decisioning/proposal/dispatch.mjs.map +1 -1
- package/dist/lib/server/decisioning/proposal/index.d.mts +1 -1
- package/dist/lib/server/decisioning/proposal/index.d.ts +1 -1
- package/dist/lib/server/decisioning/proposal/index.d.ts.map +1 -1
- package/dist/lib/server/decisioning/proposal/index.js.map +1 -1
- package/dist/lib/server/decisioning/proposal/index.mjs.map +1 -1
- package/dist/lib/server/decisioning/proposal/lifecycle.d.mts +3 -2
- package/dist/lib/server/decisioning/proposal/lifecycle.d.ts +3 -2
- package/dist/lib/server/decisioning/proposal/lifecycle.d.ts.map +1 -1
- package/dist/lib/server/decisioning/proposal/lifecycle.js.map +1 -1
- package/dist/lib/server/decisioning/proposal/lifecycle.mjs.map +1 -1
- package/dist/lib/server/decisioning/proposal/mock-manager.d.mts +3 -3
- package/dist/lib/server/decisioning/proposal/mock-manager.d.ts +3 -3
- package/dist/lib/server/decisioning/proposal/mock-manager.d.ts.map +1 -1
- package/dist/lib/server/decisioning/proposal/mock-manager.js.map +1 -1
- package/dist/lib/server/decisioning/proposal/mock-manager.mjs.map +1 -1
- package/dist/lib/server/decisioning/proposal/types.d.mts +10 -6
- package/dist/lib/server/decisioning/proposal/types.d.ts +10 -6
- package/dist/lib/server/decisioning/proposal/types.d.ts.map +1 -1
- package/dist/lib/server/decisioning/proposal/types.js.map +1 -1
- package/dist/lib/server/decisioning/proposal/types.mjs.map +1 -1
- package/dist/lib/server/decisioning/runtime/from-platform.d.mts +21 -2
- package/dist/lib/server/decisioning/runtime/from-platform.d.ts +21 -2
- package/dist/lib/server/decisioning/runtime/from-platform.d.ts.map +1 -1
- package/dist/lib/server/decisioning/runtime/from-platform.js +754 -81
- package/dist/lib/server/decisioning/runtime/from-platform.js.map +1 -1
- package/dist/lib/server/decisioning/runtime/from-platform.mjs +763 -81
- package/dist/lib/server/decisioning/runtime/from-platform.mjs.map +1 -1
- package/dist/lib/server/decisioning/specialisms/brand-rights.d.mts +9 -9
- package/dist/lib/server/decisioning/specialisms/brand-rights.d.ts +9 -9
- package/dist/lib/server/decisioning/specialisms/brand-rights.d.ts.map +1 -1
- package/dist/lib/server/decisioning/specialisms/brand-rights.js.map +1 -1
- package/dist/lib/server/decisioning/specialisms/content-standards.d.mts +16 -16
- package/dist/lib/server/decisioning/specialisms/content-standards.d.ts +16 -16
- package/dist/lib/server/decisioning/specialisms/content-standards.d.ts.map +1 -1
- package/dist/lib/server/decisioning/specialisms/content-standards.js.map +1 -1
- package/dist/lib/server/decisioning/specialisms/creative-ad-server.d.mts +15 -13
- package/dist/lib/server/decisioning/specialisms/creative-ad-server.d.ts +15 -13
- package/dist/lib/server/decisioning/specialisms/creative-ad-server.d.ts.map +1 -1
- package/dist/lib/server/decisioning/specialisms/creative-ad-server.js.map +1 -1
- package/dist/lib/server/decisioning/specialisms/creative.d.mts +12 -11
- package/dist/lib/server/decisioning/specialisms/creative.d.ts +12 -11
- package/dist/lib/server/decisioning/specialisms/creative.d.ts.map +1 -1
- package/dist/lib/server/decisioning/specialisms/creative.js.map +1 -1
- package/dist/lib/server/decisioning/specialisms/sales.d.mts +25 -15
- package/dist/lib/server/decisioning/specialisms/sales.d.ts +25 -15
- package/dist/lib/server/decisioning/specialisms/sales.d.ts.map +1 -1
- package/dist/lib/server/decisioning/specialisms/sales.js.map +1 -1
- package/dist/lib/server/index.d.mts +3 -3
- package/dist/lib/server/index.d.ts +3 -3
- package/dist/lib/server/index.d.ts.map +1 -1
- package/dist/lib/server/index.js +72 -72
- package/dist/lib/server/index.js.map +1 -1
- package/dist/lib/server/index.mjs +36 -36
- package/dist/lib/server/index.mjs.map +1 -1
- package/dist/lib/server/legacy/v5/index.d.mts +1 -0
- package/dist/lib/server/legacy/v5/index.d.ts +1 -0
- package/dist/lib/server/legacy/v5/index.d.ts.map +1 -1
- package/dist/lib/server/legacy/v5/index.js +74 -1
- package/dist/lib/server/legacy/v5/index.js.map +1 -1
- package/dist/lib/server/legacy/v5/index.mjs +75 -1
- package/dist/lib/server/legacy/v5/index.mjs.map +1 -1
- package/dist/lib/server/responses.d.mts +2 -2
- package/dist/lib/server/responses.d.ts +2 -2
- package/dist/lib/server/responses.js.map +1 -1
- package/dist/lib/server/responses.mjs.map +1 -1
- package/dist/lib/testing/client.js +1 -1
- package/dist/lib/testing/client.js.map +1 -1
- package/dist/lib/testing/client.mjs +1 -1
- package/dist/lib/testing/client.mjs.map +1 -1
- package/dist/lib/testing/scenarios/brand-rights.js +6 -6
- package/dist/lib/testing/scenarios/brand-rights.js.map +1 -1
- package/dist/lib/testing/scenarios/brand-rights.mjs +6 -6
- package/dist/lib/testing/scenarios/brand-rights.mjs.map +1 -1
- package/dist/lib/testing/scenarios/creative.js +7 -7
- package/dist/lib/testing/scenarios/creative.js.map +1 -1
- package/dist/lib/testing/scenarios/creative.mjs +7 -7
- package/dist/lib/testing/scenarios/creative.mjs.map +1 -1
- package/dist/lib/testing/scenarios/discovery.js +2 -2
- package/dist/lib/testing/scenarios/discovery.js.map +1 -1
- package/dist/lib/testing/scenarios/discovery.mjs +2 -2
- package/dist/lib/testing/scenarios/discovery.mjs.map +1 -1
- package/dist/lib/testing/scenarios/edge-cases.js +11 -11
- package/dist/lib/testing/scenarios/edge-cases.js.map +1 -1
- package/dist/lib/testing/scenarios/edge-cases.mjs +11 -11
- package/dist/lib/testing/scenarios/edge-cases.mjs.map +1 -1
- package/dist/lib/testing/scenarios/governance.d.ts.map +1 -1
- package/dist/lib/testing/scenarios/governance.js +16 -22
- package/dist/lib/testing/scenarios/governance.js.map +1 -1
- package/dist/lib/testing/scenarios/governance.mjs +16 -22
- package/dist/lib/testing/scenarios/governance.mjs.map +1 -1
- package/dist/lib/testing/scenarios/media-buy.js +5 -5
- package/dist/lib/testing/scenarios/media-buy.js.map +1 -1
- package/dist/lib/testing/scenarios/media-buy.mjs +5 -5
- package/dist/lib/testing/scenarios/media-buy.mjs.map +1 -1
- package/dist/lib/testing/scenarios/schema-compliance.js +1 -1
- package/dist/lib/testing/scenarios/schema-compliance.js.map +1 -1
- package/dist/lib/testing/scenarios/schema-compliance.mjs +1 -1
- package/dist/lib/testing/scenarios/schema-compliance.mjs.map +1 -1
- package/dist/lib/testing/scenarios/trusted-match.d.ts.map +1 -1
- package/dist/lib/testing/scenarios/trusted-match.js +4 -3
- package/dist/lib/testing/scenarios/trusted-match.js.map +1 -1
- package/dist/lib/testing/scenarios/trusted-match.mjs +4 -3
- package/dist/lib/testing/scenarios/trusted-match.mjs.map +1 -1
- package/dist/lib/testing/storyboard/shape-drift-hints.d.mts +3 -4
- package/dist/lib/testing/storyboard/shape-drift-hints.d.ts +3 -4
- package/dist/lib/testing/storyboard/shape-drift-hints.d.ts.map +1 -1
- package/dist/lib/testing/storyboard/shape-drift-hints.js +27 -27
- package/dist/lib/testing/storyboard/shape-drift-hints.js.map +1 -1
- package/dist/lib/testing/storyboard/shape-drift-hints.mjs +27 -27
- package/dist/lib/testing/storyboard/shape-drift-hints.mjs.map +1 -1
- package/dist/lib/testing/storyboard/task-map.d.ts.map +1 -1
- package/dist/lib/testing/storyboard/task-map.js +43 -5
- package/dist/lib/testing/storyboard/task-map.js.map +1 -1
- package/dist/lib/testing/storyboard/task-map.mjs +43 -5
- package/dist/lib/testing/storyboard/task-map.mjs.map +1 -1
- package/dist/lib/testing/test-helpers.d.mts +2 -2
- package/dist/lib/testing/test-helpers.d.ts +2 -2
- package/dist/lib/testing/test-helpers.js.map +1 -1
- package/dist/lib/testing/test-helpers.mjs.map +1 -1
- package/dist/lib/types/adcp.d.mts +14 -14
- package/dist/lib/types/adcp.d.ts +14 -14
- package/dist/lib/types/adcp.d.ts.map +1 -1
- package/dist/lib/types/adcp.js.map +1 -1
- package/dist/lib/types/adcp.mjs.map +1 -1
- package/dist/lib/types/index.d.mts +2 -2
- package/dist/lib/types/index.d.ts +2 -2
- package/dist/lib/types/index.d.ts.map +1 -1
- package/dist/lib/types/index.js.map +1 -1
- package/dist/lib/types/index.mjs.map +1 -1
- package/dist/lib/utils/capabilities.d.mts +6 -0
- package/dist/lib/utils/capabilities.d.ts +6 -0
- package/dist/lib/utils/capabilities.d.ts.map +1 -1
- package/dist/lib/utils/capabilities.js +4 -0
- package/dist/lib/utils/capabilities.js.map +1 -1
- package/dist/lib/utils/capabilities.mjs +4 -0
- package/dist/lib/utils/capabilities.mjs.map +1 -1
- package/dist/lib/utils/creative-delivery.d.mts +52 -10
- package/dist/lib/utils/creative-delivery.d.ts +52 -10
- package/dist/lib/utils/creative-delivery.d.ts.map +1 -1
- package/dist/lib/utils/creative-delivery.js +104 -9
- package/dist/lib/utils/creative-delivery.js.map +1 -1
- package/dist/lib/utils/creative-delivery.mjs +104 -8
- package/dist/lib/utils/creative-delivery.mjs.map +1 -1
- package/dist/lib/utils/format-assets.d.mts +41 -38
- package/dist/lib/utils/format-assets.d.ts +41 -38
- package/dist/lib/utils/format-assets.d.ts.map +1 -1
- package/dist/lib/utils/format-assets.js +3 -1
- package/dist/lib/utils/format-assets.js.map +1 -1
- package/dist/lib/utils/format-assets.mjs +3 -1
- package/dist/lib/utils/format-assets.mjs.map +1 -1
- package/dist/lib/utils/index.d.mts +8 -8
- package/dist/lib/utils/index.d.ts +8 -8
- package/dist/lib/utils/index.d.ts.map +1 -1
- package/dist/lib/utils/index.js +11 -9
- package/dist/lib/utils/index.js.map +1 -1
- package/dist/lib/utils/index.mjs +9 -8
- package/dist/lib/utils/index.mjs.map +1 -1
- package/dist/lib/utils/preview-utils.d.mts +11 -8
- package/dist/lib/utils/preview-utils.d.ts +11 -8
- package/dist/lib/utils/preview-utils.d.ts.map +1 -1
- package/dist/lib/utils/preview-utils.js +4 -4
- package/dist/lib/utils/preview-utils.js.map +1 -1
- package/dist/lib/utils/preview-utils.mjs +3 -3
- package/dist/lib/utils/preview-utils.mjs.map +1 -1
- package/dist/lib/utils/tool-request-schemas.d.mts +2 -2
- package/dist/lib/utils/tool-request-schemas.d.ts +2 -2
- package/dist/lib/v2/projection/aao-reference-formats.json +492 -0
- package/dist/lib/v2/projection/augment-response.d.mts +31 -11
- package/dist/lib/v2/projection/augment-response.d.ts +31 -11
- package/dist/lib/v2/projection/augment-response.d.ts.map +1 -1
- package/dist/lib/v2/projection/augment-response.js +233 -27
- package/dist/lib/v2/projection/augment-response.js.map +1 -1
- package/dist/lib/v2/projection/augment-response.mjs +232 -27
- package/dist/lib/v2/projection/augment-response.mjs.map +1 -1
- package/dist/lib/v2/projection/builders.d.mts +7 -6
- package/dist/lib/v2/projection/builders.d.ts +7 -6
- package/dist/lib/v2/projection/builders.d.ts.map +1 -1
- package/dist/lib/v2/projection/builders.js +11 -8
- package/dist/lib/v2/projection/builders.js.map +1 -1
- package/dist/lib/v2/projection/builders.mjs +9 -6
- package/dist/lib/v2/projection/builders.mjs.map +1 -1
- package/dist/lib/v2/projection/catalog-snapshot.d.mts +47 -0
- package/dist/lib/v2/projection/catalog-snapshot.d.ts +48 -0
- package/dist/lib/v2/projection/catalog-snapshot.d.ts.map +1 -0
- package/dist/lib/v2/projection/catalog-snapshot.js +291 -0
- package/dist/lib/v2/projection/catalog-snapshot.js.map +1 -0
- package/dist/lib/v2/projection/catalog-snapshot.mjs +265 -0
- package/dist/lib/v2/projection/catalog-snapshot.mjs.map +1 -0
- package/dist/lib/v2/projection/catalog.d.mts +23 -2
- package/dist/lib/v2/projection/catalog.d.ts +23 -2
- package/dist/lib/v2/projection/catalog.d.ts.map +1 -1
- package/dist/lib/v2/projection/catalog.js +34 -10
- package/dist/lib/v2/projection/catalog.js.map +1 -1
- package/dist/lib/v2/projection/catalog.mjs +33 -10
- package/dist/lib/v2/projection/catalog.mjs.map +1 -1
- package/dist/lib/v2/projection/creative-delivery.d.mts +157 -0
- package/dist/lib/v2/projection/creative-delivery.d.ts +158 -0
- package/dist/lib/v2/projection/creative-delivery.d.ts.map +1 -0
- package/dist/lib/v2/projection/creative-delivery.js +967 -0
- package/dist/lib/v2/projection/creative-delivery.js.map +1 -0
- package/dist/lib/v2/projection/creative-delivery.mjs +941 -0
- package/dist/lib/v2/projection/creative-delivery.mjs.map +1 -0
- package/dist/lib/v2/projection/index.d.mts +8 -6
- package/dist/lib/v2/projection/index.d.ts +8 -6
- package/dist/lib/v2/projection/index.d.ts.map +1 -1
- package/dist/lib/v2/projection/index.js +26 -4
- package/dist/lib/v2/projection/index.js.map +1 -1
- package/dist/lib/v2/projection/index.mjs +28 -4
- package/dist/lib/v2/projection/index.mjs.map +1 -1
- package/dist/lib/v2/projection/legacy-metadata.d.mts +21 -0
- package/dist/lib/v2/projection/legacy-metadata.d.ts +22 -0
- package/dist/lib/v2/projection/legacy-metadata.d.ts.map +1 -0
- package/dist/lib/v2/projection/legacy-metadata.js +73 -0
- package/dist/lib/v2/projection/legacy-metadata.js.map +1 -0
- package/dist/lib/v2/projection/legacy-metadata.mjs +45 -0
- package/dist/lib/v2/projection/legacy-metadata.mjs.map +1 -0
- package/dist/lib/v2/projection/types.d.mts +29 -23
- package/dist/lib/v2/projection/types.d.ts +29 -23
- package/dist/lib/v2/projection/types.d.ts.map +1 -1
- package/dist/lib/v2/projection/types.js.map +1 -1
- package/dist/lib/v2/projection/v1-to-v2.d.mts +29 -6
- package/dist/lib/v2/projection/v1-to-v2.d.ts +29 -6
- package/dist/lib/v2/projection/v1-to-v2.d.ts.map +1 -1
- package/dist/lib/v2/projection/v1-to-v2.js +218 -9
- package/dist/lib/v2/projection/v1-to-v2.js.map +1 -1
- package/dist/lib/v2/projection/v1-to-v2.mjs +219 -10
- package/dist/lib/v2/projection/v1-to-v2.mjs.map +1 -1
- package/dist/lib/v2/projection/v2-to-v1.d.mts +31 -2
- package/dist/lib/v2/projection/v2-to-v1.d.ts +31 -2
- package/dist/lib/v2/projection/v2-to-v1.d.ts.map +1 -1
- package/dist/lib/v2/projection/v2-to-v1.js +138 -7
- package/dist/lib/v2/projection/v2-to-v1.js.map +1 -1
- package/dist/lib/v2/projection/v2-to-v1.mjs +135 -6
- package/dist/lib/v2/projection/v2-to-v1.mjs.map +1 -1
- package/dist/lib/v2/projection/write-side.d.mts +12 -13
- package/dist/lib/v2/projection/write-side.d.ts +12 -13
- package/dist/lib/v2/projection/write-side.d.ts.map +1 -1
- package/dist/lib/v2/projection/write-side.js +16 -12
- package/dist/lib/v2/projection/write-side.js.map +1 -1
- package/dist/lib/v2/projection/write-side.mjs +16 -12
- package/dist/lib/v2/projection/write-side.mjs.map +1 -1
- package/dist/lib/version.d.mts +3 -3
- package/dist/lib/version.d.ts +3 -3
- package/dist/lib/version.d.ts.map +1 -1
- 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/guides/BUILD-AN-AGENT.md +7 -2
- package/docs/guides/PREVIEW-ASSET-DURABILITY.md +6 -2
- package/docs/llms.txt +1 -0
- package/examples/README.md +1 -1
- package/examples/batch-preview-test.ts +3 -3
- package/examples/conversation-client.ts +2 -2
- package/examples/debug-preview-response.ts +1 -1
- package/examples/decisioning-platform-broadcast-tv.ts +10 -4
- package/examples/decisioning-platform-mock-seller.ts +16 -9
- package/examples/decisioning-platform-programmatic.ts +18 -7
- package/examples/error-compliant-server.ts +26 -16
- package/examples/generative-creative-demo.ts +7 -19
- package/examples/hello_creative_adapter_ad_server.ts +37 -16
- package/examples/hello_creative_adapter_template.ts +12 -10
- package/examples/hello_seller_adapter_guaranteed.ts +52 -23
- package/examples/hello_seller_adapter_multi_tenant.ts +6 -3
- package/examples/hello_seller_adapter_non_guaranteed.ts +54 -26
- package/examples/hello_seller_adapter_proposal_mode.ts +40 -14
- package/examples/hello_seller_adapter_social.ts +2 -7
- package/examples/inspect-card-formats.ts +1 -1
- package/examples/simple-getting-started.ts +1 -1
- package/examples/test-helpers-demo.ts +1 -1
- package/package.json +2 -2
- package/skills/build-decisioning-creative-template/SKILL.md +11 -11
- package/skills/build-decisioning-platform/advanced/REFERENCE.md +8 -2
|
@@ -18,14 +18,116 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
18
18
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
19
|
var v2_to_v1_exports = {};
|
|
20
20
|
__export(v2_to_v1_exports, {
|
|
21
|
-
|
|
21
|
+
CanonicalFormatLegacyResolutionError: () => CanonicalFormatLegacyResolutionError,
|
|
22
|
+
projectV2ProductToV1: () => projectV2ProductToV1,
|
|
23
|
+
resolveCanonicalFormatLegacyRefs: () => resolveCanonicalFormatLegacyRefs
|
|
22
24
|
});
|
|
23
25
|
module.exports = __toCommonJS(v2_to_v1_exports);
|
|
24
26
|
var import_registry = require('./registry.js');
|
|
25
27
|
var import_canonical_properties = require('./canonical-properties.js');
|
|
26
28
|
var import_catalog = require('./catalog.js');
|
|
27
29
|
var import_version = require('../../version.js');
|
|
30
|
+
var import_legacy_metadata = require('./legacy-metadata.js');
|
|
28
31
|
const SDK_ID = `@adcp/sdk@${import_version.LIBRARY_VERSION}`;
|
|
32
|
+
class CanonicalFormatLegacyResolutionError extends Error {
|
|
33
|
+
name = "CanonicalFormatLegacyResolutionError";
|
|
34
|
+
}
|
|
35
|
+
function resolverRef(value) {
|
|
36
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
37
|
+
throw new CanonicalFormatLegacyResolutionError("resolver output must be a format-id object");
|
|
38
|
+
}
|
|
39
|
+
const descriptors = Object.getOwnPropertyDescriptors(value);
|
|
40
|
+
for (const descriptor of Object.values(descriptors)) {
|
|
41
|
+
if (descriptor.enumerable && !("value" in descriptor)) {
|
|
42
|
+
throw new CanonicalFormatLegacyResolutionError("resolver output must not contain enumerable accessors");
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
const read = (key) => {
|
|
46
|
+
const descriptor = descriptors[key];
|
|
47
|
+
return descriptor && "value" in descriptor ? descriptor.value : void 0;
|
|
48
|
+
};
|
|
49
|
+
const agentUrl = read("agent_url");
|
|
50
|
+
const id = read("id");
|
|
51
|
+
if (typeof agentUrl !== "string" || agentUrl.length === 0 || typeof id !== "string" || !/^[a-zA-Z0-9_-]+$/.test(id)) {
|
|
52
|
+
throw new CanonicalFormatLegacyResolutionError("resolver output has an invalid agent_url or id");
|
|
53
|
+
}
|
|
54
|
+
try {
|
|
55
|
+
new URL(agentUrl);
|
|
56
|
+
} catch {
|
|
57
|
+
throw new CanonicalFormatLegacyResolutionError("resolver output agent_url must be an absolute URI");
|
|
58
|
+
}
|
|
59
|
+
const width = read("width");
|
|
60
|
+
const height = read("height");
|
|
61
|
+
const durationMs = read("duration_ms");
|
|
62
|
+
if (width === void 0 !== (height === void 0)) {
|
|
63
|
+
throw new CanonicalFormatLegacyResolutionError("resolver output width and height must be provided together");
|
|
64
|
+
}
|
|
65
|
+
if (width !== void 0 && (!Number.isInteger(width) || width < 1)) {
|
|
66
|
+
throw new CanonicalFormatLegacyResolutionError("resolver output width must be a positive integer");
|
|
67
|
+
}
|
|
68
|
+
if (height !== void 0 && (!Number.isInteger(height) || height < 1)) {
|
|
69
|
+
throw new CanonicalFormatLegacyResolutionError("resolver output height must be a positive integer");
|
|
70
|
+
}
|
|
71
|
+
if (durationMs !== void 0 && (typeof durationMs !== "number" || !Number.isFinite(durationMs) || durationMs < 1)) {
|
|
72
|
+
throw new CanonicalFormatLegacyResolutionError("resolver output duration_ms must be a positive number");
|
|
73
|
+
}
|
|
74
|
+
return {
|
|
75
|
+
agent_url: agentUrl,
|
|
76
|
+
id,
|
|
77
|
+
...width !== void 0 ? { width, height } : {},
|
|
78
|
+
...durationMs !== void 0 ? { duration_ms: durationMs } : {}
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
function resolveCanonicalFormatLegacyRefs(resolver, context, requireSingle = false) {
|
|
82
|
+
if (!resolver) return void 0;
|
|
83
|
+
let output;
|
|
84
|
+
try {
|
|
85
|
+
output = resolver(context);
|
|
86
|
+
} catch {
|
|
87
|
+
throw new CanonicalFormatLegacyResolutionError("canonical format legacy resolver threw");
|
|
88
|
+
}
|
|
89
|
+
if (output == null) return void 0;
|
|
90
|
+
try {
|
|
91
|
+
let values;
|
|
92
|
+
if (Array.isArray(output)) {
|
|
93
|
+
const descriptors = Object.getOwnPropertyDescriptors(output);
|
|
94
|
+
const lengthDescriptor = descriptors["length"];
|
|
95
|
+
const length = lengthDescriptor && "value" in lengthDescriptor ? lengthDescriptor.value : void 0;
|
|
96
|
+
if (typeof length !== "number" || !Number.isSafeInteger(length) || length < 0) {
|
|
97
|
+
throw new CanonicalFormatLegacyResolutionError("resolver returned an invalid format-id array");
|
|
98
|
+
}
|
|
99
|
+
values = [];
|
|
100
|
+
for (let index = 0; index < length; index++) {
|
|
101
|
+
const descriptor = descriptors[String(index)];
|
|
102
|
+
if (!descriptor || !("value" in descriptor)) {
|
|
103
|
+
throw new CanonicalFormatLegacyResolutionError(
|
|
104
|
+
"resolver output arrays must be dense data-only format-id arrays"
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
|
+
values.push(descriptor.value);
|
|
108
|
+
}
|
|
109
|
+
} else {
|
|
110
|
+
values = [output];
|
|
111
|
+
}
|
|
112
|
+
if (values.length === 0 || requireSingle && values.length !== 1) {
|
|
113
|
+
throw new CanonicalFormatLegacyResolutionError(
|
|
114
|
+
requireSingle ? "resolver must return exactly one format-id for a creative" : "resolver returned no format ids"
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
const refs = values.map(resolverRef);
|
|
118
|
+
return [
|
|
119
|
+
...new Map(
|
|
120
|
+
refs.map((ref) => [
|
|
121
|
+
`${ref.agent_url}|${ref.id}|${ref.width ?? ""}|${ref.height ?? ""}|${ref.duration_ms ?? ""}`,
|
|
122
|
+
ref
|
|
123
|
+
])
|
|
124
|
+
).values()
|
|
125
|
+
];
|
|
126
|
+
} catch (error) {
|
|
127
|
+
if (error instanceof CanonicalFormatLegacyResolutionError) throw error;
|
|
128
|
+
throw new CanonicalFormatLegacyResolutionError("canonical format legacy resolver returned unreadable output");
|
|
129
|
+
}
|
|
130
|
+
}
|
|
29
131
|
function detectLossyMultiSize(decl) {
|
|
30
132
|
if (decl.format_kind !== "image" && decl.format_kind !== "html5" && decl.format_kind !== "display_tag") {
|
|
31
133
|
return null;
|
|
@@ -63,7 +165,7 @@ function tryFanOutMultiSize(decl, v1Refs) {
|
|
|
63
165
|
}
|
|
64
166
|
return found.length >= 2 ? found : null;
|
|
65
167
|
}
|
|
66
|
-
function projectDeclaration(decl, productId, field) {
|
|
168
|
+
function projectDeclaration(decl, productId, field, options) {
|
|
67
169
|
if (decl.canonical_formats_only === true) {
|
|
68
170
|
return {
|
|
69
171
|
diagnostic: {
|
|
@@ -99,8 +201,9 @@ function projectDeclaration(decl, productId, field) {
|
|
|
99
201
|
}
|
|
100
202
|
};
|
|
101
203
|
}
|
|
102
|
-
|
|
103
|
-
|
|
204
|
+
const declaredLegacyRefs = (0, import_legacy_metadata.legacyFormatRefsForDeclaration)(decl);
|
|
205
|
+
if (declaredLegacyRefs.length > 0) {
|
|
206
|
+
const refs = [...declaredLegacyRefs];
|
|
104
207
|
const lossy = detectLossyMultiSize(decl);
|
|
105
208
|
if (lossy) {
|
|
106
209
|
const declaredCount = lossy.count ?? 0;
|
|
@@ -137,6 +240,32 @@ function projectDeclaration(decl, productId, field) {
|
|
|
137
240
|
if (result.kind === "match") {
|
|
138
241
|
return { v1: result.v1 };
|
|
139
242
|
}
|
|
243
|
+
try {
|
|
244
|
+
const resolved = resolveCanonicalFormatLegacyRefs(options?.canonicalFormatLegacyResolver, {
|
|
245
|
+
source: "product",
|
|
246
|
+
declaration: decl,
|
|
247
|
+
productId,
|
|
248
|
+
field
|
|
249
|
+
});
|
|
250
|
+
if (resolved) return { v1: resolved };
|
|
251
|
+
} catch {
|
|
252
|
+
return {
|
|
253
|
+
diagnostic: {
|
|
254
|
+
source: "sdk",
|
|
255
|
+
sdk_id: SDK_ID,
|
|
256
|
+
field,
|
|
257
|
+
code: "FORMAT_PROJECTION_FAILED",
|
|
258
|
+
error: {
|
|
259
|
+
details: {
|
|
260
|
+
format_kind: decl.format_kind,
|
|
261
|
+
product_id: productId,
|
|
262
|
+
format_option_id: decl.format_option_id,
|
|
263
|
+
resolution_failure: "custom_converter_failed"
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
};
|
|
268
|
+
}
|
|
140
269
|
if (result.kind === "ambiguous") {
|
|
141
270
|
return {
|
|
142
271
|
diagnostic: {
|
|
@@ -172,13 +301,13 @@ function projectDeclaration(decl, productId, field) {
|
|
|
172
301
|
}
|
|
173
302
|
};
|
|
174
303
|
}
|
|
175
|
-
function projectV2ProductToV1(v2) {
|
|
304
|
+
function projectV2ProductToV1(v2, options) {
|
|
176
305
|
const format_ids = [];
|
|
177
306
|
const diagnostics = [];
|
|
178
307
|
for (let i = 0; i < v2.format_options.length; i++) {
|
|
179
308
|
const decl = v2.format_options[i];
|
|
180
309
|
const field = `products[${v2.product_id}].format_options[${i}]`;
|
|
181
|
-
const { v1, diagnostic } = projectDeclaration(decl, v2.product_id, field);
|
|
310
|
+
const { v1, diagnostic } = projectDeclaration(decl, v2.product_id, field, options);
|
|
182
311
|
if (v1) {
|
|
183
312
|
if (Array.isArray(v1)) {
|
|
184
313
|
for (const id of v1) format_ids.push(id);
|
|
@@ -198,6 +327,8 @@ function projectV2ProductToV1(v2) {
|
|
|
198
327
|
}
|
|
199
328
|
// Annotate the CommonJS export names for ESM import in node:
|
|
200
329
|
0 && (module.exports = {
|
|
201
|
-
|
|
330
|
+
CanonicalFormatLegacyResolutionError,
|
|
331
|
+
projectV2ProductToV1,
|
|
332
|
+
resolveCanonicalFormatLegacyRefs
|
|
202
333
|
});
|
|
203
334
|
//# sourceMappingURL=v2-to-v1.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/lib/v2/projection/v2-to-v1.ts"],"sourcesContent":["/**\n * v2 → v1 Product projection (the downgrade direction).\n *\n * Used when the SDK negotiated to a v1 seller for a buyer that wrote V2\n * code. The 8.0 design at `docs/development/v3.1-sdk-design.md` makes V2\n * the public mental model — adopters never see v1 vocabulary on\n * `Product` — so this is the symmetric counterpart to v1→v2 projection\n * on the read side.\n *\n * Resolution order per declaration (aligns with the normative rules in\n * `core/registries/v1-canonical-mapping.json` and the canonical schemas'\n * `v1_translatable` field):\n *\n * 1. `format_kind: \"custom\"` + `canonical_formats_only: true` → no v1\n * emit. Seller has explicitly opted out. SDK-local diagnostic\n * `FORMAT_DECLARATION_V1_NOT_APPLICABLE` surfaces the opt-out so\n * v1-only buyers see why a product disappeared.\n * 2. Canonical declares `v1_translatable: false` (the 4 inherently-v2\n * canonicals at 3.1 GA: image_carousel, sponsored_placement,\n * responsive_creative, agent_placement) → no v1 emit. SDK-local\n * `CANONICAL_NOT_V1_TRANSLATABLE` diagnostic. **Spec is explicit:\n * MUST NOT emit FORMAT_PROJECTION_FAILED** — these aren't\n * registry-coverage gaps, they're structural unreachability.\n * 3. `v1_format_ref` present → use verbatim. Seller-asserted v1↔v2\n * pairing is the only normative path to a specific v1 `format_id`.\n * 4. Registry has invertible literal match (`format_id_glob` with no\n * `*`, params narrow compatibly) → MAY synthesize a v1 `format_id`,\n * but the projection is **non-normative** per the registry's\n * direction-of-truth statement. Downstream consumers MUST NOT\n * depend on this — and the spec explicitly notes that a synthesized\n * `agent_url` is implementation-defined inter-SDK divergence. We\n * do synthesize (best-effort, surfaces the registry's invertible\n * entries) but flag the result as non-normative via the registry\n * lookup return type.\n * 5. Registry has family-level matches (structural or wildcarded\n * globs) but none invertible → `FORMAT_DECLARATION_V1_AMBIGUOUS`.\n * Spec code. Seller's path: add `v1_format_ref` to disambiguate.\n * 6. Registry has no entries for this canonical (and v1_translatable\n * is true) → `FORMAT_PROJECTION_FAILED`. Spec code. Registry-\n * coverage gap; correctable by filing a registry PR.\n *\n * SDK identity for `sdk_id` is read from package.json at module-load\n * time so multi-hop deduplication can pin diagnostics to the SDK that\n * emitted them.\n */\n\nimport type { V2Product, V2ProductFormatDeclaration, V1Product, V1FormatId, ProjectionDiagnostic } from './types';\nimport { reverseLookup } from './registry';\nimport { isCanonicalV1Translatable } from './canonical-properties';\nimport { findCatalogEntryByCanonicalAndSize, parseSizedIdTemplate } from './catalog';\nimport { LIBRARY_VERSION } from '../../version';\n\nconst SDK_ID = `@adcp/sdk@${LIBRARY_VERSION}`;\n\nexport interface V2ToV1Result {\n v1: V1Product;\n diagnostics: ProjectionDiagnostic[];\n}\n\n/**\n * Inspect a v2 declaration's params for multi-size or responsive-range\n * shapes that a single v1 `format_id` can't represent. Returns the\n * detected mode + count so the caller emits the diagnostic with full\n * context. Returns null when params declare a single fixed (width,\n * height) — the case that round-trips through v1 cleanly.\n */\nfunction detectLossyMultiSize(\n decl: V2ProductFormatDeclaration\n): { mode: 'sizes' | 'responsive_range'; count?: number } | null {\n // Only image/html5/display_tag canonicals carry these new size modes\n // at 3.1 GA. Other canonicals (video_*, audio_*) use their own param\n // shapes that the v1 format_id can carry inline.\n if (decl.format_kind !== 'image' && decl.format_kind !== 'html5' && decl.format_kind !== 'display_tag') {\n return null;\n }\n const params = decl.params ?? {};\n if (Array.isArray(params.sizes) && params.sizes.length > 1) {\n return { mode: 'sizes', count: params.sizes.length };\n }\n // Responsive range mode: any of min_*/max_* present means the v1\n // format_id (which carries exactly one width+height pair) is lossy.\n if (\n typeof params.min_width === 'number' ||\n typeof params.max_width === 'number' ||\n typeof params.min_height === 'number' ||\n typeof params.max_height === 'number'\n ) {\n return { mode: 'responsive_range' };\n }\n return null;\n}\n\n/**\n * Try to fan out a multi-size v2 declaration to N v1 format_ids by\n * looking up each declared size in the AAO catalog. When successful,\n * v1 buyers see ALL the sizes the v2 declaration covers — not just\n * the seller-asserted rep. The spec doesn't require this (the rep +\n * lossy advisory is the minimum-bar projection) but the catalog\n * already publishes the per-size entries, so the SDK can use them\n * without inter-SDK divergence.\n *\n * Returns the fanned-out v1 ids when at least 2 sizes resolved\n * (otherwise the rep alone is fine; no value in fan-out). The lossy\n * advisory is still emitted — its details now reflect how many\n * sizes were actually covered vs declared.\n */\nfunction tryFanOutMultiSize(decl: V2ProductFormatDeclaration, v1Refs: V1FormatId[]): V1FormatId[] | null {\n const sizes = decl.params?.sizes;\n if (!Array.isArray(sizes) || sizes.length <= 1) return null;\n // Parse the first seller-asserted ref's id to extract its\n // `<prefix>_<W>x<H>_<suffix>` template, then constrain the fan-out\n // to siblings sharing the same prefix + suffix. Otherwise multiple\n // entries with the same `canonical:` annotation but different\n // families (e.g., image's `display_*_image` and `display_*_generative`\n // both annotated `canonical: image`) would collide and the SDK could\n // emit ids from the wrong family.\n const rep = v1Refs[0];\n if (!rep?.id) return null;\n const template = parseSizedIdTemplate(rep.id);\n const lookupOpts = template ? { prefix: template.prefix, suffix: template.suffix } : undefined;\n\n const found: V1FormatId[] = [];\n for (const size of sizes) {\n if (!size || typeof size !== 'object') continue;\n const sz = size as { width?: unknown; height?: unknown };\n if (typeof sz.width !== 'number' || typeof sz.height !== 'number') continue;\n const entry = findCatalogEntryByCanonicalAndSize(decl.format_kind, sz.width, sz.height, rep.agent_url, lookupOpts);\n if (entry) {\n found.push({\n agent_url: entry.format_id.agent_url,\n id: entry.format_id.id,\n width: sz.width,\n height: sz.height,\n });\n }\n }\n return found.length >= 2 ? found : null;\n}\n\n/**\n * Project a single V2 declaration. Returns one v1 format_id (the\n * common case) OR a fanned-out list of v1 format_ids (multi-size\n * fan-out) OR a diagnostic, plus an optional advisory diagnostic\n * (multi-size loses some sizes). The structural invariant: every\n * declaration produces AT LEAST ONE of (v1 emit, diagnostic) — may\n * produce both.\n */\nfunction projectDeclaration(\n decl: V2ProductFormatDeclaration,\n productId: string,\n field: string\n): { v1?: V1FormatId | V1FormatId[]; diagnostic?: ProjectionDiagnostic } {\n // Step 1: seller-asserted product-level opt-out. canonical_formats_only\n // is REQUIRED on `custom` declarations without v1_format_ref, but it's\n // ALSO valid on any non-custom canonical when the seller wants to opt\n // out of v1 emission for this product (e.g. the catalog has no v1 entry\n // for the format yet — `audio_daast` is the 3.1 example).\n if (decl.canonical_formats_only === true) {\n return {\n diagnostic: {\n source: 'sdk',\n sdk_id: SDK_ID,\n field,\n code: 'FORMAT_DECLARATION_V1_NOT_APPLICABLE',\n error: {\n details: {\n format_kind: decl.format_kind,\n product_id: productId,\n format_option_id: decl.format_option_id,\n reason: 'canonical_formats_only',\n },\n },\n },\n };\n }\n\n // Step 2: canonical-level structural unreachability.\n if (!isCanonicalV1Translatable(decl.format_kind)) {\n return {\n diagnostic: {\n source: 'sdk',\n sdk_id: SDK_ID,\n field,\n code: 'CANONICAL_NOT_V1_TRANSLATABLE',\n error: {\n details: {\n format_kind: decl.format_kind,\n product_id: productId,\n format_option_id: decl.format_option_id,\n },\n },\n },\n };\n }\n\n // Step 3: seller-asserted v1 link — the only normative path to a\n // specific v1 format_id. The spec normatively requires `v1_format_ref`\n // to be an array (single-ref is `[{...}]`); multi-size SHOULD carry\n // one entry per size.\n //\n // When the seller-asserted refs cover every declared size, emit all\n // refs verbatim — no advisory needed. When refs < sizes, emit what\n // the seller asserted (+ catalog-driven fan-out for missing sizes when\n // available) and surface the lossy advisory so the buyer knows the\n // v1 wire is partial-coverage. Responsive ranges (min_*/max_*) are\n // always advisory — the v1 wire can't carry a range.\n if (decl.v1_format_ref && decl.v1_format_ref.length > 0) {\n const refs = decl.v1_format_ref;\n const lossy = detectLossyMultiSize(decl);\n if (lossy) {\n // Sizes mode: seller asserted ≥1 ref; try to widen via catalog\n // lookup when refs < sizes; otherwise emit seller's refs verbatim.\n // Responsive range: refs are the seller's representative anchors;\n // can't be widened.\n const declaredCount = lossy.count ?? 0;\n const sellerCoversAllSizes = lossy.mode === 'sizes' && refs.length >= declaredCount;\n if (sellerCoversAllSizes) {\n return { v1: refs };\n }\n const fanned = lossy.mode === 'sizes' && refs.length < declaredCount ? tryFanOutMultiSize(decl, refs) : null;\n // Merge: prefer the fanned-out catalog entries when they cover more sizes\n // than the seller's refs; otherwise emit seller's refs.\n const emit = fanned && fanned.length > refs.length ? fanned : refs;\n return {\n v1: emit,\n diagnostic: {\n source: 'sdk',\n sdk_id: SDK_ID,\n field,\n code: 'FORMAT_DECLARATION_V1_LOSSY_MULTI_SIZE',\n error: {\n details: {\n format_kind: decl.format_kind,\n product_id: productId,\n format_option_id: decl.format_option_id,\n size_mode: lossy.mode,\n declared_sizes_count: lossy.count,\n emitted_sizes_count: emit.length,\n v1_emit_represents_size: refs[0] ? { width: refs[0].width, height: refs[0].height } : undefined,\n },\n },\n },\n };\n }\n return { v1: refs };\n }\n\n // Steps 4-6: registry lookup.\n const result = reverseLookup(decl.format_kind, decl.params);\n if (result.kind === 'match') {\n // Step 4: non-normative best-effort. Documented limitation: the\n // synthesized agent_url is implementation-defined; downstream\n // consumers MUST NOT depend on this projection.\n return { v1: result.v1 };\n }\n if (result.kind === 'ambiguous') {\n // Step 5: family known, specific format not pickable.\n return {\n diagnostic: {\n source: 'sdk',\n sdk_id: SDK_ID,\n field,\n code: 'FORMAT_DECLARATION_V1_AMBIGUOUS',\n error: {\n details: {\n format_kind: decl.format_kind,\n product_id: productId,\n format_option_id: decl.format_option_id,\n registry_matches: result.matchedEntries,\n },\n },\n },\n };\n }\n // Step 6: registry-coverage gap.\n return {\n diagnostic: {\n source: 'sdk',\n sdk_id: SDK_ID,\n field,\n code: 'FORMAT_PROJECTION_FAILED',\n error: {\n details: {\n format_kind: decl.format_kind,\n product_id: productId,\n format_option_id: decl.format_option_id,\n resolution_failure: 'no_registry_match',\n },\n },\n },\n };\n}\n\n/**\n * Project a V2 Product to a v1 Product. Drops format_options from the\n * output and rebuilds format_ids per the resolution order.\n *\n * The function always returns a Product shape so adopters can inspect\n * what got dropped via diagnostics without parallel happy/sad-path\n * branches. Caller filters out v1 Products with empty format_ids before\n * sending to a v1-only seller (the spec requires `format_ids` to have\n * minItems: 1).\n */\nexport function projectV2ProductToV1(v2: V2Product): V2ToV1Result {\n const format_ids: V1FormatId[] = [];\n const diagnostics: ProjectionDiagnostic[] = [];\n\n for (let i = 0; i < v2.format_options.length; i++) {\n const decl = v2.format_options[i]!;\n const field = `products[${v2.product_id}].format_options[${i}]`;\n const { v1, diagnostic } = projectDeclaration(decl, v2.product_id, field);\n if (v1) {\n if (Array.isArray(v1)) {\n for (const id of v1) format_ids.push(id);\n } else {\n format_ids.push(v1);\n }\n }\n if (diagnostic) diagnostics.push(diagnostic);\n }\n\n const { format_options: _drop, ...rest } = v2;\n void _drop;\n const v1Product: V1Product = {\n ...(rest as Omit<V2Product, 'format_options'>),\n format_ids,\n } as V1Product;\n\n return { v1: v1Product, diagnostics };\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../../../src/lib/v2/projection/v2-to-v1.ts"],"sourcesContent":["/**\n * v2 → v1 Product projection (the downgrade direction).\n *\n * Used when the SDK negotiated to a v1 seller for a buyer that wrote V2\n * code. The 8.0 design at `docs/development/v3.1-sdk-design.md` makes V2\n * the public mental model — adopters never see v1 vocabulary on\n * `Product` — so this is the symmetric counterpart to v1→v2 projection\n * on the read side.\n *\n * Resolution order per declaration (aligns with the normative rules in\n * `core/registries/v1-canonical-mapping.json` and the canonical schemas'\n * `v1_translatable` field):\n *\n * 1. `format_kind: \"custom\"` + `canonical_formats_only: true` → no v1\n * emit. Seller has explicitly opted out. SDK-local diagnostic\n * `FORMAT_DECLARATION_V1_NOT_APPLICABLE` surfaces the opt-out so\n * v1-only buyers see why a product disappeared.\n * 2. Canonical declares `v1_translatable: false` (the 4 inherently-v2\n * canonicals at 3.1 GA: image_carousel, sponsored_placement,\n * responsive_creative, agent_placement) → no v1 emit. SDK-local\n * `CANONICAL_NOT_V1_TRANSLATABLE` diagnostic. **Spec is explicit:\n * MUST NOT emit FORMAT_PROJECTION_FAILED** — these aren't\n * registry-coverage gaps, they're structural unreachability.\n * 3. `v1_format_ref` present → use verbatim. Seller-asserted v1↔v2\n * pairing is the only normative path to a specific v1 `format_id`.\n * 4. Registry has invertible literal match (`format_id_glob` with no\n * `*`, params narrow compatibly) → MAY synthesize a v1 `format_id`,\n * but the projection is **non-normative** per the registry's\n * direction-of-truth statement. Downstream consumers MUST NOT\n * depend on this — and the spec explicitly notes that a synthesized\n * `agent_url` is implementation-defined inter-SDK divergence. We\n * do synthesize (best-effort, surfaces the registry's invertible\n * entries) but flag the result as non-normative via the registry\n * lookup return type.\n * 5. Registry has family-level matches (structural or wildcarded\n * globs) but none invertible → `FORMAT_DECLARATION_V1_AMBIGUOUS`.\n * Spec code. Seller's path: add `v1_format_ref` to disambiguate.\n * 6. Registry has no entries for this canonical (and v1_translatable\n * is true) → `FORMAT_PROJECTION_FAILED`. Spec code. Registry-\n * coverage gap; correctable by filing a registry PR.\n *\n * SDK identity for `sdk_id` is read from package.json at module-load\n * time so multi-hop deduplication can pin diagnostics to the SDK that\n * emitted them.\n */\n\nimport type { V2Product, V2ProductFormatDeclaration, V1Product, V1FormatId, ProjectionDiagnostic } from './types';\nimport { reverseLookup } from './registry';\nimport { isCanonicalV1Translatable } from './canonical-properties';\nimport { findCatalogEntryByCanonicalAndSize, parseSizedIdTemplate } from './catalog';\nimport { LIBRARY_VERSION } from '../../version';\nimport { legacyFormatRefsForDeclaration } from './legacy-metadata';\n\nconst SDK_ID = `@adcp/sdk@${LIBRARY_VERSION}`;\n\nexport interface CanonicalProductFormatLegacyResolutionContext {\n source: 'product';\n declaration: Readonly<V2ProductFormatDeclaration>;\n productId: string;\n field: string;\n}\n\nexport interface CanonicalCreativeFormatLegacyResolutionContext {\n source: 'creative';\n creative: Readonly<Record<string, unknown>>;\n selector: Readonly<Record<string, unknown>>;\n operation: string;\n field: string;\n}\n\nexport interface CanonicalSelectorFormatLegacyResolutionContext {\n source: 'selector';\n selector: Readonly<Record<string, unknown>>;\n operation: string;\n field: string;\n}\n\nexport type CanonicalFormatLegacyResolutionContext =\n | CanonicalProductFormatLegacyResolutionContext\n | CanonicalCreativeFormatLegacyResolutionContext\n | CanonicalSelectorFormatLegacyResolutionContext;\n\n/** Explicit canonical → legacy resolver for persisted routes the SDK cannot safely infer. */\nexport type CanonicalFormatLegacyResolver = (\n context: CanonicalFormatLegacyResolutionContext\n) => V1FormatId | readonly V1FormatId[] | null | undefined;\n\nexport interface V2ToV1ProjectionOptions {\n canonicalFormatLegacyResolver?: CanonicalFormatLegacyResolver;\n}\n\nexport class CanonicalFormatLegacyResolutionError extends Error {\n readonly name = 'CanonicalFormatLegacyResolutionError' as const;\n}\n\nfunction resolverRef(value: unknown): V1FormatId {\n if (value === null || typeof value !== 'object' || Array.isArray(value)) {\n throw new CanonicalFormatLegacyResolutionError('resolver output must be a format-id object');\n }\n const descriptors = Object.getOwnPropertyDescriptors(value);\n for (const descriptor of Object.values(descriptors)) {\n if (descriptor.enumerable && !('value' in descriptor)) {\n throw new CanonicalFormatLegacyResolutionError('resolver output must not contain enumerable accessors');\n }\n }\n const read = (key: string): unknown => {\n const descriptor = descriptors[key];\n return descriptor && 'value' in descriptor ? descriptor.value : undefined;\n };\n const agentUrl = read('agent_url');\n const id = read('id');\n if (typeof agentUrl !== 'string' || agentUrl.length === 0 || typeof id !== 'string' || !/^[a-zA-Z0-9_-]+$/.test(id)) {\n throw new CanonicalFormatLegacyResolutionError('resolver output has an invalid agent_url or id');\n }\n try {\n new URL(agentUrl);\n } catch {\n throw new CanonicalFormatLegacyResolutionError('resolver output agent_url must be an absolute URI');\n }\n const width = read('width');\n const height = read('height');\n const durationMs = read('duration_ms');\n if ((width === undefined) !== (height === undefined)) {\n throw new CanonicalFormatLegacyResolutionError('resolver output width and height must be provided together');\n }\n if (width !== undefined && (!Number.isInteger(width) || (width as number) < 1)) {\n throw new CanonicalFormatLegacyResolutionError('resolver output width must be a positive integer');\n }\n if (height !== undefined && (!Number.isInteger(height) || (height as number) < 1)) {\n throw new CanonicalFormatLegacyResolutionError('resolver output height must be a positive integer');\n }\n if (durationMs !== undefined && (typeof durationMs !== 'number' || !Number.isFinite(durationMs) || durationMs < 1)) {\n throw new CanonicalFormatLegacyResolutionError('resolver output duration_ms must be a positive number');\n }\n return {\n agent_url: agentUrl,\n id,\n ...(width !== undefined ? { width: width as number, height: height as number } : {}),\n ...(durationMs !== undefined ? { duration_ms: durationMs } : {}),\n };\n}\n\nexport function resolveCanonicalFormatLegacyRefs(\n resolver: CanonicalFormatLegacyResolver | undefined,\n context: CanonicalFormatLegacyResolutionContext,\n requireSingle = false\n): V1FormatId[] | undefined {\n if (!resolver) return undefined;\n let output: V1FormatId | readonly V1FormatId[] | null | undefined;\n try {\n output = resolver(context);\n } catch {\n throw new CanonicalFormatLegacyResolutionError('canonical format legacy resolver threw');\n }\n if (output == null) return undefined;\n try {\n let values: unknown[];\n if (Array.isArray(output)) {\n const descriptors = Object.getOwnPropertyDescriptors(output) as Record<string, PropertyDescriptor>;\n const lengthDescriptor = descriptors['length'];\n const length = lengthDescriptor && 'value' in lengthDescriptor ? lengthDescriptor.value : undefined;\n if (typeof length !== 'number' || !Number.isSafeInteger(length) || length < 0) {\n throw new CanonicalFormatLegacyResolutionError('resolver returned an invalid format-id array');\n }\n values = [];\n for (let index = 0; index < length; index++) {\n const descriptor = descriptors[String(index)];\n if (!descriptor || !('value' in descriptor)) {\n throw new CanonicalFormatLegacyResolutionError(\n 'resolver output arrays must be dense data-only format-id arrays'\n );\n }\n values.push(descriptor.value);\n }\n } else {\n values = [output];\n }\n if (values.length === 0 || (requireSingle && values.length !== 1)) {\n throw new CanonicalFormatLegacyResolutionError(\n requireSingle ? 'resolver must return exactly one format-id for a creative' : 'resolver returned no format ids'\n );\n }\n const refs = values.map(resolverRef);\n return [\n ...new Map(\n refs.map(ref => [\n `${ref.agent_url}|${ref.id}|${ref.width ?? ''}|${ref.height ?? ''}|${ref.duration_ms ?? ''}`,\n ref,\n ])\n ).values(),\n ];\n } catch (error) {\n if (error instanceof CanonicalFormatLegacyResolutionError) throw error;\n throw new CanonicalFormatLegacyResolutionError('canonical format legacy resolver returned unreadable output');\n }\n}\n\nexport interface V2ToV1Result {\n v1: V1Product;\n diagnostics: ProjectionDiagnostic[];\n}\n\n/**\n * Inspect a v2 declaration's params for multi-size or responsive-range\n * shapes that a single v1 `format_id` can't represent. Returns the\n * detected mode + count so the caller emits the diagnostic with full\n * context. Returns null when params declare a single fixed (width,\n * height) — the case that round-trips through v1 cleanly.\n */\nfunction detectLossyMultiSize(\n decl: V2ProductFormatDeclaration\n): { mode: 'sizes' | 'responsive_range'; count?: number } | null {\n // Only image/html5/display_tag canonicals carry these new size modes\n // at 3.1 GA. Other canonicals (video_*, audio_*) use their own param\n // shapes that the v1 format_id can carry inline.\n if (decl.format_kind !== 'image' && decl.format_kind !== 'html5' && decl.format_kind !== 'display_tag') {\n return null;\n }\n const params = decl.params ?? {};\n if (Array.isArray(params.sizes) && params.sizes.length > 1) {\n return { mode: 'sizes', count: params.sizes.length };\n }\n // Responsive range mode: any of min_*/max_* present means the v1\n // format_id (which carries exactly one width+height pair) is lossy.\n if (\n typeof params.min_width === 'number' ||\n typeof params.max_width === 'number' ||\n typeof params.min_height === 'number' ||\n typeof params.max_height === 'number'\n ) {\n return { mode: 'responsive_range' };\n }\n return null;\n}\n\n/**\n * Try to fan out a multi-size v2 declaration to N v1 format_ids by\n * looking up each declared size in the AAO catalog. When successful,\n * v1 buyers see ALL the sizes the v2 declaration covers — not just\n * the seller-asserted rep. The spec doesn't require this (the rep +\n * lossy advisory is the minimum-bar projection) but the catalog\n * already publishes the per-size entries, so the SDK can use them\n * without inter-SDK divergence.\n *\n * Returns the fanned-out v1 ids when at least 2 sizes resolved\n * (otherwise the rep alone is fine; no value in fan-out). The lossy\n * advisory is still emitted — its details now reflect how many\n * sizes were actually covered vs declared.\n */\nfunction tryFanOutMultiSize(decl: V2ProductFormatDeclaration, v1Refs: V1FormatId[]): V1FormatId[] | null {\n const sizes = decl.params?.sizes;\n if (!Array.isArray(sizes) || sizes.length <= 1) return null;\n // Parse the first seller-asserted ref's id to extract its\n // `<prefix>_<W>x<H>_<suffix>` template, then constrain the fan-out\n // to siblings sharing the same prefix + suffix. Otherwise multiple\n // entries with the same `canonical:` annotation but different\n // families (e.g., image's `display_*_image` and `display_*_generative`\n // both annotated `canonical: image`) would collide and the SDK could\n // emit ids from the wrong family.\n const rep = v1Refs[0];\n if (!rep?.id) return null;\n const template = parseSizedIdTemplate(rep.id);\n const lookupOpts = template ? { prefix: template.prefix, suffix: template.suffix } : undefined;\n\n const found: V1FormatId[] = [];\n for (const size of sizes) {\n if (!size || typeof size !== 'object') continue;\n const sz = size as { width?: unknown; height?: unknown };\n if (typeof sz.width !== 'number' || typeof sz.height !== 'number') continue;\n const entry = findCatalogEntryByCanonicalAndSize(decl.format_kind, sz.width, sz.height, rep.agent_url, lookupOpts);\n if (entry) {\n found.push({\n agent_url: entry.format_id.agent_url,\n id: entry.format_id.id,\n width: sz.width,\n height: sz.height,\n });\n }\n }\n return found.length >= 2 ? found : null;\n}\n\n/**\n * Project a single V2 declaration. Returns one v1 format_id (the\n * common case) OR a fanned-out list of v1 format_ids (multi-size\n * fan-out) OR a diagnostic, plus an optional advisory diagnostic\n * (multi-size loses some sizes). The structural invariant: every\n * declaration produces AT LEAST ONE of (v1 emit, diagnostic) — may\n * produce both.\n */\nfunction projectDeclaration(\n decl: V2ProductFormatDeclaration,\n productId: string,\n field: string,\n options?: V2ToV1ProjectionOptions\n): { v1?: V1FormatId | V1FormatId[]; diagnostic?: ProjectionDiagnostic } {\n // Step 1: seller-asserted product-level opt-out. canonical_formats_only\n // is REQUIRED on `custom` declarations without v1_format_ref, but it's\n // ALSO valid on any non-custom canonical when the seller wants to opt\n // out of v1 emission for this product (e.g. the catalog has no v1 entry\n // for the format yet — `audio_daast` is the 3.1 example).\n if (decl.canonical_formats_only === true) {\n return {\n diagnostic: {\n source: 'sdk',\n sdk_id: SDK_ID,\n field,\n code: 'FORMAT_DECLARATION_V1_NOT_APPLICABLE',\n error: {\n details: {\n format_kind: decl.format_kind,\n product_id: productId,\n format_option_id: decl.format_option_id,\n reason: 'canonical_formats_only',\n },\n },\n },\n };\n }\n\n // Step 2: canonical-level structural unreachability.\n if (!isCanonicalV1Translatable(decl.format_kind)) {\n return {\n diagnostic: {\n source: 'sdk',\n sdk_id: SDK_ID,\n field,\n code: 'CANONICAL_NOT_V1_TRANSLATABLE',\n error: {\n details: {\n format_kind: decl.format_kind,\n product_id: productId,\n format_option_id: decl.format_option_id,\n },\n },\n },\n };\n }\n\n // Step 3: seller-asserted v1 link — the only normative path to a\n // specific v1 format_id. The spec normatively requires `v1_format_ref`\n // to be an array (single-ref is `[{...}]`); multi-size SHOULD carry\n // one entry per size.\n //\n // When the seller-asserted refs cover every declared size, emit all\n // refs verbatim — no advisory needed. When refs < sizes, emit what\n // the seller asserted (+ catalog-driven fan-out for missing sizes when\n // available) and surface the lossy advisory so the buyer knows the\n // v1 wire is partial-coverage. Responsive ranges (min_*/max_*) are\n // always advisory — the v1 wire can't carry a range.\n const declaredLegacyRefs = legacyFormatRefsForDeclaration(decl);\n if (declaredLegacyRefs.length > 0) {\n const refs = [...declaredLegacyRefs];\n const lossy = detectLossyMultiSize(decl);\n if (lossy) {\n // Sizes mode: seller asserted ≥1 ref; try to widen via catalog\n // lookup when refs < sizes; otherwise emit seller's refs verbatim.\n // Responsive range: refs are the seller's representative anchors;\n // can't be widened.\n const declaredCount = lossy.count ?? 0;\n const sellerCoversAllSizes = lossy.mode === 'sizes' && refs.length >= declaredCount;\n if (sellerCoversAllSizes) {\n return { v1: refs };\n }\n const fanned = lossy.mode === 'sizes' && refs.length < declaredCount ? tryFanOutMultiSize(decl, refs) : null;\n // Merge: prefer the fanned-out catalog entries when they cover more sizes\n // than the seller's refs; otherwise emit seller's refs.\n const emit = fanned && fanned.length > refs.length ? fanned : refs;\n return {\n v1: emit,\n diagnostic: {\n source: 'sdk',\n sdk_id: SDK_ID,\n field,\n code: 'FORMAT_DECLARATION_V1_LOSSY_MULTI_SIZE',\n error: {\n details: {\n format_kind: decl.format_kind,\n product_id: productId,\n format_option_id: decl.format_option_id,\n size_mode: lossy.mode,\n declared_sizes_count: lossy.count,\n emitted_sizes_count: emit.length,\n v1_emit_represents_size: refs[0] ? { width: refs[0].width, height: refs[0].height } : undefined,\n },\n },\n },\n };\n }\n return { v1: refs };\n }\n\n // Steps 4-6: registry lookup.\n const result = reverseLookup(decl.format_kind, decl.params);\n if (result.kind === 'match') {\n // Step 4: non-normative best-effort. Documented limitation: the\n // synthesized agent_url is implementation-defined; downstream\n // consumers MUST NOT depend on this projection.\n return { v1: result.v1 };\n }\n try {\n const resolved = resolveCanonicalFormatLegacyRefs(options?.canonicalFormatLegacyResolver, {\n source: 'product',\n declaration: decl,\n productId,\n field,\n });\n if (resolved) return { v1: resolved };\n } catch {\n return {\n diagnostic: {\n source: 'sdk',\n sdk_id: SDK_ID,\n field,\n code: 'FORMAT_PROJECTION_FAILED',\n error: {\n details: {\n format_kind: decl.format_kind,\n product_id: productId,\n format_option_id: decl.format_option_id,\n resolution_failure: 'custom_converter_failed',\n },\n },\n },\n };\n }\n if (result.kind === 'ambiguous') {\n // Step 5: family known, specific format not pickable.\n return {\n diagnostic: {\n source: 'sdk',\n sdk_id: SDK_ID,\n field,\n code: 'FORMAT_DECLARATION_V1_AMBIGUOUS',\n error: {\n details: {\n format_kind: decl.format_kind,\n product_id: productId,\n format_option_id: decl.format_option_id,\n registry_matches: result.matchedEntries,\n },\n },\n },\n };\n }\n // Step 6: registry-coverage gap.\n return {\n diagnostic: {\n source: 'sdk',\n sdk_id: SDK_ID,\n field,\n code: 'FORMAT_PROJECTION_FAILED',\n error: {\n details: {\n format_kind: decl.format_kind,\n product_id: productId,\n format_option_id: decl.format_option_id,\n resolution_failure: 'no_registry_match',\n },\n },\n },\n };\n}\n\n/**\n * Project a V2 Product to a v1 Product. Drops format_options from the\n * output and rebuilds format_ids per the resolution order.\n *\n * The function always returns a Product shape so adopters can inspect\n * what got dropped via diagnostics without parallel happy/sad-path\n * branches. Caller filters out v1 Products with empty format_ids before\n * sending to a v1-only seller (the spec requires `format_ids` to have\n * minItems: 1).\n */\nexport function projectV2ProductToV1(v2: V2Product, options?: V2ToV1ProjectionOptions): V2ToV1Result {\n const format_ids: V1FormatId[] = [];\n const diagnostics: ProjectionDiagnostic[] = [];\n\n for (let i = 0; i < v2.format_options.length; i++) {\n const decl = v2.format_options[i]!;\n const field = `products[${v2.product_id}].format_options[${i}]`;\n const { v1, diagnostic } = projectDeclaration(decl, v2.product_id, field, options);\n if (v1) {\n if (Array.isArray(v1)) {\n for (const id of v1) format_ids.push(id);\n } else {\n format_ids.push(v1);\n }\n }\n if (diagnostic) diagnostics.push(diagnostic);\n }\n\n const { format_options: _drop, ...rest } = v2;\n void _drop;\n const v1Product: V1Product = {\n ...(rest as Omit<V2Product, 'format_options'>),\n format_ids,\n } as V1Product;\n\n return { v1: v1Product, diagnostics };\n}\n"],"mappings":"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","names":[]}
|
|
@@ -2,7 +2,107 @@ import { reverseLookup } from "./registry.mjs";
|
|
|
2
2
|
import { isCanonicalV1Translatable } from "./canonical-properties.mjs";
|
|
3
3
|
import { findCatalogEntryByCanonicalAndSize, parseSizedIdTemplate } from "./catalog.mjs";
|
|
4
4
|
import { LIBRARY_VERSION } from "../../version.mjs";
|
|
5
|
+
import { legacyFormatRefsForDeclaration } from "./legacy-metadata.mjs";
|
|
5
6
|
const SDK_ID = `@adcp/sdk@${LIBRARY_VERSION}`;
|
|
7
|
+
class CanonicalFormatLegacyResolutionError extends Error {
|
|
8
|
+
name = "CanonicalFormatLegacyResolutionError";
|
|
9
|
+
}
|
|
10
|
+
function resolverRef(value) {
|
|
11
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
12
|
+
throw new CanonicalFormatLegacyResolutionError("resolver output must be a format-id object");
|
|
13
|
+
}
|
|
14
|
+
const descriptors = Object.getOwnPropertyDescriptors(value);
|
|
15
|
+
for (const descriptor of Object.values(descriptors)) {
|
|
16
|
+
if (descriptor.enumerable && !("value" in descriptor)) {
|
|
17
|
+
throw new CanonicalFormatLegacyResolutionError("resolver output must not contain enumerable accessors");
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
const read = (key) => {
|
|
21
|
+
const descriptor = descriptors[key];
|
|
22
|
+
return descriptor && "value" in descriptor ? descriptor.value : void 0;
|
|
23
|
+
};
|
|
24
|
+
const agentUrl = read("agent_url");
|
|
25
|
+
const id = read("id");
|
|
26
|
+
if (typeof agentUrl !== "string" || agentUrl.length === 0 || typeof id !== "string" || !/^[a-zA-Z0-9_-]+$/.test(id)) {
|
|
27
|
+
throw new CanonicalFormatLegacyResolutionError("resolver output has an invalid agent_url or id");
|
|
28
|
+
}
|
|
29
|
+
try {
|
|
30
|
+
new URL(agentUrl);
|
|
31
|
+
} catch {
|
|
32
|
+
throw new CanonicalFormatLegacyResolutionError("resolver output agent_url must be an absolute URI");
|
|
33
|
+
}
|
|
34
|
+
const width = read("width");
|
|
35
|
+
const height = read("height");
|
|
36
|
+
const durationMs = read("duration_ms");
|
|
37
|
+
if (width === void 0 !== (height === void 0)) {
|
|
38
|
+
throw new CanonicalFormatLegacyResolutionError("resolver output width and height must be provided together");
|
|
39
|
+
}
|
|
40
|
+
if (width !== void 0 && (!Number.isInteger(width) || width < 1)) {
|
|
41
|
+
throw new CanonicalFormatLegacyResolutionError("resolver output width must be a positive integer");
|
|
42
|
+
}
|
|
43
|
+
if (height !== void 0 && (!Number.isInteger(height) || height < 1)) {
|
|
44
|
+
throw new CanonicalFormatLegacyResolutionError("resolver output height must be a positive integer");
|
|
45
|
+
}
|
|
46
|
+
if (durationMs !== void 0 && (typeof durationMs !== "number" || !Number.isFinite(durationMs) || durationMs < 1)) {
|
|
47
|
+
throw new CanonicalFormatLegacyResolutionError("resolver output duration_ms must be a positive number");
|
|
48
|
+
}
|
|
49
|
+
return {
|
|
50
|
+
agent_url: agentUrl,
|
|
51
|
+
id,
|
|
52
|
+
...width !== void 0 ? { width, height } : {},
|
|
53
|
+
...durationMs !== void 0 ? { duration_ms: durationMs } : {}
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
function resolveCanonicalFormatLegacyRefs(resolver, context, requireSingle = false) {
|
|
57
|
+
if (!resolver) return void 0;
|
|
58
|
+
let output;
|
|
59
|
+
try {
|
|
60
|
+
output = resolver(context);
|
|
61
|
+
} catch {
|
|
62
|
+
throw new CanonicalFormatLegacyResolutionError("canonical format legacy resolver threw");
|
|
63
|
+
}
|
|
64
|
+
if (output == null) return void 0;
|
|
65
|
+
try {
|
|
66
|
+
let values;
|
|
67
|
+
if (Array.isArray(output)) {
|
|
68
|
+
const descriptors = Object.getOwnPropertyDescriptors(output);
|
|
69
|
+
const lengthDescriptor = descriptors["length"];
|
|
70
|
+
const length = lengthDescriptor && "value" in lengthDescriptor ? lengthDescriptor.value : void 0;
|
|
71
|
+
if (typeof length !== "number" || !Number.isSafeInteger(length) || length < 0) {
|
|
72
|
+
throw new CanonicalFormatLegacyResolutionError("resolver returned an invalid format-id array");
|
|
73
|
+
}
|
|
74
|
+
values = [];
|
|
75
|
+
for (let index = 0; index < length; index++) {
|
|
76
|
+
const descriptor = descriptors[String(index)];
|
|
77
|
+
if (!descriptor || !("value" in descriptor)) {
|
|
78
|
+
throw new CanonicalFormatLegacyResolutionError(
|
|
79
|
+
"resolver output arrays must be dense data-only format-id arrays"
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
values.push(descriptor.value);
|
|
83
|
+
}
|
|
84
|
+
} else {
|
|
85
|
+
values = [output];
|
|
86
|
+
}
|
|
87
|
+
if (values.length === 0 || requireSingle && values.length !== 1) {
|
|
88
|
+
throw new CanonicalFormatLegacyResolutionError(
|
|
89
|
+
requireSingle ? "resolver must return exactly one format-id for a creative" : "resolver returned no format ids"
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
const refs = values.map(resolverRef);
|
|
93
|
+
return [
|
|
94
|
+
...new Map(
|
|
95
|
+
refs.map((ref) => [
|
|
96
|
+
`${ref.agent_url}|${ref.id}|${ref.width ?? ""}|${ref.height ?? ""}|${ref.duration_ms ?? ""}`,
|
|
97
|
+
ref
|
|
98
|
+
])
|
|
99
|
+
).values()
|
|
100
|
+
];
|
|
101
|
+
} catch (error) {
|
|
102
|
+
if (error instanceof CanonicalFormatLegacyResolutionError) throw error;
|
|
103
|
+
throw new CanonicalFormatLegacyResolutionError("canonical format legacy resolver returned unreadable output");
|
|
104
|
+
}
|
|
105
|
+
}
|
|
6
106
|
function detectLossyMultiSize(decl) {
|
|
7
107
|
if (decl.format_kind !== "image" && decl.format_kind !== "html5" && decl.format_kind !== "display_tag") {
|
|
8
108
|
return null;
|
|
@@ -40,7 +140,7 @@ function tryFanOutMultiSize(decl, v1Refs) {
|
|
|
40
140
|
}
|
|
41
141
|
return found.length >= 2 ? found : null;
|
|
42
142
|
}
|
|
43
|
-
function projectDeclaration(decl, productId, field) {
|
|
143
|
+
function projectDeclaration(decl, productId, field, options) {
|
|
44
144
|
if (decl.canonical_formats_only === true) {
|
|
45
145
|
return {
|
|
46
146
|
diagnostic: {
|
|
@@ -76,8 +176,9 @@ function projectDeclaration(decl, productId, field) {
|
|
|
76
176
|
}
|
|
77
177
|
};
|
|
78
178
|
}
|
|
79
|
-
|
|
80
|
-
|
|
179
|
+
const declaredLegacyRefs = legacyFormatRefsForDeclaration(decl);
|
|
180
|
+
if (declaredLegacyRefs.length > 0) {
|
|
181
|
+
const refs = [...declaredLegacyRefs];
|
|
81
182
|
const lossy = detectLossyMultiSize(decl);
|
|
82
183
|
if (lossy) {
|
|
83
184
|
const declaredCount = lossy.count ?? 0;
|
|
@@ -114,6 +215,32 @@ function projectDeclaration(decl, productId, field) {
|
|
|
114
215
|
if (result.kind === "match") {
|
|
115
216
|
return { v1: result.v1 };
|
|
116
217
|
}
|
|
218
|
+
try {
|
|
219
|
+
const resolved = resolveCanonicalFormatLegacyRefs(options?.canonicalFormatLegacyResolver, {
|
|
220
|
+
source: "product",
|
|
221
|
+
declaration: decl,
|
|
222
|
+
productId,
|
|
223
|
+
field
|
|
224
|
+
});
|
|
225
|
+
if (resolved) return { v1: resolved };
|
|
226
|
+
} catch {
|
|
227
|
+
return {
|
|
228
|
+
diagnostic: {
|
|
229
|
+
source: "sdk",
|
|
230
|
+
sdk_id: SDK_ID,
|
|
231
|
+
field,
|
|
232
|
+
code: "FORMAT_PROJECTION_FAILED",
|
|
233
|
+
error: {
|
|
234
|
+
details: {
|
|
235
|
+
format_kind: decl.format_kind,
|
|
236
|
+
product_id: productId,
|
|
237
|
+
format_option_id: decl.format_option_id,
|
|
238
|
+
resolution_failure: "custom_converter_failed"
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
};
|
|
243
|
+
}
|
|
117
244
|
if (result.kind === "ambiguous") {
|
|
118
245
|
return {
|
|
119
246
|
diagnostic: {
|
|
@@ -149,13 +276,13 @@ function projectDeclaration(decl, productId, field) {
|
|
|
149
276
|
}
|
|
150
277
|
};
|
|
151
278
|
}
|
|
152
|
-
function projectV2ProductToV1(v2) {
|
|
279
|
+
function projectV2ProductToV1(v2, options) {
|
|
153
280
|
const format_ids = [];
|
|
154
281
|
const diagnostics = [];
|
|
155
282
|
for (let i = 0; i < v2.format_options.length; i++) {
|
|
156
283
|
const decl = v2.format_options[i];
|
|
157
284
|
const field = `products[${v2.product_id}].format_options[${i}]`;
|
|
158
|
-
const { v1, diagnostic } = projectDeclaration(decl, v2.product_id, field);
|
|
285
|
+
const { v1, diagnostic } = projectDeclaration(decl, v2.product_id, field, options);
|
|
159
286
|
if (v1) {
|
|
160
287
|
if (Array.isArray(v1)) {
|
|
161
288
|
for (const id of v1) format_ids.push(id);
|
|
@@ -174,6 +301,8 @@ function projectV2ProductToV1(v2) {
|
|
|
174
301
|
return { v1: v1Product, diagnostics };
|
|
175
302
|
}
|
|
176
303
|
export {
|
|
177
|
-
|
|
304
|
+
CanonicalFormatLegacyResolutionError,
|
|
305
|
+
projectV2ProductToV1,
|
|
306
|
+
resolveCanonicalFormatLegacyRefs
|
|
178
307
|
};
|
|
179
308
|
//# sourceMappingURL=v2-to-v1.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/lib/v2/projection/v2-to-v1.ts"],"sourcesContent":["/**\n * v2 → v1 Product projection (the downgrade direction).\n *\n * Used when the SDK negotiated to a v1 seller for a buyer that wrote V2\n * code. The 8.0 design at `docs/development/v3.1-sdk-design.md` makes V2\n * the public mental model — adopters never see v1 vocabulary on\n * `Product` — so this is the symmetric counterpart to v1→v2 projection\n * on the read side.\n *\n * Resolution order per declaration (aligns with the normative rules in\n * `core/registries/v1-canonical-mapping.json` and the canonical schemas'\n * `v1_translatable` field):\n *\n * 1. `format_kind: \"custom\"` + `canonical_formats_only: true` → no v1\n * emit. Seller has explicitly opted out. SDK-local diagnostic\n * `FORMAT_DECLARATION_V1_NOT_APPLICABLE` surfaces the opt-out so\n * v1-only buyers see why a product disappeared.\n * 2. Canonical declares `v1_translatable: false` (the 4 inherently-v2\n * canonicals at 3.1 GA: image_carousel, sponsored_placement,\n * responsive_creative, agent_placement) → no v1 emit. SDK-local\n * `CANONICAL_NOT_V1_TRANSLATABLE` diagnostic. **Spec is explicit:\n * MUST NOT emit FORMAT_PROJECTION_FAILED** — these aren't\n * registry-coverage gaps, they're structural unreachability.\n * 3. `v1_format_ref` present → use verbatim. Seller-asserted v1↔v2\n * pairing is the only normative path to a specific v1 `format_id`.\n * 4. Registry has invertible literal match (`format_id_glob` with no\n * `*`, params narrow compatibly) → MAY synthesize a v1 `format_id`,\n * but the projection is **non-normative** per the registry's\n * direction-of-truth statement. Downstream consumers MUST NOT\n * depend on this — and the spec explicitly notes that a synthesized\n * `agent_url` is implementation-defined inter-SDK divergence. We\n * do synthesize (best-effort, surfaces the registry's invertible\n * entries) but flag the result as non-normative via the registry\n * lookup return type.\n * 5. Registry has family-level matches (structural or wildcarded\n * globs) but none invertible → `FORMAT_DECLARATION_V1_AMBIGUOUS`.\n * Spec code. Seller's path: add `v1_format_ref` to disambiguate.\n * 6. Registry has no entries for this canonical (and v1_translatable\n * is true) → `FORMAT_PROJECTION_FAILED`. Spec code. Registry-\n * coverage gap; correctable by filing a registry PR.\n *\n * SDK identity for `sdk_id` is read from package.json at module-load\n * time so multi-hop deduplication can pin diagnostics to the SDK that\n * emitted them.\n */\n\nimport type { V2Product, V2ProductFormatDeclaration, V1Product, V1FormatId, ProjectionDiagnostic } from './types';\nimport { reverseLookup } from './registry';\nimport { isCanonicalV1Translatable } from './canonical-properties';\nimport { findCatalogEntryByCanonicalAndSize, parseSizedIdTemplate } from './catalog';\nimport { LIBRARY_VERSION } from '../../version';\n\nconst SDK_ID = `@adcp/sdk@${LIBRARY_VERSION}`;\n\nexport interface V2ToV1Result {\n v1: V1Product;\n diagnostics: ProjectionDiagnostic[];\n}\n\n/**\n * Inspect a v2 declaration's params for multi-size or responsive-range\n * shapes that a single v1 `format_id` can't represent. Returns the\n * detected mode + count so the caller emits the diagnostic with full\n * context. Returns null when params declare a single fixed (width,\n * height) — the case that round-trips through v1 cleanly.\n */\nfunction detectLossyMultiSize(\n decl: V2ProductFormatDeclaration\n): { mode: 'sizes' | 'responsive_range'; count?: number } | null {\n // Only image/html5/display_tag canonicals carry these new size modes\n // at 3.1 GA. Other canonicals (video_*, audio_*) use their own param\n // shapes that the v1 format_id can carry inline.\n if (decl.format_kind !== 'image' && decl.format_kind !== 'html5' && decl.format_kind !== 'display_tag') {\n return null;\n }\n const params = decl.params ?? {};\n if (Array.isArray(params.sizes) && params.sizes.length > 1) {\n return { mode: 'sizes', count: params.sizes.length };\n }\n // Responsive range mode: any of min_*/max_* present means the v1\n // format_id (which carries exactly one width+height pair) is lossy.\n if (\n typeof params.min_width === 'number' ||\n typeof params.max_width === 'number' ||\n typeof params.min_height === 'number' ||\n typeof params.max_height === 'number'\n ) {\n return { mode: 'responsive_range' };\n }\n return null;\n}\n\n/**\n * Try to fan out a multi-size v2 declaration to N v1 format_ids by\n * looking up each declared size in the AAO catalog. When successful,\n * v1 buyers see ALL the sizes the v2 declaration covers — not just\n * the seller-asserted rep. The spec doesn't require this (the rep +\n * lossy advisory is the minimum-bar projection) but the catalog\n * already publishes the per-size entries, so the SDK can use them\n * without inter-SDK divergence.\n *\n * Returns the fanned-out v1 ids when at least 2 sizes resolved\n * (otherwise the rep alone is fine; no value in fan-out). The lossy\n * advisory is still emitted — its details now reflect how many\n * sizes were actually covered vs declared.\n */\nfunction tryFanOutMultiSize(decl: V2ProductFormatDeclaration, v1Refs: V1FormatId[]): V1FormatId[] | null {\n const sizes = decl.params?.sizes;\n if (!Array.isArray(sizes) || sizes.length <= 1) return null;\n // Parse the first seller-asserted ref's id to extract its\n // `<prefix>_<W>x<H>_<suffix>` template, then constrain the fan-out\n // to siblings sharing the same prefix + suffix. Otherwise multiple\n // entries with the same `canonical:` annotation but different\n // families (e.g., image's `display_*_image` and `display_*_generative`\n // both annotated `canonical: image`) would collide and the SDK could\n // emit ids from the wrong family.\n const rep = v1Refs[0];\n if (!rep?.id) return null;\n const template = parseSizedIdTemplate(rep.id);\n const lookupOpts = template ? { prefix: template.prefix, suffix: template.suffix } : undefined;\n\n const found: V1FormatId[] = [];\n for (const size of sizes) {\n if (!size || typeof size !== 'object') continue;\n const sz = size as { width?: unknown; height?: unknown };\n if (typeof sz.width !== 'number' || typeof sz.height !== 'number') continue;\n const entry = findCatalogEntryByCanonicalAndSize(decl.format_kind, sz.width, sz.height, rep.agent_url, lookupOpts);\n if (entry) {\n found.push({\n agent_url: entry.format_id.agent_url,\n id: entry.format_id.id,\n width: sz.width,\n height: sz.height,\n });\n }\n }\n return found.length >= 2 ? found : null;\n}\n\n/**\n * Project a single V2 declaration. Returns one v1 format_id (the\n * common case) OR a fanned-out list of v1 format_ids (multi-size\n * fan-out) OR a diagnostic, plus an optional advisory diagnostic\n * (multi-size loses some sizes). The structural invariant: every\n * declaration produces AT LEAST ONE of (v1 emit, diagnostic) — may\n * produce both.\n */\nfunction projectDeclaration(\n decl: V2ProductFormatDeclaration,\n productId: string,\n field: string\n): { v1?: V1FormatId | V1FormatId[]; diagnostic?: ProjectionDiagnostic } {\n // Step 1: seller-asserted product-level opt-out. canonical_formats_only\n // is REQUIRED on `custom` declarations without v1_format_ref, but it's\n // ALSO valid on any non-custom canonical when the seller wants to opt\n // out of v1 emission for this product (e.g. the catalog has no v1 entry\n // for the format yet — `audio_daast` is the 3.1 example).\n if (decl.canonical_formats_only === true) {\n return {\n diagnostic: {\n source: 'sdk',\n sdk_id: SDK_ID,\n field,\n code: 'FORMAT_DECLARATION_V1_NOT_APPLICABLE',\n error: {\n details: {\n format_kind: decl.format_kind,\n product_id: productId,\n format_option_id: decl.format_option_id,\n reason: 'canonical_formats_only',\n },\n },\n },\n };\n }\n\n // Step 2: canonical-level structural unreachability.\n if (!isCanonicalV1Translatable(decl.format_kind)) {\n return {\n diagnostic: {\n source: 'sdk',\n sdk_id: SDK_ID,\n field,\n code: 'CANONICAL_NOT_V1_TRANSLATABLE',\n error: {\n details: {\n format_kind: decl.format_kind,\n product_id: productId,\n format_option_id: decl.format_option_id,\n },\n },\n },\n };\n }\n\n // Step 3: seller-asserted v1 link — the only normative path to a\n // specific v1 format_id. The spec normatively requires `v1_format_ref`\n // to be an array (single-ref is `[{...}]`); multi-size SHOULD carry\n // one entry per size.\n //\n // When the seller-asserted refs cover every declared size, emit all\n // refs verbatim — no advisory needed. When refs < sizes, emit what\n // the seller asserted (+ catalog-driven fan-out for missing sizes when\n // available) and surface the lossy advisory so the buyer knows the\n // v1 wire is partial-coverage. Responsive ranges (min_*/max_*) are\n // always advisory — the v1 wire can't carry a range.\n if (decl.v1_format_ref && decl.v1_format_ref.length > 0) {\n const refs = decl.v1_format_ref;\n const lossy = detectLossyMultiSize(decl);\n if (lossy) {\n // Sizes mode: seller asserted ≥1 ref; try to widen via catalog\n // lookup when refs < sizes; otherwise emit seller's refs verbatim.\n // Responsive range: refs are the seller's representative anchors;\n // can't be widened.\n const declaredCount = lossy.count ?? 0;\n const sellerCoversAllSizes = lossy.mode === 'sizes' && refs.length >= declaredCount;\n if (sellerCoversAllSizes) {\n return { v1: refs };\n }\n const fanned = lossy.mode === 'sizes' && refs.length < declaredCount ? tryFanOutMultiSize(decl, refs) : null;\n // Merge: prefer the fanned-out catalog entries when they cover more sizes\n // than the seller's refs; otherwise emit seller's refs.\n const emit = fanned && fanned.length > refs.length ? fanned : refs;\n return {\n v1: emit,\n diagnostic: {\n source: 'sdk',\n sdk_id: SDK_ID,\n field,\n code: 'FORMAT_DECLARATION_V1_LOSSY_MULTI_SIZE',\n error: {\n details: {\n format_kind: decl.format_kind,\n product_id: productId,\n format_option_id: decl.format_option_id,\n size_mode: lossy.mode,\n declared_sizes_count: lossy.count,\n emitted_sizes_count: emit.length,\n v1_emit_represents_size: refs[0] ? { width: refs[0].width, height: refs[0].height } : undefined,\n },\n },\n },\n };\n }\n return { v1: refs };\n }\n\n // Steps 4-6: registry lookup.\n const result = reverseLookup(decl.format_kind, decl.params);\n if (result.kind === 'match') {\n // Step 4: non-normative best-effort. Documented limitation: the\n // synthesized agent_url is implementation-defined; downstream\n // consumers MUST NOT depend on this projection.\n return { v1: result.v1 };\n }\n if (result.kind === 'ambiguous') {\n // Step 5: family known, specific format not pickable.\n return {\n diagnostic: {\n source: 'sdk',\n sdk_id: SDK_ID,\n field,\n code: 'FORMAT_DECLARATION_V1_AMBIGUOUS',\n error: {\n details: {\n format_kind: decl.format_kind,\n product_id: productId,\n format_option_id: decl.format_option_id,\n registry_matches: result.matchedEntries,\n },\n },\n },\n };\n }\n // Step 6: registry-coverage gap.\n return {\n diagnostic: {\n source: 'sdk',\n sdk_id: SDK_ID,\n field,\n code: 'FORMAT_PROJECTION_FAILED',\n error: {\n details: {\n format_kind: decl.format_kind,\n product_id: productId,\n format_option_id: decl.format_option_id,\n resolution_failure: 'no_registry_match',\n },\n },\n },\n };\n}\n\n/**\n * Project a V2 Product to a v1 Product. Drops format_options from the\n * output and rebuilds format_ids per the resolution order.\n *\n * The function always returns a Product shape so adopters can inspect\n * what got dropped via diagnostics without parallel happy/sad-path\n * branches. Caller filters out v1 Products with empty format_ids before\n * sending to a v1-only seller (the spec requires `format_ids` to have\n * minItems: 1).\n */\nexport function projectV2ProductToV1(v2: V2Product): V2ToV1Result {\n const format_ids: V1FormatId[] = [];\n const diagnostics: ProjectionDiagnostic[] = [];\n\n for (let i = 0; i < v2.format_options.length; i++) {\n const decl = v2.format_options[i]!;\n const field = `products[${v2.product_id}].format_options[${i}]`;\n const { v1, diagnostic } = projectDeclaration(decl, v2.product_id, field);\n if (v1) {\n if (Array.isArray(v1)) {\n for (const id of v1) format_ids.push(id);\n } else {\n format_ids.push(v1);\n }\n }\n if (diagnostic) diagnostics.push(diagnostic);\n }\n\n const { format_options: _drop, ...rest } = v2;\n void _drop;\n const v1Product: V1Product = {\n ...(rest as Omit<V2Product, 'format_options'>),\n format_ids,\n } as V1Product;\n\n return { v1: v1Product, diagnostics };\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../../../src/lib/v2/projection/v2-to-v1.ts"],"sourcesContent":["/**\n * v2 → v1 Product projection (the downgrade direction).\n *\n * Used when the SDK negotiated to a v1 seller for a buyer that wrote V2\n * code. The 8.0 design at `docs/development/v3.1-sdk-design.md` makes V2\n * the public mental model — adopters never see v1 vocabulary on\n * `Product` — so this is the symmetric counterpart to v1→v2 projection\n * on the read side.\n *\n * Resolution order per declaration (aligns with the normative rules in\n * `core/registries/v1-canonical-mapping.json` and the canonical schemas'\n * `v1_translatable` field):\n *\n * 1. `format_kind: \"custom\"` + `canonical_formats_only: true` → no v1\n * emit. Seller has explicitly opted out. SDK-local diagnostic\n * `FORMAT_DECLARATION_V1_NOT_APPLICABLE` surfaces the opt-out so\n * v1-only buyers see why a product disappeared.\n * 2. Canonical declares `v1_translatable: false` (the 4 inherently-v2\n * canonicals at 3.1 GA: image_carousel, sponsored_placement,\n * responsive_creative, agent_placement) → no v1 emit. SDK-local\n * `CANONICAL_NOT_V1_TRANSLATABLE` diagnostic. **Spec is explicit:\n * MUST NOT emit FORMAT_PROJECTION_FAILED** — these aren't\n * registry-coverage gaps, they're structural unreachability.\n * 3. `v1_format_ref` present → use verbatim. Seller-asserted v1↔v2\n * pairing is the only normative path to a specific v1 `format_id`.\n * 4. Registry has invertible literal match (`format_id_glob` with no\n * `*`, params narrow compatibly) → MAY synthesize a v1 `format_id`,\n * but the projection is **non-normative** per the registry's\n * direction-of-truth statement. Downstream consumers MUST NOT\n * depend on this — and the spec explicitly notes that a synthesized\n * `agent_url` is implementation-defined inter-SDK divergence. We\n * do synthesize (best-effort, surfaces the registry's invertible\n * entries) but flag the result as non-normative via the registry\n * lookup return type.\n * 5. Registry has family-level matches (structural or wildcarded\n * globs) but none invertible → `FORMAT_DECLARATION_V1_AMBIGUOUS`.\n * Spec code. Seller's path: add `v1_format_ref` to disambiguate.\n * 6. Registry has no entries for this canonical (and v1_translatable\n * is true) → `FORMAT_PROJECTION_FAILED`. Spec code. Registry-\n * coverage gap; correctable by filing a registry PR.\n *\n * SDK identity for `sdk_id` is read from package.json at module-load\n * time so multi-hop deduplication can pin diagnostics to the SDK that\n * emitted them.\n */\n\nimport type { V2Product, V2ProductFormatDeclaration, V1Product, V1FormatId, ProjectionDiagnostic } from './types';\nimport { reverseLookup } from './registry';\nimport { isCanonicalV1Translatable } from './canonical-properties';\nimport { findCatalogEntryByCanonicalAndSize, parseSizedIdTemplate } from './catalog';\nimport { LIBRARY_VERSION } from '../../version';\nimport { legacyFormatRefsForDeclaration } from './legacy-metadata';\n\nconst SDK_ID = `@adcp/sdk@${LIBRARY_VERSION}`;\n\nexport interface CanonicalProductFormatLegacyResolutionContext {\n source: 'product';\n declaration: Readonly<V2ProductFormatDeclaration>;\n productId: string;\n field: string;\n}\n\nexport interface CanonicalCreativeFormatLegacyResolutionContext {\n source: 'creative';\n creative: Readonly<Record<string, unknown>>;\n selector: Readonly<Record<string, unknown>>;\n operation: string;\n field: string;\n}\n\nexport interface CanonicalSelectorFormatLegacyResolutionContext {\n source: 'selector';\n selector: Readonly<Record<string, unknown>>;\n operation: string;\n field: string;\n}\n\nexport type CanonicalFormatLegacyResolutionContext =\n | CanonicalProductFormatLegacyResolutionContext\n | CanonicalCreativeFormatLegacyResolutionContext\n | CanonicalSelectorFormatLegacyResolutionContext;\n\n/** Explicit canonical → legacy resolver for persisted routes the SDK cannot safely infer. */\nexport type CanonicalFormatLegacyResolver = (\n context: CanonicalFormatLegacyResolutionContext\n) => V1FormatId | readonly V1FormatId[] | null | undefined;\n\nexport interface V2ToV1ProjectionOptions {\n canonicalFormatLegacyResolver?: CanonicalFormatLegacyResolver;\n}\n\nexport class CanonicalFormatLegacyResolutionError extends Error {\n readonly name = 'CanonicalFormatLegacyResolutionError' as const;\n}\n\nfunction resolverRef(value: unknown): V1FormatId {\n if (value === null || typeof value !== 'object' || Array.isArray(value)) {\n throw new CanonicalFormatLegacyResolutionError('resolver output must be a format-id object');\n }\n const descriptors = Object.getOwnPropertyDescriptors(value);\n for (const descriptor of Object.values(descriptors)) {\n if (descriptor.enumerable && !('value' in descriptor)) {\n throw new CanonicalFormatLegacyResolutionError('resolver output must not contain enumerable accessors');\n }\n }\n const read = (key: string): unknown => {\n const descriptor = descriptors[key];\n return descriptor && 'value' in descriptor ? descriptor.value : undefined;\n };\n const agentUrl = read('agent_url');\n const id = read('id');\n if (typeof agentUrl !== 'string' || agentUrl.length === 0 || typeof id !== 'string' || !/^[a-zA-Z0-9_-]+$/.test(id)) {\n throw new CanonicalFormatLegacyResolutionError('resolver output has an invalid agent_url or id');\n }\n try {\n new URL(agentUrl);\n } catch {\n throw new CanonicalFormatLegacyResolutionError('resolver output agent_url must be an absolute URI');\n }\n const width = read('width');\n const height = read('height');\n const durationMs = read('duration_ms');\n if ((width === undefined) !== (height === undefined)) {\n throw new CanonicalFormatLegacyResolutionError('resolver output width and height must be provided together');\n }\n if (width !== undefined && (!Number.isInteger(width) || (width as number) < 1)) {\n throw new CanonicalFormatLegacyResolutionError('resolver output width must be a positive integer');\n }\n if (height !== undefined && (!Number.isInteger(height) || (height as number) < 1)) {\n throw new CanonicalFormatLegacyResolutionError('resolver output height must be a positive integer');\n }\n if (durationMs !== undefined && (typeof durationMs !== 'number' || !Number.isFinite(durationMs) || durationMs < 1)) {\n throw new CanonicalFormatLegacyResolutionError('resolver output duration_ms must be a positive number');\n }\n return {\n agent_url: agentUrl,\n id,\n ...(width !== undefined ? { width: width as number, height: height as number } : {}),\n ...(durationMs !== undefined ? { duration_ms: durationMs } : {}),\n };\n}\n\nexport function resolveCanonicalFormatLegacyRefs(\n resolver: CanonicalFormatLegacyResolver | undefined,\n context: CanonicalFormatLegacyResolutionContext,\n requireSingle = false\n): V1FormatId[] | undefined {\n if (!resolver) return undefined;\n let output: V1FormatId | readonly V1FormatId[] | null | undefined;\n try {\n output = resolver(context);\n } catch {\n throw new CanonicalFormatLegacyResolutionError('canonical format legacy resolver threw');\n }\n if (output == null) return undefined;\n try {\n let values: unknown[];\n if (Array.isArray(output)) {\n const descriptors = Object.getOwnPropertyDescriptors(output) as Record<string, PropertyDescriptor>;\n const lengthDescriptor = descriptors['length'];\n const length = lengthDescriptor && 'value' in lengthDescriptor ? lengthDescriptor.value : undefined;\n if (typeof length !== 'number' || !Number.isSafeInteger(length) || length < 0) {\n throw new CanonicalFormatLegacyResolutionError('resolver returned an invalid format-id array');\n }\n values = [];\n for (let index = 0; index < length; index++) {\n const descriptor = descriptors[String(index)];\n if (!descriptor || !('value' in descriptor)) {\n throw new CanonicalFormatLegacyResolutionError(\n 'resolver output arrays must be dense data-only format-id arrays'\n );\n }\n values.push(descriptor.value);\n }\n } else {\n values = [output];\n }\n if (values.length === 0 || (requireSingle && values.length !== 1)) {\n throw new CanonicalFormatLegacyResolutionError(\n requireSingle ? 'resolver must return exactly one format-id for a creative' : 'resolver returned no format ids'\n );\n }\n const refs = values.map(resolverRef);\n return [\n ...new Map(\n refs.map(ref => [\n `${ref.agent_url}|${ref.id}|${ref.width ?? ''}|${ref.height ?? ''}|${ref.duration_ms ?? ''}`,\n ref,\n ])\n ).values(),\n ];\n } catch (error) {\n if (error instanceof CanonicalFormatLegacyResolutionError) throw error;\n throw new CanonicalFormatLegacyResolutionError('canonical format legacy resolver returned unreadable output');\n }\n}\n\nexport interface V2ToV1Result {\n v1: V1Product;\n diagnostics: ProjectionDiagnostic[];\n}\n\n/**\n * Inspect a v2 declaration's params for multi-size or responsive-range\n * shapes that a single v1 `format_id` can't represent. Returns the\n * detected mode + count so the caller emits the diagnostic with full\n * context. Returns null when params declare a single fixed (width,\n * height) — the case that round-trips through v1 cleanly.\n */\nfunction detectLossyMultiSize(\n decl: V2ProductFormatDeclaration\n): { mode: 'sizes' | 'responsive_range'; count?: number } | null {\n // Only image/html5/display_tag canonicals carry these new size modes\n // at 3.1 GA. Other canonicals (video_*, audio_*) use their own param\n // shapes that the v1 format_id can carry inline.\n if (decl.format_kind !== 'image' && decl.format_kind !== 'html5' && decl.format_kind !== 'display_tag') {\n return null;\n }\n const params = decl.params ?? {};\n if (Array.isArray(params.sizes) && params.sizes.length > 1) {\n return { mode: 'sizes', count: params.sizes.length };\n }\n // Responsive range mode: any of min_*/max_* present means the v1\n // format_id (which carries exactly one width+height pair) is lossy.\n if (\n typeof params.min_width === 'number' ||\n typeof params.max_width === 'number' ||\n typeof params.min_height === 'number' ||\n typeof params.max_height === 'number'\n ) {\n return { mode: 'responsive_range' };\n }\n return null;\n}\n\n/**\n * Try to fan out a multi-size v2 declaration to N v1 format_ids by\n * looking up each declared size in the AAO catalog. When successful,\n * v1 buyers see ALL the sizes the v2 declaration covers — not just\n * the seller-asserted rep. The spec doesn't require this (the rep +\n * lossy advisory is the minimum-bar projection) but the catalog\n * already publishes the per-size entries, so the SDK can use them\n * without inter-SDK divergence.\n *\n * Returns the fanned-out v1 ids when at least 2 sizes resolved\n * (otherwise the rep alone is fine; no value in fan-out). The lossy\n * advisory is still emitted — its details now reflect how many\n * sizes were actually covered vs declared.\n */\nfunction tryFanOutMultiSize(decl: V2ProductFormatDeclaration, v1Refs: V1FormatId[]): V1FormatId[] | null {\n const sizes = decl.params?.sizes;\n if (!Array.isArray(sizes) || sizes.length <= 1) return null;\n // Parse the first seller-asserted ref's id to extract its\n // `<prefix>_<W>x<H>_<suffix>` template, then constrain the fan-out\n // to siblings sharing the same prefix + suffix. Otherwise multiple\n // entries with the same `canonical:` annotation but different\n // families (e.g., image's `display_*_image` and `display_*_generative`\n // both annotated `canonical: image`) would collide and the SDK could\n // emit ids from the wrong family.\n const rep = v1Refs[0];\n if (!rep?.id) return null;\n const template = parseSizedIdTemplate(rep.id);\n const lookupOpts = template ? { prefix: template.prefix, suffix: template.suffix } : undefined;\n\n const found: V1FormatId[] = [];\n for (const size of sizes) {\n if (!size || typeof size !== 'object') continue;\n const sz = size as { width?: unknown; height?: unknown };\n if (typeof sz.width !== 'number' || typeof sz.height !== 'number') continue;\n const entry = findCatalogEntryByCanonicalAndSize(decl.format_kind, sz.width, sz.height, rep.agent_url, lookupOpts);\n if (entry) {\n found.push({\n agent_url: entry.format_id.agent_url,\n id: entry.format_id.id,\n width: sz.width,\n height: sz.height,\n });\n }\n }\n return found.length >= 2 ? found : null;\n}\n\n/**\n * Project a single V2 declaration. Returns one v1 format_id (the\n * common case) OR a fanned-out list of v1 format_ids (multi-size\n * fan-out) OR a diagnostic, plus an optional advisory diagnostic\n * (multi-size loses some sizes). The structural invariant: every\n * declaration produces AT LEAST ONE of (v1 emit, diagnostic) — may\n * produce both.\n */\nfunction projectDeclaration(\n decl: V2ProductFormatDeclaration,\n productId: string,\n field: string,\n options?: V2ToV1ProjectionOptions\n): { v1?: V1FormatId | V1FormatId[]; diagnostic?: ProjectionDiagnostic } {\n // Step 1: seller-asserted product-level opt-out. canonical_formats_only\n // is REQUIRED on `custom` declarations without v1_format_ref, but it's\n // ALSO valid on any non-custom canonical when the seller wants to opt\n // out of v1 emission for this product (e.g. the catalog has no v1 entry\n // for the format yet — `audio_daast` is the 3.1 example).\n if (decl.canonical_formats_only === true) {\n return {\n diagnostic: {\n source: 'sdk',\n sdk_id: SDK_ID,\n field,\n code: 'FORMAT_DECLARATION_V1_NOT_APPLICABLE',\n error: {\n details: {\n format_kind: decl.format_kind,\n product_id: productId,\n format_option_id: decl.format_option_id,\n reason: 'canonical_formats_only',\n },\n },\n },\n };\n }\n\n // Step 2: canonical-level structural unreachability.\n if (!isCanonicalV1Translatable(decl.format_kind)) {\n return {\n diagnostic: {\n source: 'sdk',\n sdk_id: SDK_ID,\n field,\n code: 'CANONICAL_NOT_V1_TRANSLATABLE',\n error: {\n details: {\n format_kind: decl.format_kind,\n product_id: productId,\n format_option_id: decl.format_option_id,\n },\n },\n },\n };\n }\n\n // Step 3: seller-asserted v1 link — the only normative path to a\n // specific v1 format_id. The spec normatively requires `v1_format_ref`\n // to be an array (single-ref is `[{...}]`); multi-size SHOULD carry\n // one entry per size.\n //\n // When the seller-asserted refs cover every declared size, emit all\n // refs verbatim — no advisory needed. When refs < sizes, emit what\n // the seller asserted (+ catalog-driven fan-out for missing sizes when\n // available) and surface the lossy advisory so the buyer knows the\n // v1 wire is partial-coverage. Responsive ranges (min_*/max_*) are\n // always advisory — the v1 wire can't carry a range.\n const declaredLegacyRefs = legacyFormatRefsForDeclaration(decl);\n if (declaredLegacyRefs.length > 0) {\n const refs = [...declaredLegacyRefs];\n const lossy = detectLossyMultiSize(decl);\n if (lossy) {\n // Sizes mode: seller asserted ≥1 ref; try to widen via catalog\n // lookup when refs < sizes; otherwise emit seller's refs verbatim.\n // Responsive range: refs are the seller's representative anchors;\n // can't be widened.\n const declaredCount = lossy.count ?? 0;\n const sellerCoversAllSizes = lossy.mode === 'sizes' && refs.length >= declaredCount;\n if (sellerCoversAllSizes) {\n return { v1: refs };\n }\n const fanned = lossy.mode === 'sizes' && refs.length < declaredCount ? tryFanOutMultiSize(decl, refs) : null;\n // Merge: prefer the fanned-out catalog entries when they cover more sizes\n // than the seller's refs; otherwise emit seller's refs.\n const emit = fanned && fanned.length > refs.length ? fanned : refs;\n return {\n v1: emit,\n diagnostic: {\n source: 'sdk',\n sdk_id: SDK_ID,\n field,\n code: 'FORMAT_DECLARATION_V1_LOSSY_MULTI_SIZE',\n error: {\n details: {\n format_kind: decl.format_kind,\n product_id: productId,\n format_option_id: decl.format_option_id,\n size_mode: lossy.mode,\n declared_sizes_count: lossy.count,\n emitted_sizes_count: emit.length,\n v1_emit_represents_size: refs[0] ? { width: refs[0].width, height: refs[0].height } : undefined,\n },\n },\n },\n };\n }\n return { v1: refs };\n }\n\n // Steps 4-6: registry lookup.\n const result = reverseLookup(decl.format_kind, decl.params);\n if (result.kind === 'match') {\n // Step 4: non-normative best-effort. Documented limitation: the\n // synthesized agent_url is implementation-defined; downstream\n // consumers MUST NOT depend on this projection.\n return { v1: result.v1 };\n }\n try {\n const resolved = resolveCanonicalFormatLegacyRefs(options?.canonicalFormatLegacyResolver, {\n source: 'product',\n declaration: decl,\n productId,\n field,\n });\n if (resolved) return { v1: resolved };\n } catch {\n return {\n diagnostic: {\n source: 'sdk',\n sdk_id: SDK_ID,\n field,\n code: 'FORMAT_PROJECTION_FAILED',\n error: {\n details: {\n format_kind: decl.format_kind,\n product_id: productId,\n format_option_id: decl.format_option_id,\n resolution_failure: 'custom_converter_failed',\n },\n },\n },\n };\n }\n if (result.kind === 'ambiguous') {\n // Step 5: family known, specific format not pickable.\n return {\n diagnostic: {\n source: 'sdk',\n sdk_id: SDK_ID,\n field,\n code: 'FORMAT_DECLARATION_V1_AMBIGUOUS',\n error: {\n details: {\n format_kind: decl.format_kind,\n product_id: productId,\n format_option_id: decl.format_option_id,\n registry_matches: result.matchedEntries,\n },\n },\n },\n };\n }\n // Step 6: registry-coverage gap.\n return {\n diagnostic: {\n source: 'sdk',\n sdk_id: SDK_ID,\n field,\n code: 'FORMAT_PROJECTION_FAILED',\n error: {\n details: {\n format_kind: decl.format_kind,\n product_id: productId,\n format_option_id: decl.format_option_id,\n resolution_failure: 'no_registry_match',\n },\n },\n },\n };\n}\n\n/**\n * Project a V2 Product to a v1 Product. Drops format_options from the\n * output and rebuilds format_ids per the resolution order.\n *\n * The function always returns a Product shape so adopters can inspect\n * what got dropped via diagnostics without parallel happy/sad-path\n * branches. Caller filters out v1 Products with empty format_ids before\n * sending to a v1-only seller (the spec requires `format_ids` to have\n * minItems: 1).\n */\nexport function projectV2ProductToV1(v2: V2Product, options?: V2ToV1ProjectionOptions): V2ToV1Result {\n const format_ids: V1FormatId[] = [];\n const diagnostics: ProjectionDiagnostic[] = [];\n\n for (let i = 0; i < v2.format_options.length; i++) {\n const decl = v2.format_options[i]!;\n const field = `products[${v2.product_id}].format_options[${i}]`;\n const { v1, diagnostic } = projectDeclaration(decl, v2.product_id, field, options);\n if (v1) {\n if (Array.isArray(v1)) {\n for (const id of v1) format_ids.push(id);\n } else {\n format_ids.push(v1);\n }\n }\n if (diagnostic) diagnostics.push(diagnostic);\n }\n\n const { format_options: _drop, ...rest } = v2;\n void _drop;\n const v1Product: V1Product = {\n ...(rest as Omit<V2Product, 'format_options'>),\n format_ids,\n } as V1Product;\n\n return { v1: v1Product, diagnostics };\n}\n"],"mappings":"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","names":[]}
|