@adcp/sdk 14.0.0-beta.8 → 14.0.0-beta.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (150) hide show
  1. package/dist/lib/core/AgentClient.d.ts.map +1 -1
  2. package/dist/lib/core/ConversationTypes.d.mts +8 -0
  3. package/dist/lib/core/ConversationTypes.d.ts +8 -0
  4. package/dist/lib/core/ConversationTypes.d.ts.map +1 -1
  5. package/dist/lib/core/ConversationTypes.js.map +1 -1
  6. package/dist/lib/core/SingleAgentClient.d.ts.map +1 -1
  7. package/dist/lib/core/SingleAgentClient.js +30 -3
  8. package/dist/lib/core/SingleAgentClient.js.map +1 -1
  9. package/dist/lib/core/SingleAgentClient.mjs +30 -3
  10. package/dist/lib/core/SingleAgentClient.mjs.map +1 -1
  11. package/dist/lib/core/TaskExecutor.d.ts.map +1 -1
  12. package/dist/lib/core/TaskExecutor.js +32 -5
  13. package/dist/lib/core/TaskExecutor.js.map +1 -1
  14. package/dist/lib/core/TaskExecutor.mjs +32 -5
  15. package/dist/lib/core/TaskExecutor.mjs.map +1 -1
  16. package/dist/lib/index.d.mts +2 -2
  17. package/dist/lib/index.d.ts +2 -2
  18. package/dist/lib/index.d.ts.map +1 -1
  19. package/dist/lib/index.js +2 -0
  20. package/dist/lib/index.js.map +1 -1
  21. package/dist/lib/index.mjs +2 -1
  22. package/dist/lib/index.mjs.map +1 -1
  23. package/dist/lib/schemas/index.d.mts +14 -0
  24. package/dist/lib/schemas/index.d.ts +14 -0
  25. package/dist/lib/schemas/index.d.ts.map +1 -1
  26. package/dist/lib/schemas/index.js +13 -1
  27. package/dist/lib/schemas/index.js.map +1 -1
  28. package/dist/lib/schemas/index.mjs +13 -1
  29. package/dist/lib/schemas/index.mjs.map +1 -1
  30. package/dist/lib/schemas-data/v2.5/_provenance.json +1 -1
  31. package/dist/lib/server/adcp-server.d.mts +1 -0
  32. package/dist/lib/server/adcp-server.d.ts +1 -0
  33. package/dist/lib/server/adcp-server.d.ts.map +1 -1
  34. package/dist/lib/server/adcp-server.js +1 -1
  35. package/dist/lib/server/adcp-server.js.map +1 -1
  36. package/dist/lib/server/adcp-server.mjs +1 -1
  37. package/dist/lib/server/adcp-server.mjs.map +1 -1
  38. package/dist/lib/server/create-adcp-server.d.mts +10 -2
  39. package/dist/lib/server/create-adcp-server.d.ts +10 -2
  40. package/dist/lib/server/create-adcp-server.d.ts.map +1 -1
  41. package/dist/lib/server/create-adcp-server.js +26 -19
  42. package/dist/lib/server/create-adcp-server.js.map +1 -1
  43. package/dist/lib/server/create-adcp-server.mjs +26 -26
  44. package/dist/lib/server/create-adcp-server.mjs.map +1 -1
  45. package/dist/lib/server/decisioning/capabilities.d.mts +15 -1
  46. package/dist/lib/server/decisioning/capabilities.d.ts +15 -1
  47. package/dist/lib/server/decisioning/capabilities.d.ts.map +1 -1
  48. package/dist/lib/server/decisioning/capabilities.js.map +1 -1
  49. package/dist/lib/server/decisioning/capabilities.mjs.map +1 -1
  50. package/dist/lib/server/decisioning/index.d.mts +2 -2
  51. package/dist/lib/server/decisioning/index.d.ts +2 -2
  52. package/dist/lib/server/decisioning/index.d.ts.map +1 -1
  53. package/dist/lib/server/decisioning/index.js +2 -0
  54. package/dist/lib/server/decisioning/index.js.map +1 -1
  55. package/dist/lib/server/decisioning/index.mjs +2 -0
  56. package/dist/lib/server/decisioning/index.mjs.map +1 -1
  57. package/dist/lib/server/decisioning/proposal/types.d.mts +3 -2
  58. package/dist/lib/server/decisioning/proposal/types.d.ts +3 -2
  59. package/dist/lib/server/decisioning/proposal/types.d.ts.map +1 -1
  60. package/dist/lib/server/decisioning/proposal/types.js.map +1 -1
  61. package/dist/lib/server/decisioning/proposal/types.mjs.map +1 -1
  62. package/dist/lib/server/decisioning/runtime/from-platform.d.mts +14 -1
  63. package/dist/lib/server/decisioning/runtime/from-platform.d.ts +14 -1
  64. package/dist/lib/server/decisioning/runtime/from-platform.d.ts.map +1 -1
  65. package/dist/lib/server/decisioning/runtime/from-platform.js +402 -9
  66. package/dist/lib/server/decisioning/runtime/from-platform.js.map +1 -1
  67. package/dist/lib/server/decisioning/runtime/from-platform.mjs +405 -9
  68. package/dist/lib/server/decisioning/runtime/from-platform.mjs.map +1 -1
  69. package/dist/lib/server/decisioning/runtime/validate-platform.d.mts +1 -1
  70. package/dist/lib/server/decisioning/runtime/validate-platform.d.ts +1 -1
  71. package/dist/lib/server/decisioning/runtime/validate-platform.d.ts.map +1 -1
  72. package/dist/lib/server/decisioning/runtime/validate-platform.js +2 -2
  73. package/dist/lib/server/decisioning/runtime/validate-platform.js.map +1 -1
  74. package/dist/lib/server/decisioning/runtime/validate-platform.mjs +2 -2
  75. package/dist/lib/server/decisioning/runtime/validate-platform.mjs.map +1 -1
  76. package/dist/lib/server/decisioning/specialisms/sales.d.mts +7 -1
  77. package/dist/lib/server/decisioning/specialisms/sales.d.ts +7 -1
  78. package/dist/lib/server/decisioning/specialisms/sales.d.ts.map +1 -1
  79. package/dist/lib/server/decisioning/specialisms/sales.js.map +1 -1
  80. package/dist/lib/storage/interfaces.d.mts +2 -0
  81. package/dist/lib/storage/interfaces.d.ts +2 -0
  82. package/dist/lib/storage/interfaces.d.ts.map +1 -1
  83. package/dist/lib/storage/interfaces.js.map +1 -1
  84. package/dist/lib/testing/compliance/comply.d.mts +18 -0
  85. package/dist/lib/testing/compliance/comply.d.ts +18 -0
  86. package/dist/lib/testing/compliance/comply.d.ts.map +1 -1
  87. package/dist/lib/testing/compliance/comply.js +30 -5
  88. package/dist/lib/testing/compliance/comply.js.map +1 -1
  89. package/dist/lib/testing/compliance/comply.mjs +28 -4
  90. package/dist/lib/testing/compliance/comply.mjs.map +1 -1
  91. package/dist/lib/testing/compliance/index.d.mts +1 -1
  92. package/dist/lib/testing/compliance/index.d.ts +1 -1
  93. package/dist/lib/testing/compliance/index.d.ts.map +1 -1
  94. package/dist/lib/testing/compliance/index.js +4 -2
  95. package/dist/lib/testing/compliance/index.js.map +1 -1
  96. package/dist/lib/testing/compliance/index.mjs +4 -2
  97. package/dist/lib/testing/compliance/index.mjs.map +1 -1
  98. package/dist/lib/testing/storyboard/validations.d.mts +1 -1
  99. package/dist/lib/testing/storyboard/validations.d.ts +1 -1
  100. package/dist/lib/types/list-creatives.d.ts +129 -6
  101. package/dist/lib/types/schemas.generated.d.ts +411 -16
  102. package/dist/lib/types/schemas.generated.d.ts.map +1 -1
  103. package/dist/lib/types/schemas.generated.js +22 -6
  104. package/dist/lib/types/schemas.generated.js.map +1 -1
  105. package/dist/lib/types/schemas.generated.mjs +22 -6
  106. package/dist/lib/types/schemas.generated.mjs.map +1 -1
  107. package/dist/lib/types/tools.generated.d.mts +80 -6
  108. package/dist/lib/types/tools.generated.d.ts +80 -6
  109. package/dist/lib/types/tools.generated.d.ts.map +1 -1
  110. package/dist/lib/types/tools.generated.js.map +1 -1
  111. package/dist/lib/utils/adcp-version-config.d.mts +12 -0
  112. package/dist/lib/utils/adcp-version-config.d.ts +12 -0
  113. package/dist/lib/utils/adcp-version-config.d.ts.map +1 -1
  114. package/dist/lib/utils/adcp-version-config.js +5 -3
  115. package/dist/lib/utils/adcp-version-config.js.map +1 -1
  116. package/dist/lib/utils/adcp-version-config.mjs +4 -3
  117. package/dist/lib/utils/adcp-version-config.mjs.map +1 -1
  118. package/dist/lib/utils/pricing-adapter.d.mts +2 -0
  119. package/dist/lib/utils/pricing-adapter.d.ts +2 -0
  120. package/dist/lib/utils/pricing-adapter.d.ts.map +1 -1
  121. package/dist/lib/utils/pricing-adapter.js +68 -1
  122. package/dist/lib/utils/pricing-adapter.js.map +1 -1
  123. package/dist/lib/utils/pricing-adapter.mjs +67 -1
  124. package/dist/lib/utils/pricing-adapter.mjs.map +1 -1
  125. package/dist/lib/v2/projection/index.d.mts +1 -1
  126. package/dist/lib/v2/projection/index.d.ts +1 -1
  127. package/dist/lib/v2/projection/index.d.ts.map +1 -1
  128. package/dist/lib/v2/projection/index.js.map +1 -1
  129. package/dist/lib/v2/projection/index.mjs.map +1 -1
  130. package/dist/lib/v2/projection/v1-to-v2.d.mts +17 -3
  131. package/dist/lib/v2/projection/v1-to-v2.d.ts +17 -3
  132. package/dist/lib/v2/projection/v1-to-v2.d.ts.map +1 -1
  133. package/dist/lib/v2/projection/v1-to-v2.js.map +1 -1
  134. package/dist/lib/v2/projection/v1-to-v2.mjs.map +1 -1
  135. package/dist/lib/validation/schema-loader.d.mts +17 -0
  136. package/dist/lib/validation/schema-loader.d.ts +17 -0
  137. package/dist/lib/validation/schema-loader.d.ts.map +1 -1
  138. package/dist/lib/validation/schema-loader.js +24 -0
  139. package/dist/lib/validation/schema-loader.js.map +1 -1
  140. package/dist/lib/validation/schema-loader.mjs +23 -0
  141. package/dist/lib/validation/schema-loader.mjs.map +1 -1
  142. package/dist/lib/version.d.mts +3 -3
  143. package/dist/lib/version.d.ts +3 -3
  144. package/dist/lib/version.js +3 -3
  145. package/dist/lib/version.js.map +1 -1
  146. package/dist/lib/version.mjs +3 -3
  147. package/dist/lib/version.mjs.map +1 -1
  148. package/docs/llms.txt +2 -2
  149. package/docs/migration-13-to-14.md +59 -0
  150. package/package.json +1 -1
@@ -51,3 +51,15 @@ export declare function shouldOmit31Fields(resolvedClientVersion: string | undef
51
51
  * Returns the original reference unchanged when `brand_kit_override` is absent.
52
52
  */
53
53
  export declare function omit31BrandFields<T>(brand: T): T;
54
+ /**
55
+ * Filter `COMPATIBLE_ADCP_VERSIONS` to entries with a shipped bundle on this
56
+ * SDK build. The compatibility list is the historical set of accepted version
57
+ * strings (legacy aliases, superseded betas, current GA), but only a subset
58
+ * has bundles in `dist/lib/schemas-data/` at any given build. Returning
59
+ * "currently bundled: v2.5, v2.6, v3, 3.0.0-beta.3, 3.0.0, 3.0.1" would
60
+ * mislead a caller who pinned `'v2.5'` and got "no bundle".
61
+ *
62
+ * Always includes `ADCP_VERSION` even when its bundle hasn't been built yet
63
+ * (dev-tree case) so the error remains actionable on first build.
64
+ */
65
+ export declare function listBundledAdcpVersions(): string[];
@@ -51,4 +51,16 @@ export declare function shouldOmit31Fields(resolvedClientVersion: string | undef
51
51
  * Returns the original reference unchanged when `brand_kit_override` is absent.
52
52
  */
53
53
  export declare function omit31BrandFields<T>(brand: T): T;
54
+ /**
55
+ * Filter `COMPATIBLE_ADCP_VERSIONS` to entries with a shipped bundle on this
56
+ * SDK build. The compatibility list is the historical set of accepted version
57
+ * strings (legacy aliases, superseded betas, current GA), but only a subset
58
+ * has bundles in `dist/lib/schemas-data/` at any given build. Returning
59
+ * "currently bundled: v2.5, v2.6, v3, 3.0.0-beta.3, 3.0.0, 3.0.1" would
60
+ * mislead a caller who pinned `'v2.5'` and got "no bundle".
61
+ *
62
+ * Always includes `ADCP_VERSION` even when its bundle hasn't been built yet
63
+ * (dev-tree case) so the error remains actionable on first build.
64
+ */
65
+ export declare function listBundledAdcpVersions(): string[];
54
66
  //# sourceMappingURL=adcp-version-config.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"adcp-version-config.d.ts","sourceRoot":"","sources":["../../../src/lib/utils/adcp-version-config.ts"],"names":[],"mappings":"AA0BA,yEAAyE;AACzE,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAK1F;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,kBAAkB,CAAC,WAAW,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CA4C1E;AAED,uFAAuF;AACvF,wBAAgB,iCAAiC,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAG1E;AAED,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,EAAE,kBAAkB,UAAO,GAAG,GAAG,CAAC,MAAM,CAAC,CAqB1F;AAED,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,MAAM,EAAE,iBAAiB,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CASrG;AAED,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAgBvE;AAED,0FAA0F;AAC1F,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAgBvE;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAChC,IAAI,EAAE;IAAE,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAC;IAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,GAAG,SAAS,GACxG,OAAO,CAST;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAChC,qBAAqB,EAAE,MAAM,GAAG,SAAS,EACzC,IAAI,EAAE;IAAE,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAC;IAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,GAAG,SAAS,GACxG,OAAO,CAGT;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG,CAAC,CAMhD"}
1
+ {"version":3,"file":"adcp-version-config.d.ts","sourceRoot":"","sources":["../../../src/lib/utils/adcp-version-config.ts"],"names":[],"mappings":"AA0BA,yEAAyE;AACzE,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAK1F;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,kBAAkB,CAAC,WAAW,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CA4C1E;AAED,uFAAuF;AACvF,wBAAgB,iCAAiC,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAG1E;AAED,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,EAAE,kBAAkB,UAAO,GAAG,GAAG,CAAC,MAAM,CAAC,CAqB1F;AAED,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,MAAM,EAAE,iBAAiB,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CASrG;AAED,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAgBvE;AAED,0FAA0F;AAC1F,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAgBvE;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAChC,IAAI,EAAE;IAAE,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAC;IAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,GAAG,SAAS,GACxG,OAAO,CAST;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAChC,qBAAqB,EAAE,MAAM,GAAG,SAAS,EACzC,IAAI,EAAE;IAAE,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAC;IAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,GAAG,SAAS,GACxG,OAAO,CAGT;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG,CAAC,CAMhD;AAOD;;;;;;;;;;GAUG;AACH,wBAAgB,uBAAuB,IAAI,MAAM,EAAE,CAMlD"}
@@ -24,6 +24,7 @@ __export(adcp_version_config_exports, {
24
24
  isMovingAdcpPrereleaseFamilyAlias: () => isMovingAdcpPrereleaseFamilyAlias,
25
25
  isPre31AdcpVersion: () => isPre31AdcpVersion,
26
26
  isPre32AdcpVersion: () => isPre32AdcpVersion,
27
+ listBundledAdcpVersions: () => listBundledAdcpVersions,
27
28
  omit31BrandFields: () => omit31BrandFields,
28
29
  resolveAdcpVersion: () => resolveAdcpVersion,
29
30
  sellerAdvertises31: () => sellerAdvertises31,
@@ -58,7 +59,7 @@ function resolveAdcpVersion(adcpVersion) {
58
59
  const major = (0, import_version.parseAdcpMajorVersion)(adcpVersion);
59
60
  if (!Number.isFinite(major)) {
60
61
  throw new import_errors.ConfigurationError(
61
- `adcpVersion ${JSON.stringify(adcpVersion)} is not a valid AdCP version. Expected a semver string (e.g. '3.0.1', '3.1.0-beta.1') or a legacy alias. Currently bundled: ${listBundledVersions().join(", ")}.`,
62
+ `adcpVersion ${JSON.stringify(adcpVersion)} is not a valid AdCP version. Expected a semver string (e.g. '3.0.1', '3.1.0-beta.1') or a legacy alias. Currently bundled: ${listBundledAdcpVersions().join(", ")}.`,
62
63
  "adcpVersion"
63
64
  );
64
65
  }
@@ -66,7 +67,7 @@ function resolveAdcpVersion(adcpVersion) {
66
67
  if (!(0, import_schema_loader.hasSchemaBundle)(adcpVersion)) {
67
68
  const resolvedKey = (0, import_schema_loader.resolveBundleKey)(adcpVersion);
68
69
  throw new import_errors.ConfigurationError(
69
- `adcpVersion ${JSON.stringify(adcpVersion)} resolves to bundle key "${resolvedKey}", but no schema bundle for that key ships with this SDK build. Currently bundled: ${listBundledVersions().join(", ")}. If you're testing against a beta that the spec repo has tagged but the SDK hasn't synced yet, run \`npm run sync-schemas\` and \`npm run build:lib\` to populate the cache, then re-construct.`,
70
+ `adcpVersion ${JSON.stringify(adcpVersion)} resolves to bundle key "${resolvedKey}", but no schema bundle for that key ships with this SDK build. Currently bundled: ${listBundledAdcpVersions().join(", ")}. If you're testing against a beta that the spec repo has tagged but the SDK hasn't synced yet, run \`npm run sync-schemas\` and \`npm run build:lib\` to populate the cache, then re-construct.`,
70
71
  "adcpVersion"
71
72
  );
72
73
  }
@@ -158,7 +159,7 @@ function prereleaseFamilyAlias(version) {
158
159
  const match = /^(\d+\.\d+-[0-9A-Za-z-]+)\.\d+$/.exec(version);
159
160
  return match?.[1];
160
161
  }
161
- function listBundledVersions() {
162
+ function listBundledAdcpVersions() {
162
163
  const bundled = import_version.COMPATIBLE_ADCP_VERSIONS.filter((v) => (0, import_schema_loader.hasSchemaBundle)(v));
163
164
  if (!bundled.includes(import_version.ADCP_VERSION)) {
164
165
  return [import_version.ADCP_VERSION, ...bundled];
@@ -173,6 +174,7 @@ function listBundledVersions() {
173
174
  isMovingAdcpPrereleaseFamilyAlias,
174
175
  isPre31AdcpVersion,
175
176
  isPre32AdcpVersion,
177
+ listBundledAdcpVersions,
176
178
  omit31BrandFields,
177
179
  resolveAdcpVersion,
178
180
  sellerAdvertises31,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/lib/utils/adcp-version-config.ts"],"sourcesContent":["// Validation helpers for the per-instance `adcpVersion` constructor option.\n//\n// Stage 2 plumbed the option onto every client/server constructor with a\n// fence: cross-major pins were rejected at construction because the wire\n// field and validators still keyed off the global `ADCP_MAJOR_VERSION`.\n//\n// Stage 3 lifts that fence. Validators now key off the resolved bundle in\n// `dist/lib/schemas-data/<MAJOR.MINOR>/`, and the protocol layer derives\n// the wire-level `adcp_major_version` from the per-instance pin. Construction\n// now accepts any pin that has a schema bundle reachable from the SDK build,\n// and rejects pins for which no bundle is present (`'4.0.0'` while the SDK\n// only ships major-3 schemas, mistyped versions, etc.) with a clear pointer\n// at `sync-schemas` + `build:lib`.\n\nimport { ADCP_VERSION, COMPATIBLE_ADCP_VERSIONS, parseAdcpMajorVersion } from '../version';\nimport { ConfigurationError } from '../errors';\nimport { hasSchemaBundle, resolveBundleKey, toReleasePrecisionWire } from '../validation/schema-loader';\nimport { gte as semverGte, valid as validSemver } from 'semver';\n\nfunction comparableAdcpSemver(version: string): string | undefined {\n const match = /^v?(\\d+)\\.(\\d+)(?:\\.(\\d+))?((?:-[0-9A-Za-z.-]+)?(?:\\+[0-9A-Za-z.-]+)?)$/.exec(version.trim());\n if (!match) return undefined;\n const normalized = `${match[1]}.${match[2]}.${match[3] ?? '0'}${match[4] ?? ''}`;\n return validSemver(normalized) ?? undefined;\n}\n\n/** Compare full-semver and release-precision AdCP identifiers safely. */\nexport function isAdcpVersionAtLeast(version: string | undefined, minimum: string): boolean {\n if (version === undefined) return false;\n const comparable = comparableAdcpSemver(version);\n const comparableMinimum = comparableAdcpSemver(minimum);\n return comparable !== undefined && comparableMinimum !== undefined && semverGte(comparable, comparableMinimum);\n}\n\n/**\n * Resolve and validate a configured `adcpVersion`. Returns the value to store\n * on the instance — either the caller's pin or the SDK default.\n *\n * Throws `ConfigurationError` when:\n * - The pin is unparseable as a semver / legacy alias\n * - The pin parses but no schema bundle exists for the resolved key\n *\n * Cross-major pins are accepted as long as the corresponding bundle ships\n * with this SDK build (e.g. `'3.1.0-beta.1'` when `dist/lib/schemas-data/\n * 3.1.0-beta.1/` exists). Pins of the SDK's currently-pinned `ADCP_VERSION`\n * always succeed without an fs check — the bundle is guaranteed.\n */\nexport function resolveAdcpVersion(adcpVersion: string | undefined): string {\n if (adcpVersion === undefined) return ADCP_VERSION;\n\n const currentReleasePrecision = toReleasePrecisionWire(ADCP_VERSION);\n if (isMovingAdcpPrereleaseFamilyAlias(adcpVersion)) {\n throw new ConfigurationError(\n `adcpVersion ${JSON.stringify(adcpVersion)} is a moving prerelease-family alias. ` +\n `Pin the exact 3.2 beta artifact (${JSON.stringify(currentReleasePrecision)}) instead.`,\n 'adcpVersion'\n );\n }\n\n const major = parseAdcpMajorVersion(adcpVersion);\n if (!Number.isFinite(major)) {\n throw new ConfigurationError(\n `adcpVersion ${JSON.stringify(adcpVersion)} is not a valid AdCP version. ` +\n `Expected a semver string (e.g. '3.0.1', '3.1.0-beta.1') or a legacy alias. ` +\n `Currently bundled: ${listBundledVersions().join(', ')}.`,\n 'adcpVersion'\n );\n }\n\n // Skip the bundle-existence check when the pin resolves to the same bundle\n // as the SDK default — every published tarball includes that bundle by\n // construction. The bundle-key compare (rather than literal-string compare)\n // catches the common patterns: `'3.0'`, `'3.0.0'`, and `'3.0.1'` all\n // resolve to the same `'3.0'` bundle when `ADCP_VERSION === '3.0.1'`, so\n // none of those paths pay an fs round-trip.\n if (resolveBundleKey(adcpVersion) === resolveBundleKey(ADCP_VERSION)) return adcpVersion;\n\n if (!hasSchemaBundle(adcpVersion)) {\n const resolvedKey = resolveBundleKey(adcpVersion);\n throw new ConfigurationError(\n `adcpVersion ${JSON.stringify(adcpVersion)} resolves to bundle key \"${resolvedKey}\", ` +\n `but no schema bundle for that key ships with this SDK build. ` +\n `Currently bundled: ${listBundledVersions().join(', ')}. ` +\n `If you're testing against a beta that the spec repo has tagged but the SDK hasn't synced yet, ` +\n `run \\`npm run sync-schemas\\` and \\`npm run build:lib\\` to populate the cache, ` +\n `then re-construct.`,\n 'adcpVersion'\n );\n }\n\n return adcpVersion;\n}\n\n/** Whether a value is the ambiguous moving family alias for the pinned 3.2 preview. */\nexport function isMovingAdcpPrereleaseFamilyAlias(version: string): boolean {\n if (!ADCP_VERSION.startsWith('3.2.0-')) return false;\n return prereleaseFamilyAlias(toReleasePrecisionWire(ADCP_VERSION)) === version;\n}\n\nexport function adcpVersionAliases(version: string, includeFamilyAlias = true): Set<string> {\n const aliases = new Set<string>([version]);\n const add = (value: string) => {\n aliases.add(value);\n if (includeFamilyAlias) {\n const family = prereleaseFamilyAlias(value);\n if (family && !(ADCP_VERSION.startsWith('3.2.0-') && family.startsWith('3.2-'))) aliases.add(family);\n }\n };\n\n try {\n add(resolveBundleKey(version));\n } catch {\n // Keep the raw version only; the caller will report the mismatch.\n }\n try {\n add(toReleasePrecisionWire(version));\n } catch {\n // Keep the raw version only; the caller will report the mismatch.\n }\n return aliases;\n}\n\nexport function isAdcpVersionSupported(version: string, supportedVersions: readonly string[]): boolean {\n if (supportedVersions.length === 0) return true;\n const aliases = adcpVersionAliases(version);\n return supportedVersions.some(v => {\n for (const supportedAlias of adcpVersionAliases(v, false)) {\n if (aliases.has(supportedAlias)) return true;\n }\n return false;\n });\n}\n\nexport function isPre31AdcpVersion(version: string | undefined): boolean {\n if (version === undefined) return false;\n const trimmed = version.trim();\n if (trimmed.length === 0) return false;\n\n const withoutLegacyPrefix = trimmed.startsWith('v') ? trimmed.slice(1) : trimmed;\n const match = /^(\\d+)(?:\\.(\\d+))?/.exec(withoutLegacyPrefix);\n if (!match?.[1]) return false;\n\n const major = Number.parseInt(match[1], 10);\n if (!Number.isFinite(major)) return false;\n if (major < 3) return true;\n if (major > 3) return false;\n\n const minor = match[2] === undefined ? 0 : Number.parseInt(match[2], 10);\n return Number.isFinite(minor) && minor < 1;\n}\n\n/** Whether a release pin predates the canonical creative identity surface in AdCP 3.2. */\nexport function isPre32AdcpVersion(version: string | undefined): boolean {\n if (version === undefined) return false;\n const trimmed = version.trim();\n if (trimmed.length === 0) return false;\n\n const withoutLegacyPrefix = trimmed.startsWith('v') ? trimmed.slice(1) : trimmed;\n const match = /^(\\d+)(?:\\.(\\d+))?/.exec(withoutLegacyPrefix);\n if (!match?.[1]) return false;\n\n const major = Number.parseInt(match[1], 10);\n if (!Number.isFinite(major)) return false;\n if (major < 3) return true;\n if (major > 3) return false;\n\n const minor = match[2] === undefined ? 0 : Number.parseInt(match[2], 10);\n return Number.isFinite(minor) && minor < 2;\n}\n\n/**\n * Does the seller authoritatively advertise AdCP 3.1+ support?\n *\n * `supportedVersions` is the negotiation field; the response envelope's\n * `adcp_version` is direct evidence of the wire release the seller just\n * served. `buildVersion` is advisory and MUST NOT select a wire shape.\n */\nexport function sellerAdvertises31(\n caps: { supportedVersions?: string[]; buildVersion?: string; _raw?: Record<string, unknown> } | undefined\n): boolean {\n if (!caps) return false;\n const supportedVersions = caps.supportedVersions ?? [];\n if (supportedVersions.length > 0) {\n return supportedVersions.some(v => !isPre31AdcpVersion(v));\n }\n const responseVersion = typeof caps._raw?.adcp_version === 'string' ? caps._raw.adcp_version : undefined;\n if (responseVersion !== undefined && !isPre31AdcpVersion(responseVersion)) return true;\n return false;\n}\n\n/**\n * Whether to omit AdCP 3.1-only request fields from the wire. Omit when the\n * client is pinned below 3.1, or when the seller does not advertise 3.1\n * support (legacy 3.0 sellers, and sellers whose capabilities were synthesized\n * from a tool list rather than declared).\n */\nexport function shouldOmit31Fields(\n resolvedClientVersion: string | undefined,\n caps: { supportedVersions?: string[]; buildVersion?: string; _raw?: Record<string, unknown> } | undefined\n): boolean {\n if (isPre31AdcpVersion(resolvedClientVersion)) return true;\n return !sellerAdvertises31(caps);\n}\n\n/**\n * Strip the AdCP 3.1-only `brand_kit_override` field from a BrandReference.\n * `industries` and `data_subject_contestation` are declared in AdCP 3.0 and\n * must be left on the wire for 3.0 sellers that accept and route on them.\n * Returns the original reference unchanged when `brand_kit_override` is absent.\n */\nexport function omit31BrandFields<T>(brand: T): T {\n if (!brand || typeof brand !== 'object' || Array.isArray(brand)) return brand;\n const b = brand as Record<string, unknown>;\n if (!('brand_kit_override' in b)) return brand;\n const { brand_kit_override, ...rest } = b;\n return rest as T;\n}\n\nfunction prereleaseFamilyAlias(version: string): string | undefined {\n const match = /^(\\d+\\.\\d+-[0-9A-Za-z-]+)\\.\\d+$/.exec(version);\n return match?.[1];\n}\n\n/**\n * Filter `COMPATIBLE_ADCP_VERSIONS` to entries with a shipped bundle on this\n * SDK build. The compatibility list is the historical set of accepted version\n * strings (legacy aliases, superseded betas, current GA), but only a subset\n * has bundles in `dist/lib/schemas-data/` at any given build. Returning\n * \"currently bundled: v2.5, v2.6, v3, 3.0.0-beta.3, 3.0.0, 3.0.1\" would\n * mislead a caller who pinned `'v2.5'` and got \"no bundle\".\n *\n * Always includes `ADCP_VERSION` even when its bundle hasn't been built yet\n * (dev-tree case) so the error remains actionable on first build.\n */\nfunction listBundledVersions(): string[] {\n const bundled = COMPATIBLE_ADCP_VERSIONS.filter(v => hasSchemaBundle(v));\n if (!bundled.includes(ADCP_VERSION as (typeof COMPATIBLE_ADCP_VERSIONS)[number])) {\n return [ADCP_VERSION, ...bundled];\n }\n return [...bundled];\n}\n"],"mappings":"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","names":["validSemver","semverGte"]}
1
+ {"version":3,"sources":["../../../src/lib/utils/adcp-version-config.ts"],"sourcesContent":["// Validation helpers for the per-instance `adcpVersion` constructor option.\n//\n// Stage 2 plumbed the option onto every client/server constructor with a\n// fence: cross-major pins were rejected at construction because the wire\n// field and validators still keyed off the global `ADCP_MAJOR_VERSION`.\n//\n// Stage 3 lifts that fence. Validators now key off the resolved bundle in\n// `dist/lib/schemas-data/<MAJOR.MINOR>/`, and the protocol layer derives\n// the wire-level `adcp_major_version` from the per-instance pin. Construction\n// now accepts any pin that has a schema bundle reachable from the SDK build,\n// and rejects pins for which no bundle is present (`'4.0.0'` while the SDK\n// only ships major-3 schemas, mistyped versions, etc.) with a clear pointer\n// at `sync-schemas` + `build:lib`.\n\nimport { ADCP_VERSION, COMPATIBLE_ADCP_VERSIONS, parseAdcpMajorVersion } from '../version';\nimport { ConfigurationError } from '../errors';\nimport { hasSchemaBundle, resolveBundleKey, toReleasePrecisionWire } from '../validation/schema-loader';\nimport { gte as semverGte, valid as validSemver } from 'semver';\n\nfunction comparableAdcpSemver(version: string): string | undefined {\n const match = /^v?(\\d+)\\.(\\d+)(?:\\.(\\d+))?((?:-[0-9A-Za-z.-]+)?(?:\\+[0-9A-Za-z.-]+)?)$/.exec(version.trim());\n if (!match) return undefined;\n const normalized = `${match[1]}.${match[2]}.${match[3] ?? '0'}${match[4] ?? ''}`;\n return validSemver(normalized) ?? undefined;\n}\n\n/** Compare full-semver and release-precision AdCP identifiers safely. */\nexport function isAdcpVersionAtLeast(version: string | undefined, minimum: string): boolean {\n if (version === undefined) return false;\n const comparable = comparableAdcpSemver(version);\n const comparableMinimum = comparableAdcpSemver(minimum);\n return comparable !== undefined && comparableMinimum !== undefined && semverGte(comparable, comparableMinimum);\n}\n\n/**\n * Resolve and validate a configured `adcpVersion`. Returns the value to store\n * on the instance — either the caller's pin or the SDK default.\n *\n * Throws `ConfigurationError` when:\n * - The pin is unparseable as a semver / legacy alias\n * - The pin parses but no schema bundle exists for the resolved key\n *\n * Cross-major pins are accepted as long as the corresponding bundle ships\n * with this SDK build (e.g. `'3.1.0-beta.1'` when `dist/lib/schemas-data/\n * 3.1.0-beta.1/` exists). Pins of the SDK's currently-pinned `ADCP_VERSION`\n * always succeed without an fs check — the bundle is guaranteed.\n */\nexport function resolveAdcpVersion(adcpVersion: string | undefined): string {\n if (adcpVersion === undefined) return ADCP_VERSION;\n\n const currentReleasePrecision = toReleasePrecisionWire(ADCP_VERSION);\n if (isMovingAdcpPrereleaseFamilyAlias(adcpVersion)) {\n throw new ConfigurationError(\n `adcpVersion ${JSON.stringify(adcpVersion)} is a moving prerelease-family alias. ` +\n `Pin the exact 3.2 beta artifact (${JSON.stringify(currentReleasePrecision)}) instead.`,\n 'adcpVersion'\n );\n }\n\n const major = parseAdcpMajorVersion(adcpVersion);\n if (!Number.isFinite(major)) {\n throw new ConfigurationError(\n `adcpVersion ${JSON.stringify(adcpVersion)} is not a valid AdCP version. ` +\n `Expected a semver string (e.g. '3.0.1', '3.1.0-beta.1') or a legacy alias. ` +\n `Currently bundled: ${listBundledAdcpVersions().join(', ')}.`,\n 'adcpVersion'\n );\n }\n\n // Skip the bundle-existence check when the pin resolves to the same bundle\n // as the SDK default — every published tarball includes that bundle by\n // construction. The bundle-key compare (rather than literal-string compare)\n // catches the common patterns: `'3.0'`, `'3.0.0'`, and `'3.0.1'` all\n // resolve to the same `'3.0'` bundle when `ADCP_VERSION === '3.0.1'`, so\n // none of those paths pay an fs round-trip.\n if (resolveBundleKey(adcpVersion) === resolveBundleKey(ADCP_VERSION)) return adcpVersion;\n\n if (!hasSchemaBundle(adcpVersion)) {\n const resolvedKey = resolveBundleKey(adcpVersion);\n throw new ConfigurationError(\n `adcpVersion ${JSON.stringify(adcpVersion)} resolves to bundle key \"${resolvedKey}\", ` +\n `but no schema bundle for that key ships with this SDK build. ` +\n `Currently bundled: ${listBundledAdcpVersions().join(', ')}. ` +\n `If you're testing against a beta that the spec repo has tagged but the SDK hasn't synced yet, ` +\n `run \\`npm run sync-schemas\\` and \\`npm run build:lib\\` to populate the cache, ` +\n `then re-construct.`,\n 'adcpVersion'\n );\n }\n\n return adcpVersion;\n}\n\n/** Whether a value is the ambiguous moving family alias for the pinned 3.2 preview. */\nexport function isMovingAdcpPrereleaseFamilyAlias(version: string): boolean {\n if (!ADCP_VERSION.startsWith('3.2.0-')) return false;\n return prereleaseFamilyAlias(toReleasePrecisionWire(ADCP_VERSION)) === version;\n}\n\nexport function adcpVersionAliases(version: string, includeFamilyAlias = true): Set<string> {\n const aliases = new Set<string>([version]);\n const add = (value: string) => {\n aliases.add(value);\n if (includeFamilyAlias) {\n const family = prereleaseFamilyAlias(value);\n if (family && !(ADCP_VERSION.startsWith('3.2.0-') && family.startsWith('3.2-'))) aliases.add(family);\n }\n };\n\n try {\n add(resolveBundleKey(version));\n } catch {\n // Keep the raw version only; the caller will report the mismatch.\n }\n try {\n add(toReleasePrecisionWire(version));\n } catch {\n // Keep the raw version only; the caller will report the mismatch.\n }\n return aliases;\n}\n\nexport function isAdcpVersionSupported(version: string, supportedVersions: readonly string[]): boolean {\n if (supportedVersions.length === 0) return true;\n const aliases = adcpVersionAliases(version);\n return supportedVersions.some(v => {\n for (const supportedAlias of adcpVersionAliases(v, false)) {\n if (aliases.has(supportedAlias)) return true;\n }\n return false;\n });\n}\n\nexport function isPre31AdcpVersion(version: string | undefined): boolean {\n if (version === undefined) return false;\n const trimmed = version.trim();\n if (trimmed.length === 0) return false;\n\n const withoutLegacyPrefix = trimmed.startsWith('v') ? trimmed.slice(1) : trimmed;\n const match = /^(\\d+)(?:\\.(\\d+))?/.exec(withoutLegacyPrefix);\n if (!match?.[1]) return false;\n\n const major = Number.parseInt(match[1], 10);\n if (!Number.isFinite(major)) return false;\n if (major < 3) return true;\n if (major > 3) return false;\n\n const minor = match[2] === undefined ? 0 : Number.parseInt(match[2], 10);\n return Number.isFinite(minor) && minor < 1;\n}\n\n/** Whether a release pin predates the canonical creative identity surface in AdCP 3.2. */\nexport function isPre32AdcpVersion(version: string | undefined): boolean {\n if (version === undefined) return false;\n const trimmed = version.trim();\n if (trimmed.length === 0) return false;\n\n const withoutLegacyPrefix = trimmed.startsWith('v') ? trimmed.slice(1) : trimmed;\n const match = /^(\\d+)(?:\\.(\\d+))?/.exec(withoutLegacyPrefix);\n if (!match?.[1]) return false;\n\n const major = Number.parseInt(match[1], 10);\n if (!Number.isFinite(major)) return false;\n if (major < 3) return true;\n if (major > 3) return false;\n\n const minor = match[2] === undefined ? 0 : Number.parseInt(match[2], 10);\n return Number.isFinite(minor) && minor < 2;\n}\n\n/**\n * Does the seller authoritatively advertise AdCP 3.1+ support?\n *\n * `supportedVersions` is the negotiation field; the response envelope's\n * `adcp_version` is direct evidence of the wire release the seller just\n * served. `buildVersion` is advisory and MUST NOT select a wire shape.\n */\nexport function sellerAdvertises31(\n caps: { supportedVersions?: string[]; buildVersion?: string; _raw?: Record<string, unknown> } | undefined\n): boolean {\n if (!caps) return false;\n const supportedVersions = caps.supportedVersions ?? [];\n if (supportedVersions.length > 0) {\n return supportedVersions.some(v => !isPre31AdcpVersion(v));\n }\n const responseVersion = typeof caps._raw?.adcp_version === 'string' ? caps._raw.adcp_version : undefined;\n if (responseVersion !== undefined && !isPre31AdcpVersion(responseVersion)) return true;\n return false;\n}\n\n/**\n * Whether to omit AdCP 3.1-only request fields from the wire. Omit when the\n * client is pinned below 3.1, or when the seller does not advertise 3.1\n * support (legacy 3.0 sellers, and sellers whose capabilities were synthesized\n * from a tool list rather than declared).\n */\nexport function shouldOmit31Fields(\n resolvedClientVersion: string | undefined,\n caps: { supportedVersions?: string[]; buildVersion?: string; _raw?: Record<string, unknown> } | undefined\n): boolean {\n if (isPre31AdcpVersion(resolvedClientVersion)) return true;\n return !sellerAdvertises31(caps);\n}\n\n/**\n * Strip the AdCP 3.1-only `brand_kit_override` field from a BrandReference.\n * `industries` and `data_subject_contestation` are declared in AdCP 3.0 and\n * must be left on the wire for 3.0 sellers that accept and route on them.\n * Returns the original reference unchanged when `brand_kit_override` is absent.\n */\nexport function omit31BrandFields<T>(brand: T): T {\n if (!brand || typeof brand !== 'object' || Array.isArray(brand)) return brand;\n const b = brand as Record<string, unknown>;\n if (!('brand_kit_override' in b)) return brand;\n const { brand_kit_override, ...rest } = b;\n return rest as T;\n}\n\nfunction prereleaseFamilyAlias(version: string): string | undefined {\n const match = /^(\\d+\\.\\d+-[0-9A-Za-z-]+)\\.\\d+$/.exec(version);\n return match?.[1];\n}\n\n/**\n * Filter `COMPATIBLE_ADCP_VERSIONS` to entries with a shipped bundle on this\n * SDK build. The compatibility list is the historical set of accepted version\n * strings (legacy aliases, superseded betas, current GA), but only a subset\n * has bundles in `dist/lib/schemas-data/` at any given build. Returning\n * \"currently bundled: v2.5, v2.6, v3, 3.0.0-beta.3, 3.0.0, 3.0.1\" would\n * mislead a caller who pinned `'v2.5'` and got \"no bundle\".\n *\n * Always includes `ADCP_VERSION` even when its bundle hasn't been built yet\n * (dev-tree case) so the error remains actionable on first build.\n */\nexport function listBundledAdcpVersions(): string[] {\n const bundled = COMPATIBLE_ADCP_VERSIONS.filter(v => hasSchemaBundle(v));\n if (!bundled.includes(ADCP_VERSION as (typeof COMPATIBLE_ADCP_VERSIONS)[number])) {\n return [ADCP_VERSION, ...bundled];\n }\n return [...bundled];\n}\n"],"mappings":"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","names":["validSemver","semverGte"]}
@@ -26,7 +26,7 @@ function resolveAdcpVersion(adcpVersion) {
26
26
  const major = parseAdcpMajorVersion(adcpVersion);
27
27
  if (!Number.isFinite(major)) {
28
28
  throw new ConfigurationError(
29
- `adcpVersion ${JSON.stringify(adcpVersion)} is not a valid AdCP version. Expected a semver string (e.g. '3.0.1', '3.1.0-beta.1') or a legacy alias. Currently bundled: ${listBundledVersions().join(", ")}.`,
29
+ `adcpVersion ${JSON.stringify(adcpVersion)} is not a valid AdCP version. Expected a semver string (e.g. '3.0.1', '3.1.0-beta.1') or a legacy alias. Currently bundled: ${listBundledAdcpVersions().join(", ")}.`,
30
30
  "adcpVersion"
31
31
  );
32
32
  }
@@ -34,7 +34,7 @@ function resolveAdcpVersion(adcpVersion) {
34
34
  if (!hasSchemaBundle(adcpVersion)) {
35
35
  const resolvedKey = resolveBundleKey(adcpVersion);
36
36
  throw new ConfigurationError(
37
- `adcpVersion ${JSON.stringify(adcpVersion)} resolves to bundle key "${resolvedKey}", but no schema bundle for that key ships with this SDK build. Currently bundled: ${listBundledVersions().join(", ")}. If you're testing against a beta that the spec repo has tagged but the SDK hasn't synced yet, run \`npm run sync-schemas\` and \`npm run build:lib\` to populate the cache, then re-construct.`,
37
+ `adcpVersion ${JSON.stringify(adcpVersion)} resolves to bundle key "${resolvedKey}", but no schema bundle for that key ships with this SDK build. Currently bundled: ${listBundledAdcpVersions().join(", ")}. If you're testing against a beta that the spec repo has tagged but the SDK hasn't synced yet, run \`npm run sync-schemas\` and \`npm run build:lib\` to populate the cache, then re-construct.`,
38
38
  "adcpVersion"
39
39
  );
40
40
  }
@@ -126,7 +126,7 @@ function prereleaseFamilyAlias(version) {
126
126
  const match = /^(\d+\.\d+-[0-9A-Za-z-]+)\.\d+$/.exec(version);
127
127
  return match?.[1];
128
128
  }
129
- function listBundledVersions() {
129
+ function listBundledAdcpVersions() {
130
130
  const bundled = COMPATIBLE_ADCP_VERSIONS.filter((v) => hasSchemaBundle(v));
131
131
  if (!bundled.includes(ADCP_VERSION)) {
132
132
  return [ADCP_VERSION, ...bundled];
@@ -140,6 +140,7 @@ export {
140
140
  isMovingAdcpPrereleaseFamilyAlias,
141
141
  isPre31AdcpVersion,
142
142
  isPre32AdcpVersion,
143
+ listBundledAdcpVersions,
143
144
  omit31BrandFields,
144
145
  resolveAdcpVersion,
145
146
  sellerAdvertises31,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/lib/utils/adcp-version-config.ts"],"sourcesContent":["// Validation helpers for the per-instance `adcpVersion` constructor option.\n//\n// Stage 2 plumbed the option onto every client/server constructor with a\n// fence: cross-major pins were rejected at construction because the wire\n// field and validators still keyed off the global `ADCP_MAJOR_VERSION`.\n//\n// Stage 3 lifts that fence. Validators now key off the resolved bundle in\n// `dist/lib/schemas-data/<MAJOR.MINOR>/`, and the protocol layer derives\n// the wire-level `adcp_major_version` from the per-instance pin. Construction\n// now accepts any pin that has a schema bundle reachable from the SDK build,\n// and rejects pins for which no bundle is present (`'4.0.0'` while the SDK\n// only ships major-3 schemas, mistyped versions, etc.) with a clear pointer\n// at `sync-schemas` + `build:lib`.\n\nimport { ADCP_VERSION, COMPATIBLE_ADCP_VERSIONS, parseAdcpMajorVersion } from '../version';\nimport { ConfigurationError } from '../errors';\nimport { hasSchemaBundle, resolveBundleKey, toReleasePrecisionWire } from '../validation/schema-loader';\nimport { gte as semverGte, valid as validSemver } from 'semver';\n\nfunction comparableAdcpSemver(version: string): string | undefined {\n const match = /^v?(\\d+)\\.(\\d+)(?:\\.(\\d+))?((?:-[0-9A-Za-z.-]+)?(?:\\+[0-9A-Za-z.-]+)?)$/.exec(version.trim());\n if (!match) return undefined;\n const normalized = `${match[1]}.${match[2]}.${match[3] ?? '0'}${match[4] ?? ''}`;\n return validSemver(normalized) ?? undefined;\n}\n\n/** Compare full-semver and release-precision AdCP identifiers safely. */\nexport function isAdcpVersionAtLeast(version: string | undefined, minimum: string): boolean {\n if (version === undefined) return false;\n const comparable = comparableAdcpSemver(version);\n const comparableMinimum = comparableAdcpSemver(minimum);\n return comparable !== undefined && comparableMinimum !== undefined && semverGte(comparable, comparableMinimum);\n}\n\n/**\n * Resolve and validate a configured `adcpVersion`. Returns the value to store\n * on the instance — either the caller's pin or the SDK default.\n *\n * Throws `ConfigurationError` when:\n * - The pin is unparseable as a semver / legacy alias\n * - The pin parses but no schema bundle exists for the resolved key\n *\n * Cross-major pins are accepted as long as the corresponding bundle ships\n * with this SDK build (e.g. `'3.1.0-beta.1'` when `dist/lib/schemas-data/\n * 3.1.0-beta.1/` exists). Pins of the SDK's currently-pinned `ADCP_VERSION`\n * always succeed without an fs check — the bundle is guaranteed.\n */\nexport function resolveAdcpVersion(adcpVersion: string | undefined): string {\n if (adcpVersion === undefined) return ADCP_VERSION;\n\n const currentReleasePrecision = toReleasePrecisionWire(ADCP_VERSION);\n if (isMovingAdcpPrereleaseFamilyAlias(adcpVersion)) {\n throw new ConfigurationError(\n `adcpVersion ${JSON.stringify(adcpVersion)} is a moving prerelease-family alias. ` +\n `Pin the exact 3.2 beta artifact (${JSON.stringify(currentReleasePrecision)}) instead.`,\n 'adcpVersion'\n );\n }\n\n const major = parseAdcpMajorVersion(adcpVersion);\n if (!Number.isFinite(major)) {\n throw new ConfigurationError(\n `adcpVersion ${JSON.stringify(adcpVersion)} is not a valid AdCP version. ` +\n `Expected a semver string (e.g. '3.0.1', '3.1.0-beta.1') or a legacy alias. ` +\n `Currently bundled: ${listBundledVersions().join(', ')}.`,\n 'adcpVersion'\n );\n }\n\n // Skip the bundle-existence check when the pin resolves to the same bundle\n // as the SDK default — every published tarball includes that bundle by\n // construction. The bundle-key compare (rather than literal-string compare)\n // catches the common patterns: `'3.0'`, `'3.0.0'`, and `'3.0.1'` all\n // resolve to the same `'3.0'` bundle when `ADCP_VERSION === '3.0.1'`, so\n // none of those paths pay an fs round-trip.\n if (resolveBundleKey(adcpVersion) === resolveBundleKey(ADCP_VERSION)) return adcpVersion;\n\n if (!hasSchemaBundle(adcpVersion)) {\n const resolvedKey = resolveBundleKey(adcpVersion);\n throw new ConfigurationError(\n `adcpVersion ${JSON.stringify(adcpVersion)} resolves to bundle key \"${resolvedKey}\", ` +\n `but no schema bundle for that key ships with this SDK build. ` +\n `Currently bundled: ${listBundledVersions().join(', ')}. ` +\n `If you're testing against a beta that the spec repo has tagged but the SDK hasn't synced yet, ` +\n `run \\`npm run sync-schemas\\` and \\`npm run build:lib\\` to populate the cache, ` +\n `then re-construct.`,\n 'adcpVersion'\n );\n }\n\n return adcpVersion;\n}\n\n/** Whether a value is the ambiguous moving family alias for the pinned 3.2 preview. */\nexport function isMovingAdcpPrereleaseFamilyAlias(version: string): boolean {\n if (!ADCP_VERSION.startsWith('3.2.0-')) return false;\n return prereleaseFamilyAlias(toReleasePrecisionWire(ADCP_VERSION)) === version;\n}\n\nexport function adcpVersionAliases(version: string, includeFamilyAlias = true): Set<string> {\n const aliases = new Set<string>([version]);\n const add = (value: string) => {\n aliases.add(value);\n if (includeFamilyAlias) {\n const family = prereleaseFamilyAlias(value);\n if (family && !(ADCP_VERSION.startsWith('3.2.0-') && family.startsWith('3.2-'))) aliases.add(family);\n }\n };\n\n try {\n add(resolveBundleKey(version));\n } catch {\n // Keep the raw version only; the caller will report the mismatch.\n }\n try {\n add(toReleasePrecisionWire(version));\n } catch {\n // Keep the raw version only; the caller will report the mismatch.\n }\n return aliases;\n}\n\nexport function isAdcpVersionSupported(version: string, supportedVersions: readonly string[]): boolean {\n if (supportedVersions.length === 0) return true;\n const aliases = adcpVersionAliases(version);\n return supportedVersions.some(v => {\n for (const supportedAlias of adcpVersionAliases(v, false)) {\n if (aliases.has(supportedAlias)) return true;\n }\n return false;\n });\n}\n\nexport function isPre31AdcpVersion(version: string | undefined): boolean {\n if (version === undefined) return false;\n const trimmed = version.trim();\n if (trimmed.length === 0) return false;\n\n const withoutLegacyPrefix = trimmed.startsWith('v') ? trimmed.slice(1) : trimmed;\n const match = /^(\\d+)(?:\\.(\\d+))?/.exec(withoutLegacyPrefix);\n if (!match?.[1]) return false;\n\n const major = Number.parseInt(match[1], 10);\n if (!Number.isFinite(major)) return false;\n if (major < 3) return true;\n if (major > 3) return false;\n\n const minor = match[2] === undefined ? 0 : Number.parseInt(match[2], 10);\n return Number.isFinite(minor) && minor < 1;\n}\n\n/** Whether a release pin predates the canonical creative identity surface in AdCP 3.2. */\nexport function isPre32AdcpVersion(version: string | undefined): boolean {\n if (version === undefined) return false;\n const trimmed = version.trim();\n if (trimmed.length === 0) return false;\n\n const withoutLegacyPrefix = trimmed.startsWith('v') ? trimmed.slice(1) : trimmed;\n const match = /^(\\d+)(?:\\.(\\d+))?/.exec(withoutLegacyPrefix);\n if (!match?.[1]) return false;\n\n const major = Number.parseInt(match[1], 10);\n if (!Number.isFinite(major)) return false;\n if (major < 3) return true;\n if (major > 3) return false;\n\n const minor = match[2] === undefined ? 0 : Number.parseInt(match[2], 10);\n return Number.isFinite(minor) && minor < 2;\n}\n\n/**\n * Does the seller authoritatively advertise AdCP 3.1+ support?\n *\n * `supportedVersions` is the negotiation field; the response envelope's\n * `adcp_version` is direct evidence of the wire release the seller just\n * served. `buildVersion` is advisory and MUST NOT select a wire shape.\n */\nexport function sellerAdvertises31(\n caps: { supportedVersions?: string[]; buildVersion?: string; _raw?: Record<string, unknown> } | undefined\n): boolean {\n if (!caps) return false;\n const supportedVersions = caps.supportedVersions ?? [];\n if (supportedVersions.length > 0) {\n return supportedVersions.some(v => !isPre31AdcpVersion(v));\n }\n const responseVersion = typeof caps._raw?.adcp_version === 'string' ? caps._raw.adcp_version : undefined;\n if (responseVersion !== undefined && !isPre31AdcpVersion(responseVersion)) return true;\n return false;\n}\n\n/**\n * Whether to omit AdCP 3.1-only request fields from the wire. Omit when the\n * client is pinned below 3.1, or when the seller does not advertise 3.1\n * support (legacy 3.0 sellers, and sellers whose capabilities were synthesized\n * from a tool list rather than declared).\n */\nexport function shouldOmit31Fields(\n resolvedClientVersion: string | undefined,\n caps: { supportedVersions?: string[]; buildVersion?: string; _raw?: Record<string, unknown> } | undefined\n): boolean {\n if (isPre31AdcpVersion(resolvedClientVersion)) return true;\n return !sellerAdvertises31(caps);\n}\n\n/**\n * Strip the AdCP 3.1-only `brand_kit_override` field from a BrandReference.\n * `industries` and `data_subject_contestation` are declared in AdCP 3.0 and\n * must be left on the wire for 3.0 sellers that accept and route on them.\n * Returns the original reference unchanged when `brand_kit_override` is absent.\n */\nexport function omit31BrandFields<T>(brand: T): T {\n if (!brand || typeof brand !== 'object' || Array.isArray(brand)) return brand;\n const b = brand as Record<string, unknown>;\n if (!('brand_kit_override' in b)) return brand;\n const { brand_kit_override, ...rest } = b;\n return rest as T;\n}\n\nfunction prereleaseFamilyAlias(version: string): string | undefined {\n const match = /^(\\d+\\.\\d+-[0-9A-Za-z-]+)\\.\\d+$/.exec(version);\n return match?.[1];\n}\n\n/**\n * Filter `COMPATIBLE_ADCP_VERSIONS` to entries with a shipped bundle on this\n * SDK build. The compatibility list is the historical set of accepted version\n * strings (legacy aliases, superseded betas, current GA), but only a subset\n * has bundles in `dist/lib/schemas-data/` at any given build. Returning\n * \"currently bundled: v2.5, v2.6, v3, 3.0.0-beta.3, 3.0.0, 3.0.1\" would\n * mislead a caller who pinned `'v2.5'` and got \"no bundle\".\n *\n * Always includes `ADCP_VERSION` even when its bundle hasn't been built yet\n * (dev-tree case) so the error remains actionable on first build.\n */\nfunction listBundledVersions(): string[] {\n const bundled = COMPATIBLE_ADCP_VERSIONS.filter(v => hasSchemaBundle(v));\n if (!bundled.includes(ADCP_VERSION as (typeof COMPATIBLE_ADCP_VERSIONS)[number])) {\n return [ADCP_VERSION, ...bundled];\n }\n return [...bundled];\n}\n"],"mappings":"AAcA,SAAS,cAAc,0BAA0B,6BAA6B;AAC9E,SAAS,0BAA0B;AACnC,SAAS,iBAAiB,kBAAkB,8BAA8B;AAC1E,SAAS,OAAO,WAAW,SAAS,mBAAmB;AAEvD,SAAS,qBAAqB,SAAqC;AACjE,QAAM,QAAQ,0EAA0E,KAAK,QAAQ,KAAK,CAAC;AAC3G,MAAI,CAAC,MAAO,QAAO;AACnB,QAAM,aAAa,GAAG,MAAM,CAAC,CAAC,IAAI,MAAM,CAAC,CAAC,IAAI,MAAM,CAAC,KAAK,GAAG,GAAG,MAAM,CAAC,KAAK,EAAE;AAC9E,SAAO,YAAY,UAAU,KAAK;AACpC;AAGO,SAAS,qBAAqB,SAA6B,SAA0B;AAC1F,MAAI,YAAY,OAAW,QAAO;AAClC,QAAM,aAAa,qBAAqB,OAAO;AAC/C,QAAM,oBAAoB,qBAAqB,OAAO;AACtD,SAAO,eAAe,UAAa,sBAAsB,UAAa,UAAU,YAAY,iBAAiB;AAC/G;AAeO,SAAS,mBAAmB,aAAyC;AAC1E,MAAI,gBAAgB,OAAW,QAAO;AAEtC,QAAM,0BAA0B,uBAAuB,YAAY;AACnE,MAAI,kCAAkC,WAAW,GAAG;AAClD,UAAM,IAAI;AAAA,MACR,eAAe,KAAK,UAAU,WAAW,CAAC,0EACJ,KAAK,UAAU,uBAAuB,CAAC;AAAA,MAC7E;AAAA,IACF;AAAA,EACF;AAEA,QAAM,QAAQ,sBAAsB,WAAW;AAC/C,MAAI,CAAC,OAAO,SAAS,KAAK,GAAG;AAC3B,UAAM,IAAI;AAAA,MACR,eAAe,KAAK,UAAU,WAAW,CAAC,+HAElB,oBAAoB,EAAE,KAAK,IAAI,CAAC;AAAA,MACxD;AAAA,IACF;AAAA,EACF;AAQA,MAAI,iBAAiB,WAAW,MAAM,iBAAiB,YAAY,EAAG,QAAO;AAE7E,MAAI,CAAC,gBAAgB,WAAW,GAAG;AACjC,UAAM,cAAc,iBAAiB,WAAW;AAChD,UAAM,IAAI;AAAA,MACR,eAAe,KAAK,UAAU,WAAW,CAAC,4BAA4B,WAAW,sFAEzD,oBAAoB,EAAE,KAAK,IAAI,CAAC;AAAA,MAIxD;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;AAGO,SAAS,kCAAkC,SAA0B;AAC1E,MAAI,CAAC,aAAa,WAAW,QAAQ,EAAG,QAAO;AAC/C,SAAO,sBAAsB,uBAAuB,YAAY,CAAC,MAAM;AACzE;AAEO,SAAS,mBAAmB,SAAiB,qBAAqB,MAAmB;AAC1F,QAAM,UAAU,oBAAI,IAAY,CAAC,OAAO,CAAC;AACzC,QAAM,MAAM,CAAC,UAAkB;AAC7B,YAAQ,IAAI,KAAK;AACjB,QAAI,oBAAoB;AACtB,YAAM,SAAS,sBAAsB,KAAK;AAC1C,UAAI,UAAU,EAAE,aAAa,WAAW,QAAQ,KAAK,OAAO,WAAW,MAAM,GAAI,SAAQ,IAAI,MAAM;AAAA,IACrG;AAAA,EACF;AAEA,MAAI;AACF,QAAI,iBAAiB,OAAO,CAAC;AAAA,EAC/B,QAAQ;AAAA,EAER;AACA,MAAI;AACF,QAAI,uBAAuB,OAAO,CAAC;AAAA,EACrC,QAAQ;AAAA,EAER;AACA,SAAO;AACT;AAEO,SAAS,uBAAuB,SAAiB,mBAA+C;AACrG,MAAI,kBAAkB,WAAW,EAAG,QAAO;AAC3C,QAAM,UAAU,mBAAmB,OAAO;AAC1C,SAAO,kBAAkB,KAAK,OAAK;AACjC,eAAW,kBAAkB,mBAAmB,GAAG,KAAK,GAAG;AACzD,UAAI,QAAQ,IAAI,cAAc,EAAG,QAAO;AAAA,IAC1C;AACA,WAAO;AAAA,EACT,CAAC;AACH;AAEO,SAAS,mBAAmB,SAAsC;AACvE,MAAI,YAAY,OAAW,QAAO;AAClC,QAAM,UAAU,QAAQ,KAAK;AAC7B,MAAI,QAAQ,WAAW,EAAG,QAAO;AAEjC,QAAM,sBAAsB,QAAQ,WAAW,GAAG,IAAI,QAAQ,MAAM,CAAC,IAAI;AACzE,QAAM,QAAQ,qBAAqB,KAAK,mBAAmB;AAC3D,MAAI,CAAC,QAAQ,CAAC,EAAG,QAAO;AAExB,QAAM,QAAQ,OAAO,SAAS,MAAM,CAAC,GAAG,EAAE;AAC1C,MAAI,CAAC,OAAO,SAAS,KAAK,EAAG,QAAO;AACpC,MAAI,QAAQ,EAAG,QAAO;AACtB,MAAI,QAAQ,EAAG,QAAO;AAEtB,QAAM,QAAQ,MAAM,CAAC,MAAM,SAAY,IAAI,OAAO,SAAS,MAAM,CAAC,GAAG,EAAE;AACvE,SAAO,OAAO,SAAS,KAAK,KAAK,QAAQ;AAC3C;AAGO,SAAS,mBAAmB,SAAsC;AACvE,MAAI,YAAY,OAAW,QAAO;AAClC,QAAM,UAAU,QAAQ,KAAK;AAC7B,MAAI,QAAQ,WAAW,EAAG,QAAO;AAEjC,QAAM,sBAAsB,QAAQ,WAAW,GAAG,IAAI,QAAQ,MAAM,CAAC,IAAI;AACzE,QAAM,QAAQ,qBAAqB,KAAK,mBAAmB;AAC3D,MAAI,CAAC,QAAQ,CAAC,EAAG,QAAO;AAExB,QAAM,QAAQ,OAAO,SAAS,MAAM,CAAC,GAAG,EAAE;AAC1C,MAAI,CAAC,OAAO,SAAS,KAAK,EAAG,QAAO;AACpC,MAAI,QAAQ,EAAG,QAAO;AACtB,MAAI,QAAQ,EAAG,QAAO;AAEtB,QAAM,QAAQ,MAAM,CAAC,MAAM,SAAY,IAAI,OAAO,SAAS,MAAM,CAAC,GAAG,EAAE;AACvE,SAAO,OAAO,SAAS,KAAK,KAAK,QAAQ;AAC3C;AASO,SAAS,mBACd,MACS;AACT,MAAI,CAAC,KAAM,QAAO;AAClB,QAAM,oBAAoB,KAAK,qBAAqB,CAAC;AACrD,MAAI,kBAAkB,SAAS,GAAG;AAChC,WAAO,kBAAkB,KAAK,OAAK,CAAC,mBAAmB,CAAC,CAAC;AAAA,EAC3D;AACA,QAAM,kBAAkB,OAAO,KAAK,MAAM,iBAAiB,WAAW,KAAK,KAAK,eAAe;AAC/F,MAAI,oBAAoB,UAAa,CAAC,mBAAmB,eAAe,EAAG,QAAO;AAClF,SAAO;AACT;AAQO,SAAS,mBACd,uBACA,MACS;AACT,MAAI,mBAAmB,qBAAqB,EAAG,QAAO;AACtD,SAAO,CAAC,mBAAmB,IAAI;AACjC;AAQO,SAAS,kBAAqB,OAAa;AAChD,MAAI,CAAC,SAAS,OAAO,UAAU,YAAY,MAAM,QAAQ,KAAK,EAAG,QAAO;AACxE,QAAM,IAAI;AACV,MAAI,EAAE,wBAAwB,GAAI,QAAO;AACzC,QAAM,EAAE,oBAAoB,GAAG,KAAK,IAAI;AACxC,SAAO;AACT;AAEA,SAAS,sBAAsB,SAAqC;AAClE,QAAM,QAAQ,kCAAkC,KAAK,OAAO;AAC5D,SAAO,QAAQ,CAAC;AAClB;AAaA,SAAS,sBAAgC;AACvC,QAAM,UAAU,yBAAyB,OAAO,OAAK,gBAAgB,CAAC,CAAC;AACvE,MAAI,CAAC,QAAQ,SAAS,YAAyD,GAAG;AAChF,WAAO,CAAC,cAAc,GAAG,OAAO;AAAA,EAClC;AACA,SAAO,CAAC,GAAG,OAAO;AACpB;","names":[]}
1
+ {"version":3,"sources":["../../../src/lib/utils/adcp-version-config.ts"],"sourcesContent":["// Validation helpers for the per-instance `adcpVersion` constructor option.\n//\n// Stage 2 plumbed the option onto every client/server constructor with a\n// fence: cross-major pins were rejected at construction because the wire\n// field and validators still keyed off the global `ADCP_MAJOR_VERSION`.\n//\n// Stage 3 lifts that fence. Validators now key off the resolved bundle in\n// `dist/lib/schemas-data/<MAJOR.MINOR>/`, and the protocol layer derives\n// the wire-level `adcp_major_version` from the per-instance pin. Construction\n// now accepts any pin that has a schema bundle reachable from the SDK build,\n// and rejects pins for which no bundle is present (`'4.0.0'` while the SDK\n// only ships major-3 schemas, mistyped versions, etc.) with a clear pointer\n// at `sync-schemas` + `build:lib`.\n\nimport { ADCP_VERSION, COMPATIBLE_ADCP_VERSIONS, parseAdcpMajorVersion } from '../version';\nimport { ConfigurationError } from '../errors';\nimport { hasSchemaBundle, resolveBundleKey, toReleasePrecisionWire } from '../validation/schema-loader';\nimport { gte as semverGte, valid as validSemver } from 'semver';\n\nfunction comparableAdcpSemver(version: string): string | undefined {\n const match = /^v?(\\d+)\\.(\\d+)(?:\\.(\\d+))?((?:-[0-9A-Za-z.-]+)?(?:\\+[0-9A-Za-z.-]+)?)$/.exec(version.trim());\n if (!match) return undefined;\n const normalized = `${match[1]}.${match[2]}.${match[3] ?? '0'}${match[4] ?? ''}`;\n return validSemver(normalized) ?? undefined;\n}\n\n/** Compare full-semver and release-precision AdCP identifiers safely. */\nexport function isAdcpVersionAtLeast(version: string | undefined, minimum: string): boolean {\n if (version === undefined) return false;\n const comparable = comparableAdcpSemver(version);\n const comparableMinimum = comparableAdcpSemver(minimum);\n return comparable !== undefined && comparableMinimum !== undefined && semverGte(comparable, comparableMinimum);\n}\n\n/**\n * Resolve and validate a configured `adcpVersion`. Returns the value to store\n * on the instance — either the caller's pin or the SDK default.\n *\n * Throws `ConfigurationError` when:\n * - The pin is unparseable as a semver / legacy alias\n * - The pin parses but no schema bundle exists for the resolved key\n *\n * Cross-major pins are accepted as long as the corresponding bundle ships\n * with this SDK build (e.g. `'3.1.0-beta.1'` when `dist/lib/schemas-data/\n * 3.1.0-beta.1/` exists). Pins of the SDK's currently-pinned `ADCP_VERSION`\n * always succeed without an fs check — the bundle is guaranteed.\n */\nexport function resolveAdcpVersion(adcpVersion: string | undefined): string {\n if (adcpVersion === undefined) return ADCP_VERSION;\n\n const currentReleasePrecision = toReleasePrecisionWire(ADCP_VERSION);\n if (isMovingAdcpPrereleaseFamilyAlias(adcpVersion)) {\n throw new ConfigurationError(\n `adcpVersion ${JSON.stringify(adcpVersion)} is a moving prerelease-family alias. ` +\n `Pin the exact 3.2 beta artifact (${JSON.stringify(currentReleasePrecision)}) instead.`,\n 'adcpVersion'\n );\n }\n\n const major = parseAdcpMajorVersion(adcpVersion);\n if (!Number.isFinite(major)) {\n throw new ConfigurationError(\n `adcpVersion ${JSON.stringify(adcpVersion)} is not a valid AdCP version. ` +\n `Expected a semver string (e.g. '3.0.1', '3.1.0-beta.1') or a legacy alias. ` +\n `Currently bundled: ${listBundledAdcpVersions().join(', ')}.`,\n 'adcpVersion'\n );\n }\n\n // Skip the bundle-existence check when the pin resolves to the same bundle\n // as the SDK default — every published tarball includes that bundle by\n // construction. The bundle-key compare (rather than literal-string compare)\n // catches the common patterns: `'3.0'`, `'3.0.0'`, and `'3.0.1'` all\n // resolve to the same `'3.0'` bundle when `ADCP_VERSION === '3.0.1'`, so\n // none of those paths pay an fs round-trip.\n if (resolveBundleKey(adcpVersion) === resolveBundleKey(ADCP_VERSION)) return adcpVersion;\n\n if (!hasSchemaBundle(adcpVersion)) {\n const resolvedKey = resolveBundleKey(adcpVersion);\n throw new ConfigurationError(\n `adcpVersion ${JSON.stringify(adcpVersion)} resolves to bundle key \"${resolvedKey}\", ` +\n `but no schema bundle for that key ships with this SDK build. ` +\n `Currently bundled: ${listBundledAdcpVersions().join(', ')}. ` +\n `If you're testing against a beta that the spec repo has tagged but the SDK hasn't synced yet, ` +\n `run \\`npm run sync-schemas\\` and \\`npm run build:lib\\` to populate the cache, ` +\n `then re-construct.`,\n 'adcpVersion'\n );\n }\n\n return adcpVersion;\n}\n\n/** Whether a value is the ambiguous moving family alias for the pinned 3.2 preview. */\nexport function isMovingAdcpPrereleaseFamilyAlias(version: string): boolean {\n if (!ADCP_VERSION.startsWith('3.2.0-')) return false;\n return prereleaseFamilyAlias(toReleasePrecisionWire(ADCP_VERSION)) === version;\n}\n\nexport function adcpVersionAliases(version: string, includeFamilyAlias = true): Set<string> {\n const aliases = new Set<string>([version]);\n const add = (value: string) => {\n aliases.add(value);\n if (includeFamilyAlias) {\n const family = prereleaseFamilyAlias(value);\n if (family && !(ADCP_VERSION.startsWith('3.2.0-') && family.startsWith('3.2-'))) aliases.add(family);\n }\n };\n\n try {\n add(resolveBundleKey(version));\n } catch {\n // Keep the raw version only; the caller will report the mismatch.\n }\n try {\n add(toReleasePrecisionWire(version));\n } catch {\n // Keep the raw version only; the caller will report the mismatch.\n }\n return aliases;\n}\n\nexport function isAdcpVersionSupported(version: string, supportedVersions: readonly string[]): boolean {\n if (supportedVersions.length === 0) return true;\n const aliases = adcpVersionAliases(version);\n return supportedVersions.some(v => {\n for (const supportedAlias of adcpVersionAliases(v, false)) {\n if (aliases.has(supportedAlias)) return true;\n }\n return false;\n });\n}\n\nexport function isPre31AdcpVersion(version: string | undefined): boolean {\n if (version === undefined) return false;\n const trimmed = version.trim();\n if (trimmed.length === 0) return false;\n\n const withoutLegacyPrefix = trimmed.startsWith('v') ? trimmed.slice(1) : trimmed;\n const match = /^(\\d+)(?:\\.(\\d+))?/.exec(withoutLegacyPrefix);\n if (!match?.[1]) return false;\n\n const major = Number.parseInt(match[1], 10);\n if (!Number.isFinite(major)) return false;\n if (major < 3) return true;\n if (major > 3) return false;\n\n const minor = match[2] === undefined ? 0 : Number.parseInt(match[2], 10);\n return Number.isFinite(minor) && minor < 1;\n}\n\n/** Whether a release pin predates the canonical creative identity surface in AdCP 3.2. */\nexport function isPre32AdcpVersion(version: string | undefined): boolean {\n if (version === undefined) return false;\n const trimmed = version.trim();\n if (trimmed.length === 0) return false;\n\n const withoutLegacyPrefix = trimmed.startsWith('v') ? trimmed.slice(1) : trimmed;\n const match = /^(\\d+)(?:\\.(\\d+))?/.exec(withoutLegacyPrefix);\n if (!match?.[1]) return false;\n\n const major = Number.parseInt(match[1], 10);\n if (!Number.isFinite(major)) return false;\n if (major < 3) return true;\n if (major > 3) return false;\n\n const minor = match[2] === undefined ? 0 : Number.parseInt(match[2], 10);\n return Number.isFinite(minor) && minor < 2;\n}\n\n/**\n * Does the seller authoritatively advertise AdCP 3.1+ support?\n *\n * `supportedVersions` is the negotiation field; the response envelope's\n * `adcp_version` is direct evidence of the wire release the seller just\n * served. `buildVersion` is advisory and MUST NOT select a wire shape.\n */\nexport function sellerAdvertises31(\n caps: { supportedVersions?: string[]; buildVersion?: string; _raw?: Record<string, unknown> } | undefined\n): boolean {\n if (!caps) return false;\n const supportedVersions = caps.supportedVersions ?? [];\n if (supportedVersions.length > 0) {\n return supportedVersions.some(v => !isPre31AdcpVersion(v));\n }\n const responseVersion = typeof caps._raw?.adcp_version === 'string' ? caps._raw.adcp_version : undefined;\n if (responseVersion !== undefined && !isPre31AdcpVersion(responseVersion)) return true;\n return false;\n}\n\n/**\n * Whether to omit AdCP 3.1-only request fields from the wire. Omit when the\n * client is pinned below 3.1, or when the seller does not advertise 3.1\n * support (legacy 3.0 sellers, and sellers whose capabilities were synthesized\n * from a tool list rather than declared).\n */\nexport function shouldOmit31Fields(\n resolvedClientVersion: string | undefined,\n caps: { supportedVersions?: string[]; buildVersion?: string; _raw?: Record<string, unknown> } | undefined\n): boolean {\n if (isPre31AdcpVersion(resolvedClientVersion)) return true;\n return !sellerAdvertises31(caps);\n}\n\n/**\n * Strip the AdCP 3.1-only `brand_kit_override` field from a BrandReference.\n * `industries` and `data_subject_contestation` are declared in AdCP 3.0 and\n * must be left on the wire for 3.0 sellers that accept and route on them.\n * Returns the original reference unchanged when `brand_kit_override` is absent.\n */\nexport function omit31BrandFields<T>(brand: T): T {\n if (!brand || typeof brand !== 'object' || Array.isArray(brand)) return brand;\n const b = brand as Record<string, unknown>;\n if (!('brand_kit_override' in b)) return brand;\n const { brand_kit_override, ...rest } = b;\n return rest as T;\n}\n\nfunction prereleaseFamilyAlias(version: string): string | undefined {\n const match = /^(\\d+\\.\\d+-[0-9A-Za-z-]+)\\.\\d+$/.exec(version);\n return match?.[1];\n}\n\n/**\n * Filter `COMPATIBLE_ADCP_VERSIONS` to entries with a shipped bundle on this\n * SDK build. The compatibility list is the historical set of accepted version\n * strings (legacy aliases, superseded betas, current GA), but only a subset\n * has bundles in `dist/lib/schemas-data/` at any given build. Returning\n * \"currently bundled: v2.5, v2.6, v3, 3.0.0-beta.3, 3.0.0, 3.0.1\" would\n * mislead a caller who pinned `'v2.5'` and got \"no bundle\".\n *\n * Always includes `ADCP_VERSION` even when its bundle hasn't been built yet\n * (dev-tree case) so the error remains actionable on first build.\n */\nexport function listBundledAdcpVersions(): string[] {\n const bundled = COMPATIBLE_ADCP_VERSIONS.filter(v => hasSchemaBundle(v));\n if (!bundled.includes(ADCP_VERSION as (typeof COMPATIBLE_ADCP_VERSIONS)[number])) {\n return [ADCP_VERSION, ...bundled];\n }\n return [...bundled];\n}\n"],"mappings":"AAcA,SAAS,cAAc,0BAA0B,6BAA6B;AAC9E,SAAS,0BAA0B;AACnC,SAAS,iBAAiB,kBAAkB,8BAA8B;AAC1E,SAAS,OAAO,WAAW,SAAS,mBAAmB;AAEvD,SAAS,qBAAqB,SAAqC;AACjE,QAAM,QAAQ,0EAA0E,KAAK,QAAQ,KAAK,CAAC;AAC3G,MAAI,CAAC,MAAO,QAAO;AACnB,QAAM,aAAa,GAAG,MAAM,CAAC,CAAC,IAAI,MAAM,CAAC,CAAC,IAAI,MAAM,CAAC,KAAK,GAAG,GAAG,MAAM,CAAC,KAAK,EAAE;AAC9E,SAAO,YAAY,UAAU,KAAK;AACpC;AAGO,SAAS,qBAAqB,SAA6B,SAA0B;AAC1F,MAAI,YAAY,OAAW,QAAO;AAClC,QAAM,aAAa,qBAAqB,OAAO;AAC/C,QAAM,oBAAoB,qBAAqB,OAAO;AACtD,SAAO,eAAe,UAAa,sBAAsB,UAAa,UAAU,YAAY,iBAAiB;AAC/G;AAeO,SAAS,mBAAmB,aAAyC;AAC1E,MAAI,gBAAgB,OAAW,QAAO;AAEtC,QAAM,0BAA0B,uBAAuB,YAAY;AACnE,MAAI,kCAAkC,WAAW,GAAG;AAClD,UAAM,IAAI;AAAA,MACR,eAAe,KAAK,UAAU,WAAW,CAAC,0EACJ,KAAK,UAAU,uBAAuB,CAAC;AAAA,MAC7E;AAAA,IACF;AAAA,EACF;AAEA,QAAM,QAAQ,sBAAsB,WAAW;AAC/C,MAAI,CAAC,OAAO,SAAS,KAAK,GAAG;AAC3B,UAAM,IAAI;AAAA,MACR,eAAe,KAAK,UAAU,WAAW,CAAC,+HAElB,wBAAwB,EAAE,KAAK,IAAI,CAAC;AAAA,MAC5D;AAAA,IACF;AAAA,EACF;AAQA,MAAI,iBAAiB,WAAW,MAAM,iBAAiB,YAAY,EAAG,QAAO;AAE7E,MAAI,CAAC,gBAAgB,WAAW,GAAG;AACjC,UAAM,cAAc,iBAAiB,WAAW;AAChD,UAAM,IAAI;AAAA,MACR,eAAe,KAAK,UAAU,WAAW,CAAC,4BAA4B,WAAW,sFAEzD,wBAAwB,EAAE,KAAK,IAAI,CAAC;AAAA,MAI5D;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;AAGO,SAAS,kCAAkC,SAA0B;AAC1E,MAAI,CAAC,aAAa,WAAW,QAAQ,EAAG,QAAO;AAC/C,SAAO,sBAAsB,uBAAuB,YAAY,CAAC,MAAM;AACzE;AAEO,SAAS,mBAAmB,SAAiB,qBAAqB,MAAmB;AAC1F,QAAM,UAAU,oBAAI,IAAY,CAAC,OAAO,CAAC;AACzC,QAAM,MAAM,CAAC,UAAkB;AAC7B,YAAQ,IAAI,KAAK;AACjB,QAAI,oBAAoB;AACtB,YAAM,SAAS,sBAAsB,KAAK;AAC1C,UAAI,UAAU,EAAE,aAAa,WAAW,QAAQ,KAAK,OAAO,WAAW,MAAM,GAAI,SAAQ,IAAI,MAAM;AAAA,IACrG;AAAA,EACF;AAEA,MAAI;AACF,QAAI,iBAAiB,OAAO,CAAC;AAAA,EAC/B,QAAQ;AAAA,EAER;AACA,MAAI;AACF,QAAI,uBAAuB,OAAO,CAAC;AAAA,EACrC,QAAQ;AAAA,EAER;AACA,SAAO;AACT;AAEO,SAAS,uBAAuB,SAAiB,mBAA+C;AACrG,MAAI,kBAAkB,WAAW,EAAG,QAAO;AAC3C,QAAM,UAAU,mBAAmB,OAAO;AAC1C,SAAO,kBAAkB,KAAK,OAAK;AACjC,eAAW,kBAAkB,mBAAmB,GAAG,KAAK,GAAG;AACzD,UAAI,QAAQ,IAAI,cAAc,EAAG,QAAO;AAAA,IAC1C;AACA,WAAO;AAAA,EACT,CAAC;AACH;AAEO,SAAS,mBAAmB,SAAsC;AACvE,MAAI,YAAY,OAAW,QAAO;AAClC,QAAM,UAAU,QAAQ,KAAK;AAC7B,MAAI,QAAQ,WAAW,EAAG,QAAO;AAEjC,QAAM,sBAAsB,QAAQ,WAAW,GAAG,IAAI,QAAQ,MAAM,CAAC,IAAI;AACzE,QAAM,QAAQ,qBAAqB,KAAK,mBAAmB;AAC3D,MAAI,CAAC,QAAQ,CAAC,EAAG,QAAO;AAExB,QAAM,QAAQ,OAAO,SAAS,MAAM,CAAC,GAAG,EAAE;AAC1C,MAAI,CAAC,OAAO,SAAS,KAAK,EAAG,QAAO;AACpC,MAAI,QAAQ,EAAG,QAAO;AACtB,MAAI,QAAQ,EAAG,QAAO;AAEtB,QAAM,QAAQ,MAAM,CAAC,MAAM,SAAY,IAAI,OAAO,SAAS,MAAM,CAAC,GAAG,EAAE;AACvE,SAAO,OAAO,SAAS,KAAK,KAAK,QAAQ;AAC3C;AAGO,SAAS,mBAAmB,SAAsC;AACvE,MAAI,YAAY,OAAW,QAAO;AAClC,QAAM,UAAU,QAAQ,KAAK;AAC7B,MAAI,QAAQ,WAAW,EAAG,QAAO;AAEjC,QAAM,sBAAsB,QAAQ,WAAW,GAAG,IAAI,QAAQ,MAAM,CAAC,IAAI;AACzE,QAAM,QAAQ,qBAAqB,KAAK,mBAAmB;AAC3D,MAAI,CAAC,QAAQ,CAAC,EAAG,QAAO;AAExB,QAAM,QAAQ,OAAO,SAAS,MAAM,CAAC,GAAG,EAAE;AAC1C,MAAI,CAAC,OAAO,SAAS,KAAK,EAAG,QAAO;AACpC,MAAI,QAAQ,EAAG,QAAO;AACtB,MAAI,QAAQ,EAAG,QAAO;AAEtB,QAAM,QAAQ,MAAM,CAAC,MAAM,SAAY,IAAI,OAAO,SAAS,MAAM,CAAC,GAAG,EAAE;AACvE,SAAO,OAAO,SAAS,KAAK,KAAK,QAAQ;AAC3C;AASO,SAAS,mBACd,MACS;AACT,MAAI,CAAC,KAAM,QAAO;AAClB,QAAM,oBAAoB,KAAK,qBAAqB,CAAC;AACrD,MAAI,kBAAkB,SAAS,GAAG;AAChC,WAAO,kBAAkB,KAAK,OAAK,CAAC,mBAAmB,CAAC,CAAC;AAAA,EAC3D;AACA,QAAM,kBAAkB,OAAO,KAAK,MAAM,iBAAiB,WAAW,KAAK,KAAK,eAAe;AAC/F,MAAI,oBAAoB,UAAa,CAAC,mBAAmB,eAAe,EAAG,QAAO;AAClF,SAAO;AACT;AAQO,SAAS,mBACd,uBACA,MACS;AACT,MAAI,mBAAmB,qBAAqB,EAAG,QAAO;AACtD,SAAO,CAAC,mBAAmB,IAAI;AACjC;AAQO,SAAS,kBAAqB,OAAa;AAChD,MAAI,CAAC,SAAS,OAAO,UAAU,YAAY,MAAM,QAAQ,KAAK,EAAG,QAAO;AACxE,QAAM,IAAI;AACV,MAAI,EAAE,wBAAwB,GAAI,QAAO;AACzC,QAAM,EAAE,oBAAoB,GAAG,KAAK,IAAI;AACxC,SAAO;AACT;AAEA,SAAS,sBAAsB,SAAqC;AAClE,QAAM,QAAQ,kCAAkC,KAAK,OAAO;AAC5D,SAAO,QAAQ,CAAC;AAClB;AAaO,SAAS,0BAAoC;AAClD,QAAM,UAAU,yBAAyB,OAAO,OAAK,gBAAgB,CAAC,CAAC;AACvE,MAAI,CAAC,QAAQ,SAAS,YAAyD,GAAG;AAChF,WAAO,CAAC,cAAc,GAAG,OAAO;AAAA,EAClC;AACA,SAAO,CAAC,GAAG,OAAO;AACpB;","names":[]}
@@ -124,6 +124,8 @@ export declare function adaptGetProductsRequestForV2(request: any): any;
124
124
  * v2 used coarser buckets; some map one-to-many (e.g. 'video' → ['olv', 'ctv']).
125
125
  */
126
126
  export declare function normalizeProductChannels(product: any): any;
127
+ /** Normalize optional forecast timestamps at the v2.5 compatibility boundary. */
128
+ export declare function normalizeLegacyProductForecast(product: any): any;
127
129
  /**
128
130
  * Normalize all products in a get_products response to v3
129
131
  */
@@ -124,6 +124,8 @@ export declare function adaptGetProductsRequestForV2(request: any): any;
124
124
  * v2 used coarser buckets; some map one-to-many (e.g. 'video' → ['olv', 'ctv']).
125
125
  */
126
126
  export declare function normalizeProductChannels(product: any): any;
127
+ /** Normalize optional forecast timestamps at the v2.5 compatibility boundary. */
128
+ export declare function normalizeLegacyProductForecast(product: any): any;
127
129
  /**
128
130
  * Normalize all products in a get_products response to v3
129
131
  */
@@ -1 +1 @@
1
- {"version":3,"file":"pricing-adapter.d.ts","sourceRoot":"","sources":["../../../src/lib/utils/pricing-adapter.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"pricing-adapter.d.ts","sourceRoot":"","sources":["../../../src/lib/utils/pricing-adapter.ts"],"names":[],"mappings":"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"}
@@ -28,6 +28,7 @@ __export(pricing_adapter_exports, {
28
28
  isCPAPricing: () => isCPAPricing,
29
29
  isFixedPricing: () => isFixedPricing,
30
30
  normalizeGetProductsResponse: () => normalizeGetProductsResponse,
31
+ normalizeLegacyProductForecast: () => normalizeLegacyProductForecast,
31
32
  normalizeMediaBuyPricingResponse: () => normalizeMediaBuyPricingResponse,
32
33
  normalizePricingOption: () => normalizePricingOption,
33
34
  normalizeProductChannels: () => normalizeProductChannels,
@@ -203,6 +204,69 @@ function normalizeProductChannels(product) {
203
204
  const normalized = product.channels.flatMap((ch) => v3ChannelMap[ch] ?? [ch]);
204
205
  return { ...product, channels: [...new Set(normalized)] };
205
206
  }
207
+ const LEGACY_ZONED_TIMESTAMP = /^(\d{4})-(\d{2})-(\d{2})[Tt ](\d{2}):(\d{2}):(\d{2})(\.\d+)?(?:[Zz]|\s*UTC|([+-])(\d{2})(?::?(\d{2}))?)$/i;
208
+ function normalizeLegacyZonedTimestamp(value) {
209
+ let candidate = value;
210
+ if (candidate && typeof candidate === "object" && !Array.isArray(candidate)) {
211
+ const entries = Object.entries(candidate);
212
+ if (entries.length === 0) return void 0;
213
+ if (entries.length !== 1 || entries[0][0] !== "$date" && entries[0][0] !== "value") return value;
214
+ candidate = entries[0][1];
215
+ }
216
+ if (candidate === null) return void 0;
217
+ if (candidate && typeof candidate === "object" && !Array.isArray(candidate)) {
218
+ return Object.keys(candidate).length === 0 ? void 0 : value;
219
+ }
220
+ if (typeof candidate !== "string") return value;
221
+ const match = LEGACY_ZONED_TIMESTAMP.exec(candidate.trim());
222
+ if (!match) return value;
223
+ const [
224
+ ,
225
+ yearText,
226
+ monthText,
227
+ dayText,
228
+ hourText,
229
+ minuteText,
230
+ secondText,
231
+ fraction = "",
232
+ sign,
233
+ offsetHourText,
234
+ offsetMinuteText
235
+ ] = match;
236
+ const year = Number(yearText);
237
+ const month = Number(monthText);
238
+ const day = Number(dayText);
239
+ const hour = Number(hourText);
240
+ const minute = Number(minuteText);
241
+ const second = Number(secondText);
242
+ const offsetHours = offsetHourText === void 0 ? 0 : Number(offsetHourText);
243
+ const offsetMinutes = offsetMinuteText === void 0 ? 0 : Number(offsetMinuteText);
244
+ if (sign === "-" && offsetHours === 0 && offsetMinutes === 0) return value;
245
+ if (month < 1 || month > 12 || day < 1 || day > 31 || hour > 23 || minute > 59 || second > 59 || offsetHours > 23 || offsetMinutes > 59) {
246
+ return value;
247
+ }
248
+ const local = /* @__PURE__ */ new Date(0);
249
+ local.setUTCFullYear(year, month - 1, day);
250
+ local.setUTCHours(hour, minute, second, 0);
251
+ if (local.getUTCFullYear() !== year || local.getUTCMonth() !== month - 1 || local.getUTCDate() !== day || local.getUTCHours() !== hour || local.getUTCMinutes() !== minute || local.getUTCSeconds() !== second) {
252
+ return value;
253
+ }
254
+ const offset = (offsetHours * 60 + offsetMinutes) * 6e4 * (sign === "-" ? -1 : 1);
255
+ const utc = new Date(local.getTime() - offset);
256
+ if (utc.getUTCFullYear() < 0 || utc.getUTCFullYear() > 9999) return value;
257
+ return `${utc.toISOString().slice(0, 19)}${fraction}Z`;
258
+ }
259
+ function normalizeLegacyProductForecast(product) {
260
+ if (!product?.forecast || typeof product.forecast !== "object" || Array.isArray(product.forecast)) return product;
261
+ const forecast = { ...product.forecast };
262
+ for (const field of ["generated_at", "valid_until"]) {
263
+ if (!(field in forecast)) continue;
264
+ const normalized = normalizeLegacyZonedTimestamp(forecast[field]);
265
+ if (normalized === void 0) delete forecast[field];
266
+ else forecast[field] = normalized;
267
+ }
268
+ return { ...product, forecast };
269
+ }
206
270
  function normalizeGetProductsResponse(response) {
207
271
  if (!("products" in (response ?? {}))) {
208
272
  return response;
@@ -219,7 +283,9 @@ function normalizeGetProductsResponse(response) {
219
283
  }
220
284
  return {
221
285
  ...response,
222
- products: response.products.map((p) => normalizeProductChannels(normalizeProductPricing(p)))
286
+ products: response.products.map(
287
+ (p) => normalizeLegacyProductForecast(normalizeProductChannels(normalizeProductPricing(p)))
288
+ )
223
289
  };
224
290
  }
225
291
  function adaptPackagePricingForV2(pkg) {
@@ -249,6 +315,7 @@ function normalizeMediaBuyPricingResponse(response) {
249
315
  isCPAPricing,
250
316
  isFixedPricing,
251
317
  normalizeGetProductsResponse,
318
+ normalizeLegacyProductForecast,
252
319
  normalizeMediaBuyPricingResponse,
253
320
  normalizePricingOption,
254
321
  normalizeProductChannels,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/lib/utils/pricing-adapter.ts"],"sourcesContent":["/**\n * Pricing Option Adapter\n *\n * Handles conversion between v2.x and v3 pricing option field names.\n * The library exposes only v3 API (fixed_price, floor_price) to clients,\n * and internally adapts requests for v2.x servers.\n *\n * Field mappings:\n * - v3 fixed_price ↔ v2 rate (for fixed pricing)\n * - v3 floor_price ↔ v2 price_guidance.floor (for auction pricing)\n * - v3 presence of fixed_price ↔ v2 is_fixed: true/false\n */\n\n/**\n * v3 price guidance (percentiles only, floor moved to top level)\n */\nexport interface PriceGuidanceV3 {\n p25?: number;\n p50?: number;\n p75?: number;\n p90?: number;\n}\n\n/**\n * v2 price guidance (includes floor)\n */\nexport interface PriceGuidanceV2 {\n floor?: number;\n p25?: number;\n p50?: number;\n p75?: number;\n p90?: number;\n}\n\n/**\n * v3 pricing option structure\n */\nexport interface PricingOptionV3 {\n pricing_option_id: string;\n pricing_model: string;\n currency: string;\n /** Fixed price - if present, this is fixed pricing */\n fixed_price?: number;\n /** Floor price for auction - mutually exclusive with fixed_price */\n floor_price?: number;\n /** Percentile hints for auction bidding */\n price_guidance?: PriceGuidanceV3;\n min_spend_per_package?: number;\n parameters?: Record<string, unknown>;\n [key: string]: unknown;\n}\n\n/**\n * v2 pricing option structure\n */\nexport interface PricingOptionV2 {\n pricing_option_id: string;\n pricing_model: string;\n currency: string;\n /** Fixed rate (v2 name for fixed_price) */\n rate?: number;\n /** Boolean discriminator for fixed vs auction */\n is_fixed?: boolean;\n /** Price guidance including floor */\n price_guidance?: PriceGuidanceV2;\n min_spend_per_package?: number;\n parameters?: Record<string, unknown>;\n [key: string]: unknown;\n}\n\n/**\n * Check if a pricing option uses v2 field names\n */\nexport function usesV2PricingFields(option: any): boolean {\n if (!option || typeof option !== 'object') return false;\n // v2 uses 'rate' or 'is_fixed' or has floor inside price_guidance\n return option.rate !== undefined || option.is_fixed !== undefined || option.price_guidance?.floor !== undefined;\n}\n\n/**\n * Check if a pricing option uses v3 field names\n */\nexport function usesV3PricingFields(option: any): boolean {\n if (!option || typeof option !== 'object') return false;\n // v3 uses fixed_price or floor_price at top level\n return option.fixed_price !== undefined || option.floor_price !== undefined;\n}\n\n/**\n * Detect if a pricing option is fixed pricing (works with v2 or v3)\n */\nexport function isFixedPricing(option: PricingOptionV2 | PricingOptionV3): boolean {\n // v3: presence of fixed_price indicates fixed pricing\n if ((option as PricingOptionV3).fixed_price !== undefined) {\n return true;\n }\n // v2: is_fixed boolean\n if ((option as PricingOptionV2).is_fixed !== undefined) {\n return (option as PricingOptionV2).is_fixed!;\n }\n // v2: presence of rate without is_fixed defaults to fixed\n if ((option as PricingOptionV2).rate !== undefined) {\n return true;\n }\n return false;\n}\n\n/**\n * Detect if a pricing option is CPA (cost per acquisition) pricing\n */\nexport function isCPAPricing(option: PricingOptionV2 | PricingOptionV3): boolean {\n return option?.pricing_model === 'cpa';\n}\n\n/**\n * Get the price value from a pricing option (works with v2 or v3)\n */\nexport function getPrice(option: PricingOptionV2 | PricingOptionV3): number | undefined {\n // v3 field takes precedence\n if ((option as PricingOptionV3).fixed_price !== undefined) {\n return (option as PricingOptionV3).fixed_price;\n }\n // Fall back to v2 field\n return (option as PricingOptionV2).rate;\n}\n\n/**\n * Get the floor price from a pricing option (works with v2 or v3)\n */\nexport function getFloorPrice(option: PricingOptionV2 | PricingOptionV3): number | undefined {\n // v3 field takes precedence\n if ((option as PricingOptionV3).floor_price !== undefined) {\n return (option as PricingOptionV3).floor_price;\n }\n // Fall back to v2 field (inside price_guidance)\n return (option as PricingOptionV2).price_guidance?.floor;\n}\n\n/**\n * Adapt a v3-style pricing option for a v2 server.\n * Converts fixed_price → rate, floor_price → price_guidance.floor\n */\nexport function adaptPricingOptionForV2(option: PricingOptionV3): PricingOptionV2 {\n // Already v2 format or no v3 fields\n if (!usesV3PricingFields(option)) {\n return option as unknown as PricingOptionV2;\n }\n\n const { fixed_price, floor_price, price_guidance, ...rest } = option;\n\n const result: PricingOptionV2 = { ...rest } as PricingOptionV2;\n\n if (fixed_price !== undefined) {\n // Fixed pricing\n result.rate = fixed_price;\n result.is_fixed = true;\n } else {\n // Auction pricing\n result.is_fixed = false;\n\n // Build v2 price_guidance with floor inside\n if (floor_price !== undefined || price_guidance) {\n result.price_guidance = {\n ...(price_guidance || {}),\n };\n if (floor_price !== undefined) {\n result.price_guidance.floor = floor_price;\n }\n }\n }\n\n return result;\n}\n\n/**\n * Normalize a v2-style pricing option to v3.\n * Converts rate → fixed_price, price_guidance.floor → floor_price\n */\nexport function normalizePricingOption(option: PricingOptionV2 | PricingOptionV3): PricingOptionV3 {\n // Already v3 format\n if (usesV3PricingFields(option) && !usesV2PricingFields(option)) {\n return option as PricingOptionV3;\n }\n\n const v2Option = option as PricingOptionV2;\n const { rate, is_fixed, price_guidance, ...rest } = v2Option;\n\n const result: PricingOptionV3 = { ...rest } as PricingOptionV3;\n\n // Convert rate → fixed_price (for fixed pricing)\n if (rate !== undefined && (is_fixed === true || is_fixed === undefined)) {\n result.fixed_price = rate;\n }\n\n // Convert price_guidance.floor → floor_price (for auction pricing)\n if (price_guidance) {\n const { floor, ...percentiles } = price_guidance;\n\n if (floor !== undefined) {\n result.floor_price = floor;\n }\n\n // Keep percentiles in price_guidance if any exist\n if (Object.keys(percentiles).length > 0) {\n result.price_guidance = percentiles;\n }\n }\n\n return result;\n}\n\n/**\n * Adapt all pricing options in a product for v2 server\n */\nexport function adaptProductPricingForV2(product: any): any {\n if (!product?.pricing_options || !Array.isArray(product.pricing_options)) {\n return product;\n }\n\n return {\n ...product,\n pricing_options: product.pricing_options.map(adaptPricingOptionForV2),\n };\n}\n\n/**\n * Normalize all pricing options in a product to v3\n */\nexport function normalizeProductPricing(product: any): any {\n if (!product?.pricing_options || !Array.isArray(product.pricing_options)) {\n return product;\n }\n\n return {\n ...product,\n pricing_options: product.pricing_options.map(normalizePricingOption),\n };\n}\n\n/**\n * Adapt a get_products request for a v2 server.\n *\n * Converts v3 fields to their v2 equivalents:\n * - brand (BrandReference) → brand_manifest (base domain URL string)\n * - catalog → promoted_offerings (type='offering') or promoted_offerings.product_selectors (type='product')\n * - channels in filters → v2 channel names\n *\n * Strips v3-only fields v2 agents don't understand:\n * - buying_mode, buyer_campaign_ref, property_list, account_id, pagination (top-level)\n * - filters: required_features, required_axe_integrations, required_geo_targeting,\n * signal_targeting, regions, metros\n */\nexport function adaptGetProductsRequestForV2(request: any): any {\n const adapted: any = { ...request };\n\n // Convert v3 brand (BrandReference) → v2 brand_manifest (bare domain URL).\n // v2 servers resolve brand info from the base domain URL.\n if (adapted.brand?.domain) {\n adapted.brand_manifest = `https://${adapted.brand.domain}`;\n delete adapted.brand;\n }\n\n // Convert v3 catalog → v2 promoted_offerings\n if (adapted.catalog) {\n const catalog = adapted.catalog;\n if (catalog.type === 'product') {\n adapted.promoted_offerings = {\n product_selectors: {\n ...(catalog.gtins?.length && { manifest_gtins: catalog.gtins }),\n ...(catalog.ids?.length && { manifest_skus: catalog.ids }),\n ...(catalog.tags?.length && { manifest_tags: catalog.tags }),\n ...(catalog.category && { manifest_category: catalog.category }),\n ...(catalog.query && { manifest_query: catalog.query }),\n },\n };\n } else if (catalog.type === 'offering') {\n adapted.promoted_offerings = {\n ...(catalog.items?.length && { offerings: catalog.items }),\n };\n }\n delete adapted.catalog;\n }\n\n // Map v3 channel names to v2 equivalents\n if (adapted.filters?.channels) {\n const v2ChannelMap: Record<string, string> = {\n olv: 'video',\n ctv: 'video',\n streaming_audio: 'audio',\n retail_media: 'retail',\n };\n // Deduplicate after mapping (e.g. olv+ctv both become 'video')\n const mapped = adapted.filters.channels.map((ch: string) => v2ChannelMap[ch] ?? ch);\n adapted.filters = {\n ...adapted.filters,\n channels: [...new Set(mapped)],\n };\n }\n\n // Strip v3-only filter fields (format_ids exists in both v2 and v3, keep it)\n if (adapted.filters) {\n const {\n required_features,\n required_axe_integrations,\n required_geo_targeting,\n signal_targeting,\n regions,\n metros,\n ...v2Filters\n } = adapted.filters;\n adapted.filters = v2Filters;\n }\n\n // Strip v3-only top-level fields\n delete adapted.buying_mode;\n delete adapted.buyer_campaign_ref;\n delete adapted.property_list;\n delete adapted.account;\n delete adapted.pagination;\n delete adapted.adcp_major_version;\n\n return adapted;\n}\n\n/**\n * Normalize v2 channel names on a product to v3.\n * v2 used coarser buckets; some map one-to-many (e.g. 'video' → ['olv', 'ctv']).\n */\nexport function normalizeProductChannels(product: any): any {\n if (!product?.channels || !Array.isArray(product.channels)) {\n return product;\n }\n\n const v3ChannelMap: Record<string, string[]> = {\n video: ['olv', 'ctv'],\n audio: ['streaming_audio'],\n native: ['display'],\n retail: ['retail_media'],\n };\n\n const normalized = product.channels.flatMap((ch: string) => v3ChannelMap[ch] ?? [ch]);\n return { ...product, channels: [...new Set(normalized)] };\n}\n\n/**\n * Normalize all products in a get_products response to v3\n */\nexport function normalizeGetProductsResponse(response: any): any {\n // If there's no products field at all, this may be an error response — pass through\n if (!('products' in (response ?? {}))) {\n return response;\n }\n\n // If products exists but isn't an array, the response is malformed\n if (!Array.isArray(response.products)) {\n return {\n errors: [\n {\n code: 'invalid_response',\n message: `Expected products to be an array, got ${typeof response.products}`,\n },\n ],\n };\n }\n\n return {\n ...response,\n products: response.products.map((p: any) => normalizeProductChannels(normalizeProductPricing(p))),\n };\n}\n\n/**\n * Adapt a package request's pricing option selection for v2 server.\n * This handles bid_price which references pricing option floor semantics.\n */\nexport function adaptPackagePricingForV2(pkg: any): any {\n // Package requests don't typically contain pricing_options,\n // but they do contain pricing_option_id which selects one.\n // No transformation needed for bid_price - it's the same in both versions.\n return pkg;\n}\n\n/**\n * Adapt a create_media_buy request for v2 server (pricing fields)\n */\nexport function adaptMediaBuyPricingForV2(request: any): any {\n // Media buy requests select pricing options by ID, not by defining them.\n // No pricing field transformation needed in requests.\n return request;\n}\n\n/**\n * Normalize a media buy response's package pricing to v3\n */\nexport function normalizeMediaBuyPricingResponse(response: any): any {\n // Media buy responses may include products with pricing_options\n if (response?.products && Array.isArray(response.products)) {\n return {\n ...response,\n products: response.products.map(normalizeProductPricing),\n };\n }\n return response;\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../../src/lib/utils/pricing-adapter.ts"],"sourcesContent":["/**\n * Pricing Option Adapter\n *\n * Handles conversion between v2.x and v3 pricing option field names.\n * The library exposes only v3 API (fixed_price, floor_price) to clients,\n * and internally adapts requests for v2.x servers.\n *\n * Field mappings:\n * - v3 fixed_price ↔ v2 rate (for fixed pricing)\n * - v3 floor_price ↔ v2 price_guidance.floor (for auction pricing)\n * - v3 presence of fixed_price ↔ v2 is_fixed: true/false\n */\n\n/**\n * v3 price guidance (percentiles only, floor moved to top level)\n */\nexport interface PriceGuidanceV3 {\n p25?: number;\n p50?: number;\n p75?: number;\n p90?: number;\n}\n\n/**\n * v2 price guidance (includes floor)\n */\nexport interface PriceGuidanceV2 {\n floor?: number;\n p25?: number;\n p50?: number;\n p75?: number;\n p90?: number;\n}\n\n/**\n * v3 pricing option structure\n */\nexport interface PricingOptionV3 {\n pricing_option_id: string;\n pricing_model: string;\n currency: string;\n /** Fixed price - if present, this is fixed pricing */\n fixed_price?: number;\n /** Floor price for auction - mutually exclusive with fixed_price */\n floor_price?: number;\n /** Percentile hints for auction bidding */\n price_guidance?: PriceGuidanceV3;\n min_spend_per_package?: number;\n parameters?: Record<string, unknown>;\n [key: string]: unknown;\n}\n\n/**\n * v2 pricing option structure\n */\nexport interface PricingOptionV2 {\n pricing_option_id: string;\n pricing_model: string;\n currency: string;\n /** Fixed rate (v2 name for fixed_price) */\n rate?: number;\n /** Boolean discriminator for fixed vs auction */\n is_fixed?: boolean;\n /** Price guidance including floor */\n price_guidance?: PriceGuidanceV2;\n min_spend_per_package?: number;\n parameters?: Record<string, unknown>;\n [key: string]: unknown;\n}\n\n/**\n * Check if a pricing option uses v2 field names\n */\nexport function usesV2PricingFields(option: any): boolean {\n if (!option || typeof option !== 'object') return false;\n // v2 uses 'rate' or 'is_fixed' or has floor inside price_guidance\n return option.rate !== undefined || option.is_fixed !== undefined || option.price_guidance?.floor !== undefined;\n}\n\n/**\n * Check if a pricing option uses v3 field names\n */\nexport function usesV3PricingFields(option: any): boolean {\n if (!option || typeof option !== 'object') return false;\n // v3 uses fixed_price or floor_price at top level\n return option.fixed_price !== undefined || option.floor_price !== undefined;\n}\n\n/**\n * Detect if a pricing option is fixed pricing (works with v2 or v3)\n */\nexport function isFixedPricing(option: PricingOptionV2 | PricingOptionV3): boolean {\n // v3: presence of fixed_price indicates fixed pricing\n if ((option as PricingOptionV3).fixed_price !== undefined) {\n return true;\n }\n // v2: is_fixed boolean\n if ((option as PricingOptionV2).is_fixed !== undefined) {\n return (option as PricingOptionV2).is_fixed!;\n }\n // v2: presence of rate without is_fixed defaults to fixed\n if ((option as PricingOptionV2).rate !== undefined) {\n return true;\n }\n return false;\n}\n\n/**\n * Detect if a pricing option is CPA (cost per acquisition) pricing\n */\nexport function isCPAPricing(option: PricingOptionV2 | PricingOptionV3): boolean {\n return option?.pricing_model === 'cpa';\n}\n\n/**\n * Get the price value from a pricing option (works with v2 or v3)\n */\nexport function getPrice(option: PricingOptionV2 | PricingOptionV3): number | undefined {\n // v3 field takes precedence\n if ((option as PricingOptionV3).fixed_price !== undefined) {\n return (option as PricingOptionV3).fixed_price;\n }\n // Fall back to v2 field\n return (option as PricingOptionV2).rate;\n}\n\n/**\n * Get the floor price from a pricing option (works with v2 or v3)\n */\nexport function getFloorPrice(option: PricingOptionV2 | PricingOptionV3): number | undefined {\n // v3 field takes precedence\n if ((option as PricingOptionV3).floor_price !== undefined) {\n return (option as PricingOptionV3).floor_price;\n }\n // Fall back to v2 field (inside price_guidance)\n return (option as PricingOptionV2).price_guidance?.floor;\n}\n\n/**\n * Adapt a v3-style pricing option for a v2 server.\n * Converts fixed_price → rate, floor_price → price_guidance.floor\n */\nexport function adaptPricingOptionForV2(option: PricingOptionV3): PricingOptionV2 {\n // Already v2 format or no v3 fields\n if (!usesV3PricingFields(option)) {\n return option as unknown as PricingOptionV2;\n }\n\n const { fixed_price, floor_price, price_guidance, ...rest } = option;\n\n const result: PricingOptionV2 = { ...rest } as PricingOptionV2;\n\n if (fixed_price !== undefined) {\n // Fixed pricing\n result.rate = fixed_price;\n result.is_fixed = true;\n } else {\n // Auction pricing\n result.is_fixed = false;\n\n // Build v2 price_guidance with floor inside\n if (floor_price !== undefined || price_guidance) {\n result.price_guidance = {\n ...(price_guidance || {}),\n };\n if (floor_price !== undefined) {\n result.price_guidance.floor = floor_price;\n }\n }\n }\n\n return result;\n}\n\n/**\n * Normalize a v2-style pricing option to v3.\n * Converts rate → fixed_price, price_guidance.floor → floor_price\n */\nexport function normalizePricingOption(option: PricingOptionV2 | PricingOptionV3): PricingOptionV3 {\n // Already v3 format\n if (usesV3PricingFields(option) && !usesV2PricingFields(option)) {\n return option as PricingOptionV3;\n }\n\n const v2Option = option as PricingOptionV2;\n const { rate, is_fixed, price_guidance, ...rest } = v2Option;\n\n const result: PricingOptionV3 = { ...rest } as PricingOptionV3;\n\n // Convert rate → fixed_price (for fixed pricing)\n if (rate !== undefined && (is_fixed === true || is_fixed === undefined)) {\n result.fixed_price = rate;\n }\n\n // Convert price_guidance.floor → floor_price (for auction pricing)\n if (price_guidance) {\n const { floor, ...percentiles } = price_guidance;\n\n if (floor !== undefined) {\n result.floor_price = floor;\n }\n\n // Keep percentiles in price_guidance if any exist\n if (Object.keys(percentiles).length > 0) {\n result.price_guidance = percentiles;\n }\n }\n\n return result;\n}\n\n/**\n * Adapt all pricing options in a product for v2 server\n */\nexport function adaptProductPricingForV2(product: any): any {\n if (!product?.pricing_options || !Array.isArray(product.pricing_options)) {\n return product;\n }\n\n return {\n ...product,\n pricing_options: product.pricing_options.map(adaptPricingOptionForV2),\n };\n}\n\n/**\n * Normalize all pricing options in a product to v3\n */\nexport function normalizeProductPricing(product: any): any {\n if (!product?.pricing_options || !Array.isArray(product.pricing_options)) {\n return product;\n }\n\n return {\n ...product,\n pricing_options: product.pricing_options.map(normalizePricingOption),\n };\n}\n\n/**\n * Adapt a get_products request for a v2 server.\n *\n * Converts v3 fields to their v2 equivalents:\n * - brand (BrandReference) → brand_manifest (base domain URL string)\n * - catalog → promoted_offerings (type='offering') or promoted_offerings.product_selectors (type='product')\n * - channels in filters → v2 channel names\n *\n * Strips v3-only fields v2 agents don't understand:\n * - buying_mode, buyer_campaign_ref, property_list, account_id, pagination (top-level)\n * - filters: required_features, required_axe_integrations, required_geo_targeting,\n * signal_targeting, regions, metros\n */\nexport function adaptGetProductsRequestForV2(request: any): any {\n const adapted: any = { ...request };\n\n // Convert v3 brand (BrandReference) → v2 brand_manifest (bare domain URL).\n // v2 servers resolve brand info from the base domain URL.\n if (adapted.brand?.domain) {\n adapted.brand_manifest = `https://${adapted.brand.domain}`;\n delete adapted.brand;\n }\n\n // Convert v3 catalog → v2 promoted_offerings\n if (adapted.catalog) {\n const catalog = adapted.catalog;\n if (catalog.type === 'product') {\n adapted.promoted_offerings = {\n product_selectors: {\n ...(catalog.gtins?.length && { manifest_gtins: catalog.gtins }),\n ...(catalog.ids?.length && { manifest_skus: catalog.ids }),\n ...(catalog.tags?.length && { manifest_tags: catalog.tags }),\n ...(catalog.category && { manifest_category: catalog.category }),\n ...(catalog.query && { manifest_query: catalog.query }),\n },\n };\n } else if (catalog.type === 'offering') {\n adapted.promoted_offerings = {\n ...(catalog.items?.length && { offerings: catalog.items }),\n };\n }\n delete adapted.catalog;\n }\n\n // Map v3 channel names to v2 equivalents\n if (adapted.filters?.channels) {\n const v2ChannelMap: Record<string, string> = {\n olv: 'video',\n ctv: 'video',\n streaming_audio: 'audio',\n retail_media: 'retail',\n };\n // Deduplicate after mapping (e.g. olv+ctv both become 'video')\n const mapped = adapted.filters.channels.map((ch: string) => v2ChannelMap[ch] ?? ch);\n adapted.filters = {\n ...adapted.filters,\n channels: [...new Set(mapped)],\n };\n }\n\n // Strip v3-only filter fields (format_ids exists in both v2 and v3, keep it)\n if (adapted.filters) {\n const {\n required_features,\n required_axe_integrations,\n required_geo_targeting,\n signal_targeting,\n regions,\n metros,\n ...v2Filters\n } = adapted.filters;\n adapted.filters = v2Filters;\n }\n\n // Strip v3-only top-level fields\n delete adapted.buying_mode;\n delete adapted.buyer_campaign_ref;\n delete adapted.property_list;\n delete adapted.account;\n delete adapted.pagination;\n delete adapted.adcp_major_version;\n\n return adapted;\n}\n\n/**\n * Normalize v2 channel names on a product to v3.\n * v2 used coarser buckets; some map one-to-many (e.g. 'video' → ['olv', 'ctv']).\n */\nexport function normalizeProductChannels(product: any): any {\n if (!product?.channels || !Array.isArray(product.channels)) {\n return product;\n }\n\n const v3ChannelMap: Record<string, string[]> = {\n video: ['olv', 'ctv'],\n audio: ['streaming_audio'],\n native: ['display'],\n retail: ['retail_media'],\n };\n\n const normalized = product.channels.flatMap((ch: string) => v3ChannelMap[ch] ?? [ch]);\n return { ...product, channels: [...new Set(normalized)] };\n}\n\nconst LEGACY_ZONED_TIMESTAMP =\n /^(\\d{4})-(\\d{2})-(\\d{2})[Tt ](\\d{2}):(\\d{2}):(\\d{2})(\\.\\d+)?(?:[Zz]|\\s*UTC|([+-])(\\d{2})(?::?(\\d{2}))?)$/i;\n\nfunction normalizeLegacyZonedTimestamp(value: unknown): unknown {\n let candidate = value;\n if (candidate && typeof candidate === 'object' && !Array.isArray(candidate)) {\n const entries = Object.entries(candidate as Record<string, unknown>);\n if (entries.length === 0) return undefined;\n if (entries.length !== 1 || (entries[0]![0] !== '$date' && entries[0]![0] !== 'value')) return value;\n candidate = entries[0]![1];\n }\n if (candidate === null) return undefined;\n if (candidate && typeof candidate === 'object' && !Array.isArray(candidate)) {\n return Object.keys(candidate as Record<string, unknown>).length === 0 ? undefined : value;\n }\n if (typeof candidate !== 'string') return value;\n\n const match = LEGACY_ZONED_TIMESTAMP.exec(candidate.trim());\n if (!match) return value;\n const [\n ,\n yearText,\n monthText,\n dayText,\n hourText,\n minuteText,\n secondText,\n fraction = '',\n sign,\n offsetHourText,\n offsetMinuteText,\n ] = match;\n const year = Number(yearText);\n const month = Number(monthText);\n const day = Number(dayText);\n const hour = Number(hourText);\n const minute = Number(minuteText);\n const second = Number(secondText);\n const offsetHours = offsetHourText === undefined ? 0 : Number(offsetHourText);\n const offsetMinutes = offsetMinuteText === undefined ? 0 : Number(offsetMinuteText);\n if (sign === '-' && offsetHours === 0 && offsetMinutes === 0) return value;\n if (\n month < 1 ||\n month > 12 ||\n day < 1 ||\n day > 31 ||\n hour > 23 ||\n minute > 59 ||\n second > 59 ||\n offsetHours > 23 ||\n offsetMinutes > 59\n ) {\n return value;\n }\n\n const local = new Date(0);\n local.setUTCFullYear(year, month - 1, day);\n local.setUTCHours(hour, minute, second, 0);\n if (\n local.getUTCFullYear() !== year ||\n local.getUTCMonth() !== month - 1 ||\n local.getUTCDate() !== day ||\n local.getUTCHours() !== hour ||\n local.getUTCMinutes() !== minute ||\n local.getUTCSeconds() !== second\n ) {\n return value;\n }\n\n const offset = (offsetHours * 60 + offsetMinutes) * 60_000 * (sign === '-' ? -1 : 1);\n const utc = new Date(local.getTime() - offset);\n if (utc.getUTCFullYear() < 0 || utc.getUTCFullYear() > 9999) return value;\n return `${utc.toISOString().slice(0, 19)}${fraction}Z`;\n}\n\n/** Normalize optional forecast timestamps at the v2.5 compatibility boundary. */\nexport function normalizeLegacyProductForecast(product: any): any {\n if (!product?.forecast || typeof product.forecast !== 'object' || Array.isArray(product.forecast)) return product;\n const forecast = { ...product.forecast };\n for (const field of ['generated_at', 'valid_until'] as const) {\n if (!(field in forecast)) continue;\n const normalized = normalizeLegacyZonedTimestamp(forecast[field]);\n if (normalized === undefined) delete forecast[field];\n else forecast[field] = normalized;\n }\n return { ...product, forecast };\n}\n\n/**\n * Normalize all products in a get_products response to v3\n */\nexport function normalizeGetProductsResponse(response: any): any {\n // If there's no products field at all, this may be an error response — pass through\n if (!('products' in (response ?? {}))) {\n return response;\n }\n\n // If products exists but isn't an array, the response is malformed\n if (!Array.isArray(response.products)) {\n return {\n errors: [\n {\n code: 'invalid_response',\n message: `Expected products to be an array, got ${typeof response.products}`,\n },\n ],\n };\n }\n\n return {\n ...response,\n products: response.products.map((p: any) =>\n normalizeLegacyProductForecast(normalizeProductChannels(normalizeProductPricing(p)))\n ),\n };\n}\n\n/**\n * Adapt a package request's pricing option selection for v2 server.\n * This handles bid_price which references pricing option floor semantics.\n */\nexport function adaptPackagePricingForV2(pkg: any): any {\n // Package requests don't typically contain pricing_options,\n // but they do contain pricing_option_id which selects one.\n // No transformation needed for bid_price - it's the same in both versions.\n return pkg;\n}\n\n/**\n * Adapt a create_media_buy request for v2 server (pricing fields)\n */\nexport function adaptMediaBuyPricingForV2(request: any): any {\n // Media buy requests select pricing options by ID, not by defining them.\n // No pricing field transformation needed in requests.\n return request;\n}\n\n/**\n * Normalize a media buy response's package pricing to v3\n */\nexport function normalizeMediaBuyPricingResponse(response: any): any {\n // Media buy responses may include products with pricing_options\n if (response?.products && Array.isArray(response.products)) {\n return {\n ...response,\n products: response.products.map(normalizeProductPricing),\n };\n }\n return response;\n}\n"],"mappings":"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","names":[]}
@@ -165,6 +165,69 @@ function normalizeProductChannels(product) {
165
165
  const normalized = product.channels.flatMap((ch) => v3ChannelMap[ch] ?? [ch]);
166
166
  return { ...product, channels: [...new Set(normalized)] };
167
167
  }
168
+ const LEGACY_ZONED_TIMESTAMP = /^(\d{4})-(\d{2})-(\d{2})[Tt ](\d{2}):(\d{2}):(\d{2})(\.\d+)?(?:[Zz]|\s*UTC|([+-])(\d{2})(?::?(\d{2}))?)$/i;
169
+ function normalizeLegacyZonedTimestamp(value) {
170
+ let candidate = value;
171
+ if (candidate && typeof candidate === "object" && !Array.isArray(candidate)) {
172
+ const entries = Object.entries(candidate);
173
+ if (entries.length === 0) return void 0;
174
+ if (entries.length !== 1 || entries[0][0] !== "$date" && entries[0][0] !== "value") return value;
175
+ candidate = entries[0][1];
176
+ }
177
+ if (candidate === null) return void 0;
178
+ if (candidate && typeof candidate === "object" && !Array.isArray(candidate)) {
179
+ return Object.keys(candidate).length === 0 ? void 0 : value;
180
+ }
181
+ if (typeof candidate !== "string") return value;
182
+ const match = LEGACY_ZONED_TIMESTAMP.exec(candidate.trim());
183
+ if (!match) return value;
184
+ const [
185
+ ,
186
+ yearText,
187
+ monthText,
188
+ dayText,
189
+ hourText,
190
+ minuteText,
191
+ secondText,
192
+ fraction = "",
193
+ sign,
194
+ offsetHourText,
195
+ offsetMinuteText
196
+ ] = match;
197
+ const year = Number(yearText);
198
+ const month = Number(monthText);
199
+ const day = Number(dayText);
200
+ const hour = Number(hourText);
201
+ const minute = Number(minuteText);
202
+ const second = Number(secondText);
203
+ const offsetHours = offsetHourText === void 0 ? 0 : Number(offsetHourText);
204
+ const offsetMinutes = offsetMinuteText === void 0 ? 0 : Number(offsetMinuteText);
205
+ if (sign === "-" && offsetHours === 0 && offsetMinutes === 0) return value;
206
+ if (month < 1 || month > 12 || day < 1 || day > 31 || hour > 23 || minute > 59 || second > 59 || offsetHours > 23 || offsetMinutes > 59) {
207
+ return value;
208
+ }
209
+ const local = /* @__PURE__ */ new Date(0);
210
+ local.setUTCFullYear(year, month - 1, day);
211
+ local.setUTCHours(hour, minute, second, 0);
212
+ if (local.getUTCFullYear() !== year || local.getUTCMonth() !== month - 1 || local.getUTCDate() !== day || local.getUTCHours() !== hour || local.getUTCMinutes() !== minute || local.getUTCSeconds() !== second) {
213
+ return value;
214
+ }
215
+ const offset = (offsetHours * 60 + offsetMinutes) * 6e4 * (sign === "-" ? -1 : 1);
216
+ const utc = new Date(local.getTime() - offset);
217
+ if (utc.getUTCFullYear() < 0 || utc.getUTCFullYear() > 9999) return value;
218
+ return `${utc.toISOString().slice(0, 19)}${fraction}Z`;
219
+ }
220
+ function normalizeLegacyProductForecast(product) {
221
+ if (!product?.forecast || typeof product.forecast !== "object" || Array.isArray(product.forecast)) return product;
222
+ const forecast = { ...product.forecast };
223
+ for (const field of ["generated_at", "valid_until"]) {
224
+ if (!(field in forecast)) continue;
225
+ const normalized = normalizeLegacyZonedTimestamp(forecast[field]);
226
+ if (normalized === void 0) delete forecast[field];
227
+ else forecast[field] = normalized;
228
+ }
229
+ return { ...product, forecast };
230
+ }
168
231
  function normalizeGetProductsResponse(response) {
169
232
  if (!("products" in (response ?? {}))) {
170
233
  return response;
@@ -181,7 +244,9 @@ function normalizeGetProductsResponse(response) {
181
244
  }
182
245
  return {
183
246
  ...response,
184
- products: response.products.map((p) => normalizeProductChannels(normalizeProductPricing(p)))
247
+ products: response.products.map(
248
+ (p) => normalizeLegacyProductForecast(normalizeProductChannels(normalizeProductPricing(p)))
249
+ )
185
250
  };
186
251
  }
187
252
  function adaptPackagePricingForV2(pkg) {
@@ -210,6 +275,7 @@ export {
210
275
  isCPAPricing,
211
276
  isFixedPricing,
212
277
  normalizeGetProductsResponse,
278
+ normalizeLegacyProductForecast,
213
279
  normalizeMediaBuyPricingResponse,
214
280
  normalizePricingOption,
215
281
  normalizeProductChannels,