@adcp/sdk 14.0.0-rc.43 → 14.0.0-rc.45

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/dist/lib/acceptance-policy/evaluator.d.mts +46 -0
  2. package/dist/lib/acceptance-policy/evaluator.d.ts +46 -0
  3. package/dist/lib/acceptance-policy/evaluator.js +447 -0
  4. package/dist/lib/acceptance-policy/evaluator.mjs +423 -0
  5. package/dist/lib/acceptance-policy/index.d.mts +20 -0
  6. package/dist/lib/acceptance-policy/index.d.ts +20 -0
  7. package/dist/lib/acceptance-policy/index.js +43 -5
  8. package/dist/lib/acceptance-policy/index.mjs +42 -5
  9. package/dist/lib/canonical-references/index.d.mts +2 -0
  10. package/dist/lib/canonical-references/index.d.ts +2 -0
  11. package/dist/lib/canonical-references/index.js +2 -1
  12. package/dist/lib/canonical-references/index.mjs +2 -1
  13. package/dist/lib/discovery/validate-adagents.d.mts +2 -2
  14. package/dist/lib/discovery/validate-adagents.d.ts +2 -2
  15. package/dist/lib/discovery/validate-adagents.js +0 -3
  16. package/dist/lib/discovery/validate-adagents.mjs +0 -3
  17. package/dist/lib/index.d.mts +3 -2
  18. package/dist/lib/index.d.ts +3 -2
  19. package/dist/lib/index.js +2 -0
  20. package/dist/lib/index.mjs +2 -0
  21. package/dist/lib/net/index.d.mts +1 -1
  22. package/dist/lib/net/index.d.ts +1 -1
  23. package/dist/lib/net/ssrf-fetch.d.mts +41 -0
  24. package/dist/lib/net/ssrf-fetch.d.ts +41 -0
  25. package/dist/lib/net/ssrf-fetch.js +2 -1
  26. package/dist/lib/net/ssrf-fetch.mjs +2 -1
  27. package/dist/lib/protocols/transportDiagnostics.js +9 -8
  28. package/dist/lib/protocols/transportDiagnostics.mjs +9 -8
  29. package/dist/lib/registry/types.generated.d.mts +43 -1
  30. package/dist/lib/registry/types.generated.d.ts +43 -1
  31. package/dist/lib/reporting/reconciliation.js +42 -26
  32. package/dist/lib/reporting/reconciliation.mjs +42 -26
  33. package/dist/lib/schemas-data/v2.5/_provenance.json +1 -1
  34. package/dist/lib/signing/brand-jwks.d.mts +37 -3
  35. package/dist/lib/signing/brand-jwks.d.ts +37 -3
  36. package/dist/lib/signing/brand-jwks.js +6 -1
  37. package/dist/lib/signing/brand-jwks.mjs +6 -1
  38. package/dist/lib/signing/jwks-https.d.mts +4 -0
  39. package/dist/lib/signing/jwks-https.d.ts +4 -0
  40. package/dist/lib/signing/jwks-https.js +4 -1
  41. package/dist/lib/signing/jwks-https.mjs +4 -1
  42. package/dist/lib/signing/server.d.mts +1 -0
  43. package/dist/lib/signing/server.d.ts +1 -0
  44. package/dist/lib/testing/storyboard/acceptance-policy-discovery.d.mts +26 -0
  45. package/dist/lib/testing/storyboard/acceptance-policy-discovery.d.ts +26 -0
  46. package/dist/lib/testing/storyboard/acceptance-policy-discovery.js +319 -0
  47. package/dist/lib/testing/storyboard/acceptance-policy-discovery.mjs +299 -0
  48. package/dist/lib/testing/storyboard/request-signing/grader.d.mts +11 -0
  49. package/dist/lib/testing/storyboard/request-signing/grader.d.ts +11 -0
  50. package/dist/lib/testing/storyboard/request-signing/grader.js +13 -0
  51. package/dist/lib/testing/storyboard/request-signing/grader.mjs +12 -0
  52. package/dist/lib/testing/storyboard/request-signing/probe-dispatch.js +20 -1
  53. package/dist/lib/testing/storyboard/request-signing/probe-dispatch.mjs +21 -2
  54. package/dist/lib/testing/storyboard/runner.js +125 -35
  55. package/dist/lib/testing/storyboard/runner.mjs +127 -35
  56. package/dist/lib/testing/storyboard/types.d.mts +13 -0
  57. package/dist/lib/testing/storyboard/types.d.ts +13 -0
  58. package/dist/lib/testing/storyboard/validations.d.mts +1 -1
  59. package/dist/lib/testing/storyboard/validations.d.ts +1 -1
  60. package/dist/lib/v2/projection/creative-delivery.js +58 -2
  61. package/dist/lib/v2/projection/creative-delivery.mjs +64 -3
  62. package/dist/lib/v2/projection/v1-to-v2.d.mts +7 -0
  63. package/dist/lib/v2/projection/v1-to-v2.d.ts +7 -0
  64. package/dist/lib/v2/projection/v1-to-v2.js +2 -0
  65. package/dist/lib/v2/projection/v1-to-v2.mjs +1 -0
  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/ACCEPTANCE-POLICY-DISCOVERY.md +126 -2
  72. package/docs/guides/REPORTING-RECONCILIATION.md +2 -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
@@ -0,0 +1,423 @@
1
+ import { getSchemaValidatorByRef } from "../validation/schema-loader.mjs";
2
+ import { ADCP_VERSION } from "../version.mjs";
3
+ const CONTEXT_SCHEMA_REF = "media-buy/acceptance-context.json";
4
+ const PROFILE_SCHEMA_REF = "media-buy/acceptance-policy-profile.json";
5
+ const MAX_ASSESSMENT_PROFILES = 1024;
6
+ const MAX_ASSESSMENT_RULES = 1e4;
7
+ const MAX_ASSESSMENT_CONTEXT_VALUES = 4096;
8
+ const MAX_ASSESSMENT_JSON_NODES = 1e5;
9
+ const MAX_ASSESSMENT_STRING_CODE_UNITS = 1024 * 1024;
10
+ const MAX_ASSESSMENT_JSON_DEPTH = 64;
11
+ const MAX_ASSESSMENT_DIAGNOSTICS = 32;
12
+ const SURFACES = /* @__PURE__ */ new Set([
13
+ "account",
14
+ "media_buy",
15
+ "creative",
16
+ "landing_page",
17
+ "targeting",
18
+ "delivery",
19
+ "format"
20
+ ]);
21
+ const REVIEW_REQUIREMENTS = /* @__PURE__ */ new Set([
22
+ "prior_authorization",
23
+ "sales_assisted",
24
+ "funding_restriction",
25
+ "targeting_restriction",
26
+ "creative_restriction",
27
+ "destination_restriction",
28
+ "format_restriction",
29
+ "time_restriction",
30
+ "custom"
31
+ ]);
32
+ const SETUP_REQUIREMENTS = /* @__PURE__ */ new Set([
33
+ "advertiser_verification",
34
+ "advertiser_eligibility",
35
+ "certification",
36
+ "license",
37
+ "account_setup"
38
+ ]);
39
+ const DISCLOSURE_REQUIREMENTS = /* @__PURE__ */ new Set(["category_declaration", "disclosure", "transparency_reporting"]);
40
+ function diagnostic(code, profileId, ruleId) {
41
+ return {
42
+ code,
43
+ ...profileId !== void 0 && { profileId },
44
+ ...ruleId !== void 0 && { ruleId }
45
+ };
46
+ }
47
+ function dedupeDiagnostics(diagnostics) {
48
+ const seen = /* @__PURE__ */ new Set();
49
+ const result = [];
50
+ for (const value of diagnostics) {
51
+ const key = `${value.code}\0${value.profileId ?? ""}\0${value.ruleId ?? ""}`;
52
+ if (seen.has(key)) continue;
53
+ seen.add(key);
54
+ result.push(value);
55
+ if (result.length >= MAX_ASSESSMENT_DIAGNOSTICS) break;
56
+ }
57
+ return result;
58
+ }
59
+ function unknownAssessment(profileIds, diagnostics) {
60
+ return {
61
+ advisory: true,
62
+ outcome: "unknown",
63
+ profileIds,
64
+ matchingRuleIds: [],
65
+ matchedRules: [],
66
+ requirements: [],
67
+ diagnostics: dedupeDiagnostics(diagnostics)
68
+ };
69
+ }
70
+ function expandedJurisdictions(profile, groups) {
71
+ const jurisdictions = /* @__PURE__ */ new Set();
72
+ for (const group of groups) {
73
+ if (!Object.prototype.hasOwnProperty.call(profile.region_aliases ?? {}, group)) continue;
74
+ for (const jurisdiction of profile.region_aliases?.[group] ?? []) jurisdictions.add(jurisdiction);
75
+ }
76
+ return jurisdictions;
77
+ }
78
+ function profileAliasesResolve(profile) {
79
+ const aliases = profile.region_aliases ?? {};
80
+ const groups = [
81
+ ...profile.scope?.jurisdiction_groups ?? [],
82
+ ...profile.rules.flatMap((rule) => rule.jurisdiction_groups ?? [])
83
+ ];
84
+ return groups.every((group) => Object.prototype.hasOwnProperty.call(aliases, group));
85
+ }
86
+ function completeScopeCovers(profile, context, appliesTo) {
87
+ const scope = profile.scope;
88
+ if (!scope?.applies_to?.includes(appliesTo)) return false;
89
+ if (!context.subjects?.length || !scope.subject_categories?.length) return false;
90
+ const scopedCategories = new Set(scope.subject_categories);
91
+ if (context.subjects.some((subject) => !scopedCategories.has(subject.subject_category))) return false;
92
+ if (scope.all_jurisdictions === true) return true;
93
+ if (!context.delivery_jurisdictions?.length) return false;
94
+ const covered = new Set(scope.jurisdictions ?? []);
95
+ for (const jurisdiction of expandedJurisdictions(profile, scope.jurisdiction_groups ?? [])) {
96
+ covered.add(jurisdiction);
97
+ }
98
+ return context.delivery_jurisdictions.every((jurisdiction) => covered.has(jurisdiction));
99
+ }
100
+ function ruleMatches(profile, rule, contextIndex, appliesTo, evaluatedAtMs) {
101
+ if (!rule.applies_to.includes(appliesTo)) return false;
102
+ let uncertain = false;
103
+ if (rule.effective_at !== void 0) {
104
+ const effectiveAtMs = Date.parse(rule.effective_at);
105
+ if (!Number.isFinite(effectiveAtMs)) uncertain = true;
106
+ else if (evaluatedAtMs < effectiveAtMs) return false;
107
+ }
108
+ if (rule.expires_at !== void 0) {
109
+ const expiresAtMs = Date.parse(rule.expires_at);
110
+ if (!Number.isFinite(expiresAtMs)) uncertain = true;
111
+ else if (evaluatedAtMs >= expiresAtMs) return false;
112
+ }
113
+ const subjects = contextIndex.subjectsByCategory.get(rule.subject_category);
114
+ if (!contextIndex.hasSubjects) {
115
+ uncertain = true;
116
+ } else if (!subjects) {
117
+ return false;
118
+ }
119
+ if (subjects && rule.subject_facets?.length) {
120
+ if (!rule.subject_facets.some((facet) => subjects.facets.has(facet))) {
121
+ if (!subjects.hasMissingFacets) return false;
122
+ uncertain = true;
123
+ }
124
+ }
125
+ if (rule.advertiser_roles?.length) {
126
+ if (contextIndex.advertiserRoles.size === 0) {
127
+ uncertain = true;
128
+ } else if (!rule.advertiser_roles.some((role) => contextIndex.advertiserRoles.has(role))) {
129
+ return false;
130
+ }
131
+ }
132
+ if (rule.jurisdictions?.length || rule.jurisdiction_groups?.length) {
133
+ if (rule.jurisdiction_groups?.some(
134
+ (group) => !Object.prototype.hasOwnProperty.call(profile.region_aliases ?? {}, group)
135
+ )) {
136
+ return "unknown";
137
+ }
138
+ if (contextIndex.deliveryJurisdictions.size === 0) {
139
+ uncertain = true;
140
+ } else {
141
+ const expected = new Set(rule.jurisdictions ?? []);
142
+ for (const jurisdiction of expandedJurisdictions(profile, rule.jurisdiction_groups ?? [])) {
143
+ expected.add(jurisdiction);
144
+ }
145
+ if (![...expected].some((jurisdiction) => contextIndex.deliveryJurisdictions.has(jurisdiction))) return false;
146
+ }
147
+ }
148
+ return uncertain ? "unknown" : true;
149
+ }
150
+ function requirementsOutcome(requirements) {
151
+ const kinds = new Set(requirements.map((requirement) => String(requirement.kind)));
152
+ if ([...kinds].some((kind) => REVIEW_REQUIREMENTS.has(kind))) return "requires_review";
153
+ if ([...kinds].some((kind) => SETUP_REQUIREMENTS.has(kind))) return "requires_setup";
154
+ if ([...kinds].every((kind) => DISCLOSURE_REQUIREMENTS.has(kind))) return "requires_disclosure";
155
+ return "requires_review";
156
+ }
157
+ function cloneRequirement(requirement) {
158
+ return JSON.parse(JSON.stringify(requirement));
159
+ }
160
+ function consumeAssessmentComplexity(value, budget) {
161
+ const pending = [{ value, depth: 0 }];
162
+ const ancestors = /* @__PURE__ */ new WeakSet();
163
+ let queuedNodes = 1;
164
+ while (pending.length > 0) {
165
+ const current = pending.pop();
166
+ if (current.exit) {
167
+ ancestors.delete(current.value);
168
+ continue;
169
+ }
170
+ queuedNodes -= 1;
171
+ budget.nodes += 1;
172
+ if (budget.nodes > MAX_ASSESSMENT_JSON_NODES) return "limit";
173
+ if (current.depth > MAX_ASSESSMENT_JSON_DEPTH) return "invalid";
174
+ if (typeof current.value === "string") {
175
+ budget.stringCodeUnits += current.value.length;
176
+ if (budget.stringCodeUnits > MAX_ASSESSMENT_STRING_CODE_UNITS) return "limit";
177
+ continue;
178
+ }
179
+ if (current.value === null || typeof current.value !== "object") continue;
180
+ if (ancestors.has(current.value)) return "invalid";
181
+ ancestors.add(current.value);
182
+ pending.push({ ...current, exit: true });
183
+ if (Array.isArray(current.value)) {
184
+ if (budget.nodes + queuedNodes + current.value.length > MAX_ASSESSMENT_JSON_NODES) return "limit";
185
+ queuedNodes += current.value.length;
186
+ for (const child of current.value) pending.push({ value: child, depth: current.depth + 1 });
187
+ continue;
188
+ }
189
+ const record = current.value;
190
+ for (const key in record) {
191
+ if (!Object.prototype.hasOwnProperty.call(record, key)) continue;
192
+ budget.stringCodeUnits += key.length;
193
+ if (budget.stringCodeUnits > MAX_ASSESSMENT_STRING_CODE_UNITS) return "limit";
194
+ if (budget.nodes + queuedNodes + 1 > MAX_ASSESSMENT_JSON_NODES) return "limit";
195
+ queuedNodes += 1;
196
+ pending.push({ value: record[key], depth: current.depth + 1 });
197
+ }
198
+ }
199
+ return "valid";
200
+ }
201
+ function exceedsContextValueLimit(context) {
202
+ const subjects = Array.isArray(context.subjects) ? context.subjects : [];
203
+ const advertiserRoles = Array.isArray(context.advertiser_roles) ? context.advertiser_roles : [];
204
+ const deliveryJurisdictions = Array.isArray(context.delivery_jurisdictions) ? context.delivery_jurisdictions : [];
205
+ if (subjects.length > MAX_ASSESSMENT_CONTEXT_VALUES || advertiserRoles.length > MAX_ASSESSMENT_CONTEXT_VALUES || deliveryJurisdictions.length > MAX_ASSESSMENT_CONTEXT_VALUES) {
206
+ return true;
207
+ }
208
+ let values = subjects.length + advertiserRoles.length + deliveryJurisdictions.length;
209
+ for (const subject of subjects) {
210
+ if (!subject || typeof subject !== "object" || !Array.isArray(subject.subject_facets)) continue;
211
+ values += subject.subject_facets.length;
212
+ if (values > MAX_ASSESSMENT_CONTEXT_VALUES) return true;
213
+ }
214
+ return values > MAX_ASSESSMENT_CONTEXT_VALUES;
215
+ }
216
+ function assessAcceptancePolicy(input) {
217
+ if (!input || typeof input !== "object" || !Array.isArray(input.profiles)) {
218
+ return unknownAssessment([], [diagnostic("invalid_profile")]);
219
+ }
220
+ if (input.profiles.length > MAX_ASSESSMENT_PROFILES) {
221
+ return unknownAssessment([], [diagnostic("assessment_limit_exceeded")]);
222
+ }
223
+ const profileIds = [
224
+ ...new Set(
225
+ input.profiles.flatMap(
226
+ (value) => value && typeof value === "object" && value.resolution === "resolved" && typeof value.profileId === "string" ? [value.profileId] : []
227
+ )
228
+ )
229
+ ];
230
+ if (input.profiles.length === 0) {
231
+ return unknownAssessment(profileIds, [diagnostic("profile_unresolved")]);
232
+ }
233
+ if (!input.acceptanceContext || typeof input.acceptanceContext !== "object") {
234
+ return unknownAssessment(profileIds, [diagnostic("invalid_context")]);
235
+ }
236
+ if (!SURFACES.has(input.appliesTo)) {
237
+ return unknownAssessment(profileIds, [diagnostic("incomplete_context")]);
238
+ }
239
+ if (exceedsContextValueLimit(input.acceptanceContext)) {
240
+ return unknownAssessment(profileIds, [diagnostic("assessment_limit_exceeded")]);
241
+ }
242
+ const contextComplexity = consumeAssessmentComplexity(input.acceptanceContext, {
243
+ nodes: 0,
244
+ stringCodeUnits: 0
245
+ });
246
+ if (contextComplexity === "limit") {
247
+ return unknownAssessment(profileIds, [diagnostic("assessment_limit_exceeded")]);
248
+ }
249
+ if (contextComplexity === "invalid") {
250
+ return unknownAssessment(profileIds, [diagnostic("invalid_context")]);
251
+ }
252
+ let contextValidator;
253
+ let profileValidator;
254
+ try {
255
+ const adcpVersion = input.adcpVersion ?? ADCP_VERSION;
256
+ contextValidator = getSchemaValidatorByRef(CONTEXT_SCHEMA_REF, adcpVersion);
257
+ profileValidator = getSchemaValidatorByRef(PROFILE_SCHEMA_REF, adcpVersion);
258
+ } catch {
259
+ return unknownAssessment(profileIds, [diagnostic("schema_unavailable")]);
260
+ }
261
+ if (!contextValidator || !profileValidator) {
262
+ return unknownAssessment(profileIds, [diagnostic("schema_unavailable")]);
263
+ }
264
+ if (!contextValidator(input.acceptanceContext)) {
265
+ return unknownAssessment(profileIds, [diagnostic("invalid_context")]);
266
+ }
267
+ const mutableSubjectsByCategory = /* @__PURE__ */ new Map();
268
+ for (const subject of input.acceptanceContext.subjects ?? []) {
269
+ const indexed = mutableSubjectsByCategory.get(subject.subject_category) ?? {
270
+ facets: /* @__PURE__ */ new Set(),
271
+ hasMissingFacets: false
272
+ };
273
+ if (!subject.subject_facets?.length) indexed.hasMissingFacets = true;
274
+ else for (const facet of subject.subject_facets) indexed.facets.add(facet);
275
+ mutableSubjectsByCategory.set(subject.subject_category, indexed);
276
+ }
277
+ const contextIndex = {
278
+ hasSubjects: (input.acceptanceContext.subjects?.length ?? 0) > 0,
279
+ subjectsByCategory: mutableSubjectsByCategory,
280
+ advertiserRoles: new Set(input.acceptanceContext.advertiser_roles ?? []),
281
+ deliveryJurisdictions: new Set(input.acceptanceContext.delivery_jurisdictions ?? [])
282
+ };
283
+ const evaluatedAt = input.evaluatedAt ?? /* @__PURE__ */ new Date();
284
+ const evaluatedAtMs = evaluatedAt instanceof Date ? evaluatedAt.getTime() : Date.parse(evaluatedAt);
285
+ if (!Number.isFinite(evaluatedAtMs)) {
286
+ return unknownAssessment(profileIds, [diagnostic("invalid_evaluation_time")]);
287
+ }
288
+ const diagnostics = [];
289
+ const matchingRules = [];
290
+ let uncertain = false;
291
+ const profiles = [];
292
+ const profileIdentities = /* @__PURE__ */ new Map();
293
+ const seenProfileIdentities = /* @__PURE__ */ new Set();
294
+ const countedProfileIdentities = /* @__PURE__ */ new Set();
295
+ const measuredProfileObjects = /* @__PURE__ */ new WeakSet();
296
+ const invalidProfileObjects = /* @__PURE__ */ new WeakSet();
297
+ const profileValidationResults = /* @__PURE__ */ new WeakMap();
298
+ const profileAliasResults = /* @__PURE__ */ new WeakMap();
299
+ const complexityBudget = { nodes: 0, stringCodeUnits: 0 };
300
+ let candidateRuleCount = 0;
301
+ for (const value of input.profiles) {
302
+ if (!value || typeof value !== "object" || typeof value.profileId !== "string") {
303
+ uncertain = true;
304
+ diagnostics.push(diagnostic("invalid_profile"));
305
+ continue;
306
+ }
307
+ if (value.resolution !== "resolved") {
308
+ uncertain = true;
309
+ diagnostics.push(
310
+ value.resolution === "missing" || value.resolution === "unresolved" ? diagnostic("profile_unresolved", value.profileId) : diagnostic("invalid_profile", value.profileId)
311
+ );
312
+ continue;
313
+ }
314
+ const profileValue = value.profile;
315
+ if (profileValue && typeof profileValue === "object" && invalidProfileObjects.has(profileValue)) {
316
+ uncertain = true;
317
+ diagnostics.push(diagnostic("invalid_profile", value.profileId));
318
+ continue;
319
+ }
320
+ if (profileValue && typeof profileValue === "object" && !measuredProfileObjects.has(profileValue)) {
321
+ measuredProfileObjects.add(profileValue);
322
+ const complexity = consumeAssessmentComplexity(profileValue, complexityBudget);
323
+ if (complexity === "limit") {
324
+ return unknownAssessment(profileIds, [...diagnostics, diagnostic("assessment_limit_exceeded")]);
325
+ }
326
+ if (complexity === "invalid") {
327
+ invalidProfileObjects.add(profileValue);
328
+ uncertain = true;
329
+ diagnostics.push(diagnostic("invalid_profile", value.profileId));
330
+ continue;
331
+ }
332
+ }
333
+ const candidateIdentityKey = typeof value.profile?.version === "string" && typeof value.profile?.content_digest === "string" ? `${value.profileId}\0${value.profile.version}\0${value.profile.content_digest}` : void 0;
334
+ if ((candidateIdentityKey === void 0 || !countedProfileIdentities.has(candidateIdentityKey)) && Array.isArray(value.profile?.rules)) {
335
+ if (candidateIdentityKey !== void 0) countedProfileIdentities.add(candidateIdentityKey);
336
+ candidateRuleCount += value.profile.rules.length;
337
+ if (candidateRuleCount > MAX_ASSESSMENT_RULES) {
338
+ return unknownAssessment(profileIds, [...diagnostics, diagnostic("assessment_limit_exceeded")]);
339
+ }
340
+ }
341
+ let validProfile = false;
342
+ if (profileValue && typeof profileValue === "object" && profileValidationResults.has(profileValue)) {
343
+ validProfile = profileValidationResults.get(profileValue) ?? false;
344
+ } else {
345
+ try {
346
+ validProfile = profileValidator(value.profile);
347
+ } catch {
348
+ validProfile = false;
349
+ }
350
+ if (profileValue && typeof profileValue === "object") {
351
+ profileValidationResults.set(profileValue, validProfile);
352
+ }
353
+ }
354
+ if (!validProfile || value.profile.profile_id !== value.profileId) {
355
+ uncertain = true;
356
+ diagnostics.push(diagnostic("invalid_profile", value.profileId));
357
+ continue;
358
+ }
359
+ let aliasesValid = profileAliasResults.get(profileValue);
360
+ if (aliasesValid === void 0) {
361
+ aliasesValid = profileAliasesResolve(value.profile);
362
+ profileAliasResults.set(profileValue, aliasesValid);
363
+ }
364
+ if (!aliasesValid) {
365
+ uncertain = true;
366
+ diagnostics.push(diagnostic("invalid_profile", value.profileId));
367
+ continue;
368
+ }
369
+ const identityKey = `${value.profileId}\0${value.profile.version}\0${value.profile.content_digest}`;
370
+ if (seenProfileIdentities.has(identityKey)) continue;
371
+ seenProfileIdentities.add(identityKey);
372
+ const knownIdentity = profileIdentities.get(value.profileId);
373
+ if (knownIdentity && (knownIdentity.version !== value.profile.version || knownIdentity.contentDigest !== value.profile.content_digest)) {
374
+ uncertain = true;
375
+ diagnostics.push(diagnostic("profile_conflict", value.profileId));
376
+ profiles.push(value.profile);
377
+ continue;
378
+ }
379
+ if (knownIdentity) continue;
380
+ profileIdentities.set(value.profileId, {
381
+ version: value.profile.version,
382
+ contentDigest: value.profile.content_digest
383
+ });
384
+ profiles.push(value.profile);
385
+ }
386
+ for (const profile of profiles) {
387
+ if (profile.coverage === "partial") {
388
+ uncertain = true;
389
+ diagnostics.push(diagnostic("partial_coverage", profile.profile_id));
390
+ } else if (!completeScopeCovers(profile, input.acceptanceContext, input.appliesTo)) {
391
+ uncertain = true;
392
+ diagnostics.push(diagnostic("incomplete_context", profile.profile_id));
393
+ }
394
+ for (const rule of profile.rules) {
395
+ const match = ruleMatches(profile, rule, contextIndex, input.appliesTo, evaluatedAtMs);
396
+ if (match === true) matchingRules.push({ profileId: profile.profile_id, rule });
397
+ if (match === "unknown") {
398
+ uncertain = true;
399
+ diagnostics.push(diagnostic("incomplete_context", profile.profile_id, rule.rule_id));
400
+ }
401
+ }
402
+ }
403
+ const requirements = matchingRules.flatMap((value) => value.rule.requirements ?? []);
404
+ const outcome = matchingRules.some((value) => value.rule.disposition === "prohibited") ? "prohibited" : uncertain ? "unknown" : requirements.length > 0 ? requirementsOutcome(requirements) : "allowed";
405
+ return {
406
+ advisory: true,
407
+ outcome,
408
+ profileIds: [...profileIdentities.keys()],
409
+ matchingRuleIds: matchingRules.map((value) => value.rule.rule_id),
410
+ matchedRules: matchingRules.map(({ profileId, rule }) => ({
411
+ profileId,
412
+ ruleId: rule.rule_id,
413
+ disposition: rule.disposition,
414
+ policyIds: [...rule.policy_ids ?? []],
415
+ requirements: (rule.requirements ?? []).map(cloneRequirement)
416
+ })),
417
+ requirements: requirements.map(cloneRequirement),
418
+ diagnostics: dedupeDiagnostics(diagnostics)
419
+ };
420
+ }
421
+ export {
422
+ assessAcceptancePolicy
423
+ };
@@ -5,6 +5,7 @@
5
5
  * likely seller treatment; it never authorizes an exact media-buy request.
6
6
  */
7
7
  import type { ResolvePolicyResponse } from '../registry/types.mjs';
8
+ export { assessAcceptancePolicy } from './evaluator.mjs';
8
9
  export interface AcceptancePolicyDiscoveryCapability {
9
10
  catalog_url: string;
10
11
  catalog_digest: string;
@@ -15,13 +16,24 @@ export interface AcceptancePolicyReference {
15
16
  version: string;
16
17
  content_digest: string;
17
18
  }
19
+ export interface AcceptancePolicyRequirement {
20
+ kind: 'category_declaration' | 'advertiser_verification' | 'advertiser_eligibility' | 'funding_restriction' | 'certification' | 'license' | 'prior_authorization' | 'account_setup' | 'sales_assisted' | 'disclosure' | 'targeting_restriction' | 'creative_restriction' | 'destination_restriction' | 'format_restriction' | 'time_restriction' | 'transparency_reporting' | 'custom';
21
+ [key: string]: unknown;
22
+ }
23
+ export type AcceptancePolicySurface = 'account' | 'media_buy' | 'creative' | 'landing_page' | 'targeting' | 'delivery' | 'format';
18
24
  export interface AcceptancePolicyRule {
19
25
  rule_id: string;
20
26
  subject_category: string;
27
+ subject_facets?: string[];
28
+ advertiser_roles?: string[];
29
+ jurisdictions?: string[];
21
30
  jurisdiction_groups?: string[];
22
31
  applies_to: string[];
23
32
  disposition: 'allowed' | 'conditional' | 'prohibited';
33
+ requirements?: AcceptancePolicyRequirement[];
24
34
  policy_ids?: string[];
35
+ effective_at?: string;
36
+ expires_at?: string;
25
37
  [key: string]: unknown;
26
38
  }
27
39
  export interface AcceptancePolicyProfile {
@@ -31,7 +43,11 @@ export interface AcceptancePolicyProfile {
31
43
  policy_refs: AcceptancePolicyReference[];
32
44
  coverage: 'partial' | 'complete';
33
45
  scope?: {
46
+ subject_categories?: string[];
47
+ applies_to?: string[];
48
+ jurisdictions?: string[];
34
49
  jurisdiction_groups?: string[];
50
+ all_jurisdictions?: true;
35
51
  [key: string]: unknown;
36
52
  };
37
53
  region_aliases?: Record<string, string[]>;
@@ -118,6 +134,8 @@ export interface ResolveVerifiedAcceptancePolicyProfilesOptions {
118
134
  timeoutMs?: number;
119
135
  /** Maximum distinct registry profiles resolved in one call. Default and maximum 32. */
120
136
  maxRegistryProfiles?: number;
137
+ /** Caller-owned cancellation signal, composed with the registry deadline. */
138
+ signal?: AbortSignal;
121
139
  }
122
140
  export interface VerifiedAcceptancePolicyProfilesSuccess {
123
141
  ok: true;
@@ -131,6 +149,8 @@ export interface ResolveAcceptancePolicyCatalogOptions {
131
149
  adcpVersion?: string;
132
150
  /** Overall DNS/connect/body timeout for the catalog fetch. Default 5 seconds. */
133
151
  timeoutMs?: number;
152
+ /** Caller-owned cancellation signal, composed with fetch and registry deadlines. */
153
+ signal?: AbortSignal;
134
154
  /** Hard response-body cap. Default and maximum 1 MiB; callers may lower it. */
135
155
  maxBodyBytes?: number;
136
156
  /** Test/dev-only HTTP opt-in; requires allowPrivateNetwork. Production callers must leave false. */
@@ -5,6 +5,7 @@
5
5
  * likely seller treatment; it never authorizes an exact media-buy request.
6
6
  */
7
7
  import type { ResolvePolicyResponse } from '../registry/types';
8
+ export { assessAcceptancePolicy } from './evaluator';
8
9
  export interface AcceptancePolicyDiscoveryCapability {
9
10
  catalog_url: string;
10
11
  catalog_digest: string;
@@ -15,13 +16,24 @@ export interface AcceptancePolicyReference {
15
16
  version: string;
16
17
  content_digest: string;
17
18
  }
19
+ export interface AcceptancePolicyRequirement {
20
+ kind: 'category_declaration' | 'advertiser_verification' | 'advertiser_eligibility' | 'funding_restriction' | 'certification' | 'license' | 'prior_authorization' | 'account_setup' | 'sales_assisted' | 'disclosure' | 'targeting_restriction' | 'creative_restriction' | 'destination_restriction' | 'format_restriction' | 'time_restriction' | 'transparency_reporting' | 'custom';
21
+ [key: string]: unknown;
22
+ }
23
+ export type AcceptancePolicySurface = 'account' | 'media_buy' | 'creative' | 'landing_page' | 'targeting' | 'delivery' | 'format';
18
24
  export interface AcceptancePolicyRule {
19
25
  rule_id: string;
20
26
  subject_category: string;
27
+ subject_facets?: string[];
28
+ advertiser_roles?: string[];
29
+ jurisdictions?: string[];
21
30
  jurisdiction_groups?: string[];
22
31
  applies_to: string[];
23
32
  disposition: 'allowed' | 'conditional' | 'prohibited';
33
+ requirements?: AcceptancePolicyRequirement[];
24
34
  policy_ids?: string[];
35
+ effective_at?: string;
36
+ expires_at?: string;
25
37
  [key: string]: unknown;
26
38
  }
27
39
  export interface AcceptancePolicyProfile {
@@ -31,7 +43,11 @@ export interface AcceptancePolicyProfile {
31
43
  policy_refs: AcceptancePolicyReference[];
32
44
  coverage: 'partial' | 'complete';
33
45
  scope?: {
46
+ subject_categories?: string[];
47
+ applies_to?: string[];
48
+ jurisdictions?: string[];
34
49
  jurisdiction_groups?: string[];
50
+ all_jurisdictions?: true;
35
51
  [key: string]: unknown;
36
52
  };
37
53
  region_aliases?: Record<string, string[]>;
@@ -118,6 +134,8 @@ export interface ResolveVerifiedAcceptancePolicyProfilesOptions {
118
134
  timeoutMs?: number;
119
135
  /** Maximum distinct registry profiles resolved in one call. Default and maximum 32. */
120
136
  maxRegistryProfiles?: number;
137
+ /** Caller-owned cancellation signal, composed with the registry deadline. */
138
+ signal?: AbortSignal;
121
139
  }
122
140
  export interface VerifiedAcceptancePolicyProfilesSuccess {
123
141
  ok: true;
@@ -131,6 +149,8 @@ export interface ResolveAcceptancePolicyCatalogOptions {
131
149
  adcpVersion?: string;
132
150
  /** Overall DNS/connect/body timeout for the catalog fetch. Default 5 seconds. */
133
151
  timeoutMs?: number;
152
+ /** Caller-owned cancellation signal, composed with fetch and registry deadlines. */
153
+ signal?: AbortSignal;
134
154
  /** Hard response-body cap. Default and maximum 1 MiB; callers may lower it. */
135
155
  maxBodyBytes?: number;
136
156
  /** Test/dev-only HTTP opt-in; requires allowPrivateNetwork. Production callers must leave false. */