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