@adcp/sdk 13.0.0-rc.10 → 13.0.0-rc.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/lib/core/AgentClient.d.mts +12 -3
- package/dist/lib/core/AgentClient.d.ts +12 -3
- package/dist/lib/core/AgentClient.d.ts.map +1 -1
- package/dist/lib/core/AgentClient.js +12 -3
- package/dist/lib/core/AgentClient.js.map +1 -1
- package/dist/lib/core/AgentClient.mjs +12 -3
- package/dist/lib/core/AgentClient.mjs.map +1 -1
- package/dist/lib/core/SingleAgentClient.d.mts +13 -3
- package/dist/lib/core/SingleAgentClient.d.ts +13 -3
- package/dist/lib/core/SingleAgentClient.d.ts.map +1 -1
- package/dist/lib/core/SingleAgentClient.js +52 -31
- package/dist/lib/core/SingleAgentClient.js.map +1 -1
- package/dist/lib/core/SingleAgentClient.mjs +52 -31
- package/dist/lib/core/SingleAgentClient.mjs.map +1 -1
- package/dist/lib/index.d.mts +1 -1
- package/dist/lib/index.d.ts +1 -1
- package/dist/lib/index.d.ts.map +1 -1
- package/dist/lib/index.js +8 -0
- package/dist/lib/index.js.map +1 -1
- package/dist/lib/index.mjs +8 -0
- package/dist/lib/index.mjs.map +1 -1
- package/dist/lib/registry/types.generated.d.mts +48 -1
- package/dist/lib/registry/types.generated.d.ts +48 -1
- package/dist/lib/registry/types.generated.d.ts.map +1 -1
- package/dist/lib/registry/types.generated.js.map +1 -1
- package/dist/lib/schemas-data/v2.5/_provenance.json +1 -1
- package/dist/lib/server/decisioning/runtime/from-platform.d.mts +1 -1
- package/dist/lib/server/decisioning/runtime/from-platform.d.ts +1 -1
- package/dist/lib/server/decisioning/runtime/from-platform.d.ts.map +1 -1
- package/dist/lib/server/decisioning/runtime/from-platform.js +389 -65
- package/dist/lib/server/decisioning/runtime/from-platform.js.map +1 -1
- package/dist/lib/server/decisioning/runtime/from-platform.mjs +389 -65
- package/dist/lib/server/decisioning/runtime/from-platform.mjs.map +1 -1
- package/dist/lib/testing/compliance/comply.d.ts.map +1 -1
- package/dist/lib/testing/compliance/comply.js +8 -2
- package/dist/lib/testing/compliance/comply.js.map +1 -1
- package/dist/lib/testing/compliance/comply.mjs +8 -2
- package/dist/lib/testing/compliance/comply.mjs.map +1 -1
- package/dist/lib/testing/compliance/spec-conformance.d.ts.map +1 -1
- package/dist/lib/testing/compliance/spec-conformance.js +2 -0
- package/dist/lib/testing/compliance/spec-conformance.js.map +1 -1
- package/dist/lib/testing/compliance/spec-conformance.mjs +2 -0
- package/dist/lib/testing/compliance/spec-conformance.mjs.map +1 -1
- package/dist/lib/testing/compliance/summary.d.mts +2 -0
- package/dist/lib/testing/compliance/summary.d.ts +2 -0
- package/dist/lib/testing/compliance/summary.d.ts.map +1 -1
- package/dist/lib/testing/compliance/summary.js +3 -2
- package/dist/lib/testing/compliance/summary.js.map +1 -1
- package/dist/lib/testing/compliance/summary.mjs +3 -2
- package/dist/lib/testing/compliance/summary.mjs.map +1 -1
- package/dist/lib/testing/compliance/types.d.mts +4 -0
- package/dist/lib/testing/compliance/types.d.ts +4 -0
- package/dist/lib/testing/compliance/types.d.ts.map +1 -1
- package/dist/lib/testing/compliance/types.js.map +1 -1
- package/dist/lib/testing/storyboard/junit.js +49 -32
- package/dist/lib/testing/storyboard/junit.js.map +1 -1
- package/dist/lib/testing/storyboard/junit.mjs +49 -32
- package/dist/lib/testing/storyboard/junit.mjs.map +1 -1
- package/dist/lib/testing/storyboard/loader.d.ts.map +1 -1
- package/dist/lib/testing/storyboard/loader.js +26 -0
- package/dist/lib/testing/storyboard/loader.js.map +1 -1
- package/dist/lib/testing/storyboard/loader.mjs +26 -0
- package/dist/lib/testing/storyboard/loader.mjs.map +1 -1
- package/dist/lib/testing/storyboard/runner.d.ts.map +1 -1
- package/dist/lib/testing/storyboard/runner.js +66 -15
- package/dist/lib/testing/storyboard/runner.js.map +1 -1
- package/dist/lib/testing/storyboard/runner.mjs +70 -16
- package/dist/lib/testing/storyboard/runner.mjs.map +1 -1
- package/dist/lib/testing/storyboard/types.d.mts +26 -1
- package/dist/lib/testing/storyboard/types.d.ts +26 -1
- package/dist/lib/testing/storyboard/types.d.ts.map +1 -1
- package/dist/lib/testing/storyboard/types.js.map +1 -1
- package/dist/lib/testing/storyboard/types.mjs.map +1 -1
- package/dist/lib/testing/storyboard/validations.d.mts +11 -0
- package/dist/lib/testing/storyboard/validations.d.ts +11 -0
- package/dist/lib/testing/storyboard/validations.d.ts.map +1 -1
- package/dist/lib/testing/storyboard/validations.js +30 -13
- package/dist/lib/testing/storyboard/validations.js.map +1 -1
- package/dist/lib/testing/storyboard/validations.mjs +27 -13
- package/dist/lib/testing/storyboard/validations.mjs.map +1 -1
- package/dist/lib/v2/projection/augment-response.d.mts +45 -0
- package/dist/lib/v2/projection/augment-response.d.ts +45 -0
- package/dist/lib/v2/projection/augment-response.d.ts.map +1 -1
- package/dist/lib/v2/projection/augment-response.js +54 -0
- package/dist/lib/v2/projection/augment-response.js.map +1 -1
- package/dist/lib/v2/projection/augment-response.mjs +52 -0
- package/dist/lib/v2/projection/augment-response.mjs.map +1 -1
- package/dist/lib/v2/projection/index.d.mts +7 -1
- package/dist/lib/v2/projection/index.d.ts +7 -1
- package/dist/lib/v2/projection/index.d.ts.map +1 -1
- package/dist/lib/v2/projection/index.js +9 -0
- package/dist/lib/v2/projection/index.js.map +1 -1
- package/dist/lib/v2/projection/index.mjs +10 -0
- package/dist/lib/v2/projection/index.mjs.map +1 -1
- package/dist/lib/v2/projection/legacy-routes.d.mts +24 -0
- package/dist/lib/v2/projection/legacy-routes.d.ts +25 -0
- package/dist/lib/v2/projection/legacy-routes.d.ts.map +1 -0
- package/dist/lib/v2/projection/legacy-routes.js +163 -0
- package/dist/lib/v2/projection/legacy-routes.js.map +1 -0
- package/dist/lib/v2/projection/legacy-routes.mjs +138 -0
- package/dist/lib/v2/projection/legacy-routes.mjs.map +1 -0
- package/dist/lib/v2/projection/v1-to-v2.d.mts +3 -0
- package/dist/lib/v2/projection/v1-to-v2.d.ts +3 -0
- package/dist/lib/v2/projection/v1-to-v2.d.ts.map +1 -1
- package/dist/lib/v2/projection/v1-to-v2.js +2 -1
- package/dist/lib/v2/projection/v1-to-v2.js.map +1 -1
- package/dist/lib/v2/projection/v1-to-v2.mjs +2 -1
- package/dist/lib/v2/projection/v1-to-v2.mjs.map +1 -1
- package/dist/lib/version.d.mts +3 -3
- package/dist/lib/version.d.ts +3 -3
- package/dist/lib/version.js +3 -3
- package/dist/lib/version.js.map +1 -1
- package/dist/lib/version.mjs +3 -3
- package/dist/lib/version.mjs.map +1 -1
- package/docs/migration-12-to-13.md +56 -2
- package/package.json +3 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/lib/v2/projection/v1-to-v2.ts"],"sourcesContent":["/**\n * v1 → v2 Product projection (the upgrade direction).\n *\n * Used when the SDK is talking to a v1 seller but the buyer wrote V2\n * code. The 8.0 design at `docs/development/v3.1-sdk-design.md` makes\n * V2 the public mental model — the buyer never sees `Product.format_ids`\n * directly, only `Product.format_options`. This module is the wire-\n * boundary translator that makes that promise hold for v1 sellers.\n *\n * Resolution order per format_id, mirroring `v1-canonical-mapping.json`\n * step 1-4 in the forward direction:\n *\n * 1. **v1 catalog explicit `canonical` annotation**. If the v1 format\n * definition (from `reference-formats.json` or a seller's\n * `list_creative_formats`) carries `canonical: <kind>`, that's the\n * authoritative pairing. Seller-asserted, normative.\n * 2. **Registry glob match**. Look up `format_id.id` against the\n * registry's `format_id_glob` entries (including wildcards). First\n * match wins per the spec's ordering.\n * 3. **Structural match**. Match against the v1 format's declared\n * assets + version constraints. Family-level identification\n * yields a canonical (e.g., \"vast 4.x → video_vast\"). Less\n * precise on params; caller may need to fetch additional context.\n * 4. **Fail closed** → `FORMAT_PROJECTION_FAILED`. v1 product with\n * no catalog entry, no registry coverage, and no structural match\n * is invisible on the v2 side. SDK surfaces the diagnostic so\n * buyers know what got dropped.\n *\n * **Asymmetry vs the v2 → v1 direction**: every v1 format_id is a\n * specific thing, so there's no \"ambiguous family\" bucket here — if\n * the structural match identifies a family, the projection is\n * deterministic for that single format_id.\n *\n * **Scope (prototype)**:\n * - AAO catalog only — seller-specific catalogs (publisher's own\n * `list_creative_formats`) require an AgentClient hook the auto-\n * negotiation surface will provide in the full 8.0 enablement.\n * - Param extraction is dimensions-only (`width`, `height`,\n * `duration_ms`). Full canonical-specific params (slots, codecs,\n * char limits, platform_extensions) are not constructed. A v2\n * buyer reading the projected declaration's `params` sees the\n * minimum needed to identify the variant.\n * - Asset slot translation (v1 `assets[]` → v2 `slots[]` via the\n * asset_group_vocabulary aliases) is deliberately deferred. This\n * is the most adopter-relevant piece for actual creative\n * submission flows and lands in a follow-up.\n */\n\nimport type {\n V1Product,\n V1FormatId,\n V2Product,\n V2ProductFormatDeclaration,\n ProjectionDiagnostic,\n CanonicalFormatKind,\n} from './types';\nimport { forwardLookupByGlob, forwardLookupByStructural } from './registry';\nimport { lookupUniqueV1FormatById, lookupV1Format, type V1FormatDefinition } from './catalog';\nimport { AAO_CANONICAL_AGENT_URL } from './constants';\nimport { LIBRARY_VERSION } from '../../version';\nimport { ProductFormatDeclarationSchema } from '../../types/schemas.generated';\nimport { legacyFormatConverterFromCatalogSnapshots, type ProjectionCatalogSnapshot } from './catalog-snapshot';\nimport { canonicalizeAgentUrl } from '../../discovery/resolve-agent-properties';\nimport { isLikelyPrivateUrl } from '../../net/address-guards';\nimport { createHmac } from 'crypto';\n\nconst SDK_ID = `@adcp/sdk@${LIBRARY_VERSION}`;\n\nclass CatalogRequirementConflict extends Error {}\n\n/**\n * Stable identity disambiguator, not a password hash. The input is a public\n * creative-format tuple and the output is a product-local routing label. As\n * with the transport cache disambiguators, HMAC-SHA256 with an empty key gives\n * deterministic collision resistance without placing this non-secret value\n * in CodeQL's password-storage dataflow class.\n */\nfunction formatIdentityDisambiguator(identity: string): string {\n return createHmac('sha256', '').update(identity).digest('hex').slice(0, 32);\n}\n\n/**\n * Give an unnamed projected option a stable, opaque identity derived from the\n * complete legacy tuple. Positional IDs are unsafe: a seller reordering its\n * `format_ids` array could otherwise make a persisted canonical selection\n * resolve to a different legacy format on the next discovery refresh.\n */\nfunction migratedFormatOptionId(fid: V1FormatId): string {\n const identity = JSON.stringify([\n fid.agent_url,\n fid.id,\n fid.width ?? null,\n fid.height ?? null,\n fid.duration_ms ?? null,\n ]);\n return `migrated_${formatIdentityDisambiguator(identity)}`;\n}\n\nexport interface V1ToV2Result {\n v2: V2Product;\n diagnostics: ProjectionDiagnostic[];\n}\n\n/** Context passed to an adopter's seller-specific legacy format converter. */\nexport interface LegacyFormatConversionContext {\n formatId: Readonly<V1FormatId>;\n productId: string;\n field: string;\n}\n\n/**\n * Escape hatch for legacy formats owned by a custom creative agent. The\n * converter returns the canonical product declaration that the legacy ref\n * represents. For bespoke shapes, return `format_kind: 'custom'` with both\n * `format_shape` and an immutable `format_schema` reference.\n *\n * The SDK adds the source `formatId` as `v1_format_ref`; converters must not\n * set `canonical_formats_only: true` because a legacy source is, by\n * definition, round-trippable to that ref.\n */\nexport type LegacyFormatConverter = (\n context: LegacyFormatConversionContext\n) => V2ProductFormatDeclaration | null | undefined;\n\nexport interface V1ToV2ProjectionOptions {\n legacyFormatConverter?: LegacyFormatConverter;\n /** Pre-resolved exact-owner publisher/community catalogs, highest precedence first. */\n projectionCatalogs?: readonly ProjectionCatalogSnapshot[];\n /** @internal Filesystem-isolated catalog fixture used by projector tests. */\n _catalogPath?: string;\n}\n\n/**\n * Build the v2 declaration's `params` block from the v1 format_id's\n * dimensional overrides + the catalog/registry entry's recorded params.\n *\n * Prototype scope: dimensions + duration only. A full implementation\n * would walk the canonical's parameter schema and populate every field\n * the catalog entry hints at (codecs, char limits, platform_extensions).\n */\nfunction buildParams(\n fid: V1FormatId,\n registryParams: Record<string, unknown>,\n catalogEntry?: V1FormatDefinition\n): Record<string, unknown> {\n const params: Record<string, unknown> = { ...registryParams };\n\n // Catalog-authored fixed requirements are normative projection inputs.\n // Prefer the primary render dimensions when there is exactly one fixed\n // size; otherwise accept one unambiguous width/height pair from the asset\n // requirements. A fixed duration is equally safe only when min === max.\n // Never guess from a name, a range, or conflicting requirements.\n const fixedSizes = new Map<string, { width: number; height: number }>();\n let hasUnsupportedSizeRequirement = false;\n for (const render of catalogEntry?.renders ?? []) {\n const width = render.dimensions?.width;\n const height = render.dimensions?.height;\n if (width === undefined && height === undefined) continue;\n if (\n typeof width === 'number' &&\n Number.isInteger(width) &&\n width > 0 &&\n typeof height === 'number' &&\n Number.isInteger(height) &&\n height > 0\n ) {\n fixedSizes.set(`${width}x${height}`, { width, height });\n } else hasUnsupportedSizeRequirement = true;\n }\n for (const asset of catalogEntry?.assets ?? []) {\n const width = asset.requirements?.width;\n const height = asset.requirements?.height;\n const minWidth = asset.requirements?.min_width;\n const maxWidth = asset.requirements?.max_width;\n const minHeight = asset.requirements?.min_height;\n const maxHeight = asset.requirements?.max_height;\n if (minWidth !== undefined || maxWidth !== undefined || minHeight !== undefined || maxHeight !== undefined) {\n if (\n typeof minWidth === 'number' &&\n Number.isInteger(minWidth) &&\n minWidth > 0 &&\n minWidth === maxWidth &&\n typeof minHeight === 'number' &&\n Number.isInteger(minHeight) &&\n minHeight > 0 &&\n minHeight === maxHeight\n ) {\n fixedSizes.set(`${minWidth}x${minHeight}`, { width: minWidth, height: minHeight });\n } else {\n hasUnsupportedSizeRequirement = true;\n }\n }\n if (width === undefined && height === undefined) continue;\n if (\n typeof width === 'number' &&\n Number.isInteger(width) &&\n width > 0 &&\n typeof height === 'number' &&\n Number.isInteger(height) &&\n height > 0\n ) {\n fixedSizes.set(`${width}x${height}`, { width, height });\n } else hasUnsupportedSizeRequirement = true;\n }\n if (fixedSizes.size > 1 || hasUnsupportedSizeRequirement) {\n throw new CatalogRequirementConflict('catalog contains conflicting fixed dimensions');\n }\n if (fixedSizes.size === 1) {\n const fixedSize = fixedSizes.values().next().value;\n if (fixedSize) {\n if (\n (typeof fid.width === 'number' && fid.width !== fixedSize.width) ||\n (typeof fid.height === 'number' && fid.height !== fixedSize.height)\n ) {\n throw new CatalogRequirementConflict('format id conflicts with catalog dimensions');\n }\n params.width = fixedSize.width;\n params.height = fixedSize.height;\n }\n }\n\n const fixedDurations = new Set<number>();\n let hasRangedDuration = false;\n for (const asset of catalogEntry?.assets ?? []) {\n const min = asset.requirements?.min_duration_ms;\n const max = asset.requirements?.max_duration_ms;\n if (min === undefined && max === undefined) continue;\n if (\n (min !== undefined && (typeof min !== 'number' || !Number.isInteger(min) || min <= 0)) ||\n (max !== undefined && (typeof max !== 'number' || !Number.isInteger(max) || max <= 0)) ||\n (typeof min === 'number' && typeof max === 'number' && min > max)\n ) {\n throw new CatalogRequirementConflict('catalog contains invalid duration requirements');\n }\n if (typeof min === 'number' && min === max) fixedDurations.add(min);\n else hasRangedDuration = true;\n }\n if (fixedDurations.size > 1 || hasRangedDuration) {\n throw new CatalogRequirementConflict('catalog contains conflicting fixed durations');\n }\n if (fixedDurations.size === 1) {\n const fixedDuration = fixedDurations.values().next().value;\n if (fixedDuration !== undefined) {\n if (typeof fid.duration_ms === 'number' && fid.duration_ms !== fixedDuration) {\n throw new CatalogRequirementConflict('format id conflicts with catalog duration');\n }\n params.duration_ms_exact = fixedDuration;\n }\n }\n\n // Inline discriminators narrow parameterized catalog templates. Concrete\n // catalog defaults were checked for contradictions above.\n if (typeof fid.width === 'number') params.width = fid.width;\n if (typeof fid.height === 'number') params.height = fid.height;\n if (typeof fid.duration_ms === 'number') params.duration_ms_exact = fid.duration_ms;\n return params;\n}\n\nfunction findLegacyCreativeIdentity(value: unknown, seen = new WeakSet<object>()): string | undefined {\n if (value === null || typeof value !== 'object') return undefined;\n if (seen.has(value)) return undefined;\n seen.add(value);\n if (Array.isArray(value)) {\n for (const item of value) {\n const nested = findLegacyCreativeIdentity(item, seen);\n if (nested) return nested;\n }\n return undefined;\n }\n for (const [key, child] of Object.entries(value as Record<string, unknown>)) {\n if (/(^|_)(?:format_ids?|v1_format_ref|agent_url)($|_)/.test(key)) {\n return key;\n }\n const nested = findLegacyCreativeIdentity(child, seen);\n if (nested) return nested;\n }\n return undefined;\n}\n\nfunction projectWithLegacyConverter(\n fid: V1FormatId,\n productId: string,\n field: string,\n converter: LegacyFormatConverter | undefined\n): { decl?: V2ProductFormatDeclaration; diagnostic?: ProjectionDiagnostic } | undefined {\n if (!converter) return undefined;\n try {\n const converted = converter({ formatId: { ...fid }, productId, field });\n if (!converted) return undefined;\n const forbidden = findLegacyCreativeIdentity(converted);\n if (forbidden) {\n throw new Error(`canonical conversion must not return ${forbidden}`);\n }\n const completed = { ...converted, v1_format_ref: [fid] };\n if (completed.canonical_formats_only === true) {\n throw new Error('a conversion from a legacy format cannot set canonical_formats_only: true');\n }\n if (\n completed.format_kind === 'custom' &&\n (typeof completed.format_shape !== 'string' ||\n completed.format_shape.trim().length === 0 ||\n !completed.format_schema)\n ) {\n throw new Error('custom conversions require format_shape and format_schema');\n }\n if (\n completed.format_kind === 'custom' &&\n (typeof completed.format_option_id !== 'string' || completed.format_option_id.trim().length === 0)\n ) {\n throw new Error('custom conversions require format_option_id');\n }\n const parsed = ProductFormatDeclarationSchema.safeParse(completed);\n if (!parsed.success) {\n throw new Error('converter returned an invalid canonical format declaration');\n }\n return { decl: parsed.data as V2ProductFormatDeclaration };\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: 'custom',\n product_id: productId,\n resolution_failure: 'custom_converter_failed',\n },\n },\n },\n };\n }\n}\n\n/**\n * Project a single v1 `format_id` to a v2 `ProductFormatDeclaration`,\n * or to a diagnostic when no projection is possible.\n */\nfunction projectFormatId(\n fid: V1FormatId,\n productId: string,\n field: string,\n options?: V1ToV2ProjectionOptions\n): { decl?: V2ProductFormatDeclaration; diagnostic?: ProjectionDiagnostic } {\n const hasWidth = fid.width !== undefined;\n const hasHeight = fid.height !== undefined;\n const invalidDimensions =\n hasWidth !== hasHeight ||\n (hasWidth && (!Number.isInteger(fid.width) || fid.width! <= 0)) ||\n (hasHeight && (!Number.isInteger(fid.height) || fid.height! <= 0));\n const invalidDuration = fid.duration_ms !== undefined && (!Number.isInteger(fid.duration_ms) || fid.duration_ms <= 0);\n if (invalidDimensions || invalidDuration) {\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: 'custom',\n product_id: productId,\n resolution_failure: 'invalid_format_id_parameters',\n },\n },\n },\n };\n }\n\n // A publisher/community catalog carrying an explicit v1_format_ref is the\n // most specific authoritative pairing. Public availability or a matching\n // format_option_id alone is not enough: canonical_formats_only declarations\n // and declarations without an alias do not participate here.\n const catalogSnapshot = projectWithLegacyConverter(\n fid,\n productId,\n field,\n legacyFormatConverterFromCatalogSnapshots(options?.projectionCatalogs)\n );\n if (catalogSnapshot) return catalogSnapshot;\n\n // Prefer the protocol-correct composite identity. During the legacy\n // migration, deployed sellers have also copied AAO standard IDs while\n // putting their own creative-agent URL in the tuple. Treat that as an\n // inbound alias only when the AAO catalog publishes exactly one entry for\n // the bare ID. `buildParams` below still rejects contradictory inline\n // dimensions/duration, and the emitted v1_format_ref preserves `fid` so a\n // legacy write routes back to the seller rather than the AAO host.\n const exactCatalogEntry = lookupV1Format(fid, options?._catalogPath);\n const canonicalAgentUrl = canonicalizeAgentUrl(fid.agent_url);\n const mayUseUniqueAlias =\n canonicalAgentUrl !== null &&\n new URL(canonicalAgentUrl).protocol === 'https:' &&\n !isLikelyPrivateUrl(canonicalAgentUrl);\n const uniqueAliasEntry =\n exactCatalogEntry === undefined && mayUseUniqueAlias\n ? lookupUniqueV1FormatById(fid.id, options?._catalogPath)\n : undefined;\n if (uniqueAliasEntry) {\n // A caller-authored converter is more specific than this compatibility\n // heuristic. Returning undefined opts into the AAO bare-ID fallback;\n // invalid/throwing converter output fails closed and is never bypassed.\n const explicit = projectWithLegacyConverter(fid, productId, field, options?.legacyFormatConverter);\n if (explicit) return explicit;\n }\n const catalogEntry = exactCatalogEntry ?? uniqueAliasEntry;\n\n // Step 1: v1 catalog has an explicit `canonical` annotation. Always\n // object-shaped per `canonical-projection-ref.json`: required `kind`,\n // optional `asset_source` + `slots_override`. Carry the refinement\n // fields onto the v2 declaration so the projection preserves the\n // spec-authored intent (generative AI projects with text-prompt slot;\n // native projects with extended slot set; etc.).\n if (catalogEntry?.canonical) {\n const projection = catalogEntry.canonical;\n let params: Record<string, unknown>;\n try {\n params = buildParams(fid, {}, catalogEntry);\n } catch (error) {\n if (!(error instanceof CatalogRequirementConflict)) throw error;\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: projection.kind,\n product_id: productId,\n resolution_failure: 'catalog_requirement_conflict',\n },\n },\n },\n };\n }\n if (projection.asset_source) params.asset_source = projection.asset_source;\n if (projection.slots_override) params.slots = projection.slots_override;\n return {\n decl: {\n format_kind: projection.kind,\n params,\n v1_format_ref: [fid],\n },\n };\n }\n\n // Step 1b: catalog HAS the entry but no `canonical:` annotation. This\n // is the AAO saying \"no v2 mapping yet for this category\" — at 3.1\n // GA, native/DOOH/broadcast/card-scaffolding sit in this bucket.\n // Falling through to structural match would shoehorn the format to\n // a coarse `display_tag` based on a `url` asset (or similar) — which\n // contradicts the AAO's deliberate absence of annotation. Fail-closed\n // honestly so the buyer sees \"category not yet v2-mapped\" rather than\n // a semantically wrong projection. Symmetric counterpart to\n // CANONICAL_NOT_V1_TRANSLATABLE on the v2→v1 side.\n if (catalogEntry && !catalogEntry.canonical) {\n const custom = projectWithLegacyConverter(fid, productId, field, options?.legacyFormatConverter);\n if (custom) return custom;\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: 'custom',\n product_id: productId,\n resolution_failure: 'catalog_lacks_canonical_annotation',\n },\n },\n },\n };\n }\n\n // Step 2: format not in the AAO catalog. Try registry glob match\n // against format_id.id. Catches publisher-bespoke ids that share the\n // AAO catalog's naming convention.\n const globMatch = forwardLookupByGlob(fid.id);\n if (globMatch) {\n return {\n decl: {\n format_kind: globMatch.canonical,\n params: buildParams(fid, globMatch.parameters),\n v1_format_ref: [fid],\n },\n };\n }\n\n // Step 3: structural match — only fires when the format is NOT in the\n // catalog (Step 1b ate the catalog-known-but-unannotated case). For\n // truly bespoke publisher formats this is the best signal we have:\n // a VAST tag is a VAST tag regardless of seller naming.\n if (catalogEntry?.assets) {\n const assetTypes = catalogEntry.assets.map(a => a.asset_type).filter((t): t is string => typeof t === 'string');\n const structMatch = forwardLookupByStructural({ asset_types: assetTypes });\n if (structMatch) {\n return {\n decl: {\n format_kind: structMatch.canonical,\n params: buildParams(fid, structMatch.parameters, catalogEntry),\n v1_format_ref: [fid],\n },\n };\n }\n }\n\n // Step 4: give the adopter one explicit, typed escape hatch for a\n // seller/creative-agent-owned legacy format. This runs only after all\n // protocol-owned mappings fail, so a callback cannot override a canonical\n // AAO mapping accidentally.\n const custom = projectWithLegacyConverter(fid, productId, field, options?.legacyFormatConverter);\n if (custom) return custom;\n\n // Step 5: fail-closed. v1 product is invisible on the canonical side.\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: 'custom',\n product_id: productId,\n resolution_failure: 'no_match',\n },\n },\n },\n };\n}\n\n/**\n * Project a v1 Product to a v2 Product. Drops `format_ids` from the\n * public output and rebuilds `format_options` per the resolution order.\n *\n * Caller decides what to do with the result when `format_options` is\n * empty — typically filter the product out of the response payload to\n * a v2-only buyer (the spec requires `format_options` to have\n * `minItems: 1` when present). The function always returns a Product\n * shape so adopters can inspect what got dropped via diagnostics.\n *\n * @see canonicalDeclarationFromBareId — resolve a single bare format-id\n * string (no surrounding Product) to a declaration or `format_kind`.\n */\nexport function projectV1ProductToV2(v1: V1Product, options?: V1ToV2ProjectionOptions): V1ToV2Result {\n const format_options: V2ProductFormatDeclaration[] = [];\n const diagnostics: ProjectionDiagnostic[] = [];\n\n for (let i = 0; i < v1.format_ids.length; i++) {\n const fid = v1.format_ids[i]!;\n const field = `products[${v1.product_id}].format_ids[${i}]`;\n const { decl, diagnostic } = projectFormatId(fid, v1.product_id, field, options);\n if (decl) {\n format_options.push(\n typeof decl.format_option_id === 'string' && decl.format_option_id.length > 0\n ? decl\n : { ...decl, format_option_id: migratedFormatOptionId(fid) }\n );\n }\n if (diagnostic) diagnostics.push(diagnostic);\n }\n\n const { format_ids: _drop, ...rest } = v1;\n void _drop;\n const v2Product: V2Product = {\n ...(rest as Omit<V1Product, 'format_ids'>),\n format_options,\n } as V2Product;\n\n return { v2: v2Product, diagnostics };\n}\n\nexport interface BareFormatIdResolveOptions {\n /**\n * `agent_url` to attach when lifting the bare `id` to a structured\n * {@link V1FormatId} for resolution. Defaults to the canonical AAO host\n * (`https://creative.adcontextprotocol.org/`) — the publisher of every\n * AAO catalog id, and the source of essentially all bare ids persisted\n * before the `{ agent_url, id }` convention. When an exact ID has one\n * unique AAO-published meaning, a valid non-AAO `agentUrl` is accepted as\n * a legacy owner alias and is preserved in `v1_format_ref`. Unknown or\n * colliding IDs still resolve to `null`.\n */\n agentUrl?: string;\n\n /**\n * Asset-type disambiguator for an under-specified bare id. The AAO\n * catalog names per-asset-type variants `<base>_<suffix>` (e.g.\n * `display_300x250` → `display_300x250_image` / `_html` / `_generative`).\n * A size-only bare id like `display_300x250` is genuinely ambiguous and\n * resolves to `null` on its own; pass the asset type you already hold\n * (an adopter's `format_type`) and the resolver retries the disambiguated\n * catalog variant `<id>_<suffix>`.\n *\n * The vocabulary is the catalog asset type where it differs from the\n * variant suffix (`javascript` → `_js`), otherwise the suffix itself\n * (`image`, `html`, `generative`, `js`). Canonical-kind aliases are also\n * accepted (`html5` → `html`, `display_tag` → `js`) for callers already\n * holding a kind-like local value.\n *\n * Only consulted when the bare id does NOT resolve on its own (a real\n * catalog id is authoritative). Still fails closed: if `<id>_<suffix>`\n * is not a catalog entry, returns `null` — the hint narrows, it never\n * fabricates.\n */\n assetType?: string;\n\n /**\n * @deprecated Use `assetType`. Kept as a backwards-compatible alias for\n * callers who adopted the initial helper surface before issue #2289.\n */\n assetTypeHint?: string;\n}\n\n/**\n * Resolve a bare v1 format-id string to its full v2\n * `ProductFormatDeclaration`, or `null` when the id has no canonical\n * mapping.\n *\n * Adopters migrating off legacy format storage routinely hold a bare id\n * (`display_300x250_image`, `video_standard_30s`) persisted before the\n * `{ agent_url, id }` structured-ref convention. This lifts that bare id\n * to a structured ref (via `agentUrl`, default the AAO host) and runs the\n * exact resolution the v1 → v2 product projection uses — in the registry\n * spec's `v1-canonical-mapping.json` resolution order:\n *\n * - AAO catalog `canonical:` annotation — the authoritative\n * seller-asserted mapping (registry resolution-order step 2).\n * - Registry `format_id_glob` literal match (registry resolution-order\n * step 3) — future-proof; 3.1 ships zero literal globs, so this fires\n * only when a future registry adds platform-specific literals.\n *\n * Fails closed: returns `null` — never a guess — when neither path\n * resolves the id. That covers an unknown id, an under-specified id\n * (`display_300x250`, which the catalog only carries as `_image` /\n * `_html` / `_generative` variants), and a catalog entry the AAO has not\n * yet annotated with a `canonical:`. Structural matching never\n * contributes a kind: a bare id absent from the catalog carries no asset\n * shape to match on, and a catalog entry lacking a `canonical:` fails\n * closed before the structural step is reached.\n *\n * For an under-specified bare id, pass `assetType` (the asset type you\n * already hold, such as an adopter's `format_type`) and the resolver retries\n * the disambiguated catalog variant\n * `<id>_<suffix>` — so the SDK owns the `_image` / `_html` suffix\n * convention instead of every adopter re-deriving it. The hint is\n * consulted only when the bare id doesn't resolve on its own, and still\n * fails closed when the disambiguated id isn't a catalog entry.\n *\n * The returned declaration carries `v1_format_ref: [{ agent_url, id }]`\n * (the resolved id — the disambiguated `<id>_<suffix>` when a hint\n * applied), so adopters lift a bare id to a structured ref in one step\n * (the pre-projection step the migration docs encourage).\n *\n * Like the rest of the projection layer, this requires the bundled AAO\n * catalog + canonical-mapping registry; it throws (rather than returning\n * `null`) only when those are missing from the install — a corrupted\n * `@adcp/sdk` package, not a normal unresolved-id outcome.\n *\n * For just the `format_kind`, use {@link resolveCanonicalFormatKind}. For\n * the structured diagnostic explaining *why* an id did not resolve, run\n * it through {@link projectV1ProductToV2} inside a one-format product.\n */\nexport function canonicalDeclarationFromBareId(\n id: string,\n options?: BareFormatIdResolveOptions\n): V2ProductFormatDeclaration | null {\n if (!id) return null;\n const agentUrl = options?.agentUrl ?? AAO_CANONICAL_AGENT_URL;\n\n // A real catalog id is authoritative — resolve it directly first.\n const direct = projectFormatId({ agent_url: agentUrl, id }, `<bare:${id}>`, `bareFormatId(${id})`).decl;\n if (direct) return direct;\n\n // Under-specified bare id + an asset-type hint: retry the disambiguated\n // catalog variant `<id>_<suffix>`. Fails closed if that isn't a catalog\n // entry either — the hint narrows, it never fabricates.\n const assetType = options?.assetType ?? options?.assetTypeHint;\n const suffix = assetType ? normalizeAssetTypeSuffix(assetType) : '';\n if (suffix) {\n const disambiguated = `${id}_${suffix}`;\n const hinted = projectFormatId(\n { agent_url: agentUrl, id: disambiguated },\n `<bare:${disambiguated}>`,\n `bareFormatId(${disambiguated})`\n ).decl;\n if (hinted) return hinted;\n }\n\n return null;\n}\n\n/**\n * Map an `assetType` to the AAO catalog's `<base>_<suffix>` suffix.\n * Catalog asset type `javascript` and canonical-kind names that differ from\n * their suffix (`html5`, `display_tag`) are aliased. Any other value is passed\n * through lowercased so a future asset type or suffix resolves without a code\n * change — an unknown value simply misses the catalog and the caller fails\n * closed.\n */\nfunction normalizeAssetTypeSuffix(hint: string): string {\n const h = hint.trim().toLowerCase();\n if (h === 'html5') return 'html';\n if (h === 'javascript') return 'js';\n if (h === 'display_tag') return 'js';\n return h;\n}\n\n/**\n * Resolve a bare v1 format-id string to its canonical `format_kind`, or\n * `null` when the id has no canonical mapping. Registry- and\n * catalog-backed: the single source of truth that replaces hand-rolled\n * `inferFormatKindFromFormatId` heuristics adopters maintain locally.\n *\n * Thin projection of {@link canonicalDeclarationFromBareId} down to the\n * `format_kind`; see it for the resolution order, fail-closed semantics,\n * the `agentUrl` default, and the `assetType` disambiguator.\n */\nexport function resolveCanonicalFormatKind(\n id: string,\n options?: BareFormatIdResolveOptions\n): CanonicalFormatKind | null {\n return canonicalDeclarationFromBareId(id, options)?.format_kind ?? null;\n}\n"],"mappings":"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","names":["custom"]}
|
|
1
|
+
{"version":3,"sources":["../../../../src/lib/v2/projection/v1-to-v2.ts"],"sourcesContent":["/**\n * v1 → v2 Product projection (the upgrade direction).\n *\n * Used when the SDK is talking to a v1 seller but the buyer wrote V2\n * code. The 8.0 design at `docs/development/v3.1-sdk-design.md` makes\n * V2 the public mental model — the buyer never sees `Product.format_ids`\n * directly, only `Product.format_options`. This module is the wire-\n * boundary translator that makes that promise hold for v1 sellers.\n *\n * Resolution order per format_id, mirroring `v1-canonical-mapping.json`\n * step 1-4 in the forward direction:\n *\n * 1. **v1 catalog explicit `canonical` annotation**. If the v1 format\n * definition (from `reference-formats.json` or a seller's\n * `list_creative_formats`) carries `canonical: <kind>`, that's the\n * authoritative pairing. Seller-asserted, normative.\n * 2. **Registry glob match**. Look up `format_id.id` against the\n * registry's `format_id_glob` entries (including wildcards). First\n * match wins per the spec's ordering.\n * 3. **Structural match**. Match against the v1 format's declared\n * assets + version constraints. Family-level identification\n * yields a canonical (e.g., \"vast 4.x → video_vast\"). Less\n * precise on params; caller may need to fetch additional context.\n * 4. **Fail closed** → `FORMAT_PROJECTION_FAILED`. v1 product with\n * no catalog entry, no registry coverage, and no structural match\n * is invisible on the v2 side. SDK surfaces the diagnostic so\n * buyers know what got dropped.\n *\n * **Asymmetry vs the v2 → v1 direction**: every v1 format_id is a\n * specific thing, so there's no \"ambiguous family\" bucket here — if\n * the structural match identifies a family, the projection is\n * deterministic for that single format_id.\n *\n * **Scope (prototype)**:\n * - AAO catalog only — seller-specific catalogs (publisher's own\n * `list_creative_formats`) require an AgentClient hook the auto-\n * negotiation surface will provide in the full 8.0 enablement.\n * - Param extraction is dimensions-only (`width`, `height`,\n * `duration_ms`). Full canonical-specific params (slots, codecs,\n * char limits, platform_extensions) are not constructed. A v2\n * buyer reading the projected declaration's `params` sees the\n * minimum needed to identify the variant.\n * - Asset slot translation (v1 `assets[]` → v2 `slots[]` via the\n * asset_group_vocabulary aliases) is deliberately deferred. This\n * is the most adopter-relevant piece for actual creative\n * submission flows and lands in a follow-up.\n */\n\nimport type {\n V1Product,\n V1FormatId,\n V2Product,\n V2ProductFormatDeclaration,\n ProjectionDiagnostic,\n CanonicalFormatKind,\n} from './types';\nimport { forwardLookupByGlob, forwardLookupByStructural } from './registry';\nimport { lookupUniqueV1FormatById, lookupV1Format, type V1FormatDefinition } from './catalog';\nimport { AAO_CANONICAL_AGENT_URL } from './constants';\nimport { LIBRARY_VERSION } from '../../version';\nimport { ProductFormatDeclarationSchema } from '../../types/schemas.generated';\nimport { legacyFormatConverterFromCatalogSnapshots, type ProjectionCatalogSnapshot } from './catalog-snapshot';\nimport { canonicalizeAgentUrl } from '../../discovery/resolve-agent-properties';\nimport { isLikelyPrivateUrl } from '../../net/address-guards';\nimport { createHmac } from 'crypto';\nimport { legacyRoutesForProduct } from './legacy-routes';\nimport type { CanonicalFormatLegacyRoute } from './legacy-routes';\n\nconst SDK_ID = `@adcp/sdk@${LIBRARY_VERSION}`;\n\nclass CatalogRequirementConflict extends Error {}\n\n/**\n * Stable identity disambiguator, not a password hash. The input is a public\n * creative-format tuple and the output is a product-local routing label. As\n * with the transport cache disambiguators, HMAC-SHA256 with an empty key gives\n * deterministic collision resistance without placing this non-secret value\n * in CodeQL's password-storage dataflow class.\n */\nfunction formatIdentityDisambiguator(identity: string): string {\n return createHmac('sha256', '').update(identity).digest('hex').slice(0, 32);\n}\n\n/**\n * Give an unnamed projected option a stable, opaque identity derived from the\n * complete legacy tuple. Positional IDs are unsafe: a seller reordering its\n * `format_ids` array could otherwise make a persisted canonical selection\n * resolve to a different legacy format on the next discovery refresh.\n */\nfunction migratedFormatOptionId(fid: V1FormatId): string {\n const identity = JSON.stringify([\n fid.agent_url,\n fid.id,\n fid.width ?? null,\n fid.height ?? null,\n fid.duration_ms ?? null,\n ]);\n return `migrated_${formatIdentityDisambiguator(identity)}`;\n}\n\nexport interface V1ToV2Result {\n v2: V2Product;\n diagnostics: ProjectionDiagnostic[];\n /** Serializable exact routes that can be persisted for a later legacy write. */\n legacyRoutes: CanonicalFormatLegacyRoute[];\n}\n\n/** Context passed to an adopter's seller-specific legacy format converter. */\nexport interface LegacyFormatConversionContext {\n formatId: Readonly<V1FormatId>;\n productId: string;\n field: string;\n}\n\n/**\n * Escape hatch for legacy formats owned by a custom creative agent. The\n * converter returns the canonical product declaration that the legacy ref\n * represents. For bespoke shapes, return `format_kind: 'custom'` with both\n * `format_shape` and an immutable `format_schema` reference.\n *\n * The SDK adds the source `formatId` as `v1_format_ref`; converters must not\n * set `canonical_formats_only: true` because a legacy source is, by\n * definition, round-trippable to that ref.\n */\nexport type LegacyFormatConverter = (\n context: LegacyFormatConversionContext\n) => V2ProductFormatDeclaration | null | undefined;\n\nexport interface V1ToV2ProjectionOptions {\n legacyFormatConverter?: LegacyFormatConverter;\n /** Pre-resolved exact-owner publisher/community catalogs, highest precedence first. */\n projectionCatalogs?: readonly ProjectionCatalogSnapshot[];\n /** @internal Filesystem-isolated catalog fixture used by projector tests. */\n _catalogPath?: string;\n}\n\n/**\n * Build the v2 declaration's `params` block from the v1 format_id's\n * dimensional overrides + the catalog/registry entry's recorded params.\n *\n * Prototype scope: dimensions + duration only. A full implementation\n * would walk the canonical's parameter schema and populate every field\n * the catalog entry hints at (codecs, char limits, platform_extensions).\n */\nfunction buildParams(\n fid: V1FormatId,\n registryParams: Record<string, unknown>,\n catalogEntry?: V1FormatDefinition\n): Record<string, unknown> {\n const params: Record<string, unknown> = { ...registryParams };\n\n // Catalog-authored fixed requirements are normative projection inputs.\n // Prefer the primary render dimensions when there is exactly one fixed\n // size; otherwise accept one unambiguous width/height pair from the asset\n // requirements. A fixed duration is equally safe only when min === max.\n // Never guess from a name, a range, or conflicting requirements.\n const fixedSizes = new Map<string, { width: number; height: number }>();\n let hasUnsupportedSizeRequirement = false;\n for (const render of catalogEntry?.renders ?? []) {\n const width = render.dimensions?.width;\n const height = render.dimensions?.height;\n if (width === undefined && height === undefined) continue;\n if (\n typeof width === 'number' &&\n Number.isInteger(width) &&\n width > 0 &&\n typeof height === 'number' &&\n Number.isInteger(height) &&\n height > 0\n ) {\n fixedSizes.set(`${width}x${height}`, { width, height });\n } else hasUnsupportedSizeRequirement = true;\n }\n for (const asset of catalogEntry?.assets ?? []) {\n const width = asset.requirements?.width;\n const height = asset.requirements?.height;\n const minWidth = asset.requirements?.min_width;\n const maxWidth = asset.requirements?.max_width;\n const minHeight = asset.requirements?.min_height;\n const maxHeight = asset.requirements?.max_height;\n if (minWidth !== undefined || maxWidth !== undefined || minHeight !== undefined || maxHeight !== undefined) {\n if (\n typeof minWidth === 'number' &&\n Number.isInteger(minWidth) &&\n minWidth > 0 &&\n minWidth === maxWidth &&\n typeof minHeight === 'number' &&\n Number.isInteger(minHeight) &&\n minHeight > 0 &&\n minHeight === maxHeight\n ) {\n fixedSizes.set(`${minWidth}x${minHeight}`, { width: minWidth, height: minHeight });\n } else {\n hasUnsupportedSizeRequirement = true;\n }\n }\n if (width === undefined && height === undefined) continue;\n if (\n typeof width === 'number' &&\n Number.isInteger(width) &&\n width > 0 &&\n typeof height === 'number' &&\n Number.isInteger(height) &&\n height > 0\n ) {\n fixedSizes.set(`${width}x${height}`, { width, height });\n } else hasUnsupportedSizeRequirement = true;\n }\n if (fixedSizes.size > 1 || hasUnsupportedSizeRequirement) {\n throw new CatalogRequirementConflict('catalog contains conflicting fixed dimensions');\n }\n if (fixedSizes.size === 1) {\n const fixedSize = fixedSizes.values().next().value;\n if (fixedSize) {\n if (\n (typeof fid.width === 'number' && fid.width !== fixedSize.width) ||\n (typeof fid.height === 'number' && fid.height !== fixedSize.height)\n ) {\n throw new CatalogRequirementConflict('format id conflicts with catalog dimensions');\n }\n params.width = fixedSize.width;\n params.height = fixedSize.height;\n }\n }\n\n const fixedDurations = new Set<number>();\n let hasRangedDuration = false;\n for (const asset of catalogEntry?.assets ?? []) {\n const min = asset.requirements?.min_duration_ms;\n const max = asset.requirements?.max_duration_ms;\n if (min === undefined && max === undefined) continue;\n if (\n (min !== undefined && (typeof min !== 'number' || !Number.isInteger(min) || min <= 0)) ||\n (max !== undefined && (typeof max !== 'number' || !Number.isInteger(max) || max <= 0)) ||\n (typeof min === 'number' && typeof max === 'number' && min > max)\n ) {\n throw new CatalogRequirementConflict('catalog contains invalid duration requirements');\n }\n if (typeof min === 'number' && min === max) fixedDurations.add(min);\n else hasRangedDuration = true;\n }\n if (fixedDurations.size > 1 || hasRangedDuration) {\n throw new CatalogRequirementConflict('catalog contains conflicting fixed durations');\n }\n if (fixedDurations.size === 1) {\n const fixedDuration = fixedDurations.values().next().value;\n if (fixedDuration !== undefined) {\n if (typeof fid.duration_ms === 'number' && fid.duration_ms !== fixedDuration) {\n throw new CatalogRequirementConflict('format id conflicts with catalog duration');\n }\n params.duration_ms_exact = fixedDuration;\n }\n }\n\n // Inline discriminators narrow parameterized catalog templates. Concrete\n // catalog defaults were checked for contradictions above.\n if (typeof fid.width === 'number') params.width = fid.width;\n if (typeof fid.height === 'number') params.height = fid.height;\n if (typeof fid.duration_ms === 'number') params.duration_ms_exact = fid.duration_ms;\n return params;\n}\n\nfunction findLegacyCreativeIdentity(value: unknown, seen = new WeakSet<object>()): string | undefined {\n if (value === null || typeof value !== 'object') return undefined;\n if (seen.has(value)) return undefined;\n seen.add(value);\n if (Array.isArray(value)) {\n for (const item of value) {\n const nested = findLegacyCreativeIdentity(item, seen);\n if (nested) return nested;\n }\n return undefined;\n }\n for (const [key, child] of Object.entries(value as Record<string, unknown>)) {\n if (/(^|_)(?:format_ids?|v1_format_ref|agent_url)($|_)/.test(key)) {\n return key;\n }\n const nested = findLegacyCreativeIdentity(child, seen);\n if (nested) return nested;\n }\n return undefined;\n}\n\nfunction projectWithLegacyConverter(\n fid: V1FormatId,\n productId: string,\n field: string,\n converter: LegacyFormatConverter | undefined\n): { decl?: V2ProductFormatDeclaration; diagnostic?: ProjectionDiagnostic } | undefined {\n if (!converter) return undefined;\n try {\n const converted = converter({ formatId: { ...fid }, productId, field });\n if (!converted) return undefined;\n const forbidden = findLegacyCreativeIdentity(converted);\n if (forbidden) {\n throw new Error(`canonical conversion must not return ${forbidden}`);\n }\n const completed = { ...converted, v1_format_ref: [fid] };\n if (completed.canonical_formats_only === true) {\n throw new Error('a conversion from a legacy format cannot set canonical_formats_only: true');\n }\n if (\n completed.format_kind === 'custom' &&\n (typeof completed.format_shape !== 'string' ||\n completed.format_shape.trim().length === 0 ||\n !completed.format_schema)\n ) {\n throw new Error('custom conversions require format_shape and format_schema');\n }\n if (\n completed.format_kind === 'custom' &&\n (typeof completed.format_option_id !== 'string' || completed.format_option_id.trim().length === 0)\n ) {\n throw new Error('custom conversions require format_option_id');\n }\n const parsed = ProductFormatDeclarationSchema.safeParse(completed);\n if (!parsed.success) {\n throw new Error('converter returned an invalid canonical format declaration');\n }\n return { decl: parsed.data as V2ProductFormatDeclaration };\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: 'custom',\n product_id: productId,\n resolution_failure: 'custom_converter_failed',\n },\n },\n },\n };\n }\n}\n\n/**\n * Project a single v1 `format_id` to a v2 `ProductFormatDeclaration`,\n * or to a diagnostic when no projection is possible.\n */\nfunction projectFormatId(\n fid: V1FormatId,\n productId: string,\n field: string,\n options?: V1ToV2ProjectionOptions\n): { decl?: V2ProductFormatDeclaration; diagnostic?: ProjectionDiagnostic } {\n const hasWidth = fid.width !== undefined;\n const hasHeight = fid.height !== undefined;\n const invalidDimensions =\n hasWidth !== hasHeight ||\n (hasWidth && (!Number.isInteger(fid.width) || fid.width! <= 0)) ||\n (hasHeight && (!Number.isInteger(fid.height) || fid.height! <= 0));\n const invalidDuration = fid.duration_ms !== undefined && (!Number.isInteger(fid.duration_ms) || fid.duration_ms <= 0);\n if (invalidDimensions || invalidDuration) {\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: 'custom',\n product_id: productId,\n resolution_failure: 'invalid_format_id_parameters',\n },\n },\n },\n };\n }\n\n // A publisher/community catalog carrying an explicit v1_format_ref is the\n // most specific authoritative pairing. Public availability or a matching\n // format_option_id alone is not enough: canonical_formats_only declarations\n // and declarations without an alias do not participate here.\n const catalogSnapshot = projectWithLegacyConverter(\n fid,\n productId,\n field,\n legacyFormatConverterFromCatalogSnapshots(options?.projectionCatalogs)\n );\n if (catalogSnapshot) return catalogSnapshot;\n\n // Prefer the protocol-correct composite identity. During the legacy\n // migration, deployed sellers have also copied AAO standard IDs while\n // putting their own creative-agent URL in the tuple. Treat that as an\n // inbound alias only when the AAO catalog publishes exactly one entry for\n // the bare ID. `buildParams` below still rejects contradictory inline\n // dimensions/duration, and the emitted v1_format_ref preserves `fid` so a\n // legacy write routes back to the seller rather than the AAO host.\n const exactCatalogEntry = lookupV1Format(fid, options?._catalogPath);\n const canonicalAgentUrl = canonicalizeAgentUrl(fid.agent_url);\n const mayUseUniqueAlias =\n canonicalAgentUrl !== null &&\n new URL(canonicalAgentUrl).protocol === 'https:' &&\n !isLikelyPrivateUrl(canonicalAgentUrl);\n const uniqueAliasEntry =\n exactCatalogEntry === undefined && mayUseUniqueAlias\n ? lookupUniqueV1FormatById(fid.id, options?._catalogPath)\n : undefined;\n if (uniqueAliasEntry) {\n // A caller-authored converter is more specific than this compatibility\n // heuristic. Returning undefined opts into the AAO bare-ID fallback;\n // invalid/throwing converter output fails closed and is never bypassed.\n const explicit = projectWithLegacyConverter(fid, productId, field, options?.legacyFormatConverter);\n if (explicit) return explicit;\n }\n const catalogEntry = exactCatalogEntry ?? uniqueAliasEntry;\n\n // Step 1: v1 catalog has an explicit `canonical` annotation. Always\n // object-shaped per `canonical-projection-ref.json`: required `kind`,\n // optional `asset_source` + `slots_override`. Carry the refinement\n // fields onto the v2 declaration so the projection preserves the\n // spec-authored intent (generative AI projects with text-prompt slot;\n // native projects with extended slot set; etc.).\n if (catalogEntry?.canonical) {\n const projection = catalogEntry.canonical;\n let params: Record<string, unknown>;\n try {\n params = buildParams(fid, {}, catalogEntry);\n } catch (error) {\n if (!(error instanceof CatalogRequirementConflict)) throw error;\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: projection.kind,\n product_id: productId,\n resolution_failure: 'catalog_requirement_conflict',\n },\n },\n },\n };\n }\n if (projection.asset_source) params.asset_source = projection.asset_source;\n if (projection.slots_override) params.slots = projection.slots_override;\n return {\n decl: {\n format_kind: projection.kind,\n params,\n v1_format_ref: [fid],\n },\n };\n }\n\n // Step 1b: catalog HAS the entry but no `canonical:` annotation. This\n // is the AAO saying \"no v2 mapping yet for this category\" — at 3.1\n // GA, native/DOOH/broadcast/card-scaffolding sit in this bucket.\n // Falling through to structural match would shoehorn the format to\n // a coarse `display_tag` based on a `url` asset (or similar) — which\n // contradicts the AAO's deliberate absence of annotation. Fail-closed\n // honestly so the buyer sees \"category not yet v2-mapped\" rather than\n // a semantically wrong projection. Symmetric counterpart to\n // CANONICAL_NOT_V1_TRANSLATABLE on the v2→v1 side.\n if (catalogEntry && !catalogEntry.canonical) {\n const custom = projectWithLegacyConverter(fid, productId, field, options?.legacyFormatConverter);\n if (custom) return custom;\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: 'custom',\n product_id: productId,\n resolution_failure: 'catalog_lacks_canonical_annotation',\n },\n },\n },\n };\n }\n\n // Step 2: format not in the AAO catalog. Try registry glob match\n // against format_id.id. Catches publisher-bespoke ids that share the\n // AAO catalog's naming convention.\n const globMatch = forwardLookupByGlob(fid.id);\n if (globMatch) {\n return {\n decl: {\n format_kind: globMatch.canonical,\n params: buildParams(fid, globMatch.parameters),\n v1_format_ref: [fid],\n },\n };\n }\n\n // Step 3: structural match — only fires when the format is NOT in the\n // catalog (Step 1b ate the catalog-known-but-unannotated case). For\n // truly bespoke publisher formats this is the best signal we have:\n // a VAST tag is a VAST tag regardless of seller naming.\n if (catalogEntry?.assets) {\n const assetTypes = catalogEntry.assets.map(a => a.asset_type).filter((t): t is string => typeof t === 'string');\n const structMatch = forwardLookupByStructural({ asset_types: assetTypes });\n if (structMatch) {\n return {\n decl: {\n format_kind: structMatch.canonical,\n params: buildParams(fid, structMatch.parameters, catalogEntry),\n v1_format_ref: [fid],\n },\n };\n }\n }\n\n // Step 4: give the adopter one explicit, typed escape hatch for a\n // seller/creative-agent-owned legacy format. This runs only after all\n // protocol-owned mappings fail, so a callback cannot override a canonical\n // AAO mapping accidentally.\n const custom = projectWithLegacyConverter(fid, productId, field, options?.legacyFormatConverter);\n if (custom) return custom;\n\n // Step 5: fail-closed. v1 product is invisible on the canonical side.\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: 'custom',\n product_id: productId,\n resolution_failure: 'no_match',\n },\n },\n },\n };\n}\n\n/**\n * Project a v1 Product to a v2 Product. Drops `format_ids` from the\n * public output and rebuilds `format_options` per the resolution order.\n *\n * Caller decides what to do with the result when `format_options` is\n * empty — typically filter the product out of the response payload to\n * a v2-only buyer (the spec requires `format_options` to have\n * `minItems: 1` when present). The function always returns a Product\n * shape so adopters can inspect what got dropped via diagnostics.\n *\n * @see canonicalDeclarationFromBareId — resolve a single bare format-id\n * string (no surrounding Product) to a declaration or `format_kind`.\n */\nexport function projectV1ProductToV2(v1: V1Product, options?: V1ToV2ProjectionOptions): V1ToV2Result {\n const format_options: V2ProductFormatDeclaration[] = [];\n const diagnostics: ProjectionDiagnostic[] = [];\n\n for (let i = 0; i < v1.format_ids.length; i++) {\n const fid = v1.format_ids[i]!;\n const field = `products[${v1.product_id}].format_ids[${i}]`;\n const { decl, diagnostic } = projectFormatId(fid, v1.product_id, field, options);\n if (decl) {\n format_options.push(\n typeof decl.format_option_id === 'string' && decl.format_option_id.length > 0\n ? decl\n : { ...decl, format_option_id: migratedFormatOptionId(fid) }\n );\n }\n if (diagnostic) diagnostics.push(diagnostic);\n }\n\n const { format_ids: _drop, ...rest } = v1;\n void _drop;\n const v2Product: V2Product = {\n ...(rest as Omit<V1Product, 'format_ids'>),\n format_options,\n } as V2Product;\n\n return { v2: v2Product, diagnostics, legacyRoutes: legacyRoutesForProduct(v1.product_id, format_options) };\n}\n\nexport interface BareFormatIdResolveOptions {\n /**\n * `agent_url` to attach when lifting the bare `id` to a structured\n * {@link V1FormatId} for resolution. Defaults to the canonical AAO host\n * (`https://creative.adcontextprotocol.org/`) — the publisher of every\n * AAO catalog id, and the source of essentially all bare ids persisted\n * before the `{ agent_url, id }` convention. When an exact ID has one\n * unique AAO-published meaning, a valid non-AAO `agentUrl` is accepted as\n * a legacy owner alias and is preserved in `v1_format_ref`. Unknown or\n * colliding IDs still resolve to `null`.\n */\n agentUrl?: string;\n\n /**\n * Asset-type disambiguator for an under-specified bare id. The AAO\n * catalog names per-asset-type variants `<base>_<suffix>` (e.g.\n * `display_300x250` → `display_300x250_image` / `_html` / `_generative`).\n * A size-only bare id like `display_300x250` is genuinely ambiguous and\n * resolves to `null` on its own; pass the asset type you already hold\n * (an adopter's `format_type`) and the resolver retries the disambiguated\n * catalog variant `<id>_<suffix>`.\n *\n * The vocabulary is the catalog asset type where it differs from the\n * variant suffix (`javascript` → `_js`), otherwise the suffix itself\n * (`image`, `html`, `generative`, `js`). Canonical-kind aliases are also\n * accepted (`html5` → `html`, `display_tag` → `js`) for callers already\n * holding a kind-like local value.\n *\n * Only consulted when the bare id does NOT resolve on its own (a real\n * catalog id is authoritative). Still fails closed: if `<id>_<suffix>`\n * is not a catalog entry, returns `null` — the hint narrows, it never\n * fabricates.\n */\n assetType?: string;\n\n /**\n * @deprecated Use `assetType`. Kept as a backwards-compatible alias for\n * callers who adopted the initial helper surface before issue #2289.\n */\n assetTypeHint?: string;\n}\n\n/**\n * Resolve a bare v1 format-id string to its full v2\n * `ProductFormatDeclaration`, or `null` when the id has no canonical\n * mapping.\n *\n * Adopters migrating off legacy format storage routinely hold a bare id\n * (`display_300x250_image`, `video_standard_30s`) persisted before the\n * `{ agent_url, id }` structured-ref convention. This lifts that bare id\n * to a structured ref (via `agentUrl`, default the AAO host) and runs the\n * exact resolution the v1 → v2 product projection uses — in the registry\n * spec's `v1-canonical-mapping.json` resolution order:\n *\n * - AAO catalog `canonical:` annotation — the authoritative\n * seller-asserted mapping (registry resolution-order step 2).\n * - Registry `format_id_glob` literal match (registry resolution-order\n * step 3) — future-proof; 3.1 ships zero literal globs, so this fires\n * only when a future registry adds platform-specific literals.\n *\n * Fails closed: returns `null` — never a guess — when neither path\n * resolves the id. That covers an unknown id, an under-specified id\n * (`display_300x250`, which the catalog only carries as `_image` /\n * `_html` / `_generative` variants), and a catalog entry the AAO has not\n * yet annotated with a `canonical:`. Structural matching never\n * contributes a kind: a bare id absent from the catalog carries no asset\n * shape to match on, and a catalog entry lacking a `canonical:` fails\n * closed before the structural step is reached.\n *\n * For an under-specified bare id, pass `assetType` (the asset type you\n * already hold, such as an adopter's `format_type`) and the resolver retries\n * the disambiguated catalog variant\n * `<id>_<suffix>` — so the SDK owns the `_image` / `_html` suffix\n * convention instead of every adopter re-deriving it. The hint is\n * consulted only when the bare id doesn't resolve on its own, and still\n * fails closed when the disambiguated id isn't a catalog entry.\n *\n * The returned declaration carries `v1_format_ref: [{ agent_url, id }]`\n * (the resolved id — the disambiguated `<id>_<suffix>` when a hint\n * applied), so adopters lift a bare id to a structured ref in one step\n * (the pre-projection step the migration docs encourage).\n *\n * Like the rest of the projection layer, this requires the bundled AAO\n * catalog + canonical-mapping registry; it throws (rather than returning\n * `null`) only when those are missing from the install — a corrupted\n * `@adcp/sdk` package, not a normal unresolved-id outcome.\n *\n * For just the `format_kind`, use {@link resolveCanonicalFormatKind}. For\n * the structured diagnostic explaining *why* an id did not resolve, run\n * it through {@link projectV1ProductToV2} inside a one-format product.\n */\nexport function canonicalDeclarationFromBareId(\n id: string,\n options?: BareFormatIdResolveOptions\n): V2ProductFormatDeclaration | null {\n if (!id) return null;\n const agentUrl = options?.agentUrl ?? AAO_CANONICAL_AGENT_URL;\n\n // A real catalog id is authoritative — resolve it directly first.\n const direct = projectFormatId({ agent_url: agentUrl, id }, `<bare:${id}>`, `bareFormatId(${id})`).decl;\n if (direct) return direct;\n\n // Under-specified bare id + an asset-type hint: retry the disambiguated\n // catalog variant `<id>_<suffix>`. Fails closed if that isn't a catalog\n // entry either — the hint narrows, it never fabricates.\n const assetType = options?.assetType ?? options?.assetTypeHint;\n const suffix = assetType ? normalizeAssetTypeSuffix(assetType) : '';\n if (suffix) {\n const disambiguated = `${id}_${suffix}`;\n const hinted = projectFormatId(\n { agent_url: agentUrl, id: disambiguated },\n `<bare:${disambiguated}>`,\n `bareFormatId(${disambiguated})`\n ).decl;\n if (hinted) return hinted;\n }\n\n return null;\n}\n\n/**\n * Map an `assetType` to the AAO catalog's `<base>_<suffix>` suffix.\n * Catalog asset type `javascript` and canonical-kind names that differ from\n * their suffix (`html5`, `display_tag`) are aliased. Any other value is passed\n * through lowercased so a future asset type or suffix resolves without a code\n * change — an unknown value simply misses the catalog and the caller fails\n * closed.\n */\nfunction normalizeAssetTypeSuffix(hint: string): string {\n const h = hint.trim().toLowerCase();\n if (h === 'html5') return 'html';\n if (h === 'javascript') return 'js';\n if (h === 'display_tag') return 'js';\n return h;\n}\n\n/**\n * Resolve a bare v1 format-id string to its canonical `format_kind`, or\n * `null` when the id has no canonical mapping. Registry- and\n * catalog-backed: the single source of truth that replaces hand-rolled\n * `inferFormatKindFromFormatId` heuristics adopters maintain locally.\n *\n * Thin projection of {@link canonicalDeclarationFromBareId} down to the\n * `format_kind`; see it for the resolution order, fail-closed semantics,\n * the `agentUrl` default, and the `assetType` disambiguator.\n */\nexport function resolveCanonicalFormatKind(\n id: string,\n options?: BareFormatIdResolveOptions\n): CanonicalFormatKind | null {\n return canonicalDeclarationFromBareId(id, options)?.format_kind ?? null;\n}\n"],"mappings":"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","names":["custom"]}
|
package/dist/lib/version.d.mts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* AdCP SDK library version
|
|
3
3
|
*/
|
|
4
|
-
export declare const LIBRARY_VERSION = "13.0.0-rc.
|
|
4
|
+
export declare const LIBRARY_VERSION = "13.0.0-rc.13";
|
|
5
5
|
/**
|
|
6
6
|
* AdCP specification version this library is built for
|
|
7
7
|
*/
|
|
@@ -33,10 +33,10 @@ export type AdcpVersion = (typeof COMPATIBLE_ADCP_VERSIONS)[number];
|
|
|
33
33
|
* Full version information
|
|
34
34
|
*/
|
|
35
35
|
export declare const VERSION_INFO: {
|
|
36
|
-
readonly library: "13.0.0-rc.
|
|
36
|
+
readonly library: "13.0.0-rc.13";
|
|
37
37
|
readonly adcp: "3.1.11";
|
|
38
38
|
readonly compatibleVersions: readonly ["v2.5", "v2.6", "v3", "3.0.0-beta.1", "3.0-beta.1", "3.0-beta", "3.0.0-beta.3", "3.0-beta.3", "3.0.0", "3.0", "3.0.1", "3.0.2", "3.0.3", "3.0.4", "3.0.5", "3.0.6", "3.0.7", "3.0.8", "3.0.9", "3.0.10", "3.0.11", "3.0.12", "3.1.0", "3.1", "3.1.1", "3.1.2", "3.1.3", "3.1.4", "3.1.5", "3.1.6", "3.1.7", "3.1.8", "3.1.9", "3.1.10", "3.1.11"];
|
|
39
|
-
readonly generatedAt: "2026-08-
|
|
39
|
+
readonly generatedAt: "2026-08-10T05:35:45.573Z";
|
|
40
40
|
};
|
|
41
41
|
/**
|
|
42
42
|
* Get the AdCP specification version this library is built for
|
package/dist/lib/version.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* AdCP SDK library version
|
|
3
3
|
*/
|
|
4
|
-
export declare const LIBRARY_VERSION = "13.0.0-rc.
|
|
4
|
+
export declare const LIBRARY_VERSION = "13.0.0-rc.13";
|
|
5
5
|
/**
|
|
6
6
|
* AdCP specification version this library is built for
|
|
7
7
|
*/
|
|
@@ -33,10 +33,10 @@ export type AdcpVersion = (typeof COMPATIBLE_ADCP_VERSIONS)[number];
|
|
|
33
33
|
* Full version information
|
|
34
34
|
*/
|
|
35
35
|
export declare const VERSION_INFO: {
|
|
36
|
-
readonly library: "13.0.0-rc.
|
|
36
|
+
readonly library: "13.0.0-rc.13";
|
|
37
37
|
readonly adcp: "3.1.11";
|
|
38
38
|
readonly compatibleVersions: readonly ["v2.5", "v2.6", "v3", "3.0.0-beta.1", "3.0-beta.1", "3.0-beta", "3.0.0-beta.3", "3.0-beta.3", "3.0.0", "3.0", "3.0.1", "3.0.2", "3.0.3", "3.0.4", "3.0.5", "3.0.6", "3.0.7", "3.0.8", "3.0.9", "3.0.10", "3.0.11", "3.0.12", "3.1.0", "3.1", "3.1.1", "3.1.2", "3.1.3", "3.1.4", "3.1.5", "3.1.6", "3.1.7", "3.1.8", "3.1.9", "3.1.10", "3.1.11"];
|
|
39
|
-
readonly generatedAt: "2026-08-
|
|
39
|
+
readonly generatedAt: "2026-08-10T05:35:45.573Z";
|
|
40
40
|
};
|
|
41
41
|
/**
|
|
42
42
|
* Get the AdCP specification version this library is built for
|
package/dist/lib/version.js
CHANGED
|
@@ -31,7 +31,7 @@ __export(version_exports, {
|
|
|
31
31
|
toReleasePrecisionVersion: () => toReleasePrecisionVersion
|
|
32
32
|
});
|
|
33
33
|
module.exports = __toCommonJS(version_exports);
|
|
34
|
-
const LIBRARY_VERSION = "13.0.0-rc.
|
|
34
|
+
const LIBRARY_VERSION = "13.0.0-rc.13";
|
|
35
35
|
const ADCP_VERSION = "3.1.11";
|
|
36
36
|
const ADCP_MAJOR_VERSION = 3;
|
|
37
37
|
const COMPATIBLE_ADCP_VERSIONS = [
|
|
@@ -72,10 +72,10 @@ const COMPATIBLE_ADCP_VERSIONS = [
|
|
|
72
72
|
"3.1.11"
|
|
73
73
|
];
|
|
74
74
|
const VERSION_INFO = {
|
|
75
|
-
library: "13.0.0-rc.
|
|
75
|
+
library: "13.0.0-rc.13",
|
|
76
76
|
adcp: "3.1.11",
|
|
77
77
|
compatibleVersions: COMPATIBLE_ADCP_VERSIONS,
|
|
78
|
-
generatedAt: "2026-08-
|
|
78
|
+
generatedAt: "2026-08-10T05:35:45.573Z"
|
|
79
79
|
};
|
|
80
80
|
function getAdcpVersion() {
|
|
81
81
|
return ADCP_VERSION;
|
package/dist/lib/version.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/lib/version.ts"],"sourcesContent":["// Generated version information\n// This file is auto-generated by sync-version.ts\n\n/**\n * AdCP SDK library version\n */\nexport const LIBRARY_VERSION = '13.0.0-rc.
|
|
1
|
+
{"version":3,"sources":["../../src/lib/version.ts"],"sourcesContent":["// Generated version information\n// This file is auto-generated by sync-version.ts\n\n/**\n * AdCP SDK library version\n */\nexport const LIBRARY_VERSION = '13.0.0-rc.13';\n\n/**\n * AdCP specification version this library is built for\n */\nexport const ADCP_VERSION = '3.1.11';\n\n/**\n * AdCP major version sent with every request (adcp_major_version field).\n * Sellers validate this against their supported versions and return\n * VERSION_UNSUPPORTED if the version is not in range.\n */\nexport const ADCP_MAJOR_VERSION = 3;\n\n/**\n * AdCP versions this library maintains backward compatibility with.\n *\n * Auto-derived from `ADCP_VERSION` by scripts/sync-version.ts. Do not edit\n * this list by hand; bumping the AdCP pin via `npm run sync-version`\n * extends it.\n */\nexport const COMPATIBLE_ADCP_VERSIONS = [\n 'v2.5',\n 'v2.6',\n 'v3',\n '3.0.0-beta.1',\n '3.0-beta.1',\n '3.0-beta',\n '3.0.0-beta.3',\n '3.0-beta.3',\n '3.0.0',\n '3.0',\n '3.0.1',\n '3.0.2',\n '3.0.3',\n '3.0.4',\n '3.0.5',\n '3.0.6',\n '3.0.7',\n '3.0.8',\n '3.0.9',\n '3.0.10',\n '3.0.11',\n '3.0.12',\n '3.1.0',\n '3.1',\n '3.1.1',\n '3.1.2',\n '3.1.3',\n '3.1.4',\n '3.1.5',\n '3.1.6',\n '3.1.7',\n '3.1.8',\n '3.1.9',\n '3.1.10',\n '3.1.11',\n] as const;\n\n/**\n * String literal union of every AdCP version the SDK formally supports.\n *\n * Used by the per-instance `adcpVersion` constructor option to give callers\n * autocomplete in editors. The intersection with `(string & {})` in the\n * config type preserves the escape hatch — any string is still accepted at\n * the type level — while the literal union surfaces canonical values first.\n */\nexport type AdcpVersion = (typeof COMPATIBLE_ADCP_VERSIONS)[number];\n\n/**\n * Full version information\n */\nexport const VERSION_INFO = {\n library: '13.0.0-rc.13',\n adcp: '3.1.11',\n compatibleVersions: COMPATIBLE_ADCP_VERSIONS,\n generatedAt: '2026-08-10T05:35:45.573Z',\n} as const;\n\n/**\n * Get the AdCP specification version this library is built for\n */\nexport function getAdcpVersion(): string {\n return ADCP_VERSION;\n}\n\n/**\n * Get the library version\n */\nexport function getLibraryVersion(): string {\n return LIBRARY_VERSION;\n}\n\n/**\n * Check if this library version is compatible with a given AdCP version\n */\nexport function isCompatibleWith(adcpVersion: string): boolean {\n return (COMPATIBLE_ADCP_VERSIONS as readonly string[]).includes(adcpVersion);\n}\n\n/**\n * Get all AdCP versions this library is compatible with\n */\nexport function getCompatibleVersions(): readonly string[] {\n return COMPATIBLE_ADCP_VERSIONS;\n}\n\n/**\n * Extract the major version number from an AdCP version string.\n *\n * Accepts:\n * - Semver: '3.0.0', '3.0.1', '3.1.0-beta.1' → 3\n * - Legacy aliases: 'v3' → 3, 'v2.5' / 'v2.6' → 2\n *\n * Returns NaN for unrecognized strings — callers should validate before passing.\n */\nexport function parseAdcpMajorVersion(version: string): number {\n const trimmed = version.trim();\n const semverLike = trimmed.startsWith('v') ? trimmed.slice(1) : trimmed;\n const major = parseInt(semverLike.split('.')[0] ?? '', 10);\n return Number.isFinite(major) ? major : NaN;\n}\n\n/**\n * Normalize a full-semver AdCP version (`MAJOR.MINOR.PATCH[-prerelease]`) to\n * the release-precision form that AdCP 3.1+ requires on the wire:\n * `MAJOR.MINOR[-prerelease]` — the patch digit is dropped.\n *\n * Per the spec note on `adcp_version`: \"SDKs that read full-semver values\n * from bundle metadata (e.g. `ComplianceIndex.published_version =\n * \"3.1.0-beta.1\"`) MUST normalize to release-precision (`\"3.1-beta.1\"`)\n * before emitting on the wire — meta-field values are NOT valid wire\n * values.\" The wire regex (`^\\d+\\.\\d+(-[a-zA-Z0-9.-]+)?$`) rejects strings\n * with a patch digit.\n *\n * Behavior:\n * - `\"3.1.0-beta.7\"` → `\"3.1-beta.7\"`\n * - `\"3.1.0\"` → `\"3.1\"`\n * - `\"3.0.12\"` → `\"3.0\"`\n * - Already-release-precision input (`\"3.1\"`, `\"3.1-beta.7\"`) passes through\n * - Legacy aliases (`\"v2.5\"`, `\"v3\"`) pass through unchanged — the wire\n * regex doesn't accept them anyway; the v2.5 path uses\n * `adcp_major_version` instead of `adcp_version` for transport.\n * - Unrecognized strings pass through unchanged so callers can detect drift\n * via the wire validator rather than have it masked by this helper.\n */\nexport function toReleasePrecisionVersion(version: string): string {\n const trimmed = version.trim();\n // Pre-release form `MAJOR.MINOR.PATCH-prerelease` → `MAJOR.MINOR-prerelease`\n const semverMatch = trimmed.match(/^(\\d+)\\.(\\d+)\\.\\d+(-[A-Za-z0-9.-]+)?$/);\n if (semverMatch) {\n const [, major, minor, pre = ''] = semverMatch;\n return `${major}.${minor}${pre}`;\n }\n // Already release-precision (no patch digit). Includes `3.1`, `3.1-beta.7`.\n if (/^\\d+\\.\\d+(-[A-Za-z0-9.-]+)?$/.test(trimmed)) return trimmed;\n // Legacy aliases (`v3`, `v2.5`, `v2.6`) and anything we don't recognize —\n // pass through so the wire validator can flag genuine drift.\n return version;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAMO,MAAM,kBAAkB;AAKxB,MAAM,eAAe;AAOrB,MAAM,qBAAqB;AAS3B,MAAM,2BAA2B;AAAA,EACtC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAeO,MAAM,eAAe;AAAA,EAC1B,SAAS;AAAA,EACT,MAAM;AAAA,EACN,oBAAoB;AAAA,EACpB,aAAa;AACf;AAKO,SAAS,iBAAyB;AACvC,SAAO;AACT;AAKO,SAAS,oBAA4B;AAC1C,SAAO;AACT;AAKO,SAAS,iBAAiB,aAA8B;AAC7D,SAAQ,yBAA+C,SAAS,WAAW;AAC7E;AAKO,SAAS,wBAA2C;AACzD,SAAO;AACT;AAWO,SAAS,sBAAsB,SAAyB;AAC7D,QAAM,UAAU,QAAQ,KAAK;AAC7B,QAAM,aAAa,QAAQ,WAAW,GAAG,IAAI,QAAQ,MAAM,CAAC,IAAI;AAChE,QAAM,QAAQ,SAAS,WAAW,MAAM,GAAG,EAAE,CAAC,KAAK,IAAI,EAAE;AACzD,SAAO,OAAO,SAAS,KAAK,IAAI,QAAQ;AAC1C;AAyBO,SAAS,0BAA0B,SAAyB;AACjE,QAAM,UAAU,QAAQ,KAAK;AAE7B,QAAM,cAAc,QAAQ,MAAM,uCAAuC;AACzE,MAAI,aAAa;AACf,UAAM,CAAC,EAAE,OAAO,OAAO,MAAM,EAAE,IAAI;AACnC,WAAO,GAAG,KAAK,IAAI,KAAK,GAAG,GAAG;AAAA,EAChC;AAEA,MAAI,+BAA+B,KAAK,OAAO,EAAG,QAAO;AAGzD,SAAO;AACT;","names":[]}
|
package/dist/lib/version.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const LIBRARY_VERSION = "13.0.0-rc.
|
|
1
|
+
const LIBRARY_VERSION = "13.0.0-rc.13";
|
|
2
2
|
const ADCP_VERSION = "3.1.11";
|
|
3
3
|
const ADCP_MAJOR_VERSION = 3;
|
|
4
4
|
const COMPATIBLE_ADCP_VERSIONS = [
|
|
@@ -39,10 +39,10 @@ const COMPATIBLE_ADCP_VERSIONS = [
|
|
|
39
39
|
"3.1.11"
|
|
40
40
|
];
|
|
41
41
|
const VERSION_INFO = {
|
|
42
|
-
library: "13.0.0-rc.
|
|
42
|
+
library: "13.0.0-rc.13",
|
|
43
43
|
adcp: "3.1.11",
|
|
44
44
|
compatibleVersions: COMPATIBLE_ADCP_VERSIONS,
|
|
45
|
-
generatedAt: "2026-08-
|
|
45
|
+
generatedAt: "2026-08-10T05:35:45.573Z"
|
|
46
46
|
};
|
|
47
47
|
function getAdcpVersion() {
|
|
48
48
|
return ADCP_VERSION;
|
package/dist/lib/version.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/lib/version.ts"],"sourcesContent":["// Generated version information\n// This file is auto-generated by sync-version.ts\n\n/**\n * AdCP SDK library version\n */\nexport const LIBRARY_VERSION = '13.0.0-rc.
|
|
1
|
+
{"version":3,"sources":["../../src/lib/version.ts"],"sourcesContent":["// Generated version information\n// This file is auto-generated by sync-version.ts\n\n/**\n * AdCP SDK library version\n */\nexport const LIBRARY_VERSION = '13.0.0-rc.13';\n\n/**\n * AdCP specification version this library is built for\n */\nexport const ADCP_VERSION = '3.1.11';\n\n/**\n * AdCP major version sent with every request (adcp_major_version field).\n * Sellers validate this against their supported versions and return\n * VERSION_UNSUPPORTED if the version is not in range.\n */\nexport const ADCP_MAJOR_VERSION = 3;\n\n/**\n * AdCP versions this library maintains backward compatibility with.\n *\n * Auto-derived from `ADCP_VERSION` by scripts/sync-version.ts. Do not edit\n * this list by hand; bumping the AdCP pin via `npm run sync-version`\n * extends it.\n */\nexport const COMPATIBLE_ADCP_VERSIONS = [\n 'v2.5',\n 'v2.6',\n 'v3',\n '3.0.0-beta.1',\n '3.0-beta.1',\n '3.0-beta',\n '3.0.0-beta.3',\n '3.0-beta.3',\n '3.0.0',\n '3.0',\n '3.0.1',\n '3.0.2',\n '3.0.3',\n '3.0.4',\n '3.0.5',\n '3.0.6',\n '3.0.7',\n '3.0.8',\n '3.0.9',\n '3.0.10',\n '3.0.11',\n '3.0.12',\n '3.1.0',\n '3.1',\n '3.1.1',\n '3.1.2',\n '3.1.3',\n '3.1.4',\n '3.1.5',\n '3.1.6',\n '3.1.7',\n '3.1.8',\n '3.1.9',\n '3.1.10',\n '3.1.11',\n] as const;\n\n/**\n * String literal union of every AdCP version the SDK formally supports.\n *\n * Used by the per-instance `adcpVersion` constructor option to give callers\n * autocomplete in editors. The intersection with `(string & {})` in the\n * config type preserves the escape hatch — any string is still accepted at\n * the type level — while the literal union surfaces canonical values first.\n */\nexport type AdcpVersion = (typeof COMPATIBLE_ADCP_VERSIONS)[number];\n\n/**\n * Full version information\n */\nexport const VERSION_INFO = {\n library: '13.0.0-rc.13',\n adcp: '3.1.11',\n compatibleVersions: COMPATIBLE_ADCP_VERSIONS,\n generatedAt: '2026-08-10T05:35:45.573Z',\n} as const;\n\n/**\n * Get the AdCP specification version this library is built for\n */\nexport function getAdcpVersion(): string {\n return ADCP_VERSION;\n}\n\n/**\n * Get the library version\n */\nexport function getLibraryVersion(): string {\n return LIBRARY_VERSION;\n}\n\n/**\n * Check if this library version is compatible with a given AdCP version\n */\nexport function isCompatibleWith(adcpVersion: string): boolean {\n return (COMPATIBLE_ADCP_VERSIONS as readonly string[]).includes(adcpVersion);\n}\n\n/**\n * Get all AdCP versions this library is compatible with\n */\nexport function getCompatibleVersions(): readonly string[] {\n return COMPATIBLE_ADCP_VERSIONS;\n}\n\n/**\n * Extract the major version number from an AdCP version string.\n *\n * Accepts:\n * - Semver: '3.0.0', '3.0.1', '3.1.0-beta.1' → 3\n * - Legacy aliases: 'v3' → 3, 'v2.5' / 'v2.6' → 2\n *\n * Returns NaN for unrecognized strings — callers should validate before passing.\n */\nexport function parseAdcpMajorVersion(version: string): number {\n const trimmed = version.trim();\n const semverLike = trimmed.startsWith('v') ? trimmed.slice(1) : trimmed;\n const major = parseInt(semverLike.split('.')[0] ?? '', 10);\n return Number.isFinite(major) ? major : NaN;\n}\n\n/**\n * Normalize a full-semver AdCP version (`MAJOR.MINOR.PATCH[-prerelease]`) to\n * the release-precision form that AdCP 3.1+ requires on the wire:\n * `MAJOR.MINOR[-prerelease]` — the patch digit is dropped.\n *\n * Per the spec note on `adcp_version`: \"SDKs that read full-semver values\n * from bundle metadata (e.g. `ComplianceIndex.published_version =\n * \"3.1.0-beta.1\"`) MUST normalize to release-precision (`\"3.1-beta.1\"`)\n * before emitting on the wire — meta-field values are NOT valid wire\n * values.\" The wire regex (`^\\d+\\.\\d+(-[a-zA-Z0-9.-]+)?$`) rejects strings\n * with a patch digit.\n *\n * Behavior:\n * - `\"3.1.0-beta.7\"` → `\"3.1-beta.7\"`\n * - `\"3.1.0\"` → `\"3.1\"`\n * - `\"3.0.12\"` → `\"3.0\"`\n * - Already-release-precision input (`\"3.1\"`, `\"3.1-beta.7\"`) passes through\n * - Legacy aliases (`\"v2.5\"`, `\"v3\"`) pass through unchanged — the wire\n * regex doesn't accept them anyway; the v2.5 path uses\n * `adcp_major_version` instead of `adcp_version` for transport.\n * - Unrecognized strings pass through unchanged so callers can detect drift\n * via the wire validator rather than have it masked by this helper.\n */\nexport function toReleasePrecisionVersion(version: string): string {\n const trimmed = version.trim();\n // Pre-release form `MAJOR.MINOR.PATCH-prerelease` → `MAJOR.MINOR-prerelease`\n const semverMatch = trimmed.match(/^(\\d+)\\.(\\d+)\\.\\d+(-[A-Za-z0-9.-]+)?$/);\n if (semverMatch) {\n const [, major, minor, pre = ''] = semverMatch;\n return `${major}.${minor}${pre}`;\n }\n // Already release-precision (no patch digit). Includes `3.1`, `3.1-beta.7`.\n if (/^\\d+\\.\\d+(-[A-Za-z0-9.-]+)?$/.test(trimmed)) return trimmed;\n // Legacy aliases (`v3`, `v2.5`, `v2.6`) and anything we don't recognize —\n // pass through so the wire validator can flag genuine drift.\n return version;\n}\n"],"mappings":"AAMO,MAAM,kBAAkB;AAKxB,MAAM,eAAe;AAOrB,MAAM,qBAAqB;AAS3B,MAAM,2BAA2B;AAAA,EACtC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAeO,MAAM,eAAe;AAAA,EAC1B,SAAS;AAAA,EACT,MAAM;AAAA,EACN,oBAAoB;AAAA,EACpB,aAAa;AACf;AAKO,SAAS,iBAAyB;AACvC,SAAO;AACT;AAKO,SAAS,oBAA4B;AAC1C,SAAO;AACT;AAKO,SAAS,iBAAiB,aAA8B;AAC7D,SAAQ,yBAA+C,SAAS,WAAW;AAC7E;AAKO,SAAS,wBAA2C;AACzD,SAAO;AACT;AAWO,SAAS,sBAAsB,SAAyB;AAC7D,QAAM,UAAU,QAAQ,KAAK;AAC7B,QAAM,aAAa,QAAQ,WAAW,GAAG,IAAI,QAAQ,MAAM,CAAC,IAAI;AAChE,QAAM,QAAQ,SAAS,WAAW,MAAM,GAAG,EAAE,CAAC,KAAK,IAAI,EAAE;AACzD,SAAO,OAAO,SAAS,KAAK,IAAI,QAAQ;AAC1C;AAyBO,SAAS,0BAA0B,SAAyB;AACjE,QAAM,UAAU,QAAQ,KAAK;AAE7B,QAAM,cAAc,QAAQ,MAAM,uCAAuC;AACzE,MAAI,aAAa;AACf,UAAM,CAAC,EAAE,OAAO,OAAO,MAAM,EAAE,IAAI;AACnC,WAAO,GAAG,KAAK,IAAI,KAAK,GAAG,GAAG;AAAA,EAChC;AAEA,MAAI,+BAA+B,KAAK,OAAO,EAAG,QAAO;AAGzD,SAAO;AACT;","names":[]}
|
|
@@ -279,7 +279,61 @@ const legacyFormatConverter = ({ formatId }) => ({
|
|
|
279
279
|
});
|
|
280
280
|
```
|
|
281
281
|
|
|
282
|
-
Configure this once as `legacyFormatConverter` on the client to cover canonical discovery
|
|
282
|
+
Configure this once as `legacyFormatConverter` on the client to cover canonical discovery and write paths, async continuations, and webhooks. A per-call converter overrides the configured default; `syncCreatives()` gives its projection-specific converter highest precedence. The explicit `createMediaBuyLegacy()`, `updateMediaBuyLegacy()`, and `syncCreativesLegacy()` methods are raw migration escapes: they preserve legacy wire payloads without creative capability probing or projection, so converter and resolver options are ignored. An invalid conversion is rejected before adopter code receives a partially converted object. On discovery, partially mappable products remain with their mapped canonical options and sanitized `FORMAT_PROJECTION_FAILED` entries in `data.errors`. A product with no canonical option is omitted from the canonical product list because the protocol requires `format_options` to contain at least one declaration; its sanitized non-fatal error remains in `data.errors`. A valid legacy `format_ids: []` product uses `CANONICAL_PRODUCT_FORMATS_UNAVAILABLE` with `reason: 'legacy_format_list_empty'`, not `FORMAT_PROJECTION_FAILED`. `data.projection.diagnostics` mirrors SDK-local detail for convenience, but `errors[]` is the portable, multi-hop surface. Use `getProductsLegacy()` when migration tooling needs the original refs or the complete legacy product list. For ordinary downgrade, use `packageRefsForFormatOptions()` on a product with mapped format options. During one client lifetime the SDK retains the corresponding legacy ref in bounded private metadata. For a process boundary, persist the projector's `legacyRoutes` sidecar and rebuild the resolver with `canonicalFormatLegacyResolverFromRoutes()`, configure `projectionAdaptersFromCatalogSnapshots`, or supply a custom `canonicalFormatLegacyResolver`; canonical data deliberately cannot reconstruct an arbitrary seller owner by itself.
|
|
283
|
+
|
|
284
|
+
The route sidecar is JSON-safe and includes the full owner, width, height, and
|
|
285
|
+
duration tuple. It does not add legacy identity to canonical protocol objects:
|
|
286
|
+
|
|
287
|
+
```ts
|
|
288
|
+
import {
|
|
289
|
+
canonicalFormatLegacyResolverFromRoutes,
|
|
290
|
+
projectV1ProductToV2,
|
|
291
|
+
} from '@adcp/sdk/v2/projection';
|
|
292
|
+
|
|
293
|
+
const { v2: product, legacyRoutes } = projectV1ProductToV2(legacyProduct, {
|
|
294
|
+
legacyFormatConverter,
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
await products.put(product.product_id, product);
|
|
298
|
+
await creativeRoutes.put(product.product_id, legacyRoutes);
|
|
299
|
+
|
|
300
|
+
const restoredRoutes = await creativeRoutes.get(product.product_id);
|
|
301
|
+
const canonicalFormatLegacyResolver =
|
|
302
|
+
canonicalFormatLegacyResolverFromRoutes(restoredRoutes);
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
Each `CanonicalFormatLegacyRoute` contains `product_id`, the stable
|
|
306
|
+
`format_option_ref`, and one or more exact `format_ids`. Pass the rebuilt
|
|
307
|
+
resolver to `AgentClient` or `createAdcpServerFromPlatform()` without changing
|
|
308
|
+
the existing resolver callback contract.
|
|
309
|
+
|
|
310
|
+
Compatibility consumers that must retain top-level `format_ids[]` while
|
|
311
|
+
publishing URL-free canonical declarations can use the additive projection:
|
|
312
|
+
|
|
313
|
+
```ts
|
|
314
|
+
import {
|
|
315
|
+
canonicalFormatLegacyResolverFromRoutes,
|
|
316
|
+
toAdditiveCanonicalProduct,
|
|
317
|
+
} from '@adcp/sdk/v2/projection';
|
|
318
|
+
|
|
319
|
+
const { product, diagnostics, routes } =
|
|
320
|
+
toAdditiveCanonicalProduct(sourceProduct, { legacyFormatConverter });
|
|
321
|
+
|
|
322
|
+
// product.format_ids is preserved, but product.format_options never exposes
|
|
323
|
+
// v1_format_ref. Extract routes before any JSON round-trip — WeakMap metadata
|
|
324
|
+
// is gone after deserialization. Persist the sidecar before crossing a process
|
|
325
|
+
// boundary.
|
|
326
|
+
await products.put(product.product_id, product);
|
|
327
|
+
await creativeRoutes.put(product.product_id, routes);
|
|
328
|
+
|
|
329
|
+
const resolver = canonicalFormatLegacyResolverFromRoutes(
|
|
330
|
+
await creativeRoutes.get(product.product_id)
|
|
331
|
+
);
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
Source-authored canonical option ids, kinds, and params are preserved exactly;
|
|
335
|
+
only the legacy compatibility hint moves to `routes`. Legacy-only input
|
|
336
|
+
uses the same declaration and route contract after projection.
|
|
283
337
|
|
|
284
338
|
For a fixed set of temporary seller adapters, prefer one declarative catalog over
|
|
285
339
|
separate forward and reverse callbacks. `projectionAdaptersFromCatalogSnapshots`
|
|
@@ -356,7 +410,7 @@ Server platforms use the same resolver as `createAdcpServerFromPlatform(platform
|
|
|
356
410
|
## Protocol-version behavior
|
|
357
411
|
|
|
358
412
|
- AdCP 3.0 is a legacy creative wire. The SDK upgrades inbound data and downgrades canonical requests when an unambiguous mapping exists.
|
|
359
|
-
- AdCP 3.1 is not proof of canonical support
|
|
413
|
+
- AdCP 3.1 is the dual-emission transition release. On an otherwise ambiguous `get_products` request, the decisioning server preserves both `format_ids` and `format_options` when an exact legacy route is available; it does not invent a legacy identity for a canonical-only declaration. Explicit canonical or legacy request evidence still narrows the response. For peer negotiation, 3.1 alone is not proof of canonical write support: the SDK uses `media_buy.features.canonical_creatives`, then per-tool schema evidence, and fails closed when no safe mapping exists.
|
|
360
414
|
- AdCP 3.2 and later are canonical by contract. Advertising `canonical_creatives: false` at 3.2 is a capability error.
|
|
361
415
|
|
|
362
416
|
Negotiation uses the mutually supported wire release, so a 3.2 SDK talking to a 3.1-only seller follows the 3.1 rules. See [Canonical creative delivery](guides/CREATIVE-DELIVERY.md) for conversion diagnostics and server integration.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adcp/sdk",
|
|
3
|
-
"version": "13.0.0-rc.
|
|
3
|
+
"version": "13.0.0-rc.13",
|
|
4
4
|
"description": "AdCP SDK — client, server, and compliance harnesses for the AdContext Protocol (MCP + A2A)",
|
|
5
5
|
"workspaces": [
|
|
6
6
|
".",
|
|
@@ -579,6 +579,7 @@
|
|
|
579
579
|
"fast-check": "^3.23.2",
|
|
580
580
|
"jose": "^6.2.2",
|
|
581
581
|
"secure-json-parse": "^4.1.0",
|
|
582
|
+
"semver": "^7.8.5",
|
|
582
583
|
"structured-headers": "2.0.2",
|
|
583
584
|
"tldts": "^7.0.29",
|
|
584
585
|
"undici": "^6.27.0",
|
|
@@ -615,6 +616,7 @@
|
|
|
615
616
|
"@types/express": "4.17.25",
|
|
616
617
|
"@types/node": "^20.19.39",
|
|
617
618
|
"@types/pg": "^8.20.0",
|
|
619
|
+
"@types/semver": "^7.8.0",
|
|
618
620
|
"@types/tar": "^6.1.13",
|
|
619
621
|
"actionlint": "^2.0.6",
|
|
620
622
|
"esbuild": "^0.28.1",
|