@adcp/sdk 16.0.0-beta.7 → 16.0.0-beta.8

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 (75) hide show
  1. package/README.md +17 -0
  2. package/dist/lib/conformance/index.d.mts +2 -0
  3. package/dist/lib/conformance/index.d.ts +2 -0
  4. package/dist/lib/conformance/index.js +3 -0
  5. package/dist/lib/conformance/index.mjs +2 -0
  6. package/dist/lib/conformance/property-lists.d.mts +14 -0
  7. package/dist/lib/conformance/property-lists.d.ts +14 -0
  8. package/dist/lib/conformance/property-lists.js +45 -0
  9. package/dist/lib/conformance/property-lists.mjs +21 -0
  10. package/dist/lib/conformance/runConformance.js +20 -1
  11. package/dist/lib/conformance/runConformance.mjs +20 -1
  12. package/dist/lib/conformance/types.d.mts +3 -0
  13. package/dist/lib/conformance/types.d.ts +3 -0
  14. package/dist/lib/core/SingleAgentClient.d.mts +13 -3
  15. package/dist/lib/core/SingleAgentClient.d.ts +13 -3
  16. package/dist/lib/core/SingleAgentClient.js +109 -21
  17. package/dist/lib/core/SingleAgentClient.mjs +112 -22
  18. package/dist/lib/identifiers.d.mts +53 -0
  19. package/dist/lib/identifiers.d.ts +53 -0
  20. package/dist/lib/identifiers.js +212 -0
  21. package/dist/lib/identifiers.mjs +163 -0
  22. package/dist/lib/index.d.mts +3 -1
  23. package/dist/lib/index.d.ts +3 -1
  24. package/dist/lib/index.js +2 -0
  25. package/dist/lib/index.mjs +1 -0
  26. package/dist/lib/media-buy/index.d.mts +1 -1
  27. package/dist/lib/media-buy/index.d.ts +1 -1
  28. package/dist/lib/media-buy/property-policy.d.mts +15 -1
  29. package/dist/lib/media-buy/property-policy.d.ts +15 -1
  30. package/dist/lib/media-buy/property-policy.js +135 -3
  31. package/dist/lib/media-buy/property-policy.mjs +134 -3
  32. package/dist/lib/registry/index.d.mts +12 -0
  33. package/dist/lib/registry/index.d.ts +12 -0
  34. package/dist/lib/registry/index.js +159 -5
  35. package/dist/lib/registry/index.mjs +156 -4
  36. package/dist/lib/registry/types.d.mts +23 -0
  37. package/dist/lib/registry/types.d.ts +23 -0
  38. package/dist/lib/schemas-data/v2.5/_provenance.json +1 -1
  39. package/dist/lib/server/decisioning/specialisms/lists.d.mts +4 -4
  40. package/dist/lib/server/decisioning/specialisms/lists.d.ts +4 -4
  41. package/dist/lib/server/index.d.mts +3 -0
  42. package/dist/lib/server/index.d.ts +3 -0
  43. package/dist/lib/server/index.js +8 -1
  44. package/dist/lib/server/index.mjs +4 -0
  45. package/dist/lib/server/property-lists.d.mts +36 -0
  46. package/dist/lib/server/property-lists.d.ts +36 -0
  47. package/dist/lib/server/property-lists.js +154 -0
  48. package/dist/lib/server/property-lists.mjs +129 -0
  49. package/dist/lib/server/targeting-helpers.d.mts +2 -0
  50. package/dist/lib/server/targeting-helpers.d.ts +2 -0
  51. package/dist/lib/server/targeting-helpers.js +28 -1
  52. package/dist/lib/server/targeting-helpers.mjs +18 -1
  53. package/dist/lib/signing/parser.js +2 -2
  54. package/dist/lib/signing/parser.mjs +2 -2
  55. package/dist/lib/signing/server.d.mts +1 -1
  56. package/dist/lib/signing/server.d.ts +1 -1
  57. package/dist/lib/signing/server.mjs +5 -1
  58. package/dist/lib/signing/webhook-verifier.d.mts +11 -0
  59. package/dist/lib/signing/webhook-verifier.d.ts +11 -0
  60. package/dist/lib/signing/webhook-verifier.js +11 -3
  61. package/dist/lib/signing/webhook-verifier.mjs +11 -3
  62. package/dist/lib/supply-path/verify.js +12 -2
  63. package/dist/lib/supply-path/verify.mjs +2 -2
  64. package/dist/lib/testing/storyboard/validations.d.mts +1 -1
  65. package/dist/lib/testing/storyboard/validations.d.ts +1 -1
  66. package/dist/lib/version.d.mts +3 -3
  67. package/dist/lib/version.d.ts +3 -3
  68. package/dist/lib/version.js +3 -3
  69. package/dist/lib/version.mjs +3 -3
  70. package/docs/TYPE-SUMMARY.md +2 -2
  71. package/docs/guides/PROPERTY-LISTS.md +264 -0
  72. package/docs/guides/SIGNING-GUIDE.md +25 -0
  73. package/docs/llms.txt +2 -2
  74. package/docs/migration-14.x-rc-worksheet.md +4 -4
  75. package/package.json +1 -1
package/README.md CHANGED
@@ -808,6 +808,23 @@ Build a registry service that:
808
808
 
809
809
  Library provides discovery logic - you add persistence layer.
810
810
 
811
+ ### Property catalog registration and errors
812
+
813
+ For bulk property catalog registration, use `RegistryClient.resolveIdentifiersAll()`
814
+ in default `resolve` mode (requires an API key and provenance); use `mode: 'lookup'`
815
+ for read-only resolution. It splits by count and request bytes, bounds concurrency,
816
+ honors rate-limit retry delays, and returns per-input outcomes. `saveProperty()`
817
+ updates hosted publisher configuration; `saveProperties()` fans out those updates
818
+ and is intended for one configuration per publisher domain. Both validate
819
+ `publisher_domain` before HTTP. See [the property-list guide](docs/guides/PROPERTY-LISTS.md)
820
+ for identifier validators, mixed app/CTV matching, MCP list serving, signed change
821
+ notifications, and certification.
822
+
823
+ Registry HTTP errors use `RegistryRequestError` / `isRegistryRequestError` with
824
+ `status`, `method`, `retryAfterMs`, and bounded untrusted `details`. Do not parse
825
+ `error.message` for HTTP status or retry timing. Local identifier validation uses
826
+ `IdentifierValidationError`; bulk resolve failures are available in each outcome.
827
+
811
828
  ### Brand relationship verification
812
829
 
813
830
  Use `RegistryClient.lookupBrand()` to resolve a domain and verify its relationship to a house. The public v3 registry does not expose an ordered-chain endpoint; v3 hierarchy is one level deep.
@@ -19,6 +19,8 @@
19
19
  * ```
20
20
  */
21
21
  export { runConformance } from './runConformance.mjs';
22
+ export { runPropertyListConformance } from './property-lists.mjs';
23
+ export type { PropertyListConformanceReport } from './property-lists.mjs';
22
24
  export { runWebhookConformance } from './webhook.mjs';
23
25
  export { seedFixtures } from './seeder.mjs';
24
26
  export { TARGETING_GEOGRAPHY_CONFORMANCE_VECTORS, buildTargetingInputConformanceVectors, runTargetingInputConformance, } from './targeting.mjs';
@@ -19,6 +19,8 @@
19
19
  * ```
20
20
  */
21
21
  export { runConformance } from './runConformance';
22
+ export { runPropertyListConformance } from './property-lists';
23
+ export type { PropertyListConformanceReport } from './property-lists';
22
24
  export { runWebhookConformance } from './webhook';
23
25
  export { seedFixtures } from './seeder';
24
26
  export { TARGETING_GEOGRAPHY_CONFORMANCE_VECTORS, buildTargetingInputConformanceVectors, runTargetingInputConformance, } from './targeting';
@@ -29,12 +29,14 @@ __export(conformance_exports, {
29
29
  UPDATE_TIER_TOOLS: () => import_types.UPDATE_TIER_TOOLS,
30
30
  buildTargetingInputConformanceVectors: () => import_targeting.buildTargetingInputConformanceVectors,
31
31
  runConformance: () => import_runConformance.runConformance,
32
+ runPropertyListConformance: () => import_property_lists.runPropertyListConformance,
32
33
  runTargetingInputConformance: () => import_targeting.runTargetingInputConformance,
33
34
  runWebhookConformance: () => import_webhook.runWebhookConformance,
34
35
  seedFixtures: () => import_seeder.seedFixtures
35
36
  });
36
37
  module.exports = __toCommonJS(conformance_exports);
37
38
  var import_runConformance = require('./runConformance.js');
39
+ var import_property_lists = require('./property-lists.js');
38
40
  var import_webhook = require('./webhook.js');
39
41
  var import_seeder = require('./seeder.js');
40
42
  var import_targeting = require('./targeting.js');
@@ -52,6 +54,7 @@ var import_types = require('./types.js');
52
54
  UPDATE_TIER_TOOLS,
53
55
  buildTargetingInputConformanceVectors,
54
56
  runConformance,
57
+ runPropertyListConformance,
55
58
  runTargetingInputConformance,
56
59
  runWebhookConformance,
57
60
  seedFixtures
@@ -1,4 +1,5 @@
1
1
  import { runConformance } from "./runConformance.mjs";
2
+ import { runPropertyListConformance } from "./property-lists.mjs";
2
3
  import { runWebhookConformance } from "./webhook.mjs";
3
4
  import { seedFixtures } from "./seeder.mjs";
4
5
  import {
@@ -28,6 +29,7 @@ export {
28
29
  UPDATE_TIER_TOOLS,
29
30
  buildTargetingInputConformanceVectors,
30
31
  runConformance,
32
+ runPropertyListConformance,
31
33
  runTargetingInputConformance,
32
34
  runWebhookConformance,
33
35
  seedFixtures
@@ -0,0 +1,14 @@
1
+ import type { PropertyListReference } from '../types/tools.generated.mjs';
2
+ import { type ResolveListOptions } from '../server/targeting-helpers.mjs';
3
+ export interface PropertyListConformanceReport {
4
+ totalRuns: number;
5
+ totalFailures: number;
6
+ results: Array<{
7
+ index: number;
8
+ ok: boolean;
9
+ identifierCount?: number;
10
+ code?: string;
11
+ }>;
12
+ }
13
+ /** Certify buyer-issued references using the same MCP resolver sellers use, with caching disabled. */
14
+ export declare function runPropertyListConformance(references: readonly PropertyListReference[], options?: ResolveListOptions): Promise<PropertyListConformanceReport>;
@@ -0,0 +1,14 @@
1
+ import type { PropertyListReference } from '../types/tools.generated';
2
+ import { type ResolveListOptions } from '../server/targeting-helpers';
3
+ export interface PropertyListConformanceReport {
4
+ totalRuns: number;
5
+ totalFailures: number;
6
+ results: Array<{
7
+ index: number;
8
+ ok: boolean;
9
+ identifierCount?: number;
10
+ code?: string;
11
+ }>;
12
+ }
13
+ /** Certify buyer-issued references using the same MCP resolver sellers use, with caching disabled. */
14
+ export declare function runPropertyListConformance(references: readonly PropertyListReference[], options?: ResolveListOptions): Promise<PropertyListConformanceReport>;
@@ -0,0 +1,45 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+ var property_lists_exports = {};
20
+ __export(property_lists_exports, {
21
+ runPropertyListConformance: () => runPropertyListConformance
22
+ });
23
+ module.exports = __toCommonJS(property_lists_exports);
24
+ var import_targeting_helpers = require('../server/targeting-helpers.js');
25
+ async function runPropertyListConformance(references, options = {}) {
26
+ const results = [];
27
+ for (const [index, reference] of references.entries()) {
28
+ try {
29
+ const resolved = await (0, import_targeting_helpers.resolvePropertyList)(reference, {
30
+ ...options,
31
+ cache: false,
32
+ strictResponseValidation: true
33
+ });
34
+ if (resolved.listId !== reference.list_id) throw new Error("property_list_identity_mismatch");
35
+ results.push({ index, ok: true, identifierCount: resolved.identifiers.length });
36
+ } catch {
37
+ results.push({ index, ok: false, code: "property_list_reference_unresolvable" });
38
+ }
39
+ }
40
+ return { totalRuns: results.length, totalFailures: results.filter((result) => !result.ok).length, results };
41
+ }
42
+ // Annotate the CommonJS export names for ESM import in node:
43
+ 0 && (module.exports = {
44
+ runPropertyListConformance
45
+ });
@@ -0,0 +1,21 @@
1
+ import { resolvePropertyList } from "../server/targeting-helpers.mjs";
2
+ async function runPropertyListConformance(references, options = {}) {
3
+ const results = [];
4
+ for (const [index, reference] of references.entries()) {
5
+ try {
6
+ const resolved = await resolvePropertyList(reference, {
7
+ ...options,
8
+ cache: false,
9
+ strictResponseValidation: true
10
+ });
11
+ if (resolved.listId !== reference.list_id) throw new Error("property_list_identity_mismatch");
12
+ results.push({ index, ok: true, identifierCount: resolved.identifiers.length });
13
+ } catch {
14
+ results.push({ index, ok: false, code: "property_list_reference_unresolvable" });
15
+ }
16
+ }
17
+ return { totalRuns: results.length, totalFailures: results.filter((result) => !result.ok).length, results };
18
+ }
19
+ export {
20
+ runPropertyListConformance
21
+ };
@@ -23,6 +23,7 @@ __export(runConformance_exports, {
23
23
  });
24
24
  module.exports = __toCommonJS(runConformance_exports);
25
25
  var import_AgentClient = require('../core/AgentClient.js');
26
+ var import_property_lists = require('./property-lists.js');
26
27
  var import_version = require('../version.js');
27
28
  var import_types = require('./types.js');
28
29
  var import_schemaLoader = require('./schemaLoader.js');
@@ -120,6 +121,23 @@ async function runConformance(agentUrl, options = {}) {
120
121
  });
121
122
  uniformError.push(report);
122
123
  }
124
+ const propertyLists = options.propertyListReferences ? await (0, import_property_lists.runPropertyListConformance)(options.propertyListReferences) : void 0;
125
+ for (const result of propertyLists?.results ?? []) {
126
+ if (result.ok) continue;
127
+ if (failures.length >= maxFailures) {
128
+ droppedFailures++;
129
+ continue;
130
+ }
131
+ failures.push({
132
+ tool: "get_property_list",
133
+ seed,
134
+ shrunk: false,
135
+ input: { propertyListReferenceIndex: result.index },
136
+ response: null,
137
+ verdict: "invalid",
138
+ invariantFailures: ["Buyer-issued property-list reference could not be resolved."]
139
+ });
140
+ }
123
141
  const completedAt = /* @__PURE__ */ new Date();
124
142
  return {
125
143
  agentUrl,
@@ -131,8 +149,9 @@ async function runConformance(agentUrl, options = {}) {
131
149
  fixturesUsed: mergedFixtures,
132
150
  autoSeeded: !!options.autoSeed,
133
151
  seedWarnings,
134
- totalRuns,
152
+ totalRuns: totalRuns + (propertyLists?.totalRuns ?? 0),
135
153
  totalFailures: failures.length + droppedFailures,
154
+ ...propertyLists ? { propertyLists } : {},
136
155
  droppedFailures,
137
156
  perTool,
138
157
  failures,
@@ -1,4 +1,5 @@
1
1
  import { AgentClient } from "../core/AgentClient.mjs";
2
+ import { runPropertyListConformance } from "./property-lists.mjs";
2
3
  import { parseAdcpMajorVersion } from "../version.mjs";
3
4
  import {
4
5
  COMPACT_LIFECYCLE_TOOLS,
@@ -105,6 +106,23 @@ async function runConformance(agentUrl, options = {}) {
105
106
  });
106
107
  uniformError.push(report);
107
108
  }
109
+ const propertyLists = options.propertyListReferences ? await runPropertyListConformance(options.propertyListReferences) : void 0;
110
+ for (const result of propertyLists?.results ?? []) {
111
+ if (result.ok) continue;
112
+ if (failures.length >= maxFailures) {
113
+ droppedFailures++;
114
+ continue;
115
+ }
116
+ failures.push({
117
+ tool: "get_property_list",
118
+ seed,
119
+ shrunk: false,
120
+ input: { propertyListReferenceIndex: result.index },
121
+ response: null,
122
+ verdict: "invalid",
123
+ invariantFailures: ["Buyer-issued property-list reference could not be resolved."]
124
+ });
125
+ }
108
126
  const completedAt = /* @__PURE__ */ new Date();
109
127
  return {
110
128
  agentUrl,
@@ -116,8 +134,9 @@ async function runConformance(agentUrl, options = {}) {
116
134
  fixturesUsed: mergedFixtures,
117
135
  autoSeeded: !!options.autoSeed,
118
136
  seedWarnings,
119
- totalRuns,
137
+ totalRuns: totalRuns + (propertyLists?.totalRuns ?? 0),
120
138
  totalFailures: failures.length + droppedFailures,
139
+ ...propertyLists ? { propertyLists } : {},
121
140
  droppedFailures,
122
141
  perTool,
123
142
  failures,
@@ -32,6 +32,8 @@ export declare const DEFAULT_TOOLS: readonly ConformanceToolName[];
32
32
  /** Tier 1 + Tier 2 + Tier 3. Used when `autoSeed` is true or fixtures were supplied. */
33
33
  export declare const DEFAULT_TOOLS_WITH_UPDATES: readonly ConformanceToolName[];
34
34
  export interface RunConformanceOptions {
35
+ /** Buyer-issued references to certify through the seller MCP resolution path (without cached results). */
36
+ propertyListReferences?: readonly import('../types/tools.generated.mjs').PropertyListReference[];
35
37
  /** Seed for reproducible runs. Omit for a random seed. */
36
38
  seed?: number;
37
39
  /** Subset of tools to fuzz. Defaults to the full stateless tier. */
@@ -210,6 +212,7 @@ export interface ConformanceToolStats {
210
212
  skipReason?: SkipReason;
211
213
  }
212
214
  export interface ConformanceReport {
215
+ propertyLists?: import('./property-lists.mjs').PropertyListConformanceReport;
213
216
  agentUrl: string;
214
217
  seed: number;
215
218
  /** Schema version the fuzzer loaded. Pinned so a report is replayable. */
@@ -32,6 +32,8 @@ export declare const DEFAULT_TOOLS: readonly ConformanceToolName[];
32
32
  /** Tier 1 + Tier 2 + Tier 3. Used when `autoSeed` is true or fixtures were supplied. */
33
33
  export declare const DEFAULT_TOOLS_WITH_UPDATES: readonly ConformanceToolName[];
34
34
  export interface RunConformanceOptions {
35
+ /** Buyer-issued references to certify through the seller MCP resolution path (without cached results). */
36
+ propertyListReferences?: readonly import('../types/tools.generated').PropertyListReference[];
35
37
  /** Seed for reproducible runs. Omit for a random seed. */
36
38
  seed?: number;
37
39
  /** Subset of tools to fuzz. Defaults to the full stateless tier. */
@@ -210,6 +212,7 @@ export interface ConformanceToolStats {
210
212
  skipReason?: SkipReason;
211
213
  }
212
214
  export interface ConformanceReport {
215
+ propertyLists?: import('./property-lists').PropertyListConformanceReport;
213
216
  agentUrl: string;
214
217
  seed: number;
215
218
  /** Schema version the fuzzer loaded. Pinned so a report is replayable. */
@@ -18,7 +18,7 @@ import { type AgentDiscoveryCacheConfig } from './agent-discovery-cache.mjs';
18
18
  import { BuyerAccountRegistry, type AccountPolicy, type BuyerAccountStorage } from './buyer-account-registry.mjs';
19
19
  import type { ProductCache } from './product-cache.mjs';
20
20
  import { type ResolveAccountOptions, type ResolveAccountResult } from './account-resolution.mjs';
21
- import { type BuyerPropertyPolicy, type ProductPropertyPolicyMode } from '../media-buy/property-policy.mjs';
21
+ import { type BuyerPropertyPolicy, type ProductPolicyResolvedProperty, type ProductPropertyPolicyMode } from '../media-buy/property-policy.mjs';
22
22
  import { type ResolveListOptions } from '../server/targeting-helpers.mjs';
23
23
  import { type CanonicalCreateMediaBuyInput, type CanonicalCreativeResponse, type CanonicalGetProductsRequest, type CanonicalGetProductsResponse, type CanonicalListCreativesRequest, type CanonicalListCreativesResponse, type CanonicalPreviewCreativeRequest, type CanonicalPreviewCreativeResponse, type CanonicalSyncCreativesRequest, type CanonicalUpdateMediaBuyRequest, type CreativeFormatWireMode, type SyncCreativeFormatProjection } from '../v2/projection/creative-delivery.mjs';
24
24
  import type { LegacyFormatConverter } from '../v2/projection/v1-to-v2.mjs';
@@ -63,6 +63,16 @@ export declare class UnsupportedFeatureError extends Error {
63
63
  constructor(feature: string, serverVersion: 'v2' | 'v3', message?: string);
64
64
  }
65
65
  export interface ClientProductPropertyPolicy extends BuyerPropertyPolicy {
66
+ /** Resolve a complete publisher catalog for app/CTV selector matching. Failures follow unknownSelectorBehavior. */
67
+ resolvePublisherProperties?: (publisherDomain: string, options?: {
68
+ signal?: AbortSignal;
69
+ }) => Promise<readonly ProductPolicyResolvedProperty[] | undefined>;
70
+ /** Maximum distinct catalog lookups per response. Overflow follows unknownSelectorBehavior. Default: 100. */
71
+ maxPublisherLookups?: number;
72
+ /** Per-catalog timeout. The hook receives an abort signal. Default: 10 seconds. */
73
+ publisherLookupTimeoutMs?: number;
74
+ /** Total catalog-lookup budget per response. Unfinished lookups stay unknown. Default: 10 seconds. */
75
+ publisherLookupBudgetMs?: number;
66
76
  /**
67
77
  * Enforcement mode for completed `get_products` responses.
68
78
  *
@@ -72,8 +82,8 @@ export interface ClientProductPropertyPolicy extends BuyerPropertyPolicy {
72
82
  */
73
83
  mode?: ProductPropertyPolicyMode;
74
84
  /**
75
- * When true, a `get_products` request carrying `property_list` is resolved
76
- * and used as an allow-list for the returned products. Defaults to true.
85
+ * Resolve `filters.property_list` (AdCP 3.2) or deprecated `property_list`
86
+ * as an allow-list. Conflicting references fail closed. Defaults to true.
77
87
  */
78
88
  enforceRequestPropertyList?: boolean;
79
89
  /**
@@ -18,7 +18,7 @@ import { type AgentDiscoveryCacheConfig } from './agent-discovery-cache';
18
18
  import { BuyerAccountRegistry, type AccountPolicy, type BuyerAccountStorage } from './buyer-account-registry';
19
19
  import type { ProductCache } from './product-cache';
20
20
  import { type ResolveAccountOptions, type ResolveAccountResult } from './account-resolution';
21
- import { type BuyerPropertyPolicy, type ProductPropertyPolicyMode } from '../media-buy/property-policy';
21
+ import { type BuyerPropertyPolicy, type ProductPolicyResolvedProperty, type ProductPropertyPolicyMode } from '../media-buy/property-policy';
22
22
  import { type ResolveListOptions } from '../server/targeting-helpers';
23
23
  import { type CanonicalCreateMediaBuyInput, type CanonicalCreativeResponse, type CanonicalGetProductsRequest, type CanonicalGetProductsResponse, type CanonicalListCreativesRequest, type CanonicalListCreativesResponse, type CanonicalPreviewCreativeRequest, type CanonicalPreviewCreativeResponse, type CanonicalSyncCreativesRequest, type CanonicalUpdateMediaBuyRequest, type CreativeFormatWireMode, type SyncCreativeFormatProjection } from '../v2/projection/creative-delivery';
24
24
  import type { LegacyFormatConverter } from '../v2/projection/v1-to-v2';
@@ -63,6 +63,16 @@ export declare class UnsupportedFeatureError extends Error {
63
63
  constructor(feature: string, serverVersion: 'v2' | 'v3', message?: string);
64
64
  }
65
65
  export interface ClientProductPropertyPolicy extends BuyerPropertyPolicy {
66
+ /** Resolve a complete publisher catalog for app/CTV selector matching. Failures follow unknownSelectorBehavior. */
67
+ resolvePublisherProperties?: (publisherDomain: string, options?: {
68
+ signal?: AbortSignal;
69
+ }) => Promise<readonly ProductPolicyResolvedProperty[] | undefined>;
70
+ /** Maximum distinct catalog lookups per response. Overflow follows unknownSelectorBehavior. Default: 100. */
71
+ maxPublisherLookups?: number;
72
+ /** Per-catalog timeout. The hook receives an abort signal. Default: 10 seconds. */
73
+ publisherLookupTimeoutMs?: number;
74
+ /** Total catalog-lookup budget per response. Unfinished lookups stay unknown. Default: 10 seconds. */
75
+ publisherLookupBudgetMs?: number;
66
76
  /**
67
77
  * Enforcement mode for completed `get_products` responses.
68
78
  *
@@ -72,8 +82,8 @@ export interface ClientProductPropertyPolicy extends BuyerPropertyPolicy {
72
82
  */
73
83
  mode?: ProductPropertyPolicyMode;
74
84
  /**
75
- * When true, a `get_products` request carrying `property_list` is resolved
76
- * and used as an allow-list for the returned products. Defaults to true.
85
+ * Resolve `filters.property_list` (AdCP 3.2) or deprecated `property_list`
86
+ * as an allow-list. Conflicting references fail closed. Defaults to true.
77
87
  */
78
88
  enforceRequestPropertyList?: boolean;
79
89
  /**
@@ -696,7 +696,9 @@ function productIdForPricingDiagnostics(product) {
696
696
  return typeof id === "string" ? id : void 0;
697
697
  }
698
698
  function propertyListReferenceFromRequest(params) {
699
- const value = params.property_list;
699
+ const filters = params.filters;
700
+ const nested = filters && typeof filters === "object" && !Array.isArray(filters) && Object.hasOwn(filters, "property_list") && filters.property_list !== void 0;
701
+ const value = nested ? filters.property_list : params.property_list;
700
702
  if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
701
703
  const ref = value;
702
704
  if (typeof ref.agent_url !== "string" || typeof ref.list_id !== "string") return void 0;
@@ -709,21 +711,37 @@ function propertyListReferenceFromRequest(params) {
709
711
  function productPolicyRequestSnapshot(requestParams) {
710
712
  const account = canonicalAccountRoutingSnapshot(requestParams.account);
711
713
  const propertyList = propertyListReferenceFromRequest(requestParams);
714
+ const referenceError = propertyListReferenceError(requestParams);
712
715
  return Object.freeze({
713
716
  ...account ? { account } : {},
714
- ...propertyList ? { property_list: Object.freeze(propertyList) } : {}
717
+ ...propertyList ? { property_list: Object.freeze(propertyList) } : {},
718
+ ...referenceError ? { property_list_error: referenceError } : {}
715
719
  });
716
720
  }
721
+ function propertyListReferenceError(params) {
722
+ if (typeof params.property_list_error === "string") return params.property_list_error;
723
+ const filters = params.filters;
724
+ if (params.property_list !== void 0 && !propertyListReferenceFromRequest({ property_list: params.property_list }))
725
+ return "property_list_invalid_reference";
726
+ if (!filters || typeof filters !== "object" || Array.isArray(filters) || !Object.hasOwn(filters, "property_list") || filters.property_list === void 0)
727
+ return void 0;
728
+ const nested = propertyListReferenceFromRequest(params);
729
+ if (!nested) return "property_list_invalid_reference";
730
+ if (params.property_list !== void 0) {
731
+ const legacy = propertyListReferenceFromRequest({ property_list: params.property_list });
732
+ if (!legacy || nested.agent_url !== legacy.agent_url || nested.list_id !== legacy.list_id || nested.auth_token !== legacy.auth_token) {
733
+ return "property_list_conflicting_references";
734
+ }
735
+ }
736
+ return void 0;
737
+ }
717
738
  function hasProductPropertyPolicyRules(policy) {
718
739
  return Boolean(
719
740
  policy.allowedDomains?.length || policy.allowedPropertyIdentifiers?.length || policy.requireAllowedPropertyMatch || policy.excludedDomains?.length || policy.excludedPropertyIds?.length || policy.strict || policy.unknownSelectorBehavior || policy.missingPublisherPropertiesBehavior
720
741
  );
721
742
  }
722
- function comparablePropertyIdentifiers(identifiers) {
723
- return identifiers.filter((identifier) => identifier.type === "domain" || identifier.type === "subdomain").map((identifier) => ({ type: identifier.type, value: identifier.value }));
724
- }
725
743
  function unsupportedPropertyIdentifiers(identifiers) {
726
- return identifiers.filter((identifier) => identifier.type !== "domain" && identifier.type !== "subdomain").map((identifier) => ({ type: identifier.type }));
744
+ return identifiers.filter((identifier) => !(0, import_property_policy.isSupportedPropertyIdentifierType)(identifier.type)).map((identifier) => ({ type: identifier.type }));
727
745
  }
728
746
  function sanitizeDiagnosticUrl(value) {
729
747
  try {
@@ -1320,8 +1338,17 @@ class SingleAgentClient {
1320
1338
  }
1321
1339
  assertDurablePropertyListCredentialSupported(taskType, requestParams) {
1322
1340
  if (!this.config.deferredStorage || taskType !== "get_products") return;
1323
- const propertyList = propertyListReferenceFromRequest(requestParams);
1324
- if (!propertyList?.auth_token) return;
1341
+ const filters = requestParams.filters;
1342
+ const references = [
1343
+ requestParams.property_list,
1344
+ filters && typeof filters === "object" ? filters.property_list : void 0
1345
+ ];
1346
+ const hasCredential = references.some((reference) => {
1347
+ if (!reference || typeof reference !== "object") return false;
1348
+ const token = reference.auth_token;
1349
+ return token !== void 0 && token !== null && token !== "";
1350
+ });
1351
+ if (!hasCredential) return;
1325
1352
  const policyConfig = this.config.validation?.productPropertyPolicy;
1326
1353
  if (policyConfig === false || policyConfig?.enforceRequestPropertyList === false) return;
1327
1354
  throw new import_errors.ConfigurationError(
@@ -4132,39 +4159,40 @@ Hint: this usually means agent_uri does not include the MCP endpoint path. The S
4132
4159
  message = "Property list not adhered to",
4133
4160
  enforceRequestPropertyList = true,
4134
4161
  propertyListResolveOptions,
4162
+ resolvePublisherProperties,
4163
+ maxPublisherLookups = 100,
4164
+ publisherLookupTimeoutMs = 1e4,
4165
+ publisherLookupBudgetMs = 1e4,
4135
4166
  ...explicitPolicy
4136
4167
  } = policyConfig || {};
4137
4168
  const policy = {
4138
4169
  ...explicitPolicy
4139
4170
  };
4140
4171
  let resolvedRequestPropertyList;
4141
- if (requestPropertyList && enforceRequestPropertyList) {
4172
+ const referenceError = propertyListReferenceError(requestParams);
4173
+ if ((requestPropertyList || referenceError) && enforceRequestPropertyList) {
4142
4174
  try {
4175
+ if (referenceError) throw new Error(referenceError);
4143
4176
  const resolved = await (0, import_targeting_helpers.resolvePropertyList)(requestPropertyList, propertyListResolveOptions);
4144
- const comparableIdentifiers = comparablePropertyIdentifiers(resolved.identifiers);
4145
4177
  const unsupportedIdentifiers = unsupportedPropertyIdentifiers(resolved.identifiers);
4146
- if (unsupportedIdentifiers.length > 0) {
4147
- throw new Error("property_list_unsupported_identifier_types");
4148
- }
4149
- if (comparableIdentifiers.length > 0 || resolved.identifiers.length === 0) {
4150
- policy.allowedPropertyIdentifiers = [...policy.allowedPropertyIdentifiers ?? [], ...comparableIdentifiers];
4151
- policy.requireAllowedPropertyMatch = true;
4152
- }
4178
+ policy.allowedPropertyIdentifiers = [...policy.allowedPropertyIdentifiers ?? [], ...resolved.identifiers];
4179
+ policy.requireAllowedPropertyMatch = true;
4153
4180
  policy.strict = policy.strict ?? true;
4154
4181
  resolvedRequestPropertyList = {
4155
4182
  listId: resolved.listId,
4156
4183
  agentUrl: resolved.agentUrl,
4157
4184
  identifierCount: resolved.identifiers.length,
4185
+ unsupportedIdentifierTypes: [...new Set(unsupportedIdentifiers.map((identifier) => identifier.type))],
4158
4186
  ...resolved.cacheValidUntil ? { cacheValidUntil: resolved.cacheValidUntil } : {}
4159
4187
  };
4160
4188
  } catch (err) {
4161
4189
  const errorCode = propertyListResolutionErrorCode(err);
4162
- const diagnosticAgentUrl = sanitizeDiagnosticUrl(requestPropertyList.agent_url);
4190
+ const diagnosticAgentUrl = requestPropertyList ? sanitizeDiagnosticUrl(requestPropertyList.agent_url) : "invalid_url";
4163
4191
  return (0, import_match.attachMatch)({
4164
4192
  success: false,
4165
4193
  status: "failed",
4166
4194
  data: response,
4167
- error: `${message}: could not resolve property_list ${requestPropertyList.list_id}`,
4195
+ error: `${message}: could not resolve property_list`,
4168
4196
  metadata: {
4169
4197
  ...result.metadata,
4170
4198
  status: "failed",
@@ -4177,7 +4205,7 @@ Hint: this usually means agent_uri does not include the MCP endpoint path. The S
4177
4205
  message,
4178
4206
  diagnostics: [],
4179
4207
  request_property_list: {
4180
- list_id: requestPropertyList.list_id,
4208
+ list_id: requestPropertyList?.list_id ?? "invalid_reference",
4181
4209
  agent_url: diagnosticAgentUrl,
4182
4210
  resolution_error: errorCode
4183
4211
  }
@@ -4192,7 +4220,7 @@ Hint: this usually means agent_uri does not include the MCP endpoint path. The S
4192
4220
  mode,
4193
4221
  ok: false,
4194
4222
  request_property_list: {
4195
- list_id: requestPropertyList.list_id,
4223
+ list_id: requestPropertyList?.list_id,
4196
4224
  agent_url: diagnosticAgentUrl,
4197
4225
  resolution_error: errorCode
4198
4226
  }
@@ -4202,6 +4230,65 @@ Hint: this usually means agent_uri does not include the MCP endpoint path. The S
4202
4230
  }
4203
4231
  }
4204
4232
  if (!hasProductPropertyPolicyRules(policy)) return result;
4233
+ if (resolvePublisherProperties && policy.allowedPropertyIdentifiers?.some(
4234
+ (identifier) => identifier.type !== "domain" && identifier.type !== "subdomain"
4235
+ )) {
4236
+ if (!Number.isSafeInteger(maxPublisherLookups) || maxPublisherLookups < 1 || !Number.isSafeInteger(publisherLookupTimeoutMs) || publisherLookupTimeoutMs < 1 || publisherLookupTimeoutMs > 2147483647 || !Number.isSafeInteger(publisherLookupBudgetMs) || publisherLookupBudgetMs < 1 || publisherLookupBudgetMs > 2147483647) {
4237
+ throw new import_errors.ConfigurationError("Publisher lookup limits must be positive bounded integers");
4238
+ }
4239
+ const { validatePublisherDomain } = await import("../identifiers.js");
4240
+ const catalogs = Object.assign(
4241
+ /* @__PURE__ */ Object.create(null),
4242
+ policy.publisherProperties
4243
+ );
4244
+ const domains = new Set(
4245
+ response.products.flatMap(
4246
+ (product) => Array.isArray(product.publisher_properties) ? product.publisher_properties.flatMap(
4247
+ (selector) => selector && typeof selector === "object" && "publisher_domain" in selector ? [selector.publisher_domain] : []
4248
+ ) : []
4249
+ )
4250
+ );
4251
+ const hosts = [
4252
+ ...new Set(
4253
+ [...domains].flatMap(
4254
+ (domain) => typeof domain === "string" ? [(0, import_property_policy.normalizeDomainForPropertyPolicy)(domain)?.host] : []
4255
+ )
4256
+ )
4257
+ ].filter((host) => typeof host === "string" && !Object.hasOwn(catalogs, host));
4258
+ for (const host of hosts) catalogs[host] = void 0;
4259
+ const lookupStartedAt = performance.now();
4260
+ for (const host of hosts.slice(0, maxPublisherLookups)) {
4261
+ (0, import_abort2.throwIfAborted)(taskSignal);
4262
+ const remainingBudgetMs = publisherLookupBudgetMs - (performance.now() - lookupStartedAt);
4263
+ if (remainingBudgetMs <= 0) break;
4264
+ if (!validatePublisherDomain(host).ok || (0, import_net.isLikelyPrivateUrl)(`https://${host}`)) continue;
4265
+ const controller = new AbortController();
4266
+ const signal = taskSignal ? AbortSignal.any([taskSignal, controller.signal]) : controller.signal;
4267
+ let timeout;
4268
+ let budgetExpired = false;
4269
+ try {
4270
+ catalogs[host] = await Promise.race([
4271
+ resolvePublisherProperties(host, { signal }),
4272
+ new Promise((_, reject) => {
4273
+ timeout = setTimeout(
4274
+ () => {
4275
+ if (remainingBudgetMs <= publisherLookupTimeoutMs) budgetExpired = true;
4276
+ controller.abort();
4277
+ reject(new Error("publisher_catalog_timeout"));
4278
+ },
4279
+ Math.min(publisherLookupTimeoutMs, remainingBudgetMs)
4280
+ );
4281
+ })
4282
+ ]);
4283
+ } catch {
4284
+ catalogs[host] = void 0;
4285
+ } finally {
4286
+ if (timeout) clearTimeout(timeout);
4287
+ }
4288
+ if (budgetExpired) break;
4289
+ }
4290
+ policy.publisherProperties = catalogs;
4291
+ }
4205
4292
  let validation;
4206
4293
  try {
4207
4294
  validation = (0, import_property_policy.validateProductsAgainstPropertyPolicy)({
@@ -4225,6 +4312,7 @@ Hint: this usually means agent_uri does not include the MCP endpoint path. The S
4225
4312
  list_id: resolvedRequestPropertyList.listId,
4226
4313
  agent_url: sanitizeDiagnosticUrl(resolvedRequestPropertyList.agentUrl),
4227
4314
  identifier_count: resolvedRequestPropertyList.identifierCount,
4315
+ unsupported_identifier_types: resolvedRequestPropertyList.unsupportedIdentifierTypes,
4228
4316
  ...resolvedRequestPropertyList.cacheValidUntil ? { cache_valid_until: resolvedRequestPropertyList.cacheValidUntil } : {}
4229
4317
  }
4230
4318
  } : {},