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/App.ts CHANGED
@@ -5,13 +5,15 @@ import { cors as hono_cors } from 'hono/cors'
5
5
  import { HTTPException } from 'hono/http-exception'
6
6
  import { requestId, type RequestIdVariables } from 'hono/request-id'
7
7
  import { generateSpecs } from 'hono-openapi'
8
- import { Handler } from 'vocs/server'
8
+ import { Handler as vocs_Handler } from 'vocs/server'
9
9
  import * as z from 'zod/mini'
10
10
 
11
11
  import * as ApiKey from './ApiKey.js'
12
12
  import * as Assets from './Assets.js'
13
13
  import * as Auth from './internal/Auth.js'
14
+ import * as Db from './db/Db.js'
14
15
  import * as EdgeCache from './internal/EdgeCache.js'
16
+ import * as Handler from './Handler.js'
15
17
  import * as Log from './internal/Log.js'
16
18
  import * as MetricSink from './internal/MetricSink.js'
17
19
  import type * as Metrics from './Metrics.js'
@@ -22,6 +24,7 @@ import * as Response from './internal/Response.js'
22
24
  import * as Schema from './internal/Schema.js'
23
25
  import * as Tidx from './internal/Tidx.js'
24
26
  import * as Timing from './internal/Timing.js'
27
+ import * as VerifiedTokens from './internal/VerifiedTokens.js'
25
28
  import * as Viem from './internal/Viem.js'
26
29
 
27
30
  import openrpcSpec from './openrpc.json' with { type: 'json' }
@@ -50,6 +53,12 @@ export type Environment = {
50
53
  basePath: string
51
54
  /** Default Tempo chain id for this API app. */
52
55
  chainId: Viem.ChainId
56
+ /** Authoritative Postgres store, or a per-request factory. Resolve at the leaf with `Db.get`. */
57
+ db: Db.Source
58
+ /** Store for staleness-tolerant reads (a caching Hyperdrive config when configured); falls back to `db`. */
59
+ dbCached: Db.Source
60
+ /** KV state store backing API-key records, or undefined when key auth is closed. */
61
+ kv: { store: Store.State } | undefined
53
62
  /** Gets one static API asset. */
54
63
  getAsset: GetAsset
55
64
  /** Gets a Tempo RPC client for a chain id. */
@@ -64,10 +73,10 @@ export type Environment = {
64
73
  supportedChainIds: ReadonlySet<number>
65
74
  /** Configured TIDX client options used by the raw indexer passthrough. */
66
75
  tidx: Tidx.getClient.Tidx | undefined
67
- /** Verified-token feature configuration (state store + refresh window), or undefined when verified tokens are bundle-backed. */
68
- verifiedTokens: { refreshMs?: number | undefined; store: Store.State } | undefined
69
- /** Webhook feature configuration (state store, etc.), or undefined when webhooks are disabled. */
70
- webhook: { maxPerOwner?: number | undefined; store: Store.State } | undefined
76
+ /** Verified-token feature configuration (refresh window), or undefined when the feature is disabled. Token rows live in `db`. */
77
+ verifiedTokens: { refreshMs?: number | undefined } | undefined
78
+ /** Webhook feature configuration, or undefined when webhooks are disabled. Rows live in `db`. */
79
+ webhook: { maxPerOwner?: number | undefined } | undefined
71
80
  } & EdgeCache.Variables &
72
81
  Log.Variables &
73
82
  Timing.Variables
@@ -108,7 +117,7 @@ export type GetAsset = (
108
117
  const infoDescription = 'REST API for reading and interacting with Tempo'
109
118
 
110
119
  /** Creates the Tempo API Hono app. */
111
- export function create<const path extends string = '/'>(options: create.Options<path> = {}) {
120
+ export function create<const path extends string = '/'>(options: create.Options<path>) {
112
121
  const { assets, defaultChainId = Viem.defaultChainId, path = '/' as path, rpc } = options
113
122
 
114
123
  // Capture as the literal generic so `Hono.basePath(...)` preserves the
@@ -123,8 +132,8 @@ export function create<const path extends string = '/'>(options: create.Options<
123
132
  // miss simply re-fetches — so it defaults to in-memory. Point it at a
124
133
  // colo-local store (`Store.cache(caches.default)`) in production; never the
125
134
  // Durable Object, or edge-cache hits re-incur the round-trip they skip.
126
- // Authoritative state (webhooks) lives in its own state store, configured
127
- // separately via `options.webhook.store` (a Durable Object in production).
135
+ // Authoritative state lives elsewhere: webhooks + verified tokens in
136
+ // `options.db`, API-key records in `options.kv`.
128
137
  const store = options.cache?.store ?? Store.memory()
129
138
 
130
139
  // Memoize one viem client per resolved upstream. Clients are stateless HTTP
@@ -197,8 +206,8 @@ export function create<const path extends string = '/'>(options: create.Options<
197
206
  base.use(
198
207
  '*',
199
208
  Log.middleware<Environment>({
200
- emit: (entry) => {
201
- log?.(entry)
209
+ emit: async (entry) => {
210
+ await log?.(entry)
202
211
  recordRequest?.(entry)
203
212
  // Return the analytics promise so `Log.middleware` extends the
204
213
  // response with `waitUntil`; otherwise the async sink's `queue.send`
@@ -219,21 +228,29 @@ export function create<const path extends string = '/'>(options: create.Options<
219
228
  ...(options.supportedChainIds ?? []),
220
229
  ])
221
230
 
231
+ // Normalize the `db` option: the plain form is `{ source }` with no cached
232
+ // side; `cached` falls back to `source` so consumers never branch.
233
+ const db = 'source' in options.db ? options.db : { source: options.db }
234
+
222
235
  base.use('*', Timing.middleware())
223
236
  base.use('*', async (c, next) => {
224
237
  // Put app-level dependencies on Hono context so mounted handlers only
225
238
  // receive request-local state from `c`.
226
239
  c.set('basePath', basePath)
227
240
  c.set('chainId', defaultChainId)
241
+ c.set('db', db.source)
242
+ c.set('dbCached', db.cached ?? db.source)
228
243
  c.set('getAsset', createGetAsset({ assets, basePath, origin: new URL(c.req.url).origin }))
229
244
  c.set('getClient', (chainId) => getClient(chainId, Auth.getPrincipal(c)))
230
245
  c.set('getTidx', (chainId) => getTidx(chainId, Auth.getPrincipal(c)))
246
+ c.set('kv', options.kv)
231
247
  c.set('rpc', rpc)
232
248
  c.set('store', store)
233
249
  c.set('supportedChainIds', supportedChainIds)
234
250
  c.set('tidx', options.tidx)
235
- c.set('verifiedTokens', options.verifiedTokens)
236
- c.set('webhook', options.webhook)
251
+ // Feature configs default to enabled empty configs; `false` disables.
252
+ c.set('verifiedTokens', options.verifiedTokens === false ? undefined : (options.verifiedTokens ?? {})) // prettier-ignore
253
+ c.set('webhook', options.webhook === false ? undefined : (options.webhook ?? {}))
237
254
  await next()
238
255
  })
239
256
 
@@ -297,8 +314,33 @@ export function create<const path extends string = '/'>(options: create.Options<
297
314
  .route('/v1/transactions', transactionActivities())
298
315
  .route('/v1/transfers', transfers())
299
316
  .route('/v1/verified-tokens', verifiedTokens())
300
- .route('/v1/webhooks', webhooks({ enabled: options.webhook !== undefined }))
317
+ .route('/v1/webhooks', webhooks({ enabled: options.webhook !== false }))
301
318
  .route('/rpc', rpcApp())
319
+ .use(
320
+ '/rpc/relay',
321
+ Auth.policy({ apiKey: { scopes: ['rpc-relay'] }, mpp: false, public: false }),
322
+ OpenApi.describeRoute({ hide: true, summary: 'Relay RPC' }),
323
+ )
324
+ .use(
325
+ '/rpc/relay/*',
326
+ Auth.policy({ apiKey: { scopes: ['rpc-relay'] }, mpp: false, public: false }),
327
+ OpenApi.describeRoute({ hide: true, summary: 'Relay RPC' }),
328
+ )
329
+ .route(
330
+ '/rpc/relay',
331
+ Handler.relay({
332
+ cache: store,
333
+ getClient: (chainId) => getClient(chainId as Viem.ChainId | undefined),
334
+ ...(options.verifiedTokens === false
335
+ ? {}
336
+ : {
337
+ resolveTokens: async (chainId: number) => {
338
+ const snapshot = await VerifiedTokens.read(Db.get(db.cached ?? db.source), chainId)
339
+ return snapshot?.list.map((token) => token.address) ?? []
340
+ },
341
+ }),
342
+ }),
343
+ )
302
344
  .route('/gecko', coingecko())
303
345
  // Hidden compatibility route for the prior Tempo API's
304
346
  // `GET /chains/:chainId/blocks/by-timestamp` (chain id in the path).
@@ -308,7 +350,7 @@ export function create<const path extends string = '/'>(options: create.Options<
308
350
  // OAS 3.1 `webhooks`: document the signed envelope Tempo API POSTs to subscriber
309
351
  // URLs (an outbound payload, not an endpoint of this API). Only when enabled.
310
352
  const webhooksDoc = (() => {
311
- if (!options.webhook) return {}
353
+ if (options.webhook === false) return {}
312
354
  // `io: 'output'` documents the delivered (serialized) shape; `unrepresentable: 'any'`
313
355
  // tolerates the transaction payload's transforms (e.g. lazy fee-token metadata),
314
356
  // which have no JSON-Schema form, by emitting `{}` for those nodes rather than throwing.
@@ -422,7 +464,7 @@ export function create<const path extends string = '/'>(options: create.Options<
422
464
  name: 'Verified Tokens',
423
465
  description: 'A curated, trusted list of TIP-20 tokens.',
424
466
  },
425
- ...(options.webhook
467
+ ...(options.webhook !== false
426
468
  ? [
427
469
  {
428
470
  name: 'Webhooks',
@@ -470,7 +512,7 @@ export function create<const path extends string = '/'>(options: create.Options<
470
512
  if (options.docs !== false)
471
513
  app.route(
472
514
  '/',
473
- Handler.openApi(
515
+ vocs_Handler.openApi(
474
516
  {
475
517
  // Inline the OpenRPC doc so vocs expands it without a self-subrequest
476
518
  // (see `inlineOpenrpc`); the public `/openapi.json` keeps the URL ref.
@@ -529,8 +571,12 @@ export declare namespace create {
529
571
  compress?: boolean | Compress | undefined
530
572
  /** CORS for API handlers. Enabled by default (wildcard origin, no credentials); pass a config object to customize, true for defaults, or false to disable (e.g. when CORS is handled by a fronting gateway/CDN). */
531
573
  cors?: boolean | Cors | undefined
574
+ /** Authoritative Postgres store backing stateful features (webhooks, verified tokens). Pass a long-lived `Db.postgres(...)` singleton on Node, or a factory (`() => Db.postgres(...)`) on Workers so each request builds a fresh Hyperdrive-pooled connection. Pass `{ source, cached }` to route staleness-tolerant reads (verified tokens) through a second, query-caching Hyperdrive config; `source` stays uncached for read-after-write correctness. Apply migrations out of band via `tapimo admin migrate`. */
575
+ db: Db.Source | { cached?: Db.Source | undefined; source: Db.Source }
532
576
  /** Default Tempo chain id for requests that omit `chainId`. */
533
577
  defaultChainId?: Viem.ChainId | undefined
578
+ /** KV state store backing API-key records (e.g. `Store.cloudflareKv(...)`). Omit to leave API-key auth closed — every token resolves to `null`. */
579
+ kv?: Kv | undefined
534
580
  /** Vocs API reference options. Set to false to disable the `/` docs page. */
535
581
  docs?: Docs | false | undefined
536
582
  /** Request logging. Disabled by default. Pass `true` to emit one canonical structured log entry per request through the level-matched console method (indexed by Workers Logs); a level (e.g. `'warn'`) to emit only that level and above (dropping successful-request info lines); or an emit function for a custom sink. */
@@ -549,10 +595,21 @@ export declare namespace create {
549
595
  rpc?: Rpc | undefined
550
596
  /** TIDX query client options. */
551
597
  tidx?: Tidx.getClient.Tidx | undefined
552
- /** Verified-token feature configuration. Omit to serve an empty verified list (nothing is verified). Provide a state `store` (Cloudflare KV or a Durable Object in production; never the cache, which a miss would silently drop) to make the curated list API-managed and durable. */
553
- verifiedTokens?: VerifiedTokens | undefined
554
- /** Webhook feature configuration. Omit to disable webhooks: the `/webhooks` routes stay mounted (for a stable schema and typed test client) but return `404` and are hidden from the OpenAPI document. Requires a state `store` (a Durable Object in production; never the cache, which a miss would silently drop). */
555
- webhook?: Webhook | undefined
598
+ /** Verified-token feature configuration. Enabled by default (token rows live in `db`; empty until published); pass `false` to always serve an empty verified list. */
599
+ verifiedTokens?: VerifiedTokens | false | undefined
600
+ /** Webhook feature configuration. Enabled by default (subscription/delivery rows live in `db`). Pass `false` to disable: the `/webhooks` routes stay mounted (for a stable schema and typed test client) but return `404` and are hidden from the OpenAPI document. */
601
+ webhook?: Webhook | false | undefined
602
+ }
603
+
604
+ /** KV state-store configuration. */
605
+ type Kv = {
606
+ /**
607
+ * State store backing API-key records. Must be a {@link Store.State}
608
+ * (e.g. `Store.cloudflareKv(...)` or `Store.memory()`); the Web Cache
609
+ * adapter (`Store.cache`) is rejected at the type level since a miss there
610
+ * would silently drop authoritative state.
611
+ */
612
+ store: Store.State
556
613
  }
557
614
 
558
615
  /** Caching configuration. */
@@ -568,17 +625,10 @@ export declare namespace create {
568
625
  /**
569
626
  * Per-isolate soft-refresh window in milliseconds. Within this window
570
627
  * verified-token lookups are served from the in-memory snapshot with no
571
- * store I/O; after it, the next lookup re-checks the store's `head` version
628
+ * database I/O; after it, the next lookup re-checks the head version
572
629
  * (single-flight) and recompiles only when it changed. Defaults to 10_000.
573
630
  */
574
631
  refreshMs?: number | undefined
575
- /**
576
- * State store backing the curated verified-token snapshots. Must be a
577
- * {@link Store.State} (e.g. `Store.cloudflareKv(...)`, `Store.durableObject(...)`,
578
- * or `Store.memory()`); the Web Cache adapter (`Store.cache`) is rejected at
579
- * the type level since a miss there would silently drop authoritative state.
580
- */
581
- store: Store.State
582
632
  }
583
633
 
584
634
  /** Webhook feature configuration. */
@@ -589,13 +639,6 @@ export declare namespace create {
589
639
  * spam/SSRF surface and unbounded downstream delivery cost per owner.
590
640
  */
591
641
  maxPerOwner?: number | undefined
592
- /**
593
- * State store backing webhook subscriptions, cursors, and the delivery
594
- * log. Must be a {@link Store.State} (e.g. `Store.durableObject(...)` or
595
- * `Store.memory()`); the Web Cache adapter (`Store.cache`) is rejected at
596
- * the type level since a miss there would silently drop authoritative state.
597
- */
598
- store: Store.State
599
642
  }
600
643
 
601
644
  /** Binary asset store for static API assets (e.g. `Assets.cloudflareR2(...)`). */
@@ -608,7 +651,7 @@ export declare namespace create {
608
651
  type Cors = NonNullable<Parameters<typeof hono_cors>[0]>
609
652
 
610
653
  /** Vocs API reference options (theme, top navigation, logo, socials, …). */
611
- type Docs = NonNullable<Parameters<typeof Handler.openApi>[0]['vocs']>
654
+ type Docs = NonNullable<Parameters<typeof vocs_Handler.openApi>[0]['vocs']>
612
655
 
613
656
  /** Custom request-log sink, receiving one canonical entry per request. */
614
657
  type Logger = Log.Emit
@@ -1,4 +1,4 @@
1
- import { Client } from 'cadent'
1
+ import { Client } from 'tapimo'
2
2
  import type * as z from 'zod/mini'
3
3
 
4
4
  import * as Activities from './apps/activities.js'
package/src/Handler.ts ADDED
@@ -0,0 +1,154 @@
1
+ import { Hono } from 'hono'
2
+ import type { ExtractSchema, MergeSchemaPath, Schema } from 'hono/types'
3
+
4
+ import type { UnionToIntersection } from './handlers/internal/types.js'
5
+ import * as RequestListener from './handlers/internal/requestListener.js'
6
+
7
+ export { relay } from './handlers/relay.js'
8
+ export * as Multisig from './handlers/internal/multisig.js'
9
+
10
+ /**
11
+ * A request handler. Generic over the underlying Hono app shape so chained
12
+ * route definitions (`.post`, `.get`, …) can flow their type information
13
+ * through to consumers — most notably for Hono's typed RPC client
14
+ * (`hc<typeof handler>`).
15
+ */
16
+ export type Handler<app extends Hono<any, any, any> = Hono> = app & {
17
+ listener: (req: any, res: any) => void
18
+ }
19
+
20
+ /**
21
+ * Merges the route schemas of every sub-handler into a single Hono schema,
22
+ * prefixing each route key with `path`. Used by `compose()` to preserve
23
+ * typed routes for Hono's RPC client (`hc`).
24
+ */
25
+ export type ComposedSchema<subs extends readonly unknown[], path extends string> =
26
+ UnionToIntersection<
27
+ subs[number] extends infer sub
28
+ ? sub extends unknown
29
+ ? MergeSchemaPath<Extract<ExtractSchema<sub>, Schema>, path>
30
+ : never
31
+ : never
32
+ > extends infer schema extends Schema
33
+ ? schema
34
+ : never
35
+
36
+ /**
37
+ * Mounts each sub-handler onto a fresh Hono app at `path` (default `/`) and
38
+ * returns a single composed handler. Routes are dispatched by Hono's trie
39
+ * — a deterministic match per path — instead of the older "try each handler
40
+ * in order until one returns non-404" loop.
41
+ *
42
+ * The returned handler preserves each sub-handler's route schema so the
43
+ * composed app stays usable from Hono's typed RPC client:
44
+ *
45
+ * ```ts
46
+ * import { hc } from 'hono/client'
47
+ *
48
+ * const app = Handler.compose(
49
+ * [Handler.exchange(), Handler.codeAuth({ store })],
50
+ * { path: '/api' },
51
+ * )
52
+ * type App = typeof app
53
+ *
54
+ * const client = hc<App>('https://wallet.example.com')
55
+ * await client.api.exchange.quote.$post({ json: { ... } }) // typed
56
+ * ```
57
+ */
58
+ export function compose<const subs extends readonly Handler[], const path extends string = '/'>(
59
+ handlers: subs,
60
+ options: compose.Options & { path?: path } = {},
61
+ ): Handler<Hono<{}, ComposedSchema<subs, path>, '/'>> {
62
+ const mountPath = (options.path ?? '/') as path
63
+
64
+ const app = from(options) as unknown as Hono
65
+ for (const sub of handlers) app.route(mountPath, sub)
66
+ app.notFound(() => new Response('Not Found', { status: 404 }))
67
+
68
+ return app as never
69
+ }
70
+
71
+ export declare namespace compose {
72
+ export type Options = from.Options & {
73
+ /** The path to use for the handler. */
74
+ path?: string | undefined
75
+ }
76
+ }
77
+
78
+ /**
79
+ * Instantiates a new request handler.
80
+ *
81
+ * @param options - constructor options
82
+ * @returns Handler instance
83
+ */
84
+ export function from(options: from.Options = {}): Handler {
85
+ const corsHeaders = corsToHeaders(options.cors)
86
+ const mergedHeaders = new Headers(corsHeaders)
87
+ for (const [key, value] of normalizeHeaders(options.headers).entries())
88
+ mergedHeaders.set(key, value)
89
+
90
+ const app = new Hono()
91
+
92
+ app.use(async (c, next) => {
93
+ if (c.req.method === 'OPTIONS') return new Response(null, { headers: mergedHeaders })
94
+ await next()
95
+ for (const [key, value] of mergedHeaders.entries()) c.res.headers.set(key, value)
96
+ return undefined
97
+ })
98
+
99
+ return Object.assign(app, {
100
+ listener: RequestListener.fromFetchHandler((request) => app.fetch(request)),
101
+ }) as never
102
+ }
103
+
104
+ export declare namespace from {
105
+ export type Options = {
106
+ /**
107
+ * CORS configuration.
108
+ * - `true` (default): Allow all origins with default methods/headers
109
+ * - `false`: Disable CORS headers
110
+ * - Object: Custom CORS configuration
111
+ */
112
+ cors?: boolean | Cors | undefined
113
+ /** Headers to add to the response. */
114
+ headers?: Headers | Record<string, string> | undefined
115
+ }
116
+
117
+ export type Cors = {
118
+ /** Allowed origins. Defaults to `'*'`. */
119
+ origin?: string | string[] | undefined
120
+ /** Allowed methods. Defaults to `'GET, POST, PUT, DELETE, OPTIONS'`. */
121
+ methods?: string | undefined
122
+ /** Allowed headers. Defaults to `'Content-Type'`. */
123
+ headers?: string | undefined
124
+ /** Whether to allow credentials. */
125
+ credentials?: boolean | undefined
126
+ /** Headers to expose to the browser. */
127
+ exposeHeaders?: string | undefined
128
+ /** Max age for preflight cache in seconds. */
129
+ maxAge?: number | undefined
130
+ }
131
+ }
132
+
133
+ function normalizeHeaders(headers?: Headers | Record<string, string>): Headers {
134
+ if (!headers) return new Headers()
135
+ if (headers instanceof Headers) return headers
136
+ return new Headers(headers)
137
+ }
138
+
139
+ function corsToHeaders(cors?: boolean | from.Cors): Headers {
140
+ if (cors === false) return new Headers()
141
+
142
+ const config = cors === true || cors === undefined ? {} : cors
143
+
144
+ const headers = new Headers()
145
+ const origin = Array.isArray(config.origin) ? config.origin.join(', ') : (config.origin ?? '*')
146
+ headers.set('Access-Control-Allow-Origin', origin)
147
+ headers.set('Access-Control-Allow-Methods', config.methods ?? 'GET, POST, PUT, DELETE, OPTIONS')
148
+ headers.set('Access-Control-Allow-Headers', config.headers ?? 'Content-Type')
149
+ if (config.credentials) headers.set('Access-Control-Allow-Credentials', 'true')
150
+ if (config.exposeHeaders) headers.set('Access-Control-Expose-Headers', config.exposeHeaders)
151
+ if (config.maxAge !== undefined) headers.set('Access-Control-Max-Age', String(config.maxAge))
152
+
153
+ return headers
154
+ }
package/src/Scope.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * The issuable API-key scope vocabulary: the single source of truth for the
3
- * `/scopes` catalog, for key minting (`ApiKeySource.Source.mint`), and for the scopes
3
+ * `/scopes` catalog, for key minting (`ApiKeys.mint`), and for the scopes
4
4
  * required by route policies (`Auth.policy`). Exposing it publicly lets operator
5
5
  * tooling (e.g. the provisioning CLI) validate scopes against the same list the
6
6
  * server enforces.
@@ -36,6 +36,10 @@ export const catalog = [
36
36
  description: 'Run ad-hoc queries against the raw indexer passthrough.',
37
37
  scope: 'indexer:query',
38
38
  },
39
+ {
40
+ description: 'Fill and submit transactions through the relay.',
41
+ scope: 'rpc-relay',
42
+ },
39
43
  {
40
44
  description: 'Read webhook subscriptions.',
41
45
  scope: 'webhooks:read',
@@ -1,4 +1,5 @@
1
- import { Store, Webhooks } from 'cadent'
1
+ import { Webhooks } from 'tapimo'
2
+ import * as TestApp from '../test/App.js'
2
3
 
3
4
  describe('createPoller', () => {
4
5
  /** Decodes the opaque `(block, index)` keyset cursor the poller stores. */
@@ -8,9 +9,9 @@ describe('createPoller', () => {
8
9
  return JSON.parse(json) as [number, number]
9
10
  }
10
11
 
11
- test('binds a state store and runs a tick at an injected head', async () => {
12
- const store = Store.memory()
13
- const subscription = await Webhooks.createSubscription(store, {
12
+ test('binds a database and runs a tick at an injected head', async () => {
13
+ const db = TestApp.database()
14
+ const subscription = await Webhooks.createSubscription(db, {
14
15
  chainId: 42431,
15
16
  destination: { type: 'url', url: 'https://hooks.example.com/x' },
16
17
  eventType: 'token:transfer',
@@ -18,12 +19,12 @@ describe('createPoller', () => {
18
19
  })
19
20
 
20
21
  const poller = Webhooks.createPoller({
22
+ db,
21
23
  defaultChainId: 42431,
22
- webhook: { store },
23
24
  })
24
25
 
25
26
  // A brand-new subscription anchors at the confirmed head on its first tick
26
- // (no backfill), so this exercises the createPoller → poll wiring (store
27
+ // (no backfill), so this exercises the createPoller → poll wiring (db
27
28
  // binding, default chain id, injected head, result shape) without touching
28
29
  // TIDX. Detailed poll/getIndexedHead behavior lives in
29
30
  // src/apps/webhooks.test.ts.
@@ -39,7 +40,7 @@ describe('createPoller', () => {
39
40
  "subscriptions": 1,
40
41
  }
41
42
  `)
42
- expect(decodeCursor(await Webhooks.getCursor(store, subscription.id))).toMatchInlineSnapshot(`
43
+ expect(decodeCursor(await Webhooks.getCursor(db, subscription.id))).toMatchInlineSnapshot(`
43
44
  [
44
45
  198,
45
46
  2147483647,
package/src/Webhooks.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import * as AppWebhooks from './apps/webhooks.js'
2
+ import * as Db from './db/Db.js'
2
3
  import * as MetricSink from './internal/MetricSink.js'
3
- import type * as Store from './internal/Store.js'
4
+ import * as Store from './internal/Store.js'
4
5
  import type * as Metrics from './Metrics.js'
5
6
  import * as Tidx from './internal/Tidx.js'
6
7
  import * as Viem from './internal/Viem.js'
@@ -35,7 +36,9 @@ export { eventTypes, schema } from './apps/webhooks.js'
35
36
  * request state.
36
37
  */
37
38
  export function createPoller(options: createPoller.Options): createPoller.Poller {
38
- const { store } = options.webhook
39
+ // Persistent memoize cache for token metadata, shared across ticks (the
40
+ // webhook state store used to double-duty as this cache).
41
+ const cache = Store.memory()
39
42
  const getTidx = options.tidx
40
43
  ? Tidx.createGetClient({ defaultChainId: options.defaultChainId, tidx: options.tidx })
41
44
  : undefined
@@ -80,9 +83,13 @@ export function createPoller(options: createPoller.Options): createPoller.Poller
80
83
  try {
81
84
  const result = await AppWebhooks.poll({
82
85
  batchSize: input.batchSize ?? options.batchSize,
86
+ cache,
83
87
  chainId,
84
88
  client: getClient(chainId),
85
89
  confirmations: input.confirmations ?? options.confirmations,
90
+ // Resolve per tick so Workers factories build a fresh
91
+ // Hyperdrive-pooled connection each run.
92
+ db: Db.get(options.db),
86
93
  dispatch,
87
94
  eventTypes: input.eventTypes ?? options.eventTypes,
88
95
  ...(input.headBlockNumber === undefined
@@ -90,7 +97,6 @@ export function createPoller(options: createPoller.Options): createPoller.Poller
90
97
  : { headBlockNumber: input.headBlockNumber }),
91
98
  maxBlocks: input.maxBlocks ?? options.maxBlocks,
92
99
  now: input.now ?? options.now,
93
- store,
94
100
  ...(getTidx ? { tidx: getTidx(chainId) } : {}),
95
101
  })
96
102
  metrics?.record({
@@ -144,6 +150,8 @@ export declare namespace createPoller {
144
150
 
145
151
  /** Options for {@link createPoller}. */
146
152
  type Options = Defaults & {
153
+ /** Database holding subscriptions + cursors, or a factory resolved per tick (Workers/Hyperdrive). */
154
+ db: Db.Source
147
155
  /** Tempo chain id polled when a tick omits one. */
148
156
  defaultChainId?: Viem.ChainId | undefined
149
157
  /** Metrics backend; emits `webhook:tick` per tick and `webhook:enqueue` per dispatch. */
@@ -152,8 +160,6 @@ export declare namespace createPoller {
152
160
  rpc?: Viem.getClient.Rpc | undefined
153
161
  /** TIDX query client options for the legacy `token:transfer` type. */
154
162
  tidx?: Tidx.getClient.Tidx | undefined
155
- /** Webhook state store (subscriptions + cursors). */
156
- webhook: { store: Store.State }
157
163
  }
158
164
 
159
165
  /** Per-tick overrides; falls back to the {@link Options} defaults. */
@@ -29,10 +29,8 @@ const cookieName = 'CF_Authorization'
29
29
  * ```ts
30
30
  * import { App, Access } from 'tapimo/admin'
31
31
  *
32
- * const source = {} as App.create.Options['apiKey']['source']
33
- *
34
32
  * App.create({
35
- * apiKey: { source },
33
+ * // ...
36
34
  * identify: Access.cloudflareAccess({
37
35
  * teamDomain: 'https://tempo.cloudflareaccess.com',
38
36
  * }),
package/src/admin/App.ts CHANGED
@@ -3,10 +3,10 @@ import { HTTPException } from 'hono/http-exception'
3
3
  import { requestId, type RequestIdVariables } from 'hono/request-id'
4
4
 
5
5
  import * as ApiKey from '../ApiKey.js'
6
- import * as ApiKeySource from '../ApiKeySource.js'
7
6
  import type * as Assets from '../Assets.js'
8
- import * as Response from '../internal/Response.js'
7
+ import type * as Db from '../db/Db.js'
9
8
  import type * as Store from '../internal/Store.js'
9
+ import * as Response from '../internal/Response.js'
10
10
 
11
11
  import { apiKeys } from './apps/api-keys.js'
12
12
  import { scopes } from './apps/scopes.js'
@@ -24,12 +24,12 @@ export type Environment = {
24
24
  Variables: RequestIdVariables & {
25
25
  /** Static-asset writer (e.g. the R2 icon bucket), when configured. */
26
26
  assets: create.Assets | undefined
27
+ /** Authoritative Postgres store, or a per-request factory. Resolve at the leaf with `Db.get`. */
28
+ db: Db.Source
29
+ /** KV state store backing API-key records (mint/list/revoke). */
30
+ kv: { store: Store.State }
27
31
  /** The admin identity, or `null` when unauthenticated (gated routes reject). */
28
32
  identity: Identity | null
29
- /** The API-key source provisioned against (mint/list/revoke). */
30
- source: ApiKeySource.Source
31
- /** Verified-token state store, when management is enabled. */
32
- verifiedTokens: { store: Store.State } | undefined
33
33
  }
34
34
  }
35
35
 
@@ -39,7 +39,6 @@ export type App = ReturnType<typeof create>
39
39
  /** Creates the admin portal Hono app. */
40
40
  export function create(options: create.Options) {
41
41
  const { identify } = options
42
- const { source } = options.apiKey
43
42
  // `false` (disabled) collapses to `undefined` so only a real config surfaces
44
43
  // `loginUrl`/`logoutUrl` in `/config.json`.
45
44
  const ui = options.ui || undefined
@@ -55,9 +54,9 @@ export function create(options: create.Options) {
55
54
  app.use('*', async (c, next) => {
56
55
  // Admin data is per-principal and mutating: never cache.
57
56
  c.header('Cache-Control', 'no-store')
58
- c.set('source', source)
59
57
  c.set('assets', options.assets)
60
- c.set('verifiedTokens', options.verifiedTokens)
58
+ c.set('db', options.db)
59
+ c.set('kv', options.kv)
61
60
  c.set('identity', await identify(c.req.raw))
62
61
  return next()
63
62
  })
@@ -173,16 +172,22 @@ export declare namespace create {
173
172
 
174
173
  /** Options for {@link create}. */
175
174
  type Options = {
176
- /** API-key provisioning config (mirrors the API app's `apiKey` option). */
177
- apiKey: {
178
- /** Source to provision against (mint/list/revoke). */
179
- source: ApiKeySource.Source
180
- }
181
175
  /**
182
176
  * Static-asset writer for logo uploads (shared with the main API's `assets`
183
177
  * loader) to enable `PUT /verified-tokens/:address/logo`; omit for `404`.
184
178
  */
185
179
  assets?: Assets | undefined
180
+ /**
181
+ * Authoritative Postgres store shared with the main API (verified
182
+ * tokens). Pass a long-lived `Db.postgres(...)` singleton on Node, or a
183
+ * factory (`() => Db.postgres(...)`) on Workers.
184
+ */
185
+ db: Db.Source
186
+ /**
187
+ * KV state store backing API-key records (shared with the main API's
188
+ * `kv` option), provisioned against by the `/api-keys` routes.
189
+ */
190
+ kv: { store: Store.State }
186
191
  /**
187
192
  * Extracts the verified admin identity from a request, or `null` to deny.
188
193
  * Must only return an identity for an already-authenticated request (a
@@ -194,16 +199,6 @@ export declare namespace create {
194
199
  * `loginUrl`/`logoutUrl` are surfaced in `GET /config.json`.
195
200
  */
196
201
  ui?: Ui | false | undefined
197
- /**
198
- * Verified-token management config (store shared with the main API's reads)
199
- * to enable the `/verified-tokens` routes; omit to serve them as `404`.
200
- */
201
- verifiedTokens?:
202
- | {
203
- /** Verified-token state store (shared with the main API's reads). */
204
- store: Store.State
205
- }
206
- | undefined
207
202
  }
208
203
 
209
204
  /** Admin UI options surfaced to the SPA via `GET /config.json`. */