@adcp/sdk 14.0.0-rc.39 → 14.0.0-rc.40

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 (81) hide show
  1. package/README.md +2 -0
  2. package/dist/lib/core/AgentClient.d.mts +4 -3
  3. package/dist/lib/core/AgentClient.d.ts +4 -3
  4. package/dist/lib/core/AsyncHandler.d.mts +3 -0
  5. package/dist/lib/core/AsyncHandler.d.ts +3 -0
  6. package/dist/lib/core/AsyncHandler.js +3 -0
  7. package/dist/lib/core/AsyncHandler.mjs +3 -0
  8. package/dist/lib/core/SingleAgentClient.d.mts +6 -1
  9. package/dist/lib/core/SingleAgentClient.d.ts +6 -1
  10. package/dist/lib/core/SingleAgentClient.js +57 -17
  11. package/dist/lib/core/SingleAgentClient.mjs +59 -17
  12. package/dist/lib/discovery/adagents-redirects.d.mts +5 -1
  13. package/dist/lib/discovery/adagents-redirects.d.ts +5 -1
  14. package/dist/lib/discovery/adagents-redirects.js +10 -3
  15. package/dist/lib/discovery/adagents-redirects.mjs +10 -3
  16. package/dist/lib/discovery/types.d.mts +42 -0
  17. package/dist/lib/discovery/types.d.ts +42 -0
  18. package/dist/lib/index.d.mts +3 -1
  19. package/dist/lib/index.d.ts +3 -1
  20. package/dist/lib/index.js +3 -1
  21. package/dist/lib/index.mjs +1 -0
  22. package/dist/lib/net/ssrf-fetch.js +47 -21
  23. package/dist/lib/net/ssrf-fetch.mjs +47 -21
  24. package/dist/lib/registry/index.d.mts +3 -0
  25. package/dist/lib/registry/index.d.ts +3 -0
  26. package/dist/lib/registry/index.js +8 -0
  27. package/dist/lib/registry/index.mjs +8 -0
  28. package/dist/lib/registry/types.d.mts +2 -1
  29. package/dist/lib/registry/types.d.ts +2 -1
  30. package/dist/lib/schemas-data/v2.5/_provenance.json +1 -1
  31. package/dist/lib/supply-path/evaluate.d.mts +17 -0
  32. package/dist/lib/supply-path/evaluate.d.ts +17 -0
  33. package/dist/lib/supply-path/evaluate.js +436 -0
  34. package/dist/lib/supply-path/evaluate.mjs +407 -0
  35. package/dist/lib/supply-path/fetch-evidence.d.mts +36 -0
  36. package/dist/lib/supply-path/fetch-evidence.d.ts +36 -0
  37. package/dist/lib/supply-path/fetch-evidence.js +293 -0
  38. package/dist/lib/supply-path/fetch-evidence.mjs +268 -0
  39. package/dist/lib/supply-path/index.d.mts +6 -0
  40. package/dist/lib/supply-path/index.d.ts +6 -0
  41. package/dist/lib/supply-path/index.js +49 -0
  42. package/dist/lib/supply-path/index.mjs +23 -0
  43. package/dist/lib/supply-path/products.d.mts +41 -0
  44. package/dist/lib/supply-path/products.d.ts +41 -0
  45. package/dist/lib/supply-path/products.js +228 -0
  46. package/dist/lib/supply-path/products.mjs +204 -0
  47. package/dist/lib/supply-path/revocations.d.mts +55 -0
  48. package/dist/lib/supply-path/revocations.d.ts +55 -0
  49. package/dist/lib/supply-path/revocations.js +127 -0
  50. package/dist/lib/supply-path/revocations.mjs +99 -0
  51. package/dist/lib/supply-path/types.d.mts +168 -0
  52. package/dist/lib/supply-path/types.d.ts +168 -0
  53. package/dist/lib/supply-path/types.js +16 -0
  54. package/dist/lib/supply-path/types.mjs +0 -0
  55. package/dist/lib/supply-path/validation.d.mts +10 -0
  56. package/dist/lib/supply-path/validation.d.ts +10 -0
  57. package/dist/lib/supply-path/validation.js +152 -0
  58. package/dist/lib/supply-path/validation.mjs +122 -0
  59. package/dist/lib/supply-path/verify.d.mts +25 -0
  60. package/dist/lib/supply-path/verify.d.ts +25 -0
  61. package/dist/lib/supply-path/verify.js +218 -0
  62. package/dist/lib/supply-path/verify.mjs +203 -0
  63. package/dist/lib/testing/storyboard/request-signing/grader.js +19 -9
  64. package/dist/lib/testing/storyboard/request-signing/grader.mjs +19 -9
  65. package/dist/lib/testing/storyboard/validations.d.mts +1 -1
  66. package/dist/lib/testing/storyboard/validations.d.ts +1 -1
  67. package/dist/lib/v2/projection/creative-delivery.d.mts +1 -1
  68. package/dist/lib/v2/projection/creative-delivery.d.ts +1 -1
  69. package/dist/lib/validation/schema-loader.js +7 -5
  70. package/dist/lib/validation/schema-loader.mjs +7 -5
  71. package/dist/lib/version.d.mts +3 -3
  72. package/dist/lib/version.d.ts +3 -3
  73. package/dist/lib/version.js +3 -3
  74. package/dist/lib/version.mjs +3 -3
  75. package/docs/TYPE-SUMMARY.md +2 -2
  76. package/docs/guides/SUPPLY-PATH-VERIFICATION.md +101 -0
  77. package/docs/llms.txt +2 -2
  78. package/docs/migration-14.x-rc-worksheet.md +4 -4
  79. package/examples/README.md +2 -0
  80. package/examples/supply-path-verification.ts +29 -0
  81. package/package.json +2 -1
@@ -0,0 +1,407 @@
1
+ import { resolveAgentProperties } from "../discovery/resolve-agent-properties.mjs";
2
+ import {
3
+ parsePublisherPropertySelector,
4
+ expandPublisherPropertySelector
5
+ } from "../discovery/publisher-property-selector.mjs";
6
+ import { MediaChannelValues, PropertyIdentifierTypesValues, PropertyTypeValues } from "../types/enums.generated.mjs";
7
+ import { agentIdentity, domain, record, records, strings } from "./validation.mjs";
8
+ const SUPPLY_PATH_STATES = ["unverified", "owner_attested", "host_delegated", "verified_owner_sold"];
9
+ function parseInventoryPartnerDomains(content) {
10
+ const partners = /* @__PURE__ */ new Set();
11
+ for (const line of content.split(/\r?\n/)) {
12
+ const match = line.trim().match(/^inventorypartnerdomain\s*=\s*([A-Za-z0-9.-]+)\s*(?:#.*)?$/i);
13
+ const value = match ? domain(match[1]) : null;
14
+ if (value) partners.add(value);
15
+ }
16
+ return [...partners];
17
+ }
18
+ function revoked(manifest, publisher) {
19
+ if (manifest.revoked_publisher_domains === void 0) return false;
20
+ if (!Array.isArray(manifest.revoked_publisher_domains)) return true;
21
+ return manifest.revoked_publisher_domains.some(
22
+ (item) => !domain(record(item) ? item.publisher_domain : item) || domain(record(item) ? item.publisher_domain : item) === publisher
23
+ );
24
+ }
25
+ function coversCollection(entry, owner, id) {
26
+ if (entry.collections === void 0) return true;
27
+ if (!Array.isArray(entry.collections) || entry.collections.length === 0) return false;
28
+ if (!entry.collections.every(
29
+ (selector) => record(selector) && domain(selector.publisher_domain) && (selector.collection_ids === void 0 || strings(selector.collection_ids))
30
+ ))
31
+ return false;
32
+ return entry.collections.some(
33
+ (selector) => domain(selector.publisher_domain) === owner && (selector.collection_ids === void 0 || id !== void 0 && selector.collection_ids.includes(id))
34
+ );
35
+ }
36
+ const PROPERTY_TYPES = new Set(PropertyTypeValues);
37
+ const PROPERTY_IDENTIFIER_TYPES = /* @__PURE__ */ new Set([
38
+ ...PropertyIdentifierTypesValues,
39
+ // Retained only for the pinned canonical corpus, whose CTV properties use
40
+ // this distribution identifier in property records.
41
+ "roku_channel_id"
42
+ ]);
43
+ const PROPERTY_CHANNELS = new Set(MediaChannelValues);
44
+ function isPropertyToken(value) {
45
+ return typeof value === "string" && /^[a-z0-9_]+$/.test(value);
46
+ }
47
+ function isPropertyTokenList(value) {
48
+ return Array.isArray(value) && value.length > 0 && value.every(isPropertyToken);
49
+ }
50
+ function isOptionalUniqueList(value, valid) {
51
+ return value === void 0 || Array.isArray(value) && value.every(valid) && new Set(value).size === value.length;
52
+ }
53
+ function isValidPropertyRecord(value) {
54
+ if (!record(value) || typeof value.property_type !== "string" || !PROPERTY_TYPES.has(value.property_type) || typeof value.name !== "string" || value.name.length === 0 || !Array.isArray(value.identifiers) || value.identifiers.length === 0) {
55
+ return false;
56
+ }
57
+ if (value.property_id !== void 0 && !isPropertyToken(value.property_id)) return false;
58
+ if (value.publisher_domain !== void 0 && !domain(value.publisher_domain)) return false;
59
+ if (!isOptionalUniqueList(value.tags, isPropertyToken) || !isOptionalUniqueList(value.supported_channels, (item) => typeof item === "string" && PROPERTY_CHANNELS.has(item))) {
60
+ return false;
61
+ }
62
+ return value.identifiers.every(
63
+ (identifier) => record(identifier) && typeof identifier.type === "string" && PROPERTY_IDENTIFIER_TYPES.has(identifier.type) && typeof identifier.value === "string" && identifier.value.length > 0
64
+ );
65
+ }
66
+ function hasPropertyAuthorizationEnvelope(entry) {
67
+ if (entry.authorization_type === "property_ids") return isPropertyTokenList(entry.property_ids);
68
+ if (entry.authorization_type === "property_tags") return isPropertyTokenList(entry.property_tags);
69
+ if (entry.authorization_type === "inline_properties") {
70
+ return Array.isArray(entry.properties) && entry.properties.length > 0 && entry.properties.every(isValidPropertyRecord);
71
+ }
72
+ if (entry.authorization_type === "publisher_properties") {
73
+ if (!Array.isArray(entry.publisher_properties) || entry.publisher_properties.length === 0 || !entry.publisher_properties.every(record)) {
74
+ return false;
75
+ }
76
+ try {
77
+ for (const raw of entry.publisher_properties) {
78
+ const selector = parsePublisherPropertySelector(raw);
79
+ const value = raw;
80
+ if (selector.selection_type === "by_id" && (!isPropertyTokenList(value.property_ids) || value.property_tags !== void 0) || selector.selection_type === "by_tag" && (!isPropertyTokenList(value.property_tags) || value.property_ids !== void 0) || selector.selection_type === "all" && (value.property_ids !== void 0 || value.property_tags !== void 0)) {
81
+ return false;
82
+ }
83
+ }
84
+ return true;
85
+ } catch {
86
+ return false;
87
+ }
88
+ }
89
+ return false;
90
+ }
91
+ function propertyScope(entry, host, manifest, requirePublisher = false) {
92
+ if (revoked(manifest, host)) return /* @__PURE__ */ new Set();
93
+ if (!["property_ids", "property_tags", "publisher_properties", "inline_properties"].includes(
94
+ String(entry.authorization_type)
95
+ ))
96
+ return /* @__PURE__ */ new Set();
97
+ if (entry.authorization_type === "publisher_properties") {
98
+ if (!Array.isArray(entry.publisher_properties) || entry.publisher_properties.length === 0) return /* @__PURE__ */ new Set();
99
+ const properties = records(manifest.properties).filter(
100
+ (p) => isValidPropertyRecord(p) && (p.publisher_domain === void 0 && !requirePublisher || domain(p.publisher_domain) === host)
101
+ );
102
+ const ids = /* @__PURE__ */ new Set();
103
+ try {
104
+ for (const raw of entry.publisher_properties) {
105
+ const selector = parsePublisherPropertySelector(raw);
106
+ if ("property_ids" in selector && !strings(selector.property_ids)) return /* @__PURE__ */ new Set();
107
+ if ("property_tags" in selector && !strings(selector.property_tags)) return /* @__PURE__ */ new Set();
108
+ for (const single of expandPublisherPropertySelector(selector)) {
109
+ if (domain(single.publisher_domain) !== host) continue;
110
+ for (const property of properties) {
111
+ if (typeof property.property_id !== "string") continue;
112
+ if (single.selection_type === "all" || single.selection_type === "by_id" && single.property_ids.includes(property.property_id) || single.selection_type === "by_tag" && strings(property.tags) && property.tags.some((t) => single.property_tags.includes(t))) {
113
+ ids.add(property.property_id);
114
+ }
115
+ }
116
+ }
117
+ }
118
+ } catch {
119
+ return /* @__PURE__ */ new Set();
120
+ }
121
+ return ids;
122
+ }
123
+ if (entry.authorization_type === "property_ids" && !strings(entry.property_ids)) return /* @__PURE__ */ new Set();
124
+ if (entry.authorization_type === "property_tags" && !strings(entry.property_tags)) return /* @__PURE__ */ new Set();
125
+ if (manifest.properties !== void 0 && (!Array.isArray(manifest.properties) || !manifest.properties.every(record)))
126
+ return /* @__PURE__ */ new Set();
127
+ if (entry.authorization_type === "inline_properties" && (!Array.isArray(entry.properties) || !entry.properties.every(record)))
128
+ return /* @__PURE__ */ new Set();
129
+ const result = resolveAgentProperties(
130
+ { ...manifest, authorized_agents: [entry] },
131
+ String(entry.url)
132
+ );
133
+ return new Set(
134
+ result.properties.filter(
135
+ (p) => isValidPropertyRecord(p) && (p.publisher_domain === void 0 && !requirePublisher || domain(p.publisher_domain) === host)
136
+ ).map((p) => p.property_id).filter((id) => typeof id === "string" && id.length > 0)
137
+ );
138
+ }
139
+ function isUnqualifiedPropertySelector(value) {
140
+ return record(value) && Object.keys(value).every(
141
+ (key) => ["publisher_domain", "publisher_domains", "selection_type", "property_ids", "property_tags"].includes(key)
142
+ );
143
+ }
144
+ function hasValidPropertySelectorPredicate(value) {
145
+ if (!isUnqualifiedPropertySelector(value)) return false;
146
+ try {
147
+ const selector = parsePublisherPropertySelector(value);
148
+ const raw = value;
149
+ if (selector.selection_type === "by_id")
150
+ return isPropertyTokenList(raw.property_ids) && raw.property_tags === void 0;
151
+ if (selector.selection_type === "by_tag")
152
+ return isPropertyTokenList(raw.property_tags) && raw.property_ids === void 0;
153
+ return raw.property_ids === void 0 && raw.property_tags === void 0;
154
+ } catch {
155
+ return false;
156
+ }
157
+ }
158
+ const UNDERSTOOD_GRANT_FIELDS = /* @__PURE__ */ new Set([
159
+ "url",
160
+ "authorized_for",
161
+ "authorization_type",
162
+ "property_ids",
163
+ "property_tags",
164
+ "properties",
165
+ "publisher_properties",
166
+ "collections",
167
+ "delegation_type",
168
+ "exclusive",
169
+ "signing_keys",
170
+ "encryption_keys",
171
+ "last_updated"
172
+ ]);
173
+ function unsupportedConstraints(entry) {
174
+ const unknown = Object.keys(entry).filter((key) => !UNDERSTOOD_GRANT_FIELDS.has(key));
175
+ for (const selector of records(entry.collections)) {
176
+ unknown.push(
177
+ ...Object.keys(selector).filter((key) => !["publisher_domain", "collection_ids"].includes(key)).map((key) => `collections[].${key}`)
178
+ );
179
+ }
180
+ for (const selector of records(entry.publisher_properties)) {
181
+ unknown.push(
182
+ ...Object.keys(selector).filter(
183
+ (key) => !["publisher_domain", "publisher_domains", "selection_type", "property_ids", "property_tags"].includes(key)
184
+ ).map((key) => `publisher_properties[].${key}`)
185
+ );
186
+ }
187
+ return unknown;
188
+ }
189
+ function nodeCost(value) {
190
+ const stack = [value];
191
+ let cost = 0;
192
+ while (stack.length) {
193
+ const item = stack.pop();
194
+ if (++cost > 32768) return Infinity;
195
+ if (typeof item === "string" && item.length > 8192) return Infinity;
196
+ if (Array.isArray(item)) {
197
+ if (item.length > 1024) return Infinity;
198
+ stack.push(...item);
199
+ } else if (record(item)) {
200
+ const values = Object.values(item);
201
+ if (values.length > 1024) return Infinity;
202
+ stack.push(...values);
203
+ }
204
+ }
205
+ return cost;
206
+ }
207
+ function evaluationLimitVerdict() {
208
+ const failure = { ok: false, failure: "evaluation_limit_exceeded" };
209
+ return {
210
+ semantics_version: "1",
211
+ state: "unverified",
212
+ legs: {
213
+ owner_collection_declared: { ...failure },
214
+ owner_distribution_carriage: { ...failure },
215
+ owner_agent_declared: { ...failure },
216
+ host_authorization: { ...failure },
217
+ inventory_partner_domain: { ...failure }
218
+ }
219
+ };
220
+ }
221
+ function evaluateSupplyPath(input) {
222
+ const total = nodeCost(input);
223
+ if (!Number.isFinite(total)) return evaluationLimitVerdict();
224
+ const grants = nodeCost(input.hostManifest?.authorized_agents);
225
+ const properties = nodeCost(input.hostManifest?.properties);
226
+ const collections = records(input.ownerManifest?.collections).length;
227
+ if (!Number.isFinite(total) || grants * properties + Math.max(1, collections) * total * 4 > 4e6) {
228
+ return evaluationLimitVerdict();
229
+ }
230
+ if (input.collectionId === void 0) {
231
+ const collections2 = records(input.ownerManifest?.collections).filter(
232
+ (c) => typeof c.collection_id === "string" && c.collection_id.length > 0
233
+ );
234
+ if (collections2.length) {
235
+ const verdicts = collections2.map((c) => evaluateSupplyPath({ ...input, collectionId: c.collection_id }));
236
+ return verdicts.reduce(
237
+ (best, next) => SUPPLY_PATH_STATES.indexOf(next.state) > SUPPLY_PATH_STATES.indexOf(best.state) ? next : best
238
+ );
239
+ }
240
+ }
241
+ const owner = domain(input.ownerDomain);
242
+ const host = domain(input.hostDomain);
243
+ const agent = agentIdentity(input.agentUrl);
244
+ const ownerManifest = record(input.ownerManifest) ? input.ownerManifest : null;
245
+ const hostManifest = record(input.hostManifest) ? input.hostManifest : null;
246
+ const isRevoked = (role, publisher) => {
247
+ const manifest = role === "owner" ? ownerManifest : hostManifest;
248
+ return manifest !== null && revoked(manifest, publisher) || (input.heldRevocations?.[role] ?? []).some((value) => domain(value) === publisher);
249
+ };
250
+ const collectionLeg = { ok: false };
251
+ const declared = records(ownerManifest?.collections).filter(
252
+ (c) => c.publisher_domain === void 0 && !input.requireExplicitOwnerPublisherDomain || domain(c.publisher_domain) === owner
253
+ );
254
+ const targets = declared.filter((c) => c.collection_id === input.collectionId && typeof c.collection_id === "string");
255
+ if (!ownerManifest) collectionLeg.failure = "manifest_not_found";
256
+ else if (owner && isRevoked("owner", owner)) collectionLeg.failure = "publisher_revoked";
257
+ else if (!input.collectionId && !declared.length) collectionLeg.failure = "no_collections_declared";
258
+ else if (!owner || !input.collectionId?.trim() || targets.length !== 1) {
259
+ collectionLeg.failure = "collection_not_declared";
260
+ collectionLeg.detail = `owner declares ${declared.length} collection(s); none has an unambiguous collection_id "${input.collectionId}"`;
261
+ } else {
262
+ collectionLeg.ok = true;
263
+ if (typeof targets[0].kind === "string") collectionLeg.detail = `kind: ${targets[0].kind}`;
264
+ }
265
+ const carriage = { ok: false };
266
+ const entries = collectionLeg.ok ? records(targets[0].distribution).filter((e) => domain(e.publisher_domain) === host) : [];
267
+ let claimed = [];
268
+ if (!collectionLeg.ok) carriage.failure = "collection_leg_failed";
269
+ else if (!host || !entries.length) carriage.failure = "no_distribution_for_host";
270
+ else {
271
+ const valid = entries.every(
272
+ (e) => Object.keys(e).every((key) => ["publisher_domain", "property_ids", "identifiers"].includes(key)) && (e.property_ids === void 0 || strings(e.property_ids)) && (e.identifiers === void 0 || Array.isArray(e.identifiers) && e.identifiers.length > 0 && e.identifiers.every(
273
+ (i) => record(i) && typeof i.type === "string" && i.type.length > 0 && typeof i.value === "string" && i.value.length > 0
274
+ )) && (e.property_ids !== void 0 || e.identifiers !== void 0)
275
+ );
276
+ claimed = [...new Set(entries.flatMap((e) => strings(e.property_ids) ? e.property_ids : []))];
277
+ if (!valid) {
278
+ carriage.failure = "property_ids_unresolved";
279
+ carriage.detail = "Malformed distribution constraints";
280
+ } else if (!claimed.length) {
281
+ carriage.ok = true;
282
+ carriage.detail = "carriage asserted without host property_ids (identifiers only)";
283
+ } else if (!hostManifest) {
284
+ carriage.failure = "host_manifest_not_found";
285
+ carriage.property_ids_unmatched = claimed;
286
+ } else {
287
+ const properties2 = records(hostManifest.properties).filter(
288
+ (p) => isValidPropertyRecord(p) && (p.publisher_domain === void 0 && !input.requireExplicitHostPublisherDomain || domain(p.publisher_domain) === host)
289
+ );
290
+ const counts = /* @__PURE__ */ new Map();
291
+ for (const property of properties2) counts.set(property.property_id, (counts.get(property.property_id) ?? 0) + 1);
292
+ const known = new Set([...counts].filter(([, count]) => count === 1).map(([id]) => id));
293
+ carriage.property_ids_matched = claimed.filter((id) => known.has(id));
294
+ carriage.property_ids_unmatched = claimed.filter((id) => !known.has(id));
295
+ carriage.ok = carriage.property_ids_unmatched.length === 0;
296
+ if (!carriage.ok) carriage.failure = "property_ids_unresolved";
297
+ }
298
+ }
299
+ const ownerAgent = !ownerManifest ? { ok: false, failure: "manifest_not_found" } : agent && records(ownerManifest.authorized_agents).some(
300
+ (e) => agentIdentity(e.url) === agent && owner && hasPropertyAuthorizationEnvelope(e) && (!input.requireExplicitOwnerPublisherDomain || e.collections !== void 0) && coversCollection(e, owner, input.collectionId) && !unsupportedConstraints(e).length
301
+ ) ? { ok: true } : { ok: false, failure: "agent_not_declared_by_owner" };
302
+ const hostLeg = { ok: false };
303
+ if (!hostManifest) hostLeg.failure = "manifest_not_found";
304
+ else {
305
+ const agents = agent ? records(hostManifest.authorized_agents).filter((e) => agentIdentity(e.url) === agent) : [];
306
+ const covered = owner && !isRevoked("host", owner) && !(host && isRevoked("owner", host)) ? agents.filter((e) => e.collections !== void 0 && coversCollection(e, owner, input.collectionId)) : [];
307
+ if (!agents.length) hostLeg.failure = "no_agent_entry";
308
+ else if (!covered.length) hostLeg.failure = "collection_scope_mismatch";
309
+ else {
310
+ const matched = host && !isRevoked("host", host) && carriage.ok && collectionLeg.ok && covered.find((e) => {
311
+ if (!hasPropertyAuthorizationEnvelope(e) || unsupportedConstraints(e).length) return false;
312
+ const scope = propertyScope(e, host, hostManifest, input.requireExplicitHostPublisherDomain);
313
+ const required = input.requiredHostPropertyIds ?? claimed;
314
+ if (input.requiredHostPropertyIds && (!required.length || claimed.length && !required.every((id) => claimed.includes(id))))
315
+ return false;
316
+ if (!claimed.length) return false;
317
+ return required.length ? required.every((id) => scope.has(id)) : scope.size > 0;
318
+ });
319
+ if (!matched) {
320
+ const constraints = [...new Set(covered.flatMap(unsupportedConstraints))].sort();
321
+ hostLeg.failure = covered.every((e) => unsupportedConstraints(e).length) ? "unsupported_constraints" : "property_scope_mismatch";
322
+ hostLeg.detail = constraints.length ? `Unevaluated grant fields: ${constraints.join(", ")}` : "No grant proves the complete carried property scope";
323
+ } else {
324
+ hostLeg.ok = true;
325
+ hostLeg.matched_entry = {
326
+ url: String(matched.url),
327
+ ...typeof matched.authorization_type === "string" ? { authorization_type: matched.authorization_type } : {},
328
+ ...typeof matched.delegation_type === "string" ? { delegation_type: matched.delegation_type } : {},
329
+ ...Array.isArray(matched.collections) ? {
330
+ collections: matched.collections.map((s) => ({
331
+ publisher_domain: String(s.publisher_domain),
332
+ ...s.collection_ids !== void 0 ? { collection_ids: [...s.collection_ids] } : {}
333
+ }))
334
+ } : {}
335
+ };
336
+ }
337
+ }
338
+ }
339
+ const partners = input.hostInventoryPartnerDomainsByFile ? inventoryPartnersForInput(input) : input.hostInventoryPartnerDomains;
340
+ const partner = input.inventoryPartnerDomainEvaluated === false ? { ok: false, failure: "not_evaluated" } : partners === null ? { ok: false, failure: "ads_txt_unavailable" } : owner && partners.some((d) => domain(d) === owner) ? { ok: true } : { ok: false, failure: "not_declared" };
341
+ const attested = collectionLeg.ok && entries.length > 0;
342
+ const state = hostLeg.ok && collectionLeg.ok && carriage.ok ? "verified_owner_sold" : partner.ok && ownerAgent.ok && collectionLeg.ok && !(owner && isRevoked("host", owner) || host && isRevoked("host", host)) && !(host && isRevoked("owner", host)) ? "host_delegated" : attested ? "owner_attested" : "unverified";
343
+ return {
344
+ semantics_version: "1",
345
+ ...collectionLeg.ok ? { resolved_collection_id: input.collectionId } : {},
346
+ state,
347
+ legs: {
348
+ owner_collection_declared: collectionLeg,
349
+ owner_distribution_carriage: carriage,
350
+ owner_agent_declared: ownerAgent,
351
+ host_authorization: hostLeg,
352
+ inventory_partner_domain: partner
353
+ }
354
+ };
355
+ }
356
+ function supplyPathAdsTxtPolicy(input) {
357
+ if (input.collectionId === void 0) {
358
+ const candidates = records(input.ownerManifest?.collections).filter(
359
+ (c) => typeof c.collection_id === "string" && c.collection_id.length > 0
360
+ );
361
+ if (candidates.length) {
362
+ const policies = candidates.map((c) => supplyPathAdsTxtPolicy({ ...input, collectionId: String(c.collection_id) }));
363
+ return { files: [...new Set(policies.flatMap((policy) => policy.files))].sort(), requireAll: false };
364
+ }
365
+ }
366
+ const collectionIds = new Set(
367
+ records(input.ownerManifest?.collections).filter((c) => input.collectionId === void 0 || c.collection_id === input.collectionId).flatMap(
368
+ (c) => records(c.distribution).filter((d) => domain(d.publisher_domain) === domain(input.hostDomain)).flatMap((d) => strings(d.property_ids) ? d.property_ids : [])
369
+ )
370
+ );
371
+ const requested = input.requiredHostPropertyIds ? new Set(input.requiredHostPropertyIds) : collectionIds;
372
+ const properties = records(input.hostManifest?.properties).filter(
373
+ (p) => requested.has(String(p.property_id)) && (p.publisher_domain === void 0 && !input.requireExplicitHostPublisherDomain || domain(p.publisher_domain) === domain(input.hostDomain))
374
+ );
375
+ const files = /* @__PURE__ */ new Set();
376
+ for (const property of properties) {
377
+ if (property.property_type === "website") files.add("ads.txt");
378
+ if (property.property_type === "mobile_app" || property.property_type === "ctv_app") files.add("app-ads.txt");
379
+ }
380
+ if (!input.hostManifest || !properties.length) return { files: ["app-ads.txt", "ads.txt"], requireAll: false };
381
+ return { files: [...files].sort(), requireAll: true };
382
+ }
383
+ function combineInventoryPartnerDomains(contents, requireAll) {
384
+ if (!contents.length || requireAll && contents.some((c) => c === null)) return null;
385
+ const fetched = contents.filter((c) => c !== null).map(parseInventoryPartnerDomains);
386
+ if (!fetched.length) return null;
387
+ return requireAll ? fetched[0].filter((d) => fetched.every((domains) => domains.includes(d))) : [...new Set(fetched.flat())];
388
+ }
389
+ function inventoryPartnersForInput(input) {
390
+ const policy = supplyPathAdsTxtPolicy(input);
391
+ const values = policy.files.map((file) => input.hostInventoryPartnerDomainsByFile?.[file] ?? null);
392
+ if (!values.length || policy.requireAll && values.some((value) => value === null)) return null;
393
+ const fetched = values.filter((value) => value !== null);
394
+ if (!fetched.length) return null;
395
+ return policy.requireAll ? fetched[0].filter((domain2) => fetched.every((value) => value.includes(domain2))) : [...new Set(fetched.flat())];
396
+ }
397
+ export {
398
+ SUPPLY_PATH_STATES,
399
+ combineInventoryPartnerDomains,
400
+ evaluateSupplyPath,
401
+ evaluationLimitVerdict,
402
+ hasValidPropertySelectorPredicate,
403
+ inventoryPartnersForInput,
404
+ isUnqualifiedPropertySelector,
405
+ parseInventoryPartnerDomains,
406
+ supplyPathAdsTxtPolicy
407
+ };
@@ -0,0 +1,36 @@
1
+ import { type SsrfFetchResult } from '../net/ssrf-fetch.mjs';
2
+ import type { AuthoritativeSupplyPathOptions, SupplyPathEvidence, SupplyPathManifest } from './types.mjs';
3
+ import { type SupplyPathRevocation } from './revocations.mjs';
4
+ export declare function boundedOption(value: number | undefined, fallback: number, maximum: number, name: string): number;
5
+ /** A verification-local evidence session. No cached verdict survives a request. */
6
+ export declare class SupplyPathEvidenceSession {
7
+ private readonly options;
8
+ readonly evidence: SupplyPathEvidence[];
9
+ readonly crossOriginAuthorities: Set<string>;
10
+ private readonly observedRevocations;
11
+ readonly revocations: Map<string, readonly SupplyPathRevocation[]>;
12
+ readonly signal: AbortSignal;
13
+ private readonly maxBodyBytes;
14
+ private readonly timeoutMs;
15
+ private readonly deadlineAt;
16
+ private readonly timer;
17
+ private readonly controller;
18
+ private readonly abort;
19
+ private readonly responses;
20
+ private readonly provenance;
21
+ private observedBytes;
22
+ private readonly documents;
23
+ constructor(options: AuthoritativeSupplyPathOptions);
24
+ close(): void;
25
+ /** Enforce the absolute deadline even while synchronous evidence evaluation blocks timer delivery. */
26
+ assertActive(): void;
27
+ adagents(publisher: string): Promise<SupplyPathManifest | null>;
28
+ read(publisher: string, kind: SupplyPathEvidence['kind'], url: string, pointer?: boolean): Promise<SsrfFetchResult | null>;
29
+ private fetchEvidence;
30
+ private parseManifest;
31
+ private documentError;
32
+ private readAdagents;
33
+ private checkAuthority;
34
+ private readAndRemember;
35
+ private rememberRevocations;
36
+ }
@@ -0,0 +1,36 @@
1
+ import { type SsrfFetchResult } from '../net/ssrf-fetch';
2
+ import type { AuthoritativeSupplyPathOptions, SupplyPathEvidence, SupplyPathManifest } from './types';
3
+ import { type SupplyPathRevocation } from './revocations';
4
+ export declare function boundedOption(value: number | undefined, fallback: number, maximum: number, name: string): number;
5
+ /** A verification-local evidence session. No cached verdict survives a request. */
6
+ export declare class SupplyPathEvidenceSession {
7
+ private readonly options;
8
+ readonly evidence: SupplyPathEvidence[];
9
+ readonly crossOriginAuthorities: Set<string>;
10
+ private readonly observedRevocations;
11
+ readonly revocations: Map<string, readonly SupplyPathRevocation[]>;
12
+ readonly signal: AbortSignal;
13
+ private readonly maxBodyBytes;
14
+ private readonly timeoutMs;
15
+ private readonly deadlineAt;
16
+ private readonly timer;
17
+ private readonly controller;
18
+ private readonly abort;
19
+ private readonly responses;
20
+ private readonly provenance;
21
+ private observedBytes;
22
+ private readonly documents;
23
+ constructor(options: AuthoritativeSupplyPathOptions);
24
+ close(): void;
25
+ /** Enforce the absolute deadline even while synchronous evidence evaluation blocks timer delivery. */
26
+ assertActive(): void;
27
+ adagents(publisher: string): Promise<SupplyPathManifest | null>;
28
+ read(publisher: string, kind: SupplyPathEvidence['kind'], url: string, pointer?: boolean): Promise<SsrfFetchResult | null>;
29
+ private fetchEvidence;
30
+ private parseManifest;
31
+ private documentError;
32
+ private readAdagents;
33
+ private checkAuthority;
34
+ private readAndRemember;
35
+ private rememberRevocations;
36
+ }