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
@@ -1,4 +1,5 @@
1
1
  import * as ApiKey from '../../ApiKey.js'
2
+ import * as core_ApiKeys from '../../ApiKeys.js'
2
3
 
3
4
  import * as TestAdmin from '../../../test/Admin.js'
4
5
  import * as TestApp from '../../../test/App.js'
@@ -9,7 +10,7 @@ const validBody = { orgId: 'org_test', scopes: ['data:read'] } as const
9
10
 
10
11
  describe('POST /api-keys', () => {
11
12
  test('mints a key, returns the token once, persists a hashed record', async () => {
12
- const { client, source, store } = TestAdmin.setup()
13
+ const { client, store } = TestAdmin.setup()
13
14
  const response = await client['api-keys'].$post({ json: { ...validBody, name: 'CI' } })
14
15
  const body = await TestApp.json(response, ApiKeys.schema.createApiKey.Response)
15
16
 
@@ -34,26 +35,26 @@ describe('POST /api-keys', () => {
34
35
  }
35
36
  `)
36
37
 
37
- // The token resolves through the source (proving the record persisted),
38
+ // The token resolves through the module (proving the record persisted),
38
39
  // and at rest it is keyed by the token hash — never the plaintext token.
39
- const resolved = await source.resolve(body.token)
40
+ const resolved = await core_ApiKeys.resolve(store, body.token)
40
41
  expect(resolved?.orgId).toBe('org_test')
41
42
  expect(await store.get(body.token)).toBeNull()
42
43
  expect(await store.get(ApiKey.keyFor(body.token))).not.toBeNull()
43
44
  })
44
45
 
45
46
  test('defaults to the production environment', async () => {
46
- const { client, source } = TestAdmin.setup()
47
+ const { client, store } = TestAdmin.setup()
47
48
  const response = await client['api-keys'].$post({ json: validBody })
48
49
  const body = await TestApp.json(response, ApiKeys.schema.createApiKey.Response)
49
50
 
50
51
  expect(response.status).toBe(200)
51
52
  expect(body.environment).toBe('production')
52
- expect((await source.list())[0]?.environment).toBe('production')
53
+ expect((await core_ApiKeys.list(store))[0]?.environment).toBe('production')
53
54
  })
54
55
 
55
56
  test('persists a sandbox environment when provided', async () => {
56
- const { client, source } = TestAdmin.setup()
57
+ const { client, store } = TestAdmin.setup()
57
58
  const response = await client['api-keys'].$post({
58
59
  json: { ...validBody, environment: 'sandbox' },
59
60
  })
@@ -63,19 +64,19 @@ describe('POST /api-keys', () => {
63
64
  expect(body.environment).toBe('sandbox')
64
65
  // Sandbox tokens carry a distinct prefix for recognizability/leak detection.
65
66
  expect(body.token).toMatch(/^tempo_sandbox:sk:[a-f0-9]{48}$/)
66
- const resolved = await source.resolve(body.token)
67
+ const resolved = await core_ApiKeys.resolve(store, body.token)
67
68
  expect(resolved?.environment).toBe('sandbox')
68
69
  })
69
70
 
70
71
  test('records the expiry when provided', async () => {
71
- const { client, source } = TestAdmin.setup()
72
+ const { client, store } = TestAdmin.setup()
72
73
  const expiresAt = '2100-01-01T00:00:00.000Z'
73
74
  const response = await client['api-keys'].$post({ json: { ...validBody, expiresAt } })
74
75
  const body = await TestApp.json(response, ApiKeys.schema.createApiKey.Response)
75
76
 
76
77
  expect(response.status).toBe(200)
77
78
  expect(body.expiresAt).toBe(expiresAt)
78
- expect((await source.list())[0]?.expiresAt).toBe(expiresAt)
79
+ expect((await core_ApiKeys.list(store))[0]?.expiresAt).toBe(expiresAt)
79
80
  })
80
81
 
81
82
  test('rejects an unknown scope with 400', async () => {
@@ -106,8 +107,8 @@ describe('POST /api-keys', () => {
106
107
 
107
108
  describe('GET /api-keys', () => {
108
109
  test('lists records as metadata only (never the token)', async () => {
109
- const { client, source } = TestAdmin.setup()
110
- const { token } = await source.mint(validBody)
110
+ const { client, store } = TestAdmin.setup()
111
+ const { token } = await core_ApiKeys.mint(store, validBody)
111
112
 
112
113
  const response = await client['api-keys'].$get({ query: {} })
113
114
  const body = await TestApp.json(response, ApiKeys.schema.listApiKeys.Response)
@@ -120,9 +121,9 @@ describe('GET /api-keys', () => {
120
121
  })
121
122
 
122
123
  test('filters by orgId', async () => {
123
- const { client, source } = TestAdmin.setup()
124
- await source.mint({ orgId: 'org_a', scopes: ['data:read'] })
125
- await source.mint({ orgId: 'org_b', scopes: ['data:read'] })
124
+ const { client, store } = TestAdmin.setup()
125
+ await core_ApiKeys.mint(store, { orgId: 'org_a', scopes: ['data:read'] })
126
+ await core_ApiKeys.mint(store, { orgId: 'org_b', scopes: ['data:read'] })
126
127
 
127
128
  const response = await client['api-keys'].$get({ query: { orgId: 'org_a' } })
128
129
  const body = await TestApp.json(response, ApiKeys.schema.listApiKeys.Response)
@@ -133,16 +134,16 @@ describe('GET /api-keys', () => {
133
134
 
134
135
  describe('DELETE /api-keys/:id', () => {
135
136
  test('revokes a key, after which it no longer resolves or lists', async () => {
136
- const { client, source } = TestAdmin.setup()
137
- const { record, token } = await source.mint(validBody)
137
+ const { client, store } = TestAdmin.setup()
138
+ const { record, token } = await core_ApiKeys.mint(store, validBody)
138
139
 
139
140
  const response = await client['api-keys'][':id'].$delete({ param: { id: record.id } })
140
141
  const body = await TestApp.json(response, ApiKeys.schema.revokeApiKey.Response)
141
142
 
142
143
  expect(response.status).toBe(200)
143
144
  expect(body).toEqual({ id: record.id })
144
- expect(await source.resolve(token)).toBeNull()
145
- expect(await source.list()).toEqual([])
145
+ expect(await core_ApiKeys.resolve(store, token)).toBeNull()
146
+ expect(await core_ApiKeys.list(store)).toEqual([])
146
147
  })
147
148
 
148
149
  test('unknown id → 404', async () => {
@@ -1,7 +1,7 @@
1
1
  import { Hono } from 'hono'
2
2
  import * as z from 'zod/mini'
3
3
 
4
- import * as ApiKeySource from '../../ApiKeySource.js'
4
+ import * as ApiKeys from '../../ApiKeys.js'
5
5
  import * as OpenApi from '../../internal/OpenApi.js'
6
6
  import * as Response from '../../internal/Response.js'
7
7
  import * as Schema from '../../internal/Schema.js'
@@ -16,13 +16,13 @@ export namespace schema {
16
16
  * server sets from the verified identity so it can't be spoofed).
17
17
  */
18
18
  export const Body = Schema.describe(
19
- z.omit(ApiKeySource.schema.MintInput, { createdBy: true }),
19
+ z.omit(ApiKeys.schema.MintInput, { createdBy: true }),
20
20
  'Fields accepted when minting an API key.',
21
21
  )
22
22
 
23
23
  /** Response: the record plus the one-time plaintext token. */
24
24
  export const Response = Schema.describe(
25
- z.extend(ApiKeySource.schema.Record, {
25
+ z.extend(ApiKeys.schema.Record, {
26
26
  token: z
27
27
  .string()
28
28
  .check(z.describe('The plaintext token, shown once and unrecoverable afterward.')),
@@ -46,7 +46,7 @@ export namespace schema {
46
46
  export const Response = Schema.describe(
47
47
  z.object({
48
48
  data: z
49
- .array(ApiKeySource.schema.Record)
49
+ .array(ApiKeys.schema.Record)
50
50
  .check(z.describe('API-key records, newest first (metadata only — never tokens).')),
51
51
  }),
52
52
  'A list of API-key records.',
@@ -70,8 +70,7 @@ export namespace schema {
70
70
  }
71
71
 
72
72
  /**
73
- * Provisioning routes for API keys, backed by the request's
74
- * {@link App.Environment} `source`:
73
+ * Provisioning routes for API keys, backed by the app's KV state store:
75
74
  *
76
75
  * - `POST /` mints a key, recording `createdBy` from the verified identity and
77
76
  * returning the one-time token.
@@ -100,7 +99,7 @@ export function apiKeys() {
100
99
  async (c) => {
101
100
  // `createdBy` is the verified admin email, never client-supplied.
102
101
  // `requireAuth` guarantees identity on this gated route.
103
- const { record, token } = await c.get('source').mint({
102
+ const { record, token } = await ApiKeys.mint(c.get('kv').store, {
104
103
  ...c.req.valid('json'),
105
104
  createdBy: c.get('identity')!.email,
106
105
  })
@@ -123,7 +122,7 @@ export function apiKeys() {
123
122
  }),
124
123
  async (c) => {
125
124
  const { orgId } = c.req.valid('query')
126
- const data = await c.get('source').list(orgId ? { orgId } : {})
125
+ const data = await ApiKeys.list(c.get('kv').store, orgId ? { orgId } : {})
127
126
  return c.json(Response.validated(schema.listApiKeys.Response, { data }))
128
127
  },
129
128
  )
@@ -144,7 +143,7 @@ export function apiKeys() {
144
143
  }),
145
144
  async (c) => {
146
145
  const { id } = c.req.valid('param')
147
- const revoked = await c.get('source').revoke(id)
146
+ const revoked = await ApiKeys.revoke(c.get('kv').store, id)
148
147
  if (!revoked)
149
148
  return Response.error(c, {
150
149
  code: 'not_found',
@@ -22,6 +22,10 @@ describe('GET /scopes', () => {
22
22
  "description": "Run ad-hoc queries against the raw indexer passthrough.",
23
23
  "scope": "indexer:query",
24
24
  },
25
+ {
26
+ "description": "Fill and submit transactions through the relay.",
27
+ "scope": "rpc-relay",
28
+ },
25
29
  {
26
30
  "description": "Read webhook subscriptions.",
27
31
  "scope": "webhooks:read",
@@ -1,5 +1,3 @@
1
- import { Store } from 'cadent'
2
-
3
1
  import * as TestAdmin from '../../../test/Admin.js'
4
2
  import * as TestApp from '../../../test/App.js'
5
3
  import * as Verified from './verified-tokens.js'
@@ -15,29 +13,14 @@ const novel = {
15
13
 
16
14
  const chainId = '42431' as const
17
15
 
18
- /** Write-enabled admin client backed by a fresh in-memory state store. */
16
+ /** Write-enabled admin client backed by a fresh schema-isolated database. */
19
17
  function writes() {
20
- return TestAdmin.setup({ verifiedTokens: { store: Store.memory() } })
18
+ return TestAdmin.setup()
21
19
  }
22
20
 
23
- describe('feature gate', () => {
24
- test('write routes return 404 when no store is configured', async () => {
25
- const { client } = TestAdmin.setup()
26
- const response = await client['verified-tokens'].$post({
27
- json: { ...novel, chainId: 42431 },
28
- })
29
- expect(response.status).toBe(404)
30
- const body = (await response.json()) as { error?: { code?: string } }
31
- expect(body.error?.code).toBe('verified_tokens_not_enabled')
32
- })
33
- })
34
-
35
21
  describe('authentication', () => {
36
22
  test('denied identity → 401', async () => {
37
- const { client } = TestAdmin.setup({
38
- identify: () => null,
39
- verifiedTokens: { store: Store.memory() },
40
- })
23
+ const { client } = TestAdmin.setup({ identify: () => null })
41
24
  const response = await client['verified-tokens'].$post({
42
25
  json: { ...novel, chainId: 42431 },
43
26
  })
@@ -46,12 +29,6 @@ describe('authentication', () => {
46
29
  })
47
30
 
48
31
  describe('list', () => {
49
- test('returns 404 when no store is configured', async () => {
50
- const { client } = TestAdmin.setup()
51
- const response = await client['verified-tokens'].$get({ query: { chainId } })
52
- expect(response.status).toBe(404)
53
- })
54
-
55
32
  test('empty list → data [] and null version', async () => {
56
33
  const { client } = writes()
57
34
  const response = await client['verified-tokens'].$get({ query: { chainId } })
@@ -176,7 +153,6 @@ describe('create with inline logo', () => {
176
153
  get: async (key) => stored.find((entry) => entry.key === key)?.content,
177
154
  put: async (key, content) => void stored.push({ content, key }),
178
155
  },
179
- verifiedTokens: { store: Store.memory() },
180
156
  })
181
157
  return { client, stored }
182
158
  }
@@ -219,7 +195,6 @@ describe('create with inline logo', () => {
219
195
  throw new Error('upload failed')
220
196
  },
221
197
  },
222
- verifiedTokens: { store: Store.memory() },
223
198
  })
224
199
  const response = await client['verified-tokens'].$post({
225
200
  json: { ...novel, chainId: 42431, logo: svg },
@@ -297,7 +272,6 @@ describe('PUT /verified-tokens/:address/logo', () => {
297
272
  stored.push({ content, key })
298
273
  },
299
274
  },
300
- verifiedTokens: { store: Store.memory() },
301
275
  })
302
276
  return { client, stored }
303
277
  }
@@ -375,7 +349,6 @@ describe('GET /verified-tokens/:address/logo', () => {
375
349
  get: async (key) => objects.get(key),
376
350
  put: async (key, content) => void objects.set(key, content),
377
351
  },
378
- verifiedTokens: { store: Store.memory() },
379
352
  })
380
353
  return { client, objects }
381
354
  }
@@ -4,7 +4,7 @@ import * as z from 'zod/mini'
4
4
  import * as OpenApi from '../../internal/OpenApi.js'
5
5
  import * as Response from '../../internal/Response.js'
6
6
  import * as Schema from '../../internal/Schema.js'
7
- import type * as Store from '../../internal/Store.js'
7
+ import * as Db from '../../db/Db.js'
8
8
  import * as VerifiedTokens from '../../internal/VerifiedTokens.js'
9
9
  import { schema as readSchema } from '../../apps/verified-tokens.js'
10
10
  import type * as App from '../App.js'
@@ -149,9 +149,9 @@ export namespace schema {
149
149
  }
150
150
 
151
151
  /**
152
- * Management routes for the curated verified-token list, backed by the request's
153
- * `verifiedTokens` store; all `404` when no store is configured. Mutations accept
154
- * an optional `If-Match` precondition (`412` on mismatch).
152
+ * Management routes for the curated verified-token list, backed by the app
153
+ * database (snapshot rows). Mutations accept an optional `If-Match`
154
+ * precondition (`412` on mismatch).
155
155
  *
156
156
  * - `POST /` creates a token (`409` on a duplicate address or symbol).
157
157
  * - `PUT /` replaces a chain's entire list (seed/rollback).
@@ -170,7 +170,6 @@ export function verifiedTokens() {
170
170
  OpenApi.describeRoute({
171
171
  operationId: 'listVerifiedTokens',
172
172
  responses: OpenApi.responses({
173
- errors: { 404: featureDisabledDescription },
174
173
  success: {
175
174
  description: 'Curated verified tokens.',
176
175
  schema: schema.listVerifiedTokens.Response,
@@ -180,10 +179,9 @@ export function verifiedTokens() {
180
179
  tags: ['Verified Tokens'],
181
180
  }),
182
181
  async (c) => {
183
- const store = c.get('verifiedTokens')?.store
184
- if (!store) return notEnabled(c)
182
+ const db = Db.get(c.get('db'))
185
183
  const { chainId } = c.req.valid('query')
186
- const snapshot = await VerifiedTokens.read(store, chainId)
184
+ const snapshot = await VerifiedTokens.read(db, chainId)
187
185
  return c.json(
188
186
  Response.validated(schema.listVerifiedTokens.Response, {
189
187
  data: snapshot?.list ?? [],
@@ -202,7 +200,6 @@ export function verifiedTokens() {
202
200
  operationId: 'createVerifiedToken',
203
201
  responses: OpenApi.responses({
204
202
  errors: {
205
- 404: featureDisabledDescription,
206
203
  409: 'Duplicate address or symbol.',
207
204
  413: 'Logo exceeds the size limit.',
208
205
  415: 'Logo is not valid SVG.',
@@ -216,22 +213,21 @@ export function verifiedTokens() {
216
213
  tags: ['Verified Tokens'],
217
214
  }),
218
215
  async (c) => {
219
- const store = c.get('verifiedTokens')?.store
220
- if (!store) return notEnabled(c)
216
+ const db = Db.get(c.get('db'))
221
217
  const { logo, ...body } = c.req.valid('json')
222
218
  try {
223
219
  if (logo !== undefined) {
224
220
  const assets = c.get('assets')
225
221
  if (!assets?.put) return assetsNotEnabled(c)
226
222
  const svg = encodeLogo(logo)
227
- const duplicate = await findDuplicate(store, body)
223
+ const duplicate = await findDuplicate(db, body)
228
224
  if (duplicate) throw duplicate
229
225
  await assets.put(logoKey(body.chainId, body.address), {
230
226
  body: svg,
231
227
  contentType: 'image/svg+xml',
232
228
  })
233
229
  }
234
- const { snapshot, token } = await VerifiedTokens.create(store, body)
230
+ const { snapshot, token } = await VerifiedTokens.create(db, body)
235
231
  return c.json(
236
232
  Response.validated(schema.createVerifiedToken.Response, {
237
233
  data: token,
@@ -257,7 +253,6 @@ export function verifiedTokens() {
257
253
  operationId: 'replaceVerifiedTokens',
258
254
  responses: OpenApi.responses({
259
255
  errors: {
260
- 404: featureDisabledDescription,
261
256
  409: 'Duplicate address or symbol.',
262
257
  412: 'Version precondition failed.',
263
258
  },
@@ -270,12 +265,11 @@ export function verifiedTokens() {
270
265
  tags: ['Verified Tokens'],
271
266
  }),
272
267
  async (c) => {
273
- const store = c.get('verifiedTokens')?.store
274
- if (!store) return notEnabled(c)
268
+ const db = Db.get(c.get('db'))
275
269
  const { chainId } = c.req.valid('query')
276
270
  const { tokens } = c.req.valid('json')
277
271
  try {
278
- const { snapshot } = await VerifiedTokens.replace(store, chainId, tokens, {
272
+ const { snapshot } = await VerifiedTokens.replace(db, chainId, tokens, {
279
273
  ...ifMatch(c),
280
274
  })
281
275
  return c.json(
@@ -320,13 +314,12 @@ export function verifiedTokens() {
320
314
  tags: ['Verified Tokens'],
321
315
  }),
322
316
  async (c) => {
323
- const store = c.get('verifiedTokens')?.store
324
- if (!store) return notEnabled(c)
317
+ const db = Db.get(c.get('db'))
325
318
  const { address } = c.req.valid('param')
326
319
  const { chainId } = c.req.valid('query')
327
320
  const body = c.req.valid('json')
328
321
  try {
329
- const { snapshot, token } = await VerifiedTokens.patch(store, chainId, address, body, {
322
+ const { snapshot, token } = await VerifiedTokens.patch(db, chainId, address, body, {
330
323
  ...ifMatch(c),
331
324
  })
332
325
  return c.json(
@@ -366,12 +359,11 @@ export function verifiedTokens() {
366
359
  tags: ['Verified Tokens'],
367
360
  }),
368
361
  async (c) => {
369
- const store = c.get('verifiedTokens')?.store
370
- if (!store) return notEnabled(c)
362
+ const db = Db.get(c.get('db'))
371
363
  const { address } = c.req.valid('param')
372
364
  const { chainId } = c.req.valid('query')
373
365
  try {
374
- const { snapshot } = await VerifiedTokens.remove(store, chainId, address, {
366
+ const { snapshot } = await VerifiedTokens.remove(db, chainId, address, {
375
367
  ...ifMatch(c),
376
368
  })
377
369
  return c.json(
@@ -476,9 +468,6 @@ export function verifiedTokens() {
476
468
  /** Largest accepted logo upload (SVG icons are tiny; this is generous). */
477
469
  const maxLogoBytes = 256 * 1024
478
470
 
479
- /** OpenAPI description shared by write routes' 404 when the feature is disabled. */
480
- const featureDisabledDescription = 'Verified-token management is not enabled (or not found).'
481
-
482
471
  /** Asset-store key the main API's loader reads for a token's curated icon. */
483
472
  function logoKey(chainId: number, address: string): string {
484
473
  return `${chainId}/icons/${address.toLowerCase()}`
@@ -511,10 +500,10 @@ function encodeLogo(logo: string): ArrayBuffer {
511
500
  // Returns the duplicate-address/symbol error a create would throw, or undefined,
512
501
  // so the logo upload can be skipped before it overwrites an existing icon.
513
502
  async function findDuplicate(
514
- store: Store.State,
503
+ db: Db.Db,
515
504
  token: { address: string; chainId: VerifiedTokens.ChainId; symbol: string },
516
505
  ): Promise<Error | undefined> {
517
- const snapshot = await VerifiedTokens.read(store, token.chainId)
506
+ const snapshot = await VerifiedTokens.read(db, token.chainId)
518
507
  if (!snapshot) return undefined
519
508
  if (snapshot.byAddress.has(token.address.toLowerCase()))
520
509
  return new VerifiedTokens.DuplicateAddressError({ address: token.address, chainId: token.chainId }) // prettier-ignore
@@ -556,11 +545,3 @@ function mutationError(c: Context<App.Environment>, cause: unknown) {
556
545
  return Response.error(c, { code: 'version_mismatch', message: cause.message, status: 412 })
557
546
  return Response.upstream(c, cause)
558
547
  }
559
-
560
- function notEnabled(c: Context<App.Environment>) {
561
- return Response.error(c, {
562
- code: 'verified_tokens_not_enabled',
563
- message: 'Verified-token management is not enabled for this deployment.',
564
- status: 404,
565
- })
566
- }
@@ -11,7 +11,7 @@ export type Asset = {
11
11
 
12
12
  /** The prerendered SPA shell HTML (assets normalized to relative URLs). */
13
13
  export const shell =
14
- '<!DOCTYPE html><html lang="en" data-theme="dark" data-regen-radius="none"><head><meta charSet="utf-8"/><meta content="width=device-width, initial-scale=1" name="viewport"/><link rel="stylesheet" href="./assets/styles-Cd7Fp5b_.css" data-precedence="default"/><title>Tempo API Admin</title><link rel="modulepreload" href="./assets/index-CPTihwDe.js"/><link rel="icon" type="image/svg+xml" href="data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%20269%20269%22%3E%3Crect%20width%3D%22269%22%20height%3D%22269%22%20fill%3D%22%23000%22%2F%3E%3Cpath%20d%3D%22M123.273%20190.794H93.445L121.09%20105.318H85.7334L93.445%2080.2642H191.95L184.238%20105.318H150.773L123.273%20190.794Z%22%20fill%3D%22%23fff%22%2F%3E%3C%2Fsvg%3E"/></head><body><div id="root"></div><script class="$tsr" id="$tsr-stream-barrier">(self.$R=self.$R||{})["tsr"]=[];self.$_TSR={h(){this.hydrated=!0,this.c()},e(){this.streamEnded=!0,this.c()},c(){this.hydrated&&this.streamEnded&&(delete self.$_TSR,delete self.$R.tsr)},p(e){this.initialized?e():this.buffer.push(e)},buffer:[]};$_TSR.router=($R=>$R[0]={manifest:$R[1]={routes:$R[2]={__root__:$R[3]={preloads:$R[4]=["./assets/index-CPTihwDe.js"],scripts:$R[5]=[$R[6]={attrs:$R[7]={type:"module",async:!0,src:"./assets/index-CPTihwDe.js"}}]}}},matches:$R[8]=[$R[9]={i:"__root__\u0000",u:1782959721668,s:"success",ssr:!0}],lastMatchId:"__root__\u0000"})($R["tsr"]);$_TSR.e();document.currentScript.remove()</script><script type="module" async="" src="./assets/index-CPTihwDe.js"></script></body></html>'
14
+ '<!DOCTYPE html><html lang="en" data-theme="dark" data-regen-radius="none"><head><meta charSet="utf-8"/><meta content="width=device-width, initial-scale=1" name="viewport"/><link rel="stylesheet" href="./assets/styles-Cd7Fp5b_.css" data-precedence="default"/><title>Tempo API Admin</title><link rel="modulepreload" href="./assets/index-CPTihwDe.js"/><link rel="icon" type="image/svg+xml" href="data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%20269%20269%22%3E%3Crect%20width%3D%22269%22%20height%3D%22269%22%20fill%3D%22%23000%22%2F%3E%3Cpath%20d%3D%22M123.273%20190.794H93.445L121.09%20105.318H85.7334L93.445%2080.2642H191.95L184.238%20105.318H150.773L123.273%20190.794Z%22%20fill%3D%22%23fff%22%2F%3E%3C%2Fsvg%3E"/></head><body><div id="root"></div><script class="$tsr" id="$tsr-stream-barrier">(self.$R=self.$R||{})["tsr"]=[];self.$_TSR={h(){this.hydrated=!0,this.c()},e(){this.streamEnded=!0,this.c()},c(){this.hydrated&&this.streamEnded&&(delete self.$_TSR,delete self.$R.tsr)},p(e){this.initialized?e():this.buffer.push(e)},buffer:[]};$_TSR.router=($R=>$R[0]={manifest:$R[1]={routes:$R[2]={__root__:$R[3]={preloads:$R[4]=["./assets/index-CPTihwDe.js"],scripts:$R[5]=[$R[6]={attrs:$R[7]={type:"module",async:!0,src:"./assets/index-CPTihwDe.js"}}]}}},matches:$R[8]=[$R[9]={i:"__root__\u0000",u:1783028662119,s:"success",ssr:!0}],lastMatchId:"__root__\u0000"})($R["tsr"]);$_TSR.e();document.currentScript.remove()</script><script type="module" async="" src="./assets/index-CPTihwDe.js"></script></body></html>'
15
15
 
16
16
  /** Content-hashed UI assets, keyed by their path under the mount (e.g. `assets/index-*.js`). */
17
17
  export const assets: Record<string, Asset> = {
@@ -1,4 +1,4 @@
1
- import { Schema } from 'cadent'
1
+ import { Schema } from 'tapimo'
2
2
  import { Value as core_Value } from 'ox'
3
3
 
4
4
  import * as TestApp from '../../test/App.js'
@@ -12,9 +12,10 @@ import * as Tokens from './tokens.js'
12
12
  // verified tokens the bundle used to provide; a test can still override
13
13
  // `verifiedTokens`.
14
14
  const runtime = Runtime.get()
15
- const verified = TestApp.verifiedStore(runtime.chainId)
15
+ const db = TestApp.database()
16
+ await TestApp.verifiedSeed(db, runtime.chainId)
16
17
  function app(options: TestApp.create.Options = {}) {
17
- return TestApp.client({ verifiedTokens: { store: verified }, ...options })
18
+ return TestApp.client({ db, ...options })
18
19
  }
19
20
 
20
21
  async function holderWithBalance(client: ReturnType<typeof app>) {
@@ -1,4 +1,4 @@
1
- import { Schema } from 'cadent'
1
+ import { Schema } from 'tapimo'
2
2
  import { Hex } from 'ox'
3
3
  import { zeroHash } from 'viem'
4
4
 
@@ -1,5 +1,5 @@
1
1
  import type * as z from 'zod/mini'
2
- import { Schema } from 'cadent'
2
+ import { Schema } from 'tapimo'
3
3
 
4
4
  import * as TestApp from '../../test/App.js'
5
5
  import * as Exchanges from './exchanges.js'
@@ -1,4 +1,4 @@
1
- import { RateLimit, Store } from 'cadent'
1
+ import { RateLimit, Store } from 'tapimo'
2
2
  import * as TestApp from '../../test/App.js'
3
3
  import * as Mppx from '../../test/Mppx.js'
4
4
  import * as Runtime from '../../test/runtime.js'
@@ -1,4 +1,4 @@
1
- import { Schema } from 'cadent'
1
+ import { Schema } from 'tapimo'
2
2
  import { Hex } from 'ox'
3
3
  import { zeroHash } from 'viem'
4
4
 
@@ -227,16 +227,14 @@ describe('POST /rpc', () => {
227
227
  try {
228
228
  const app = TestApp.create({
229
229
  auth: {
230
- source: TestApp.source({
231
- keys: [
232
- {
233
- id: 'key_indexer_only',
234
- orgId: 'org_test',
235
- scopes: ['indexer:query'],
236
- token: 'secret_indexer_key',
237
- },
238
- ],
239
- }),
230
+ keys: [
231
+ {
232
+ id: 'key_indexer_only',
233
+ orgId: 'org_test',
234
+ scopes: ['indexer:query'],
235
+ token: 'secret_indexer_key',
236
+ },
237
+ ],
240
238
  },
241
239
  })
242
240
  const response = await app.fetch(
@@ -266,6 +264,98 @@ describe('POST /rpc', () => {
266
264
  })
267
265
  })
268
266
 
267
+ describe('POST /rpc/relay', () => {
268
+ test('rejects anonymous callers', async () => {
269
+ const app = TestApp.create()
270
+
271
+ const response = await app.fetch(
272
+ new Request('http://tempo-api.test/rpc/relay', {
273
+ body: JSON.stringify({ id: 1, jsonrpc: '2.0', method: 'eth_chainId' }),
274
+ method: 'POST',
275
+ }),
276
+ )
277
+ const body = (await response.json()) as Record<string, unknown>
278
+ const { requestId, ...stable } = body
279
+
280
+ expect(response.status).toMatchInlineSnapshot(`401`)
281
+ expect(typeof requestId).toMatchInlineSnapshot(`"string"`)
282
+ expect(stable).toMatchInlineSnapshot(`
283
+ {
284
+ "error": {
285
+ "code": "api_key_missing",
286
+ "message": "Missing API key",
287
+ },
288
+ }
289
+ `)
290
+ })
291
+
292
+ test('rejects keys missing the relay scope', async () => {
293
+ // The shared test key holds the data/indexer scopes only.
294
+ const app = TestApp.create()
295
+
296
+ const response = await app.fetch(
297
+ new Request('http://tempo-api.test/rpc/relay', {
298
+ body: JSON.stringify({ id: 1, jsonrpc: '2.0', method: 'eth_chainId' }),
299
+ headers: { 'tempo-api-key': TestApp.key.token },
300
+ method: 'POST',
301
+ }),
302
+ )
303
+ const body = (await response.json()) as Record<string, unknown>
304
+ const { requestId, ...stable } = body
305
+
306
+ expect(response.status).toMatchInlineSnapshot(`403`)
307
+ expect(typeof requestId).toMatchInlineSnapshot(`"string"`)
308
+ expect(stable).toMatchInlineSnapshot(`
309
+ {
310
+ "error": {
311
+ "code": "api_key_forbidden",
312
+ "message": "API key missing required scope",
313
+ },
314
+ }
315
+ `)
316
+ })
317
+
318
+ test('proxies for keys holding rpc-relay', async () => {
319
+ const fetch = mockRpc()
320
+
321
+ try {
322
+ const app = TestApp.create({
323
+ auth: {
324
+ keys: [
325
+ {
326
+ id: 'key_relay',
327
+ orgId: 'org_test',
328
+ scopes: ['rpc-relay'],
329
+ token: 'secret_relay_key',
330
+ },
331
+ ],
332
+ },
333
+ rpc: { url: () => 'https://internal.rpc.test/json-rpc' },
334
+ })
335
+
336
+ const response = await app.fetch(
337
+ new Request('http://tempo-api.test/rpc/relay', {
338
+ body: JSON.stringify({ id: 1, jsonrpc: '2.0', method: 'eth_chainId' }),
339
+ headers: { 'tempo-api-key': 'secret_relay_key' },
340
+ method: 'POST',
341
+ }),
342
+ )
343
+
344
+ expect(response.status).toMatchInlineSnapshot(`200`)
345
+ expect(await response.json()).toMatchInlineSnapshot(`
346
+ {
347
+ "id": 1,
348
+ "jsonrpc": "2.0",
349
+ "result": "0xa5bf",
350
+ }
351
+ `)
352
+ expect(fetch.calls[0]?.url).toMatchInlineSnapshot(`"https://internal.rpc.test/json-rpc"`)
353
+ } finally {
354
+ fetch.restore()
355
+ }
356
+ })
357
+ })
358
+
269
359
  function mockRpc() {
270
360
  const original = globalThis.fetch
271
361
  const calls: {
@@ -1,5 +1,3 @@
1
- import { Store } from 'cadent'
2
-
3
1
  import * as TestApp from '../../test/App.js'
4
2
  import * as Runtime from '../../test/runtime.js'
5
3
  import * as VerifiedTokens from '../internal/VerifiedTokens.js'
@@ -11,7 +9,7 @@ const reader = {
11
9
  orgId: 'org_test',
12
10
  scopes: ['data:read'],
13
11
  token: 'secret_reader',
14
- } satisfies TestApp.source.Key
12
+ } satisfies TestApp.kvStore.Key
15
13
 
16
14
  /** A known list published into the store before exercising the read endpoint. */
17
15
  const seedList = [
@@ -27,11 +25,11 @@ function as(key: { token: string }) {
27
25
 
28
26
  /** Client whose verified-token store holds a published {@link seedList} snapshot. */
29
27
  async function seeded() {
30
- const store = Store.memory()
31
- await VerifiedTokens.replace(store, chainId, seedList)
28
+ const db = TestApp.database()
29
+ await VerifiedTokens.replace(db, chainId, seedList)
32
30
  return TestApp.client({
33
- auth: { source: TestApp.source({ keys: [reader] }) },
34
- verifiedTokens: { store },
31
+ auth: { keys: [reader] },
32
+ db,
35
33
  })
36
34
  }
37
35