tapimo 0.2.0 → 0.3.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 (313) hide show
  1. package/README.md +31 -3
  2. package/dist/ApiKey.d.ts +1 -0
  3. package/dist/ApiKey.d.ts.map +1 -1
  4. package/dist/ApiKey.js +5 -2
  5. package/dist/ApiKey.js.map +1 -1
  6. package/dist/ApiKeys.d.ts +28 -1
  7. package/dist/ApiKeys.d.ts.map +1 -1
  8. package/dist/ApiKeys.js +34 -1
  9. package/dist/ApiKeys.js.map +1 -1
  10. package/dist/App.d.ts +7167 -4507
  11. package/dist/App.d.ts.map +1 -1
  12. package/dist/App.js +8 -0
  13. package/dist/App.js.map +1 -1
  14. package/dist/Scope.d.ts +11 -2
  15. package/dist/Scope.d.ts.map +1 -1
  16. package/dist/Scope.js +14 -3
  17. package/dist/Scope.js.map +1 -1
  18. package/dist/admin/App.d.ts +128 -118
  19. package/dist/admin/App.d.ts.map +1 -1
  20. package/dist/admin/apps/api-keys.d.ts +7 -0
  21. package/dist/admin/apps/api-keys.d.ts.map +1 -1
  22. package/dist/admin/apps/scopes.d.ts +8 -0
  23. package/dist/admin/apps/scopes.d.ts.map +1 -1
  24. package/dist/admin/apps/scopes.js +3 -0
  25. package/dist/admin/apps/scopes.js.map +1 -1
  26. package/dist/admin/apps/verified-tokens.d.ts +57 -57
  27. package/dist/admin/ui.generated.d.ts +1 -1
  28. package/dist/admin/ui.generated.js +13 -13
  29. package/dist/admin/ui.generated.js.map +1 -1
  30. package/dist/analytics/Analytics.d.ts +151 -0
  31. package/dist/analytics/Analytics.d.ts.map +1 -0
  32. package/dist/analytics/Analytics.js +212 -0
  33. package/dist/analytics/Analytics.js.map +1 -0
  34. package/dist/analytics/tables/requestEvents.d.ts +215 -0
  35. package/dist/analytics/tables/requestEvents.d.ts.map +1 -0
  36. package/dist/analytics/tables/requestEvents.js +264 -0
  37. package/dist/analytics/tables/requestEvents.js.map +1 -0
  38. package/dist/apps/data/App.d.ts +1024 -624
  39. package/dist/apps/data/App.d.ts.map +1 -1
  40. package/dist/apps/data/App.js +36 -0
  41. package/dist/apps/data/App.js.map +1 -1
  42. package/dist/apps/data/routes/balances.d.ts +45 -45
  43. package/dist/apps/data/routes/coingecko.d.ts +176 -176
  44. package/dist/apps/data/routes/exchanges.d.ts +455 -455
  45. package/dist/apps/data/routes/fee-amm.d.ts +101 -101
  46. package/dist/apps/data/routes/funding.d.ts +765 -0
  47. package/dist/apps/data/routes/funding.d.ts.map +1 -0
  48. package/dist/apps/data/routes/funding.js +383 -0
  49. package/dist/apps/data/routes/funding.js.map +1 -0
  50. package/dist/apps/data/routes/receipts.d.ts.map +1 -1
  51. package/dist/apps/data/routes/receipts.js +2 -8
  52. package/dist/apps/data/routes/receipts.js.map +1 -1
  53. package/dist/apps/data/routes/rpc.d.ts +29 -36
  54. package/dist/apps/data/routes/rpc.d.ts.map +1 -1
  55. package/dist/apps/data/routes/rpc.js +29 -1
  56. package/dist/apps/data/routes/rpc.js.map +1 -1
  57. package/dist/apps/data/routes/tokenlist.d.ts +36 -36
  58. package/dist/apps/data/routes/transfers.d.ts +56 -56
  59. package/dist/apps/data/routes/verified-tokens.d.ts +36 -36
  60. package/dist/apps/data/routes/webhooks.d.ts +45 -45
  61. package/dist/apps/data/routes/webhooks.d.ts.map +1 -1
  62. package/dist/apps/data/routes/webhooks.js +8 -8
  63. package/dist/apps/data/routes/webhooks.js.map +1 -1
  64. package/dist/apps/docs/App.d.ts +4 -2
  65. package/dist/apps/docs/App.d.ts.map +1 -1
  66. package/dist/apps/docs/App.js +27 -95
  67. package/dist/apps/docs/App.js.map +1 -1
  68. package/dist/apps/docs/reference.d.ts +21 -0
  69. package/dist/apps/docs/reference.d.ts.map +1 -0
  70. package/dist/apps/docs/reference.js +112 -0
  71. package/dist/apps/docs/reference.js.map +1 -0
  72. package/dist/apps/index.d.ts +1 -0
  73. package/dist/apps/index.d.ts.map +1 -1
  74. package/dist/apps/index.js +1 -0
  75. package/dist/apps/index.js.map +1 -1
  76. package/dist/apps/mcp.d.ts +76 -0
  77. package/dist/apps/mcp.d.ts.map +1 -0
  78. package/dist/apps/mcp.js +188 -0
  79. package/dist/apps/mcp.js.map +1 -0
  80. package/dist/apps/platform/App.d.ts +3005 -728
  81. package/dist/apps/platform/App.d.ts.map +1 -1
  82. package/dist/apps/platform/App.js +13 -3
  83. package/dist/apps/platform/App.js.map +1 -1
  84. package/dist/apps/platform/Billing.d.ts +279 -0
  85. package/dist/apps/platform/Billing.d.ts.map +1 -0
  86. package/dist/apps/platform/Billing.js +477 -0
  87. package/dist/apps/platform/Billing.js.map +1 -0
  88. package/dist/apps/platform/index.d.ts +1 -0
  89. package/dist/apps/platform/index.d.ts.map +1 -1
  90. package/dist/apps/platform/index.js +1 -0
  91. package/dist/apps/platform/index.js.map +1 -1
  92. package/dist/apps/platform/routes/api-keys.d.ts +341 -49
  93. package/dist/apps/platform/routes/api-keys.d.ts.map +1 -1
  94. package/dist/apps/platform/routes/api-keys.js +115 -13
  95. package/dist/apps/platform/routes/api-keys.js.map +1 -1
  96. package/dist/apps/platform/routes/billing.d.ts +1520 -0
  97. package/dist/apps/platform/routes/billing.d.ts.map +1 -0
  98. package/dist/apps/platform/routes/billing.js +795 -0
  99. package/dist/apps/platform/routes/billing.js.map +1 -0
  100. package/dist/apps/platform/routes/invitations.d.ts +201 -83
  101. package/dist/apps/platform/routes/invitations.d.ts.map +1 -1
  102. package/dist/apps/platform/routes/invitations.js +58 -0
  103. package/dist/apps/platform/routes/invitations.js.map +1 -1
  104. package/dist/apps/platform/routes/me.d.ts +17 -17
  105. package/dist/apps/platform/routes/orgs.d.ts +50 -36
  106. package/dist/apps/platform/routes/orgs.d.ts.map +1 -1
  107. package/dist/apps/platform/routes/orgs.js +14 -3
  108. package/dist/apps/platform/routes/orgs.js.map +1 -1
  109. package/dist/apps/platform/routes/projects.d.ts +97 -97
  110. package/dist/apps/platform/routes/scopes.d.ts +107 -0
  111. package/dist/apps/platform/routes/scopes.d.ts.map +1 -0
  112. package/dist/apps/platform/routes/scopes.js +66 -0
  113. package/dist/apps/platform/routes/scopes.js.map +1 -0
  114. package/dist/apps/platform/routes/usage.d.ts +588 -0
  115. package/dist/apps/platform/routes/usage.d.ts.map +1 -0
  116. package/dist/apps/platform/routes/usage.js +391 -0
  117. package/dist/apps/platform/routes/usage.js.map +1 -0
  118. package/dist/apps/relay/App.d.ts +4 -1
  119. package/dist/apps/relay/App.d.ts.map +1 -1
  120. package/dist/apps/relay/App.js +118 -10
  121. package/dist/apps/relay/App.js.map +1 -1
  122. package/dist/apps/relay/Sponsorships.d.ts.map +1 -1
  123. package/dist/apps/relay/Sponsorships.js +2 -1
  124. package/dist/apps/relay/Sponsorships.js.map +1 -1
  125. package/dist/cli/Cli.d.ts +8 -1
  126. package/dist/cli/Cli.d.ts.map +1 -1
  127. package/dist/cli/Cli.js +11 -3
  128. package/dist/cli/Cli.js.map +1 -1
  129. package/dist/cli/commands/admin.d.ts +4 -0
  130. package/dist/cli/commands/admin.d.ts.map +1 -1
  131. package/dist/cli/commands/admin.js +80 -1
  132. package/dist/cli/commands/admin.js.map +1 -1
  133. package/dist/db/Db.d.ts +7 -0
  134. package/dist/db/Db.d.ts.map +1 -1
  135. package/dist/db/Db.js +126 -0
  136. package/dist/db/Db.js.map +1 -1
  137. package/dist/db/Schema.d.ts +79 -0
  138. package/dist/db/Schema.d.ts.map +1 -1
  139. package/dist/db/Schema.js +88 -2
  140. package/dist/db/Schema.js.map +1 -1
  141. package/dist/db/tables/billingSettings.d.ts +55 -0
  142. package/dist/db/tables/billingSettings.d.ts.map +1 -0
  143. package/dist/db/tables/billingSettings.js +70 -0
  144. package/dist/db/tables/billingSettings.js.map +1 -0
  145. package/dist/db/tables/invitations.js +2 -2
  146. package/dist/db/tables/invitations.js.map +1 -1
  147. package/dist/db/tables/organizations.d.ts +6 -2
  148. package/dist/db/tables/organizations.d.ts.map +1 -1
  149. package/dist/db/tables/organizations.js +10 -4
  150. package/dist/db/tables/organizations.js.map +1 -1
  151. package/dist/db/tables/projects.js +2 -2
  152. package/dist/db/tables/projects.js.map +1 -1
  153. package/dist/db/tables/requestUsage.d.ts +121 -0
  154. package/dist/db/tables/requestUsage.d.ts.map +1 -0
  155. package/dist/db/tables/requestUsage.js +150 -0
  156. package/dist/db/tables/requestUsage.js.map +1 -0
  157. package/dist/db/tables/sponsoredTransactions.d.ts +119 -0
  158. package/dist/db/tables/sponsoredTransactions.d.ts.map +1 -1
  159. package/dist/db/tables/sponsoredTransactions.js +155 -2
  160. package/dist/db/tables/sponsoredTransactions.js.map +1 -1
  161. package/dist/db/tables/stripeCustomers.d.ts +81 -0
  162. package/dist/db/tables/stripeCustomers.d.ts.map +1 -0
  163. package/dist/db/tables/stripeCustomers.js +120 -0
  164. package/dist/db/tables/stripeCustomers.js.map +1 -0
  165. package/dist/db/tables/users.js +2 -2
  166. package/dist/db/tables/users.js.map +1 -1
  167. package/dist/handlers/internal/sponsorship.d.ts +5 -2
  168. package/dist/handlers/internal/sponsorship.d.ts.map +1 -1
  169. package/dist/handlers/internal/sponsorship.js +19 -5
  170. package/dist/handlers/internal/sponsorship.js.map +1 -1
  171. package/dist/handlers/internal/utils.d.ts +10 -3
  172. package/dist/handlers/internal/utils.d.ts.map +1 -1
  173. package/dist/handlers/internal/utils.js +14 -6
  174. package/dist/handlers/internal/utils.js.map +1 -1
  175. package/dist/index.d.ts +1 -0
  176. package/dist/index.d.ts.map +1 -1
  177. package/dist/index.js +1 -0
  178. package/dist/index.js.map +1 -1
  179. package/dist/internal/Auth.d.ts +18 -4
  180. package/dist/internal/Auth.d.ts.map +1 -1
  181. package/dist/internal/Auth.js +148 -76
  182. package/dist/internal/Auth.js.map +1 -1
  183. package/dist/internal/Cache.d.ts.map +1 -1
  184. package/dist/internal/Cache.js +4 -1
  185. package/dist/internal/Cache.js.map +1 -1
  186. package/dist/internal/EdgeCache.d.ts +10 -0
  187. package/dist/internal/EdgeCache.d.ts.map +1 -1
  188. package/dist/internal/EdgeCache.js +39 -8
  189. package/dist/internal/EdgeCache.js.map +1 -1
  190. package/dist/internal/Fees.d.ts +27 -0
  191. package/dist/internal/Fees.d.ts.map +1 -0
  192. package/dist/internal/Fees.js +48 -0
  193. package/dist/internal/Fees.js.map +1 -0
  194. package/dist/internal/FundingRoutes.d.ts +183 -0
  195. package/dist/internal/FundingRoutes.d.ts.map +1 -0
  196. package/dist/internal/FundingRoutes.js +490 -0
  197. package/dist/internal/FundingRoutes.js.map +1 -0
  198. package/dist/internal/Id.d.ts +15 -0
  199. package/dist/internal/Id.d.ts.map +1 -0
  200. package/dist/internal/Id.js +22 -0
  201. package/dist/internal/Id.js.map +1 -0
  202. package/dist/internal/Log.d.ts +2 -0
  203. package/dist/internal/Log.d.ts.map +1 -1
  204. package/dist/internal/Log.js +3 -0
  205. package/dist/internal/Log.js.map +1 -1
  206. package/dist/internal/OpenApi.d.ts +10 -1
  207. package/dist/internal/OpenApi.d.ts.map +1 -1
  208. package/dist/internal/OpenApi.js +8 -1
  209. package/dist/internal/OpenApi.js.map +1 -1
  210. package/dist/internal/RateLimit.d.ts +25 -0
  211. package/dist/internal/RateLimit.d.ts.map +1 -1
  212. package/dist/internal/RateLimit.js +51 -0
  213. package/dist/internal/RateLimit.js.map +1 -1
  214. package/dist/internal/Response.d.ts +1 -1
  215. package/dist/internal/Response.d.ts.map +1 -1
  216. package/dist/internal/Schema.d.ts +8 -0
  217. package/dist/internal/Schema.d.ts.map +1 -1
  218. package/dist/internal/Schema.js +7 -0
  219. package/dist/internal/Schema.js.map +1 -1
  220. package/dist/internal/Webhooks.d.ts.map +1 -1
  221. package/dist/internal/Webhooks.js +3 -3
  222. package/dist/internal/Webhooks.js.map +1 -1
  223. package/package.json +4 -3
  224. package/src/ApiKey.ts +9 -2
  225. package/src/ApiKeys.test.ts +51 -0
  226. package/src/ApiKeys.ts +50 -1
  227. package/src/App.test.ts +33 -86718
  228. package/src/App.ts +18 -0
  229. package/src/Client.test-d.ts +351 -0
  230. package/src/Scope.ts +16 -3
  231. package/src/admin/apps/scopes.test.ts +6 -0
  232. package/src/admin/apps/scopes.ts +3 -0
  233. package/src/admin/ui/src/components/Page.tsx +2 -3
  234. package/src/admin/ui/src/routes/__root.tsx +2 -2
  235. package/src/admin/ui/src/routes/api-keys.tsx +2 -2
  236. package/src/admin/ui.generated.ts +13 -13
  237. package/src/analytics/Analytics.test.ts +527 -0
  238. package/src/analytics/Analytics.ts +318 -0
  239. package/src/analytics/tables/requestEvents.ts +523 -0
  240. package/src/apps/data/App.test.ts +72 -0
  241. package/src/apps/data/App.ts +35 -0
  242. package/src/apps/data/routes/funding.test.ts +214 -0
  243. package/src/apps/data/routes/funding.ts +584 -0
  244. package/src/apps/data/routes/receipts.ts +2 -9
  245. package/src/apps/data/routes/rpc.test.ts +78 -2
  246. package/src/apps/data/routes/rpc.ts +31 -1
  247. package/src/apps/data/routes/webhooks.test.ts +1 -1
  248. package/src/apps/data/routes/webhooks.ts +11 -8
  249. package/src/apps/docs/App.ts +29 -100
  250. package/src/apps/docs/reference.ts +132 -0
  251. package/src/apps/index.ts +1 -0
  252. package/src/apps/mcp.test.ts +206 -0
  253. package/src/apps/mcp.ts +250 -0
  254. package/src/apps/platform/App.ts +43 -4
  255. package/src/apps/platform/Billing.test.ts +530 -0
  256. package/src/apps/platform/Billing.ts +682 -0
  257. package/src/apps/platform/index.ts +1 -0
  258. package/src/apps/platform/routes/api-keys.test.ts +172 -9
  259. package/src/apps/platform/routes/api-keys.ts +136 -20
  260. package/src/apps/platform/routes/billing.test.ts +1100 -0
  261. package/src/apps/platform/routes/billing.ts +966 -0
  262. package/src/apps/platform/routes/invitations.test.ts +55 -7
  263. package/src/apps/platform/routes/invitations.ts +68 -0
  264. package/src/apps/platform/routes/me.test.ts +230 -0
  265. package/src/apps/platform/routes/orgs.ts +26 -4
  266. package/src/apps/platform/routes/scopes.test.ts +29 -0
  267. package/src/apps/platform/routes/scopes.ts +85 -0
  268. package/src/apps/platform/routes/usage.test.ts +422 -0
  269. package/src/apps/platform/routes/usage.ts +527 -0
  270. package/src/apps/relay/App.test.ts +457 -8
  271. package/src/apps/relay/App.ts +120 -12
  272. package/src/apps/relay/Sponsorships.test.ts +1 -1
  273. package/src/apps/relay/Sponsorships.ts +2 -1
  274. package/src/cli/Cli.test.ts +25 -2
  275. package/src/cli/Cli.ts +17 -2
  276. package/src/cli/commands/admin.test.ts +83 -0
  277. package/src/cli/commands/admin.ts +89 -1
  278. package/src/db/Db.ts +137 -0
  279. package/src/db/Schema.ts +104 -2
  280. package/src/db/tables/billingSettings.test.ts +87 -0
  281. package/src/db/tables/billingSettings.ts +108 -0
  282. package/src/db/tables/invitations.ts +2 -2
  283. package/src/db/tables/organizations.test.ts +1 -1
  284. package/src/db/tables/organizations.ts +15 -5
  285. package/src/db/tables/projects.ts +2 -2
  286. package/src/db/tables/requestUsage.ts +231 -0
  287. package/src/db/tables/sponsoredTransactions.test.ts +108 -1
  288. package/src/db/tables/sponsoredTransactions.ts +230 -2
  289. package/src/db/tables/stripeCustomers.test.ts +109 -0
  290. package/src/db/tables/stripeCustomers.ts +162 -0
  291. package/src/db/tables/users.ts +2 -2
  292. package/src/handlers/internal/sponsorship.ts +34 -7
  293. package/src/handlers/internal/utils.ts +19 -7
  294. package/src/index.ts +1 -0
  295. package/src/internal/Auth.test.ts +175 -25
  296. package/src/internal/Auth.ts +175 -86
  297. package/src/internal/Cache.ts +5 -1
  298. package/src/internal/EdgeCache.test.ts +27 -4
  299. package/src/internal/EdgeCache.ts +47 -9
  300. package/src/internal/Fees.test.ts +32 -0
  301. package/src/internal/Fees.ts +51 -0
  302. package/src/internal/FundingRoutes.ts +583 -0
  303. package/src/internal/Id.test.ts +17 -0
  304. package/src/internal/Id.ts +24 -0
  305. package/src/internal/Log.test.ts +1 -0
  306. package/src/internal/Log.ts +5 -0
  307. package/src/internal/OpenApi.ts +12 -1
  308. package/src/internal/RateLimit.test.ts +84 -0
  309. package/src/internal/RateLimit.ts +70 -0
  310. package/src/internal/Response.ts +1 -1
  311. package/src/internal/Schema.ts +8 -0
  312. package/src/internal/Webhooks.test.ts +3 -3
  313. package/src/internal/Webhooks.ts +3 -3
package/src/App.ts CHANGED
@@ -6,6 +6,7 @@ import { HTTPException } from 'hono/http-exception'
6
6
  import { requestId, type RequestIdVariables } from 'hono/request-id'
7
7
  import { generateSpecs } from 'hono-openapi'
8
8
 
9
+ import type * as Analytics from './analytics/Analytics.js'
9
10
  import * as ApiKey from './ApiKey.js'
10
11
  import * as Assets from './Assets.js'
11
12
  import * as Auth from './internal/Auth.js'
@@ -40,6 +41,8 @@ export type PlatformApp = ReturnType<typeof platform>
40
41
  export type Environment = {
41
42
  Variables: Auth.Variables &
42
43
  RequestIdVariables & {
44
+ /** ClickHouse-backed analytics store, or a per-request factory; undefined when usage analytics is unconfigured. Resolve at the leaf with `Analytics.get`. */
45
+ analytics: Analytics.Source | undefined
43
46
  /** Base path for this API app. */
44
47
  basePath: string
45
48
  /** Default Tempo chain id for this API app. */
@@ -93,6 +96,9 @@ export type Listener = (incoming: unknown, outgoing: unknown) => Promise<void>
93
96
  /** HTTP endpoints owned by Tempo API handlers, used for typed auth overrides. */
94
97
  export type Endpoint = Auth.Endpoint<App>
95
98
 
99
+ /** Stripe billing capability for platform billing routes; owned by {@link platform}. */
100
+ export type Billing = Platform.Billing
101
+
96
102
  /** Transactional email sender for platform emails; owned by {@link platform}. */
97
103
  export type Email = Platform.Email
98
104
 
@@ -194,12 +200,22 @@ export function create<const path extends string = '/'>(options: create.Options<
194
200
  // side; `cached` falls back to `source` so consumers never branch.
195
201
  const db = 'source' in options.db ? options.db : { source: options.db }
196
202
 
203
+ // Normalize the `analytics` option: accept the bare source or `{ source }`,
204
+ // mirroring `db`. Undefined leaves usage analytics unconfigured (501).
205
+ const analytics =
206
+ options.analytics === undefined
207
+ ? undefined
208
+ : 'source' in options.analytics
209
+ ? options.analytics.source
210
+ : options.analytics
211
+
197
212
  // Built-in auth: the session surface (mounted last) plus the enforcement
198
213
  // middleware (installed below), wired together in `Auth.install`.
199
214
  const auth = Auth.install({ auth: options.auth, db: db.source, kv: options.kv })
200
215
 
201
216
  base.use('*', Timing.middleware())
202
217
  base.use('*', async (c, next) => {
218
+ c.set('analytics', analytics)
203
219
  c.set('basePath', basePath)
204
220
  c.set('db', db.source)
205
221
  c.set('dbCached', db.cached ?? db.source)
@@ -403,6 +419,8 @@ export declare namespace create {
403
419
  * configuration belongs to {@link data}; email to {@link platform}.
404
420
  */
405
421
  type Options<path extends string = '/'> = {
422
+ /** Analytics store for usage reads (e.g. `Analytics.clickhouse(...)`), or a per-request factory. Accepts `{ source }` to mirror `db`. Omit to leave usage analytics unconfigured — usage routes answer 501. Ingest happens outside this app (queue consumer via `Analytics.handleQueue`). */
423
+ analytics?: Analytics.Source | { source: Analytics.Source } | undefined
406
424
  /** Authentication: lane defaults, quotas, and credentials for the built-in enforcement middleware, plus the `session` sign-in surface. Omit for default-closed policies with in-memory rate limiting; pass false to leave policies unenforced (tests only). */
407
425
  auth?: Auth.install.Options['auth'] | false | undefined
408
426
  /** Caching configuration: the cache store plus the edge response cache toggle. */
@@ -3,14 +3,17 @@ import type * as z from 'zod/mini'
3
3
 
4
4
  import * as Activities from './apps/data/routes/activities.js'
5
5
  import * as Balances from './apps/data/routes/balances.js'
6
+ import * as Billing from './apps/platform/routes/billing.js'
6
7
  import * as Blocks from './apps/data/routes/blocks.js'
7
8
  import * as Exchanges from './apps/data/routes/exchanges.js'
8
9
  import * as FeeAmm from './apps/data/routes/fee-amm.js'
10
+ import * as Funding from './apps/data/routes/funding.js'
9
11
  import * as Indexer from './apps/data/routes/indexer.js'
10
12
  import * as Me from './apps/platform/routes/me.js'
11
13
  import * as Orgs from './apps/platform/routes/orgs.js'
12
14
  import * as Receipts from './apps/data/routes/receipts.js'
13
15
  import * as Rpc from './apps/data/routes/rpc.js'
16
+ import * as Scopes from './apps/platform/routes/scopes.js'
14
17
  import * as Tokens from './apps/data/routes/tokens.js'
15
18
  import * as Transactions from './apps/data/routes/transactions.js'
16
19
  import * as Transfers from './apps/data/routes/transfers.js'
@@ -127,6 +130,30 @@ describe('narrows responses by status', () => {
127
130
  }
128
131
  })
129
132
 
133
+ test('GET /v1/scopes', async () => {
134
+ const client = Client.create()
135
+ const res = await client.v1.scopes.$get()
136
+
137
+ expectTypeOf(res.status).toEqualTypeOf<200 | PublicAccessStatus>()
138
+
139
+ switch (res.status) {
140
+ case 200: {
141
+ const json = await res.json()
142
+ expectTypeOf(json.data[0]!.scope).toEqualTypeOf<
143
+ z.output<typeof Scopes.schema.getScopes.Response>['data'][number]['scope']
144
+ >()
145
+ void json
146
+ return
147
+ }
148
+ case 400: {
149
+ const json = await res.json()
150
+ expectTypeOf(json.error.code).toEqualTypeOf<'api_key_malformed'>()
151
+ void json
152
+ return
153
+ }
154
+ }
155
+ })
156
+
130
157
  test('POST /v1/orgs', async () => {
131
158
  const client = Client.create()
132
159
  const res = await client.v1.orgs.$post({ json: { name: 'Acme' } })
@@ -173,6 +200,164 @@ describe('narrows responses by status', () => {
173
200
  }
174
201
  })
175
202
 
203
+ test('GET /v1/orgs/:orgId/billing', async () => {
204
+ const client = Client.create()
205
+ const res = await client.v1.orgs[':orgId{org_[A-Za-z0-9_-]+}'].billing.$get({
206
+ param: { orgId: 'org_test' },
207
+ })
208
+
209
+ expectTypeOf(res.status).toEqualTypeOf<200 | 400 | 401 | 403 | 404 | 501 | 502>()
210
+
211
+ switch (res.status) {
212
+ case 200: {
213
+ const json = await res.json()
214
+ expectTypeOf(json.status).toEqualTypeOf<z.output<typeof Billing.schema.Billing>['status']>()
215
+ void json
216
+ return
217
+ }
218
+ case 501: {
219
+ const json = await res.json()
220
+ expectTypeOf(json.error.code).toEqualTypeOf<'billing_unconfigured'>()
221
+ void json
222
+ return
223
+ }
224
+ }
225
+ })
226
+
227
+ test('GET /v1/orgs/:orgId/usage/sponsorships', async () => {
228
+ const client = Client.create()
229
+ const res = await client.v1.orgs[':orgId{org_[A-Za-z0-9_-]+}'].usage.sponsorships.$get({
230
+ param: { orgId: 'org_test' },
231
+ })
232
+
233
+ expectTypeOf(res.status).toEqualTypeOf<200 | 400 | 401 | 403 | 404 | 502>()
234
+
235
+ switch (res.status) {
236
+ case 200: {
237
+ const json = await res.json()
238
+ expectTypeOf(json.data[0]!.count).toEqualTypeOf<number>()
239
+ expectTypeOf(json.data[0]!.feeTotal).toEqualTypeOf<{ amount: string; currency: 'usd' }>()
240
+ expectTypeOf(json.data[0]!.timestamp).toEqualTypeOf<string>()
241
+ void json
242
+ return
243
+ }
244
+ case 400: {
245
+ const json = await res.json()
246
+ expectTypeOf(json.error.code).toEqualTypeOf<Access400<'param_invalid' | 'query_invalid'>>()
247
+ void json
248
+ return
249
+ }
250
+ }
251
+ })
252
+
253
+ test('PATCH /v1/orgs/:orgId/billing', async () => {
254
+ const client = Client.create()
255
+ const res = await client.v1.orgs[':orgId{org_[A-Za-z0-9_-]+}'].billing.$patch({
256
+ json: { spendLimit: { amount: '250' }, txFeeLimit: null },
257
+ param: { orgId: 'org_test' },
258
+ })
259
+
260
+ expectTypeOf(res.status).toEqualTypeOf<200 | 400 | 401 | 403 | 404 | 501 | 502>()
261
+
262
+ switch (res.status) {
263
+ case 200: {
264
+ const json = await res.json()
265
+ expectTypeOf(json.spendLimit).toEqualTypeOf<
266
+ z.output<typeof Billing.schema.SpendLimit> | undefined
267
+ >()
268
+ expectTypeOf(json.spend).toEqualTypeOf<z.output<typeof Billing.schema.Billing>['spend']>()
269
+ void json
270
+ return
271
+ }
272
+ case 501: {
273
+ const json = await res.json()
274
+ expectTypeOf(json.error.code).toEqualTypeOf<'billing_unconfigured'>()
275
+ void json
276
+ return
277
+ }
278
+ }
279
+ })
280
+
281
+ test('POST /v1/orgs/:orgId/billing/stripe/manage', async () => {
282
+ const client = Client.create()
283
+ const res = await client.v1.orgs[':orgId{org_[A-Za-z0-9_-]+}'].billing.stripe.manage.$post({
284
+ param: { orgId: 'org_test' },
285
+ })
286
+
287
+ expectTypeOf(res.status).toEqualTypeOf<200 | 400 | 401 | 403 | 404 | 501 | 502>()
288
+
289
+ switch (res.status) {
290
+ case 200: {
291
+ const json = await res.json()
292
+ expectTypeOf(json.url).toEqualTypeOf<string>()
293
+ void json
294
+ return
295
+ }
296
+ case 404: {
297
+ const json = await res.json()
298
+ expectTypeOf(json.error.code).toEqualTypeOf<
299
+ 'billing_not_found' | 'organization_not_found'
300
+ >()
301
+ void json
302
+ return
303
+ }
304
+ }
305
+ })
306
+
307
+ test('GET /v1/orgs/:orgId/billing/payment-methods', async () => {
308
+ const client = Client.create()
309
+ const res = await client.v1.orgs[':orgId{org_[A-Za-z0-9_-]+}'].billing['payment-methods'].$get({
310
+ param: { orgId: 'org_test' },
311
+ })
312
+
313
+ expectTypeOf(res.status).toEqualTypeOf<200 | 400 | 401 | 403 | 404 | 501 | 502>()
314
+
315
+ switch (res.status) {
316
+ case 200: {
317
+ const json = await res.json()
318
+ expectTypeOf(json.data[0]!.provider).toEqualTypeOf<'stripe'>()
319
+ expectTypeOf(json.data[0]!.default).toEqualTypeOf<boolean>()
320
+ expectTypeOf(json.data[0]!.card).toEqualTypeOf<
321
+ { brand: string; expMonth: number; expYear: number; last4: string } | undefined
322
+ >()
323
+ void json
324
+ return
325
+ }
326
+ case 501: {
327
+ const json = await res.json()
328
+ expectTypeOf(json.error.code).toEqualTypeOf<'billing_unconfigured'>()
329
+ void json
330
+ return
331
+ }
332
+ }
333
+ })
334
+
335
+ test('DELETE /v1/orgs/:orgId/billing/payment-methods/:methodId', async () => {
336
+ const client = Client.create()
337
+ const res = await client.v1.orgs[':orgId{org_[A-Za-z0-9_-]+}'].billing['payment-methods'][
338
+ ':methodId{pm_[A-Za-z0-9_-]+}'
339
+ ].$delete({ param: { methodId: 'pm_test', orgId: 'org_test' } })
340
+
341
+ expectTypeOf(res.status).toEqualTypeOf<200 | 400 | 401 | 403 | 404 | 501 | 502>()
342
+
343
+ switch (res.status) {
344
+ case 200: {
345
+ const json = await res.json()
346
+ expectTypeOf(json.status).toEqualTypeOf<'active' | 'canceled' | 'none' | 'past_due'>()
347
+ void json
348
+ return
349
+ }
350
+ case 404: {
351
+ const json = await res.json()
352
+ expectTypeOf(json.error.code).toEqualTypeOf<
353
+ 'billing_not_found' | 'organization_not_found' | 'payment_method_not_found'
354
+ >()
355
+ void json
356
+ return
357
+ }
358
+ }
359
+ })
360
+
176
361
  test('DELETE /v1/orgs/:orgId', async () => {
177
362
  const client = Client.create()
178
363
  const res = await client.v1.orgs[':orgId{org_[A-Za-z0-9_-]+}'].$delete({
@@ -271,6 +456,29 @@ describe('narrows responses by status', () => {
271
456
  }
272
457
  })
273
458
 
459
+ test('GET /v1/orgs/:orgId/api-keys', async () => {
460
+ const client = Client.create()
461
+ const res = await client.v1.orgs[':orgId{org_[A-Za-z0-9_-]+}']['api-keys'].$get({
462
+ // The environment filter is optional (defaults to production server-side).
463
+ query: { environment: 'sandbox' },
464
+ param: { orgId: 'org_test' },
465
+ })
466
+
467
+ expectTypeOf(res.status).toEqualTypeOf<200 | 400 | 401 | 403 | 404 | 502>()
468
+
469
+ switch (res.status) {
470
+ case 200: {
471
+ const json = await res.json()
472
+ expectTypeOf(json.data[0]!.id).toEqualTypeOf<string>()
473
+ expectTypeOf(json.data[0]!.environment).toEqualTypeOf<'production' | 'sandbox'>()
474
+ // Absent on org-wide keys minted without project attribution.
475
+ expectTypeOf(json.data[0]!.projectId).toEqualTypeOf<string | undefined>()
476
+ void json
477
+ return
478
+ }
479
+ }
480
+ })
481
+
274
482
  test('DELETE /v1/orgs/:orgId/projects/:projectId/api-keys/:keyId', async () => {
275
483
  const client = Client.create()
276
484
  const res = await client.v1.orgs[':orgId{org_[A-Za-z0-9_-]+}'].projects[
@@ -296,6 +504,41 @@ describe('narrows responses by status', () => {
296
504
  }
297
505
  })
298
506
 
507
+ test('GET /v1/orgs/:orgId/usage/requests', async () => {
508
+ const client = Client.create()
509
+ const res = await client.v1.orgs[':orgId{org_[A-Za-z0-9_-]+}'].usage.requests.$get({
510
+ param: { orgId: 'org_test' },
511
+ query: { projectId: 'prj_test' },
512
+ })
513
+
514
+ expectTypeOf(res.status).toEqualTypeOf<200 | 400 | 401 | 403 | 404 | 501 | 502>()
515
+
516
+ switch (res.status) {
517
+ case 200: {
518
+ const json = await res.json()
519
+ expectTypeOf(json.from).toEqualTypeOf<string>()
520
+ expectTypeOf(json.interval).toEqualTypeOf<'day' | 'hour'>()
521
+ expectTypeOf(json.to).toEqualTypeOf<string>()
522
+ expectTypeOf(json.totals.requests).toEqualTypeOf<number>()
523
+ expectTypeOf(json.byKey[0]?.apiKeyId).toEqualTypeOf<string | undefined>()
524
+ void json
525
+ return
526
+ }
527
+ case 404: {
528
+ const json = await res.json()
529
+ expectTypeOf(json.error.code).toEqualTypeOf<'organization_not_found'>()
530
+ void json
531
+ return
532
+ }
533
+ case 501: {
534
+ const json = await res.json()
535
+ expectTypeOf(json.error.code).toEqualTypeOf<'analytics_unconfigured'>()
536
+ void json
537
+ return
538
+ }
539
+ }
540
+ })
541
+
299
542
  test('GET /v1/orgs/:orgId/members', async () => {
300
543
  const client = Client.create()
301
544
  const res = await client.v1.orgs[':orgId{org_[A-Za-z0-9_-]+}'].members.$get({
@@ -356,6 +599,30 @@ describe('narrows responses by status', () => {
356
599
  }
357
600
  })
358
601
 
602
+ test('POST /v1/invitations/:invitationId/decline', async () => {
603
+ const client = Client.create()
604
+ const res = await client.v1.invitations[':invitationId{inv_[A-Za-z0-9_-]+}'].decline.$post({
605
+ param: { invitationId: 'inv_test' },
606
+ })
607
+
608
+ expectTypeOf(res.status).toEqualTypeOf<200 | 400 | 401 | 403 | 404 | 502>()
609
+
610
+ switch (res.status) {
611
+ case 200: {
612
+ const json = await res.json()
613
+ expectTypeOf(json.id).toEqualTypeOf<string>()
614
+ void json
615
+ return
616
+ }
617
+ case 404: {
618
+ const json = await res.json()
619
+ expectTypeOf(json.error.code).toEqualTypeOf<'invitation_not_found'>()
620
+ void json
621
+ return
622
+ }
623
+ }
624
+ })
625
+
359
626
  test('GET /v1/orgs', async () => {
360
627
  const client = Client.create()
361
628
  const res = await client.v1.orgs.$get()
@@ -922,6 +1189,90 @@ describe('narrows responses by status', () => {
922
1189
  }
923
1190
  })
924
1191
 
1192
+ test('GET /v1/funding/routes', async () => {
1193
+ const client = Client.create()
1194
+ const res = await client.v1.funding.routes.$get({
1195
+ query: {
1196
+ sourceAmount: '1000000',
1197
+ sourceChain: 'base',
1198
+ sourceToken: 'USDC',
1199
+ },
1200
+ })
1201
+
1202
+ expectTypeOf(res.status).toEqualTypeOf<200 | 400 | PaidAccessStatus>()
1203
+
1204
+ switch (res.status) {
1205
+ case 200: {
1206
+ const json = await res.json()
1207
+ expectTypeOf(json.data[0]!.sourceAmount).toEqualTypeOf<
1208
+ z.output<typeof Funding.schema.listFundingRoutes.Response>['data'][number]['sourceAmount']
1209
+ >()
1210
+ expectTypeOf(json.data[0]!.provider.id).toEqualTypeOf<string>()
1211
+ void json
1212
+ return
1213
+ }
1214
+ case 400: {
1215
+ const json = await res.json()
1216
+ expectTypeOf(json.error.code).toEqualTypeOf<Access400<'query_invalid'>>()
1217
+ expectTypeOf(json.error.message).toEqualTypeOf<string>()
1218
+ void json
1219
+ return
1220
+ }
1221
+ }
1222
+ })
1223
+
1224
+ test('GET /v1/funding/chains', async () => {
1225
+ const client = Client.create()
1226
+ const res = await client.v1.funding.chains.$get()
1227
+
1228
+ expectTypeOf(res.status).toEqualTypeOf<200 | PaidAccessStatus>()
1229
+
1230
+ switch (res.status) {
1231
+ case 200: {
1232
+ const json = await res.json()
1233
+ expectTypeOf(json.data[0]!.tokens[0]!.symbol).toEqualTypeOf<
1234
+ z.output<
1235
+ typeof Funding.schema.listFundingChains.Response
1236
+ >['data'][number]['tokens'][number]['symbol']
1237
+ >()
1238
+ void json
1239
+ return
1240
+ }
1241
+ case 400: {
1242
+ const json = await res.json()
1243
+ expectTypeOf(json.error.code).toEqualTypeOf<'api_key_malformed'>()
1244
+ expectTypeOf(json.error.message).toEqualTypeOf<string>()
1245
+ void json
1246
+ return
1247
+ }
1248
+ }
1249
+ })
1250
+
1251
+ test('GET /v1/providers', async () => {
1252
+ const client = Client.create()
1253
+ const res = await client.v1.providers.$get({ query: { capability: 'fundingRoutes' } })
1254
+
1255
+ expectTypeOf(res.status).toEqualTypeOf<200 | 400 | PaidAccessStatus>()
1256
+
1257
+ switch (res.status) {
1258
+ case 200: {
1259
+ const json = await res.json()
1260
+ expectTypeOf(json.data[0]!.capabilities).toEqualTypeOf<
1261
+ z.output<typeof Funding.schema.listProviders.Response>['data'][number]['capabilities']
1262
+ >()
1263
+ void json
1264
+ return
1265
+ }
1266
+ case 400: {
1267
+ const json = await res.json()
1268
+ expectTypeOf(json.error.code).toEqualTypeOf<Access400<'query_invalid'>>()
1269
+ expectTypeOf(json.error.message).toEqualTypeOf<string>()
1270
+ void json
1271
+ return
1272
+ }
1273
+ }
1274
+ })
1275
+
925
1276
  test('GET /v1/exchange/swaps', async () => {
926
1277
  const client = Client.create()
927
1278
  const res = await client.v1.exchange.swaps.$get()
package/src/Scope.ts CHANGED
@@ -18,6 +18,8 @@ type Entry = {
18
18
  description: string
19
19
  /** Scope identifier used in a key's `scopes` and in route policies. */
20
20
  scope: string
21
+ /** Whether a session may self-mint this scope (else super-admin-only). */
22
+ selfServe: boolean
21
23
  }
22
24
 
23
25
  /**
@@ -30,26 +32,32 @@ export const catalog = [
30
32
  {
31
33
  description: 'Read public chain data: tokens, transactions, transfers, blocks, and receipts.',
32
34
  scope: 'data:read',
35
+ selfServe: true,
33
36
  },
34
37
  {
35
38
  description: 'Run ad-hoc queries against the raw indexer passthrough.',
36
39
  scope: 'indexer:query',
40
+ selfServe: true,
37
41
  },
38
42
  {
39
43
  description: 'Fill and submit transactions through the relay.',
40
44
  scope: 'rpc-relay:read',
45
+ selfServe: true,
41
46
  },
42
47
  {
43
48
  description: 'Sponsor transaction fees through the relay as the managed fee payer.',
44
49
  scope: 'rpc-relay:sponsor',
50
+ selfServe: true,
45
51
  },
46
52
  {
47
53
  description: 'Read webhook subscriptions.',
48
54
  scope: 'webhooks:read',
55
+ selfServe: false,
49
56
  },
50
57
  {
51
58
  description: 'Create, update, and delete webhook subscriptions.',
52
59
  scope: 'webhooks:write',
60
+ selfServe: false,
53
61
  },
54
62
  ] as const satisfies readonly Entry[]
55
63
 
@@ -57,7 +65,12 @@ export const catalog = [
57
65
  export type Id = (typeof catalog)[number]['scope']
58
66
 
59
67
  /**
60
- * Scopes a session may self-mint through the developer platform. Empty: every
61
- * scope requires super admin issuance.
68
+ * Scopes a session may self-mint through the developer platform, derived from
69
+ * the catalog's `selfServe` flag. Paid usage (mainnet sponsorship) is gated by
70
+ * an active billing source at request time, so self-serve users cannot consume
71
+ * paid APIs for free. Webhooks stay super-admin-only, as does {@link wildcard}
72
+ * (intentionally absent from the catalog).
62
73
  */
63
- export const selfServe: readonly Id[] = []
74
+ export const selfServe: readonly Id[] = catalog
75
+ .filter((entry) => entry.selfServe)
76
+ .map((entry) => entry.scope)
@@ -17,26 +17,32 @@ describe('GET /scopes', () => {
17
17
  {
18
18
  "description": "Read public chain data: tokens, transactions, transfers, blocks, and receipts.",
19
19
  "scope": "data:read",
20
+ "selfServe": true,
20
21
  },
21
22
  {
22
23
  "description": "Run ad-hoc queries against the raw indexer passthrough.",
23
24
  "scope": "indexer:query",
25
+ "selfServe": true,
24
26
  },
25
27
  {
26
28
  "description": "Fill and submit transactions through the relay.",
27
29
  "scope": "rpc-relay:read",
30
+ "selfServe": true,
28
31
  },
29
32
  {
30
33
  "description": "Sponsor transaction fees through the relay as the managed fee payer.",
31
34
  "scope": "rpc-relay:sponsor",
35
+ "selfServe": true,
32
36
  },
33
37
  {
34
38
  "description": "Read webhook subscriptions.",
35
39
  "scope": "webhooks:read",
40
+ "selfServe": false,
36
41
  },
37
42
  {
38
43
  "description": "Create, update, and delete webhook subscriptions.",
39
44
  "scope": "webhooks:write",
45
+ "selfServe": false,
40
46
  },
41
47
  ],
42
48
  }
@@ -18,6 +18,9 @@ export namespace schema {
18
18
  scope: z
19
19
  .string()
20
20
  .check(z.describe('Scope identifier used in API-key grants and route policies.')),
21
+ selfServe: z
22
+ .boolean()
23
+ .check(z.describe('Whether a session may self-mint this scope (else super-admin-only).')),
21
24
  })
22
25
  .check(z.describe('A single issuable API-key scope.'))
23
26
 
@@ -1,4 +1,5 @@
1
1
  import type { ReactNode } from 'react'
2
+ import { cx } from 'regen-ui'
2
3
 
3
4
  import { MobileProfile } from './MobileProfile'
4
5
 
@@ -37,9 +38,7 @@ export function Page(props: {
37
38
  /** Bordered surface container used for lists and forms. */
38
39
  export function Card(props: { children: ReactNode; className?: string }) {
39
40
  return (
40
- <div className={`border border-border bg-surface ${props.className ?? ''}`}>
41
- {props.children}
42
- </div>
41
+ <div className={cx('border border-border bg-surface', props.className)}>{props.children}</div>
43
42
  )
44
43
  }
45
44
 
@@ -207,12 +207,12 @@ function MobileNav() {
207
207
 
208
208
  function SignInRequired({ loginUrl }: { loginUrl?: string }) {
209
209
  return (
210
- <div className="flex min-h-screen flex-col items-center justify-center gap-3 bg-background text-foreground">
210
+ <div className="flex min-h-dvh flex-col items-center justify-center gap-3 bg-background text-foreground">
211
211
  <p className="font-medium">Sign in required</p>
212
212
  {loginUrl ? (
213
213
  <Button render={<a href={loginUrl} />}>Sign in</Button>
214
214
  ) : (
215
- <p className="text-sm text-foreground-secondary">Your session has expired.</p>
215
+ <p className="text-copy-14 text-foreground-secondary">Your session has expired.</p>
216
216
  )}
217
217
  </div>
218
218
  )
@@ -1,6 +1,6 @@
1
1
  import { createFileRoute } from '@tanstack/react-router'
2
2
  import { type ReactNode, useState } from 'react'
3
- import { Button, Fields, Input, Rows, Select, useCopy } from 'regen-ui'
3
+ import { Button, cx, Fields, Input, Rows, Select, useCopy } from 'regen-ui'
4
4
  import { Check, ChevronRight, Copy as CopyIcon, Plus, Trash2, X } from 'lucide-react'
5
5
 
6
6
  import { Card, Notice, Page } from '../components/Page'
@@ -313,7 +313,7 @@ function KeyRow(props: { record: ApiKeys.ApiKey }) {
313
313
 
314
314
  function Detail(props: { children: ReactNode; className?: string; label: ReactNode }) {
315
315
  return (
316
- <div className={`flex min-w-0 flex-col gap-1 ${props.className ?? ''}`}>
316
+ <div className={cx('flex min-w-0 flex-col gap-1', props.className)}>
317
317
  <dt className="text-label-12 text-foreground-secondary">{props.label}</dt>
318
318
  <dd className="min-w-0 text-label-13 text-foreground">{props.children}</dd>
319
319
  </div>