@adcp/sdk 16.1.0 → 16.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +2 -0
- package/dist/lib/core/AgentClient.js +17 -1
- package/dist/lib/core/AgentClient.mjs +17 -1
- package/dist/lib/core/SingleAgentClient.js +13 -0
- package/dist/lib/core/SingleAgentClient.mjs +13 -0
- package/dist/lib/core/TaskExecutor.js +88 -13
- package/dist/lib/core/TaskExecutor.mjs +89 -14
- package/dist/lib/errors/index.d.mts +34 -0
- package/dist/lib/errors/index.d.ts +34 -0
- package/dist/lib/errors/index.js +11 -0
- package/dist/lib/errors/index.mjs +10 -0
- package/dist/lib/governance/authorization.d.mts +66 -0
- package/dist/lib/governance/authorization.d.ts +66 -0
- package/dist/lib/governance/authorization.js +181 -37
- package/dist/lib/governance/authorization.mjs +183 -36
- package/dist/lib/governance/index.d.mts +2 -2
- package/dist/lib/governance/index.d.ts +2 -2
- package/dist/lib/governance/index.js +4 -2
- package/dist/lib/governance/index.mjs +3 -1
- package/dist/lib/identity-3.3/index.d.mts +1 -0
- package/dist/lib/identity-3.3/index.d.ts +1 -0
- package/dist/lib/identity-3.3/index.js +3 -1
- package/dist/lib/identity-3.3/index.mjs +1 -0
- package/dist/lib/identity-3.3/jws-artifacts.d.mts +147 -0
- package/dist/lib/identity-3.3/jws-artifacts.d.ts +147 -0
- package/dist/lib/identity-3.3/jws-artifacts.js +258 -0
- package/dist/lib/identity-3.3/jws-artifacts.mjs +234 -0
- package/dist/lib/identity-3.3/revocation.d.mts +7 -0
- package/dist/lib/identity-3.3/revocation.d.ts +7 -0
- package/dist/lib/identity-3.3/revocation.js +13 -0
- package/dist/lib/identity-3.3/revocation.mjs +13 -0
- package/dist/lib/index.d.mts +3 -3
- package/dist/lib/index.d.ts +3 -3
- package/dist/lib/index.js +4 -0
- package/dist/lib/index.mjs +4 -0
- package/dist/lib/media-buy/compatibility.d.mts +1 -0
- package/dist/lib/media-buy/compatibility.d.ts +1 -0
- package/dist/lib/media-buy/compatibility.js +33 -10
- package/dist/lib/media-buy/compatibility.mjs +32 -14
- package/dist/lib/negotiation/buyer.js +11 -5
- package/dist/lib/negotiation/buyer.mjs +11 -5
- package/dist/lib/negotiation/types.d.mts +4 -2
- package/dist/lib/negotiation/types.d.ts +4 -2
- package/dist/lib/negotiation/verification.d.mts +15 -6
- package/dist/lib/negotiation/verification.d.ts +15 -6
- package/dist/lib/negotiation/verification.js +146 -27
- package/dist/lib/negotiation/verification.mjs +146 -28
- package/dist/lib/negotiation/version.d.mts +6 -0
- package/dist/lib/negotiation/version.d.ts +6 -0
- package/dist/lib/negotiation/version.js +60 -0
- package/dist/lib/negotiation/version.mjs +33 -0
- package/dist/lib/registry/types.generated.d.mts +24 -1
- package/dist/lib/registry/types.generated.d.ts +24 -1
- package/dist/lib/server/create-adcp-server.js +1 -1
- package/dist/lib/server/create-adcp-server.mjs +1 -1
- package/dist/lib/server/error-arm-tools.d.mts +10 -10
- package/dist/lib/server/error-arm-tools.d.ts +10 -10
- package/dist/lib/server/error-arm-tools.js +22 -58
- package/dist/lib/server/error-arm-tools.mjs +30 -65
- package/dist/lib/server/index.d.mts +2 -2
- package/dist/lib/server/index.d.ts +2 -2
- package/dist/lib/server/index.js +2 -0
- package/dist/lib/server/index.mjs +2 -0
- package/dist/lib/signing/web-bot-auth.js +5 -0
- package/dist/lib/signing/web-bot-auth.mjs +4 -0
- package/dist/lib/testing/compliance/comply.js +17 -0
- package/dist/lib/testing/compliance/comply.mjs +17 -0
- package/dist/lib/testing/index.d.mts +3 -0
- package/dist/lib/testing/index.d.ts +3 -0
- package/dist/lib/testing/index.js +44 -2
- package/dist/lib/testing/index.mjs +42 -0
- package/dist/lib/testing/storyboard/collection-selection.d.mts +259 -0
- package/dist/lib/testing/storyboard/collection-selection.d.ts +259 -0
- package/dist/lib/testing/storyboard/collection-selection.js +384 -0
- package/dist/lib/testing/storyboard/collection-selection.mjs +348 -0
- package/dist/lib/testing/storyboard/context.js +12 -6
- package/dist/lib/testing/storyboard/context.mjs +12 -6
- package/dist/lib/testing/storyboard/fixture-resolution.d.mts +39 -1
- package/dist/lib/testing/storyboard/fixture-resolution.d.ts +39 -1
- package/dist/lib/testing/storyboard/fixture-resolution.js +216 -12
- package/dist/lib/testing/storyboard/fixture-resolution.mjs +218 -12
- package/dist/lib/testing/storyboard/index.d.mts +9 -2
- package/dist/lib/testing/storyboard/index.d.ts +9 -2
- package/dist/lib/testing/storyboard/index.js +51 -2
- package/dist/lib/testing/storyboard/index.mjs +49 -1
- package/dist/lib/testing/storyboard/prepared-fixtures.d.mts +471 -0
- package/dist/lib/testing/storyboard/prepared-fixtures.d.ts +471 -0
- package/dist/lib/testing/storyboard/prepared-fixtures.js +434 -0
- package/dist/lib/testing/storyboard/prepared-fixtures.mjs +389 -0
- package/dist/lib/testing/storyboard/protected-context.d.mts +127 -0
- package/dist/lib/testing/storyboard/protected-context.d.ts +127 -0
- package/dist/lib/testing/storyboard/protected-context.js +301 -0
- package/dist/lib/testing/storyboard/protected-context.mjs +267 -0
- package/dist/lib/testing/storyboard/rejection-hints.js +13 -4
- package/dist/lib/testing/storyboard/rejection-hints.mjs +13 -4
- package/dist/lib/testing/storyboard/runner.d.mts +24 -0
- package/dist/lib/testing/storyboard/runner.d.ts +24 -0
- package/dist/lib/testing/storyboard/runner.js +1061 -105
- package/dist/lib/testing/storyboard/runner.mjs +1075 -106
- package/dist/lib/testing/storyboard/seeding.d.mts +15 -1
- package/dist/lib/testing/storyboard/seeding.d.ts +15 -1
- package/dist/lib/testing/storyboard/seeding.js +272 -12
- package/dist/lib/testing/storyboard/seeding.mjs +274 -13
- package/dist/lib/testing/storyboard/types.d.mts +207 -10
- package/dist/lib/testing/storyboard/types.d.ts +207 -10
- package/dist/lib/testing/storyboard/types.js +1 -1
- package/dist/lib/testing/storyboard/types.mjs +1 -1
- package/dist/lib/testing/storyboard/validations.d.mts +1 -1
- package/dist/lib/testing/storyboard/validations.d.ts +1 -1
- package/dist/lib/testing/storyboard/validations.js +95 -0
- package/dist/lib/testing/storyboard/validations.mjs +95 -0
- package/dist/lib/validation/bundled-schema-store.js +5 -0
- package/dist/lib/validation/bundled-schema-store.mjs +4 -0
- package/dist/lib/validation/schema-loader.js +19 -2
- package/dist/lib/validation/schema-loader.mjs +22 -3
- package/dist/lib/version.d.mts +3 -3
- package/dist/lib/version.d.ts +3 -3
- package/dist/lib/version.js +3 -3
- package/dist/lib/version.mjs +3 -3
- package/docs/TYPE-SUMMARY.md +2 -2
- package/docs/guides/COLLECTION-FIXTURE-FOUNDATION.md +850 -0
- package/docs/guides/PROPOSAL-TERMS-VERIFICATION.md +66 -0
- package/docs/llms.txt +4 -3
- package/docs/migration-14.x-rc-worksheet.md +4 -4
- package/docs/migration-agent-identity-3.3.md +51 -6
- package/package.json +6 -4
|
@@ -0,0 +1,384 @@
|
|
|
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 collection_selection_exports = {};
|
|
20
|
+
__export(collection_selection_exports, {
|
|
21
|
+
COLLECTION_DIAGNOSTIC_LIMIT: () => COLLECTION_DIAGNOSTIC_LIMIT,
|
|
22
|
+
COLLECTION_PAIR_LIMIT: () => COLLECTION_PAIR_LIMIT,
|
|
23
|
+
COLLECTION_TARGETING_MODES: () => COLLECTION_TARGETING_MODES,
|
|
24
|
+
COLLECTION_WITNESS_KINDS: () => COLLECTION_WITNESS_KINDS,
|
|
25
|
+
compareCollectionPairs: () => compareCollectionPairs,
|
|
26
|
+
diffCollectionPairSets: () => diffCollectionPairSets,
|
|
27
|
+
evaluateCollectionSelection: () => evaluateCollectionSelection,
|
|
28
|
+
formatCollectionPair: () => formatCollectionPair,
|
|
29
|
+
normalizeCollectionSelectionSelector: () => normalizeCollectionSelectionSelector,
|
|
30
|
+
normalizeCollectionSelectors: () => normalizeCollectionSelectors,
|
|
31
|
+
productSatisfiesCollectionSelection: () => productSatisfiesCollectionSelection,
|
|
32
|
+
readCollectionWitness: () => readCollectionWitness,
|
|
33
|
+
summarizeCollectionPairs: () => summarizeCollectionPairs
|
|
34
|
+
});
|
|
35
|
+
module.exports = __toCommonJS(collection_selection_exports);
|
|
36
|
+
const PAIR_LIMIT_DEFAULT = 1e4;
|
|
37
|
+
const COLLECTION_PAIR_LIMIT = PAIR_LIMIT_DEFAULT;
|
|
38
|
+
const COLLECTION_DIAGNOSTIC_LIMIT = 20;
|
|
39
|
+
function isRecord(value) {
|
|
40
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
41
|
+
}
|
|
42
|
+
function utf8Compare(a, b) {
|
|
43
|
+
return Buffer.compare(Buffer.from(a, "utf8"), Buffer.from(b, "utf8"));
|
|
44
|
+
}
|
|
45
|
+
function compareCollectionPairs(a, b) {
|
|
46
|
+
const domain = utf8Compare(a.publisher_domain, b.publisher_domain);
|
|
47
|
+
if (domain !== 0) return domain;
|
|
48
|
+
if (a.collection_id === b.collection_id) return 0;
|
|
49
|
+
if (a.collection_id === null) return -1;
|
|
50
|
+
if (b.collection_id === null) return 1;
|
|
51
|
+
return utf8Compare(a.collection_id, b.collection_id);
|
|
52
|
+
}
|
|
53
|
+
function formatCollectionPair(pair) {
|
|
54
|
+
return pair.collection_id === null ? `${JSON.stringify(pair.publisher_domain)}:*` : `${JSON.stringify(pair.publisher_domain)}:${JSON.stringify(pair.collection_id)}`;
|
|
55
|
+
}
|
|
56
|
+
function pairKey(pair) {
|
|
57
|
+
return pair.collection_id === null ? `${JSON.stringify(pair.publisher_domain)}\0` : `${JSON.stringify(pair.publisher_domain)}\0${JSON.stringify(pair.collection_id)}`;
|
|
58
|
+
}
|
|
59
|
+
function normalizeCollectionSelectors(value, options = {}) {
|
|
60
|
+
const limit = options.limit ?? PAIR_LIMIT_DEFAULT;
|
|
61
|
+
if (!Array.isArray(value)) {
|
|
62
|
+
return {
|
|
63
|
+
ok: false,
|
|
64
|
+
failure: {
|
|
65
|
+
code: "not_an_array",
|
|
66
|
+
detail: `expected an array of collection selectors; received ${describe(value)}`
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
const seen = /* @__PURE__ */ new Map();
|
|
71
|
+
for (let index = 0; index < value.length; index++) {
|
|
72
|
+
const selector = value[index];
|
|
73
|
+
if (!isRecord(selector)) {
|
|
74
|
+
return {
|
|
75
|
+
ok: false,
|
|
76
|
+
failure: {
|
|
77
|
+
code: "selector_not_object",
|
|
78
|
+
detail: `collections[${index}] must be an object; received ${describe(selector)}`
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
const domain = selector.publisher_domain;
|
|
83
|
+
if (typeof domain !== "string" || domain.length === 0) {
|
|
84
|
+
return {
|
|
85
|
+
ok: false,
|
|
86
|
+
failure: {
|
|
87
|
+
code: "publisher_domain_invalid",
|
|
88
|
+
detail: `collections[${index}].publisher_domain must be a non-empty string; received ${describe(domain)}`
|
|
89
|
+
}
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
const ids = selector.collection_ids;
|
|
93
|
+
if (ids === void 0 || ids === null) {
|
|
94
|
+
addPair(seen, { publisher_domain: domain, collection_id: null });
|
|
95
|
+
} else {
|
|
96
|
+
if (!Array.isArray(ids) || ids.length === 0) {
|
|
97
|
+
return {
|
|
98
|
+
ok: false,
|
|
99
|
+
failure: {
|
|
100
|
+
code: "collection_ids_invalid",
|
|
101
|
+
detail: `collections[${index}].collection_ids must be a non-empty array when present; received ${describe(ids)}`
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
for (let idIndex = 0; idIndex < ids.length; idIndex++) {
|
|
106
|
+
const id = ids[idIndex];
|
|
107
|
+
if (typeof id !== "string") {
|
|
108
|
+
return {
|
|
109
|
+
ok: false,
|
|
110
|
+
failure: {
|
|
111
|
+
code: "collection_id_invalid",
|
|
112
|
+
detail: `collections[${index}].collection_ids[${idIndex}] must be a string; received ${describe(id)}`
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
addPair(seen, { publisher_domain: domain, collection_id: id });
|
|
117
|
+
if (seen.size > limit) return pairLimitExceeded(limit, index);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
if (seen.size > limit) return pairLimitExceeded(limit, index);
|
|
121
|
+
}
|
|
122
|
+
const pairs = [...seen.values()].sort(compareCollectionPairs);
|
|
123
|
+
const domains = [...new Set(pairs.map((pair) => pair.publisher_domain))].sort(utf8Compare);
|
|
124
|
+
return { ok: true, value: { pairs, selector_count: value.length, domains } };
|
|
125
|
+
}
|
|
126
|
+
function pairLimitExceeded(limit, index) {
|
|
127
|
+
return {
|
|
128
|
+
ok: false,
|
|
129
|
+
failure: {
|
|
130
|
+
code: "pair_limit_exceeded",
|
|
131
|
+
detail: `normalized collection pairs exceeded the ${limit}-pair safety limit at collections[${index}]`
|
|
132
|
+
}
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
function addPair(seen, pair) {
|
|
136
|
+
const key = pairKey(pair);
|
|
137
|
+
if (!seen.has(key)) seen.set(key, pair);
|
|
138
|
+
}
|
|
139
|
+
function describe(value) {
|
|
140
|
+
if (value === void 0) return "undefined";
|
|
141
|
+
if (value === null) return "null";
|
|
142
|
+
if (Array.isArray(value)) return `an array of length ${value.length}`;
|
|
143
|
+
return typeof value === "object" ? "an object" : JSON.stringify(value);
|
|
144
|
+
}
|
|
145
|
+
function diffCollectionPairSets(actual, expected) {
|
|
146
|
+
const expectedKeys = new Set(expected.map(pairKey));
|
|
147
|
+
const actualKeys = new Set(actual.map(pairKey));
|
|
148
|
+
const missing = expected.filter((pair) => !actualKeys.has(pairKey(pair)));
|
|
149
|
+
const unexpected = actual.filter((pair) => !expectedKeys.has(pairKey(pair)));
|
|
150
|
+
return { equal: missing.length === 0 && unexpected.length === 0, missing, unexpected };
|
|
151
|
+
}
|
|
152
|
+
function summarizeCollectionPairs(pairs) {
|
|
153
|
+
const shown = pairs.slice(0, COLLECTION_DIAGNOSTIC_LIMIT).map(formatCollectionPair);
|
|
154
|
+
const elided = pairs.length - shown.length;
|
|
155
|
+
return elided > 0 ? `${shown.join(", ")} (+${elided} more)` : shown.join(", ");
|
|
156
|
+
}
|
|
157
|
+
const COLLECTION_TARGETING_MODES = ["selectable", "fixed", "any"];
|
|
158
|
+
function normalizeCollectionSelectionSelector(value, location) {
|
|
159
|
+
if (!isRecord(value)) throw new Error(`${location}: must be an object`);
|
|
160
|
+
const unknown = Object.keys(value).filter(
|
|
161
|
+
(key) => !["collection_targeting", "minimum_pairs", "require_cross_publisher_collection"].includes(key)
|
|
162
|
+
);
|
|
163
|
+
if (unknown.length > 0) {
|
|
164
|
+
throw new Error(`${location}: unknown key(s): ${unknown.join(", ")}; the operand is closed`);
|
|
165
|
+
}
|
|
166
|
+
const targeting = value.collection_targeting;
|
|
167
|
+
if (typeof targeting !== "string" || !COLLECTION_TARGETING_MODES.includes(targeting)) {
|
|
168
|
+
throw new Error(`${location}.collection_targeting: must be one of ${COLLECTION_TARGETING_MODES.join(", ")}`);
|
|
169
|
+
}
|
|
170
|
+
let minimumPairs = 1;
|
|
171
|
+
if (value.minimum_pairs !== void 0) {
|
|
172
|
+
if (value.minimum_pairs !== 1 && value.minimum_pairs !== 2) {
|
|
173
|
+
throw new Error(`${location}.minimum_pairs: must be 1 or 2`);
|
|
174
|
+
}
|
|
175
|
+
minimumPairs = value.minimum_pairs;
|
|
176
|
+
}
|
|
177
|
+
if (value.require_cross_publisher_collection !== void 0 && typeof value.require_cross_publisher_collection !== "boolean") {
|
|
178
|
+
throw new Error(`${location}.require_cross_publisher_collection: must be boolean`);
|
|
179
|
+
}
|
|
180
|
+
return {
|
|
181
|
+
collection_targeting: targeting,
|
|
182
|
+
minimum_pairs: minimumPairs,
|
|
183
|
+
require_cross_publisher_collection: value.require_cross_publisher_collection === true
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
function collectHostDomains(candidate) {
|
|
187
|
+
const domains = /* @__PURE__ */ new Set();
|
|
188
|
+
const properties = candidate.publisher_properties;
|
|
189
|
+
if (Array.isArray(properties)) {
|
|
190
|
+
for (const entry of properties) {
|
|
191
|
+
if (!isRecord(entry)) continue;
|
|
192
|
+
if (typeof entry.publisher_domain === "string" && entry.publisher_domain.length > 0) {
|
|
193
|
+
domains.add(entry.publisher_domain);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
return [...domains].sort(utf8Compare);
|
|
198
|
+
}
|
|
199
|
+
function cloneSelectorBundle(value) {
|
|
200
|
+
return value.map((selector) => ({
|
|
201
|
+
publisher_domain: selector.publisher_domain,
|
|
202
|
+
...Array.isArray(selector.collection_ids) && { collection_ids: [...selector.collection_ids] }
|
|
203
|
+
}));
|
|
204
|
+
}
|
|
205
|
+
function buildUndeclaredCollectionIdSelectors(domain, pairs) {
|
|
206
|
+
const taken = new Set(pairs.filter((pair) => pair.publisher_domain === domain).map((pair) => pair.collection_id));
|
|
207
|
+
let candidate = "adcp-absent-collection";
|
|
208
|
+
for (let suffix = 0; taken.has(candidate); suffix++) {
|
|
209
|
+
candidate = `adcp-absent-collection-${suffix}`;
|
|
210
|
+
}
|
|
211
|
+
return [{ publisher_domain: domain, collection_ids: [candidate] }];
|
|
212
|
+
}
|
|
213
|
+
function buildUndeclaredPublisherDomainSelectors(domains, collectionId) {
|
|
214
|
+
let candidate = "adcp-absent-owner.invalid";
|
|
215
|
+
for (let suffix = 0; domains.has(candidate); suffix++) {
|
|
216
|
+
candidate = `adcp-absent-owner-${suffix}.invalid`;
|
|
217
|
+
}
|
|
218
|
+
return [{ publisher_domain: candidate, collection_ids: [collectionId] }];
|
|
219
|
+
}
|
|
220
|
+
function evaluateCollectionSelection(candidate, selector) {
|
|
221
|
+
if (selector !== null && typeof selector === "object" && "declared_selectors" in selector) {
|
|
222
|
+
throw new Error(
|
|
223
|
+
'evaluateCollectionSelection: received a CollectionSelectionWitness where a CollectionSelectionSelector was expected. A witness is the OUTPUT of an evaluation, not an operand; build the operand explicitly, e.g. { collection_targeting: "selectable", minimum_pairs: 2 }.'
|
|
224
|
+
);
|
|
225
|
+
}
|
|
226
|
+
const resolved = {
|
|
227
|
+
collection_targeting: selector.collection_targeting,
|
|
228
|
+
minimum_pairs: selector.minimum_pairs ?? 1,
|
|
229
|
+
require_cross_publisher_collection: selector.require_cross_publisher_collection === true
|
|
230
|
+
};
|
|
231
|
+
return evaluateResolvedCollectionSelection(candidate, resolved);
|
|
232
|
+
}
|
|
233
|
+
function evaluateResolvedCollectionSelection(candidate, selector) {
|
|
234
|
+
if (!isRecord(candidate)) return { eligible: false, reason: "candidate is not an object" };
|
|
235
|
+
const flag = candidate.collection_targeting_allowed;
|
|
236
|
+
if (flag !== void 0 && typeof flag !== "boolean") {
|
|
237
|
+
return {
|
|
238
|
+
eligible: false,
|
|
239
|
+
reason: `collection_targeting_allowed must be boolean or absent; observed ${describe(flag)}`
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
const effectiveTargeting = flag === true ? "selectable" : "fixed";
|
|
243
|
+
if (selector.collection_targeting !== "any" && selector.collection_targeting !== effectiveTargeting) {
|
|
244
|
+
return {
|
|
245
|
+
eligible: false,
|
|
246
|
+
reason: selector.collection_targeting === "fixed" ? "collection_targeting_allowed is true; a fixed bundle requires literal false or absence" : "collection_targeting_allowed is not literal true"
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
const normalized = normalizeCollectionSelectors(candidate.collections);
|
|
250
|
+
if (!normalized.ok) {
|
|
251
|
+
return { eligible: false, reason: `collections did not normalize: ${normalized.failure.detail}` };
|
|
252
|
+
}
|
|
253
|
+
if (normalized.value.pairs.some((pair) => pair.collection_id === null)) {
|
|
254
|
+
return {
|
|
255
|
+
eligible: false,
|
|
256
|
+
reason: "product collections contain a domain-only bulk-grant selector; product composition requires explicit collection_ids on every entry"
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
const concrete = normalized.value.pairs;
|
|
260
|
+
if (concrete.length < selector.minimum_pairs) {
|
|
261
|
+
return {
|
|
262
|
+
eligible: false,
|
|
263
|
+
reason: `product exposes ${concrete.length} distinct concrete collection pair(s); ${selector.minimum_pairs} required`
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
const hostDomains = collectHostDomains(candidate);
|
|
267
|
+
const bundle = cloneSelectorBundle(candidate.collections);
|
|
268
|
+
let relation = null;
|
|
269
|
+
if (selector.require_cross_publisher_collection) {
|
|
270
|
+
const hostSet = new Set(hostDomains);
|
|
271
|
+
const externalDomain = normalized.value.domains.find((domain) => !hostSet.has(domain));
|
|
272
|
+
if (externalDomain === void 0) {
|
|
273
|
+
return {
|
|
274
|
+
eligible: false,
|
|
275
|
+
reason: hostDomains.length === 0 ? "product declares no publisher_properties domains, so no owner/host relation is observable" : "every collection namespace is also a declared property namespace"
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
const carrying = bundle.filter((entry) => entry.publisher_domain === externalDomain).sort((left, right) => {
|
|
279
|
+
const l = JSON.stringify([...left.collection_ids ?? []].sort());
|
|
280
|
+
const r = JSON.stringify([...right.collection_ids ?? []].sort());
|
|
281
|
+
return l < r ? -1 : l > r ? 1 : 0;
|
|
282
|
+
})[0];
|
|
283
|
+
relation = {
|
|
284
|
+
collection_publisher_domain: externalDomain,
|
|
285
|
+
property_publisher_domains: hostDomains,
|
|
286
|
+
selector: {
|
|
287
|
+
...carrying,
|
|
288
|
+
...carrying.collection_ids && { collection_ids: [...carrying.collection_ids] }
|
|
289
|
+
}
|
|
290
|
+
};
|
|
291
|
+
}
|
|
292
|
+
const subsetSource = relation === null ? concrete[0] : (
|
|
293
|
+
// Non-null is sound, not optimism: `relation.collection_publisher_domain`
|
|
294
|
+
// is taken from `normalized.value.domains`, which is derived from
|
|
295
|
+
// `concrete`, so the domain is present by construction. The previous
|
|
296
|
+
// `?? concrete[0]` read as a carriage-vs-local fallback and was not
|
|
297
|
+
// one — it could only ever have masked a normalizer bug.
|
|
298
|
+
concrete.find((pair) => pair.publisher_domain === relation.collection_publisher_domain)
|
|
299
|
+
);
|
|
300
|
+
const strictSubset = concrete.length >= 2 ? [{ publisher_domain: subsetSource.publisher_domain, collection_ids: [subsetSource.collection_id] }] : null;
|
|
301
|
+
const witness = {
|
|
302
|
+
product_id: typeof candidate.product_id === "string" ? candidate.product_id : null,
|
|
303
|
+
collection_targeting: effectiveTargeting,
|
|
304
|
+
collection_targeting_allowed: flag,
|
|
305
|
+
declared_selectors: bundle,
|
|
306
|
+
distinct_pairs: concrete,
|
|
307
|
+
strict_subset: strictSubset,
|
|
308
|
+
undeclared_collection_id_selectors: buildUndeclaredCollectionIdSelectors(
|
|
309
|
+
subsetSource.publisher_domain,
|
|
310
|
+
normalized.value.pairs
|
|
311
|
+
),
|
|
312
|
+
undeclared_publisher_domain_selectors: buildUndeclaredPublisherDomainSelectors(
|
|
313
|
+
new Set(normalized.value.domains),
|
|
314
|
+
subsetSource.collection_id
|
|
315
|
+
),
|
|
316
|
+
cross_publisher_relation: relation,
|
|
317
|
+
property_publisher_domains: hostDomains
|
|
318
|
+
};
|
|
319
|
+
return { eligible: true, witness };
|
|
320
|
+
}
|
|
321
|
+
function productSatisfiesCollectionSelection(candidate, selector) {
|
|
322
|
+
return evaluateCollectionSelection(candidate, selector).eligible;
|
|
323
|
+
}
|
|
324
|
+
const COLLECTION_WITNESS_KINDS = [
|
|
325
|
+
"declared_selectors",
|
|
326
|
+
"distinct_pairs",
|
|
327
|
+
"strict_subset",
|
|
328
|
+
"undeclared_collection_id_selectors",
|
|
329
|
+
"undeclared_publisher_domain_selectors",
|
|
330
|
+
"cross_publisher_relation",
|
|
331
|
+
"property_publisher_domains"
|
|
332
|
+
];
|
|
333
|
+
function readCollectionWitness(witness, kind) {
|
|
334
|
+
if (!witness) {
|
|
335
|
+
return {
|
|
336
|
+
ok: false,
|
|
337
|
+
detail: `witness capture "${kind}" requires a collection_selection_satisfies predicate on the same declaration`
|
|
338
|
+
};
|
|
339
|
+
}
|
|
340
|
+
switch (kind) {
|
|
341
|
+
case "declared_selectors":
|
|
342
|
+
return { ok: true, value: witness.declared_selectors };
|
|
343
|
+
case "distinct_pairs":
|
|
344
|
+
return { ok: true, value: witness.distinct_pairs };
|
|
345
|
+
case "strict_subset":
|
|
346
|
+
if (witness.strict_subset === null) {
|
|
347
|
+
return {
|
|
348
|
+
ok: false,
|
|
349
|
+
detail: "strict_subset requires a product with at least two distinct concrete collection pairs; declare minimum_pairs: 2"
|
|
350
|
+
};
|
|
351
|
+
}
|
|
352
|
+
return { ok: true, value: witness.strict_subset };
|
|
353
|
+
case "undeclared_collection_id_selectors":
|
|
354
|
+
return { ok: true, value: witness.undeclared_collection_id_selectors };
|
|
355
|
+
case "undeclared_publisher_domain_selectors":
|
|
356
|
+
return { ok: true, value: witness.undeclared_publisher_domain_selectors };
|
|
357
|
+
case "cross_publisher_relation":
|
|
358
|
+
if (witness.cross_publisher_relation === null) {
|
|
359
|
+
return {
|
|
360
|
+
ok: false,
|
|
361
|
+
detail: "cross_publisher_relation requires require_cross_publisher_collection: true on the collection_selection_satisfies operand"
|
|
362
|
+
};
|
|
363
|
+
}
|
|
364
|
+
return { ok: true, value: witness.cross_publisher_relation };
|
|
365
|
+
case "property_publisher_domains":
|
|
366
|
+
return { ok: true, value: witness.property_publisher_domains };
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
370
|
+
0 && (module.exports = {
|
|
371
|
+
COLLECTION_DIAGNOSTIC_LIMIT,
|
|
372
|
+
COLLECTION_PAIR_LIMIT,
|
|
373
|
+
COLLECTION_TARGETING_MODES,
|
|
374
|
+
COLLECTION_WITNESS_KINDS,
|
|
375
|
+
compareCollectionPairs,
|
|
376
|
+
diffCollectionPairSets,
|
|
377
|
+
evaluateCollectionSelection,
|
|
378
|
+
formatCollectionPair,
|
|
379
|
+
normalizeCollectionSelectionSelector,
|
|
380
|
+
normalizeCollectionSelectors,
|
|
381
|
+
productSatisfiesCollectionSelection,
|
|
382
|
+
readCollectionWitness,
|
|
383
|
+
summarizeCollectionPairs
|
|
384
|
+
});
|