@adcp/sdk 16.3.0 → 16.4.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/dist/lib/adapters/version/3.1/assignments.d.mts +12 -0
- package/dist/lib/adapters/version/3.1/assignments.d.ts +12 -0
- package/dist/lib/adapters/version/3.1/assignments.js +67 -0
- package/dist/lib/adapters/version/3.1/assignments.mjs +43 -0
- package/dist/lib/adapters/version/3.1/bidding.d.mts +3 -0
- package/dist/lib/adapters/version/3.1/bidding.d.ts +3 -0
- package/dist/lib/adapters/version/3.1/bidding.js +115 -0
- package/dist/lib/adapters/version/3.1/bidding.mjs +90 -0
- package/dist/lib/adapters/version/3.1/delivery.d.mts +7 -0
- package/dist/lib/adapters/version/3.1/delivery.d.ts +7 -0
- package/dist/lib/adapters/version/3.1/delivery.js +78 -0
- package/dist/lib/adapters/version/3.1/delivery.mjs +54 -0
- package/dist/lib/adapters/version/3.1/index.d.mts +3 -0
- package/dist/lib/adapters/version/3.1/index.d.ts +3 -0
- package/dist/lib/adapters/version/3.1/index.js +36 -0
- package/dist/lib/adapters/version/3.1/index.mjs +12 -0
- package/dist/lib/adapters/version/index.d.mts +59 -7
- package/dist/lib/adapters/version/index.d.ts +59 -7
- package/dist/lib/adapters/version/index.js +51 -2
- package/dist/lib/adapters/version/index.mjs +49 -1
- package/dist/lib/adapters/version/negotiate.d.mts +71 -0
- package/dist/lib/adapters/version/negotiate.d.ts +71 -0
- package/dist/lib/adapters/version/negotiate.js +101 -0
- package/dist/lib/adapters/version/negotiate.mjs +73 -0
- package/dist/lib/adapters/version/schema-projection.d.mts +33 -0
- package/dist/lib/adapters/version/schema-projection.d.ts +33 -0
- package/dist/lib/adapters/version/schema-projection.js +294 -0
- package/dist/lib/adapters/version/schema-projection.mjs +269 -0
- package/dist/lib/adapters/version/translate.d.mts +122 -0
- package/dist/lib/adapters/version/translate.d.ts +122 -0
- package/dist/lib/adapters/version/translate.js +169 -0
- package/dist/lib/adapters/version/translate.mjs +141 -0
- package/dist/lib/adapters/version/types.d.mts +51 -10
- package/dist/lib/adapters/version/types.d.ts +51 -10
- package/dist/lib/core/AgentClient.d.mts +1 -1
- package/dist/lib/core/AgentClient.d.ts +1 -1
- package/dist/lib/core/ConversationTypes.d.mts +7 -0
- package/dist/lib/core/ConversationTypes.d.ts +7 -0
- package/dist/lib/core/SingleAgentClient.d.mts +82 -0
- package/dist/lib/core/SingleAgentClient.d.ts +82 -0
- package/dist/lib/core/SingleAgentClient.js +323 -15
- package/dist/lib/core/SingleAgentClient.mjs +337 -17
- package/dist/lib/core/TaskExecutor.d.mts +1 -1
- package/dist/lib/core/TaskExecutor.d.ts +1 -1
- package/dist/lib/core/TaskExecutor.js +97 -21
- package/dist/lib/core/TaskExecutor.mjs +97 -21
- package/dist/lib/core/account-reduction.d.mts +36 -0
- package/dist/lib/core/account-reduction.d.ts +36 -0
- package/dist/lib/core/account-reduction.js +127 -0
- package/dist/lib/core/account-reduction.mjs +98 -0
- package/dist/lib/core/buyer-account-registry.d.mts +52 -0
- package/dist/lib/core/buyer-account-registry.d.ts +52 -0
- package/dist/lib/core/buyer-account-registry.js +142 -6
- package/dist/lib/core/buyer-account-registry.mjs +142 -6
- package/dist/lib/media-buy/action-contracts.js +4 -4
- package/dist/lib/media-buy/action-contracts.mjs +4 -4
- package/dist/lib/media-buy/action-types.d.mts +3 -2
- package/dist/lib/media-buy/action-types.d.ts +3 -2
- package/dist/lib/media-buy/update-fields.generated.d.mts +3 -2
- package/dist/lib/media-buy/update-fields.generated.d.ts +3 -2
- package/dist/lib/registry/types.generated.d.mts +1 -1
- package/dist/lib/registry/types.generated.d.ts +1 -1
- package/dist/lib/server/create-adcp-server.d.mts +21 -0
- package/dist/lib/server/create-adcp-server.d.ts +21 -0
- package/dist/lib/server/create-adcp-server.js +97 -15
- package/dist/lib/server/create-adcp-server.mjs +83 -1
- package/dist/lib/server/index.d.mts +1 -1
- package/dist/lib/server/index.d.ts +1 -1
- package/dist/lib/storage/interfaces.d.mts +2 -0
- package/dist/lib/storage/interfaces.d.ts +2 -0
- package/dist/lib/testing/storyboard/validations.d.mts +1 -1
- package/dist/lib/testing/storyboard/validations.d.ts +1 -1
- package/dist/lib/version.d.mts +3 -3
- package/dist/lib/version.d.ts +3 -3
- package/dist/lib/version.js +3 -3
- package/dist/lib/version.mjs +3 -3
- package/docs/TYPE-SUMMARY.md +2 -2
- package/docs/llms.txt +3 -3
- package/docs/migration-14.x-rc-worksheet.md +4 -4
- package/package.json +1 -1
|
@@ -0,0 +1,127 @@
|
|
|
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 account_reduction_exports = {};
|
|
20
|
+
__export(account_reduction_exports, {
|
|
21
|
+
accountKeySites: () => accountKeySites,
|
|
22
|
+
naturalAccountKey: () => naturalAccountKey,
|
|
23
|
+
reducedFields: () => reducedFields,
|
|
24
|
+
sameAccountKey: () => sameAccountKey,
|
|
25
|
+
valueAt: () => valueAt,
|
|
26
|
+
withReducedKeyAt: () => withReducedKeyAt
|
|
27
|
+
});
|
|
28
|
+
module.exports = __toCommonJS(account_reduction_exports);
|
|
29
|
+
var import_account_key = require('./account-key.js');
|
|
30
|
+
function isObject(value) {
|
|
31
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
32
|
+
}
|
|
33
|
+
const REDUCIBLE_FIELDS = ["operator_unit", "currency", "timezone"];
|
|
34
|
+
function naturalAccountKey(value) {
|
|
35
|
+
if (!isObject(value) || typeof value.account_id === "string") return void 0;
|
|
36
|
+
const brand = value.brand;
|
|
37
|
+
if (!isObject(brand) || typeof brand.domain !== "string" || typeof value.operator !== "string") return void 0;
|
|
38
|
+
return {
|
|
39
|
+
brand: {
|
|
40
|
+
domain: brand.domain,
|
|
41
|
+
...typeof brand.brand_id === "string" && { brand_id: brand.brand_id },
|
|
42
|
+
...Array.isArray(brand.countries) && { countries: brand.countries }
|
|
43
|
+
},
|
|
44
|
+
operator: value.operator,
|
|
45
|
+
...isObject(value.operator_unit) && { operator_unit: value.operator_unit },
|
|
46
|
+
...typeof value.currency === "string" && { currency: value.currency },
|
|
47
|
+
...typeof value.timezone === "string" && { timezone: value.timezone },
|
|
48
|
+
...typeof value.sandbox === "boolean" && { sandbox: value.sandbox }
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
function accountKeySites(params) {
|
|
52
|
+
const sites = [];
|
|
53
|
+
const visit = (value, path, depth) => {
|
|
54
|
+
if (depth > 8) return;
|
|
55
|
+
if (Array.isArray(value)) {
|
|
56
|
+
value.forEach((item, index) => visit(item, [...path, index], depth + 1));
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
if (!isObject(value)) return;
|
|
60
|
+
for (const [field, child] of Object.entries(value)) {
|
|
61
|
+
if (field === "account") {
|
|
62
|
+
const key = naturalAccountKey(child);
|
|
63
|
+
if (key) sites.push({ path: [...path, field], key });
|
|
64
|
+
} else if (field === "accounts" && Array.isArray(child)) {
|
|
65
|
+
child.forEach((entry, index) => {
|
|
66
|
+
const key = naturalAccountKey(entry);
|
|
67
|
+
if (key) sites.push({ path: [...path, field, index], key });
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
visit(child, [...path, field], depth + 1);
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
visit(params, [], 0);
|
|
74
|
+
return sites;
|
|
75
|
+
}
|
|
76
|
+
function valueAt(root, path) {
|
|
77
|
+
let current = root;
|
|
78
|
+
for (const segment of path) {
|
|
79
|
+
if (current === null || typeof current !== "object") return void 0;
|
|
80
|
+
current = current[segment];
|
|
81
|
+
}
|
|
82
|
+
return current;
|
|
83
|
+
}
|
|
84
|
+
function withReducedKeyAt(root, path, reduced) {
|
|
85
|
+
if (path.length === 0) {
|
|
86
|
+
if (!isObject(root) || "account_id" in reduced) return root;
|
|
87
|
+
const next = { ...root };
|
|
88
|
+
for (const field of REDUCIBLE_FIELDS) {
|
|
89
|
+
if (reduced[field] === void 0) delete next[field];
|
|
90
|
+
}
|
|
91
|
+
if (isObject(next.brand) && reduced.brand.countries === void 0 && "countries" in next.brand) {
|
|
92
|
+
const { countries: _, ...brand } = next.brand;
|
|
93
|
+
next.brand = brand;
|
|
94
|
+
}
|
|
95
|
+
return next;
|
|
96
|
+
}
|
|
97
|
+
const [head, ...rest] = path;
|
|
98
|
+
if (Array.isArray(root) && typeof head === "number") {
|
|
99
|
+
const copy = [...root];
|
|
100
|
+
copy[head] = withReducedKeyAt(copy[head], rest, reduced);
|
|
101
|
+
return copy;
|
|
102
|
+
}
|
|
103
|
+
if (isObject(root) && typeof head === "string") {
|
|
104
|
+
return { ...root, [head]: withReducedKeyAt(root[head], rest, reduced) };
|
|
105
|
+
}
|
|
106
|
+
return root;
|
|
107
|
+
}
|
|
108
|
+
function reducedFields(full, reduced) {
|
|
109
|
+
if ("account_id" in full || "account_id" in reduced) return [];
|
|
110
|
+
const fields = REDUCIBLE_FIELDS.filter(
|
|
111
|
+
(field) => full[field] !== void 0 && reduced[field] === void 0
|
|
112
|
+
);
|
|
113
|
+
if (full.brand.countries !== void 0 && reduced.brand.countries === void 0) fields.push("brand.countries");
|
|
114
|
+
return fields;
|
|
115
|
+
}
|
|
116
|
+
function sameAccountKey(left, right) {
|
|
117
|
+
return (0, import_account_key.accountReferenceKey)(left) === (0, import_account_key.accountReferenceKey)(right);
|
|
118
|
+
}
|
|
119
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
120
|
+
0 && (module.exports = {
|
|
121
|
+
accountKeySites,
|
|
122
|
+
naturalAccountKey,
|
|
123
|
+
reducedFields,
|
|
124
|
+
sameAccountKey,
|
|
125
|
+
valueAt,
|
|
126
|
+
withReducedKeyAt
|
|
127
|
+
});
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { accountReferenceKey } from "./account-key.mjs";
|
|
2
|
+
function isObject(value) {
|
|
3
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
4
|
+
}
|
|
5
|
+
const REDUCIBLE_FIELDS = ["operator_unit", "currency", "timezone"];
|
|
6
|
+
function naturalAccountKey(value) {
|
|
7
|
+
if (!isObject(value) || typeof value.account_id === "string") return void 0;
|
|
8
|
+
const brand = value.brand;
|
|
9
|
+
if (!isObject(brand) || typeof brand.domain !== "string" || typeof value.operator !== "string") return void 0;
|
|
10
|
+
return {
|
|
11
|
+
brand: {
|
|
12
|
+
domain: brand.domain,
|
|
13
|
+
...typeof brand.brand_id === "string" && { brand_id: brand.brand_id },
|
|
14
|
+
...Array.isArray(brand.countries) && { countries: brand.countries }
|
|
15
|
+
},
|
|
16
|
+
operator: value.operator,
|
|
17
|
+
...isObject(value.operator_unit) && { operator_unit: value.operator_unit },
|
|
18
|
+
...typeof value.currency === "string" && { currency: value.currency },
|
|
19
|
+
...typeof value.timezone === "string" && { timezone: value.timezone },
|
|
20
|
+
...typeof value.sandbox === "boolean" && { sandbox: value.sandbox }
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
function accountKeySites(params) {
|
|
24
|
+
const sites = [];
|
|
25
|
+
const visit = (value, path, depth) => {
|
|
26
|
+
if (depth > 8) return;
|
|
27
|
+
if (Array.isArray(value)) {
|
|
28
|
+
value.forEach((item, index) => visit(item, [...path, index], depth + 1));
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
if (!isObject(value)) return;
|
|
32
|
+
for (const [field, child] of Object.entries(value)) {
|
|
33
|
+
if (field === "account") {
|
|
34
|
+
const key = naturalAccountKey(child);
|
|
35
|
+
if (key) sites.push({ path: [...path, field], key });
|
|
36
|
+
} else if (field === "accounts" && Array.isArray(child)) {
|
|
37
|
+
child.forEach((entry, index) => {
|
|
38
|
+
const key = naturalAccountKey(entry);
|
|
39
|
+
if (key) sites.push({ path: [...path, field, index], key });
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
visit(child, [...path, field], depth + 1);
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
visit(params, [], 0);
|
|
46
|
+
return sites;
|
|
47
|
+
}
|
|
48
|
+
function valueAt(root, path) {
|
|
49
|
+
let current = root;
|
|
50
|
+
for (const segment of path) {
|
|
51
|
+
if (current === null || typeof current !== "object") return void 0;
|
|
52
|
+
current = current[segment];
|
|
53
|
+
}
|
|
54
|
+
return current;
|
|
55
|
+
}
|
|
56
|
+
function withReducedKeyAt(root, path, reduced) {
|
|
57
|
+
if (path.length === 0) {
|
|
58
|
+
if (!isObject(root) || "account_id" in reduced) return root;
|
|
59
|
+
const next = { ...root };
|
|
60
|
+
for (const field of REDUCIBLE_FIELDS) {
|
|
61
|
+
if (reduced[field] === void 0) delete next[field];
|
|
62
|
+
}
|
|
63
|
+
if (isObject(next.brand) && reduced.brand.countries === void 0 && "countries" in next.brand) {
|
|
64
|
+
const { countries: _, ...brand } = next.brand;
|
|
65
|
+
next.brand = brand;
|
|
66
|
+
}
|
|
67
|
+
return next;
|
|
68
|
+
}
|
|
69
|
+
const [head, ...rest] = path;
|
|
70
|
+
if (Array.isArray(root) && typeof head === "number") {
|
|
71
|
+
const copy = [...root];
|
|
72
|
+
copy[head] = withReducedKeyAt(copy[head], rest, reduced);
|
|
73
|
+
return copy;
|
|
74
|
+
}
|
|
75
|
+
if (isObject(root) && typeof head === "string") {
|
|
76
|
+
return { ...root, [head]: withReducedKeyAt(root[head], rest, reduced) };
|
|
77
|
+
}
|
|
78
|
+
return root;
|
|
79
|
+
}
|
|
80
|
+
function reducedFields(full, reduced) {
|
|
81
|
+
if ("account_id" in full || "account_id" in reduced) return [];
|
|
82
|
+
const fields = REDUCIBLE_FIELDS.filter(
|
|
83
|
+
(field) => full[field] !== void 0 && reduced[field] === void 0
|
|
84
|
+
);
|
|
85
|
+
if (full.brand.countries !== void 0 && reduced.brand.countries === void 0) fields.push("brand.countries");
|
|
86
|
+
return fields;
|
|
87
|
+
}
|
|
88
|
+
function sameAccountKey(left, right) {
|
|
89
|
+
return accountReferenceKey(left) === accountReferenceKey(right);
|
|
90
|
+
}
|
|
91
|
+
export {
|
|
92
|
+
accountKeySites,
|
|
93
|
+
naturalAccountKey,
|
|
94
|
+
reducedFields,
|
|
95
|
+
sameAccountKey,
|
|
96
|
+
valueAt,
|
|
97
|
+
withReducedKeyAt
|
|
98
|
+
};
|
|
@@ -24,6 +24,17 @@ export interface ProvisionedAccount {
|
|
|
24
24
|
idempotencyKey: string;
|
|
25
25
|
startedAt: string;
|
|
26
26
|
};
|
|
27
|
+
/**
|
|
28
|
+
* Set on a buyer key that reached the seller reduced to an older release's
|
|
29
|
+
* account key (`accountIdentityDowngrade: 'reduce'`): the key the seller
|
|
30
|
+
* knows the account by, and the release it was reduced for.
|
|
31
|
+
*/
|
|
32
|
+
reduction?: {
|
|
33
|
+
account: AccountReference;
|
|
34
|
+
adcpVersion: string;
|
|
35
|
+
};
|
|
36
|
+
/** Set on a reduced seller key: the one buyer key allowed to use it. */
|
|
37
|
+
reducedFrom?: AccountReference;
|
|
27
38
|
}
|
|
28
39
|
/** Partitioned by seller and caller scope. Implementations must provide read-your-writes. */
|
|
29
40
|
export interface BuyerAccountStorage {
|
|
@@ -61,6 +72,8 @@ export declare class BuyerAccountRegistry {
|
|
|
61
72
|
private repair?;
|
|
62
73
|
private readonly registryOptions;
|
|
63
74
|
private entries;
|
|
75
|
+
/** Reduction bindings when no storage is configured (never evicted). */
|
|
76
|
+
private reductions;
|
|
64
77
|
private pending;
|
|
65
78
|
private writes;
|
|
66
79
|
private repairs;
|
|
@@ -96,4 +109,43 @@ export declare class BuyerAccountRegistry {
|
|
|
96
109
|
}, options?: TaskOptions): Promise<void>;
|
|
97
110
|
private repairStatus;
|
|
98
111
|
private runRepair;
|
|
112
|
+
private reductionKey;
|
|
113
|
+
private readReduction;
|
|
114
|
+
/** Insert-if-absent; returns false when another writer already holds the key. */
|
|
115
|
+
private insertReduction;
|
|
116
|
+
private replaceReduction;
|
|
117
|
+
/**
|
|
118
|
+
* Bind reduced account keys to the buyer keys they came from, before a
|
|
119
|
+
* request carrying them is sent to a seller on an older release. Each
|
|
120
|
+
* reduced key serves one buyer key only. A key sent unreduced claims itself
|
|
121
|
+
* (`account` and `reduced` equal), so a plain key and a reduced one can never
|
|
122
|
+
* land on the same seller account in either order.
|
|
123
|
+
*
|
|
124
|
+
* All-or-nothing: every claim is checked, against storage and against the
|
|
125
|
+
* other claims in the same request, before any is written. On a conflict
|
|
126
|
+
* nothing is written and the index and owner of the first conflicting claim
|
|
127
|
+
* are returned. A claim lost to a concurrent writer rolls back the claims
|
|
128
|
+
* this call already wrote.
|
|
129
|
+
*/
|
|
130
|
+
claimReductions(claims: ReadonlyArray<{
|
|
131
|
+
account: AccountReference;
|
|
132
|
+
reduced: AccountReference;
|
|
133
|
+
adcpVersion: string;
|
|
134
|
+
}>): Promise<{
|
|
135
|
+
index: number;
|
|
136
|
+
owner: AccountReference;
|
|
137
|
+
} | undefined>;
|
|
138
|
+
/** The buyer key a reduced seller key was claimed for, if any. */
|
|
139
|
+
reductionOwner(reduced: AccountReference): Promise<AccountReference | undefined>;
|
|
140
|
+
/** The reduced key a buyer key reached an older seller with, if any. */
|
|
141
|
+
reductionFor(account: AccountReference): Promise<ProvisionedAccount['reduction']>;
|
|
142
|
+
/**
|
|
143
|
+
* Forget that `account` reached this seller under a reduced key. Call once
|
|
144
|
+
* the account has moved to its full key: after a `sync_accounts` settings
|
|
145
|
+
* update rekeys `operator_unit` in place, or after opening a new account
|
|
146
|
+
* under the full key when the reduced fields (`currency`, `timezone`,
|
|
147
|
+
* `brand.countries`) cannot change on an existing account. Until then the
|
|
148
|
+
* client keeps addressing the seller account by its reduced key.
|
|
149
|
+
*/
|
|
150
|
+
clearReduction(account: AccountReference): Promise<void>;
|
|
99
151
|
}
|
|
@@ -24,6 +24,17 @@ export interface ProvisionedAccount {
|
|
|
24
24
|
idempotencyKey: string;
|
|
25
25
|
startedAt: string;
|
|
26
26
|
};
|
|
27
|
+
/**
|
|
28
|
+
* Set on a buyer key that reached the seller reduced to an older release's
|
|
29
|
+
* account key (`accountIdentityDowngrade: 'reduce'`): the key the seller
|
|
30
|
+
* knows the account by, and the release it was reduced for.
|
|
31
|
+
*/
|
|
32
|
+
reduction?: {
|
|
33
|
+
account: AccountReference;
|
|
34
|
+
adcpVersion: string;
|
|
35
|
+
};
|
|
36
|
+
/** Set on a reduced seller key: the one buyer key allowed to use it. */
|
|
37
|
+
reducedFrom?: AccountReference;
|
|
27
38
|
}
|
|
28
39
|
/** Partitioned by seller and caller scope. Implementations must provide read-your-writes. */
|
|
29
40
|
export interface BuyerAccountStorage {
|
|
@@ -61,6 +72,8 @@ export declare class BuyerAccountRegistry {
|
|
|
61
72
|
private repair?;
|
|
62
73
|
private readonly registryOptions;
|
|
63
74
|
private entries;
|
|
75
|
+
/** Reduction bindings when no storage is configured (never evicted). */
|
|
76
|
+
private reductions;
|
|
64
77
|
private pending;
|
|
65
78
|
private writes;
|
|
66
79
|
private repairs;
|
|
@@ -96,4 +109,43 @@ export declare class BuyerAccountRegistry {
|
|
|
96
109
|
}, options?: TaskOptions): Promise<void>;
|
|
97
110
|
private repairStatus;
|
|
98
111
|
private runRepair;
|
|
112
|
+
private reductionKey;
|
|
113
|
+
private readReduction;
|
|
114
|
+
/** Insert-if-absent; returns false when another writer already holds the key. */
|
|
115
|
+
private insertReduction;
|
|
116
|
+
private replaceReduction;
|
|
117
|
+
/**
|
|
118
|
+
* Bind reduced account keys to the buyer keys they came from, before a
|
|
119
|
+
* request carrying them is sent to a seller on an older release. Each
|
|
120
|
+
* reduced key serves one buyer key only. A key sent unreduced claims itself
|
|
121
|
+
* (`account` and `reduced` equal), so a plain key and a reduced one can never
|
|
122
|
+
* land on the same seller account in either order.
|
|
123
|
+
*
|
|
124
|
+
* All-or-nothing: every claim is checked, against storage and against the
|
|
125
|
+
* other claims in the same request, before any is written. On a conflict
|
|
126
|
+
* nothing is written and the index and owner of the first conflicting claim
|
|
127
|
+
* are returned. A claim lost to a concurrent writer rolls back the claims
|
|
128
|
+
* this call already wrote.
|
|
129
|
+
*/
|
|
130
|
+
claimReductions(claims: ReadonlyArray<{
|
|
131
|
+
account: AccountReference;
|
|
132
|
+
reduced: AccountReference;
|
|
133
|
+
adcpVersion: string;
|
|
134
|
+
}>): Promise<{
|
|
135
|
+
index: number;
|
|
136
|
+
owner: AccountReference;
|
|
137
|
+
} | undefined>;
|
|
138
|
+
/** The buyer key a reduced seller key was claimed for, if any. */
|
|
139
|
+
reductionOwner(reduced: AccountReference): Promise<AccountReference | undefined>;
|
|
140
|
+
/** The reduced key a buyer key reached an older seller with, if any. */
|
|
141
|
+
reductionFor(account: AccountReference): Promise<ProvisionedAccount['reduction']>;
|
|
142
|
+
/**
|
|
143
|
+
* Forget that `account` reached this seller under a reduced key. Call once
|
|
144
|
+
* the account has moved to its full key: after a `sync_accounts` settings
|
|
145
|
+
* update rekeys `operator_unit` in place, or after opening a new account
|
|
146
|
+
* under the full key when the reduced fields (`currency`, `timezone`,
|
|
147
|
+
* `brand.countries`) cannot change on an existing account. Until then the
|
|
148
|
+
* client keeps addressing the seller account by its reduced key.
|
|
149
|
+
*/
|
|
150
|
+
clearReduction(account: AccountReference): Promise<void>;
|
|
99
151
|
}
|
|
@@ -45,6 +45,10 @@ function isDefinitiveRejection(result) {
|
|
|
45
45
|
result.adcpError && !result.adcpError.synthetic && !result.adcpError.code.startsWith("IDEMPOTENCY_") && ["terminal", "correctable"].includes(result.adcpError.recovery ?? "")
|
|
46
46
|
);
|
|
47
47
|
}
|
|
48
|
+
function rowMatchesReference(ref, row) {
|
|
49
|
+
if ("account_id" in ref) return row.account_id === ref.account_id;
|
|
50
|
+
return row.brand?.domain === ref.brand.domain && row.operator === ref.operator && (!ref.brand.brand_id || row.brand.brand_id === ref.brand.brand_id) && (0, import_account_resolution.sameCountrySet)(ref.brand.countries, row.brand.countries) && (!ref.operator_unit || row.operator_unit?.id === ref.operator_unit.id) && (!ref.currency || row.currency === ref.currency) && (!ref.timezone || row.timezone === ref.timezone) && (row.sandbox === void 0 || row.sandbox === true === (ref.sandbox === true));
|
|
51
|
+
}
|
|
48
52
|
class BuyerAccountRegistry {
|
|
49
53
|
constructor(scope, provision, storage, repair, registryOptions = {}) {
|
|
50
54
|
this.scope = scope;
|
|
@@ -63,6 +67,8 @@ class BuyerAccountRegistry {
|
|
|
63
67
|
repair;
|
|
64
68
|
registryOptions;
|
|
65
69
|
entries = /* @__PURE__ */ new Map();
|
|
70
|
+
/** Reduction bindings when no storage is configured (never evicted). */
|
|
71
|
+
reductions = /* @__PURE__ */ new Map();
|
|
66
72
|
pending = /* @__PURE__ */ new Map();
|
|
67
73
|
writes = Promise.resolve();
|
|
68
74
|
repairs = /* @__PURE__ */ new Map();
|
|
@@ -444,12 +450,13 @@ class BuyerAccountRegistry {
|
|
|
444
450
|
return this.enqueue(async () => {
|
|
445
451
|
if (scope !== this.scope()) return;
|
|
446
452
|
for (const ref of refs) {
|
|
447
|
-
const
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
+
const isRow = (value) => !!value && typeof value === "object";
|
|
454
|
+
let candidates = rows.filter((value) => isRow(value) && rowMatchesReference(ref, value));
|
|
455
|
+
if (candidates.length === 0 && !("account_id" in ref)) {
|
|
456
|
+
const reduction = (await this.reductionFor(ref))?.account;
|
|
457
|
+
if (scope !== this.scope()) return;
|
|
458
|
+
if (reduction) candidates = rows.filter((value) => isRow(value) && rowMatchesReference(reduction, value));
|
|
459
|
+
}
|
|
453
460
|
if (candidates.length !== 1) continue;
|
|
454
461
|
let row = candidates[0];
|
|
455
462
|
if (row.action === "failed" || typeof row.status !== "string") continue;
|
|
@@ -614,6 +621,135 @@ class BuyerAccountRegistry {
|
|
|
614
621
|
}
|
|
615
622
|
}
|
|
616
623
|
}
|
|
624
|
+
reductionKey(kind, account, scope = this.scope()) {
|
|
625
|
+
return `${scope}:${kind}:${(0, import_account_key.accountReferenceKey)(account)}`;
|
|
626
|
+
}
|
|
627
|
+
async readReduction(key) {
|
|
628
|
+
const entry = this.storage ? await this.storage.get(key) : this.reductions.get(key);
|
|
629
|
+
return entry && entry.status === "reduced" ? structuredClone(entry) : void 0;
|
|
630
|
+
}
|
|
631
|
+
/** Insert-if-absent; returns false when another writer already holds the key. */
|
|
632
|
+
async insertReduction(key, entry) {
|
|
633
|
+
const prior = this.storage ? await this.storage.get(key) : this.reductions.get(key);
|
|
634
|
+
if (prior && prior.status === "reduced") return false;
|
|
635
|
+
if (this.storage?.compareAndSet) {
|
|
636
|
+
return this.storage.compareAndSet(key, prior?.revision, { ...entry, revision: (prior?.revision ?? 0) + 1 });
|
|
637
|
+
}
|
|
638
|
+
if (this.storage) await this.storage.set(key, structuredClone(entry));
|
|
639
|
+
else this.reductions.set(key, structuredClone(entry));
|
|
640
|
+
return true;
|
|
641
|
+
}
|
|
642
|
+
async replaceReduction(key, entry) {
|
|
643
|
+
const prior = this.storage ? await this.storage.get(key) : this.reductions.get(key);
|
|
644
|
+
if (this.storage?.compareAndSet) {
|
|
645
|
+
const next = { ...entry, revision: (prior?.revision ?? 0) + 1 };
|
|
646
|
+
if (!await this.storage.compareAndSet(key, prior?.revision, next))
|
|
647
|
+
throw new import_errors.AccountSetupRequiredError("Account storage revision changed. Reload and reconcile before retrying.");
|
|
648
|
+
} else if (this.storage) await this.storage.set(key, structuredClone(entry));
|
|
649
|
+
else this.reductions.set(key, structuredClone(entry));
|
|
650
|
+
}
|
|
651
|
+
/**
|
|
652
|
+
* Bind reduced account keys to the buyer keys they came from, before a
|
|
653
|
+
* request carrying them is sent to a seller on an older release. Each
|
|
654
|
+
* reduced key serves one buyer key only. A key sent unreduced claims itself
|
|
655
|
+
* (`account` and `reduced` equal), so a plain key and a reduced one can never
|
|
656
|
+
* land on the same seller account in either order.
|
|
657
|
+
*
|
|
658
|
+
* All-or-nothing: every claim is checked, against storage and against the
|
|
659
|
+
* other claims in the same request, before any is written. On a conflict
|
|
660
|
+
* nothing is written and the index and owner of the first conflicting claim
|
|
661
|
+
* are returned. A claim lost to a concurrent writer rolls back the claims
|
|
662
|
+
* this call already wrote.
|
|
663
|
+
*/
|
|
664
|
+
async claimReductions(claims) {
|
|
665
|
+
const scope = this.scope();
|
|
666
|
+
let conflict;
|
|
667
|
+
await this.enqueue(async () => {
|
|
668
|
+
const pending = /* @__PURE__ */ new Map();
|
|
669
|
+
const toInsert = [];
|
|
670
|
+
for (const [index, claim] of claims.entries()) {
|
|
671
|
+
const reducedKey = (0, import_account_key.accountReferenceKey)(claim.reduced);
|
|
672
|
+
const inRequest = pending.get(reducedKey);
|
|
673
|
+
if (inRequest && (0, import_account_key.accountReferenceKey)(inRequest) !== (0, import_account_key.accountReferenceKey)(claim.account)) {
|
|
674
|
+
conflict = { index, owner: inRequest };
|
|
675
|
+
return;
|
|
676
|
+
}
|
|
677
|
+
pending.set(reducedKey, claim.account);
|
|
678
|
+
const owner = (await this.readReduction(this.reductionKey("reduced", claim.reduced, scope)))?.reducedFrom;
|
|
679
|
+
if (owner && (0, import_account_key.accountReferenceKey)(owner) !== (0, import_account_key.accountReferenceKey)(claim.account)) {
|
|
680
|
+
conflict = { index, owner };
|
|
681
|
+
return;
|
|
682
|
+
}
|
|
683
|
+
if (!owner && !inRequest) toInsert.push(index);
|
|
684
|
+
}
|
|
685
|
+
const inserted = [];
|
|
686
|
+
for (const index of toInsert) {
|
|
687
|
+
const claim = claims[index];
|
|
688
|
+
const reverseKey = this.reductionKey("reduced", claim.reduced, scope);
|
|
689
|
+
const ok = await this.insertReduction(reverseKey, {
|
|
690
|
+
account: structuredClone(claim.reduced),
|
|
691
|
+
status: "reduced",
|
|
692
|
+
reducedFrom: structuredClone(claim.account)
|
|
693
|
+
});
|
|
694
|
+
const winner = ok ? void 0 : (await this.readReduction(reverseKey))?.reducedFrom;
|
|
695
|
+
if (!ok && winner && (0, import_account_key.accountReferenceKey)(winner) !== (0, import_account_key.accountReferenceKey)(claim.account)) {
|
|
696
|
+
for (const done of inserted) {
|
|
697
|
+
const doneClaim = claims[done];
|
|
698
|
+
await this.replaceReduction(this.reductionKey("reduced", doneClaim.reduced, scope), {
|
|
699
|
+
account: structuredClone(doneClaim.reduced),
|
|
700
|
+
status: "released"
|
|
701
|
+
});
|
|
702
|
+
}
|
|
703
|
+
conflict = { index, owner: winner };
|
|
704
|
+
return;
|
|
705
|
+
}
|
|
706
|
+
if (ok) inserted.push(index);
|
|
707
|
+
}
|
|
708
|
+
for (const claim of claims) {
|
|
709
|
+
if ((0, import_account_key.accountReferenceKey)(claim.account) === (0, import_account_key.accountReferenceKey)(claim.reduced)) continue;
|
|
710
|
+
const forwardKey = this.reductionKey("reduction", claim.account, scope);
|
|
711
|
+
const current = await this.readReduction(forwardKey);
|
|
712
|
+
const reduction = { account: claim.reduced, adcpVersion: claim.adcpVersion };
|
|
713
|
+
if (current?.reduction && (0, import_node_util.isDeepStrictEqual)(current.reduction, reduction)) continue;
|
|
714
|
+
await this.replaceReduction(forwardKey, {
|
|
715
|
+
account: structuredClone(claim.account),
|
|
716
|
+
status: "reduced",
|
|
717
|
+
reduction: structuredClone(reduction)
|
|
718
|
+
});
|
|
719
|
+
}
|
|
720
|
+
});
|
|
721
|
+
return conflict;
|
|
722
|
+
}
|
|
723
|
+
/** The buyer key a reduced seller key was claimed for, if any. */
|
|
724
|
+
async reductionOwner(reduced) {
|
|
725
|
+
return (await this.readReduction(this.reductionKey("reduced", reduced)))?.reducedFrom;
|
|
726
|
+
}
|
|
727
|
+
/** The reduced key a buyer key reached an older seller with, if any. */
|
|
728
|
+
async reductionFor(account) {
|
|
729
|
+
return (await this.readReduction(this.reductionKey("reduction", account)))?.reduction;
|
|
730
|
+
}
|
|
731
|
+
/**
|
|
732
|
+
* Forget that `account` reached this seller under a reduced key. Call once
|
|
733
|
+
* the account has moved to its full key: after a `sync_accounts` settings
|
|
734
|
+
* update rekeys `operator_unit` in place, or after opening a new account
|
|
735
|
+
* under the full key when the reduced fields (`currency`, `timezone`,
|
|
736
|
+
* `brand.countries`) cannot change on an existing account. Until then the
|
|
737
|
+
* client keeps addressing the seller account by its reduced key.
|
|
738
|
+
*/
|
|
739
|
+
async clearReduction(account) {
|
|
740
|
+
const scope = this.scope();
|
|
741
|
+
await this.enqueue(async () => {
|
|
742
|
+
const forwardKey = this.reductionKey("reduction", account, scope);
|
|
743
|
+
const forward = await this.readReduction(forwardKey);
|
|
744
|
+
if (!forward?.reduction) return;
|
|
745
|
+
const reverseKey = this.reductionKey("reduced", forward.reduction.account, scope);
|
|
746
|
+
const reverse = await this.readReduction(reverseKey);
|
|
747
|
+
if (reverse?.reducedFrom && (0, import_account_key.accountReferenceKey)(reverse.reducedFrom) === (0, import_account_key.accountReferenceKey)(account)) {
|
|
748
|
+
await this.replaceReduction(reverseKey, { account: reverse.account, status: "released" });
|
|
749
|
+
}
|
|
750
|
+
await this.replaceReduction(forwardKey, { account: forward.account, status: "released" });
|
|
751
|
+
});
|
|
752
|
+
}
|
|
617
753
|
}
|
|
618
754
|
// Annotate the CommonJS export names for ESM import in node:
|
|
619
755
|
0 && (module.exports = {
|