@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,41 @@
1
+ import type { ListProductsResponse } from '../types/index.mjs';
2
+ import type { SupplyPathState, RegistrySupplyPathResult, AuthoritativeSupplyPathResult, RegistrySupplyPathOptions, AuthoritativeSupplyPathOptions } from './types.mjs';
3
+ export interface ProductSupplyPathAnnotation {
4
+ /** Weakest external owner/host/collection path. Not a whole-product authorization decision. */
5
+ supply_path_state?: SupplyPathState;
6
+ supply_path_verification?: ({
7
+ source: 'registry';
8
+ scope: 'owner_host_collection';
9
+ paths: RegistrySupplyPathResult[];
10
+ } | {
11
+ source: 'authoritative';
12
+ scope: 'product_properties';
13
+ paths: AuthoritativeSupplyPathResult[];
14
+ }) & {
15
+ errors: string[];
16
+ };
17
+ }
18
+ export type AnnotatedSupplyPathProduct<T extends object> = Omit<T, keyof ProductSupplyPathAnnotation> & ProductSupplyPathAnnotation;
19
+ type AnnotatedListProductsResponse<T> = T extends unknown ? {
20
+ [K in keyof T]: K extends 'products' ? Array<AnnotatedSupplyPathProduct<NonNullable<ListProductsResponse['products']>[number]>> : T[K];
21
+ } : never;
22
+ export type ListProductsResponseWithSupplyPath = AnnotatedListProductsResponse<ListProductsResponse>;
23
+ type ProductSupplyPathVerificationOptions = RegistrySupplyPathOptions | Omit<AuthoritativeSupplyPathOptions, 'propertySelectors'>;
24
+ export type ProductSupplyPathOptions = ProductSupplyPathVerificationOptions & {
25
+ /** Maximum distinct paths in one discovery response. Default 64, max 256. */
26
+ maxPaths?: number;
27
+ /** Overall batch deadline, including all paths. Default 15 seconds, max 60 seconds. */
28
+ timeoutMs?: number;
29
+ signal?: AbortSignal;
30
+ };
31
+ /**
32
+ * Annotate products with external-domain collection paths. Preserves actual
33
+ * seller products, order, and all unrelated fields. Seller-authored verification
34
+ * fields are replaced. No result means no evidence, never a synthetic product.
35
+ *
36
+ * Authoritative mode also proves the selected host property scope. The registry
37
+ * endpoint accepts only path identity. Neither mode supplies placement, country,
38
+ * or time context or replaces the caller's buying policy.
39
+ */
40
+ export declare function annotateProductsSupplyPaths<T extends object>(products: readonly T[], agentUrl: string, options?: ProductSupplyPathOptions): Promise<Array<AnnotatedSupplyPathProduct<T>>>;
41
+ export {};
@@ -0,0 +1,41 @@
1
+ import type { ListProductsResponse } from '../types';
2
+ import type { SupplyPathState, RegistrySupplyPathResult, AuthoritativeSupplyPathResult, RegistrySupplyPathOptions, AuthoritativeSupplyPathOptions } from './types';
3
+ export interface ProductSupplyPathAnnotation {
4
+ /** Weakest external owner/host/collection path. Not a whole-product authorization decision. */
5
+ supply_path_state?: SupplyPathState;
6
+ supply_path_verification?: ({
7
+ source: 'registry';
8
+ scope: 'owner_host_collection';
9
+ paths: RegistrySupplyPathResult[];
10
+ } | {
11
+ source: 'authoritative';
12
+ scope: 'product_properties';
13
+ paths: AuthoritativeSupplyPathResult[];
14
+ }) & {
15
+ errors: string[];
16
+ };
17
+ }
18
+ export type AnnotatedSupplyPathProduct<T extends object> = Omit<T, keyof ProductSupplyPathAnnotation> & ProductSupplyPathAnnotation;
19
+ type AnnotatedListProductsResponse<T> = T extends unknown ? {
20
+ [K in keyof T]: K extends 'products' ? Array<AnnotatedSupplyPathProduct<NonNullable<ListProductsResponse['products']>[number]>> : T[K];
21
+ } : never;
22
+ export type ListProductsResponseWithSupplyPath = AnnotatedListProductsResponse<ListProductsResponse>;
23
+ type ProductSupplyPathVerificationOptions = RegistrySupplyPathOptions | Omit<AuthoritativeSupplyPathOptions, 'propertySelectors'>;
24
+ export type ProductSupplyPathOptions = ProductSupplyPathVerificationOptions & {
25
+ /** Maximum distinct paths in one discovery response. Default 64, max 256. */
26
+ maxPaths?: number;
27
+ /** Overall batch deadline, including all paths. Default 15 seconds, max 60 seconds. */
28
+ timeoutMs?: number;
29
+ signal?: AbortSignal;
30
+ };
31
+ /**
32
+ * Annotate products with external-domain collection paths. Preserves actual
33
+ * seller products, order, and all unrelated fields. Seller-authored verification
34
+ * fields are replaced. No result means no evidence, never a synthetic product.
35
+ *
36
+ * Authoritative mode also proves the selected host property scope. The registry
37
+ * endpoint accepts only path identity. Neither mode supplies placement, country,
38
+ * or time context or replaces the caller's buying policy.
39
+ */
40
+ export declare function annotateProductsSupplyPaths<T extends object>(products: readonly T[], agentUrl: string, options?: ProductSupplyPathOptions): Promise<Array<AnnotatedSupplyPathProduct<T>>>;
41
+ export {};
@@ -0,0 +1,228 @@
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 products_exports = {};
20
+ __export(products_exports, {
21
+ annotateProductsSupplyPaths: () => annotateProductsSupplyPaths
22
+ });
23
+ module.exports = __toCommonJS(products_exports);
24
+ var import_node_crypto = require("node:crypto");
25
+ var import_verify = require('./verify.js');
26
+ var import_evaluate = require('./evaluate.js');
27
+ var import_fetch_evidence = require('./fetch-evidence.js');
28
+ var import_validation = require('./validation.js');
29
+ var import_publisher_property_selector = require('../discovery/publisher-property-selector.js');
30
+ const MAX_SELECTOR_PREPROCESS_VALUES = 32768;
31
+ const MAX_SELECTOR_PREPROCESS_BYTES = 4 * 1024 * 1024;
32
+ function selectorPredicateFingerprint(selector) {
33
+ const predicate = selector.selection_type === "by_id" ? selector.property_ids : selector.selection_type === "by_tag" ? selector.property_tags : [];
34
+ const hash = (0, import_node_crypto.createHash)("sha256").update(selector.selection_type);
35
+ let bytes = selector.selection_type.length;
36
+ for (const value of predicate) {
37
+ if (value.length > 8192) throw new Error("selector_work_limit_exceeded");
38
+ hash.update("\0").update(value);
39
+ bytes += value.length;
40
+ }
41
+ return { key: hash.digest("hex"), values: predicate.length, bytes };
42
+ }
43
+ async function annotateProductsSupplyPaths(products, agentUrl, options = { source: "authoritative" }) {
44
+ if (!(0, import_validation.agentIdentity)(agentUrl)) throw new TypeError("agentUrl must be an HTTPS URL without credentials");
45
+ const maxPaths = (0, import_fetch_evidence.boundedOption)(options.maxPaths, 64, 256, "maxPaths");
46
+ const timeoutMs = (0, import_fetch_evidence.boundedOption)(options.timeoutMs, 15e3, 6e4, "timeoutMs");
47
+ if (options.source === "authoritative")
48
+ (0, import_fetch_evidence.boundedOption)(options.maxBodyBytes, 256 * 1024, 20 * 1024 * 1024, "maxBodyBytes");
49
+ const deadlineAt = Date.now() + timeoutMs;
50
+ const checkPreprocessingBudget = () => {
51
+ options.signal?.throwIfAborted();
52
+ if (Date.now() >= deadlineAt) throw new Error("Supply-path discovery deadline exceeded");
53
+ };
54
+ const requests = /* @__PURE__ */ new Map();
55
+ const selections = products.map((product) => {
56
+ checkPreprocessingBudget();
57
+ const value = product;
58
+ const keys = [];
59
+ const errors = [];
60
+ if (value.collections === void 0) return { keys, errors };
61
+ if (!Array.isArray(value.collections) || !value.collections.length || !Array.isArray(value.publisher_properties) || !value.publisher_properties.length || value.collections.length > 1024 || value.publisher_properties.length > 1024 || value.collections.some((s) => (0, import_validation.record)(s) && Array.isArray(s.collection_ids) && s.collection_ids.length > 1024) || value.publisher_properties.some(
62
+ (s) => (0, import_validation.record)(s) && [s.publisher_domains, s.property_ids, s.property_tags].some((v) => Array.isArray(v) && v.length > 1024)
63
+ )) {
64
+ return { keys, errors: ["invalid_product_selectors"] };
65
+ }
66
+ try {
67
+ if (!value.publisher_properties.every(import_evaluate.hasValidPropertySelectorPredicate))
68
+ throw new Error("invalid_product_selector_predicate");
69
+ const selectorsByHost = /* @__PURE__ */ new Map();
70
+ let selectorExpansions = 0;
71
+ let selectorValues = 0;
72
+ let selectorBytes = 0;
73
+ for (const raw of value.publisher_properties) {
74
+ checkPreprocessingBudget();
75
+ const parsed = (0, import_publisher_property_selector.parsePublisherPropertySelector)(raw);
76
+ const domains = "publisher_domains" in parsed ? parsed.publisher_domains : [parsed.publisher_domain];
77
+ const single = parsed.selection_type === "all" ? { selection_type: "all", publisher_domain: domains[0] } : parsed.selection_type === "by_id" ? {
78
+ selection_type: "by_id",
79
+ publisher_domain: domains[0],
80
+ property_ids: parsed.property_ids
81
+ } : {
82
+ selection_type: "by_tag",
83
+ publisher_domain: domains[0],
84
+ property_tags: parsed.property_tags
85
+ };
86
+ const predicate = selectorPredicateFingerprint(single);
87
+ selectorExpansions += domains.length;
88
+ selectorValues += domains.length + predicate.values * Math.min(domains.length, maxPaths);
89
+ selectorBytes += domains.reduce((total, publisher) => total + publisher.length, 0) + predicate.bytes * Math.min(domains.length, maxPaths);
90
+ if (selectorExpansions > 4096) throw new Error("selector_work_limit_exceeded");
91
+ if (selectorValues > MAX_SELECTOR_PREPROCESS_VALUES || selectorBytes > MAX_SELECTOR_PREPROCESS_BYTES)
92
+ throw new Error("selector_work_limit_exceeded");
93
+ for (const publisher_domain of domains) {
94
+ checkPreprocessingBudget();
95
+ let selectors = selectorsByHost.get(publisher_domain);
96
+ if (!selectors) {
97
+ selectors = /* @__PURE__ */ new Map();
98
+ selectorsByHost.set(publisher_domain, selectors);
99
+ }
100
+ selectors.set(predicate.key, { ...single, publisher_domain });
101
+ }
102
+ }
103
+ const collectionIdsByOwner = /* @__PURE__ */ new Map();
104
+ for (const selector of value.collections) {
105
+ checkPreprocessingBudget();
106
+ if (!(0, import_validation.record)(selector) || !Object.keys(selector).every((key) => ["publisher_domain", "collection_ids"].includes(key)) || !(0, import_validation.domain)(selector.publisher_domain) || !(0, import_validation.strings)(selector.collection_ids))
107
+ throw new Error("invalid_collection_selector");
108
+ const owner = (0, import_validation.domain)(selector.publisher_domain);
109
+ let ids = collectionIdsByOwner.get(owner);
110
+ if (!ids) {
111
+ ids = /* @__PURE__ */ new Set();
112
+ collectionIdsByOwner.set(owner, ids);
113
+ }
114
+ for (const id of selector.collection_ids) ids.add(id);
115
+ }
116
+ for (const [owner, collectionIds] of collectionIdsByOwner) {
117
+ for (const [host, selectorsByKey] of selectorsByHost) {
118
+ if ((0, import_validation.domain)(host) === owner) continue;
119
+ const selectorEntries = [...selectorsByKey.entries()].sort(([left], [right]) => left.localeCompare(right));
120
+ const selectors = selectorEntries.map(([, selector]) => selector);
121
+ const selectorKey = selectorEntries.map(([key]) => key).join(":");
122
+ for (const id of collectionIds) {
123
+ checkPreprocessingBudget();
124
+ const request = (0, import_validation.validateSupplyPathRequest)({
125
+ owner_domain: owner,
126
+ host_domain: host,
127
+ agent_url: agentUrl,
128
+ collection_id: id
129
+ });
130
+ const key = `${JSON.stringify(request)}\0${selectorKey}`;
131
+ if (!requests.has(key) && requests.size >= maxPaths) {
132
+ if (!errors.includes("path_limit_exceeded")) errors.push("path_limit_exceeded");
133
+ return { keys, errors };
134
+ }
135
+ requests.set(key, { request, selectors });
136
+ if (!keys.includes(key)) keys.push(key);
137
+ }
138
+ }
139
+ }
140
+ } catch (cause) {
141
+ const code = cause instanceof Error ? cause.message : "";
142
+ errors.push(
143
+ ["invalid_product_selector_predicate", "selector_work_limit_exceeded", "invalid_collection_selector"].includes(
144
+ code
145
+ ) ? code : "invalid_product_selectors"
146
+ );
147
+ }
148
+ return { keys, errors };
149
+ });
150
+ const outcomes = /* @__PURE__ */ new Map();
151
+ const pending = [...requests.entries()];
152
+ let position = 0;
153
+ checkPreprocessingBudget();
154
+ const controller = new AbortController();
155
+ const onAbort = () => controller.abort(options.signal?.reason);
156
+ options.signal?.addEventListener("abort", onAbort, { once: true });
157
+ if (options.signal?.aborted) onAbort();
158
+ const timer = setTimeout(
159
+ () => controller.abort(new Error("Supply-path discovery deadline exceeded")),
160
+ Math.max(1, deadlineAt - Date.now())
161
+ );
162
+ let session;
163
+ let onDeadline = () => {
164
+ };
165
+ try {
166
+ session = options.source === "authoritative" ? new import_fetch_evidence.SupplyPathEvidenceSession({
167
+ ...options,
168
+ signal: controller.signal,
169
+ timeoutMs: Math.max(1, deadlineAt - Date.now())
170
+ }) : void 0;
171
+ controller.signal.throwIfAborted();
172
+ const aborted = new Promise((_, reject) => {
173
+ onDeadline = () => reject(controller.signal.reason);
174
+ controller.signal.addEventListener("abort", onDeadline, { once: true });
175
+ });
176
+ await Promise.race([
177
+ aborted,
178
+ Promise.all(
179
+ Array.from({ length: Math.min(4, pending.length) }, async () => {
180
+ while (position < pending.length) {
181
+ controller.signal.throwIfAborted();
182
+ const [key, { request, selectors }] = pending[position++];
183
+ outcomes.set(
184
+ key,
185
+ options.source === "authoritative" ? await (0, import_verify.verifyAuthoritativeSupplyPath)(request, { ...options, propertySelectors: selectors }, session) : await (0, import_verify.verifySupplyPath)(request, options)
186
+ );
187
+ }
188
+ })
189
+ )
190
+ ]);
191
+ } finally {
192
+ session?.close();
193
+ controller.abort(new Error("Supply-path annotation batch closed"));
194
+ clearTimeout(timer);
195
+ options.signal?.removeEventListener("abort", onAbort);
196
+ controller.signal.removeEventListener("abort", onDeadline);
197
+ }
198
+ return products.map((product, index) => {
199
+ const {
200
+ supply_path_state: _state,
201
+ supply_path_verification: _verification,
202
+ ...rest
203
+ } = product;
204
+ void _state;
205
+ void _verification;
206
+ const { keys, errors } = selections[index];
207
+ if (!keys.length && !errors.length) return rest;
208
+ const paths = keys.map((key) => outcomes.get(key));
209
+ const state = errors.length ? "unverified" : paths.reduce(
210
+ (least, result) => import_evaluate.SUPPLY_PATH_STATES.indexOf(result.state) < import_evaluate.SUPPLY_PATH_STATES.indexOf(least) ? result.state : least,
211
+ "verified_owner_sold"
212
+ );
213
+ return {
214
+ ...rest,
215
+ supply_path_state: state,
216
+ supply_path_verification: {
217
+ source: options.source,
218
+ scope: options.source === "authoritative" ? "product_properties" : "owner_host_collection",
219
+ paths,
220
+ errors
221
+ }
222
+ };
223
+ });
224
+ }
225
+ // Annotate the CommonJS export names for ESM import in node:
226
+ 0 && (module.exports = {
227
+ annotateProductsSupplyPaths
228
+ });
@@ -0,0 +1,204 @@
1
+ import { createHash } from "node:crypto";
2
+ import { verifySupplyPath, verifyAuthoritativeSupplyPath } from "./verify.mjs";
3
+ import { SUPPLY_PATH_STATES, hasValidPropertySelectorPredicate } from "./evaluate.mjs";
4
+ import { boundedOption, SupplyPathEvidenceSession } from "./fetch-evidence.mjs";
5
+ import { agentIdentity, domain, record, strings, validateSupplyPathRequest } from "./validation.mjs";
6
+ import { parsePublisherPropertySelector } from "../discovery/publisher-property-selector.mjs";
7
+ const MAX_SELECTOR_PREPROCESS_VALUES = 32768;
8
+ const MAX_SELECTOR_PREPROCESS_BYTES = 4 * 1024 * 1024;
9
+ function selectorPredicateFingerprint(selector) {
10
+ const predicate = selector.selection_type === "by_id" ? selector.property_ids : selector.selection_type === "by_tag" ? selector.property_tags : [];
11
+ const hash = createHash("sha256").update(selector.selection_type);
12
+ let bytes = selector.selection_type.length;
13
+ for (const value of predicate) {
14
+ if (value.length > 8192) throw new Error("selector_work_limit_exceeded");
15
+ hash.update("\0").update(value);
16
+ bytes += value.length;
17
+ }
18
+ return { key: hash.digest("hex"), values: predicate.length, bytes };
19
+ }
20
+ async function annotateProductsSupplyPaths(products, agentUrl, options = { source: "authoritative" }) {
21
+ if (!agentIdentity(agentUrl)) throw new TypeError("agentUrl must be an HTTPS URL without credentials");
22
+ const maxPaths = boundedOption(options.maxPaths, 64, 256, "maxPaths");
23
+ const timeoutMs = boundedOption(options.timeoutMs, 15e3, 6e4, "timeoutMs");
24
+ if (options.source === "authoritative")
25
+ boundedOption(options.maxBodyBytes, 256 * 1024, 20 * 1024 * 1024, "maxBodyBytes");
26
+ const deadlineAt = Date.now() + timeoutMs;
27
+ const checkPreprocessingBudget = () => {
28
+ options.signal?.throwIfAborted();
29
+ if (Date.now() >= deadlineAt) throw new Error("Supply-path discovery deadline exceeded");
30
+ };
31
+ const requests = /* @__PURE__ */ new Map();
32
+ const selections = products.map((product) => {
33
+ checkPreprocessingBudget();
34
+ const value = product;
35
+ const keys = [];
36
+ const errors = [];
37
+ if (value.collections === void 0) return { keys, errors };
38
+ if (!Array.isArray(value.collections) || !value.collections.length || !Array.isArray(value.publisher_properties) || !value.publisher_properties.length || value.collections.length > 1024 || value.publisher_properties.length > 1024 || value.collections.some((s) => record(s) && Array.isArray(s.collection_ids) && s.collection_ids.length > 1024) || value.publisher_properties.some(
39
+ (s) => record(s) && [s.publisher_domains, s.property_ids, s.property_tags].some((v) => Array.isArray(v) && v.length > 1024)
40
+ )) {
41
+ return { keys, errors: ["invalid_product_selectors"] };
42
+ }
43
+ try {
44
+ if (!value.publisher_properties.every(hasValidPropertySelectorPredicate))
45
+ throw new Error("invalid_product_selector_predicate");
46
+ const selectorsByHost = /* @__PURE__ */ new Map();
47
+ let selectorExpansions = 0;
48
+ let selectorValues = 0;
49
+ let selectorBytes = 0;
50
+ for (const raw of value.publisher_properties) {
51
+ checkPreprocessingBudget();
52
+ const parsed = parsePublisherPropertySelector(raw);
53
+ const domains = "publisher_domains" in parsed ? parsed.publisher_domains : [parsed.publisher_domain];
54
+ const single = parsed.selection_type === "all" ? { selection_type: "all", publisher_domain: domains[0] } : parsed.selection_type === "by_id" ? {
55
+ selection_type: "by_id",
56
+ publisher_domain: domains[0],
57
+ property_ids: parsed.property_ids
58
+ } : {
59
+ selection_type: "by_tag",
60
+ publisher_domain: domains[0],
61
+ property_tags: parsed.property_tags
62
+ };
63
+ const predicate = selectorPredicateFingerprint(single);
64
+ selectorExpansions += domains.length;
65
+ selectorValues += domains.length + predicate.values * Math.min(domains.length, maxPaths);
66
+ selectorBytes += domains.reduce((total, publisher) => total + publisher.length, 0) + predicate.bytes * Math.min(domains.length, maxPaths);
67
+ if (selectorExpansions > 4096) throw new Error("selector_work_limit_exceeded");
68
+ if (selectorValues > MAX_SELECTOR_PREPROCESS_VALUES || selectorBytes > MAX_SELECTOR_PREPROCESS_BYTES)
69
+ throw new Error("selector_work_limit_exceeded");
70
+ for (const publisher_domain of domains) {
71
+ checkPreprocessingBudget();
72
+ let selectors = selectorsByHost.get(publisher_domain);
73
+ if (!selectors) {
74
+ selectors = /* @__PURE__ */ new Map();
75
+ selectorsByHost.set(publisher_domain, selectors);
76
+ }
77
+ selectors.set(predicate.key, { ...single, publisher_domain });
78
+ }
79
+ }
80
+ const collectionIdsByOwner = /* @__PURE__ */ new Map();
81
+ for (const selector of value.collections) {
82
+ checkPreprocessingBudget();
83
+ if (!record(selector) || !Object.keys(selector).every((key) => ["publisher_domain", "collection_ids"].includes(key)) || !domain(selector.publisher_domain) || !strings(selector.collection_ids))
84
+ throw new Error("invalid_collection_selector");
85
+ const owner = domain(selector.publisher_domain);
86
+ let ids = collectionIdsByOwner.get(owner);
87
+ if (!ids) {
88
+ ids = /* @__PURE__ */ new Set();
89
+ collectionIdsByOwner.set(owner, ids);
90
+ }
91
+ for (const id of selector.collection_ids) ids.add(id);
92
+ }
93
+ for (const [owner, collectionIds] of collectionIdsByOwner) {
94
+ for (const [host, selectorsByKey] of selectorsByHost) {
95
+ if (domain(host) === owner) continue;
96
+ const selectorEntries = [...selectorsByKey.entries()].sort(([left], [right]) => left.localeCompare(right));
97
+ const selectors = selectorEntries.map(([, selector]) => selector);
98
+ const selectorKey = selectorEntries.map(([key]) => key).join(":");
99
+ for (const id of collectionIds) {
100
+ checkPreprocessingBudget();
101
+ const request = validateSupplyPathRequest({
102
+ owner_domain: owner,
103
+ host_domain: host,
104
+ agent_url: agentUrl,
105
+ collection_id: id
106
+ });
107
+ const key = `${JSON.stringify(request)}\0${selectorKey}`;
108
+ if (!requests.has(key) && requests.size >= maxPaths) {
109
+ if (!errors.includes("path_limit_exceeded")) errors.push("path_limit_exceeded");
110
+ return { keys, errors };
111
+ }
112
+ requests.set(key, { request, selectors });
113
+ if (!keys.includes(key)) keys.push(key);
114
+ }
115
+ }
116
+ }
117
+ } catch (cause) {
118
+ const code = cause instanceof Error ? cause.message : "";
119
+ errors.push(
120
+ ["invalid_product_selector_predicate", "selector_work_limit_exceeded", "invalid_collection_selector"].includes(
121
+ code
122
+ ) ? code : "invalid_product_selectors"
123
+ );
124
+ }
125
+ return { keys, errors };
126
+ });
127
+ const outcomes = /* @__PURE__ */ new Map();
128
+ const pending = [...requests.entries()];
129
+ let position = 0;
130
+ checkPreprocessingBudget();
131
+ const controller = new AbortController();
132
+ const onAbort = () => controller.abort(options.signal?.reason);
133
+ options.signal?.addEventListener("abort", onAbort, { once: true });
134
+ if (options.signal?.aborted) onAbort();
135
+ const timer = setTimeout(
136
+ () => controller.abort(new Error("Supply-path discovery deadline exceeded")),
137
+ Math.max(1, deadlineAt - Date.now())
138
+ );
139
+ let session;
140
+ let onDeadline = () => {
141
+ };
142
+ try {
143
+ session = options.source === "authoritative" ? new SupplyPathEvidenceSession({
144
+ ...options,
145
+ signal: controller.signal,
146
+ timeoutMs: Math.max(1, deadlineAt - Date.now())
147
+ }) : void 0;
148
+ controller.signal.throwIfAborted();
149
+ const aborted = new Promise((_, reject) => {
150
+ onDeadline = () => reject(controller.signal.reason);
151
+ controller.signal.addEventListener("abort", onDeadline, { once: true });
152
+ });
153
+ await Promise.race([
154
+ aborted,
155
+ Promise.all(
156
+ Array.from({ length: Math.min(4, pending.length) }, async () => {
157
+ while (position < pending.length) {
158
+ controller.signal.throwIfAborted();
159
+ const [key, { request, selectors }] = pending[position++];
160
+ outcomes.set(
161
+ key,
162
+ options.source === "authoritative" ? await verifyAuthoritativeSupplyPath(request, { ...options, propertySelectors: selectors }, session) : await verifySupplyPath(request, options)
163
+ );
164
+ }
165
+ })
166
+ )
167
+ ]);
168
+ } finally {
169
+ session?.close();
170
+ controller.abort(new Error("Supply-path annotation batch closed"));
171
+ clearTimeout(timer);
172
+ options.signal?.removeEventListener("abort", onAbort);
173
+ controller.signal.removeEventListener("abort", onDeadline);
174
+ }
175
+ return products.map((product, index) => {
176
+ const {
177
+ supply_path_state: _state,
178
+ supply_path_verification: _verification,
179
+ ...rest
180
+ } = product;
181
+ void _state;
182
+ void _verification;
183
+ const { keys, errors } = selections[index];
184
+ if (!keys.length && !errors.length) return rest;
185
+ const paths = keys.map((key) => outcomes.get(key));
186
+ const state = errors.length ? "unverified" : paths.reduce(
187
+ (least, result) => SUPPLY_PATH_STATES.indexOf(result.state) < SUPPLY_PATH_STATES.indexOf(least) ? result.state : least,
188
+ "verified_owner_sold"
189
+ );
190
+ return {
191
+ ...rest,
192
+ supply_path_state: state,
193
+ supply_path_verification: {
194
+ source: options.source,
195
+ scope: options.source === "authoritative" ? "product_properties" : "owner_host_collection",
196
+ paths,
197
+ errors
198
+ }
199
+ };
200
+ });
201
+ }
202
+ export {
203
+ annotateProductsSupplyPaths
204
+ };
@@ -0,0 +1,55 @@
1
+ export interface SupplyPathRevocation {
2
+ publisher_domain: string;
3
+ /** Publisher timestamp, or `unspecified` for a legacy domain-only declaration. Never use this as the hold expiry. */
4
+ revoked_at: string;
5
+ }
6
+ /**
7
+ * Atomic, authority-scoped seven-day revocation hold. Durable implementations
8
+ * must key by authority and publisher_domain, preserving first observation
9
+ * across changed publisher timestamps, and reject on storage failure instead of returning an empty set.
10
+ * Capture tenant identity from trusted application context in the store instance;
11
+ * include that tenant in every key and transaction. Publisher evidence must never select a tenant.
12
+ * Persist `revoked_at` as publisher metadata that may be the `unspecified` sentinel;
13
+ * maintain the seven-day first-observation expiry in a separate timestamp column.
14
+ */
15
+ export interface SupplyPathRevocationStore {
16
+ observe(authority: string, revoked: readonly SupplyPathRevocation[]): Promise<readonly SupplyPathRevocation[]>;
17
+ }
18
+ /** Process-local default. Use a durable shared store across workers/restarts. */
19
+ export declare class InMemorySupplyPathRevocationStore implements SupplyPathRevocationStore {
20
+ private readonly admission;
21
+ private readonly entries;
22
+ observe(authority: string, revoked: readonly SupplyPathRevocation[]): Promise<readonly SupplyPathRevocation[]>;
23
+ }
24
+ export declare const defaultSupplyPathRevocations: InMemorySupplyPathRevocationStore;
25
+ export declare function parseRevocations(value: unknown): SupplyPathRevocation[] | null;
26
+ /**
27
+ * Trust-on-first-successful-use storage. Capture the authenticated tenant in the
28
+ * store instance and namespace every pin by that tenant. Neither a read nor a
29
+ * failed fetch may create a pin. Storage failures must reject.
30
+ * Keys are the exact serialized HTTPS URL supplied by the verifier, with no
31
+ * fragment delimiter. Preserve query/path spelling; do not merge distinct URLs.
32
+ * A deadline may abandon an observe promise while its transaction completes.
33
+ * Such a commit still represents a validated manifest; retries must be idempotent.
34
+ */
35
+ export interface SupplyPathAuthorityStore {
36
+ /** Read-only precheck: true if absent or equal; false for a different existing pin. */
37
+ check(publisherDomain: string, location: string): Promise<boolean>;
38
+ /**
39
+ * After successful manifest validation, atomically insert if absent or compare
40
+ * with the existing pin. Return false on mismatch, including a concurrent change
41
+ * since check(). A successful precheck is never a reservation or authorization.
42
+ */
43
+ observe(publisherDomain: string, location: string): Promise<boolean>;
44
+ }
45
+ /** Process-local pointer integrity. Use durable shared storage across workers/restarts. */
46
+ export declare class InMemorySupplyPathAuthorityStore implements SupplyPathAuthorityStore {
47
+ private readonly admission;
48
+ private readonly locations;
49
+ check(publisherDomain: string, location: string): Promise<boolean>;
50
+ observe(publisherDomain: string, location: string): Promise<boolean>;
51
+ /** Call only after independently confirming a publisher's migration. Never call from an automatic retry. */
52
+ approveChange(publisherDomain: string, location: string): void;
53
+ private assertBoundedLocation;
54
+ }
55
+ export declare const defaultSupplyPathAuthorities: InMemorySupplyPathAuthorityStore;
@@ -0,0 +1,55 @@
1
+ export interface SupplyPathRevocation {
2
+ publisher_domain: string;
3
+ /** Publisher timestamp, or `unspecified` for a legacy domain-only declaration. Never use this as the hold expiry. */
4
+ revoked_at: string;
5
+ }
6
+ /**
7
+ * Atomic, authority-scoped seven-day revocation hold. Durable implementations
8
+ * must key by authority and publisher_domain, preserving first observation
9
+ * across changed publisher timestamps, and reject on storage failure instead of returning an empty set.
10
+ * Capture tenant identity from trusted application context in the store instance;
11
+ * include that tenant in every key and transaction. Publisher evidence must never select a tenant.
12
+ * Persist `revoked_at` as publisher metadata that may be the `unspecified` sentinel;
13
+ * maintain the seven-day first-observation expiry in a separate timestamp column.
14
+ */
15
+ export interface SupplyPathRevocationStore {
16
+ observe(authority: string, revoked: readonly SupplyPathRevocation[]): Promise<readonly SupplyPathRevocation[]>;
17
+ }
18
+ /** Process-local default. Use a durable shared store across workers/restarts. */
19
+ export declare class InMemorySupplyPathRevocationStore implements SupplyPathRevocationStore {
20
+ private readonly admission;
21
+ private readonly entries;
22
+ observe(authority: string, revoked: readonly SupplyPathRevocation[]): Promise<readonly SupplyPathRevocation[]>;
23
+ }
24
+ export declare const defaultSupplyPathRevocations: InMemorySupplyPathRevocationStore;
25
+ export declare function parseRevocations(value: unknown): SupplyPathRevocation[] | null;
26
+ /**
27
+ * Trust-on-first-successful-use storage. Capture the authenticated tenant in the
28
+ * store instance and namespace every pin by that tenant. Neither a read nor a
29
+ * failed fetch may create a pin. Storage failures must reject.
30
+ * Keys are the exact serialized HTTPS URL supplied by the verifier, with no
31
+ * fragment delimiter. Preserve query/path spelling; do not merge distinct URLs.
32
+ * A deadline may abandon an observe promise while its transaction completes.
33
+ * Such a commit still represents a validated manifest; retries must be idempotent.
34
+ */
35
+ export interface SupplyPathAuthorityStore {
36
+ /** Read-only precheck: true if absent or equal; false for a different existing pin. */
37
+ check(publisherDomain: string, location: string): Promise<boolean>;
38
+ /**
39
+ * After successful manifest validation, atomically insert if absent or compare
40
+ * with the existing pin. Return false on mismatch, including a concurrent change
41
+ * since check(). A successful precheck is never a reservation or authorization.
42
+ */
43
+ observe(publisherDomain: string, location: string): Promise<boolean>;
44
+ }
45
+ /** Process-local pointer integrity. Use durable shared storage across workers/restarts. */
46
+ export declare class InMemorySupplyPathAuthorityStore implements SupplyPathAuthorityStore {
47
+ private readonly admission;
48
+ private readonly locations;
49
+ check(publisherDomain: string, location: string): Promise<boolean>;
50
+ observe(publisherDomain: string, location: string): Promise<boolean>;
51
+ /** Call only after independently confirming a publisher's migration. Never call from an automatic retry. */
52
+ approveChange(publisherDomain: string, location: string): void;
53
+ private assertBoundedLocation;
54
+ }
55
+ export declare const defaultSupplyPathAuthorities: InMemorySupplyPathAuthorityStore;