@adcp/sdk 14.0.0 → 14.2.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/implicit-account-store.d.mts +12 -7
- package/dist/lib/adapters/implicit-account-store.d.ts +12 -7
- package/dist/lib/adapters/implicit-account-store.js +69 -15
- package/dist/lib/adapters/implicit-account-store.mjs +69 -15
- package/dist/lib/core/AgentClient.d.mts +1 -0
- package/dist/lib/core/AgentClient.d.ts +1 -0
- package/dist/lib/core/AgentClient.js +3 -0
- package/dist/lib/core/AgentClient.mjs +3 -0
- package/dist/lib/core/SingleAgentClient.d.mts +35 -2
- package/dist/lib/core/SingleAgentClient.d.ts +35 -2
- package/dist/lib/core/SingleAgentClient.js +377 -36
- package/dist/lib/core/SingleAgentClient.mjs +387 -38
- package/dist/lib/core/TaskExecutor.d.mts +3 -1
- package/dist/lib/core/TaskExecutor.d.ts +3 -1
- package/dist/lib/core/TaskExecutor.js +17 -12
- package/dist/lib/core/TaskExecutor.mjs +17 -12
- package/dist/lib/core/account-key.d.mts +3 -0
- package/dist/lib/core/account-key.d.ts +3 -0
- package/dist/lib/core/account-key.js +41 -0
- package/dist/lib/core/account-key.mjs +17 -0
- package/dist/lib/core/account-resolution.d.mts +2 -0
- package/dist/lib/core/account-resolution.d.ts +2 -0
- package/dist/lib/core/buyer-account-registry.d.mts +93 -0
- package/dist/lib/core/buyer-account-registry.d.ts +93 -0
- package/dist/lib/core/buyer-account-registry.js +602 -0
- package/dist/lib/core/buyer-account-registry.mjs +578 -0
- package/dist/lib/core/product-cache.d.mts +18 -0
- package/dist/lib/core/product-cache.d.ts +18 -0
- package/dist/lib/core/product-cache.js +137 -0
- package/dist/lib/core/product-cache.mjs +112 -0
- package/dist/lib/errors/index.d.mts +40 -1
- package/dist/lib/errors/index.d.ts +40 -1
- package/dist/lib/errors/index.js +69 -3
- package/dist/lib/errors/index.mjs +64 -3
- package/dist/lib/governance/authorization.d.mts +17 -1
- package/dist/lib/governance/authorization.d.ts +17 -1
- package/dist/lib/governance/authorization.js +55 -7
- package/dist/lib/governance/authorization.mjs +59 -7
- package/dist/lib/governance/index.d.mts +2 -2
- package/dist/lib/governance/index.d.ts +2 -2
- package/dist/lib/governance/index.js +2 -0
- package/dist/lib/governance/index.mjs +3 -1
- package/dist/lib/index.d.mts +7 -4
- package/dist/lib/index.d.ts +7 -4
- package/dist/lib/index.js +29 -0
- package/dist/lib/index.mjs +31 -1
- package/dist/lib/net/agent-transport-fetch.d.mts +4 -0
- package/dist/lib/net/agent-transport-fetch.d.ts +4 -0
- package/dist/lib/net/agent-transport-fetch.js +18 -5
- package/dist/lib/net/agent-transport-fetch.mjs +17 -5
- package/dist/lib/protocols/a2a.js +9 -1
- package/dist/lib/protocols/a2a.mjs +9 -1
- package/dist/lib/protocols/index.js +9 -2
- package/dist/lib/protocols/index.mjs +9 -2
- package/dist/lib/protocols/mcp-modern.js +2 -1
- package/dist/lib/protocols/mcp-modern.mjs +2 -1
- package/dist/lib/protocols/mcp.js +5 -2
- package/dist/lib/protocols/mcp.mjs +5 -2
- package/dist/lib/protocols/rawResponseCapture.d.mts +6 -0
- package/dist/lib/protocols/rawResponseCapture.d.ts +6 -0
- package/dist/lib/protocols/rawResponseCapture.js +41 -29
- package/dist/lib/protocols/rawResponseCapture.mjs +40 -29
- package/dist/lib/protocols/signedRequestRejection.d.mts +9 -0
- package/dist/lib/protocols/signedRequestRejection.d.ts +9 -0
- package/dist/lib/protocols/signedRequestRejection.js +209 -0
- package/dist/lib/protocols/signedRequestRejection.mjs +189 -0
- package/dist/lib/protocols/transportDiagnostics.d.mts +1 -0
- package/dist/lib/protocols/transportDiagnostics.d.ts +1 -0
- package/dist/lib/protocols/transportDiagnostics.js +2 -0
- package/dist/lib/protocols/transportDiagnostics.mjs +1 -0
- package/dist/lib/registry/types.generated.d.mts +112 -45
- package/dist/lib/registry/types.generated.d.ts +112 -45
- package/dist/lib/schemas-data/v2.5/_provenance.json +1 -1
- package/dist/lib/server/account-provisioning.d.mts +2 -0
- package/dist/lib/server/account-provisioning.d.ts +2 -0
- package/dist/lib/server/account-provisioning.js +30 -0
- package/dist/lib/server/account-provisioning.mjs +6 -0
- package/dist/lib/server/account-reference-warnings.d.mts +12 -0
- package/dist/lib/server/account-reference-warnings.d.ts +12 -0
- package/dist/lib/server/account-reference-warnings.js +48 -0
- package/dist/lib/server/account-reference-warnings.mjs +23 -0
- package/dist/lib/server/auth-signature.js +1 -0
- package/dist/lib/server/auth-signature.mjs +1 -0
- package/dist/lib/server/create-adcp-server.d.mts +34 -0
- package/dist/lib/server/create-adcp-server.d.ts +34 -0
- package/dist/lib/server/create-adcp-server.js +225 -14
- package/dist/lib/server/create-adcp-server.mjs +225 -14
- package/dist/lib/server/decisioning/account.d.mts +2 -0
- package/dist/lib/server/decisioning/account.d.ts +2 -0
- package/dist/lib/server/decisioning/runtime/from-platform.js +49 -10
- package/dist/lib/server/decisioning/runtime/from-platform.mjs +49 -10
- 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 +3 -1
- package/dist/lib/signing/agent-resolver/consistency.d.mts +6 -13
- package/dist/lib/signing/agent-resolver/consistency.d.ts +6 -13
- package/dist/lib/signing/agent-resolver/consistency.js +0 -1
- package/dist/lib/signing/agent-resolver/consistency.mjs +0 -1
- package/dist/lib/signing/agent-resolver/errors.d.mts +3 -1
- package/dist/lib/signing/agent-resolver/errors.d.ts +3 -1
- package/dist/lib/signing/agent-resolver/errors.js +6 -0
- package/dist/lib/signing/agent-resolver/errors.mjs +6 -0
- package/dist/lib/signing/agent-resolver/fetch-helpers.d.mts +11 -0
- package/dist/lib/signing/agent-resolver/fetch-helpers.d.ts +11 -0
- package/dist/lib/signing/agent-resolver/fetch-helpers.js +37 -2
- package/dist/lib/signing/agent-resolver/fetch-helpers.mjs +40 -3
- package/dist/lib/signing/agent-resolver/jwks-set.js +24 -3
- package/dist/lib/signing/agent-resolver/jwks-set.mjs +24 -3
- package/dist/lib/signing/agent-resolver/legacy-brand.d.mts +15 -0
- package/dist/lib/signing/agent-resolver/legacy-brand.d.ts +15 -0
- package/dist/lib/signing/agent-resolver/legacy-brand.js +60 -0
- package/dist/lib/signing/agent-resolver/legacy-brand.mjs +36 -0
- package/dist/lib/signing/agent-resolver/operator-authorization.d.mts +13 -0
- package/dist/lib/signing/agent-resolver/operator-authorization.d.ts +13 -0
- package/dist/lib/signing/agent-resolver/operator-authorization.js +108 -0
- package/dist/lib/signing/agent-resolver/operator-authorization.mjs +84 -0
- package/dist/lib/signing/agent-resolver/resolve-agent.d.mts +14 -4
- package/dist/lib/signing/agent-resolver/resolve-agent.d.ts +14 -4
- package/dist/lib/signing/agent-resolver/resolve-agent.js +148 -137
- package/dist/lib/signing/agent-resolver/resolve-agent.mjs +157 -139
- package/dist/lib/signing/agent-resolver/resolved-agent-jwks.d.mts +7 -1
- package/dist/lib/signing/agent-resolver/resolved-agent-jwks.d.ts +7 -1
- package/dist/lib/signing/agent-resolver/resolved-agent-jwks.js +50 -17
- package/dist/lib/signing/agent-resolver/resolved-agent-jwks.mjs +50 -17
- package/dist/lib/signing/agent-resolver/select-agent.d.mts +14 -15
- package/dist/lib/signing/agent-resolver/select-agent.d.ts +14 -15
- package/dist/lib/signing/agent-resolver/select-agent.js +98 -13
- package/dist/lib/signing/agent-resolver/select-agent.mjs +95 -13
- package/dist/lib/signing/brand-jwks.d.mts +29 -75
- package/dist/lib/signing/brand-jwks.d.ts +29 -75
- package/dist/lib/signing/brand-jwks.js +120 -182
- package/dist/lib/signing/brand-jwks.mjs +120 -182
- package/dist/lib/signing/errors.d.mts +7 -3
- package/dist/lib/signing/errors.d.ts +7 -3
- package/dist/lib/signing/errors.js +7 -2
- package/dist/lib/signing/errors.mjs +7 -2
- package/dist/lib/signing/jwks-https.d.mts +7 -0
- package/dist/lib/signing/jwks-https.d.ts +7 -0
- package/dist/lib/signing/jwks-https.js +31 -8
- package/dist/lib/signing/jwks-https.mjs +31 -8
- package/dist/lib/signing/jwks.d.mts +8 -0
- package/dist/lib/signing/jwks.d.ts +8 -0
- package/dist/lib/signing/middleware.js +2 -1
- package/dist/lib/signing/middleware.mjs +2 -1
- package/dist/lib/signing/publisher-pins.d.mts +11 -0
- package/dist/lib/signing/publisher-pins.d.ts +11 -0
- package/dist/lib/signing/publisher-pins.js +125 -0
- package/dist/lib/signing/publisher-pins.mjs +101 -0
- package/dist/lib/signing/server.d.mts +1 -0
- package/dist/lib/signing/server.d.ts +1 -0
- package/dist/lib/signing/types.d.mts +5 -0
- package/dist/lib/signing/types.d.ts +5 -0
- package/dist/lib/signing/verifier.js +50 -5
- package/dist/lib/signing/verifier.mjs +50 -5
- package/dist/lib/signing/webhook-verifier.d.mts +7 -2
- package/dist/lib/signing/webhook-verifier.d.ts +7 -2
- package/dist/lib/signing/webhook-verifier.js +42 -2
- package/dist/lib/signing/webhook-verifier.mjs +42 -2
- package/dist/lib/testing/storyboard/account-policy.d.mts +2 -0
- package/dist/lib/testing/storyboard/account-policy.d.ts +2 -0
- package/dist/lib/testing/storyboard/account-policy.js +35 -0
- package/dist/lib/testing/storyboard/account-policy.mjs +11 -0
- package/dist/lib/testing/storyboard/context.js +6 -0
- package/dist/lib/testing/storyboard/context.mjs +6 -0
- package/dist/lib/testing/storyboard/request-builder.js +12 -2
- package/dist/lib/testing/storyboard/request-builder.mjs +12 -2
- package/dist/lib/testing/storyboard/runner.js +3 -2
- package/dist/lib/testing/storyboard/runner.mjs +3 -2
- package/dist/lib/testing/storyboard/validations.d.mts +1 -1
- package/dist/lib/testing/storyboard/validations.d.ts +1 -1
- package/dist/lib/types/accept-proposal.d.ts +19 -1
- package/dist/lib/types/buy-products.d.ts +19 -1
- package/dist/lib/types/check-governance.d.ts +19 -1
- package/dist/lib/types/comply-test-controller.d.ts +19 -1
- package/dist/lib/types/control-media-buy.d.ts +19 -1
- package/dist/lib/types/core.generated.d.mts +14 -1
- package/dist/lib/types/core.generated.d.ts +14 -1
- package/dist/lib/types/create-media-buy.d.ts +14 -1
- package/dist/lib/types/get-media-buys.d.ts +14 -1
- package/dist/lib/types/get-products.d.ts +19 -1
- package/dist/lib/types/list-products.d.ts +19 -1
- package/dist/lib/types/refine-proposals.d.ts +19 -1
- package/dist/lib/types/request-proposals.d.ts +19 -1
- package/dist/lib/types/schemas.generated.d.ts +12 -3
- package/dist/lib/types/schemas.generated.js +4 -1
- package/dist/lib/types/schemas.generated.mjs +4 -1
- package/dist/lib/types/tools.generated.d.mts +14 -1
- package/dist/lib/types/tools.generated.d.ts +14 -1
- package/dist/lib/types/update-media-buy.d.ts +14 -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/dist/lib/webhooks/index.d.mts +24 -0
- package/dist/lib/webhooks/index.d.ts +24 -0
- package/dist/lib/webhooks/index.js +50 -24
- package/dist/lib/webhooks/index.mjs +49 -24
- package/dist/lib/wholesale-feed-sync/index.d.mts +2 -0
- package/dist/lib/wholesale-feed-sync/index.d.ts +2 -0
- package/dist/lib/wholesale-feed-sync/index.js +7 -0
- package/dist/lib/wholesale-feed-sync/index.mjs +4 -0
- package/dist/lib/wholesale-feed-sync/mirror.d.mts +97 -0
- package/dist/lib/wholesale-feed-sync/mirror.d.ts +97 -0
- package/dist/lib/wholesale-feed-sync/mirror.js +350 -0
- package/dist/lib/wholesale-feed-sync/mirror.mjs +322 -0
- package/dist/lib/wholesale-feed-sync/sync.d.mts +13 -29
- package/dist/lib/wholesale-feed-sync/sync.d.ts +13 -29
- package/dist/lib/wholesale-feed-sync/sync.js +208 -281
- package/dist/lib/wholesale-feed-sync/sync.mjs +213 -281
- package/dist/lib/wholesale-feed-sync/types.d.mts +4 -3
- package/dist/lib/wholesale-feed-sync/types.d.ts +4 -3
- package/docs/README.md +6 -0
- package/docs/TYPE-SUMMARY.md +2 -2
- package/docs/guides/BUILD-AN-AGENT.md +2 -2
- package/docs/guides/BUYER-QUICKSTART-3.2.md +2 -0
- package/docs/guides/BUYER-STORAGE.md +3 -0
- package/docs/guides/FIRST-CALL-TO-A-SELLER.md +106 -0
- package/docs/guides/SIGNING-GUIDE.md +16 -7
- package/docs/guides/account-resolution.md +132 -10
- package/docs/llms.txt +3 -2
- package/docs/migration-14.0-to-14.1.md +85 -0
- package/docs/migration-14.x-rc-worksheet.md +4 -4
- package/docs/migration-4.x-to-5.x.md +1 -0
- package/docs/migration-agent-resolution-3.3.md +125 -0
- package/docs/recipes/verifying-inbound-webhooks.md +60 -15
- package/package.json +3 -2
- package/skills/adcp-brand.previous/SKILL.md +0 -200
- package/skills/adcp-creative.previous/SKILL.md +0 -305
- package/skills/adcp-governance.previous/SKILL.md +0 -566
- package/skills/adcp-measurement.previous/SKILL.md +0 -136
- package/skills/adcp-media-buy.previous/SKILL.md +0 -556
- package/skills/adcp-si.previous/SKILL.md +0 -206
- package/skills/adcp-signals.previous/SKILL.md +0 -204
|
@@ -0,0 +1,602 @@
|
|
|
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 buyer_account_registry_exports = {};
|
|
20
|
+
__export(buyer_account_registry_exports, {
|
|
21
|
+
BuyerAccountRegistry: () => BuyerAccountRegistry
|
|
22
|
+
});
|
|
23
|
+
module.exports = __toCommonJS(buyer_account_registry_exports);
|
|
24
|
+
var import_node_util = require("node:util");
|
|
25
|
+
var import_node_crypto = require("node:crypto");
|
|
26
|
+
var import_account_key = require('./account-key.js');
|
|
27
|
+
var import_account_resolution = require('./account-resolution.js');
|
|
28
|
+
var import_errors = require('../errors/index.js');
|
|
29
|
+
var import_abort = require('../protocols/abort.js');
|
|
30
|
+
var import_task_deadline = require('./task-deadline.js');
|
|
31
|
+
var import_idempotency = require('../utils/idempotency.js');
|
|
32
|
+
function setupTerms(options) {
|
|
33
|
+
const stable = (value) => Array.isArray(value) ? value.map(stable) : value && typeof value === "object" ? Object.fromEntries(
|
|
34
|
+
Object.entries(value).sort(([a], [b]) => a.localeCompare(b)).map(([key, item]) => [key, stable(item)])
|
|
35
|
+
) : value;
|
|
36
|
+
return Object.fromEntries(
|
|
37
|
+
Object.entries(options).filter(([key]) => key !== "idempotencyKey").map(([key, value]) => [
|
|
38
|
+
key,
|
|
39
|
+
(0, import_node_crypto.createHash)("sha256").update(JSON.stringify(stable(value))).digest("hex")
|
|
40
|
+
])
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
function isDefinitiveRejection(result) {
|
|
44
|
+
return Boolean(
|
|
45
|
+
result.adcpError && !result.adcpError.synthetic && !result.adcpError.code.startsWith("IDEMPOTENCY_") && ["terminal", "correctable"].includes(result.adcpError.recovery ?? "")
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
class BuyerAccountRegistry {
|
|
49
|
+
constructor(scope, provision, storage, repair, registryOptions = {}) {
|
|
50
|
+
this.scope = scope;
|
|
51
|
+
this.provision = provision;
|
|
52
|
+
this.storage = storage;
|
|
53
|
+
this.repair = repair;
|
|
54
|
+
this.registryOptions = registryOptions;
|
|
55
|
+
this.maxEntries = registryOptions.maxEntries ?? 1e4;
|
|
56
|
+
this.normalizeOptions = registryOptions.normalizeOptions;
|
|
57
|
+
if (!Number.isSafeInteger(this.maxEntries) || this.maxEntries < 2)
|
|
58
|
+
throw new Error("Account registry maxEntries must be an integer >= 2.");
|
|
59
|
+
}
|
|
60
|
+
scope;
|
|
61
|
+
provision;
|
|
62
|
+
storage;
|
|
63
|
+
repair;
|
|
64
|
+
registryOptions;
|
|
65
|
+
entries = /* @__PURE__ */ new Map();
|
|
66
|
+
pending = /* @__PURE__ */ new Map();
|
|
67
|
+
writes = Promise.resolve();
|
|
68
|
+
repairs = /* @__PURE__ */ new Map();
|
|
69
|
+
repairWaiters = /* @__PURE__ */ new Map();
|
|
70
|
+
invalidatedHandles = /* @__PURE__ */ new Set();
|
|
71
|
+
observationEpoch = 0;
|
|
72
|
+
observationFloor = 0;
|
|
73
|
+
statusEpochs = /* @__PURE__ */ new Map();
|
|
74
|
+
maxEntries;
|
|
75
|
+
normalizeOptions;
|
|
76
|
+
key(account, scope = this.scope()) {
|
|
77
|
+
return `${scope}:${(0, import_account_key.accountReferenceKey)(account)}`;
|
|
78
|
+
}
|
|
79
|
+
/** Capture before a seller request; observations older than a status repair are ignored. */
|
|
80
|
+
captureObservation() {
|
|
81
|
+
return this.observationEpoch;
|
|
82
|
+
}
|
|
83
|
+
observationIsCurrent(accountId, epoch, scope) {
|
|
84
|
+
return epoch === void 0 || epoch >= this.observationFloor && (!accountId || (this.statusEpochs.get(this.key({ account_id: accountId }, scope)) ?? 0) <= epoch);
|
|
85
|
+
}
|
|
86
|
+
remember(key, entry) {
|
|
87
|
+
if (!this.entries.has(key) && this.entries.size >= this.maxEntries) {
|
|
88
|
+
if (!this.storage)
|
|
89
|
+
throw new import_errors.AccountSetupRequiredError(
|
|
90
|
+
"Account registry capacity reached; configure durable storage or increase maxEntries."
|
|
91
|
+
);
|
|
92
|
+
this.entries.delete(this.entries.keys().next().value);
|
|
93
|
+
}
|
|
94
|
+
this.entries.set(key, structuredClone(entry));
|
|
95
|
+
}
|
|
96
|
+
async get(account) {
|
|
97
|
+
const key = this.key(account);
|
|
98
|
+
let entry = this.storage?.compareAndSet ? await this.storage.get(key) : this.entries.get(key);
|
|
99
|
+
if (!entry && !this.storage?.compareAndSet) {
|
|
100
|
+
const persisted = await this.storage?.get(key);
|
|
101
|
+
entry = this.entries.get(key) ?? persisted;
|
|
102
|
+
}
|
|
103
|
+
if (key !== this.key(account) || entry && (0, import_account_key.accountReferenceKey)(entry.account) !== (0, import_account_key.accountReferenceKey)(account))
|
|
104
|
+
return void 0;
|
|
105
|
+
if (entry && this.storage?.compareAndSet && (!Number.isSafeInteger(entry.revision) || entry.revision < 1))
|
|
106
|
+
throw new Error("CAS account storage must return a positive integer revision.");
|
|
107
|
+
if (entry) this.remember(key, entry);
|
|
108
|
+
if (entry?.account_id && this.invalidatedHandles.has(this.key({ account_id: entry.account_id })))
|
|
109
|
+
return { ...structuredClone(entry), status: "unknown" };
|
|
110
|
+
return entry ? structuredClone(entry) : void 0;
|
|
111
|
+
}
|
|
112
|
+
async ensure(account, options = {}, taskOptions, onTaskResult) {
|
|
113
|
+
(0, import_abort.throwIfAborted)(taskOptions?.signal);
|
|
114
|
+
if (taskOptions?.timeout !== void 0 && (!Number.isFinite(taskOptions.timeout) || taskOptions.timeout < 0 || taskOptions.timeout > import_abort.MAX_TIMER_DELAY_MS))
|
|
115
|
+
throw new RangeError(`timeout must be a finite non-negative number <= ${import_abort.MAX_TIMER_DELAY_MS}`);
|
|
116
|
+
account = structuredClone(account);
|
|
117
|
+
if (this.normalizeOptions) options = await this.normalizeOptions(structuredClone(options), taskOptions);
|
|
118
|
+
options = structuredClone(Object.fromEntries(Object.entries(options).filter(([, value]) => value !== void 0)));
|
|
119
|
+
if (options.idempotencyKey !== void 0 && !(0, import_idempotency.isValidIdempotencyKey)(options.idempotencyKey))
|
|
120
|
+
throw new TypeError("idempotencyKey must match [A-Za-z0-9_.:-]{16,255}.");
|
|
121
|
+
const scope = this.scope();
|
|
122
|
+
const key = this.key(account, scope);
|
|
123
|
+
const current = await this.get(account);
|
|
124
|
+
(0, import_abort.throwIfAborted)(taskOptions?.signal);
|
|
125
|
+
if (scope !== this.scope()) throw new import_errors.AccountNotFoundError("Caller credentials changed during provisioning.");
|
|
126
|
+
const shared = this.pending.get(key);
|
|
127
|
+
const wait = (promise) => (0, import_task_deadline.withTaskDeadline)(
|
|
128
|
+
taskOptions,
|
|
129
|
+
(options2) => (0, import_abort.withAbortSignal)([options2.signal], void 0, () => promise.then((entry2) => structuredClone(entry2)))
|
|
130
|
+
);
|
|
131
|
+
if (shared) {
|
|
132
|
+
if (!(0, import_node_util.isDeepStrictEqual)(shared.options, options))
|
|
133
|
+
throw new import_errors.AccountSetupRequiredError(
|
|
134
|
+
"Provisioning is already in progress with different billing terms or idempotency keys."
|
|
135
|
+
);
|
|
136
|
+
const entry2 = await wait(shared.promise);
|
|
137
|
+
if (shared.result) onTaskResult?.(shared.result);
|
|
138
|
+
return entry2;
|
|
139
|
+
}
|
|
140
|
+
const terms = setupTerms(options);
|
|
141
|
+
if (current?.status === "failed_provisioning" && current.setupTerms && !(0, import_node_util.isDeepStrictEqual)(current.setupTerms, terms))
|
|
142
|
+
throw new import_errors.AccountSetupRequiredError(
|
|
143
|
+
"The pending account was provisioned with different billing terms. Call syncAccounts explicitly to change terms."
|
|
144
|
+
);
|
|
145
|
+
if (current && current.status !== "failed_provisioning") {
|
|
146
|
+
if (Object.entries(terms ?? {}).some(
|
|
147
|
+
([name, value]) => current.setupTerms?.[name] !== void 0 && current.setupTerms[name] !== value
|
|
148
|
+
))
|
|
149
|
+
throw new import_errors.AccountSetupRequiredError(
|
|
150
|
+
"The account was provisioned with different billing terms. Call syncAccounts explicitly to change terms."
|
|
151
|
+
);
|
|
152
|
+
if (current.status !== "active" && current.account_id && this.repair) {
|
|
153
|
+
try {
|
|
154
|
+
await this.repairStatus(current.account_id, taskOptions, false);
|
|
155
|
+
} catch (error) {
|
|
156
|
+
(0, import_abort.throwIfAborted)(taskOptions?.signal);
|
|
157
|
+
if ((0, import_abort.isAbortOrTimeoutError)(error)) throw error;
|
|
158
|
+
if (scope !== this.scope())
|
|
159
|
+
throw new import_errors.AccountNotFoundError("Caller credentials changed during reconciliation.");
|
|
160
|
+
return current;
|
|
161
|
+
}
|
|
162
|
+
const refreshed = await this.get(account) ?? current;
|
|
163
|
+
if (scope !== this.scope()) throw new import_errors.AccountNotFoundError("Caller credentials changed during reconciliation.");
|
|
164
|
+
if (refreshed.status === "unknown")
|
|
165
|
+
throw new import_errors.AccountNotFoundError(
|
|
166
|
+
"The seller no longer lists this account. Call syncAccounts explicitly to reestablish it."
|
|
167
|
+
);
|
|
168
|
+
return refreshed;
|
|
169
|
+
}
|
|
170
|
+
if (current.status === "unknown")
|
|
171
|
+
throw new import_errors.AccountNotFoundError(
|
|
172
|
+
"Reconcile this account with listAccounts or explicitly reestablish it with syncAccounts."
|
|
173
|
+
);
|
|
174
|
+
if (current.status === "provisioning" && current.dispatch)
|
|
175
|
+
throw new import_errors.AccountSetupRequiredError(
|
|
176
|
+
`Provisioning is claimed or in doubt in durable storage.${current.pendingTaskId ? ` Recover task ${current.pendingTaskId}.` : ""} Reconcile the recorded idempotency key and observeSync before retrying.`
|
|
177
|
+
);
|
|
178
|
+
if (current.status === "provisioning" && current.pendingTaskId)
|
|
179
|
+
throw new import_errors.AccountSetupRequiredError(
|
|
180
|
+
`Reconcile pending sync_accounts task ${current.pendingTaskId} and observe its completed account rows before provisioning again.`
|
|
181
|
+
);
|
|
182
|
+
if (current.status !== "pending_approval" || current.account_id) return current;
|
|
183
|
+
if (!current.setupTerms || !(0, import_node_util.isDeepStrictEqual)(current.setupTerms, terms))
|
|
184
|
+
throw new import_errors.AccountSetupRequiredError(
|
|
185
|
+
"Provide the original billing options to refresh a legacy account without a handle, or call syncAccounts explicitly."
|
|
186
|
+
);
|
|
187
|
+
}
|
|
188
|
+
const requiredEntries = current ? 1 : 2;
|
|
189
|
+
if (!this.storage && this.entries.size + this.pending.size * 2 + requiredEntries > this.maxEntries) {
|
|
190
|
+
throw new import_errors.AccountSetupRequiredError(
|
|
191
|
+
"Account registry capacity reached; configure durable storage or increase maxEntries."
|
|
192
|
+
);
|
|
193
|
+
}
|
|
194
|
+
const pending = { promise: void 0, options: structuredClone(options) };
|
|
195
|
+
let callbackFailed = false;
|
|
196
|
+
let callbackError;
|
|
197
|
+
const notify = (result) => {
|
|
198
|
+
try {
|
|
199
|
+
onTaskResult?.(result);
|
|
200
|
+
} catch (error) {
|
|
201
|
+
callbackFailed = true;
|
|
202
|
+
callbackError = error;
|
|
203
|
+
}
|
|
204
|
+
};
|
|
205
|
+
const operationOptions = { ...taskOptions, signal: void 0, timeout: void 0 };
|
|
206
|
+
const observationEpoch = this.captureObservation();
|
|
207
|
+
const idempotencyKey = options.idempotencyKey ?? (0, import_node_crypto.randomUUID)();
|
|
208
|
+
const dispatch = { storageKey: key, account: structuredClone(account), idempotencyKey };
|
|
209
|
+
let claimRevision;
|
|
210
|
+
let dispatched = false;
|
|
211
|
+
const releaseClaim = async () => {
|
|
212
|
+
if (!this.storage?.compareAndSet || claimRevision === void 0) return;
|
|
213
|
+
const latest = await this.storage.get(key);
|
|
214
|
+
if (latest?.revision !== claimRevision || latest.dispatch?.idempotencyKey !== idempotencyKey) return;
|
|
215
|
+
await this.write({ ...latest, status: "failed_provisioning", dispatch: void 0 }, scope);
|
|
216
|
+
};
|
|
217
|
+
const beforeDispatch = async () => {
|
|
218
|
+
if (scope !== this.scope()) throw new import_errors.AccountNotFoundError("Caller credentials changed before dispatch.");
|
|
219
|
+
if (this.storage?.compareAndSet) {
|
|
220
|
+
await this.write(
|
|
221
|
+
{
|
|
222
|
+
account,
|
|
223
|
+
status: "provisioning",
|
|
224
|
+
setupTerms: terms,
|
|
225
|
+
revision: current?.revision,
|
|
226
|
+
dispatch: { idempotencyKey, startedAt: (/* @__PURE__ */ new Date()).toISOString() }
|
|
227
|
+
},
|
|
228
|
+
scope
|
|
229
|
+
);
|
|
230
|
+
claimRevision = (current?.revision ?? 0) + 1;
|
|
231
|
+
}
|
|
232
|
+
await this.registryOptions.onDispatchStart?.(structuredClone(dispatch));
|
|
233
|
+
if (scope !== this.scope()) throw new import_errors.AccountNotFoundError("Caller credentials changed before dispatch.");
|
|
234
|
+
dispatched = true;
|
|
235
|
+
};
|
|
236
|
+
const request = (async () => {
|
|
237
|
+
try {
|
|
238
|
+
if (!this.registryOptions.provisionOwnsDispatchBoundary) await beforeDispatch();
|
|
239
|
+
return await this.provision(structuredClone(account), pending.options, operationOptions, {
|
|
240
|
+
idempotencyKey,
|
|
241
|
+
beforeDispatch,
|
|
242
|
+
requireIdempotencyKey: Boolean(
|
|
243
|
+
options.idempotencyKey || this.storage?.compareAndSet || this.registryOptions.onDispatchStart || this.registryOptions.onResult
|
|
244
|
+
)
|
|
245
|
+
});
|
|
246
|
+
} catch (error) {
|
|
247
|
+
if (!dispatched) await releaseClaim();
|
|
248
|
+
throw error;
|
|
249
|
+
}
|
|
250
|
+
})().then(async (initial) => {
|
|
251
|
+
let result = initial;
|
|
252
|
+
pending.result = result;
|
|
253
|
+
if (dispatched) await this.registryOptions.onResult?.({ ...structuredClone(dispatch), result });
|
|
254
|
+
notify(result);
|
|
255
|
+
if (this.key(account) !== key) throw new import_errors.AccountNotFoundError("Caller credentials changed during provisioning.");
|
|
256
|
+
if (!result.success) {
|
|
257
|
+
if (!dispatched || isDefinitiveRejection(result)) await releaseClaim();
|
|
258
|
+
throw (0, import_errors.adcpErrorToTypedError)(result.adcpError ?? { code: "ACCOUNT_NOT_FOUND", message: result.error }) ?? new Error(result.error);
|
|
259
|
+
}
|
|
260
|
+
if (result.status !== "completed") {
|
|
261
|
+
const provisional = {
|
|
262
|
+
account: structuredClone(account),
|
|
263
|
+
status: "provisioning",
|
|
264
|
+
setupTerms: current?.setupTerms ?? terms,
|
|
265
|
+
...(result.metadata.serverTaskId ?? result.metadata.taskId) && {
|
|
266
|
+
pendingTaskId: result.metadata.serverTaskId ?? result.metadata.taskId
|
|
267
|
+
}
|
|
268
|
+
};
|
|
269
|
+
await this.enqueue(async () => {
|
|
270
|
+
const latest = await this.get(account);
|
|
271
|
+
if (this.storage?.compareAndSet ? latest?.revision === claimRevision && latest?.dispatch?.idempotencyKey === idempotencyKey : !latest || latest.status === "failed_provisioning" || latest.status === current?.status && latest.account_id === current?.account_id) {
|
|
272
|
+
await this.write({ ...provisional, revision: latest?.revision, dispatch: latest?.dispatch }, scope);
|
|
273
|
+
if (claimRevision !== void 0) claimRevision = (latest?.revision ?? 0) + 1;
|
|
274
|
+
}
|
|
275
|
+
});
|
|
276
|
+
if (!result.submitted) return provisional;
|
|
277
|
+
result = await result.submitted.waitForCompletion();
|
|
278
|
+
pending.result = result;
|
|
279
|
+
await this.registryOptions.onResult?.({ ...structuredClone(dispatch), result });
|
|
280
|
+
notify(result);
|
|
281
|
+
if (!result.success) {
|
|
282
|
+
if (this.storage?.compareAndSet) {
|
|
283
|
+
if (isDefinitiveRejection(result)) await releaseClaim();
|
|
284
|
+
} else {
|
|
285
|
+
await this.enqueue(async () => {
|
|
286
|
+
const latest = await this.get(account);
|
|
287
|
+
if (!latest || latest.status === "provisioning")
|
|
288
|
+
await this.write(
|
|
289
|
+
{
|
|
290
|
+
account: structuredClone(account),
|
|
291
|
+
status: "failed_provisioning",
|
|
292
|
+
setupTerms: current?.setupTerms ?? terms
|
|
293
|
+
},
|
|
294
|
+
scope
|
|
295
|
+
);
|
|
296
|
+
});
|
|
297
|
+
}
|
|
298
|
+
throw (0, import_errors.adcpErrorToTypedError)(result.adcpError ?? { code: "ACCOUNT_SETUP_REQUIRED", message: result.error }) ?? new Error(result.error);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
if (this.key(account) !== key) throw new import_errors.AccountNotFoundError("Caller credentials changed during provisioning.");
|
|
302
|
+
if (result.status !== "completed" || !result.data || !("accounts" in result.data))
|
|
303
|
+
throw new import_errors.AccountSetupRequiredError(
|
|
304
|
+
"sync_accounts has not completed; observe its completion before provisioning again."
|
|
305
|
+
);
|
|
306
|
+
await this.observeSync([account], result.data.accounts, false, observationEpoch, claimRevision);
|
|
307
|
+
const entry2 = await this.get(account);
|
|
308
|
+
if (scope !== this.scope()) throw new import_errors.AccountNotFoundError("Caller credentials changed during provisioning.");
|
|
309
|
+
if (!entry2 || entry2.status === "provisioning" || entry2.status === "failed_provisioning") {
|
|
310
|
+
if (this.storage?.compareAndSet) await releaseClaim();
|
|
311
|
+
else
|
|
312
|
+
await this.enqueue(async () => {
|
|
313
|
+
const latest = await this.get(account);
|
|
314
|
+
if (!latest || ["provisioning", "failed_provisioning"].includes(latest.status))
|
|
315
|
+
await this.write(
|
|
316
|
+
{ account, status: "failed_provisioning", setupTerms: current?.setupTerms ?? terms },
|
|
317
|
+
scope
|
|
318
|
+
);
|
|
319
|
+
});
|
|
320
|
+
const failed = result.data.accounts.find((row) => row.action === "failed");
|
|
321
|
+
const error = failed?.errors?.[0];
|
|
322
|
+
throw (error && (0, import_errors.adcpErrorToTypedError)(error)) ?? new import_errors.AccountNotFoundError("sync_accounts did not establish the requested account.");
|
|
323
|
+
}
|
|
324
|
+
if (claimRevision !== void 0 && entry2.revision !== claimRevision + 1) return entry2;
|
|
325
|
+
let settled = entry2;
|
|
326
|
+
await this.enqueue(async () => {
|
|
327
|
+
const latest = await this.get(account);
|
|
328
|
+
if (scope !== this.scope()) throw new import_errors.AccountNotFoundError("Caller credentials changed during provisioning.");
|
|
329
|
+
if (!latest) throw new import_errors.AccountNotFoundError("The provisioned account is no longer registered.");
|
|
330
|
+
settled = latest;
|
|
331
|
+
if (latest.account_id !== entry2.account_id || this.storage?.compareAndSet && latest.revision !== entry2.revision)
|
|
332
|
+
return;
|
|
333
|
+
settled = { ...latest, setupTerms: setupTerms(options) };
|
|
334
|
+
await this.write(settled, scope);
|
|
335
|
+
settled = await this.get(account) ?? settled;
|
|
336
|
+
if (settled.account_id && !("account_id" in account)) {
|
|
337
|
+
const idEntry = await this.get({ account_id: settled.account_id });
|
|
338
|
+
if (scope !== this.scope()) throw new import_errors.AccountNotFoundError("Caller credentials changed during provisioning.");
|
|
339
|
+
if (idEntry) await this.write({ ...idEntry, setupTerms: settled.setupTerms }, scope);
|
|
340
|
+
}
|
|
341
|
+
});
|
|
342
|
+
return settled;
|
|
343
|
+
});
|
|
344
|
+
pending.promise = request;
|
|
345
|
+
this.pending.set(key, pending);
|
|
346
|
+
const cleanup = () => {
|
|
347
|
+
if (this.pending.get(key) === pending) this.pending.delete(key);
|
|
348
|
+
};
|
|
349
|
+
void request.then(cleanup, cleanup);
|
|
350
|
+
const entry = await wait(request);
|
|
351
|
+
if (callbackFailed) throw callbackError;
|
|
352
|
+
return entry;
|
|
353
|
+
}
|
|
354
|
+
async write(entry, scope, isCurrent = () => true) {
|
|
355
|
+
if (!isCurrent()) return;
|
|
356
|
+
const key = this.key(entry.account, scope);
|
|
357
|
+
if (this.storage?.compareAndSet) {
|
|
358
|
+
const revision = entry.revision;
|
|
359
|
+
entry = { ...entry, revision: (revision ?? 0) + 1 };
|
|
360
|
+
if (!await this.storage.compareAndSet(key, revision, structuredClone(entry)))
|
|
361
|
+
throw new import_errors.AccountSetupRequiredError("Account storage revision changed. Reload and reconcile before retrying.");
|
|
362
|
+
} else {
|
|
363
|
+
await this.storage?.set(key, structuredClone(entry));
|
|
364
|
+
}
|
|
365
|
+
if (scope === this.scope()) this.remember(key, entry);
|
|
366
|
+
}
|
|
367
|
+
async record(ref, row, scope, preserveTerms = false, isCurrent = () => true, expectedRevision) {
|
|
368
|
+
const entry = {
|
|
369
|
+
account: structuredClone(ref),
|
|
370
|
+
status: row.status,
|
|
371
|
+
...row.account_id && { account_id: row.account_id }
|
|
372
|
+
};
|
|
373
|
+
const prior = await this.get(ref);
|
|
374
|
+
if (expectedRevision !== void 0 && prior?.revision !== expectedRevision) return;
|
|
375
|
+
if (scope !== this.scope() || !isCurrent()) return;
|
|
376
|
+
const idRef = row.account_id && !("account_id" in ref) ? { account_id: row.account_id } : void 0;
|
|
377
|
+
const priorId = idRef ? await this.get(idRef) : void 0;
|
|
378
|
+
if (scope !== this.scope() || !isCurrent()) return;
|
|
379
|
+
const aliases = new Map((priorId?.aliases ?? []).map((alias) => [(0, import_account_key.accountReferenceKey)(alias), alias]));
|
|
380
|
+
if (idRef) {
|
|
381
|
+
aliases.set((0, import_account_key.accountReferenceKey)(ref), structuredClone(ref));
|
|
382
|
+
if (aliases.size > this.maxEntries)
|
|
383
|
+
throw new import_errors.AccountSetupRequiredError("Account alias capacity reached; increase registry maxEntries.");
|
|
384
|
+
}
|
|
385
|
+
if (!("account_id" in ref) && prior?.account_id && prior.account_id !== row.account_id) {
|
|
386
|
+
const oldId = await this.get({ account_id: prior.account_id });
|
|
387
|
+
if (scope !== this.scope() || !isCurrent()) return;
|
|
388
|
+
if (oldId?.aliases) {
|
|
389
|
+
await this.write(
|
|
390
|
+
{ ...oldId, aliases: oldId.aliases.filter((alias) => (0, import_account_key.accountReferenceKey)(alias) !== (0, import_account_key.accountReferenceKey)(ref)) },
|
|
391
|
+
scope,
|
|
392
|
+
isCurrent
|
|
393
|
+
);
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
entry.revision = prior?.revision;
|
|
397
|
+
if (preserveTerms && prior?.setupTerms) entry.setupTerms = prior.setupTerms;
|
|
398
|
+
if ("account_id" in ref) {
|
|
399
|
+
if (prior?.aliases) entry.aliases = prior.aliases;
|
|
400
|
+
}
|
|
401
|
+
await this.write(entry, scope, isCurrent);
|
|
402
|
+
if (!idRef || scope !== this.scope() || !isCurrent()) return;
|
|
403
|
+
await this.write(
|
|
404
|
+
{
|
|
405
|
+
account: idRef,
|
|
406
|
+
revision: priorId?.revision,
|
|
407
|
+
account_id: row.account_id,
|
|
408
|
+
status: row.status,
|
|
409
|
+
aliases: [...aliases.values()],
|
|
410
|
+
...preserveTerms && priorId?.setupTerms && { setupTerms: priorId.setupTerms }
|
|
411
|
+
},
|
|
412
|
+
scope,
|
|
413
|
+
isCurrent
|
|
414
|
+
);
|
|
415
|
+
}
|
|
416
|
+
enqueue(run) {
|
|
417
|
+
const next = this.writes.then(run);
|
|
418
|
+
this.writes = next.catch(() => {
|
|
419
|
+
});
|
|
420
|
+
return next;
|
|
421
|
+
}
|
|
422
|
+
async observeSync(refs, rows, dryRun = false, observationEpoch, expectedRevision) {
|
|
423
|
+
if (dryRun) return;
|
|
424
|
+
const scope = this.scope();
|
|
425
|
+
return this.enqueue(async () => {
|
|
426
|
+
if (scope !== this.scope()) return;
|
|
427
|
+
for (const ref of refs) {
|
|
428
|
+
const candidates = rows.filter((value) => {
|
|
429
|
+
if (!value || typeof value !== "object") return false;
|
|
430
|
+
const row2 = value;
|
|
431
|
+
if ("account_id" in ref) return row2.account_id === ref.account_id;
|
|
432
|
+
return row2.brand?.domain === ref.brand.domain && row2.operator === ref.operator && (!ref.brand.brand_id || row2.brand.brand_id === ref.brand.brand_id) && (0, import_account_resolution.sameCountrySet)(ref.brand.countries, row2.brand.countries) && (!ref.operator_unit || row2.operator_unit?.id === ref.operator_unit.id) && (!ref.currency || row2.currency === ref.currency) && (!ref.timezone || row2.timezone === ref.timezone) && (row2.sandbox === void 0 || row2.sandbox === true === (ref.sandbox === true));
|
|
433
|
+
});
|
|
434
|
+
if (candidates.length !== 1) continue;
|
|
435
|
+
let row = candidates[0];
|
|
436
|
+
if (row.action === "failed" || typeof row.status !== "string") continue;
|
|
437
|
+
const prior = await this.get(ref);
|
|
438
|
+
if (scope !== this.scope()) return;
|
|
439
|
+
let rowEpoch = observationEpoch;
|
|
440
|
+
let repairedBinding = false;
|
|
441
|
+
if (!this.observationIsCurrent(row.account_id, rowEpoch, scope)) {
|
|
442
|
+
if (!row.account_id || prior?.account_id && prior.account_id !== row.account_id) continue;
|
|
443
|
+
const captured = this.captureObservation();
|
|
444
|
+
const authoritative = await this.get({ account_id: row.account_id });
|
|
445
|
+
if (scope !== this.scope()) return;
|
|
446
|
+
row = {
|
|
447
|
+
...row,
|
|
448
|
+
status: captured === this.captureObservation() ? authoritative?.status ?? "unknown" : "unknown"
|
|
449
|
+
};
|
|
450
|
+
rowEpoch = this.captureObservation();
|
|
451
|
+
repairedBinding = true;
|
|
452
|
+
}
|
|
453
|
+
const isCurrent = () => scope === this.scope() && this.observationIsCurrent(row.account_id, rowEpoch, scope);
|
|
454
|
+
await this.record(ref, row, scope, false, isCurrent, expectedRevision);
|
|
455
|
+
if (!isCurrent() || repairedBinding) continue;
|
|
456
|
+
for (const id of [row.account_id])
|
|
457
|
+
if (id) {
|
|
458
|
+
const key = this.key({ account_id: id }, scope);
|
|
459
|
+
this.repairs.delete(key);
|
|
460
|
+
this.invalidatedHandles.delete(key);
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
});
|
|
464
|
+
}
|
|
465
|
+
async reconcileId(accountId, status, scope, isCurrent = () => true) {
|
|
466
|
+
if (scope !== this.scope() || !isCurrent()) return;
|
|
467
|
+
const idRef = { account_id: accountId };
|
|
468
|
+
const indexed = await this.get(idRef);
|
|
469
|
+
if (scope !== this.scope() || !isCurrent()) return;
|
|
470
|
+
const aliases = new Map((indexed?.aliases ?? []).map((ref) => [(0, import_account_key.accountReferenceKey)(ref), ref]));
|
|
471
|
+
for (const [key, entry] of this.entries) {
|
|
472
|
+
if (key === this.key(entry.account, scope) && entry.account_id === accountId && !("account_id" in entry.account)) {
|
|
473
|
+
aliases.set((0, import_account_key.accountReferenceKey)(entry.account), entry.account);
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
for (const ref of aliases.values()) {
|
|
477
|
+
const prior = await this.get(ref);
|
|
478
|
+
if (scope !== this.scope() || !isCurrent()) return;
|
|
479
|
+
if (!prior || prior.account_id !== accountId) {
|
|
480
|
+
aliases.delete((0, import_account_key.accountReferenceKey)(ref));
|
|
481
|
+
continue;
|
|
482
|
+
}
|
|
483
|
+
await this.write({ ...prior, account: ref, account_id: accountId, status }, scope, isCurrent);
|
|
484
|
+
}
|
|
485
|
+
if (scope !== this.scope() || !isCurrent()) return;
|
|
486
|
+
if (indexed || aliases.size)
|
|
487
|
+
await this.write(
|
|
488
|
+
{ ...indexed, account: idRef, account_id: accountId, status, aliases: [...aliases.values()] },
|
|
489
|
+
scope,
|
|
490
|
+
isCurrent
|
|
491
|
+
);
|
|
492
|
+
}
|
|
493
|
+
async observeList(rows, queriedAccountId, observationEpoch) {
|
|
494
|
+
const scope = this.scope();
|
|
495
|
+
return this.observeListAtScope(rows, queriedAccountId, scope, void 0, observationEpoch);
|
|
496
|
+
}
|
|
497
|
+
async observeListAtScope(rows, queriedAccountId, scope, isCurrent, observationEpoch) {
|
|
498
|
+
return this.enqueue(async () => {
|
|
499
|
+
if (scope !== this.scope() || isCurrent && !isCurrent()) return;
|
|
500
|
+
if (!this.observationIsCurrent(queriedAccountId, observationEpoch, scope)) return;
|
|
501
|
+
let foundQuery = false;
|
|
502
|
+
for (const value of rows) {
|
|
503
|
+
if (!value || typeof value !== "object") continue;
|
|
504
|
+
const row = value;
|
|
505
|
+
if (typeof row.account_id !== "string" || typeof row.status !== "string") continue;
|
|
506
|
+
if (!this.observationIsCurrent(row.account_id, observationEpoch, scope)) continue;
|
|
507
|
+
const current = () => scope === this.scope() && (!isCurrent || isCurrent()) && this.observationIsCurrent(row.account_id, observationEpoch, scope);
|
|
508
|
+
if (row.account_id === queriedAccountId) foundQuery = true;
|
|
509
|
+
await this.reconcileId(row.account_id, row.status, scope, current);
|
|
510
|
+
await this.record({ account_id: row.account_id }, row, scope, true, current);
|
|
511
|
+
if (row.brand?.domain && row.operator) {
|
|
512
|
+
const ref = {
|
|
513
|
+
brand: row.brand,
|
|
514
|
+
operator: row.operator,
|
|
515
|
+
...row.operator_unit && { operator_unit: row.operator_unit },
|
|
516
|
+
...row.currency && { currency: row.currency },
|
|
517
|
+
...row.timezone && { timezone: row.timezone },
|
|
518
|
+
...row.sandbox !== void 0 && { sandbox: row.sandbox }
|
|
519
|
+
};
|
|
520
|
+
const prior = await this.get(ref);
|
|
521
|
+
if (scope !== this.scope()) return;
|
|
522
|
+
if (!prior?.account_id || prior.account_id === row.account_id)
|
|
523
|
+
await this.record(ref, row, scope, true, current);
|
|
524
|
+
}
|
|
525
|
+
if (!isCurrent && current()) {
|
|
526
|
+
const key = this.key({ account_id: row.account_id }, scope);
|
|
527
|
+
this.repairs.delete(key);
|
|
528
|
+
this.invalidatedHandles.delete(key);
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
if (queriedAccountId && !foundQuery) {
|
|
532
|
+
const current = () => scope === this.scope() && (!isCurrent || isCurrent()) && this.observationIsCurrent(queriedAccountId, observationEpoch, scope);
|
|
533
|
+
await this.reconcileId(queriedAccountId, "unknown", scope, current);
|
|
534
|
+
if (!isCurrent && current()) {
|
|
535
|
+
const key = this.key({ account_id: queriedAccountId }, scope);
|
|
536
|
+
this.repairs.delete(key);
|
|
537
|
+
this.invalidatedHandles.delete(key);
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
});
|
|
541
|
+
}
|
|
542
|
+
/** After verifying account.status_changed, repair aliases by handle from the authoritative seller. */
|
|
543
|
+
async applyStatusChange(notification, options) {
|
|
544
|
+
return this.repairStatus(notification.account_id, options, true);
|
|
545
|
+
}
|
|
546
|
+
async repairStatus(accountId, options, invalidate) {
|
|
547
|
+
(0, import_abort.throwIfAborted)(options?.signal);
|
|
548
|
+
const key = this.key({ account_id: accountId });
|
|
549
|
+
let operation = !invalidate ? this.repairWaiters.get(key) : void 0;
|
|
550
|
+
if (!operation) {
|
|
551
|
+
operation = this.runRepair(accountId, { ...options, signal: void 0, timeout: void 0 }, invalidate);
|
|
552
|
+
this.repairWaiters.set(key, operation);
|
|
553
|
+
const cleanup = () => {
|
|
554
|
+
if (this.repairWaiters.get(key) === operation) this.repairWaiters.delete(key);
|
|
555
|
+
};
|
|
556
|
+
void operation.then(cleanup, cleanup);
|
|
557
|
+
}
|
|
558
|
+
const wait = (promise) => (0, import_task_deadline.withTaskDeadline)(options, (taskOptions) => (0, import_abort.withAbortSignal)([taskOptions.signal], void 0, () => promise));
|
|
559
|
+
await wait(operation);
|
|
560
|
+
let newer = this.repairWaiters.get(key);
|
|
561
|
+
while (newer && newer !== operation) {
|
|
562
|
+
operation = newer;
|
|
563
|
+
await wait(operation);
|
|
564
|
+
newer = this.repairWaiters.get(key);
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
async runRepair(accountId, options, invalidate) {
|
|
568
|
+
if (!this.repair) throw new Error("Account registry requires a list_accounts repair callback.");
|
|
569
|
+
const scope = this.scope();
|
|
570
|
+
const key = this.key({ account_id: accountId }, scope);
|
|
571
|
+
const token = {};
|
|
572
|
+
this.statusEpochs.delete(key);
|
|
573
|
+
this.statusEpochs.set(key, ++this.observationEpoch);
|
|
574
|
+
if (this.statusEpochs.size > this.maxEntries) {
|
|
575
|
+
const oldestKey = this.statusEpochs.keys().next().value;
|
|
576
|
+
this.observationFloor = this.statusEpochs.get(oldestKey);
|
|
577
|
+
this.statusEpochs.delete(oldestKey);
|
|
578
|
+
}
|
|
579
|
+
this.repairs.set(key, token);
|
|
580
|
+
if (invalidate) this.invalidatedHandles.add(key);
|
|
581
|
+
let invalidationPersisted = !invalidate;
|
|
582
|
+
try {
|
|
583
|
+
if (invalidate) {
|
|
584
|
+
await this.enqueue(() => this.reconcileId(accountId, "unknown", scope, () => this.repairs.get(key) === token));
|
|
585
|
+
invalidationPersisted = true;
|
|
586
|
+
}
|
|
587
|
+
const rows = (await this.repair(accountId, options)).filter(
|
|
588
|
+
(row) => row !== null && typeof row === "object" && row.account_id === accountId
|
|
589
|
+
);
|
|
590
|
+
await this.observeListAtScope(rows, accountId, scope, () => this.repairs.get(key) === token);
|
|
591
|
+
} finally {
|
|
592
|
+
if (this.repairs.get(key) === token) {
|
|
593
|
+
this.repairs.delete(key);
|
|
594
|
+
if (invalidationPersisted) this.invalidatedHandles.delete(key);
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
600
|
+
0 && (module.exports = {
|
|
601
|
+
BuyerAccountRegistry
|
|
602
|
+
});
|