@adcp/sdk 15.9.1 → 16.0.0-beta.2

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 (321) hide show
  1. package/bin/adcp-identity.js +74 -0
  2. package/bin/adcp.js +2 -0
  3. package/dist/lib/adapters/keyed-account-backend.d.mts +37 -0
  4. package/dist/lib/adapters/keyed-account-backend.d.ts +37 -0
  5. package/dist/lib/adapters/keyed-account-backend.js +121 -0
  6. package/dist/lib/adapters/keyed-account-backend.mjs +94 -0
  7. package/dist/lib/adapters/keyed-account-store.d.mts +28 -0
  8. package/dist/lib/adapters/keyed-account-store.d.ts +28 -0
  9. package/dist/lib/adapters/keyed-account-store.js +227 -0
  10. package/dist/lib/adapters/keyed-account-store.mjs +203 -0
  11. package/dist/lib/core/SingleAgentClient.d.mts +25 -2
  12. package/dist/lib/core/SingleAgentClient.d.ts +25 -2
  13. package/dist/lib/core/SingleAgentClient.js +280 -62
  14. package/dist/lib/core/SingleAgentClient.mjs +282 -57
  15. package/dist/lib/core/TaskExecutor.d.mts +6 -0
  16. package/dist/lib/core/TaskExecutor.d.ts +6 -0
  17. package/dist/lib/core/TaskExecutor.js +66 -5
  18. package/dist/lib/core/TaskExecutor.mjs +71 -5
  19. package/dist/lib/core/postgres-webhook-registration-store.js +32 -5
  20. package/dist/lib/core/postgres-webhook-registration-store.mjs +32 -5
  21. package/dist/lib/core/redis-webhook-registration-store.js +23 -1
  22. package/dist/lib/core/redis-webhook-registration-store.mjs +23 -1
  23. package/dist/lib/core/webhook-registration.d.mts +7 -1
  24. package/dist/lib/core/webhook-registration.d.ts +7 -1
  25. package/dist/lib/core/webhook-registration.js +24 -2
  26. package/dist/lib/core/webhook-registration.mjs +23 -2
  27. package/dist/lib/identity-3.3/generated-compat.d.mts +34 -0
  28. package/dist/lib/identity-3.3/generated-compat.d.ts +34 -0
  29. package/dist/lib/identity-3.3/generated-compat.js +16 -0
  30. package/dist/lib/identity-3.3/generated-compat.mjs +0 -0
  31. package/dist/lib/identity-3.3/index.d.mts +10 -0
  32. package/dist/lib/identity-3.3/index.d.ts +10 -0
  33. package/dist/lib/identity-3.3/index.js +79 -0
  34. package/dist/lib/identity-3.3/index.mjs +50 -0
  35. package/dist/lib/identity-3.3/lifecycle.d.mts +125 -0
  36. package/dist/lib/identity-3.3/lifecycle.d.ts +125 -0
  37. package/dist/lib/identity-3.3/lifecycle.js +294 -0
  38. package/dist/lib/identity-3.3/lifecycle.mjs +272 -0
  39. package/dist/lib/identity-3.3/revocation.d.mts +86 -0
  40. package/dist/lib/identity-3.3/revocation.d.ts +86 -0
  41. package/dist/lib/identity-3.3/revocation.js +308 -0
  42. package/dist/lib/identity-3.3/revocation.mjs +286 -0
  43. package/dist/lib/identity-3.3/schemas.draft.d.mts +2472 -0
  44. package/dist/lib/identity-3.3/schemas.draft.d.ts +2472 -0
  45. package/dist/lib/identity-3.3/schemas.draft.js +1448 -0
  46. package/dist/lib/identity-3.3/schemas.draft.mjs +1428 -0
  47. package/dist/lib/identity-3.3/server.d.mts +46 -0
  48. package/dist/lib/identity-3.3/server.d.ts +46 -0
  49. package/dist/lib/identity-3.3/server.js +279 -0
  50. package/dist/lib/identity-3.3/server.mjs +260 -0
  51. package/dist/lib/identity-3.3/store.d.mts +24 -0
  52. package/dist/lib/identity-3.3/store.d.ts +24 -0
  53. package/dist/lib/identity-3.3/store.js +191 -0
  54. package/dist/lib/identity-3.3/store.mjs +169 -0
  55. package/dist/lib/identity-3.3/trust.d.mts +155 -0
  56. package/dist/lib/identity-3.3/trust.d.ts +155 -0
  57. package/dist/lib/identity-3.3/trust.js +383 -0
  58. package/dist/lib/identity-3.3/trust.mjs +342 -0
  59. package/dist/lib/identity-3.3/types.d.mts +155 -0
  60. package/dist/lib/identity-3.3/types.d.ts +155 -0
  61. package/dist/lib/identity-3.3/types.js +16 -0
  62. package/dist/lib/identity-3.3/types.mjs +0 -0
  63. package/dist/lib/identity-3.3/validation.d.mts +2 -0
  64. package/dist/lib/identity-3.3/validation.d.ts +2 -0
  65. package/dist/lib/identity-3.3/validation.js +125 -0
  66. package/dist/lib/identity-3.3/validation.mjs +91 -0
  67. package/dist/lib/index.d.mts +1 -1
  68. package/dist/lib/index.d.ts +1 -1
  69. package/dist/lib/index.js +2 -0
  70. package/dist/lib/index.mjs +2 -0
  71. package/dist/lib/protocols/index.d.mts +1 -1
  72. package/dist/lib/protocols/index.d.ts +1 -1
  73. package/dist/lib/protocols/index.js +2 -2
  74. package/dist/lib/protocols/index.mjs +2 -2
  75. package/dist/lib/reporting/azure/index.d.mts +5 -0
  76. package/dist/lib/reporting/azure/index.d.ts +5 -0
  77. package/dist/lib/reporting/azure/index.js +26 -7
  78. package/dist/lib/reporting/azure/index.mjs +26 -7
  79. package/dist/lib/reporting/bigquery/index.d.mts +39 -5
  80. package/dist/lib/reporting/bigquery/index.d.ts +39 -5
  81. package/dist/lib/reporting/bigquery/index.js +26 -20
  82. package/dist/lib/reporting/bigquery/index.mjs +26 -20
  83. package/dist/lib/reporting/gcs/adapter.d.mts +8 -1
  84. package/dist/lib/reporting/gcs/adapter.d.ts +8 -1
  85. package/dist/lib/reporting/gcs/errors.d.mts +6 -0
  86. package/dist/lib/reporting/gcs/errors.d.ts +6 -0
  87. package/dist/lib/reporting/gcs/fence.d.mts +5 -0
  88. package/dist/lib/reporting/gcs/fence.d.ts +5 -0
  89. package/dist/lib/reporting/gcs/index.d.mts +9 -0
  90. package/dist/lib/reporting/gcs/index.d.ts +9 -0
  91. package/dist/lib/reporting/gcs/reader.d.mts +27 -3
  92. package/dist/lib/reporting/gcs/reader.d.ts +27 -3
  93. package/dist/lib/reporting/gcs/row-provider.d.mts +7 -0
  94. package/dist/lib/reporting/gcs/row-provider.d.ts +7 -0
  95. package/dist/lib/reporting/gcs/row-provider.js +34 -14
  96. package/dist/lib/reporting/gcs/row-provider.mjs +34 -14
  97. package/dist/lib/reporting/ledger/change-feed.d.mts +22 -1
  98. package/dist/lib/reporting/ledger/change-feed.d.ts +22 -1
  99. package/dist/lib/reporting/ledger/index.d.mts +11 -0
  100. package/dist/lib/reporting/ledger/index.d.ts +11 -0
  101. package/dist/lib/reporting/ledger/managed-postgres.d.mts +8 -0
  102. package/dist/lib/reporting/ledger/managed-postgres.d.ts +8 -0
  103. package/dist/lib/reporting/ledger/managed-router.d.mts +27 -2
  104. package/dist/lib/reporting/ledger/managed-router.d.ts +27 -2
  105. package/dist/lib/reporting/ledger/managed.d.mts +86 -6
  106. package/dist/lib/reporting/ledger/managed.d.ts +86 -6
  107. package/dist/lib/reporting/ledger/notification-activity.d.mts +65 -3
  108. package/dist/lib/reporting/ledger/notification-activity.d.ts +65 -3
  109. package/dist/lib/reporting/ledger/object-writes.d.mts +64 -5
  110. package/dist/lib/reporting/ledger/object-writes.d.ts +64 -5
  111. package/dist/lib/reporting/ledger/postgres.d.mts +43 -9
  112. package/dist/lib/reporting/ledger/postgres.d.ts +43 -9
  113. package/dist/lib/reporting/ledger/postgres.js +43 -27
  114. package/dist/lib/reporting/ledger/postgres.mjs +43 -27
  115. package/dist/lib/reporting/ledger/row-encoding.d.mts +92 -6
  116. package/dist/lib/reporting/ledger/row-encoding.d.ts +92 -6
  117. package/dist/lib/reporting/ledger/row-encoding.js +8 -4
  118. package/dist/lib/reporting/ledger/row-encoding.mjs +6 -3
  119. package/dist/lib/reporting/ledger/row-storage-errors.d.mts +11 -1
  120. package/dist/lib/reporting/ledger/row-storage-errors.d.ts +11 -1
  121. package/dist/lib/reporting/ledger/row-storage-migration.d.mts +19 -1
  122. package/dist/lib/reporting/ledger/row-storage-migration.d.ts +19 -1
  123. package/dist/lib/reporting/ledger/row-storage-migration.js +133 -18
  124. package/dist/lib/reporting/ledger/row-storage-migration.mjs +133 -18
  125. package/dist/lib/reporting/ledger/row-storage-object.d.mts +103 -12
  126. package/dist/lib/reporting/ledger/row-storage-object.d.ts +103 -12
  127. package/dist/lib/reporting/ledger/row-storage-object.js +106 -31
  128. package/dist/lib/reporting/ledger/row-storage-object.mjs +104 -31
  129. package/dist/lib/reporting/ledger/row-storage.d.mts +87 -20
  130. package/dist/lib/reporting/ledger/row-storage.d.ts +87 -20
  131. package/dist/lib/reporting/ledger/row-storage.js +126 -66
  132. package/dist/lib/reporting/ledger/row-storage.mjs +129 -67
  133. package/dist/lib/reporting/ledger/schedule.d.mts +13 -0
  134. package/dist/lib/reporting/ledger/schedule.d.ts +13 -0
  135. package/dist/lib/reporting/ledger/schedule.js +24 -0
  136. package/dist/lib/reporting/ledger/schedule.mjs +23 -0
  137. package/dist/lib/reporting/ledger/types.d.mts +23 -1
  138. package/dist/lib/reporting/ledger/types.d.ts +23 -1
  139. package/dist/lib/reporting/s3/index.d.mts +5 -0
  140. package/dist/lib/reporting/s3/index.d.ts +5 -0
  141. package/dist/lib/reporting/s3/index.js +37 -14
  142. package/dist/lib/reporting/s3/index.mjs +37 -14
  143. package/dist/lib/reporting/service/index.d.mts +10 -0
  144. package/dist/lib/reporting/service/index.d.ts +10 -0
  145. package/dist/lib/reporting/service/maintenance.d.mts +28 -2
  146. package/dist/lib/reporting/service/maintenance.d.ts +28 -2
  147. package/dist/lib/schemas-data/v2.5/_provenance.json +1 -1
  148. package/dist/lib/server/account-sync-contract.d.mts +14 -0
  149. package/dist/lib/server/account-sync-contract.d.ts +14 -0
  150. package/dist/lib/server/account-sync-contract.js +225 -0
  151. package/dist/lib/server/account-sync-contract.mjs +197 -0
  152. package/dist/lib/server/adcp-server.js +5 -0
  153. package/dist/lib/server/adcp-server.mjs +10 -0
  154. package/dist/lib/server/auth-signature.d.mts +15 -0
  155. package/dist/lib/server/auth-signature.d.ts +15 -0
  156. package/dist/lib/server/auth-signature.js +72 -2
  157. package/dist/lib/server/auth-signature.mjs +81 -2
  158. package/dist/lib/server/auth.d.mts +6 -0
  159. package/dist/lib/server/auth.d.ts +6 -0
  160. package/dist/lib/server/auth.js +6 -0
  161. package/dist/lib/server/auth.mjs +6 -0
  162. package/dist/lib/server/create-adcp-server.d.mts +19 -5
  163. package/dist/lib/server/create-adcp-server.d.ts +19 -5
  164. package/dist/lib/server/create-adcp-server.js +259 -19
  165. package/dist/lib/server/create-adcp-server.mjs +266 -10
  166. package/dist/lib/server/decisioning/account.d.mts +24 -1
  167. package/dist/lib/server/decisioning/account.d.ts +24 -1
  168. package/dist/lib/server/decisioning/index.d.mts +1 -1
  169. package/dist/lib/server/decisioning/index.d.ts +1 -1
  170. package/dist/lib/server/decisioning/runtime/from-platform.d.mts +0 -50
  171. package/dist/lib/server/decisioning/runtime/from-platform.d.ts +0 -50
  172. package/dist/lib/server/decisioning/runtime/from-platform.js +61 -11
  173. package/dist/lib/server/decisioning/runtime/from-platform.mjs +65 -12
  174. package/dist/lib/server/index.d.mts +2 -0
  175. package/dist/lib/server/index.d.ts +2 -0
  176. package/dist/lib/server/index.js +12 -0
  177. package/dist/lib/server/index.mjs +12 -0
  178. package/dist/lib/server/notification-subscriptions/runtime.js +9 -1
  179. package/dist/lib/server/notification-subscriptions/runtime.mjs +9 -1
  180. package/dist/lib/server/notification-subscriptions/types.d.mts +5 -0
  181. package/dist/lib/server/notification-subscriptions/types.d.ts +5 -0
  182. package/dist/lib/server/principal/types.d.mts +1 -1
  183. package/dist/lib/server/principal/types.d.ts +1 -1
  184. package/dist/lib/server/serve.js +5 -0
  185. package/dist/lib/server/serve.mjs +5 -0
  186. package/dist/lib/server/webhook-delivery/recovery.d.mts +1 -0
  187. package/dist/lib/server/webhook-delivery/recovery.d.ts +1 -0
  188. package/dist/lib/server/webhook-delivery/recovery.js +4 -0
  189. package/dist/lib/server/webhook-delivery/recovery.mjs +4 -0
  190. package/dist/lib/server/webhook-emitter.d.mts +10 -0
  191. package/dist/lib/server/webhook-emitter.d.ts +10 -0
  192. package/dist/lib/server/webhook-emitter.js +22 -0
  193. package/dist/lib/server/webhook-emitter.mjs +22 -0
  194. package/dist/lib/signing/agent-context.d.mts +0 -5
  195. package/dist/lib/signing/agent-context.d.ts +0 -5
  196. package/dist/lib/signing/agent-context.js +9 -1
  197. package/dist/lib/signing/agent-context.mjs +9 -1
  198. package/dist/lib/signing/agent-fetch.js +27 -2
  199. package/dist/lib/signing/agent-fetch.mjs +32 -2
  200. package/dist/lib/signing/agent-resolver/canonicalize.d.mts +10 -15
  201. package/dist/lib/signing/agent-resolver/canonicalize.d.ts +10 -15
  202. package/dist/lib/signing/agent-resolver/canonicalize.js +17 -5
  203. package/dist/lib/signing/agent-resolver/canonicalize.mjs +16 -5
  204. package/dist/lib/signing/agent-resolver/index.d.mts +1 -0
  205. package/dist/lib/signing/agent-resolver/index.d.ts +1 -0
  206. package/dist/lib/signing/agent-resolver/index.js +5 -0
  207. package/dist/lib/signing/agent-resolver/index.mjs +3 -0
  208. package/dist/lib/signing/agent-resolver/resolved-agent-jwks.d.mts +3 -2
  209. package/dist/lib/signing/agent-resolver/resolved-agent-jwks.d.ts +3 -2
  210. package/dist/lib/signing/agent-resolver/resolved-agent-jwks.js +30 -3
  211. package/dist/lib/signing/agent-resolver/resolved-agent-jwks.mjs +30 -3
  212. package/dist/lib/signing/agent-resolver/strict-json.d.mts +2 -2
  213. package/dist/lib/signing/agent-resolver/strict-json.d.ts +2 -2
  214. package/dist/lib/signing/agent-resolver/strict-json.js +5 -3
  215. package/dist/lib/signing/agent-resolver/strict-json.mjs +5 -3
  216. package/dist/lib/signing/brand-jwks.d.mts +2 -2
  217. package/dist/lib/signing/brand-jwks.d.ts +2 -2
  218. package/dist/lib/signing/brand-jwks.js +4 -4
  219. package/dist/lib/signing/brand-jwks.mjs +4 -4
  220. package/dist/lib/signing/client.d.mts +1 -0
  221. package/dist/lib/signing/client.d.ts +1 -0
  222. package/dist/lib/signing/client.js +5 -0
  223. package/dist/lib/signing/client.mjs +3 -0
  224. package/dist/lib/signing/crypto.js +8 -0
  225. package/dist/lib/signing/crypto.mjs +9 -1
  226. package/dist/lib/signing/fetch-async.js +1 -1
  227. package/dist/lib/signing/fetch-async.mjs +1 -1
  228. package/dist/lib/signing/fetch.js +1 -1
  229. package/dist/lib/signing/fetch.mjs +1 -1
  230. package/dist/lib/signing/jwks-https.d.mts +1 -1
  231. package/dist/lib/signing/jwks-https.d.ts +1 -1
  232. package/dist/lib/signing/jwks-https.js +1 -1
  233. package/dist/lib/signing/jwks-https.mjs +1 -1
  234. package/dist/lib/signing/jwks.d.mts +11 -3
  235. package/dist/lib/signing/jwks.d.ts +11 -3
  236. package/dist/lib/signing/jwks.js +1 -1
  237. package/dist/lib/signing/jwks.mjs +1 -1
  238. package/dist/lib/signing/middleware.js +9 -0
  239. package/dist/lib/signing/middleware.mjs +14 -0
  240. package/dist/lib/signing/parser.d.mts +9 -6
  241. package/dist/lib/signing/parser.d.ts +9 -6
  242. package/dist/lib/signing/parser.js +82 -64
  243. package/dist/lib/signing/parser.mjs +83 -65
  244. package/dist/lib/signing/revocation.d.mts +4 -1
  245. package/dist/lib/signing/revocation.d.ts +4 -1
  246. package/dist/lib/signing/revocation.js +36 -3
  247. package/dist/lib/signing/revocation.mjs +34 -2
  248. package/dist/lib/signing/server.d.mts +1 -1
  249. package/dist/lib/signing/server.d.ts +1 -1
  250. package/dist/lib/signing/server.js +4 -0
  251. package/dist/lib/signing/server.mjs +4 -0
  252. package/dist/lib/signing/types.d.mts +3 -0
  253. package/dist/lib/signing/types.d.ts +3 -0
  254. package/dist/lib/signing/verifier.js +1 -1
  255. package/dist/lib/signing/verifier.mjs +1 -1
  256. package/dist/lib/signing/web-bot-auth.d.mts +191 -0
  257. package/dist/lib/signing/web-bot-auth.d.ts +191 -0
  258. package/dist/lib/signing/web-bot-auth.js +837 -0
  259. package/dist/lib/signing/web-bot-auth.mjs +796 -0
  260. package/dist/lib/signing/webhook-verifier.js +1 -1
  261. package/dist/lib/signing/webhook-verifier.mjs +1 -1
  262. package/dist/lib/testing/client.d.mts +1 -4
  263. package/dist/lib/testing/client.d.ts +1 -4
  264. package/dist/lib/testing/client.js +141 -20
  265. package/dist/lib/testing/client.mjs +137 -19
  266. package/dist/lib/testing/compliance/comply.js +29 -10
  267. package/dist/lib/testing/compliance/comply.mjs +37 -11
  268. package/dist/lib/testing/storyboard/account-sync-contract.d.mts +3 -0
  269. package/dist/lib/testing/storyboard/account-sync-contract.d.ts +3 -0
  270. package/dist/lib/testing/storyboard/account-sync-contract.js +79 -0
  271. package/dist/lib/testing/storyboard/account-sync-contract.mjs +55 -0
  272. package/dist/lib/testing/storyboard/default-invariants.js +2 -0
  273. package/dist/lib/testing/storyboard/default-invariants.mjs +2 -0
  274. package/dist/lib/testing/storyboard/runner.js +23 -8
  275. package/dist/lib/testing/storyboard/runner.mjs +24 -8
  276. package/dist/lib/testing/storyboard/validations.d.mts +1 -1
  277. package/dist/lib/testing/storyboard/validations.d.ts +1 -1
  278. package/dist/lib/types/adcp.d.mts +4 -0
  279. package/dist/lib/types/adcp.d.ts +4 -0
  280. package/dist/lib/types/schemas.generated.d.ts +6 -6
  281. package/dist/lib/utils/capabilities.d.mts +1 -0
  282. package/dist/lib/utils/capabilities.d.ts +1 -0
  283. package/dist/lib/utils/capabilities.js +5 -0
  284. package/dist/lib/utils/capabilities.mjs +5 -0
  285. package/dist/lib/v2/projection/catalog-snapshot.js +3 -1
  286. package/dist/lib/v2/projection/catalog-snapshot.mjs +3 -1
  287. package/dist/lib/v2/projection/creative-delivery.d.mts +34 -10
  288. package/dist/lib/v2/projection/creative-delivery.d.ts +34 -10
  289. package/dist/lib/v2/projection/creative-delivery.js +26 -10
  290. package/dist/lib/v2/projection/creative-delivery.mjs +26 -10
  291. package/dist/lib/v2/projection/index.d.mts +1 -1
  292. package/dist/lib/v2/projection/index.d.ts +1 -1
  293. package/dist/lib/v2/projection/index.js +2 -0
  294. package/dist/lib/v2/projection/index.mjs +2 -0
  295. package/dist/lib/v2/projection/legacy-routes.d.mts +34 -2
  296. package/dist/lib/v2/projection/legacy-routes.d.ts +34 -2
  297. package/dist/lib/v2/projection/legacy-routes.js +35 -2
  298. package/dist/lib/v2/projection/legacy-routes.mjs +32 -2
  299. package/dist/lib/validation/schema-loader.d.mts +2 -0
  300. package/dist/lib/validation/schema-loader.d.ts +2 -0
  301. package/dist/lib/validation/schema-loader.js +43 -4
  302. package/dist/lib/validation/schema-loader.mjs +39 -3
  303. package/dist/lib/version.d.mts +3 -3
  304. package/dist/lib/version.d.ts +3 -3
  305. package/dist/lib/version.js +3 -3
  306. package/dist/lib/version.mjs +3 -3
  307. package/docs/TYPE-SUMMARY.md +2 -2
  308. package/docs/guides/DISCOVERY-CACHE-AND-SESSION-REUSE.md +4 -0
  309. package/docs/guides/REPORTING-LEDGER-CUTOVER.md +1 -1
  310. package/docs/guides/REPORTING-LEDGER.md +99 -29
  311. package/docs/guides/REPORTING-OPERATIONS.md +29 -13
  312. package/docs/guides/REPORTING-STABILITY.md +76 -0
  313. package/docs/guides/account-resolution.md +101 -0
  314. package/docs/llms.txt +3 -2
  315. package/docs/migration-12-to-13.md +15 -0
  316. package/docs/migration-14.x-rc-worksheet.md +4 -4
  317. package/docs/migration-agent-identity-3.3.md +494 -0
  318. package/docs/migration-agent-resolution-3.3.md +5 -0
  319. package/docs/migration-reporting-row-storage-alignment.md +137 -0
  320. package/examples/storyboards/account-sync-contract.yaml +57 -0
  321. package/package.json +17 -1
@@ -0,0 +1,74 @@
1
+ 'use strict';
2
+ const { readFileSync, writeFileSync } = require('node:fs');
3
+ const { parseStrictJson } = require('../dist/lib/signing/agent-resolver/strict-json.js');
4
+ const {
5
+ createSignedSignatureDirectory,
6
+ createSignedOriginRevocationList,
7
+ validateTrustRecord,
8
+ } = require('../dist/lib/identity-3.3/index.js');
9
+
10
+ async function handleIdentityCommand(args) {
11
+ const command = args[0];
12
+ const option = name => {
13
+ const index = args.indexOf(name);
14
+ return index >= 0 ? args[index + 1] : undefined;
15
+ };
16
+ if (command === 'trust-validate') {
17
+ if (!args[1]) throw new Error('Usage: adcp identity trust-validate trust.json');
18
+ const bytes = readFileSync(args[1]);
19
+ if (bytes.length > 2 * 1024 * 1024) throw new Error('trust.json exceeds 2 MiB');
20
+ validateTrustRecord(parseStrictJson(new TextDecoder('utf-8', { fatal: true }).decode(bytes), 32));
21
+ process.stdout.write('Valid experimental trust v1 record\n');
22
+ return;
23
+ }
24
+ if (command === 'revocations') {
25
+ const origin = option('--origin'),
26
+ keyFile = option('--keys'),
27
+ output = option('--output'),
28
+ snapshotFile = option('--snapshot');
29
+ if (!origin || !keyFile || !output)
30
+ throw new Error(
31
+ 'Usage: adcp identity revocations --origin https://agent.example.com --keys private-jwks.json --output revocations.jws [--snapshot revocations.json]'
32
+ );
33
+ const input = parseStrictJson(readFileSync(keyFile, 'utf8'), 32);
34
+ if (!input?.keys?.length) throw new Error('Key file must contain a private JWK Set');
35
+ const now = Date.now();
36
+ const snapshot = snapshotFile
37
+ ? parseStrictJson(readFileSync(snapshotFile, 'utf8'), 32)
38
+ : {
39
+ issuer: origin,
40
+ updated: new Date(now).toISOString(),
41
+ next_update: new Date(now + 300000).toISOString(),
42
+ revoked_kids: [],
43
+ };
44
+ if (snapshot.issuer !== origin) throw new Error('Snapshot issuer must match --origin');
45
+ const body = await createSignedOriginRevocationList(snapshot, input.keys[0]);
46
+ writeFileSync(output, body + '\n', { mode: 0o600 });
47
+ process.stdout.write(
48
+ 'Signed list written; serve at /.well-known/governance-revocations.json and refresh before next_update.\n'
49
+ );
50
+ return;
51
+ }
52
+ if (command === 'directory') {
53
+ const origin = option('--origin'),
54
+ keyFile = option('--keys'),
55
+ output = option('--output');
56
+ if (!origin || !keyFile || !output)
57
+ throw new Error(
58
+ 'Usage: adcp identity directory --origin https://agent.example.com --keys private-jwks.json --output directory-response.json'
59
+ );
60
+ const input = parseStrictJson(readFileSync(keyFile, 'utf8'), 32);
61
+ if (!input || !Array.isArray(input.keys)) throw new Error('Key file must contain a private JWK Set');
62
+ const response = await createSignedSignatureDirectory(
63
+ origin,
64
+ input.keys.map(privateKey => ({ privateKey }))
65
+ );
66
+ writeFileSync(output, JSON.stringify(response, null, 2) + '\n', { mode: 0o600 });
67
+ process.stdout.write(
68
+ 'Signed directory response written; serve its exact body and headers at the well-known path.\n'
69
+ );
70
+ return;
71
+ }
72
+ throw new Error('Identity commands: trust-validate, revocations, directory (generate or re-sign after rotation)');
73
+ }
74
+ module.exports = { handleIdentityCommand };
package/bin/adcp.js CHANGED
@@ -1975,6 +1975,7 @@ COMMANDS:
1975
1975
  the per-step trace. Triages request_signature_brand_*
1976
1976
  rejections.
1977
1977
  signing <subcommand> RFC 9421 signing key tools (generate, verify)
1978
+ identity <subcommand> Experimental 3.3 trust validation and signed directory authoring
1978
1979
  check-network Validate managed publisher network deployment
1979
1980
  diagnose-auth <alias|url> Diagnose OAuth handshake with ranked hypotheses
1980
1981
  (alias: "adcp diagnose oauth <alias|url>")
@@ -5835,6 +5836,7 @@ function formatVerdict(verdict) {
5835
5836
 
5836
5837
  async function main() {
5837
5838
  const args = process.argv.slice(2);
5839
+ if (args[0] === 'identity') return require('./adcp-identity.js').handleIdentityCommand(args.slice(1));
5838
5840
 
5839
5841
  // `--version` / `-v` is handled before subcommands and before the
5840
5842
  // staleness probe — it should be a fast, side-effect-free identity
@@ -0,0 +1,37 @@
1
+ import type { Account } from '../server/decisioning/account.mjs';
2
+ import type { AccountReference } from '../types/index.mjs';
3
+ import type { PgQueryable } from '../server/postgres-task-store.mjs';
4
+ export interface KeyedAccountRecord<TCtxMeta = Record<string, unknown>> {
5
+ key: string;
6
+ ref: AccountReference;
7
+ account: Account<TCtxMeta>;
8
+ }
9
+ export interface KeyedAccountSnapshot<TCtxMeta = Record<string, unknown>> {
10
+ version: string;
11
+ records: KeyedAccountRecord<TCtxMeta>[];
12
+ }
13
+ /** Persistent, non-expiring storage. KV adapters must implement atomic CAS. */
14
+ export interface KeyedAccountBackend<TCtxMeta = Record<string, unknown>> {
15
+ load(scope: string): Promise<KeyedAccountSnapshot<TCtxMeta> | null>;
16
+ /** Null expects absence; otherwise replace only the exact loaded version. */
17
+ compareAndSwap(scope: string, version: string | null, records: KeyedAccountRecord<TCtxMeta>[]): Promise<boolean>;
18
+ }
19
+ /** Single-process backend; share the instance between server factories. No TTL. */
20
+ export declare function memoryKeyedAccountBackend<TCtxMeta = Record<string, unknown>>(): KeyedAccountBackend<TCtxMeta>;
21
+ export interface PgKeyedAccountBackendOptions {
22
+ tableName?: string;
23
+ }
24
+ export declare function getKeyedAccountMigration(options?: PgKeyedAccountBackendOptions): string;
25
+ /** Atomic SQL CAS works across processes, without an account expiry window. */
26
+ export declare function pgKeyedAccountBackend<TCtxMeta = Record<string, unknown>>(db: PgQueryable, options?: PgKeyedAccountBackendOptions): KeyedAccountBackend<TCtxMeta>;
27
+ /** Minimal node-redis compatible API. Keys must never expire or be evicted. */
28
+ export interface KeyedAccountRedisClient {
29
+ get(key: string): Promise<string | null>;
30
+ eval(script: string, options: {
31
+ keys: string[];
32
+ arguments: string[];
33
+ }): Promise<unknown>;
34
+ }
35
+ export declare function redisKeyedAccountBackend<TCtxMeta = Record<string, unknown>>(client: KeyedAccountRedisClient, options?: {
36
+ keyPrefix?: string;
37
+ }): KeyedAccountBackend<TCtxMeta>;
@@ -0,0 +1,37 @@
1
+ import type { Account } from '../server/decisioning/account';
2
+ import type { AccountReference } from '../types';
3
+ import type { PgQueryable } from '../server/postgres-task-store';
4
+ export interface KeyedAccountRecord<TCtxMeta = Record<string, unknown>> {
5
+ key: string;
6
+ ref: AccountReference;
7
+ account: Account<TCtxMeta>;
8
+ }
9
+ export interface KeyedAccountSnapshot<TCtxMeta = Record<string, unknown>> {
10
+ version: string;
11
+ records: KeyedAccountRecord<TCtxMeta>[];
12
+ }
13
+ /** Persistent, non-expiring storage. KV adapters must implement atomic CAS. */
14
+ export interface KeyedAccountBackend<TCtxMeta = Record<string, unknown>> {
15
+ load(scope: string): Promise<KeyedAccountSnapshot<TCtxMeta> | null>;
16
+ /** Null expects absence; otherwise replace only the exact loaded version. */
17
+ compareAndSwap(scope: string, version: string | null, records: KeyedAccountRecord<TCtxMeta>[]): Promise<boolean>;
18
+ }
19
+ /** Single-process backend; share the instance between server factories. No TTL. */
20
+ export declare function memoryKeyedAccountBackend<TCtxMeta = Record<string, unknown>>(): KeyedAccountBackend<TCtxMeta>;
21
+ export interface PgKeyedAccountBackendOptions {
22
+ tableName?: string;
23
+ }
24
+ export declare function getKeyedAccountMigration(options?: PgKeyedAccountBackendOptions): string;
25
+ /** Atomic SQL CAS works across processes, without an account expiry window. */
26
+ export declare function pgKeyedAccountBackend<TCtxMeta = Record<string, unknown>>(db: PgQueryable, options?: PgKeyedAccountBackendOptions): KeyedAccountBackend<TCtxMeta>;
27
+ /** Minimal node-redis compatible API. Keys must never expire or be evicted. */
28
+ export interface KeyedAccountRedisClient {
29
+ get(key: string): Promise<string | null>;
30
+ eval(script: string, options: {
31
+ keys: string[];
32
+ arguments: string[];
33
+ }): Promise<unknown>;
34
+ }
35
+ export declare function redisKeyedAccountBackend<TCtxMeta = Record<string, unknown>>(client: KeyedAccountRedisClient, options?: {
36
+ keyPrefix?: string;
37
+ }): KeyedAccountBackend<TCtxMeta>;
@@ -0,0 +1,121 @@
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 keyed_account_backend_exports = {};
20
+ __export(keyed_account_backend_exports, {
21
+ getKeyedAccountMigration: () => getKeyedAccountMigration,
22
+ memoryKeyedAccountBackend: () => memoryKeyedAccountBackend,
23
+ pgKeyedAccountBackend: () => pgKeyedAccountBackend,
24
+ redisKeyedAccountBackend: () => redisKeyedAccountBackend
25
+ });
26
+ module.exports = __toCommonJS(keyed_account_backend_exports);
27
+ var import_node_crypto = require("node:crypto");
28
+ function memoryKeyedAccountBackend() {
29
+ const data = /* @__PURE__ */ new Map();
30
+ return {
31
+ async load(scope) {
32
+ return structuredClone(data.get(scope) ?? null);
33
+ },
34
+ async compareAndSwap(scope, version, records) {
35
+ if ((data.get(scope)?.version ?? null) !== version) return false;
36
+ data.set(scope, structuredClone({ version: (0, import_node_crypto.randomUUID)(), records }));
37
+ return true;
38
+ }
39
+ };
40
+ }
41
+ async function backendOperation(name, run) {
42
+ try {
43
+ return await run();
44
+ } catch (cause) {
45
+ throw new Error(`Keyed account backend ${name} failed`, { cause });
46
+ }
47
+ }
48
+ function tableFor(options) {
49
+ const name = options.tableName ?? "adcp_keyed_accounts";
50
+ if (!/^[a-z_][a-z0-9_]{0,62}$/.test(name)) throw new TypeError("Invalid keyed account table name");
51
+ return `"${name}"`;
52
+ }
53
+ function getKeyedAccountMigration(options = {}) {
54
+ return `CREATE TABLE IF NOT EXISTS ${tableFor(options)} (scope TEXT PRIMARY KEY, version TEXT NOT NULL, records JSONB NOT NULL);`;
55
+ }
56
+ function pgKeyedAccountBackend(db, options = {}) {
57
+ const table = tableFor(options);
58
+ return {
59
+ async load(scope) {
60
+ const result = await backendOperation(
61
+ "load",
62
+ () => db.query(`SELECT version, records FROM ${table} WHERE scope = $1`, [scope])
63
+ );
64
+ const row = result.rows[0];
65
+ return row ? { version: row.version, records: row.records } : null;
66
+ },
67
+ async compareAndSwap(scope, version, records) {
68
+ const next = (0, import_node_crypto.randomUUID)();
69
+ const result = await backendOperation(
70
+ "compareAndSwap",
71
+ async () => version === null ? await db.query(
72
+ `INSERT INTO ${table} (scope, version, records) VALUES ($1, $2, $3::jsonb) ON CONFLICT (scope) DO NOTHING`,
73
+ [scope, next, JSON.stringify(records)]
74
+ ) : await db.query(`UPDATE ${table} SET version = $2, records = $3::jsonb WHERE scope = $1 AND version = $4`, [
75
+ scope,
76
+ next,
77
+ JSON.stringify(records),
78
+ version
79
+ ])
80
+ );
81
+ return result.rowCount === 1;
82
+ }
83
+ };
84
+ }
85
+ const CAS = `
86
+ local current = redis.call('GET', KEYS[1])
87
+ if ARGV[1] == '' then
88
+ if current then return 0 end
89
+ else
90
+ if not current or cjson.decode(current).version ~= ARGV[1] then return 0 end
91
+ end
92
+ redis.call('SET', KEYS[1], ARGV[2])
93
+ return 1`;
94
+ function redisKeyedAccountBackend(client, options = {}) {
95
+ const prefix = options.keyPrefix ?? "adcp:keyed-accounts:";
96
+ return {
97
+ async load(scope) {
98
+ return backendOperation("load", async () => {
99
+ const value = await client.get(prefix + scope);
100
+ return value === null ? null : JSON.parse(value);
101
+ });
102
+ },
103
+ async compareAndSwap(scope, version, records) {
104
+ const result = await backendOperation(
105
+ "compareAndSwap",
106
+ () => client.eval(CAS, {
107
+ keys: [prefix + scope],
108
+ arguments: [version ?? "", JSON.stringify({ version: (0, import_node_crypto.randomUUID)(), records })]
109
+ })
110
+ );
111
+ return Number(result) === 1;
112
+ }
113
+ };
114
+ }
115
+ // Annotate the CommonJS export names for ESM import in node:
116
+ 0 && (module.exports = {
117
+ getKeyedAccountMigration,
118
+ memoryKeyedAccountBackend,
119
+ pgKeyedAccountBackend,
120
+ redisKeyedAccountBackend
121
+ });
@@ -0,0 +1,94 @@
1
+ import { randomUUID } from "node:crypto";
2
+ function memoryKeyedAccountBackend() {
3
+ const data = /* @__PURE__ */ new Map();
4
+ return {
5
+ async load(scope) {
6
+ return structuredClone(data.get(scope) ?? null);
7
+ },
8
+ async compareAndSwap(scope, version, records) {
9
+ if ((data.get(scope)?.version ?? null) !== version) return false;
10
+ data.set(scope, structuredClone({ version: randomUUID(), records }));
11
+ return true;
12
+ }
13
+ };
14
+ }
15
+ async function backendOperation(name, run) {
16
+ try {
17
+ return await run();
18
+ } catch (cause) {
19
+ throw new Error(`Keyed account backend ${name} failed`, { cause });
20
+ }
21
+ }
22
+ function tableFor(options) {
23
+ const name = options.tableName ?? "adcp_keyed_accounts";
24
+ if (!/^[a-z_][a-z0-9_]{0,62}$/.test(name)) throw new TypeError("Invalid keyed account table name");
25
+ return `"${name}"`;
26
+ }
27
+ function getKeyedAccountMigration(options = {}) {
28
+ return `CREATE TABLE IF NOT EXISTS ${tableFor(options)} (scope TEXT PRIMARY KEY, version TEXT NOT NULL, records JSONB NOT NULL);`;
29
+ }
30
+ function pgKeyedAccountBackend(db, options = {}) {
31
+ const table = tableFor(options);
32
+ return {
33
+ async load(scope) {
34
+ const result = await backendOperation(
35
+ "load",
36
+ () => db.query(`SELECT version, records FROM ${table} WHERE scope = $1`, [scope])
37
+ );
38
+ const row = result.rows[0];
39
+ return row ? { version: row.version, records: row.records } : null;
40
+ },
41
+ async compareAndSwap(scope, version, records) {
42
+ const next = randomUUID();
43
+ const result = await backendOperation(
44
+ "compareAndSwap",
45
+ async () => version === null ? await db.query(
46
+ `INSERT INTO ${table} (scope, version, records) VALUES ($1, $2, $3::jsonb) ON CONFLICT (scope) DO NOTHING`,
47
+ [scope, next, JSON.stringify(records)]
48
+ ) : await db.query(`UPDATE ${table} SET version = $2, records = $3::jsonb WHERE scope = $1 AND version = $4`, [
49
+ scope,
50
+ next,
51
+ JSON.stringify(records),
52
+ version
53
+ ])
54
+ );
55
+ return result.rowCount === 1;
56
+ }
57
+ };
58
+ }
59
+ const CAS = `
60
+ local current = redis.call('GET', KEYS[1])
61
+ if ARGV[1] == '' then
62
+ if current then return 0 end
63
+ else
64
+ if not current or cjson.decode(current).version ~= ARGV[1] then return 0 end
65
+ end
66
+ redis.call('SET', KEYS[1], ARGV[2])
67
+ return 1`;
68
+ function redisKeyedAccountBackend(client, options = {}) {
69
+ const prefix = options.keyPrefix ?? "adcp:keyed-accounts:";
70
+ return {
71
+ async load(scope) {
72
+ return backendOperation("load", async () => {
73
+ const value = await client.get(prefix + scope);
74
+ return value === null ? null : JSON.parse(value);
75
+ });
76
+ },
77
+ async compareAndSwap(scope, version, records) {
78
+ const result = await backendOperation(
79
+ "compareAndSwap",
80
+ () => client.eval(CAS, {
81
+ keys: [prefix + scope],
82
+ arguments: [version ?? "", JSON.stringify({ version: randomUUID(), records })]
83
+ })
84
+ );
85
+ return Number(result) === 1;
86
+ }
87
+ };
88
+ }
89
+ export {
90
+ getKeyedAccountMigration,
91
+ memoryKeyedAccountBackend,
92
+ pgKeyedAccountBackend,
93
+ redisKeyedAccountBackend
94
+ };
@@ -0,0 +1,28 @@
1
+ import type { Account, AccountStore, ResolveContext } from '../server/decisioning/account.mjs';
2
+ import type { SyncAccountsRequest } from '../types/index.mjs';
3
+ import type { KeyedAccountBackend } from './keyed-account-backend.mjs';
4
+ export interface KeyedAccountStoreOptions<TCtxMeta = Record<string, unknown>> {
5
+ backend: KeyedAccountBackend<TCtxMeta>;
6
+ /** Trusted namespace for this seller/deployment; required even with a shared backend. */
7
+ namespace: string;
8
+ resolution?: 'explicit' | 'implicit';
9
+ /** Maximum records per authenticated principal. Default 1000. */
10
+ maxAccounts?: number;
11
+ /** Stable, non-secret authorization partition. Must be deterministic; may run several times per request. Defaults to the authenticated credential identity. */
12
+ principalKey?: (ctx?: ResolveContext) => string | undefined | Promise<string | undefined>;
13
+ /**
14
+ * Accept/reject commercial terms and return the real account record.
15
+ * MUST use accountId as its ID. May run again after a concurrent CAS;
16
+ * upstream side effects must be idempotent on accountId. Never persist
17
+ * credentials in ctx_metadata (authInfo is stripped automatically).
18
+ * When dryRun is true, preview only: no upstream writes or other side effects.
19
+ */
20
+ buildAccount: (ref: SyncAccountsRequest['accounts'][number], context: {
21
+ accountId: string;
22
+ existing: Account<TCtxMeta> | null;
23
+ dryRun: boolean;
24
+ ctx?: ResolveContext;
25
+ }) => Account<TCtxMeta> | Promise<Account<TCtxMeta>>;
26
+ }
27
+ /** Durable natural-key identity, additive syncs, principal isolation, no TTL. */
28
+ export declare function createKeyedAccountStore<TCtxMeta = Record<string, unknown>>(options: KeyedAccountStoreOptions<TCtxMeta>): AccountStore<TCtxMeta>;
@@ -0,0 +1,28 @@
1
+ import type { Account, AccountStore, ResolveContext } from '../server/decisioning/account';
2
+ import type { SyncAccountsRequest } from '../types';
3
+ import type { KeyedAccountBackend } from './keyed-account-backend';
4
+ export interface KeyedAccountStoreOptions<TCtxMeta = Record<string, unknown>> {
5
+ backend: KeyedAccountBackend<TCtxMeta>;
6
+ /** Trusted namespace for this seller/deployment; required even with a shared backend. */
7
+ namespace: string;
8
+ resolution?: 'explicit' | 'implicit';
9
+ /** Maximum records per authenticated principal. Default 1000. */
10
+ maxAccounts?: number;
11
+ /** Stable, non-secret authorization partition. Must be deterministic; may run several times per request. Defaults to the authenticated credential identity. */
12
+ principalKey?: (ctx?: ResolveContext) => string | undefined | Promise<string | undefined>;
13
+ /**
14
+ * Accept/reject commercial terms and return the real account record.
15
+ * MUST use accountId as its ID. May run again after a concurrent CAS;
16
+ * upstream side effects must be idempotent on accountId. Never persist
17
+ * credentials in ctx_metadata (authInfo is stripped automatically).
18
+ * When dryRun is true, preview only: no upstream writes or other side effects.
19
+ */
20
+ buildAccount: (ref: SyncAccountsRequest['accounts'][number], context: {
21
+ accountId: string;
22
+ existing: Account<TCtxMeta> | null;
23
+ dryRun: boolean;
24
+ ctx?: ResolveContext;
25
+ }) => Account<TCtxMeta> | Promise<Account<TCtxMeta>>;
26
+ }
27
+ /** Durable natural-key identity, additive syncs, principal isolation, no TTL. */
28
+ export declare function createKeyedAccountStore<TCtxMeta = Record<string, unknown>>(options: KeyedAccountStoreOptions<TCtxMeta>): AccountStore<TCtxMeta>;
@@ -0,0 +1,227 @@
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 keyed_account_store_exports = {};
20
+ __export(keyed_account_store_exports, {
21
+ createKeyedAccountStore: () => createKeyedAccountStore
22
+ });
23
+ module.exports = __toCommonJS(keyed_account_store_exports);
24
+ var import_node_crypto = require("node:crypto");
25
+ var import_node_util = require("node:util");
26
+ var import_account_key = require('../core/account-key.js');
27
+ var import_implicit_account_store = require('./implicit-account-store.js');
28
+ var import_async_outcome = require('../server/decisioning/async-outcome.js');
29
+ var import_account = require('../server/decisioning/account.js');
30
+ var import_account_mode = require('../server/account-mode.js');
31
+ var import_account_sync_contract = require('../server/account-sync-contract.js');
32
+ function entryRef(entry) {
33
+ return entry.account ?? entry;
34
+ }
35
+ function recordFor(records, ref) {
36
+ return "account_id" in ref ? records.find((row) => row.account.id === ref.account_id) : records.find((row) => row.key === (0, import_account_key.accountReferenceKey)(ref));
37
+ }
38
+ function resultRow(account, action) {
39
+ const wire = (0, import_account.toWireAccount)(account);
40
+ return { ...wire, brand: account.brand, operator: account.operator, action };
41
+ }
42
+ function createKeyedAccountStore(options) {
43
+ if (!options.namespace) throw new TypeError("A keyed account store requires a namespace");
44
+ const maxAccounts = options.maxAccounts ?? 1e3;
45
+ if (!Number.isSafeInteger(maxAccounts) || maxAccounts < 1)
46
+ throw new TypeError("maxAccounts must be a positive integer");
47
+ const principalKey = options.principalKey ?? ((ctx) => ctx?.authInfo ? (0, import_implicit_account_store.defaultImplicitKeyFn)(ctx.authInfo) : void 0);
48
+ const scopeFor = async (ctx) => {
49
+ const principal = await principalKey(options.principalKey && ctx ? (0, import_account_sync_contract.isolateAccountCallbackInput)(ctx) : ctx);
50
+ if (typeof principal !== "string" || !principal.trim())
51
+ throw new import_async_outcome.AdcpError("AUTH_MISSING", {
52
+ message: "An authenticated account principal is required.",
53
+ recovery: "terminal"
54
+ });
55
+ return JSON.stringify([options.namespace, principal]);
56
+ };
57
+ return {
58
+ resolution: options.resolution ?? "explicit",
59
+ strictIdempotency: true,
60
+ async resolve(ref, ctx) {
61
+ const records = (await options.backend.load(await scopeFor(ctx)))?.records ?? [];
62
+ return ref ? recordFor(records, ref)?.account ?? null : records.length === 1 ? records[0].account : null;
63
+ },
64
+ async upsert(entries, ctx) {
65
+ const dryRun = ctx?.input?.dry_run === true;
66
+ const deleteMissing = ctx?.input?.delete_missing === true;
67
+ const input = ctx?.input === void 0 ? void 0 : structuredClone(ctx.input);
68
+ entries = structuredClone(entries);
69
+ const scope = await scopeFor(ctx);
70
+ if (deleteMissing)
71
+ throw new import_async_outcome.AdcpError("UNSUPPORTED_FEATURE", {
72
+ message: "The keyed account store uses additive syncs; close accounts explicitly.",
73
+ recovery: "terminal"
74
+ });
75
+ for (let attempt = 0; attempt < 10; attempt++) {
76
+ const snapshot = await options.backend.load(scope);
77
+ const records = structuredClone(snapshot?.records ?? []);
78
+ const rows = [];
79
+ for (const entry of entries) {
80
+ const ref = entryRef(entry);
81
+ if ("account_id" in ref && !recordFor(records, ref))
82
+ throw new import_async_outcome.AdcpError("ACCOUNT_NOT_FOUND", { message: "Account not found", recovery: "terminal" });
83
+ }
84
+ for (const entry of entries) {
85
+ const ref = entryRef(entry);
86
+ const hit = recordFor(records, ref);
87
+ const source = hit?.ref ?? ref;
88
+ const natural = {
89
+ brand: source.brand,
90
+ operator: source.operator,
91
+ ...source.operator_unit !== void 0 && { operator_unit: source.operator_unit },
92
+ ...source.currency !== void 0 && { currency: source.currency },
93
+ ...source.timezone !== void 0 && { timezone: source.timezone },
94
+ ...source.sandbox !== void 0 && { sandbox: source.sandbox }
95
+ };
96
+ const key = (0, import_account_key.accountReferenceKey)(natural);
97
+ const accountId = hit?.account.id ?? `acct_${(0, import_node_crypto.createHash)("sha256").update(JSON.stringify([scope, key])).digest("hex")}`;
98
+ try {
99
+ if (!hit && records.length >= maxAccounts)
100
+ throw new import_async_outcome.AdcpError("INVALID_REQUEST", {
101
+ message: "The account limit for this principal has been reached.",
102
+ recovery: "correctable"
103
+ });
104
+ if ("account" in entry && !hit)
105
+ throw new import_async_outcome.AdcpError("UNSUPPORTED_PROVISIONING", {
106
+ message: "Settings updates require an existing account.",
107
+ recovery: "correctable"
108
+ });
109
+ if ("operator_identity" in entry)
110
+ throw new import_async_outcome.AdcpError("UNSUPPORTED_FEATURE", {
111
+ message: "The keyed account store does not implement identity reconciliation.",
112
+ recovery: "terminal"
113
+ });
114
+ const built = await options.buildAccount(
115
+ structuredClone(entry),
116
+ {
117
+ accountId,
118
+ existing: hit ? structuredClone(hit.account) : null,
119
+ dryRun,
120
+ ctx: ctx && (0, import_account_sync_contract.isolateAccountCallbackInput)({ ...ctx, ...input !== void 0 && { input } })
121
+ }
122
+ );
123
+ if (built.id !== accountId) throw new Error("buildAccount must preserve the supplied accountId");
124
+ const { authInfo: _authInfo, ...persisted } = built;
125
+ const account = JSON.parse(
126
+ JSON.stringify({
127
+ ...persisted,
128
+ brand: natural.brand,
129
+ operator: natural.operator
130
+ })
131
+ );
132
+ if (natural.sandbox === true && !(0, import_account_mode.isSandboxOrMockAccount)(account))
133
+ throw new import_async_outcome.AdcpError("UNSUPPORTED_FEATURE", {
134
+ message: "The seller did not authorize a sandbox account.",
135
+ recovery: "terminal"
136
+ });
137
+ const request = entry;
138
+ if (request.billing === "agent" && account.billing?.invoicedTo !== "agent") {
139
+ throw new import_async_outcome.AdcpError("BILLING_NOT_SUPPORTED", {
140
+ message: "The seller did not accept agent billing.",
141
+ recovery: "correctable"
142
+ });
143
+ }
144
+ if (account.status === "active" && request.payment_terms !== void 0 && account.payment_terms !== request.payment_terms) {
145
+ throw new import_async_outcome.AdcpError("PAYMENT_TERMS_NOT_SUPPORTED", {
146
+ message: "The seller did not accept the requested payment terms.",
147
+ recovery: "correctable"
148
+ });
149
+ }
150
+ if (account.billing?.invoicedTo === "agent" && account.payment_terms === void 0) {
151
+ throw new import_async_outcome.AdcpError("PAYMENT_TERMS_NOT_SUPPORTED", {
152
+ message: "Agent-billed accounts require explicit payment terms.",
153
+ recovery: "correctable"
154
+ });
155
+ }
156
+ const action = !hit ? "created" : (0, import_node_util.isDeepStrictEqual)(hit.account, account) ? "unchanged" : "updated";
157
+ rows.push(resultRow(account, action));
158
+ const record = { key, ref: natural, account };
159
+ if (hit) records[records.indexOf(hit)] = record;
160
+ else records.push(record);
161
+ } catch (error) {
162
+ if (!(error instanceof import_async_outcome.AdcpError)) throw error;
163
+ rows.push({
164
+ brand: natural.brand,
165
+ operator: natural.operator,
166
+ ...hit && { account_id: hit.account.id },
167
+ action: "failed",
168
+ status: hit?.account.status ?? "rejected",
169
+ errors: [error.toStructuredError()]
170
+ });
171
+ }
172
+ }
173
+ if (dryRun || (0, import_node_util.isDeepStrictEqual)(snapshot?.records ?? [], records)) return rows;
174
+ if (await options.backend.compareAndSwap(scope, snapshot?.version ?? null, records)) return rows;
175
+ }
176
+ throw new import_async_outcome.AdcpError("CONFLICT", {
177
+ message: "Concurrent account sync; retry the request.",
178
+ recovery: "correctable"
179
+ });
180
+ },
181
+ async list(request, ctx) {
182
+ const scope = await scopeFor(ctx);
183
+ const snapshot = await options.backend.load(scope);
184
+ let records = snapshot?.records ?? [];
185
+ if (request.account) records = records.filter((record) => recordFor([record], request.account) !== void 0);
186
+ if (request.status) records = records.filter((record) => record.account.status === request.status);
187
+ if (request.sandbox !== void 0)
188
+ records = records.filter((record) => (0, import_account_mode.isSandboxOrMockAccount)(record.account) === request.sandbox);
189
+ const cursor = request.pagination?.cursor;
190
+ let offset = 0;
191
+ if (cursor !== void 0) {
192
+ try {
193
+ const parsed = JSON.parse(Buffer.from(cursor, "base64url").toString());
194
+ const binding2 = (0, import_node_crypto.createHash)("sha256").update(JSON.stringify([scope, snapshot?.version, request.account, request.status, request.sandbox])).digest("hex");
195
+ if (parsed.binding !== binding2 || !Number.isSafeInteger(parsed.offset) || parsed.offset < 0)
196
+ throw new Error();
197
+ offset = parsed.offset;
198
+ } catch {
199
+ throw new import_async_outcome.AdcpError("INVALID_REQUEST", {
200
+ message: "Invalid or stale account cursor.",
201
+ recovery: "correctable"
202
+ });
203
+ }
204
+ }
205
+ const limit = request.pagination?.max_results ?? 100;
206
+ if (!Number.isSafeInteger(limit) || limit < 1)
207
+ throw new import_async_outcome.AdcpError("INVALID_REQUEST", {
208
+ message: "max_results must be a positive integer.",
209
+ recovery: "correctable"
210
+ });
211
+ const items = records.slice(offset, offset + limit).map((record) => record.account);
212
+ const next = offset + items.length;
213
+ const binding = (0, import_node_crypto.createHash)("sha256").update(JSON.stringify([scope, snapshot?.version, request.account, request.status, request.sandbox])).digest("hex");
214
+ return {
215
+ items,
216
+ totalCount: records.length,
217
+ ...next < records.length && {
218
+ nextCursor: Buffer.from(JSON.stringify({ binding, offset: next })).toString("base64url")
219
+ }
220
+ };
221
+ }
222
+ };
223
+ }
224
+ // Annotate the CommonJS export names for ESM import in node:
225
+ 0 && (module.exports = {
226
+ createKeyedAccountStore
227
+ });