@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.
- package/README.md +17 -0
- package/dist/lib/conformance/index.d.mts +2 -0
- package/dist/lib/conformance/index.d.ts +2 -0
- package/dist/lib/conformance/index.js +3 -0
- package/dist/lib/conformance/index.mjs +2 -0
- package/dist/lib/conformance/property-lists.d.mts +14 -0
- package/dist/lib/conformance/property-lists.d.ts +14 -0
- package/dist/lib/conformance/property-lists.js +45 -0
- package/dist/lib/conformance/property-lists.mjs +21 -0
- package/dist/lib/conformance/runConformance.js +20 -1
- package/dist/lib/conformance/runConformance.mjs +20 -1
- package/dist/lib/conformance/types.d.mts +3 -0
- package/dist/lib/conformance/types.d.ts +3 -0
- package/dist/lib/core/SingleAgentClient.d.mts +13 -3
- package/dist/lib/core/SingleAgentClient.d.ts +13 -3
- package/dist/lib/core/SingleAgentClient.js +109 -21
- package/dist/lib/core/SingleAgentClient.mjs +112 -22
- package/dist/lib/identifiers.d.mts +53 -0
- package/dist/lib/identifiers.d.ts +53 -0
- package/dist/lib/identifiers.js +212 -0
- package/dist/lib/identifiers.mjs +163 -0
- package/dist/lib/index.d.mts +3 -1
- package/dist/lib/index.d.ts +3 -1
- package/dist/lib/index.js +2 -0
- package/dist/lib/index.mjs +1 -0
- package/dist/lib/media-buy/index.d.mts +1 -1
- package/dist/lib/media-buy/index.d.ts +1 -1
- package/dist/lib/media-buy/property-policy.d.mts +15 -1
- package/dist/lib/media-buy/property-policy.d.ts +15 -1
- package/dist/lib/media-buy/property-policy.js +135 -3
- package/dist/lib/media-buy/property-policy.mjs +134 -3
- package/dist/lib/registry/index.d.mts +12 -0
- package/dist/lib/registry/index.d.ts +12 -0
- package/dist/lib/registry/index.js +159 -5
- package/dist/lib/registry/index.mjs +156 -4
- package/dist/lib/registry/types.d.mts +23 -0
- package/dist/lib/registry/types.d.ts +23 -0
- package/dist/lib/schemas-data/v2.5/_provenance.json +1 -1
- package/dist/lib/server/decisioning/specialisms/lists.d.mts +4 -4
- package/dist/lib/server/decisioning/specialisms/lists.d.ts +4 -4
- package/dist/lib/server/index.d.mts +3 -0
- package/dist/lib/server/index.d.ts +3 -0
- package/dist/lib/server/index.js +8 -1
- package/dist/lib/server/index.mjs +4 -0
- package/dist/lib/server/property-lists.d.mts +36 -0
- package/dist/lib/server/property-lists.d.ts +36 -0
- package/dist/lib/server/property-lists.js +154 -0
- package/dist/lib/server/property-lists.mjs +129 -0
- package/dist/lib/server/targeting-helpers.d.mts +2 -0
- package/dist/lib/server/targeting-helpers.d.ts +2 -0
- package/dist/lib/server/targeting-helpers.js +28 -1
- package/dist/lib/server/targeting-helpers.mjs +18 -1
- package/dist/lib/signing/parser.js +2 -2
- package/dist/lib/signing/parser.mjs +2 -2
- package/dist/lib/signing/server.d.mts +1 -1
- package/dist/lib/signing/server.d.ts +1 -1
- package/dist/lib/signing/server.mjs +5 -1
- package/dist/lib/signing/webhook-verifier.d.mts +11 -0
- package/dist/lib/signing/webhook-verifier.d.ts +11 -0
- package/dist/lib/signing/webhook-verifier.js +11 -3
- package/dist/lib/signing/webhook-verifier.mjs +11 -3
- package/dist/lib/supply-path/verify.js +12 -2
- package/dist/lib/supply-path/verify.mjs +2 -2
- package/dist/lib/testing/storyboard/validations.d.mts +1 -1
- package/dist/lib/testing/storyboard/validations.d.ts +1 -1
- package/dist/lib/version.d.mts +3 -3
- package/dist/lib/version.d.ts +3 -3
- package/dist/lib/version.js +3 -3
- package/dist/lib/version.mjs +3 -3
- package/docs/TYPE-SUMMARY.md +2 -2
- package/docs/guides/PROPERTY-LISTS.md +264 -0
- package/docs/guides/SIGNING-GUIDE.md +25 -0
- package/docs/llms.txt +2 -2
- package/docs/migration-14.x-rc-worksheet.md +4 -4
- 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
|
-
*
|
|
76
|
-
*
|
|
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
|
-
*
|
|
76
|
-
*
|
|
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
|
|
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) =>
|
|
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
|
|
1324
|
-
|
|
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
|
-
|
|
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
|
-
|
|
4147
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
} : {},
|