tapimo 0.10.0 → 0.10.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (295) hide show
  1. package/dist/ApiKey.d.ts +2 -1
  2. package/dist/ApiKey.d.ts.map +1 -1
  3. package/dist/ApiKeys.d.ts +4 -2
  4. package/dist/ApiKeys.d.ts.map +1 -1
  5. package/dist/ApiKeys.js +26 -1
  6. package/dist/ApiKeys.js.map +1 -1
  7. package/dist/App.d.ts +6007 -5003
  8. package/dist/App.d.ts.map +1 -1
  9. package/dist/App.js +1 -0
  10. package/dist/App.js.map +1 -1
  11. package/dist/Webhooks.d.ts +1 -1
  12. package/dist/Webhooks.d.ts.map +1 -1
  13. package/dist/Webhooks.js +6 -6
  14. package/dist/Webhooks.js.map +1 -1
  15. package/dist/admin/App.d.ts +91 -84
  16. package/dist/admin/App.d.ts.map +1 -1
  17. package/dist/admin/apps/api-keys.d.ts +22 -13
  18. package/dist/admin/apps/api-keys.d.ts.map +1 -1
  19. package/dist/admin/apps/api-keys.js +6 -0
  20. package/dist/admin/apps/api-keys.js.map +1 -1
  21. package/dist/admin/apps/organizations.d.ts +4 -2
  22. package/dist/admin/apps/organizations.d.ts.map +1 -1
  23. package/dist/admin/ui.generated.d.ts +1 -1
  24. package/dist/admin/ui.generated.js +69 -69
  25. package/dist/admin/ui.generated.js.map +1 -1
  26. package/dist/analytics/Analytics.d.ts.map +1 -1
  27. package/dist/analytics/Analytics.js +232 -0
  28. package/dist/analytics/Analytics.js.map +1 -1
  29. package/dist/analytics/tables/requestEvents.d.ts +4 -0
  30. package/dist/analytics/tables/requestEvents.d.ts.map +1 -1
  31. package/dist/analytics/tables/requestEvents.js +2 -0
  32. package/dist/analytics/tables/requestEvents.js.map +1 -1
  33. package/dist/apps/Zones.d.ts +4266 -82
  34. package/dist/apps/Zones.d.ts.map +1 -1
  35. package/dist/apps/Zones.js +27 -8
  36. package/dist/apps/Zones.js.map +1 -1
  37. package/dist/apps/data/App.d.ts +3467 -2696
  38. package/dist/apps/data/App.d.ts.map +1 -1
  39. package/dist/apps/data/App.js +2 -0
  40. package/dist/apps/data/App.js.map +1 -1
  41. package/dist/apps/data/routes/activities.d.ts +2999 -100
  42. package/dist/apps/data/routes/activities.d.ts.map +1 -1
  43. package/dist/apps/data/routes/activities.js +340 -2
  44. package/dist/apps/data/routes/activities.js.map +1 -1
  45. package/dist/apps/data/routes/addresses.d.ts +514 -0
  46. package/dist/apps/data/routes/addresses.d.ts.map +1 -1
  47. package/dist/apps/data/routes/balances.d.ts +26 -26
  48. package/dist/apps/data/routes/balances.d.ts.map +1 -1
  49. package/dist/apps/data/routes/balances.js +7 -3
  50. package/dist/apps/data/routes/balances.js.map +1 -1
  51. package/dist/apps/data/routes/blocks.d.ts +4 -4
  52. package/dist/apps/data/routes/earn.d.ts +75 -75
  53. package/dist/apps/data/routes/earn.d.ts.map +1 -1
  54. package/dist/apps/data/routes/earn.js +65 -36
  55. package/dist/apps/data/routes/earn.js.map +1 -1
  56. package/dist/apps/data/routes/exchanges.d.ts +124 -120
  57. package/dist/apps/data/routes/exchanges.d.ts.map +1 -1
  58. package/dist/apps/data/routes/exchanges.js +115 -77
  59. package/dist/apps/data/routes/exchanges.js.map +1 -1
  60. package/dist/apps/data/routes/fee-amm.d.ts +24 -24
  61. package/dist/apps/data/routes/indexer.d.ts +2 -1
  62. package/dist/apps/data/routes/indexer.d.ts.map +1 -1
  63. package/dist/apps/data/routes/indexer.js +3 -2
  64. package/dist/apps/data/routes/indexer.js.map +1 -1
  65. package/dist/apps/data/routes/receipts.d.ts +4 -3
  66. package/dist/apps/data/routes/receipts.d.ts.map +1 -1
  67. package/dist/apps/data/routes/receipts.js +15 -14
  68. package/dist/apps/data/routes/receipts.js.map +1 -1
  69. package/dist/apps/data/routes/rpc.d.ts.map +1 -1
  70. package/dist/apps/data/routes/rpc.js +5 -1
  71. package/dist/apps/data/routes/rpc.js.map +1 -1
  72. package/dist/apps/data/routes/tokenlist.d.ts +12 -12
  73. package/dist/apps/data/routes/tokens.d.ts +1 -1
  74. package/dist/apps/data/routes/tokens.d.ts.map +1 -1
  75. package/dist/apps/data/routes/tokens.js +4 -3
  76. package/dist/apps/data/routes/tokens.js.map +1 -1
  77. package/dist/apps/data/routes/transfers.d.ts.map +1 -1
  78. package/dist/apps/data/routes/transfers.js +25 -7
  79. package/dist/apps/data/routes/transfers.js.map +1 -1
  80. package/dist/apps/data/routes/verified-tokens.d.ts +37 -37
  81. package/dist/apps/data/routes/webhooks.d.ts +71 -1
  82. package/dist/apps/data/routes/webhooks.d.ts.map +1 -1
  83. package/dist/apps/data/routes/webhooks.js +177 -23
  84. package/dist/apps/data/routes/webhooks.js.map +1 -1
  85. package/dist/apps/data/routes/zones.js +1 -1
  86. package/dist/apps/data/routes/zones.js.map +1 -1
  87. package/dist/apps/funding/App.d.ts +322 -89
  88. package/dist/apps/funding/App.d.ts.map +1 -1
  89. package/dist/apps/funding/routes/deposit-addresses.d.ts +375 -45
  90. package/dist/apps/funding/routes/deposit-addresses.d.ts.map +1 -1
  91. package/dist/apps/funding/routes/deposit-addresses.js +208 -12
  92. package/dist/apps/funding/routes/deposit-addresses.js.map +1 -1
  93. package/dist/apps/funding/routes/transfers.d.ts +32 -32
  94. package/dist/apps/funding/routes/transfers.js +1 -1
  95. package/dist/apps/funding/routes/transfers.js.map +1 -1
  96. package/dist/apps/management/App.d.ts +418 -418
  97. package/dist/apps/management/routes/api-keys.d.ts +50 -50
  98. package/dist/apps/management/routes/billing.d.ts +72 -72
  99. package/dist/apps/management/routes/billing.js +1 -1
  100. package/dist/apps/management/routes/billing.js.map +1 -1
  101. package/dist/apps/management/routes/faucet.d.ts +8 -8
  102. package/dist/apps/management/routes/faucet.js +2 -2
  103. package/dist/apps/management/routes/faucet.js.map +1 -1
  104. package/dist/apps/management/routes/invite-links.d.ts +42 -42
  105. package/dist/apps/management/routes/me.d.ts +6 -6
  106. package/dist/apps/management/routes/members.d.ts +14 -14
  107. package/dist/apps/management/routes/projects.d.ts +40 -40
  108. package/dist/apps/management/routes/usage.d.ts +8 -8
  109. package/dist/apps/management/routes/webhooks.d.ts +49 -49
  110. package/dist/apps/mcp.d.ts.map +1 -1
  111. package/dist/apps/mcp.js +4 -1
  112. package/dist/apps/mcp.js.map +1 -1
  113. package/dist/db/Db.d.ts +2 -0
  114. package/dist/db/Db.d.ts.map +1 -1
  115. package/dist/db/Db.js +90 -0
  116. package/dist/db/Db.js.map +1 -1
  117. package/dist/db/Schema.d.ts +19 -0
  118. package/dist/db/Schema.d.ts.map +1 -1
  119. package/dist/db/Schema.js +24 -0
  120. package/dist/db/Schema.js.map +1 -1
  121. package/dist/db/tables/fundingDepositAddresses.d.ts +20 -0
  122. package/dist/db/tables/fundingDepositAddresses.d.ts.map +1 -1
  123. package/dist/db/tables/fundingDepositAddresses.js +17 -0
  124. package/dist/db/tables/fundingDepositAddresses.js.map +1 -1
  125. package/dist/db/tables/fundingDeposits.d.ts +41 -0
  126. package/dist/db/tables/fundingDeposits.d.ts.map +1 -1
  127. package/dist/db/tables/fundingDeposits.js +69 -0
  128. package/dist/db/tables/fundingDeposits.js.map +1 -1
  129. package/dist/db/tables/webhookQueueEvents.d.ts +122 -0
  130. package/dist/db/tables/webhookQueueEvents.d.ts.map +1 -0
  131. package/dist/db/tables/webhookQueueEvents.js +160 -0
  132. package/dist/db/tables/webhookQueueEvents.js.map +1 -0
  133. package/dist/db/tables/webhookSubscriptions.d.ts +2 -0
  134. package/dist/db/tables/webhookSubscriptions.d.ts.map +1 -1
  135. package/dist/db/tables/webhookSubscriptions.js +10 -0
  136. package/dist/db/tables/webhookSubscriptions.js.map +1 -1
  137. package/dist/internal/Auth.d.ts +3 -26
  138. package/dist/internal/Auth.d.ts.map +1 -1
  139. package/dist/internal/Auth.js +6 -37
  140. package/dist/internal/Auth.js.map +1 -1
  141. package/dist/internal/Credential.d.ts +27 -0
  142. package/dist/internal/Credential.d.ts.map +1 -0
  143. package/dist/internal/Credential.js +34 -0
  144. package/dist/internal/Credential.js.map +1 -0
  145. package/dist/internal/EarnRates.js +11 -7
  146. package/dist/internal/EarnRates.js.map +1 -1
  147. package/dist/internal/Log.d.ts +8 -0
  148. package/dist/internal/Log.d.ts.map +1 -1
  149. package/dist/internal/Log.js +4 -0
  150. package/dist/internal/Log.js.map +1 -1
  151. package/dist/internal/MetricSink.d.ts +51 -0
  152. package/dist/internal/MetricSink.d.ts.map +1 -1
  153. package/dist/internal/MetricSink.js +54 -2
  154. package/dist/internal/MetricSink.js.map +1 -1
  155. package/dist/internal/RateLimit.d.ts +4 -6
  156. package/dist/internal/RateLimit.d.ts.map +1 -1
  157. package/dist/internal/RateLimit.js +15 -14
  158. package/dist/internal/RateLimit.js.map +1 -1
  159. package/dist/internal/Store.d.ts +2 -1
  160. package/dist/internal/Store.d.ts.map +1 -1
  161. package/dist/internal/Store.js +57 -18
  162. package/dist/internal/Store.js.map +1 -1
  163. package/dist/internal/Tidx.d.ts +4 -3
  164. package/dist/internal/Tidx.d.ts.map +1 -1
  165. package/dist/internal/Tidx.js +2 -2
  166. package/dist/internal/Tidx.js.map +1 -1
  167. package/dist/internal/Value.d.ts +2 -0
  168. package/dist/internal/Value.d.ts.map +1 -1
  169. package/dist/internal/Value.js +25 -0
  170. package/dist/internal/Value.js.map +1 -1
  171. package/dist/internal/Viem.d.ts +4 -3
  172. package/dist/internal/Viem.d.ts.map +1 -1
  173. package/dist/internal/Viem.js +3 -2
  174. package/dist/internal/Viem.js.map +1 -1
  175. package/dist/internal/WebhookTransfer.d.ts +20 -0
  176. package/dist/internal/WebhookTransfer.d.ts.map +1 -1
  177. package/dist/internal/WebhookTransfer.js +7 -4
  178. package/dist/internal/WebhookTransfer.js.map +1 -1
  179. package/dist/internal/Webhooks.d.ts +141 -0
  180. package/dist/internal/Webhooks.d.ts.map +1 -1
  181. package/dist/internal/Webhooks.js +139 -10
  182. package/dist/internal/Webhooks.js.map +1 -1
  183. package/dist/internal/funding/providers/relay.d.ts +3 -4
  184. package/dist/internal/funding/providers/relay.d.ts.map +1 -1
  185. package/dist/internal/funding/providers/relay.js +59 -8
  186. package/dist/internal/funding/providers/relay.js.map +1 -1
  187. package/dist/internal/index.d.ts +1 -0
  188. package/dist/internal/index.d.ts.map +1 -1
  189. package/dist/internal/index.js +1 -0
  190. package/dist/internal/index.js.map +1 -1
  191. package/package.json +3 -3
  192. package/src/ApiKeys.test.ts +20 -0
  193. package/src/ApiKeys.ts +28 -1
  194. package/src/App.test.ts +40 -8
  195. package/src/App.ts +1 -0
  196. package/src/Client.test-d.ts +34 -1
  197. package/src/Webhooks.ts +20 -18
  198. package/src/admin/apps/api-keys.test.ts +9 -0
  199. package/src/admin/apps/api-keys.ts +15 -0
  200. package/src/admin/ui/src/lib/query-client.ts +3 -2
  201. package/src/admin/ui/src/routes/api-keys.tsx +31 -9
  202. package/src/admin/ui.generated.ts +69 -69
  203. package/src/analytics/Analytics.test.ts +42 -0
  204. package/src/analytics/Analytics.ts +232 -0
  205. package/src/analytics/tables/requestEvents.ts +6 -0
  206. package/src/apps/Zones.test.ts +20 -3
  207. package/src/apps/Zones.ts +28 -8
  208. package/src/apps/composition.test.ts +2 -0
  209. package/src/apps/data/App.test.ts +2 -2
  210. package/src/apps/data/App.ts +2 -0
  211. package/src/apps/data/routes/activities.bench.ts +41 -0
  212. package/src/apps/data/routes/activities.test.ts +103 -2
  213. package/src/apps/data/routes/activities.ts +401 -2
  214. package/src/apps/data/routes/balances.bench.ts +21 -0
  215. package/src/apps/data/routes/balances.ts +7 -3
  216. package/src/apps/data/routes/blocks.bench.ts +34 -0
  217. package/src/apps/data/routes/coingecko.bench.ts +53 -0
  218. package/src/apps/data/routes/earn.bench.ts +71 -0
  219. package/src/apps/data/routes/earn.ts +66 -36
  220. package/src/apps/data/routes/exchanges.bench.ts +85 -0
  221. package/src/apps/data/routes/exchanges.test.ts +8 -3
  222. package/src/apps/data/routes/exchanges.ts +134 -82
  223. package/src/apps/data/routes/fee-amm.bench.ts +13 -0
  224. package/src/apps/data/routes/indexer.bench.ts +8 -0
  225. package/src/apps/data/routes/indexer.test.ts +2 -1
  226. package/src/apps/data/routes/indexer.ts +3 -2
  227. package/src/apps/data/routes/receipts.bench.ts +39 -0
  228. package/src/apps/data/routes/receipts.ts +16 -14
  229. package/src/apps/data/routes/rpc.bench.ts +14 -0
  230. package/src/apps/data/routes/rpc.test.ts +27 -0
  231. package/src/apps/data/routes/rpc.ts +5 -1
  232. package/src/apps/data/routes/tokenlist.bench.ts +3 -0
  233. package/src/apps/data/routes/tokens.bench.ts +76 -0
  234. package/src/apps/data/routes/tokens.ts +3 -2
  235. package/src/apps/data/routes/transactions.bench.ts +48 -0
  236. package/src/apps/data/routes/transfers.bench.ts +28 -0
  237. package/src/apps/data/routes/transfers.ts +36 -10
  238. package/src/apps/data/routes/valuation.bench.ts +14 -0
  239. package/src/apps/data/routes/verified-tokens.bench.ts +8 -0
  240. package/src/apps/data/routes/webhooks.bench.ts +26 -0
  241. package/src/apps/data/routes/webhooks.scan.test.ts +326 -0
  242. package/src/apps/data/routes/webhooks.test.ts +53 -6
  243. package/src/apps/data/routes/webhooks.ts +252 -24
  244. package/src/apps/data/routes/zones.bench.ts +32 -0
  245. package/src/apps/data/routes/zones.test.ts +1 -1
  246. package/src/apps/data/routes/zones.ts +1 -1
  247. package/src/apps/funding/routes/chains.test.ts +1 -1
  248. package/src/apps/funding/routes/deposit-addresses.test.ts +217 -3
  249. package/src/apps/funding/routes/deposit-addresses.ts +255 -19
  250. package/src/apps/funding/routes/providers.test.ts +1 -1
  251. package/src/apps/funding/routes/quotes.test.ts +1 -1
  252. package/src/apps/funding/routes/transfers.test.ts +20 -8
  253. package/src/apps/funding/routes/transfers.ts +1 -1
  254. package/src/apps/management/routes/billing.ts +1 -1
  255. package/src/apps/management/routes/faucet.ts +2 -2
  256. package/src/apps/management/routes/me.test.ts +31 -0
  257. package/src/apps/mcp.ts +4 -1
  258. package/src/db/Db.ts +117 -0
  259. package/src/db/Schema.ts +32 -0
  260. package/src/db/tables/fundingDepositAddresses.test.ts +28 -0
  261. package/src/db/tables/fundingDepositAddresses.ts +39 -0
  262. package/src/db/tables/fundingDeposits.test.ts +82 -0
  263. package/src/db/tables/fundingDeposits.ts +130 -0
  264. package/src/db/tables/webhookQueueEvents.ts +285 -0
  265. package/src/db/tables/webhookSubscriptions.ts +15 -0
  266. package/src/internal/Auth.test.ts +61 -98
  267. package/src/internal/Auth.ts +9 -63
  268. package/src/internal/Credential.test.ts +51 -0
  269. package/src/internal/Credential.ts +58 -0
  270. package/src/internal/EarnRates.ts +13 -7
  271. package/src/internal/EarnVaults.test.ts +2 -2
  272. package/src/internal/Log.test.ts +3 -1
  273. package/src/internal/Log.ts +14 -0
  274. package/src/internal/MetricSink.test.ts +108 -2
  275. package/src/internal/MetricSink.ts +112 -2
  276. package/src/internal/RateLimit.test.ts +23 -18
  277. package/src/internal/RateLimit.ts +23 -15
  278. package/src/internal/Store.test.ts +99 -13
  279. package/src/internal/Store.ts +52 -17
  280. package/src/internal/Tidx.test.ts +4 -4
  281. package/src/internal/Tidx.ts +5 -4
  282. package/src/internal/Value.test.ts +25 -0
  283. package/src/internal/Value.ts +29 -0
  284. package/src/internal/Viem.test.ts +4 -0
  285. package/src/internal/Viem.ts +6 -4
  286. package/src/internal/WebhookTransfer.test.ts +89 -0
  287. package/src/internal/WebhookTransfer.ts +11 -7
  288. package/src/internal/Webhooks.test.ts +488 -0
  289. package/src/internal/Webhooks.ts +291 -10
  290. package/src/internal/funding/Provider.test.ts +1 -1
  291. package/src/internal/funding/providers/index.test.ts +73 -3
  292. package/src/internal/funding/providers/relay.test.ts +1 -5
  293. package/src/internal/funding/providers/relay.ts +56 -12
  294. package/src/internal/index.test.ts +1 -0
  295. package/src/internal/index.ts +1 -0
package/src/Webhooks.ts CHANGED
@@ -19,7 +19,7 @@ export * from './internal/Webhooks.js'
19
19
  // type delivery bodies: `Webhooks.schema.Envelope` (a discriminated union on
20
20
  // `type`), plus the per-event `TransferEvent` / `TransactionEvent` / `PingEvent`
21
21
  // payloads. Infer a type with `z.infer<typeof Webhooks.schema.Envelope>`.
22
- export { eventTypes, schema } from './apps/data/routes/webhooks.js'
22
+ export { enrichEventData, eventTypes, scanBlock, schema } from './apps/data/routes/webhooks.js'
23
23
 
24
24
  /**
25
25
  * Builds a webhook poller bound to a state store and chain config.
@@ -74,23 +74,25 @@ export function createPoller(options: createPoller.Options): createPoller.Poller
74
74
  return result
75
75
  }
76
76
  : inner
77
- const dispatchBatch =
78
- metrics && innerBatch
79
- ? async (dispatchables: readonly AppWebhooks.poll.Dispatchable[]) => {
80
- const start = Date.now()
81
- const result = await innerBatch(dispatchables)
82
- const durationMs = Date.now() - start
83
- for (const dispatchable of dispatchables)
84
- metrics.record({
85
- chainId: dispatchable.envelope.chainId,
86
- durationMs,
87
- eventType: dispatchable.envelope.type,
88
- ok: result.ok,
89
- type: 'webhook:enqueue',
90
- })
91
- return result
92
- }
93
- : innerBatch
77
+ const dispatchBatch = innerBatch
78
+ ? async (
79
+ dispatchables: readonly AppWebhooks.poll.Dispatchable[],
80
+ context: AppWebhooks.poll.DispatchContext,
81
+ ) => {
82
+ const start = Date.now()
83
+ const result = await innerBatch(dispatchables, context)
84
+ const durationMs = Date.now() - start
85
+ for (const dispatchable of dispatchables)
86
+ metrics?.record({
87
+ chainId: dispatchable.envelope.chainId,
88
+ durationMs,
89
+ eventType: dispatchable.envelope.type,
90
+ ok: result.ok,
91
+ type: 'webhook:enqueue',
92
+ })
93
+ return result
94
+ }
95
+ : undefined
94
96
 
95
97
  // Resolve per tick so Workers factories build a fresh Hyperdrive-pooled
96
98
  // connection each run.
@@ -105,6 +105,15 @@ describe('POST /api-keys', () => {
105
105
  expect((await core_ApiKeys.list(store))[0]?.expiresAt).toBe(expiresAt)
106
106
  })
107
107
 
108
+ test('rejects an expiry without the minimum storage lead time', async () => {
109
+ const { client } = await setup()
110
+ const response = await client['api-keys'].$post({
111
+ json: { ...validBody, expiresAt: new Date(Date.now() + 60_000).toISOString() },
112
+ })
113
+
114
+ expect(response.status).toBe(400)
115
+ })
116
+
108
117
  test('rejects an unknown scope with 400', async () => {
109
118
  const { client } = await setup()
110
119
  const response = await client['api-keys'].$post({
@@ -15,6 +15,9 @@ import type * as App from '../App.js'
15
15
 
16
16
  /** Zod schemas owned by the admin API-keys handler. */
17
17
  export namespace schema {
18
+ // Leave time for validation and database lookups before KV writes its minimum 60-second TTL.
19
+ const minimumExpiryLeadMs = 2 * 60_000
20
+
18
21
  const Name = z
19
22
  .pipe(
20
23
  z.string(),
@@ -27,6 +30,17 @@ export namespace schema {
27
30
  z.meta({ examples: ['Explorer testnet'] }),
28
31
  )
29
32
 
33
+ const ExpiresAt = z.optional(z.iso.datetime()).check(
34
+ z.refine(
35
+ (value) => value === undefined || Date.parse(value) >= Date.now() + minimumExpiryLeadMs,
36
+ {
37
+ error: 'Expiry must be at least two minutes in the future.',
38
+ },
39
+ ),
40
+ z.describe('ISO 8601 expiry timestamp. Omit for a non-expiring key.'),
41
+ z.meta({ examples: ['2100-01-01T00:00:00.000Z'] }),
42
+ )
43
+
30
44
  const ApiKey = Schema.describe(
31
45
  z.extend(ApiKeys.schema.Record, {
32
46
  createdByLabel: z
@@ -59,6 +73,7 @@ export namespace schema {
59
73
  */
60
74
  export const Body = Schema.describe(
61
75
  z.extend(z.omit(ApiKeys.schema.MintInput, { createdBy: true, name: true }), {
76
+ expiresAt: ExpiresAt,
62
77
  name: Name,
63
78
  scopes: z
64
79
  .readonly(
@@ -17,12 +17,13 @@ const persister = createAsyncStoragePersister({
17
17
  })
18
18
 
19
19
  export const persistOptions: Omit<PersistQueryClientOptions, 'queryClient'> = {
20
- maxAge: 1000 * 60 * 60 * 24,
21
- persister,
20
+ buster: 'rate-limit-periods',
22
21
  dehydrateOptions: {
23
22
  shouldDehydrateQuery: (query) =>
24
23
  query.queryKey[0] !== 'config' &&
25
24
  query.queryKey[0] !== 'organizations' &&
26
25
  defaultShouldDehydrateQuery(query),
27
26
  },
27
+ maxAge: 1000 * 60 * 60 * 24,
28
+ persister,
28
29
  }
@@ -59,9 +59,11 @@ function RouteComponent() {
59
59
  alert('Enter a name that identifies what uses this key.')
60
60
  return
61
61
  }
62
+ const expiresAt_value = field('expiresAt')
63
+ const expiresAt = expiresAt_value ? new Date(expiresAt_value).toISOString() : undefined
62
64
  const orgId = filters.orgId ?? field('orgId')
63
65
  const rateLimit = field('rateLimit')
64
- const perMinute = rateLimit ? Number(rateLimit) : undefined
66
+ const limit = rateLimit ? Number(rateLimit) : undefined
65
67
  const environment = field('environment') === 'sandbox' ? 'sandbox' : 'production'
66
68
  const pendingZoneScopes = (['read', 'write'] as const).map((access) => {
67
69
  const chainId = field(`zoneChainId_${access}`)
@@ -87,10 +89,13 @@ function RouteComponent() {
87
89
  provision.mutate(
88
90
  {
89
91
  environment,
92
+ ...(expiresAt === undefined ? {} : { expiresAt }),
90
93
  name,
91
94
  ...(orgId ? { orgId } : {}),
92
95
  ...(filters.projectId ? { projectId: filters.projectId } : {}),
93
- ...(perMinute === undefined ? {} : { rateLimits: { '*': { perMinute } } }),
96
+ ...(limit === undefined
97
+ ? {}
98
+ : { rateLimits: { '*': { limit, period: 'minute' as const } } }),
94
99
  scopes: scopes as ApiKeys.provision.Input['scopes'],
95
100
  },
96
101
  { onError: (error) => alert(error.message), onSuccess: () => setOpen(false) },
@@ -273,7 +278,7 @@ function ProvisionForm(props: {
273
278
  </div>
274
279
  ) : null}
275
280
  <Fields
276
- className="min-h-0 flex-1 gap-6 [grid-template-rows:auto_auto_auto_auto_minmax(0,1fr)]"
281
+ className="min-h-0 flex-1 gap-6 overflow-y-auto overscroll-contain"
277
282
  variant="default"
278
283
  >
279
284
  <OrganizationAutocomplete orgId={props.orgId} />
@@ -289,6 +294,12 @@ function ProvisionForm(props: {
289
294
  <option value="sandbox">Sandbox</option>
290
295
  </select>
291
296
  </div>
297
+ <Input
298
+ label="Expires at (optional)"
299
+ min={toLocalDateTimeCeil(new Date(Date.now() + 2 * 60_000))}
300
+ name="expiresAt"
301
+ type="datetime-local"
302
+ />
292
303
  <Input
293
304
  defaultValue="10000"
294
305
  label="Rate limit"
@@ -298,14 +309,14 @@ function ProvisionForm(props: {
298
309
  suffix="req/min"
299
310
  type="number"
300
311
  />
301
- <div className="flex min-h-0 flex-col gap-2 overflow-hidden">
312
+ <div className="flex flex-col gap-2">
302
313
  <span className="label-13 text-foreground-secondary">Scopes</span>
303
314
  {scopes.isPending ? (
304
315
  <span className="text-label-13 text-foreground-secondary">Loading scopes…</span>
305
316
  ) : scopes.error ? (
306
317
  <span className="text-label-13 text-negative">{scopes.error.message}</span>
307
318
  ) : (
308
- <div className="min-h-0 flex-1 overflow-y-auto overscroll-contain border border-border">
319
+ <div className="border border-border">
309
320
  <Rows variant="surface">
310
321
  {(scopes.data?.data ?? []).map((entry) => {
311
322
  const zoneAccess = Scopes.accessFromTemplate(entry.scope)
@@ -618,7 +629,7 @@ function KeyList(props: {
618
629
  function KeyRow(props: { currentUser?: string | undefined; record: ApiKeys.ApiKey }) {
619
630
  const { currentUser, record } = props
620
631
  const [expanded, setExpanded] = useState(false)
621
- const perMinute = record.rateLimits?.['*']?.perMinute
632
+ const rateLimit = record.rateLimits?.['*']
622
633
 
623
634
  return (
624
635
  <>
@@ -659,7 +670,9 @@ function KeyRow(props: { currentUser?: string | undefined; record: ApiKeys.ApiKe
659
670
  <Detail label="Key ID">{record.id}</Detail>
660
671
  <Detail label="Environment">{record.environment ?? 'production'}</Detail>
661
672
  <Detail label="Rate limit">
662
- {perMinute === undefined ? 'Inherited' : `${perMinute} req/min`}
673
+ {rateLimit === undefined
674
+ ? 'Inherited'
675
+ : `${rateLimit.limit} req/${rateLimit.period === 'minute' ? 'min' : 'sec'}`}
663
676
  </Detail>
664
677
  <Detail label="Token">···{record.tokenLast4}</Detail>
665
678
  <Detail label="Organization">{organization(record)}</Detail>
@@ -804,9 +817,11 @@ function RevokeDialog(props: { currentUser?: string | undefined; record: ApiKeys
804
817
  <Detail label="Project">{project(record)}</Detail>
805
818
  <Detail label="Provisioned by">{creator ?? 'Unknown'}</Detail>
806
819
  <Detail label="Rate limit">
807
- {record.rateLimits?.['*']?.perMinute === undefined
820
+ {record.rateLimits?.['*'] === undefined
808
821
  ? 'Inherited'
809
- : `${record.rateLimits['*']!.perMinute} req/min`}
822
+ : `${record.rateLimits['*']!.limit} req/${
823
+ record.rateLimits['*']!.period === 'minute' ? 'min' : 'sec'
824
+ }`}
810
825
  </Detail>
811
826
  </dl>
812
827
  {differentCreator ? (
@@ -858,6 +873,13 @@ function Detail(props: { children: ReactNode; className?: string; label: ReactNo
858
873
  )
859
874
  }
860
875
 
876
+ /** Rounds an instant up for a minute-precision `datetime-local` control. */
877
+ function toLocalDateTimeCeil(date: Date) {
878
+ const minute = 60_000
879
+ const time = Math.ceil(date.getTime() / minute) * minute
880
+ return new Date(time - date.getTimezoneOffset() * minute).toISOString().slice(0, 16)
881
+ }
882
+
861
883
  function matches(record: ApiKeys.ApiKey, query?: string) {
862
884
  const normalized = query?.trim().toLowerCase()
863
885
  if (!normalized) return true