tapimo 0.0.2 → 0.1.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 (265) hide show
  1. package/README.md +11 -2
  2. package/dist/ApiKey.d.ts +12 -11
  3. package/dist/ApiKey.d.ts.map +1 -1
  4. package/dist/ApiKey.js +10 -12
  5. package/dist/ApiKey.js.map +1 -1
  6. package/dist/ApiKeys.d.ts +139 -0
  7. package/dist/ApiKeys.d.ts.map +1 -0
  8. package/dist/ApiKeys.js +166 -0
  9. package/dist/ApiKeys.js.map +1 -0
  10. package/dist/App.d.ts +920 -910
  11. package/dist/App.d.ts.map +1 -1
  12. package/dist/App.js +36 -12
  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 +4 -1
  19. package/dist/Scope.d.ts.map +1 -1
  20. package/dist/Scope.js +5 -1
  21. package/dist/Scope.js.map +1 -1
  22. package/dist/Webhooks.d.ts +3 -5
  23. package/dist/Webhooks.d.ts.map +1 -1
  24. package/dist/Webhooks.js +9 -2
  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 +98 -209
  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 +7 -5
  35. package/dist/admin/apps/api-keys.d.ts.map +1 -1
  36. package/dist/admin/apps/api-keys.js +8 -9
  37. package/dist/admin/apps/api-keys.js.map +1 -1
  38. package/dist/admin/apps/scopes.d.ts +3 -0
  39. package/dist/admin/apps/scopes.d.ts.map +1 -1
  40. package/dist/admin/apps/verified-tokens.d.ts +101 -215
  41. package/dist/admin/apps/verified-tokens.d.ts.map +1 -1
  42. package/dist/admin/apps/verified-tokens.js +17 -38
  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/balances.d.ts +2 -2
  47. package/dist/apps/receipts.d.ts +43 -43
  48. package/dist/apps/tokens.d.ts +454 -454
  49. package/dist/apps/transactions.d.ts +43 -43
  50. package/dist/apps/transfers.d.ts +29 -29
  51. package/dist/apps/webhooks.d.ts +5 -2
  52. package/dist/apps/webhooks.d.ts.map +1 -1
  53. package/dist/apps/webhooks.js +38 -34
  54. package/dist/apps/webhooks.js.map +1 -1
  55. package/dist/{Cli.d.ts → cli/Cli.d.ts} +13 -2
  56. package/dist/cli/Cli.d.ts.map +1 -0
  57. package/dist/cli/Cli.js +19 -0
  58. package/dist/cli/Cli.js.map +1 -0
  59. package/dist/cli/bin.d.ts +3 -0
  60. package/dist/cli/bin.d.ts.map +1 -0
  61. package/dist/cli/bin.js +4 -0
  62. package/dist/cli/bin.js.map +1 -0
  63. package/dist/cli/commands/admin.d.ts +15 -0
  64. package/dist/cli/commands/admin.d.ts.map +1 -0
  65. package/dist/cli/commands/admin.js +60 -0
  66. package/dist/cli/commands/admin.js.map +1 -0
  67. package/dist/cloudflare.d.ts +7 -0
  68. package/dist/cloudflare.d.ts.map +1 -1
  69. package/dist/cloudflare.js +9 -0
  70. package/dist/cloudflare.js.map +1 -1
  71. package/dist/db/Db.d.ts +103 -0
  72. package/dist/db/Db.d.ts.map +1 -0
  73. package/dist/db/Db.js +274 -0
  74. package/dist/db/Db.js.map +1 -0
  75. package/dist/db/Schema.d.ts +88 -0
  76. package/dist/db/Schema.d.ts.map +1 -0
  77. package/dist/db/Schema.js +130 -0
  78. package/dist/db/Schema.js.map +1 -0
  79. package/dist/db/tables/organizations.d.ts +79 -0
  80. package/dist/db/tables/organizations.d.ts.map +1 -0
  81. package/dist/db/tables/organizations.js +81 -0
  82. package/dist/db/tables/organizations.js.map +1 -0
  83. package/dist/db/tables/verifiedTokens.d.ts +53 -0
  84. package/dist/db/tables/verifiedTokens.d.ts.map +1 -0
  85. package/dist/db/tables/verifiedTokens.js +74 -0
  86. package/dist/db/tables/verifiedTokens.js.map +1 -0
  87. package/dist/db/tables/webhookDeliveries.d.ts +71 -0
  88. package/dist/db/tables/webhookDeliveries.d.ts.map +1 -0
  89. package/dist/db/tables/webhookDeliveries.js +119 -0
  90. package/dist/db/tables/webhookDeliveries.js.map +1 -0
  91. package/dist/db/tables/webhookSubscriptions.d.ts +158 -0
  92. package/dist/db/tables/webhookSubscriptions.d.ts.map +1 -0
  93. package/dist/db/tables/webhookSubscriptions.js +272 -0
  94. package/dist/db/tables/webhookSubscriptions.js.map +1 -0
  95. package/dist/handlers/internal/executionError.d.ts +25 -0
  96. package/dist/handlers/internal/executionError.d.ts.map +1 -0
  97. package/dist/handlers/internal/executionError.js +184 -0
  98. package/dist/handlers/internal/executionError.js.map +1 -0
  99. package/dist/handlers/internal/multisig.d.ts +237 -0
  100. package/dist/handlers/internal/multisig.d.ts.map +1 -0
  101. package/dist/handlers/internal/multisig.js +567 -0
  102. package/dist/handlers/internal/multisig.js.map +1 -0
  103. package/dist/handlers/internal/requestListener.d.ts +124 -0
  104. package/dist/handlers/internal/requestListener.d.ts.map +1 -0
  105. package/dist/handlers/internal/requestListener.js +173 -0
  106. package/dist/handlers/internal/requestListener.js.map +1 -0
  107. package/dist/handlers/internal/sponsorship.d.ts +83 -0
  108. package/dist/handlers/internal/sponsorship.d.ts.map +1 -0
  109. package/dist/handlers/internal/sponsorship.js +103 -0
  110. package/dist/handlers/internal/sponsorship.js.map +1 -0
  111. package/dist/handlers/internal/types.d.ts +284 -0
  112. package/dist/handlers/internal/types.d.ts.map +1 -0
  113. package/dist/handlers/internal/types.js +2 -0
  114. package/dist/handlers/internal/types.js.map +1 -0
  115. package/dist/handlers/internal/utils.d.ts +34 -0
  116. package/dist/handlers/internal/utils.d.ts.map +1 -0
  117. package/dist/handlers/internal/utils.js +131 -0
  118. package/dist/handlers/internal/utils.js.map +1 -0
  119. package/dist/handlers/relay.d.ts +119 -0
  120. package/dist/handlers/relay.d.ts.map +1 -0
  121. package/dist/handlers/relay.js +1264 -0
  122. package/dist/handlers/relay.js.map +1 -0
  123. package/dist/index.d.ts +4 -2
  124. package/dist/index.d.ts.map +1 -1
  125. package/dist/index.js +4 -2
  126. package/dist/index.js.map +1 -1
  127. package/dist/internal/Auth.d.ts +16 -10
  128. package/dist/internal/Auth.d.ts.map +1 -1
  129. package/dist/internal/Auth.js +72 -12
  130. package/dist/internal/Auth.js.map +1 -1
  131. package/dist/internal/RpcSchema.d.ts +10 -0
  132. package/dist/internal/RpcSchema.d.ts.map +1 -0
  133. package/dist/internal/RpcSchema.js +2 -0
  134. package/dist/internal/RpcSchema.js.map +1 -0
  135. package/dist/internal/Store.d.ts +42 -3
  136. package/dist/internal/Store.d.ts.map +1 -1
  137. package/dist/internal/Store.js +65 -3
  138. package/dist/internal/Store.js.map +1 -1
  139. package/dist/internal/VerifiedTokens.d.ts +15 -34
  140. package/dist/internal/VerifiedTokens.d.ts.map +1 -1
  141. package/dist/internal/VerifiedTokens.js +115 -159
  142. package/dist/internal/VerifiedTokens.js.map +1 -1
  143. package/dist/internal/Webhooks.d.ts +49 -35
  144. package/dist/internal/Webhooks.d.ts.map +1 -1
  145. package/dist/internal/Webhooks.js +92 -205
  146. package/dist/internal/Webhooks.js.map +1 -1
  147. package/package.json +7 -2
  148. package/src/ApiKey.test.ts +3 -3
  149. package/src/ApiKey.ts +10 -12
  150. package/src/ApiKeys.test.ts +175 -0
  151. package/src/ApiKeys.ts +205 -0
  152. package/src/App.test-d.ts +1 -1
  153. package/src/App.test.ts +10768 -351
  154. package/src/App.ts +79 -36
  155. package/src/Client.test-d.ts +1 -1
  156. package/src/Handler.ts +154 -0
  157. package/src/Scope.ts +5 -1
  158. package/src/Webhooks.test.ts +8 -7
  159. package/src/Webhooks.ts +11 -5
  160. package/src/admin/Access.ts +1 -3
  161. package/src/admin/App.ts +19 -24
  162. package/src/admin/apps/api-keys.test.ts +19 -18
  163. package/src/admin/apps/api-keys.ts +8 -9
  164. package/src/admin/apps/scopes.test.ts +4 -0
  165. package/src/admin/apps/verified-tokens.test.ts +3 -30
  166. package/src/admin/apps/verified-tokens.ts +17 -36
  167. package/src/admin/ui.generated.ts +1 -1
  168. package/src/apps/balances.test.ts +4 -3
  169. package/src/apps/blocks.test.ts +1 -1
  170. package/src/apps/exchanges.test.ts +1 -1
  171. package/src/apps/indexer.test.ts +1 -1
  172. package/src/apps/receipts.test.ts +1 -1
  173. package/src/apps/rpc.test.ts +100 -10
  174. package/src/apps/tokenlist.test.ts +5 -7
  175. package/src/apps/tokens.test.ts +4 -3
  176. package/src/apps/transactions.test.ts +1 -1
  177. package/src/apps/transfers.test.ts +1 -1
  178. package/src/apps/verified-tokens.test.ts +24 -12
  179. package/src/apps/webhooks.test.ts +130 -143
  180. package/src/apps/webhooks.ts +45 -47
  181. package/src/{Cli.test.ts → cli/Cli.test.ts} +10 -9
  182. package/src/{Cli.ts → cli/Cli.ts} +16 -9
  183. package/src/cli/bin.ts +4 -0
  184. package/src/cli/commands/admin.test.ts +71 -0
  185. package/src/cli/commands/admin.ts +60 -0
  186. package/src/cloudflare.ts +10 -0
  187. package/src/db/Db.test.ts +84 -0
  188. package/src/db/Db.ts +383 -0
  189. package/src/db/Schema.ts +164 -0
  190. package/src/db/tables/organizations.test.ts +124 -0
  191. package/src/db/tables/organizations.ts +127 -0
  192. package/src/db/tables/verifiedTokens.ts +111 -0
  193. package/src/db/tables/webhookDeliveries.ts +159 -0
  194. package/src/db/tables/webhookSubscriptions.ts +358 -0
  195. package/src/handlers/internal/executionError.ts +205 -0
  196. package/src/handlers/internal/multisig.localnet.test.ts +146 -0
  197. package/src/handlers/internal/multisig.test.ts +744 -0
  198. package/src/handlers/internal/multisig.ts +858 -0
  199. package/src/handlers/internal/relay.localnet.test.ts +2035 -0
  200. package/src/handlers/internal/requestListener.ts +273 -0
  201. package/src/handlers/internal/sponsorship.ts +186 -0
  202. package/src/handlers/internal/types.ts +378 -0
  203. package/src/handlers/internal/utils.ts +153 -0
  204. package/src/handlers/relay.ts +1605 -0
  205. package/src/index.ts +4 -2
  206. package/src/internal/Auth.test.ts +21 -21
  207. package/src/internal/Auth.ts +92 -19
  208. package/src/internal/RateLimit.test.ts +1 -1
  209. package/src/internal/RpcSchema.ts +10 -0
  210. package/src/internal/Store.test.ts +98 -10
  211. package/src/internal/Store.ts +112 -6
  212. package/src/internal/VerifiedTokens.test.ts +99 -115
  213. package/src/internal/VerifiedTokens.ts +123 -175
  214. package/src/internal/Webhooks.test.ts +107 -107
  215. package/src/internal/Webhooks.ts +102 -220
  216. package/dist/ApiKeySource.d.ts +0 -166
  217. package/dist/ApiKeySource.d.ts.map +0 -1
  218. package/dist/ApiKeySource.js +0 -234
  219. package/dist/ApiKeySource.js.map +0 -1
  220. package/dist/Cli.d.ts.map +0 -1
  221. package/dist/Cli.js +0 -13
  222. package/dist/Cli.js.map +0 -1
  223. package/seeds/31318/icons/0x20c0000000000000000000000000000000000000.svg +0 -1
  224. package/seeds/31318/icons/0x20c0000000000000000000000000000000000001.svg +0 -1
  225. package/seeds/31318/icons/0x20c0000000000000000000000000000000000002.svg +0 -1
  226. package/seeds/31318/icons/0x20c0000000000000000000000000000000000003.svg +0 -1
  227. package/seeds/31318/verified-tokens.json +0 -30
  228. package/seeds/4217/icons/0x20c0000000000000000000000000000000000000.svg +0 -25
  229. package/seeds/4217/icons/0x20c000000000000000000000048c8f36df1c9a4a.svg +0 -1
  230. package/seeds/4217/icons/0x20c0000000000000000000000520792dcccccccc.svg +0 -1
  231. package/seeds/4217/icons/0x20c0000000000000000000000a6da882d075a4c3.svg +0 -4
  232. package/seeds/4217/icons/0x20c000000000000000000000111111111e910f0f.svg +0 -30
  233. package/seeds/4217/icons/0x20c00000000000000000000014f22ca97301eb73.svg +0 -1
  234. package/seeds/4217/icons/0x20c0000000000000000000001621e21f71cf12fb.svg +0 -1
  235. package/seeds/4217/icons/0x20c0000000000000000000002f52d5cc21a3207b.svg +0 -1
  236. package/seeds/4217/icons/0x20c0000000000000000000003158081efd85bfc2.svg +0 -11
  237. package/seeds/4217/icons/0x20c0000000000000000000003554d28269e0f3c2.svg +0 -1
  238. package/seeds/4217/icons/0x20c000000000000000000000383a23bacb546ab9.svg +0 -1
  239. package/seeds/4217/icons/0x20c00000000000000000000042109aef2f8b28e1.png +0 -0
  240. package/seeds/4217/icons/0x20c0000000000000000000005c0bac7cef389a11.svg +0 -1
  241. package/seeds/4217/icons/0x20c0000000000000000000006fd9a167923ba194.png +0 -0
  242. package/seeds/4217/icons/0x20c0000000000000000000007f7ba549dd0251b9.svg +0 -1
  243. package/seeds/4217/icons/0x20c0000000000000000000008191667423f70e67.svg +0 -5
  244. package/seeds/4217/icons/0x20c0000000000000000000008ee4fcff88888888.svg +0 -1
  245. package/seeds/4217/icons/0x20c0000000000000000000009a4a4b17e0dc6651.svg +0 -1
  246. package/seeds/4217/icons/0x20c000000000000000000000ab02d39df30bd17e.svg +0 -1
  247. package/seeds/4217/icons/0x20c000000000000000000000ae247a1130450f09.svg +0 -1
  248. package/seeds/4217/icons/0x20c000000000000000000000aeed2ec36a54d0e5.svg +0 -1
  249. package/seeds/4217/icons/0x20c000000000000000000000b9537d11c60e8b50.svg +0 -1
  250. package/seeds/4217/icons/0x20c000000000000000000000bd95bfb69fbe6ce3.svg +0 -1
  251. package/seeds/4217/icons/0x20c000000000000000000000c412ec89d0c08be5.svg +0 -1
  252. package/seeds/4217/icons/0x20c000000000000000000000f047dd7018e50367.png +0 -0
  253. package/seeds/4217/verified-tokens.json +0 -177
  254. package/seeds/42431/icons/0x20c0000000000000000000000000000000000000.svg +0 -25
  255. package/seeds/42431/icons/0x20c0000000000000000000000000000000000001.svg +0 -1
  256. package/seeds/42431/icons/0x20c0000000000000000000000000000000000002.svg +0 -1
  257. package/seeds/42431/icons/0x20c0000000000000000000000000000000000003.svg +0 -1
  258. package/seeds/42431/icons/0x20c0000000000000000000009e8d7eb59b783726.svg +0 -1
  259. package/seeds/42431/icons/0x20c000000000000000000000d72572838bbee59c.svg +0 -1
  260. package/seeds/42431/verified-tokens.json +0 -44
  261. package/seeds/openrpc/README.md +0 -27
  262. package/seeds/openrpc/execution-apis.json +0 -8989
  263. package/src/ApiKeySource.test-d.ts +0 -11
  264. package/src/ApiKeySource.test.ts +0 -295
  265. package/src/ApiKeySource.ts +0 -303
package/src/index.ts CHANGED
@@ -1,10 +1,12 @@
1
1
  export * as ApiKey from './ApiKey.js'
2
- export * as ApiKeySource from './ApiKeySource.js'
2
+ export * as ApiKeys from './ApiKeys.js'
3
3
  export * as App from './App.js'
4
4
  export * as Assets from './Assets.js'
5
5
  export * as Auth from './Auth.js'
6
- export * as Cli from './Cli.js'
6
+ export * as Cli from './cli/Cli.js'
7
7
  export * as Client from './Client.js'
8
+ export * as Db from './db/Db.js'
9
+ export * as Handler from './Handler.js'
8
10
  export * as Log from './internal/Log.js'
9
11
  export * as Metrics from './Metrics.js'
10
12
  export * as RateLimit from './internal/RateLimit.js'
@@ -1,7 +1,7 @@
1
1
  import { Hono } from 'hono'
2
2
  import { requestId } from 'hono/request-id'
3
3
  import { Mppx, tempo } from 'mppx/hono'
4
- import { RateLimit } from 'cadent'
4
+ import { RateLimit } from 'tapimo'
5
5
  import * as ApiKey from '../ApiKey.js'
6
6
  import * as Auth from './Auth.js'
7
7
  import * as Log from './Log.js'
@@ -96,7 +96,7 @@ describe('require', () => {
96
96
  // the downstream validator rejects it as `query_invalid` and the legacy
97
97
  // `?key=...` lane breaks on every strict-query endpoint.
98
98
  const auth = {
99
- apiKey: { source: TestApp.source({ keys: [{ ...key, token }] }) },
99
+ apiKey: { resolve: TestApp.resolver({ keys: [{ ...key, token }] }) },
100
100
  rateLimit: RateLimit.memory(),
101
101
  }
102
102
  const app = new Hono<Auth.Environment>()
@@ -241,7 +241,7 @@ describe('require', () => {
241
241
  const app = createApp({
242
242
  auth: {
243
243
  apiKey: {
244
- source: TestApp.source({
244
+ resolve: TestApp.resolver({
245
245
  keys: [{ ...key, scopes: [Scope.wildcard], token }],
246
246
  }),
247
247
  },
@@ -273,7 +273,7 @@ describe('require', () => {
273
273
  function createChainApp(options: { public?: boolean } = {}) {
274
274
  const auth: Auth.Context = {
275
275
  apiKey: {
276
- source: TestApp.source({
276
+ resolve: TestApp.resolver({
277
277
  keys: [
278
278
  { ...key, scopes: [Scope.wildcard], token },
279
279
  {
@@ -399,7 +399,7 @@ describe('require', () => {
399
399
  const mpp = createMppServer()
400
400
  const app = createApp({
401
401
  auth: {
402
- apiKey: { source: TestApp.source({ keys: [{ ...key, token }] }) },
402
+ apiKey: { resolve: TestApp.resolver({ keys: [{ ...key, token }] }) },
403
403
  mpp,
404
404
  rateLimit: overQuota(),
405
405
  },
@@ -420,7 +420,7 @@ describe('require', () => {
420
420
  const mpp = createMppServer()
421
421
  const app = createApp({
422
422
  auth: {
423
- apiKey: { source: TestApp.source({ keys: [{ ...key, token }] }) },
423
+ apiKey: { resolve: TestApp.resolver({ keys: [{ ...key, token }] }) },
424
424
  mpp,
425
425
  rateLimit: overQuotaOnce(),
426
426
  },
@@ -451,7 +451,7 @@ describe('require', () => {
451
451
  const mpp = createMppServer()
452
452
  const app = createApp({
453
453
  auth: {
454
- apiKey: { source: TestApp.source({ keys: [{ ...key, token }] }) },
454
+ apiKey: { resolve: TestApp.resolver({ keys: [{ ...key, token }] }) },
455
455
  mpp,
456
456
  rateLimit: overQuota(),
457
457
  },
@@ -477,7 +477,7 @@ describe('require', () => {
477
477
  const mpp = createMppServer()
478
478
  const app = createApp({
479
479
  auth: {
480
- apiKey: { source: TestApp.source({ keys: [{ ...key, token }] }) },
480
+ apiKey: { resolve: TestApp.resolver({ keys: [{ ...key, token }] }) },
481
481
  mpp,
482
482
  rateLimit: overQuotaOnce(),
483
483
  },
@@ -537,7 +537,7 @@ describe('require', () => {
537
537
  test('returns rate-limit error without payment context', async () => {
538
538
  const app = createApp({
539
539
  auth: {
540
- apiKey: { source: TestApp.source({ keys: [{ ...key, token }] }) },
540
+ apiKey: { resolve: TestApp.resolver({ keys: [{ ...key, token }] }) },
541
541
  rateLimit: overQuota(),
542
542
  },
543
543
  policy: { public: { rateLimit: { perMinute: 1 } } },
@@ -561,7 +561,7 @@ describe('require', () => {
561
561
  try {
562
562
  const app = createApp({
563
563
  auth: {
564
- apiKey: { source: TestApp.source({ keys: [{ ...key, token }] }) },
564
+ apiKey: { resolve: TestApp.resolver({ keys: [{ ...key, token }] }) },
565
565
  rateLimit: {
566
566
  async consume() {
567
567
  throw new Error('Network connection lost.')
@@ -594,7 +594,7 @@ describe('require', () => {
594
594
  try {
595
595
  const app = createApp({
596
596
  auth: {
597
- apiKey: { source: TestApp.source({ keys: [{ ...key, token }] }) },
597
+ apiKey: { resolve: TestApp.resolver({ keys: [{ ...key, token }] }) },
598
598
  rateLimit: {
599
599
  async consume() {
600
600
  throw new Error('Network connection lost.')
@@ -632,7 +632,7 @@ describe('require', () => {
632
632
  const auth = {
633
633
  apiKey: {
634
634
  rateLimits: { 'data:read': { perMinute: 1 }, 'webhooks:write': { perMinute: 1 } },
635
- source: TestApp.source({
635
+ resolve: TestApp.resolver({
636
636
  keys: [{ ...key, scopes: [Scope.wildcard], token }],
637
637
  }),
638
638
  },
@@ -686,7 +686,7 @@ describe('require', () => {
686
686
  const auth = {
687
687
  apiKey: {
688
688
  rateLimits: { 'data:read': { perMinute: 1_000 } },
689
- source: TestApp.source({
689
+ resolve: TestApp.resolver({
690
690
  keys: [{ ...key, scopes: [Scope.wildcard], token }],
691
691
  }),
692
692
  },
@@ -710,7 +710,7 @@ describe('require', () => {
710
710
  })
711
711
 
712
712
  test('per-key default overrides config default but not a route cap', async () => {
713
- const source = TestApp.source({
713
+ const resolve = TestApp.resolver({
714
714
  keys: [
715
715
  {
716
716
  ...key,
@@ -723,7 +723,7 @@ describe('require', () => {
723
723
 
724
724
  const open = createScopedApp({
725
725
  auth: {
726
- apiKey: { rateLimits: { '*': { perMinute: 1_000 } }, source },
726
+ apiKey: { rateLimits: { '*': { perMinute: 1_000 } }, resolve },
727
727
  rateLimit: RateLimit.memory(),
728
728
  },
729
729
  routes: [{ path: '/protected', policy: { apiKey: { scopes: ['data:read'] } } }],
@@ -734,7 +734,7 @@ describe('require', () => {
734
734
 
735
735
  const capped = createScopedApp({
736
736
  auth: {
737
- apiKey: { rateLimits: { '*': { perMinute: 1_000 } }, source },
737
+ apiKey: { rateLimits: { '*': { perMinute: 1_000 } }, resolve },
738
738
  rateLimit: RateLimit.memory(),
739
739
  },
740
740
  routes: [
@@ -757,7 +757,7 @@ describe('require', () => {
757
757
  test('per-key per-scope overrides everything including a route cap', async () => {
758
758
  const auth = {
759
759
  apiKey: {
760
- source: TestApp.source({
760
+ resolve: TestApp.resolver({
761
761
  keys: [
762
762
  {
763
763
  ...key,
@@ -790,7 +790,7 @@ describe('require', () => {
790
790
  const entries: Log.Entry[] = []
791
791
  const app = createApp({
792
792
  auth: {
793
- apiKey: { source: TestApp.source({ keys: [{ ...key, token }] }) },
793
+ apiKey: { resolve: TestApp.resolver({ keys: [{ ...key, token }] }) },
794
794
  rateLimit: overQuota(),
795
795
  },
796
796
  logger: (entry) => void entries.push(entry),
@@ -817,7 +817,7 @@ describe('require', () => {
817
817
  const mpp = createMppServer()
818
818
  const app = createApp({
819
819
  auth: {
820
- apiKey: { source: TestApp.source({ keys: [{ ...key, token }] }) },
820
+ apiKey: { resolve: TestApp.resolver({ keys: [{ ...key, token }] }) },
821
821
  mpp,
822
822
  rateLimit: overQuota(),
823
823
  },
@@ -846,7 +846,7 @@ function createScopedApp(options: {
846
846
  }) {
847
847
  const auth = options.auth ?? {
848
848
  apiKey: {
849
- source: TestApp.source({ keys: [{ ...key, scopes: [Scope.wildcard], token }] }),
849
+ resolve: TestApp.resolver({ keys: [{ ...key, scopes: [Scope.wildcard], token }] }),
850
850
  },
851
851
  rateLimit: RateLimit.memory(),
852
852
  }
@@ -872,7 +872,7 @@ type Options = {
872
872
 
873
873
  function createApp(options: Options = {}) {
874
874
  const auth = options.auth ?? {
875
- apiKey: { source: TestApp.source({ keys: [{ ...key, token }] }) },
875
+ apiKey: { resolve: TestApp.resolver({ keys: [{ ...key, token }] }) },
876
876
  rateLimit: RateLimit.memory(),
877
877
  }
878
878
  const app = new Hono<Auth.Environment>()
@@ -7,8 +7,9 @@ import { findTargetHandler } from 'hono/utils/handler'
7
7
  import { Credential, type Method } from 'mppx'
8
8
  import { Mppx, tempo } from 'mppx/hono'
9
9
 
10
- import * as ApiKeySource from '../ApiKeySource.js'
11
10
  import * as ApiKey from '../ApiKey.js'
11
+ import * as ApiKeys from '../ApiKeys.js'
12
+ import type * as Store from './Store.js'
12
13
  import * as OpenApi from './OpenApi.js'
13
14
  import * as RateLimit from './RateLimit.js'
14
15
  import * as Response from './Response.js'
@@ -29,8 +30,8 @@ export type Context = {
29
30
  * per-key {@link ApiKey.ApiKey.rateLimits} shape.
30
31
  */
31
32
  rateLimits?: Record<string, RateLimit.Limit> | undefined
32
- /** Backing API-key source. */
33
- source: ApiKeySource.Source
33
+ /** Resolves a presented token against the app's KV store on `c`. */
34
+ resolve: (c: hono_Context, token: string) => Promise<ApiKey.ApiKey | null>
34
35
  }
35
36
  | undefined
36
37
  /** MPP payment configuration. */
@@ -217,14 +218,20 @@ export function middleware<
217
218
  const { charge = {}, ...mpp } = options.mpp === false ? {} : (options.mpp ?? {})
218
219
  const defaults = laneDefaults(options)
219
220
  const base = basePolicy(options, defaults)
221
+ // Keys resolve against the app's KV state store on request context
222
+ // (`App.create({ kv })`), so the middleware needs no storage wiring; when no
223
+ // store is configured, every token resolves to `null` (closed).
220
224
  // Resolution is cached per isolate by default (positive 60s / negative 10s,
221
- // keyed by token sha256 — see `ApiKeySource.cached`) so hot keys skip the
222
- // backend read on the auth path of every request. Pass `apiKey.cache: false`
223
- // when instant revocation matters more than the per-request read.
224
- const source = (() => {
225
- if (options.apiKey === false || !options.apiKey?.source) return ApiKeySource.noop
226
- if (options.apiKey.cache === false) return options.apiKey.source
227
- return ApiKeySource.cached(options.apiKey.source, options.apiKey.cache)
225
+ // keyed by token sha256) so hot keys skip the backend read on the auth path
226
+ // of every request. Pass `apiKey.cache: false` when instant revocation
227
+ // matters more than the per-request read.
228
+ const resolve = (() => {
229
+ const uncached = (c: hono_Context, token: string) => {
230
+ const kv = contextKv(c)
231
+ return kv ? ApiKeys.resolve(kv.store, token) : Promise.resolve(null)
232
+ }
233
+ if (options.apiKey === false || options.apiKey?.cache === false) return uncached
234
+ return cachedResolve(uncached, options.apiKey?.cache ?? {})
228
235
  })()
229
236
  const auth: Context = {
230
237
  ...(options.apiKey === false
@@ -232,7 +239,7 @@ export function middleware<
232
239
  : {
233
240
  apiKey: {
234
241
  ...(options.apiKey?.rateLimits ? { rateLimits: options.apiKey.rateLimits } : {}),
235
- source,
242
+ resolve,
236
243
  },
237
244
  }),
238
245
  ...(options.mpp === false
@@ -280,16 +287,14 @@ export declare namespace middleware {
280
287
  apiKey?:
281
288
  | {
282
289
  /**
283
- * Resolve-cache tuning for the source (per-isolate, positive 60s /
284
- * negative 10s by default), or `false` to resolve uncached on every
285
- * request — e.g. when instant revocation matters more than the
286
- * per-request backend read.
290
+ * Resolve-cache tuning (per-isolate, positive 60s / negative 10s by
291
+ * default), or `false` to resolve uncached on every request — e.g.
292
+ * when instant revocation matters more than the per-request backend
293
+ * read.
287
294
  */
288
- cache?: ApiKeySource.cached.Options | false | undefined
295
+ cache?: Cache | false | undefined
289
296
  /** API-key quotas keyed by quota scope (reserved `'*'` is the config default). */
290
297
  rateLimits?: Record<string, RateLimit.Limit> | undefined
291
- /** Backing API-key source. Defaults to an empty source (rejects all). */
292
- source?: ApiKeySource.Source | undefined
293
298
  /** Required API-key scopes. */
294
299
  scopes?: readonly Scope.Id[] | undefined
295
300
  }
@@ -311,6 +316,16 @@ export declare namespace middleware {
311
316
  rateLimit?: RateLimit.memory.Options | undefined
312
317
  }
313
318
 
319
+ /** Resolve-cache tuning for API-key resolution. */
320
+ type Cache = {
321
+ /** Time-to-live for negative (`null`) results in milliseconds. */
322
+ negativeTtl?: number | undefined
323
+ /** Clock used for expiry. */
324
+ now?: (() => number) | undefined
325
+ /** Time-to-live for positive results in milliseconds. */
326
+ ttl?: number | undefined
327
+ }
328
+
314
329
  /** MPP payment options used by auth middleware. */
315
330
  type MppOptions = Omit<NonNullable<Parameters<typeof Mppx.create>[0]>, 'methods'> & {
316
331
  /** Global Tempo charge options passed to the MPP method instantiator. */
@@ -612,7 +627,7 @@ export function require<environment extends Environment = Environment>(
612
627
 
613
628
  const hasPayment = hasPaymentCredential(c) && isPaymentEnabled(auth, policy)
614
629
  if (credential) {
615
- const apiKey = await auth.apiKey?.source.resolve(credential.token)
630
+ const apiKey = await auth.apiKey?.resolve(c, credential.token)
616
631
  if (!apiKey)
617
632
  return jsonError(c, {
618
633
  code: 'api_key_invalid',
@@ -1010,3 +1025,61 @@ function setRateLimitHeaders(c: hono_Context, result: RateLimit.Result) {
1010
1025
  c.header('RateLimit-Remaining', String(result.remaining))
1011
1026
  c.header('RateLimit-Reset', String(result.reset))
1012
1027
  }
1028
+
1029
+ // Reads the app's KV state store from request context (set by `App.create`
1030
+ // from its `kv` option); `undefined` leaves API-key auth closed.
1031
+ function contextKv(c: hono_Context): { store: Store.State } | undefined {
1032
+ return (c.get as (key: string) => unknown)('kv') as { store: Store.State } | undefined
1033
+ }
1034
+
1035
+ /** Maximum resolve-cache entries held per isolate before eviction. */
1036
+ const cachedMaxEntries = 10_000
1037
+
1038
+ /**
1039
+ * Wraps token resolution with a short-TTL per-isolate cache so hot keys skip
1040
+ * the backend read on every request. Positive results cache for `ttl`
1041
+ * (default 60s), negative for `negativeTtl` (default 10s) so a freshly minted
1042
+ * key is usable quickly; concurrent misses for one token are single-flighted.
1043
+ * Entries are keyed by the token's sha256 (`ApiKey.hash`) — never the
1044
+ * plaintext. A revoked key may stay resolvable for up to `ttl` — the accepted
1045
+ * trade-off for removing a backend read from the auth path.
1046
+ */
1047
+ function cachedResolve(
1048
+ resolve: (c: hono_Context, token: string) => Promise<ApiKey.ApiKey | null>,
1049
+ options: middleware.Cache,
1050
+ ): (c: hono_Context, token: string) => Promise<ApiKey.ApiKey | null> {
1051
+ const { negativeTtl = 10_000, now = Date.now, ttl = 60_000 } = options
1052
+ type Cell = { expiresAt: number; value: ApiKey.ApiKey | null }
1053
+ const cells = new Map<string, Cell>()
1054
+ const flights = new Map<string, Promise<ApiKey.ApiKey | null>>()
1055
+
1056
+ return async (c, token) => {
1057
+ // Versioned so a future record-shape change can't serve stale shapes.
1058
+ const key = `v1:${ApiKey.hash(token)}`
1059
+ const cell = cells.get(key)
1060
+ if (cell && cell.expiresAt > now()) return cell.value
1061
+ if (cell) cells.delete(key)
1062
+
1063
+ const existing = flights.get(key)
1064
+ if (existing) return existing
1065
+
1066
+ const flight = (async () => {
1067
+ const value = await resolve(c, token)
1068
+ // Bound the cache: random invalid tokens each add a (negative) entry,
1069
+ // so sweep expired cells at the cap and then drop the oldest.
1070
+ if (cells.size >= cachedMaxEntries) {
1071
+ const time = now()
1072
+ for (const [name, entry] of cells) if (entry.expiresAt <= time) cells.delete(name)
1073
+ while (cells.size >= cachedMaxEntries) cells.delete(cells.keys().next().value as string)
1074
+ }
1075
+ cells.set(key, { expiresAt: now() + (value ? ttl : negativeTtl), value })
1076
+ return value
1077
+ })()
1078
+ flights.set(key, flight)
1079
+ try {
1080
+ return await flight
1081
+ } finally {
1082
+ flights.delete(key)
1083
+ }
1084
+ }
1085
+ }
@@ -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
 
@@ -193,6 +275,16 @@ describe('cloudflareKv', () => {
193
275
  })
194
276
 
195
277
  describe('durableObject', () => {
278
+ // The stub contract requires native `increment` and `swap` (our DO class
279
+ // implements them); derive them for the in-memory fakes.
280
+ function stubFor(backing: Store.State): Store.durableObject.Stub {
281
+ return {
282
+ ...backing,
283
+ increment: (key, options) => Store.increment(backing, key, options),
284
+ swap: (key, expected, next, options) => backing.swap!(key, expected, next, options),
285
+ }
286
+ }
287
+
196
288
  test('adapts a Durable Object namespace without caching stubs', async () => {
197
289
  const calls: string[] = []
198
290
  const backing = Store.memory({
@@ -201,12 +293,7 @@ describe('durableObject', () => {
201
293
  const namespace = {
202
294
  getByName(name) {
203
295
  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
- }
296
+ return stubFor(backing)
210
297
  },
211
298
  } satisfies Store.durableObject.Namespace
212
299
  const store = Store.durableObject(namespace, { name: 'tempo-api' })
@@ -250,6 +337,7 @@ describe('durableObject', () => {
250
337
  increment: (key, options) => Store.increment(backing, key, options),
251
338
  list: (options) => backing.list(options),
252
339
  put: (key, value, options) => backing.put(key, value, options),
340
+ swap: (key, expected, next, options) => backing.swap!(key, expected, next, options),
253
341
  } satisfies Store.durableObject.Stub
254
342
  return new Proxy(stub, {
255
343
  get(target, method: string) {
@@ -294,7 +382,7 @@ describe('durableObject', () => {
294
382
  getByName() {
295
383
  stubs += 1
296
384
  return {
297
- ...backing,
385
+ ...stubFor(backing),
298
386
  increment: (key, options) => {
299
387
  if (failures > 0) {
300
388
  failures -= 1
@@ -317,7 +405,7 @@ describe('durableObject', () => {
317
405
  const namespace = {
318
406
  getByName() {
319
407
  return {
320
- ...Store.memory(),
408
+ ...stubFor(Store.memory()),
321
409
  increment: () => {
322
410
  attempts += 1
323
411
  throw Object.assign(new Error('Network connection lost.'), { retryable: true })
@@ -339,7 +427,7 @@ describe('durableObject', () => {
339
427
  ({
340
428
  getByName() {
341
429
  return {
342
- ...Store.memory(),
430
+ ...stubFor(Store.memory()),
343
431
  increment: () => {
344
432
  attempts += 1
345
433
  throw error