tapimo 0.0.2 → 0.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 (517) hide show
  1. package/README.md +38 -16
  2. package/dist/ApiKey.d.ts +14 -11
  3. package/dist/ApiKey.d.ts.map +1 -1
  4. package/dist/ApiKey.js +11 -12
  5. package/dist/ApiKey.js.map +1 -1
  6. package/dist/ApiKeys.d.ts +207 -0
  7. package/dist/ApiKeys.d.ts.map +1 -0
  8. package/dist/ApiKeys.js +282 -0
  9. package/dist/ApiKeys.js.map +1 -0
  10. package/dist/App.d.ts +6771 -3819
  11. package/dist/App.d.ts.map +1 -1
  12. package/dist/App.js +145 -317
  13. package/dist/App.js.map +1 -1
  14. package/dist/Handler.d.ts +86 -0
  15. package/dist/Handler.d.ts.map +1 -0
  16. package/dist/Handler.js +83 -0
  17. package/dist/Handler.js.map +1 -0
  18. package/dist/Scope.d.ts +14 -4
  19. package/dist/Scope.d.ts.map +1 -1
  20. package/dist/Scope.js +16 -4
  21. package/dist/Scope.js.map +1 -1
  22. package/dist/Webhooks.d.ts +5 -7
  23. package/dist/Webhooks.d.ts.map +1 -1
  24. package/dist/Webhooks.js +11 -4
  25. package/dist/Webhooks.js.map +1 -1
  26. package/dist/admin/Access.d.ts +1 -3
  27. package/dist/admin/Access.d.ts.map +1 -1
  28. package/dist/admin/Access.js +1 -3
  29. package/dist/admin/Access.js.map +1 -1
  30. package/dist/admin/App.d.ts +167 -205
  31. package/dist/admin/App.d.ts.map +1 -1
  32. package/dist/admin/App.js +2 -4
  33. package/dist/admin/App.js.map +1 -1
  34. package/dist/admin/apps/api-keys.d.ts +105 -5
  35. package/dist/admin/apps/api-keys.d.ts.map +1 -1
  36. package/dist/admin/apps/api-keys.js +75 -9
  37. package/dist/admin/apps/api-keys.js.map +1 -1
  38. package/dist/admin/apps/scopes.d.ts +6 -0
  39. package/dist/admin/apps/scopes.d.ts.map +1 -1
  40. package/dist/admin/apps/verified-tokens.d.ts +35 -149
  41. package/dist/admin/apps/verified-tokens.d.ts.map +1 -1
  42. package/dist/admin/apps/verified-tokens.js +18 -39
  43. package/dist/admin/apps/verified-tokens.js.map +1 -1
  44. package/dist/admin/ui.generated.d.ts +1 -1
  45. package/dist/admin/ui.generated.js +1 -1
  46. package/dist/apps/data/App.d.ts +19176 -0
  47. package/dist/apps/data/App.d.ts.map +1 -0
  48. package/dist/apps/data/App.js +236 -0
  49. package/dist/apps/data/App.js.map +1 -0
  50. package/dist/apps/data/index.d.ts +3 -0
  51. package/dist/apps/data/index.d.ts.map +1 -0
  52. package/dist/apps/data/index.js +3 -0
  53. package/dist/apps/data/index.js.map +1 -0
  54. package/dist/apps/{activities.d.ts → data/routes/activities.d.ts} +32 -32
  55. package/dist/apps/{activities.d.ts.map → data/routes/activities.d.ts.map} +1 -1
  56. package/dist/apps/{activities.js → data/routes/activities.js} +13 -13
  57. package/dist/apps/data/routes/activities.js.map +1 -0
  58. package/dist/apps/{addresses.d.ts → data/routes/addresses.d.ts} +22 -22
  59. package/dist/apps/{addresses.d.ts.map → data/routes/addresses.d.ts.map} +1 -1
  60. package/dist/apps/{addresses.js → data/routes/addresses.js} +5 -5
  61. package/dist/apps/data/routes/addresses.js.map +1 -0
  62. package/dist/apps/{balances.d.ts → data/routes/balances.d.ts} +49 -49
  63. package/dist/apps/data/routes/balances.d.ts.map +1 -0
  64. package/dist/apps/{balances.js → data/routes/balances.js} +42 -11
  65. package/dist/apps/data/routes/balances.js.map +1 -0
  66. package/dist/apps/{blocks.d.ts → data/routes/blocks.d.ts} +47 -47
  67. package/dist/apps/data/routes/blocks.d.ts.map +1 -0
  68. package/dist/apps/{blocks.js → data/routes/blocks.js} +13 -13
  69. package/dist/apps/data/routes/blocks.js.map +1 -0
  70. package/dist/apps/{coingecko.d.ts → data/routes/coingecko.d.ts} +183 -183
  71. package/dist/apps/data/routes/coingecko.d.ts.map +1 -0
  72. package/dist/apps/{coingecko.js → data/routes/coingecko.js} +13 -13
  73. package/dist/apps/data/routes/coingecko.js.map +1 -0
  74. package/dist/apps/{exchanges.d.ts → data/routes/exchanges.d.ts} +460 -460
  75. package/dist/apps/data/routes/exchanges.d.ts.map +1 -0
  76. package/dist/apps/{exchanges.js → data/routes/exchanges.js} +19 -19
  77. package/dist/apps/data/routes/exchanges.js.map +1 -0
  78. package/dist/apps/{fee-amm.d.ts → data/routes/fee-amm.d.ts} +94 -94
  79. package/dist/apps/data/routes/fee-amm.d.ts.map +1 -0
  80. package/dist/apps/{fee-amm.js → data/routes/fee-amm.js} +17 -17
  81. package/dist/apps/data/routes/fee-amm.js.map +1 -0
  82. package/dist/apps/{indexer.d.ts → data/routes/indexer.d.ts} +4 -4
  83. package/dist/apps/data/routes/indexer.d.ts.map +1 -0
  84. package/dist/apps/{indexer.js → data/routes/indexer.js} +13 -9
  85. package/dist/apps/data/routes/indexer.js.map +1 -0
  86. package/dist/apps/{receipts.d.ts → data/routes/receipts.d.ts} +6 -6
  87. package/dist/apps/data/routes/receipts.d.ts.map +1 -0
  88. package/dist/apps/{receipts.js → data/routes/receipts.js} +12 -12
  89. package/dist/apps/data/routes/receipts.js.map +1 -0
  90. package/dist/apps/{rpc.d.ts → data/routes/rpc.d.ts} +4 -4
  91. package/dist/apps/data/routes/rpc.d.ts.map +1 -0
  92. package/dist/apps/{rpc.js → data/routes/rpc.js} +6 -6
  93. package/dist/apps/data/routes/rpc.js.map +1 -0
  94. package/dist/apps/{tokenlist.d.ts → data/routes/tokenlist.d.ts} +4 -4
  95. package/dist/apps/data/routes/tokenlist.d.ts.map +1 -0
  96. package/dist/apps/{tokenlist.js → data/routes/tokenlist.js} +8 -8
  97. package/dist/apps/data/routes/tokenlist.js.map +1 -0
  98. package/dist/apps/{tokens.d.ts → data/routes/tokens.d.ts} +11 -11
  99. package/dist/apps/data/routes/tokens.d.ts.map +1 -0
  100. package/dist/apps/{tokens.js → data/routes/tokens.js} +19 -19
  101. package/dist/apps/data/routes/tokens.js.map +1 -0
  102. package/dist/apps/{transactions.d.ts → data/routes/transactions.d.ts} +288 -288
  103. package/dist/apps/data/routes/transactions.d.ts.map +1 -0
  104. package/dist/apps/{transactions.js → data/routes/transactions.js} +13 -13
  105. package/dist/apps/data/routes/transactions.js.map +1 -0
  106. package/dist/apps/{transfers.d.ts → data/routes/transfers.d.ts} +35 -35
  107. package/dist/apps/data/routes/transfers.d.ts.map +1 -0
  108. package/dist/apps/{transfers.js → data/routes/transfers.js} +13 -13
  109. package/dist/apps/data/routes/transfers.js.map +1 -0
  110. package/dist/apps/{verified-tokens.d.ts → data/routes/verified-tokens.d.ts} +42 -42
  111. package/dist/apps/data/routes/verified-tokens.d.ts.map +1 -0
  112. package/dist/apps/{verified-tokens.js → data/routes/verified-tokens.js} +9 -9
  113. package/dist/apps/data/routes/verified-tokens.js.map +1 -0
  114. package/dist/apps/{webhooks.d.ts → data/routes/webhooks.d.ts} +66 -63
  115. package/dist/apps/{webhooks.d.ts.map → data/routes/webhooks.d.ts.map} +1 -1
  116. package/dist/apps/{webhooks.js → data/routes/webhooks.js} +67 -59
  117. package/dist/apps/data/routes/webhooks.js.map +1 -0
  118. package/dist/apps/docs/App.d.ts +27 -0
  119. package/dist/apps/docs/App.d.ts.map +1 -0
  120. package/dist/apps/docs/App.js +122 -0
  121. package/dist/apps/docs/App.js.map +1 -0
  122. package/dist/{internal/OpenApi.fonts.d.ts → apps/docs/fonts.d.ts} +1 -1
  123. package/dist/apps/docs/fonts.d.ts.map +1 -0
  124. package/dist/{internal/OpenApi.fonts.js → apps/docs/fonts.js} +1 -1
  125. package/dist/apps/docs/fonts.js.map +1 -0
  126. package/dist/apps/docs/index.d.ts +2 -0
  127. package/dist/apps/docs/index.d.ts.map +1 -0
  128. package/dist/apps/docs/index.js +2 -0
  129. package/dist/apps/docs/index.js.map +1 -0
  130. package/dist/{internal/OpenApi.logo.d.ts → apps/docs/logo.d.ts} +1 -1
  131. package/dist/apps/docs/logo.d.ts.map +1 -0
  132. package/dist/{internal/OpenApi.logo.js → apps/docs/logo.js} +1 -1
  133. package/dist/apps/docs/logo.js.map +1 -0
  134. package/dist/apps/index.d.ts +4 -0
  135. package/dist/apps/index.d.ts.map +1 -0
  136. package/dist/apps/index.js +4 -0
  137. package/dist/apps/index.js.map +1 -0
  138. package/dist/apps/metadata.d.ts +7 -0
  139. package/dist/apps/metadata.d.ts.map +1 -0
  140. package/dist/apps/metadata.js +15 -0
  141. package/dist/apps/metadata.js.map +1 -0
  142. package/dist/apps/platform/App.d.ts +2985 -0
  143. package/dist/apps/platform/App.d.ts.map +1 -0
  144. package/dist/apps/platform/App.js +74 -0
  145. package/dist/apps/platform/App.js.map +1 -0
  146. package/dist/apps/platform/index.d.ts +2 -0
  147. package/dist/apps/platform/index.d.ts.map +1 -0
  148. package/dist/apps/platform/index.js +2 -0
  149. package/dist/apps/platform/index.js.map +1 -0
  150. package/dist/apps/platform/routes/api-keys.d.ts +681 -0
  151. package/dist/apps/platform/routes/api-keys.d.ts.map +1 -0
  152. package/dist/apps/platform/routes/api-keys.js +328 -0
  153. package/dist/apps/platform/routes/api-keys.js.map +1 -0
  154. package/dist/apps/platform/routes/invitations.d.ts +733 -0
  155. package/dist/apps/platform/routes/invitations.d.ts.map +1 -0
  156. package/dist/apps/platform/routes/invitations.js +413 -0
  157. package/dist/apps/platform/routes/invitations.js.map +1 -0
  158. package/dist/apps/platform/routes/me.d.ts +95 -0
  159. package/dist/apps/platform/routes/me.d.ts.map +1 -0
  160. package/dist/apps/platform/routes/me.js +81 -0
  161. package/dist/apps/platform/routes/me.js.map +1 -0
  162. package/dist/apps/platform/routes/members.d.ts +554 -0
  163. package/dist/apps/platform/routes/members.d.ts.map +1 -0
  164. package/dist/apps/platform/routes/members.js +251 -0
  165. package/dist/apps/platform/routes/members.js.map +1 -0
  166. package/dist/apps/platform/routes/orgs.d.ts +582 -0
  167. package/dist/apps/platform/routes/orgs.d.ts.map +1 -0
  168. package/dist/apps/platform/routes/orgs.js +265 -0
  169. package/dist/apps/platform/routes/orgs.js.map +1 -0
  170. package/dist/apps/platform/routes/projects.d.ts +740 -0
  171. package/dist/apps/platform/routes/projects.d.ts.map +1 -0
  172. package/dist/apps/platform/routes/projects.js +286 -0
  173. package/dist/apps/platform/routes/projects.js.map +1 -0
  174. package/dist/apps/relay/App.d.ts +41 -0
  175. package/dist/apps/relay/App.d.ts.map +1 -0
  176. package/dist/apps/relay/App.js +103 -0
  177. package/dist/apps/relay/App.js.map +1 -0
  178. package/dist/apps/relay/Sponsorships.d.ts +107 -0
  179. package/dist/apps/relay/Sponsorships.d.ts.map +1 -0
  180. package/dist/apps/relay/Sponsorships.js +193 -0
  181. package/dist/apps/relay/Sponsorships.js.map +1 -0
  182. package/dist/apps/relay/index.d.ts +3 -0
  183. package/dist/apps/relay/index.d.ts.map +1 -0
  184. package/dist/apps/relay/index.js +3 -0
  185. package/dist/apps/relay/index.js.map +1 -0
  186. package/dist/{Cli.d.ts → cli/Cli.d.ts} +13 -2
  187. package/dist/cli/Cli.d.ts.map +1 -0
  188. package/dist/cli/Cli.js +19 -0
  189. package/dist/cli/Cli.js.map +1 -0
  190. package/dist/cli/bin.d.ts +3 -0
  191. package/dist/cli/bin.d.ts.map +1 -0
  192. package/dist/cli/bin.js +4 -0
  193. package/dist/cli/bin.js.map +1 -0
  194. package/dist/cli/commands/admin.d.ts +15 -0
  195. package/dist/cli/commands/admin.d.ts.map +1 -0
  196. package/dist/cli/commands/admin.js +60 -0
  197. package/dist/cli/commands/admin.js.map +1 -0
  198. package/dist/cloudflare.d.ts +7 -0
  199. package/dist/cloudflare.d.ts.map +1 -1
  200. package/dist/cloudflare.js +9 -0
  201. package/dist/cloudflare.js.map +1 -1
  202. package/dist/db/Db.d.ts +113 -0
  203. package/dist/db/Db.d.ts.map +1 -0
  204. package/dist/db/Db.js +402 -0
  205. package/dist/db/Db.js.map +1 -0
  206. package/dist/db/Schema.d.ts +168 -0
  207. package/dist/db/Schema.d.ts.map +1 -0
  208. package/dist/db/Schema.js +229 -0
  209. package/dist/db/Schema.js.map +1 -0
  210. package/dist/db/tables/invitations.d.ts +84 -0
  211. package/dist/db/tables/invitations.d.ts.map +1 -0
  212. package/dist/db/tables/invitations.js +165 -0
  213. package/dist/db/tables/invitations.js.map +1 -0
  214. package/dist/db/tables/memberships.d.ts +108 -0
  215. package/dist/db/tables/memberships.d.ts.map +1 -0
  216. package/dist/db/tables/memberships.js +153 -0
  217. package/dist/db/tables/memberships.js.map +1 -0
  218. package/dist/db/tables/organizations.d.ts +123 -0
  219. package/dist/db/tables/organizations.d.ts.map +1 -0
  220. package/dist/db/tables/organizations.js +170 -0
  221. package/dist/db/tables/organizations.js.map +1 -0
  222. package/dist/db/tables/projects.d.ts +65 -0
  223. package/dist/db/tables/projects.d.ts.map +1 -0
  224. package/dist/db/tables/projects.js +75 -0
  225. package/dist/db/tables/projects.js.map +1 -0
  226. package/dist/db/tables/sponsoredTransactions.d.ts +120 -0
  227. package/dist/db/tables/sponsoredTransactions.d.ts.map +1 -0
  228. package/dist/db/tables/sponsoredTransactions.js +161 -0
  229. package/dist/db/tables/sponsoredTransactions.js.map +1 -0
  230. package/dist/db/tables/users.d.ts +48 -0
  231. package/dist/db/tables/users.d.ts.map +1 -0
  232. package/dist/db/tables/users.js +65 -0
  233. package/dist/db/tables/users.js.map +1 -0
  234. package/dist/db/tables/verifiedTokens.d.ts +53 -0
  235. package/dist/db/tables/verifiedTokens.d.ts.map +1 -0
  236. package/dist/db/tables/verifiedTokens.js +74 -0
  237. package/dist/db/tables/verifiedTokens.js.map +1 -0
  238. package/dist/db/tables/webhookDeliveries.d.ts +71 -0
  239. package/dist/db/tables/webhookDeliveries.d.ts.map +1 -0
  240. package/dist/db/tables/webhookDeliveries.js +119 -0
  241. package/dist/db/tables/webhookDeliveries.js.map +1 -0
  242. package/dist/db/tables/webhookSubscriptions.d.ts +158 -0
  243. package/dist/db/tables/webhookSubscriptions.d.ts.map +1 -0
  244. package/dist/db/tables/webhookSubscriptions.js +272 -0
  245. package/dist/db/tables/webhookSubscriptions.js.map +1 -0
  246. package/dist/handlers/internal/executionError.d.ts +25 -0
  247. package/dist/handlers/internal/executionError.d.ts.map +1 -0
  248. package/dist/handlers/internal/executionError.js +186 -0
  249. package/dist/handlers/internal/executionError.js.map +1 -0
  250. package/dist/handlers/internal/multisig.d.ts +233 -0
  251. package/dist/handlers/internal/multisig.d.ts.map +1 -0
  252. package/dist/handlers/internal/multisig.js +542 -0
  253. package/dist/handlers/internal/multisig.js.map +1 -0
  254. package/dist/handlers/internal/requestListener.d.ts +124 -0
  255. package/dist/handlers/internal/requestListener.d.ts.map +1 -0
  256. package/dist/handlers/internal/requestListener.js +173 -0
  257. package/dist/handlers/internal/requestListener.js.map +1 -0
  258. package/dist/handlers/internal/sponsorship.d.ts +113 -0
  259. package/dist/handlers/internal/sponsorship.d.ts.map +1 -0
  260. package/dist/handlers/internal/sponsorship.js +128 -0
  261. package/dist/handlers/internal/sponsorship.js.map +1 -0
  262. package/dist/handlers/internal/types.d.ts +284 -0
  263. package/dist/handlers/internal/types.d.ts.map +1 -0
  264. package/dist/handlers/internal/types.js +2 -0
  265. package/dist/handlers/internal/types.js.map +1 -0
  266. package/dist/handlers/internal/utils.d.ts +34 -0
  267. package/dist/handlers/internal/utils.d.ts.map +1 -0
  268. package/dist/handlers/internal/utils.js +131 -0
  269. package/dist/handlers/internal/utils.js.map +1 -0
  270. package/dist/handlers/relay.d.ts +123 -0
  271. package/dist/handlers/relay.d.ts.map +1 -0
  272. package/dist/handlers/relay.js +1278 -0
  273. package/dist/handlers/relay.js.map +1 -0
  274. package/dist/index.d.ts +4 -2
  275. package/dist/index.d.ts.map +1 -1
  276. package/dist/index.js +4 -2
  277. package/dist/index.js.map +1 -1
  278. package/dist/internal/Auth.d.ts +224 -16
  279. package/dist/internal/Auth.d.ts.map +1 -1
  280. package/dist/internal/Auth.js +572 -33
  281. package/dist/internal/Auth.js.map +1 -1
  282. package/dist/internal/Log.d.ts +7 -3
  283. package/dist/internal/Log.d.ts.map +1 -1
  284. package/dist/internal/Log.js +4 -0
  285. package/dist/internal/Log.js.map +1 -1
  286. package/dist/internal/OpenApi.d.ts +0 -19
  287. package/dist/internal/OpenApi.d.ts.map +1 -1
  288. package/dist/internal/OpenApi.js +0 -57
  289. package/dist/internal/OpenApi.js.map +1 -1
  290. package/dist/internal/RpcSchema.d.ts +10 -0
  291. package/dist/internal/RpcSchema.d.ts.map +1 -0
  292. package/dist/internal/RpcSchema.js +2 -0
  293. package/dist/internal/RpcSchema.js.map +1 -0
  294. package/dist/internal/Store.d.ts +49 -4
  295. package/dist/internal/Store.d.ts.map +1 -1
  296. package/dist/internal/Store.js +81 -5
  297. package/dist/internal/Store.js.map +1 -1
  298. package/dist/internal/VerifiedTokens.d.ts +15 -34
  299. package/dist/internal/VerifiedTokens.d.ts.map +1 -1
  300. package/dist/internal/VerifiedTokens.js +115 -159
  301. package/dist/internal/VerifiedTokens.js.map +1 -1
  302. package/dist/internal/Viem.d.ts +84 -0
  303. package/dist/internal/Viem.d.ts.map +1 -1
  304. package/dist/internal/Webhooks.d.ts +50 -36
  305. package/dist/internal/Webhooks.d.ts.map +1 -1
  306. package/dist/internal/Webhooks.js +92 -205
  307. package/dist/internal/Webhooks.js.map +1 -1
  308. package/package.json +37 -6
  309. package/src/ApiKey.test.ts +3 -3
  310. package/src/ApiKey.ts +11 -12
  311. package/src/ApiKeys.test.ts +278 -0
  312. package/src/ApiKeys.ts +358 -0
  313. package/src/App.test-d.ts +1 -1
  314. package/src/App.test.ts +40508 -19083
  315. package/src/App.ts +239 -416
  316. package/src/Client.test-d.ts +307 -16
  317. package/src/Handler.ts +154 -0
  318. package/src/Scope.ts +17 -4
  319. package/src/Webhooks.test.ts +8 -7
  320. package/src/Webhooks.ts +13 -7
  321. package/src/admin/Access.ts +1 -3
  322. package/src/admin/App.ts +19 -24
  323. package/src/admin/apps/api-keys.test.ts +70 -18
  324. package/src/admin/apps/api-keys.ts +92 -9
  325. package/src/admin/apps/scopes.test.ts +8 -0
  326. package/src/admin/apps/verified-tokens.test.ts +3 -30
  327. package/src/admin/apps/verified-tokens.ts +18 -37
  328. package/src/admin/ui.generated.ts +1 -1
  329. package/src/apps/composition.test.ts +66 -0
  330. package/src/apps/data/App.ts +295 -0
  331. package/src/apps/data/index.ts +2 -0
  332. package/src/apps/{activities.test.ts → data/routes/activities.test.ts} +3 -3
  333. package/src/apps/{activities.ts → data/routes/activities.ts} +15 -15
  334. package/src/apps/{addresses.ts → data/routes/addresses.ts} +6 -6
  335. package/src/apps/{balances.test.ts → data/routes/balances.test.ts} +6 -5
  336. package/src/apps/{balances.ts → data/routes/balances.ts} +46 -12
  337. package/src/apps/{blocks.test.ts → data/routes/blocks.test.ts} +4 -4
  338. package/src/apps/{blocks.ts → data/routes/blocks.ts} +14 -14
  339. package/src/apps/{coingecko.test.ts → data/routes/coingecko.test.ts} +2 -2
  340. package/src/apps/{coingecko.ts → data/routes/coingecko.ts} +14 -14
  341. package/src/apps/{exchanges.test.ts → data/routes/exchanges.test.ts} +2 -2
  342. package/src/apps/{exchanges.ts → data/routes/exchanges.ts} +20 -20
  343. package/src/apps/{fee-amm.test.ts → data/routes/fee-amm.test.ts} +3 -3
  344. package/src/apps/{fee-amm.ts → data/routes/fee-amm.ts} +18 -18
  345. package/src/apps/{indexer.test.ts → data/routes/indexer.test.ts} +27 -6
  346. package/src/apps/{indexer.ts → data/routes/indexer.ts} +13 -10
  347. package/src/apps/{receipts.test.ts → data/routes/receipts.test.ts} +4 -4
  348. package/src/apps/{receipts.ts → data/routes/receipts.ts} +13 -13
  349. package/src/apps/{rpc.test.ts → data/routes/rpc.test.ts} +142 -13
  350. package/src/apps/{rpc.ts → data/routes/rpc.ts} +7 -7
  351. package/src/apps/{tokenlist.test.ts → data/routes/tokenlist.test.ts} +8 -10
  352. package/src/apps/{tokenlist.ts → data/routes/tokenlist.ts} +9 -9
  353. package/src/apps/{tokens.test.ts → data/routes/tokens.test.ts} +6 -5
  354. package/src/apps/{tokens.ts → data/routes/tokens.ts} +20 -20
  355. package/src/apps/{transactions.test.ts → data/routes/transactions.test.ts} +4 -4
  356. package/src/apps/{transactions.ts → data/routes/transactions.ts} +15 -15
  357. package/src/apps/{transfers.test.ts → data/routes/transfers.test.ts} +3 -3
  358. package/src/apps/{transfers.ts → data/routes/transfers.ts} +15 -15
  359. package/src/apps/{verified-tokens.test.ts → data/routes/verified-tokens.test.ts} +27 -15
  360. package/src/apps/{verified-tokens.ts → data/routes/verified-tokens.ts} +10 -10
  361. package/src/apps/{webhooks.test.ts → data/routes/webhooks.test.ts} +137 -150
  362. package/src/apps/{webhooks.ts → data/routes/webhooks.ts} +83 -81
  363. package/src/apps/docs/App.ts +136 -0
  364. package/src/apps/docs/index.ts +1 -0
  365. package/src/apps/index.ts +3 -0
  366. package/src/apps/metadata.ts +23 -0
  367. package/src/apps/platform/App.ts +130 -0
  368. package/src/apps/platform/index.ts +1 -0
  369. package/src/apps/platform/routes/api-keys.test.ts +330 -0
  370. package/src/apps/platform/routes/api-keys.ts +427 -0
  371. package/src/apps/platform/routes/invitations.test.ts +361 -0
  372. package/src/apps/platform/routes/invitations.ts +519 -0
  373. package/src/apps/platform/routes/me.test.ts +121 -0
  374. package/src/apps/platform/routes/me.ts +106 -0
  375. package/src/apps/platform/routes/members.test.ts +195 -0
  376. package/src/apps/platform/routes/members.ts +313 -0
  377. package/src/apps/platform/routes/orgs.test.ts +313 -0
  378. package/src/apps/platform/routes/orgs.ts +343 -0
  379. package/src/apps/platform/routes/projects.test.ts +181 -0
  380. package/src/apps/platform/routes/projects.ts +367 -0
  381. package/src/apps/relay/App.test.ts +484 -0
  382. package/src/apps/relay/App.ts +128 -0
  383. package/src/apps/relay/Sponsorships.test.ts +375 -0
  384. package/src/apps/relay/Sponsorships.ts +305 -0
  385. package/src/apps/relay/index.ts +2 -0
  386. package/src/{Cli.test.ts → cli/Cli.test.ts} +10 -9
  387. package/src/{Cli.ts → cli/Cli.ts} +16 -9
  388. package/src/cli/bin.ts +4 -0
  389. package/src/cli/commands/admin.test.ts +71 -0
  390. package/src/cli/commands/admin.ts +60 -0
  391. package/src/cloudflare.ts +10 -0
  392. package/src/db/Db.test.ts +84 -0
  393. package/src/db/Db.ts +542 -0
  394. package/src/db/Schema.ts +301 -0
  395. package/src/db/tables/invitations.ts +198 -0
  396. package/src/db/tables/memberships.ts +208 -0
  397. package/src/db/tables/organizations.test.ts +160 -0
  398. package/src/db/tables/organizations.ts +232 -0
  399. package/src/db/tables/projects.ts +107 -0
  400. package/src/db/tables/sponsoredTransactions.test.ts +218 -0
  401. package/src/db/tables/sponsoredTransactions.ts +236 -0
  402. package/src/db/tables/users.ts +85 -0
  403. package/src/db/tables/verifiedTokens.ts +111 -0
  404. package/src/db/tables/webhookDeliveries.ts +159 -0
  405. package/src/db/tables/webhookSubscriptions.ts +358 -0
  406. package/src/handlers/internal/executionError.ts +207 -0
  407. package/src/handlers/internal/multisig.test.ts +818 -0
  408. package/src/handlers/internal/multisig.ts +823 -0
  409. package/src/handlers/internal/requestListener.ts +273 -0
  410. package/src/handlers/internal/sponsorship.ts +245 -0
  411. package/src/handlers/internal/types.ts +378 -0
  412. package/src/handlers/internal/utils.ts +153 -0
  413. package/src/handlers/relay.test.ts +2035 -0
  414. package/src/handlers/relay.ts +1623 -0
  415. package/src/index.ts +4 -2
  416. package/src/internal/Auth.test.ts +337 -21
  417. package/src/internal/Auth.ts +783 -44
  418. package/src/internal/Cursor.test.ts +3 -2
  419. package/src/internal/Log.test.ts +1 -1
  420. package/src/internal/Log.ts +11 -3
  421. package/src/internal/MetricSink.ts +1 -1
  422. package/src/internal/OpenApi.ts +0 -59
  423. package/src/internal/RateLimit.test.ts +1 -1
  424. package/src/internal/RpcSchema.ts +10 -0
  425. package/src/internal/Store.test.ts +150 -10
  426. package/src/internal/Store.ts +136 -11
  427. package/src/internal/Tidx.local.test.ts +1 -1
  428. package/src/internal/VerifiedTokens.test.ts +99 -115
  429. package/src/internal/VerifiedTokens.ts +123 -175
  430. package/src/internal/WebhookDestination.test.ts +1 -1
  431. package/src/internal/Webhooks.test.ts +111 -111
  432. package/src/internal/Webhooks.ts +103 -221
  433. package/dist/ApiKeySource.d.ts +0 -166
  434. package/dist/ApiKeySource.d.ts.map +0 -1
  435. package/dist/ApiKeySource.js +0 -234
  436. package/dist/ApiKeySource.js.map +0 -1
  437. package/dist/Cli.d.ts.map +0 -1
  438. package/dist/Cli.js +0 -13
  439. package/dist/Cli.js.map +0 -1
  440. package/dist/apps/activities.js.map +0 -1
  441. package/dist/apps/addresses.js.map +0 -1
  442. package/dist/apps/balances.d.ts.map +0 -1
  443. package/dist/apps/balances.js.map +0 -1
  444. package/dist/apps/blocks.d.ts.map +0 -1
  445. package/dist/apps/blocks.js.map +0 -1
  446. package/dist/apps/coingecko.d.ts.map +0 -1
  447. package/dist/apps/coingecko.js.map +0 -1
  448. package/dist/apps/exchanges.d.ts.map +0 -1
  449. package/dist/apps/exchanges.js.map +0 -1
  450. package/dist/apps/fee-amm.d.ts.map +0 -1
  451. package/dist/apps/fee-amm.js.map +0 -1
  452. package/dist/apps/indexer.d.ts.map +0 -1
  453. package/dist/apps/indexer.js.map +0 -1
  454. package/dist/apps/receipts.d.ts.map +0 -1
  455. package/dist/apps/receipts.js.map +0 -1
  456. package/dist/apps/rpc.d.ts.map +0 -1
  457. package/dist/apps/rpc.js.map +0 -1
  458. package/dist/apps/tokenlist.d.ts.map +0 -1
  459. package/dist/apps/tokenlist.js.map +0 -1
  460. package/dist/apps/tokens.d.ts.map +0 -1
  461. package/dist/apps/tokens.js.map +0 -1
  462. package/dist/apps/transactions.d.ts.map +0 -1
  463. package/dist/apps/transactions.js.map +0 -1
  464. package/dist/apps/transfers.d.ts.map +0 -1
  465. package/dist/apps/transfers.js.map +0 -1
  466. package/dist/apps/verified-tokens.d.ts.map +0 -1
  467. package/dist/apps/verified-tokens.js.map +0 -1
  468. package/dist/apps/webhooks.js.map +0 -1
  469. package/dist/internal/OpenApi.fonts.d.ts.map +0 -1
  470. package/dist/internal/OpenApi.fonts.js.map +0 -1
  471. package/dist/internal/OpenApi.logo.d.ts.map +0 -1
  472. package/dist/internal/OpenApi.logo.js.map +0 -1
  473. package/seeds/31318/icons/0x20c0000000000000000000000000000000000000.svg +0 -1
  474. package/seeds/31318/icons/0x20c0000000000000000000000000000000000001.svg +0 -1
  475. package/seeds/31318/icons/0x20c0000000000000000000000000000000000002.svg +0 -1
  476. package/seeds/31318/icons/0x20c0000000000000000000000000000000000003.svg +0 -1
  477. package/seeds/31318/verified-tokens.json +0 -30
  478. package/seeds/4217/icons/0x20c0000000000000000000000000000000000000.svg +0 -25
  479. package/seeds/4217/icons/0x20c000000000000000000000048c8f36df1c9a4a.svg +0 -1
  480. package/seeds/4217/icons/0x20c0000000000000000000000520792dcccccccc.svg +0 -1
  481. package/seeds/4217/icons/0x20c0000000000000000000000a6da882d075a4c3.svg +0 -4
  482. package/seeds/4217/icons/0x20c000000000000000000000111111111e910f0f.svg +0 -30
  483. package/seeds/4217/icons/0x20c00000000000000000000014f22ca97301eb73.svg +0 -1
  484. package/seeds/4217/icons/0x20c0000000000000000000001621e21f71cf12fb.svg +0 -1
  485. package/seeds/4217/icons/0x20c0000000000000000000002f52d5cc21a3207b.svg +0 -1
  486. package/seeds/4217/icons/0x20c0000000000000000000003158081efd85bfc2.svg +0 -11
  487. package/seeds/4217/icons/0x20c0000000000000000000003554d28269e0f3c2.svg +0 -1
  488. package/seeds/4217/icons/0x20c000000000000000000000383a23bacb546ab9.svg +0 -1
  489. package/seeds/4217/icons/0x20c00000000000000000000042109aef2f8b28e1.png +0 -0
  490. package/seeds/4217/icons/0x20c0000000000000000000005c0bac7cef389a11.svg +0 -1
  491. package/seeds/4217/icons/0x20c0000000000000000000006fd9a167923ba194.png +0 -0
  492. package/seeds/4217/icons/0x20c0000000000000000000007f7ba549dd0251b9.svg +0 -1
  493. package/seeds/4217/icons/0x20c0000000000000000000008191667423f70e67.svg +0 -5
  494. package/seeds/4217/icons/0x20c0000000000000000000008ee4fcff88888888.svg +0 -1
  495. package/seeds/4217/icons/0x20c0000000000000000000009a4a4b17e0dc6651.svg +0 -1
  496. package/seeds/4217/icons/0x20c000000000000000000000ab02d39df30bd17e.svg +0 -1
  497. package/seeds/4217/icons/0x20c000000000000000000000ae247a1130450f09.svg +0 -1
  498. package/seeds/4217/icons/0x20c000000000000000000000aeed2ec36a54d0e5.svg +0 -1
  499. package/seeds/4217/icons/0x20c000000000000000000000b9537d11c60e8b50.svg +0 -1
  500. package/seeds/4217/icons/0x20c000000000000000000000bd95bfb69fbe6ce3.svg +0 -1
  501. package/seeds/4217/icons/0x20c000000000000000000000c412ec89d0c08be5.svg +0 -1
  502. package/seeds/4217/icons/0x20c000000000000000000000f047dd7018e50367.png +0 -0
  503. package/seeds/4217/verified-tokens.json +0 -177
  504. package/seeds/42431/icons/0x20c0000000000000000000000000000000000000.svg +0 -25
  505. package/seeds/42431/icons/0x20c0000000000000000000000000000000000001.svg +0 -1
  506. package/seeds/42431/icons/0x20c0000000000000000000000000000000000002.svg +0 -1
  507. package/seeds/42431/icons/0x20c0000000000000000000000000000000000003.svg +0 -1
  508. package/seeds/42431/icons/0x20c0000000000000000000009e8d7eb59b783726.svg +0 -1
  509. package/seeds/42431/icons/0x20c000000000000000000000d72572838bbee59c.svg +0 -1
  510. package/seeds/42431/verified-tokens.json +0 -44
  511. package/seeds/openrpc/README.md +0 -27
  512. package/seeds/openrpc/execution-apis.json +0 -8989
  513. package/src/ApiKeySource.test-d.ts +0 -11
  514. package/src/ApiKeySource.test.ts +0 -295
  515. package/src/ApiKeySource.ts +0 -303
  516. /package/src/{internal/OpenApi.fonts.ts → apps/docs/fonts.ts} +0 -0
  517. /package/src/{internal/OpenApi.logo.ts → apps/docs/logo.ts} +0 -0
@@ -90,11 +90,12 @@ describe('Cursor', () => {
90
90
  })
91
91
 
92
92
  test('falls back to an OR expansion for a mixed-direction keyset', () => {
93
+ const holder = `'0x${'ab'.repeat(20)}'`
93
94
  expect(
94
95
  Cursor.keyset([
95
96
  { literal: '1000', name: 'balance', order: 'desc' },
96
- { literal: "'0xabc'", name: 'holder', order: 'asc' },
97
+ { literal: holder, name: 'holder', order: 'asc' },
97
98
  ]),
98
- ).toBe("(balance <= 1000 AND ((balance < 1000) OR (balance = 1000 AND holder > '0xabc')))")
99
+ ).toBe(`(balance <= 1000 AND ((balance < 1000) OR (balance = 1000 AND holder > ${holder})))`)
99
100
  })
100
101
  })
@@ -92,7 +92,7 @@ describe('middleware', () => {
92
92
  api.use('*', async (c, next) => {
93
93
  c.set('principal', {
94
94
  id: 'ip:203.0.113.7',
95
- payment: { payer: '0xpayer', reason: 'public_over_quota', type: 'mpp' },
95
+ payment: { payer: `0x${'aa'.repeat(20)}`, reason: 'public_over_quota', type: 'mpp' },
96
96
  type: 'public',
97
97
  })
98
98
  await next()
@@ -42,17 +42,21 @@ export type Entry = {
42
42
  path: string
43
43
  /** MPP payment reason when the request was paid per call. */
44
44
  payment?: Auth.Payment['reason'] | undefined
45
- /** Caller identity. Raw API-key tokens are never logged. */
45
+ /** Caller identity. Raw API-key tokens and the super admin secret are never logged. */
46
46
  principal?:
47
47
  | {
48
+ /** Attributed super admin identity for super admin callers. */
49
+ actor?: string | undefined
48
50
  /** Key environment for API-key callers. */
49
51
  environment?: ApiKey.ApiKey['environment'] | undefined
50
- /** Principal id: the API key id, or the public quota identity. */
52
+ /** Principal id: the API key id, the public quota identity, the user id, or `super_admin`. */
51
53
  id: string
52
54
  /** Owning organization id for API-key callers. */
53
55
  orgId?: string | undefined
56
+ /** Owning project id for project-attributed API-key callers. */
57
+ projectId?: string | undefined
54
58
  /** Principal kind. */
55
- type: 'api_key' | 'public'
59
+ type: 'api_key' | 'public' | 'session' | 'super_admin'
56
60
  }
57
61
  | undefined
58
62
  /** Raw query string (no leading `?`) with API-key tokens redacted, when present. */
@@ -146,9 +150,13 @@ export function middleware<environment extends Environment = Environment>(
146
150
  ? {}
147
151
  : {
148
152
  principal: {
153
+ ...(principal.type === 'super_admin' ? { actor: principal.actor } : {}),
149
154
  ...(principal.type === 'api_key' ? { environment: principal.environment } : {}),
150
155
  id: principal.id,
151
156
  ...(principal.type === 'api_key' ? { orgId: principal.orgId } : {}),
157
+ ...(principal.type === 'api_key' && principal.projectId !== undefined
158
+ ? { projectId: principal.projectId }
159
+ : {}),
152
160
  type: principal.type,
153
161
  },
154
162
  }),
@@ -60,7 +60,7 @@ type RouteTags = {
60
60
  cache: 'hit' | 'miss'
61
61
  method: string
62
62
  principal_environment: 'none' | 'production' | 'sandbox'
63
- principal_type: 'api_key' | 'public' | 'unknown'
63
+ principal_type: 'api_key' | 'public' | 'session' | 'super_admin' | 'unknown'
64
64
  route: string
65
65
  }
66
66
 
@@ -2,7 +2,6 @@ import type { Context, Env, MiddlewareHandler, ValidationTargets } from 'hono'
2
2
  import type { HasRequiredKeys } from 'hono/utils/types'
3
3
  import { resolver, type ResponsesWithResolver, validator } from 'hono-openapi'
4
4
  import * as z from 'zod/mini'
5
- import { pilatFontFaces } from './OpenApi.fonts.js'
6
5
  import * as Response from './Response.js'
7
6
  import * as Schema from './Schema.js'
8
7
 
@@ -50,64 +49,6 @@ export {
50
49
  validator,
51
50
  } from 'hono-openapi'
52
51
 
53
- // Theme-aware Tempo API wordmark for the docs header (`vocs.logoUrl`), inlined
54
- // as SVG data URIs. See {@link file://./OpenApi.logo.ts}.
55
- export { logoUrl } from './OpenApi.logo.js'
56
-
57
- /**
58
- * Custom CSS injected into the standalone reference's shell `<head>` (via
59
- * `Handler.openApi(_, { css })`), after the Vocs design-system styles so it
60
- * overrides them:
61
- *
62
- * - Page/surface backgrounds use Tempo's near-black dark palette and pure-white
63
- * light palette via `light-dark(<light>, <dark>)`.
64
- * - The dark border color is darkened (the light theme keeps its default).
65
- * - Every corner is squared. The universal `!important` rule beats hard-coded
66
- * `border-radius` values and inline styles (e.g. the Scalar playground), which
67
- * overriding the radius scale variables alone would miss.
68
- * - The default sans/body font is Pilat (Vocs reads `--vocs-font-family`); the
69
- * webfont is embedded via {@link pilatFontFaces}. Body stays at the default
70
- * weight (400); headings are pinned to 500, which renders Pilat Demi (the
71
- * family has no 500 cut, so Demi covers the 500-600 range — see
72
- * {@link pilatFontFaces}).
73
- */
74
- export const css = `${pilatFontFaces}
75
-
76
- :root {
77
- --vocs-font-family: "Pilat", Arial, Helvetica, sans-serif;
78
- --vocs-background-color-primary: light-dark(#fafafa, #0a0a0a);
79
- --vocs-background-color-surface: light-dark(#fff, #121212);
80
- }
81
-
82
- :is(h1, h2, h3, h4, h5, h6)[data-v] {
83
- font-weight: 500;
84
- }
85
-
86
- /* Nudge the header logo a touch larger than the default max-h-7 (1.75rem). */
87
- [data-v-logo-image] {
88
- max-height: 2rem;
89
- }
90
-
91
- /* Sidebar leaf items render Regular (the nav's 450 default otherwise resolves to
92
- * Demi via the 500-600 range). Group labels ([data-v-sidebar-section-header])
93
- * keep their heavier weight. */
94
- [data-v-sidebar-item] {
95
- font-weight: 400;
96
- }
97
-
98
- [data-vocs-theme='dark'] {
99
- --vocs-color-gray4: oklch(20% 0 0);
100
- --vocs-color-gray5: oklch(0.23 0 0);
101
- --vocs-border-color-primary: oklch(0.25 0 0);
102
- --vocs-border-color-secondary: oklch(0.25 0 0);
103
- }
104
-
105
- *,
106
- *::before,
107
- *::after {
108
- border-radius: 0 !important;
109
- }`
110
-
111
52
  /**
112
53
  * Standard machine-readable `error.code` values per status, shared API-wide.
113
54
  * The reference enumerates `error.code` to these so generated clients narrow it
@@ -1,4 +1,4 @@
1
- import { RateLimit, Store } from 'cadent'
1
+ import { RateLimit, Store } from 'tapimo'
2
2
 
3
3
  describe('memory', () => {
4
4
  test('tracks fixed-window quota in a store', async () => {
@@ -0,0 +1,10 @@
1
+ import type { RpcSchema } from 'ox'
2
+
3
+ /**
4
+ * Ox-compatible JSON-RPC schema union used to type the relay handler's incoming
5
+ * requests (`RpcRequest.RpcRequest<Ox>`). The relay switches on `request.method`
6
+ * string literals and casts params per method, so a permissive schema whose
7
+ * `params` is an array is sufficient — the full method-by-method schema union
8
+ * from the accounts SDK is not needed here.
9
+ */
10
+ export type Ox = RpcSchema.Generic<string, readonly unknown[]>
@@ -1,4 +1,4 @@
1
- import { Store } from 'cadent'
1
+ import { Store } from 'tapimo'
2
2
 
3
3
  describe('from', () => {
4
4
  const ops = {
@@ -68,7 +68,89 @@ describe('increment', () => {
68
68
  })
69
69
  })
70
70
 
71
+ describe('update', () => {
72
+ test('falls back to a get + put read-modify-write without a native swap', async () => {
73
+ const cells = new Map<string, string>()
74
+ const store = Store.from({
75
+ async delete(key) {
76
+ cells.delete(key)
77
+ },
78
+ async get(key) {
79
+ return cells.get(key) ?? null
80
+ },
81
+ async list() {
82
+ return { keys: [] }
83
+ },
84
+ async put(key, value) {
85
+ cells.set(key, value)
86
+ },
87
+ })
88
+
89
+ expect(await Store.update(store, 'value', (current) => `${current ?? 'start'}+`)).toMatchInlineSnapshot(`"start+"`) // prettier-ignore
90
+ expect(await Store.update(store, 'value', (current) => `${current}+`)).toMatchInlineSnapshot(`"start++"`) // prettier-ignore
91
+ })
92
+
93
+ test('retries the transform when the compare-and-swap loses', async () => {
94
+ const store = Store.memory()
95
+ await store.put('value', 'a')
96
+
97
+ // Interleave a competing write between the read and the swap once.
98
+ let raced = false
99
+ const get = store.get.bind(store)
100
+ store.get = async (key) => {
101
+ const current = await get(key)
102
+ if (!raced) {
103
+ raced = true
104
+ await store.put('value', 'b')
105
+ }
106
+ return current
107
+ }
108
+
109
+ const transforms: (null | string)[] = []
110
+ const written = await Store.update(store, 'value', (current) => {
111
+ transforms.push(current)
112
+ return `${current}c`
113
+ })
114
+
115
+ expect(written).toMatchInlineSnapshot(`"bc"`)
116
+ expect(transforms).toMatchInlineSnapshot(`
117
+ [
118
+ "a",
119
+ "b",
120
+ ]
121
+ `)
122
+ })
123
+
124
+ test('fails after exhausting compare-and-swap attempts', async () => {
125
+ const store = Store.memory()
126
+ await store.put('value', 'a')
127
+
128
+ // Every read is immediately invalidated by a competing write.
129
+ const get = store.get.bind(store)
130
+ store.get = async (key) => {
131
+ const current = await get(key)
132
+ await store.put('value', `${current}!`)
133
+ return current
134
+ }
135
+
136
+ await expect(
137
+ Store.update(store, 'value', (current) => `${current}c`, { attempts: 2 }),
138
+ ).rejects.toThrowErrorMatchingInlineSnapshot(
139
+ `[Store.UpdateContentionError: Store update on "value" lost 2 compare-and-swap attempts.]`,
140
+ )
141
+ })
142
+ })
143
+
71
144
  describe('memory', () => {
145
+ test('swaps atomically against the expected value', async () => {
146
+ const store = Store.memory()
147
+
148
+ expect(await store.swap!('value', null, 'first')).toMatchInlineSnapshot(`true`)
149
+ expect(await store.swap!('value', null, 'clobber')).toMatchInlineSnapshot(`false`)
150
+ expect(await store.swap!('value', 'first', 'second')).toMatchInlineSnapshot(`true`)
151
+ expect(await store.get('value')).toMatchInlineSnapshot(`"second"`)
152
+ })
153
+
72
154
  test('treats expired entries as absent', async () => {
73
155
  const store = Store.memory()
74
156
 
@@ -148,6 +230,8 @@ describe('cloudflareKv', () => {
148
230
  .filter((key) => !options.prefix || key.startsWith(options.prefix))
149
231
  .sort()
150
232
  .map((name) => ({ name })),
233
+ list_complete: true,
234
+ cursor: '',
151
235
  }
152
236
  },
153
237
  async put(key, value, options) {
@@ -190,9 +274,69 @@ describe('cloudflareKv', () => {
190
274
  `[RangeError: Cloudflare KV expirationTtl must be at least 60 seconds]`,
191
275
  )
192
276
  })
277
+
278
+ test('lists every Cloudflare Workers KV page', async () => {
279
+ const calls: unknown[] = []
280
+ const namespace = {
281
+ async delete() {},
282
+ async get() {
283
+ return null
284
+ },
285
+ async list(options = {}) {
286
+ calls.push(options)
287
+ if (options.cursor === 'page-2')
288
+ return {
289
+ keys: [{ name: 'secret:2' }],
290
+ list_complete: true,
291
+ cursor: '',
292
+ }
293
+ return {
294
+ keys: [{ name: 'secret:1' }],
295
+ list_complete: false,
296
+ cursor: 'page-2',
297
+ }
298
+ },
299
+ async put() {},
300
+ } satisfies Store.cloudflareKv.Namespace
301
+ const store = Store.cloudflareKv(namespace)
302
+
303
+ expect(await store.list({ prefix: 'secret:' })).toMatchInlineSnapshot(`
304
+ {
305
+ "keys": [
306
+ {
307
+ "name": "secret:1",
308
+ },
309
+ {
310
+ "name": "secret:2",
311
+ },
312
+ ],
313
+ }
314
+ `)
315
+ expect(calls).toMatchInlineSnapshot(`
316
+ [
317
+ {
318
+ "prefix": "secret:",
319
+ },
320
+ {
321
+ "cursor": "page-2",
322
+ "prefix": "secret:",
323
+ },
324
+ ]
325
+ `)
326
+ })
193
327
  })
194
328
 
195
329
  describe('durableObject', () => {
330
+ // The stub contract requires native `increment` and `swap` (our DO class
331
+ // implements them); derive them for the in-memory fakes.
332
+ function stubFor(backing: Store.State): Store.durableObject.Stub {
333
+ return {
334
+ ...backing,
335
+ increment: (key, options) => Store.increment(backing, key, options),
336
+ swap: (key, expected, next, options) => backing.swap!(key, expected, next, options),
337
+ }
338
+ }
339
+
196
340
  test('adapts a Durable Object namespace without caching stubs', async () => {
197
341
  const calls: string[] = []
198
342
  const backing = Store.memory({
@@ -201,12 +345,7 @@ describe('durableObject', () => {
201
345
  const namespace = {
202
346
  getByName(name) {
203
347
  calls.push(name)
204
- // The stub contract requires a native `increment` (our DO class
205
- // implements it); derive one for the in-memory fake.
206
- return {
207
- ...backing,
208
- increment: (key, options) => Store.increment(backing, key, options),
209
- }
348
+ return stubFor(backing)
210
349
  },
211
350
  } satisfies Store.durableObject.Namespace
212
351
  const store = Store.durableObject(namespace, { name: 'tempo-api' })
@@ -250,6 +389,7 @@ describe('durableObject', () => {
250
389
  increment: (key, options) => Store.increment(backing, key, options),
251
390
  list: (options) => backing.list(options),
252
391
  put: (key, value, options) => backing.put(key, value, options),
392
+ swap: (key, expected, next, options) => backing.swap!(key, expected, next, options),
253
393
  } satisfies Store.durableObject.Stub
254
394
  return new Proxy(stub, {
255
395
  get(target, method: string) {
@@ -294,7 +434,7 @@ describe('durableObject', () => {
294
434
  getByName() {
295
435
  stubs += 1
296
436
  return {
297
- ...backing,
437
+ ...stubFor(backing),
298
438
  increment: (key, options) => {
299
439
  if (failures > 0) {
300
440
  failures -= 1
@@ -317,7 +457,7 @@ describe('durableObject', () => {
317
457
  const namespace = {
318
458
  getByName() {
319
459
  return {
320
- ...Store.memory(),
460
+ ...stubFor(Store.memory()),
321
461
  increment: () => {
322
462
  attempts += 1
323
463
  throw Object.assign(new Error('Network connection lost.'), { retryable: true })
@@ -339,7 +479,7 @@ describe('durableObject', () => {
339
479
  ({
340
480
  getByName() {
341
481
  return {
342
- ...Store.memory(),
482
+ ...stubFor(Store.memory()),
343
483
  increment: () => {
344
484
  attempts += 1
345
485
  throw error
@@ -1,3 +1,5 @@
1
+ import { Json } from 'ox'
2
+
1
3
  /** Minimal string store contract used by Tempo API internals. */
2
4
  export type Store = {
3
5
  /** Deletes a key. */
@@ -26,6 +28,22 @@ export type Store = {
26
28
  }>
27
29
  /** Writes a string value, optionally with a time-to-live. */
28
30
  put(key: string, value: string, options?: Store.PutOptions): Promise<void>
31
+ /**
32
+ * Optional atomic compare-and-swap: writes `next` only when the current
33
+ * value equals `expected` (`null` = only-if-absent) and reports whether the
34
+ * write happened. Implement when the backend can execute the compare and
35
+ * write in one place (in-memory, inside a Durable Object); consumers go
36
+ * through the {@link update} helper, which falls back to a get + put when
37
+ * this is absent.
38
+ */
39
+ swap?:
40
+ | ((
41
+ key: string,
42
+ expected: null | string,
43
+ next: string,
44
+ options?: Store.PutOptions,
45
+ ) => Promise<boolean>)
46
+ | undefined
29
47
  /**
30
48
  * Store kind discriminant. `'state'` marks an authoritative, enumerable
31
49
  * store ({@link State}); `'cache'` marks a cache (e.g. the Web Cache,
@@ -102,6 +120,50 @@ export async function increment(
102
120
  return count
103
121
  }
104
122
 
123
+ /**
124
+ * Atomically transforms a value on a store: reads the current value, applies
125
+ * `fn`, and writes the result, returning the written value.
126
+ *
127
+ * Uses the store's native {@link Store.swap} compare-and-swap when implemented,
128
+ * re-running `fn` on contention (optimistic concurrency, bounded by
129
+ * `attempts`). Otherwise falls back to a get + put read-modify-write, which
130
+ * can lose concurrent writes — fine for dev/in-process backends, not for
131
+ * contended distributed state.
132
+ */
133
+ export async function update(
134
+ store: Store,
135
+ key: string,
136
+ fn: (current: null | string) => string,
137
+ options: update.Options = {},
138
+ ): Promise<string> {
139
+ const { attempts = 8, ...put } = options
140
+
141
+ if (!store.swap) {
142
+ const next = fn(await store.get(key))
143
+ await store.put(key, next, put)
144
+ return next
145
+ }
146
+
147
+ for (let attempt = 0; attempt < attempts; attempt++) {
148
+ const current = await store.get(key)
149
+ const next = fn(current)
150
+ if (await store.swap(key, current, next, put)) return next
151
+ }
152
+ throw new UpdateContentionError(key, attempts)
153
+ }
154
+
155
+ export declare namespace update {
156
+ /** Options for {@link update}. */
157
+ type Options = Store.PutOptions & {
158
+ /**
159
+ * Maximum compare-and-swap attempts before failing with
160
+ * {@link UpdateContentionError}.
161
+ * @default 8
162
+ */
163
+ attempts?: number | undefined
164
+ }
165
+ }
166
+
105
167
  /** Creates an in-memory store with TTL support. */
106
168
  export function memory(options: memory.Options = {}): State {
107
169
  // Track value + optional expiration in one cell so `get` can lazily evict
@@ -148,6 +210,14 @@ export function memory(options: memory.Options = {}): State {
148
210
  const expiresAt = options.ttl === undefined ? undefined : Date.now() + options.ttl
149
211
  cells.set(key, { expiresAt, value })
150
212
  },
213
+ // Native swap: the compare and write are synchronous, so concurrent
214
+ // consumers cannot interleave between them.
215
+ async swap(key, expected, next, options = {}) {
216
+ if ((read(key)?.value ?? null) !== expected) return false
217
+ const expiresAt = options.ttl === undefined ? undefined : Date.now() + options.ttl
218
+ cells.set(key, { expiresAt, value: next })
219
+ return true
220
+ },
151
221
  })
152
222
  }
153
223
 
@@ -211,10 +281,23 @@ export function cloudflareKv(namespace: cloudflareKv.Namespace): State {
211
281
  return namespace.get(key)
212
282
  },
213
283
  async list(options = {}) {
214
- const result = await namespace.list(
215
- options.prefix === undefined ? undefined : { prefix: options.prefix },
216
- )
217
- return { keys: result.keys.map(({ name }) => ({ name })) }
284
+ const keys: { name: string }[] = []
285
+ let cursor: string | undefined
286
+
287
+ for (;;) {
288
+ const result = await namespace.list(
289
+ options.prefix === undefined && cursor === undefined
290
+ ? undefined
291
+ : {
292
+ ...(options.prefix === undefined ? {} : { prefix: options.prefix }),
293
+ ...(cursor === undefined ? {} : { cursor }),
294
+ },
295
+ )
296
+ keys.push(...result.keys.map(({ name }) => ({ name })))
297
+ if (result.list_complete) return { keys }
298
+ if (result.cursor === undefined) throw new Error('Cloudflare KV list is missing a cursor.')
299
+ cursor = result.cursor
300
+ }
218
301
  },
219
302
  async put(key, value, options = {}) {
220
303
  const expirationTtl = cloudflareKvExpirationTtl(options.ttl)
@@ -238,13 +321,19 @@ export declare namespace cloudflareKv {
238
321
  /** Lists keys. */
239
322
  list(options?: {
240
323
  /** Restrict results to keys with this prefix. */
241
- prefix?: string | null | undefined
324
+ prefix?: string | undefined
325
+ /** Continue from a previous page. */
326
+ cursor?: string | undefined
242
327
  }): Promise<{
243
328
  /** Matching keys. */
244
329
  keys: readonly {
245
330
  /** Key name. */
246
331
  name: string
247
332
  }[]
333
+ /** Whether every matching key was returned. */
334
+ list_complete: boolean
335
+ /** Cursor for the next page. */
336
+ cursor?: string | undefined
248
337
  }>
249
338
  /** Writes a text value. */
250
339
  put(
@@ -302,6 +391,19 @@ function durableObjectStorage(storage: durableObject.Storage): State {
302
391
 
303
392
  await storage.put(key, durableObjectValue(value, options))
304
393
  },
394
+ // Native swap: the object is single-threaded and its input gate holds
395
+ // other events while storage operations are in flight, so the compare and
396
+ // write cannot interleave with another request.
397
+ async swap(key, expected, next, options = {}) {
398
+ const current = (await durableObjectRead(storage, key, await storage.get(key)))?.value ?? null
399
+ if (current !== expected) return false
400
+ if (options.ttl !== undefined && options.ttl <= 0) {
401
+ await storage.delete(key)
402
+ return true
403
+ }
404
+ await storage.put(key, durableObjectValue(next, options))
405
+ return true
406
+ },
305
407
  })
306
408
  }
307
409
 
@@ -341,12 +443,20 @@ export declare namespace durableObject {
341
443
  /**
342
444
  * Minimal Durable Object RPC stub shape used by this adapter — just the store
343
445
  * operations (no `type` discriminant, which an RPC stub doesn't carry).
344
- * `increment` is required: the stub mirrors the `tapimo/cloudflare`
345
- * `DurableObject` class, whose RPC surface implements it.
446
+ * `increment` and `swap` are required: the stub mirrors the
447
+ * `tapimo/cloudflare` `DurableObject` class, whose RPC surface implements
448
+ * them.
346
449
  */
347
- type Stub = Omit<Store, 'increment' | 'type'> & {
450
+ type Stub = Omit<Store, 'increment' | 'swap' | 'type'> & {
348
451
  /** Atomically increments an integer counter inside the object. */
349
452
  increment(key: string, options?: Store.PutOptions): Promise<number>
453
+ /** Atomically compares-and-swaps a value inside the object. */
454
+ swap(
455
+ key: string,
456
+ expected: null | string,
457
+ next: string,
458
+ options?: Store.PutOptions,
459
+ ): Promise<boolean>
350
460
  }
351
461
  }
352
462
 
@@ -364,7 +474,7 @@ export async function memoize<value>(
364
474
  const raw = await store.get(key)
365
475
  if (raw) {
366
476
  try {
367
- return JSON.parse(raw) as value
477
+ return Json.parse(raw) as value
368
478
  } catch {
369
479
  // Corrupt entries are treated as misses so the next call refreshes.
370
480
  }
@@ -379,7 +489,7 @@ export async function memoize<value>(
379
489
  // fetch is still in flight.
380
490
  const flight = (async () => {
381
491
  const value = await fetch()
382
- await store.put(key, JSON.stringify(value), { ttl })
492
+ await store.put(key, Json.stringify(value), { ttl })
383
493
  return value
384
494
  })()
385
495
  flights.set(key, flight)
@@ -450,6 +560,12 @@ function durableObjectStub(
450
560
  async put(key, value, options) {
451
561
  await durableObjectRetry(() => stub(key).put(key, value, options))
452
562
  },
563
+ // Native RPC swap: the compare-and-write runs inside the object. A lost
564
+ // response retried after a successful write compares stale and returns
565
+ // false, which optimistic callers already handle as contention.
566
+ async swap(key, expected, next, options) {
567
+ return durableObjectRetry(() => stub(key).swap(key, expected, next, options))
568
+ },
453
569
  })
454
570
  }
455
571
 
@@ -482,7 +598,7 @@ function cloudflareKvExpirationTtl(ttl: number | undefined) {
482
598
  return seconds
483
599
  }
484
600
 
485
- const durableObjectValueType = 'cadent:store:v1'
601
+ const durableObjectValueType = 'tempo-api:store:v1'
486
602
 
487
603
  type DurableObjectValue = {
488
604
  expiresAt?: number | undefined
@@ -524,3 +640,12 @@ function isDurableObjectValue(value: unknown): value is DurableObjectValue {
524
640
  typeof value.expiresAt === 'number')
525
641
  )
526
642
  }
643
+
644
+ /** Thrown when {@link update} exhausts its compare-and-swap attempts. */
645
+ export class UpdateContentionError extends Error {
646
+ override name = 'Store.UpdateContentionError'
647
+
648
+ constructor(key: string, attempts: number) {
649
+ super(`Store update on "${key}" lost ${attempts} compare-and-swap attempts.`)
650
+ }
651
+ }
@@ -1,6 +1,6 @@
1
1
  import * as TestApp from '../../test/App.js'
2
2
  import * as Runtime from '../../test/runtime.js'
3
- import * as Indexer from '../apps/indexer.js'
3
+ import * as Indexer from '../apps/data/routes/indexer.js'
4
4
 
5
5
  describe('local TIDX runtime', () => {
6
6
  test.runIf(Runtime.get().mode === 'localnet')(