tapimo 0.4.0 → 0.4.3

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 (255) hide show
  1. package/README.md +1 -1
  2. package/dist/ApiKey.d.ts +1 -9
  3. package/dist/ApiKey.d.ts.map +1 -1
  4. package/dist/ApiKey.js +2 -3
  5. package/dist/ApiKey.js.map +1 -1
  6. package/dist/ApiKeys.d.ts +53 -27
  7. package/dist/ApiKeys.d.ts.map +1 -1
  8. package/dist/ApiKeys.js +32 -17
  9. package/dist/ApiKeys.js.map +1 -1
  10. package/dist/App.d.ts +2451 -1533
  11. package/dist/App.d.ts.map +1 -1
  12. package/dist/App.js +23 -9
  13. package/dist/App.js.map +1 -1
  14. package/dist/Assets.d.ts +10 -0
  15. package/dist/Assets.d.ts.map +1 -1
  16. package/dist/Assets.js +4 -0
  17. package/dist/Assets.js.map +1 -1
  18. package/dist/Auth.d.ts +2 -1
  19. package/dist/Auth.d.ts.map +1 -1
  20. package/dist/Auth.js +2 -1
  21. package/dist/Auth.js.map +1 -1
  22. package/dist/Scope.d.ts +40 -15
  23. package/dist/Scope.d.ts.map +1 -1
  24. package/dist/Scope.js +44 -13
  25. package/dist/Scope.js.map +1 -1
  26. package/dist/admin/App.d.ts +560 -9
  27. package/dist/admin/App.d.ts.map +1 -1
  28. package/dist/admin/App.js +38 -6
  29. package/dist/admin/App.js.map +1 -1
  30. package/dist/admin/apps/api-keys.d.ts +10 -24
  31. package/dist/admin/apps/api-keys.d.ts.map +1 -1
  32. package/dist/admin/apps/api-keys.js +10 -3
  33. package/dist/admin/apps/api-keys.js.map +1 -1
  34. package/dist/admin/apps/organizations.d.ts +833 -0
  35. package/dist/admin/apps/organizations.d.ts.map +1 -0
  36. package/dist/admin/apps/organizations.js +733 -0
  37. package/dist/admin/apps/organizations.js.map +1 -0
  38. package/dist/admin/apps/scopes.d.ts +8 -0
  39. package/dist/admin/apps/scopes.d.ts.map +1 -1
  40. package/dist/admin/ui.generated.d.ts +1 -1
  41. package/dist/admin/ui.generated.d.ts.map +1 -1
  42. package/dist/admin/ui.generated.js +53 -13
  43. package/dist/admin/ui.generated.js.map +1 -1
  44. package/dist/analytics/Analytics.d.ts +10 -0
  45. package/dist/analytics/Analytics.d.ts.map +1 -1
  46. package/dist/analytics/Analytics.js +8 -0
  47. package/dist/analytics/Analytics.js.map +1 -1
  48. package/dist/analytics/tables/requestEvents.d.ts +9 -0
  49. package/dist/analytics/tables/requestEvents.d.ts.map +1 -1
  50. package/dist/analytics/tables/requestEvents.js +14 -0
  51. package/dist/analytics/tables/requestEvents.js.map +1 -1
  52. package/dist/apps/data/App.d.ts +1354 -984
  53. package/dist/apps/data/App.d.ts.map +1 -1
  54. package/dist/apps/data/App.js +20 -3
  55. package/dist/apps/data/App.js.map +1 -1
  56. package/dist/apps/data/FundingRouteProviders.d.ts +94 -5
  57. package/dist/apps/data/FundingRouteProviders.d.ts.map +1 -1
  58. package/dist/apps/data/FundingRouteProviders.js +196 -5
  59. package/dist/apps/data/FundingRouteProviders.js.map +1 -1
  60. package/dist/apps/data/routes/activities.d.ts +38 -38
  61. package/dist/apps/data/routes/addresses.d.ts +24 -24
  62. package/dist/apps/data/routes/balances.d.ts +45 -45
  63. package/dist/apps/data/routes/blocks.d.ts +39 -39
  64. package/dist/apps/data/routes/coingecko.d.ts +176 -176
  65. package/dist/apps/data/routes/exchanges.d.ts +453 -453
  66. package/dist/apps/data/routes/fee-amm.d.ts +101 -101
  67. package/dist/apps/data/routes/funding.d.ts +730 -138
  68. package/dist/apps/data/routes/funding.d.ts.map +1 -1
  69. package/dist/apps/data/routes/funding.js +234 -0
  70. package/dist/apps/data/routes/funding.js.map +1 -1
  71. package/dist/apps/data/routes/indexer.d.ts +5 -5
  72. package/dist/apps/data/routes/rpc.d.ts +31 -31
  73. package/dist/apps/data/routes/verified-tokens.d.ts +81 -81
  74. package/dist/apps/management/App.d.ts +1156 -615
  75. package/dist/apps/management/App.d.ts.map +1 -1
  76. package/dist/apps/management/App.js +2 -2
  77. package/dist/apps/management/Billing.d.ts +3 -0
  78. package/dist/apps/management/Billing.d.ts.map +1 -1
  79. package/dist/apps/management/Billing.js +7 -2
  80. package/dist/apps/management/Billing.js.map +1 -1
  81. package/dist/apps/management/routes/api-keys.d.ts +106 -90
  82. package/dist/apps/management/routes/api-keys.d.ts.map +1 -1
  83. package/dist/apps/management/routes/api-keys.js +41 -30
  84. package/dist/apps/management/routes/api-keys.js.map +1 -1
  85. package/dist/apps/management/routes/billing.d.ts +278 -146
  86. package/dist/apps/management/routes/billing.d.ts.map +1 -1
  87. package/dist/apps/management/routes/billing.js +14 -14
  88. package/dist/apps/management/routes/billing.js.map +1 -1
  89. package/dist/apps/management/routes/invitations.d.ts +93 -20
  90. package/dist/apps/management/routes/invitations.d.ts.map +1 -1
  91. package/dist/apps/management/routes/invitations.js +12 -15
  92. package/dist/apps/management/routes/invitations.js.map +1 -1
  93. package/dist/apps/management/routes/me.d.ts +13 -13
  94. package/dist/apps/management/routes/members.d.ts +89 -18
  95. package/dist/apps/management/routes/members.d.ts.map +1 -1
  96. package/dist/apps/management/routes/members.js +10 -10
  97. package/dist/apps/management/routes/members.js.map +1 -1
  98. package/dist/apps/management/routes/orgs.d.ts +130 -70
  99. package/dist/apps/management/routes/orgs.d.ts.map +1 -1
  100. package/dist/apps/management/routes/orgs.js +19 -19
  101. package/dist/apps/management/routes/orgs.js.map +1 -1
  102. package/dist/apps/management/routes/projects.d.ts +137 -48
  103. package/dist/apps/management/routes/projects.d.ts.map +1 -1
  104. package/dist/apps/management/routes/projects.js +12 -13
  105. package/dist/apps/management/routes/projects.js.map +1 -1
  106. package/dist/apps/management/routes/scopes.d.ts +14 -28
  107. package/dist/apps/management/routes/scopes.d.ts.map +1 -1
  108. package/dist/apps/management/routes/scopes.js +6 -7
  109. package/dist/apps/management/routes/scopes.js.map +1 -1
  110. package/dist/apps/management/routes/usage.d.ts +63 -19
  111. package/dist/apps/management/routes/usage.d.ts.map +1 -1
  112. package/dist/apps/management/routes/usage.js +9 -10
  113. package/dist/apps/management/routes/usage.js.map +1 -1
  114. package/dist/apps/mcp.d.ts.map +1 -1
  115. package/dist/apps/mcp.js +35 -3
  116. package/dist/apps/mcp.js.map +1 -1
  117. package/dist/apps/relay/App.d.ts +26 -4
  118. package/dist/apps/relay/App.d.ts.map +1 -1
  119. package/dist/apps/relay/App.js +17 -14
  120. package/dist/apps/relay/App.js.map +1 -1
  121. package/dist/db/Db.d.ts +5 -3
  122. package/dist/db/Db.d.ts.map +1 -1
  123. package/dist/db/Db.js +52 -0
  124. package/dist/db/Db.js.map +1 -1
  125. package/dist/db/Schema.d.ts +13 -0
  126. package/dist/db/Schema.d.ts.map +1 -1
  127. package/dist/db/Schema.js +16 -1
  128. package/dist/db/Schema.js.map +1 -1
  129. package/dist/db/tables/adminAuditLogs.d.ts +27 -0
  130. package/dist/db/tables/adminAuditLogs.d.ts.map +1 -0
  131. package/dist/db/tables/adminAuditLogs.js +19 -0
  132. package/dist/db/tables/adminAuditLogs.js.map +1 -0
  133. package/dist/db/tables/invitations.d.ts +1 -1
  134. package/dist/db/tables/invitations.d.ts.map +1 -1
  135. package/dist/db/tables/memberships.d.ts +32 -4
  136. package/dist/db/tables/memberships.d.ts.map +1 -1
  137. package/dist/db/tables/memberships.js +31 -0
  138. package/dist/db/tables/memberships.js.map +1 -1
  139. package/dist/db/tables/organizations.d.ts +43 -1
  140. package/dist/db/tables/organizations.d.ts.map +1 -1
  141. package/dist/db/tables/organizations.js +103 -0
  142. package/dist/db/tables/organizations.js.map +1 -1
  143. package/dist/db/tables/projects.d.ts +25 -0
  144. package/dist/db/tables/projects.d.ts.map +1 -1
  145. package/dist/db/tables/projects.js +22 -0
  146. package/dist/db/tables/projects.js.map +1 -1
  147. package/dist/db/tables/webhookDeliveries.d.ts +26 -0
  148. package/dist/db/tables/webhookDeliveries.d.ts.map +1 -1
  149. package/dist/db/tables/webhookDeliveries.js +37 -0
  150. package/dist/db/tables/webhookDeliveries.js.map +1 -1
  151. package/dist/handlers/relay.d.ts +13 -10
  152. package/dist/handlers/relay.d.ts.map +1 -1
  153. package/dist/handlers/relay.js +46 -16
  154. package/dist/handlers/relay.js.map +1 -1
  155. package/dist/internal/Auth.d.ts +13 -18
  156. package/dist/internal/Auth.d.ts.map +1 -1
  157. package/dist/internal/Auth.js +78 -58
  158. package/dist/internal/Auth.js.map +1 -1
  159. package/dist/internal/Cursor.d.ts +1 -1
  160. package/dist/internal/Cursor.d.ts.map +1 -1
  161. package/dist/internal/Cursor.js +4 -0
  162. package/dist/internal/Cursor.js.map +1 -1
  163. package/dist/internal/EdgeCache.d.ts.map +1 -1
  164. package/dist/internal/EdgeCache.js +2 -0
  165. package/dist/internal/EdgeCache.js.map +1 -1
  166. package/dist/internal/FundingRoutes.d.ts +170 -0
  167. package/dist/internal/FundingRoutes.d.ts.map +1 -1
  168. package/dist/internal/FundingRoutes.js +68 -6
  169. package/dist/internal/FundingRoutes.js.map +1 -1
  170. package/dist/internal/Log.d.ts +16 -1
  171. package/dist/internal/Log.d.ts.map +1 -1
  172. package/dist/internal/Log.js +5 -3
  173. package/dist/internal/Log.js.map +1 -1
  174. package/package.json +2 -2
  175. package/src/ApiKey.ts +2 -3
  176. package/src/ApiKeys.ts +95 -17
  177. package/src/App.test.ts +44 -2
  178. package/src/App.ts +29 -9
  179. package/src/Assets.ts +8 -0
  180. package/src/Auth.test-d.ts +36 -0
  181. package/src/Auth.ts +20 -1
  182. package/src/Client.test-d.ts +64 -17
  183. package/src/Scope.test.ts +34 -0
  184. package/src/Scope.ts +53 -17
  185. package/src/admin/App.test.ts +33 -3
  186. package/src/admin/App.ts +49 -14
  187. package/src/admin/apps/api-keys.ts +10 -3
  188. package/src/admin/apps/organizations.test.ts +455 -0
  189. package/src/admin/apps/organizations.ts +953 -0
  190. package/src/admin/apps/scopes.test.ts +10 -0
  191. package/src/admin/ui/plugin.ts +1 -1
  192. package/src/admin/ui/src/components/MobileProfile.tsx +2 -0
  193. package/src/admin/ui/src/components/ThemeToggle.tsx +20 -0
  194. package/src/admin/ui/src/lib/api-keys.ts +36 -19
  195. package/src/admin/ui/src/lib/config.ts +2 -3
  196. package/src/admin/ui/src/lib/organizations.test.ts +89 -0
  197. package/src/admin/ui/src/lib/organizations.ts +262 -0
  198. package/src/admin/ui/src/lib/query-client.ts +3 -1
  199. package/src/admin/ui/src/lib/theme.ts +57 -0
  200. package/src/admin/ui/src/routeTree.gen.ts +47 -4
  201. package/src/admin/ui/src/routes/__root.tsx +18 -3
  202. package/src/admin/ui/src/routes/api-keys.tsx +52 -7
  203. package/src/admin/ui/src/routes/index.tsx +1 -1
  204. package/src/admin/ui/src/routes/organizations.tsx +158 -0
  205. package/src/admin/ui/src/routes/organizations_.$orgId.tsx +710 -0
  206. package/src/admin/ui/src/start.ts +6 -0
  207. package/src/admin/ui.generated.ts +63 -13
  208. package/src/analytics/Analytics.ts +25 -0
  209. package/src/analytics/tables/requestEvents.ts +33 -0
  210. package/src/apps/composition.test.ts +82 -2
  211. package/src/apps/data/App.ts +24 -3
  212. package/src/apps/data/FundingRouteProviders.test.ts +204 -0
  213. package/src/apps/data/FundingRouteProviders.ts +294 -9
  214. package/src/apps/data/routes/funding.test.ts +90 -23
  215. package/src/apps/data/routes/funding.ts +315 -0
  216. package/src/apps/data/routes/rpc.test.ts +33 -0
  217. package/src/apps/management/App.ts +2 -2
  218. package/src/apps/management/Billing.ts +10 -2
  219. package/src/apps/management/routes/api-keys.test.ts +137 -1
  220. package/src/apps/management/routes/api-keys.ts +63 -38
  221. package/src/apps/management/routes/billing.ts +14 -14
  222. package/src/apps/management/routes/invitations.test.ts +102 -1
  223. package/src/apps/management/routes/invitations.ts +13 -15
  224. package/src/apps/management/routes/members.ts +10 -10
  225. package/src/apps/management/routes/orgs.test.ts +78 -16
  226. package/src/apps/management/routes/orgs.ts +21 -20
  227. package/src/apps/management/routes/projects.ts +12 -13
  228. package/src/apps/management/routes/scopes.test.ts +16 -0
  229. package/src/apps/management/routes/scopes.ts +6 -7
  230. package/src/apps/management/routes/usage.ts +9 -14
  231. package/src/apps/mcp.test.ts +25 -1
  232. package/src/apps/mcp.ts +43 -3
  233. package/src/apps/relay/App.test.ts +23 -18
  234. package/src/apps/relay/App.ts +35 -14
  235. package/src/db/Db.ts +59 -3
  236. package/src/db/Schema.ts +20 -1
  237. package/src/db/tables/adminAuditLogs.ts +47 -0
  238. package/src/db/tables/invitations.ts +1 -1
  239. package/src/db/tables/memberships.ts +59 -3
  240. package/src/db/tables/organizations.ts +148 -1
  241. package/src/db/tables/projects.ts +47 -0
  242. package/src/db/tables/webhookDeliveries.ts +83 -0
  243. package/src/handlers/relay.test-d.ts +8 -0
  244. package/src/handlers/relay.test.ts +2 -2
  245. package/src/handlers/relay.ts +62 -22
  246. package/src/internal/Auth.test.ts +174 -7
  247. package/src/internal/Auth.ts +90 -62
  248. package/src/internal/Cursor.test.ts +10 -0
  249. package/src/internal/Cursor.ts +5 -1
  250. package/src/internal/EdgeCache.test.ts +11 -0
  251. package/src/internal/EdgeCache.ts +2 -0
  252. package/src/internal/FundingRoutes.test.ts +26 -0
  253. package/src/internal/FundingRoutes.ts +187 -18
  254. package/src/internal/Log.test.ts +41 -3
  255. package/src/internal/Log.ts +23 -4
@@ -182,7 +182,7 @@ describe('narrows responses by status', () => {
182
182
  param: { orgId: 'org_test' },
183
183
  })
184
184
 
185
- expectTypeOf(res.status).toEqualTypeOf<200 | 400 | 401 | 403 | 404>()
185
+ expectTypeOf(res.status).toEqualTypeOf<200 | 404 | PublicAccessStatus>()
186
186
 
187
187
  switch (res.status) {
188
188
  case 200: {
@@ -206,7 +206,7 @@ describe('narrows responses by status', () => {
206
206
  param: { orgId: 'org_test' },
207
207
  })
208
208
 
209
- expectTypeOf(res.status).toEqualTypeOf<200 | 400 | 401 | 403 | 404 | 501 | 502>()
209
+ expectTypeOf(res.status).toEqualTypeOf<200 | 404 | 501 | 502 | PublicAccessStatus>()
210
210
 
211
211
  switch (res.status) {
212
212
  case 200: {
@@ -230,7 +230,7 @@ describe('narrows responses by status', () => {
230
230
  param: { orgId: 'org_test' },
231
231
  })
232
232
 
233
- expectTypeOf(res.status).toEqualTypeOf<200 | 400 | 401 | 403 | 404 | 502>()
233
+ expectTypeOf(res.status).toEqualTypeOf<200 | 404 | 502 | PublicAccessStatus>()
234
234
 
235
235
  switch (res.status) {
236
236
  case 200: {
@@ -257,7 +257,7 @@ describe('narrows responses by status', () => {
257
257
  param: { orgId: 'org_test' },
258
258
  })
259
259
 
260
- expectTypeOf(res.status).toEqualTypeOf<200 | 400 | 401 | 403 | 404 | 501 | 502>()
260
+ expectTypeOf(res.status).toEqualTypeOf<200 | 404 | 501 | 502 | PublicAccessStatus>()
261
261
 
262
262
  switch (res.status) {
263
263
  case 200: {
@@ -284,7 +284,7 @@ describe('narrows responses by status', () => {
284
284
  param: { orgId: 'org_test' },
285
285
  })
286
286
 
287
- expectTypeOf(res.status).toEqualTypeOf<200 | 400 | 401 | 403 | 404 | 501 | 502>()
287
+ expectTypeOf(res.status).toEqualTypeOf<200 | 404 | 501 | 502 | PublicAccessStatus>()
288
288
 
289
289
  switch (res.status) {
290
290
  case 200: {
@@ -310,7 +310,7 @@ describe('narrows responses by status', () => {
310
310
  param: { orgId: 'org_test' },
311
311
  })
312
312
 
313
- expectTypeOf(res.status).toEqualTypeOf<200 | 400 | 401 | 403 | 404 | 501 | 502>()
313
+ expectTypeOf(res.status).toEqualTypeOf<200 | 404 | 501 | 502 | PublicAccessStatus>()
314
314
 
315
315
  switch (res.status) {
316
316
  case 200: {
@@ -338,7 +338,7 @@ describe('narrows responses by status', () => {
338
338
  ':methodId{pm_[A-Za-z0-9_-]+}'
339
339
  ].$delete({ param: { methodId: 'pm_test', orgId: 'org_test' } })
340
340
 
341
- expectTypeOf(res.status).toEqualTypeOf<200 | 400 | 401 | 403 | 404 | 501 | 502>()
341
+ expectTypeOf(res.status).toEqualTypeOf<200 | 404 | 501 | 502 | PublicAccessStatus>()
342
342
 
343
343
  switch (res.status) {
344
344
  case 200: {
@@ -364,7 +364,7 @@ describe('narrows responses by status', () => {
364
364
  param: { orgId: 'org_test' },
365
365
  })
366
366
 
367
- expectTypeOf(res.status).toEqualTypeOf<200 | 400 | 401 | 403 | 404 | 502>()
367
+ expectTypeOf(res.status).toEqualTypeOf<200 | 404 | 502 | PublicAccessStatus>()
368
368
 
369
369
  switch (res.status) {
370
370
  case 200: {
@@ -388,7 +388,7 @@ describe('narrows responses by status', () => {
388
388
  ':projectId{prj_[A-Za-z0-9_-]+}'
389
389
  ].$get({ param: { orgId: 'org_test', projectId: 'prj_test' } })
390
390
 
391
- expectTypeOf(res.status).toEqualTypeOf<200 | 400 | 401 | 403 | 404>()
391
+ expectTypeOf(res.status).toEqualTypeOf<200 | 404 | PublicAccessStatus>()
392
392
 
393
393
  switch (res.status) {
394
394
  case 200: {
@@ -414,7 +414,7 @@ describe('narrows responses by status', () => {
414
414
  ':projectId{prj_[A-Za-z0-9_-]+}'
415
415
  ].$delete({ param: { orgId: 'org_test', projectId: 'prj_test' } })
416
416
 
417
- expectTypeOf(res.status).toEqualTypeOf<200 | 400 | 401 | 403 | 404 | 502>()
417
+ expectTypeOf(res.status).toEqualTypeOf<200 | 404 | 502 | PublicAccessStatus>()
418
418
 
419
419
  switch (res.status) {
420
420
  case 200: {
@@ -464,7 +464,7 @@ describe('narrows responses by status', () => {
464
464
  param: { orgId: 'org_test' },
465
465
  })
466
466
 
467
- expectTypeOf(res.status).toEqualTypeOf<200 | 400 | 401 | 403 | 404 | 502>()
467
+ expectTypeOf(res.status).toEqualTypeOf<200 | 404 | 502 | PublicAccessStatus>()
468
468
 
469
469
  switch (res.status) {
470
470
  case 200: {
@@ -487,7 +487,7 @@ describe('narrows responses by status', () => {
487
487
  param: { keyId: 'key_test', orgId: 'org_test', projectId: 'prj_test' },
488
488
  })
489
489
 
490
- expectTypeOf(res.status).toEqualTypeOf<200 | 400 | 401 | 403 | 404 | 502>()
490
+ expectTypeOf(res.status).toEqualTypeOf<200 | 404 | 502 | PublicAccessStatus>()
491
491
 
492
492
  switch (res.status) {
493
493
  case 404: {
@@ -511,7 +511,7 @@ describe('narrows responses by status', () => {
511
511
  query: { projectId: 'prj_test' },
512
512
  })
513
513
 
514
- expectTypeOf(res.status).toEqualTypeOf<200 | 400 | 401 | 403 | 404 | 501 | 502>()
514
+ expectTypeOf(res.status).toEqualTypeOf<200 | 404 | 501 | 502 | PublicAccessStatus>()
515
515
 
516
516
  switch (res.status) {
517
517
  case 200: {
@@ -545,7 +545,7 @@ describe('narrows responses by status', () => {
545
545
  param: { orgId: 'org_test' },
546
546
  })
547
547
 
548
- expectTypeOf(res.status).toEqualTypeOf<200 | 400 | 401 | 403 | 404 | 502>()
548
+ expectTypeOf(res.status).toEqualTypeOf<200 | 404 | 502 | PublicAccessStatus>()
549
549
 
550
550
  switch (res.status) {
551
551
  case 200: {
@@ -563,7 +563,7 @@ describe('narrows responses by status', () => {
563
563
  ':userId{usr_[A-Za-z0-9_-]+}'
564
564
  ].$patch({ json: { role: 'admin' }, param: { orgId: 'org_test', userId: 'usr_test' } })
565
565
 
566
- expectTypeOf(res.status).toEqualTypeOf<200 | 400 | 401 | 403 | 404 | 409 | 502>()
566
+ expectTypeOf(res.status).toEqualTypeOf<200 | 404 | 409 | 502 | PublicAccessStatus>()
567
567
 
568
568
  switch (res.status) {
569
569
  case 409: {
@@ -627,7 +627,7 @@ describe('narrows responses by status', () => {
627
627
  const client = Client.create()
628
628
  const res = await client.v1.orgs.$get()
629
629
 
630
- expectTypeOf(res.status).toEqualTypeOf<200 | 400 | 401 | 403 | 502>()
630
+ expectTypeOf(res.status).toEqualTypeOf<200 | 502 | PublicAccessStatus>()
631
631
 
632
632
  switch (res.status) {
633
633
  case 200: {
@@ -640,7 +640,7 @@ describe('narrows responses by status', () => {
640
640
  }
641
641
  case 403: {
642
642
  const json = await res.json()
643
- expectTypeOf(json.error.code).toEqualTypeOf<'forbidden'>()
643
+ expectTypeOf(json.error.code).toEqualTypeOf<'api_key_forbidden'>()
644
644
  expectTypeOf(json.error.message).toEqualTypeOf<string>()
645
645
  void json
646
646
  return
@@ -1221,6 +1221,53 @@ describe('narrows responses by status', () => {
1221
1221
  }
1222
1222
  })
1223
1223
 
1224
+ test('POST /v1/funding/routes/quote', async () => {
1225
+ const client = Client.create()
1226
+ const res = await client.v1.funding.routes.quote.$post({
1227
+ json: {
1228
+ depositor: '0x1111111111111111111111111111111111111111',
1229
+ provider: 'across',
1230
+ sourceAmount: '1000000',
1231
+ sourceChain: 'base',
1232
+ sourceToken: 'USDC',
1233
+ },
1234
+ })
1235
+
1236
+ expectTypeOf(res.status).toEqualTypeOf<200 | 400 | 404 | 501 | 502 | PaidAccessStatus>()
1237
+
1238
+ switch (res.status) {
1239
+ case 200: {
1240
+ const json = await res.json()
1241
+ expectTypeOf(json).toEqualTypeOf<
1242
+ z.output<typeof Funding.schema.quoteFundingRoute.Response>
1243
+ >()
1244
+ return
1245
+ }
1246
+ case 400: {
1247
+ const json = await res.json()
1248
+ expectTypeOf(json.error.code).toEqualTypeOf<Access400<'body_invalid'>>()
1249
+ return
1250
+ }
1251
+ case 404: {
1252
+ const json = await res.json()
1253
+ expectTypeOf(json.error.code).toEqualTypeOf<'quote_unavailable' | 'route_not_found'>()
1254
+ return
1255
+ }
1256
+ case 501: {
1257
+ const json = await res.json()
1258
+ expectTypeOf(json.error.code).toEqualTypeOf<'provider_unconfigured'>()
1259
+ return
1260
+ }
1261
+ case 502: {
1262
+ const json = await res.json()
1263
+ expectTypeOf(json.error.code).toEqualTypeOf<
1264
+ 'provider_error' | 'provider_rate_limited' | 'provider_timeout'
1265
+ >()
1266
+ return
1267
+ }
1268
+ }
1269
+ })
1270
+
1224
1271
  test('GET /v1/funding/chains', async () => {
1225
1272
  const client = Client.create()
1226
1273
  const res = await client.v1.funding.chains.$get()
@@ -0,0 +1,34 @@
1
+ import * as Scope from './Scope.js'
2
+
3
+ describe('extend', () => {
4
+ test('appends host scopes to the built-in catalog', () => {
5
+ expect(
6
+ Scope.extend([
7
+ {
8
+ description: 'Read project accounts.',
9
+ scope: 'accounts:read',
10
+ selfServe: true,
11
+ },
12
+ ]),
13
+ ).toEqual([
14
+ ...Scope.catalog,
15
+ {
16
+ description: 'Read project accounts.',
17
+ scope: 'accounts:read',
18
+ selfServe: true,
19
+ },
20
+ ])
21
+ })
22
+
23
+ test('rejects duplicate scope identifiers', () => {
24
+ expect(() =>
25
+ Scope.extend([
26
+ {
27
+ description: 'Duplicate data scope.',
28
+ scope: 'data:read',
29
+ selfServe: true,
30
+ },
31
+ ]),
32
+ ).toThrowErrorMatchingInlineSnapshot(`[Error: Duplicate scope \`data:read\`.]`)
33
+ })
34
+ })
package/src/Scope.ts CHANGED
@@ -1,8 +1,7 @@
1
1
  /**
2
- * The issuable API-key scope vocabulary: the single source of truth for the
3
- * `/scopes` catalog, for key minting (`ApiKeys.mint`), and for the scopes
4
- * required by route policies (`Auth.policy`). Exposing it publicly lets super
5
- * admin tooling validate scopes against the same list the server enforces.
2
+ * The built-in API-key scope vocabulary. Hosts append entries through
3
+ * `App.create({ scopes })`; the merged catalog drives discovery, minting, and
4
+ * API-key validation.
6
5
  */
7
6
 
8
7
  /**
@@ -13,20 +12,18 @@
13
12
  export const wildcard = '*'
14
13
 
15
14
  /** Catalog entry describing an issuable API-key scope. */
16
- type Entry = {
15
+ export type Entry = {
17
16
  /** Human-readable explanation of what the scope grants. */
18
17
  description: string
19
18
  /** Scope identifier used in a key's `scopes` and in route policies. */
20
19
  scope: string
21
- /** Whether a session may self-mint this scope (else super-admin-only). */
20
+ /** Whether an eligible session may self-mint this scope (else super-admin-only). */
22
21
  selfServe: boolean
23
22
  }
24
23
 
25
24
  /**
26
- * The issuable API-key scope vocabulary the single source of truth for the
27
- * `/scopes` catalog and for key minting. Route policies require values from this
28
- * set; {@link wildcard} (`'*'`) is an implicit grant-all and is intentionally
29
- * not listed as an issuable scope.
25
+ * The built-in issuable API-key scopes. {@link wildcard} (`'*'`) is an implicit
26
+ * grant-all and is intentionally not listed as an issuable scope.
30
27
  */
31
28
  export const catalog = [
32
29
  {
@@ -39,6 +36,17 @@ export const catalog = [
39
36
  scope: 'indexer:query',
40
37
  selfServe: true,
41
38
  },
39
+ {
40
+ description: 'Read organization resources across projects and environments.',
41
+ scope: 'management:read',
42
+ selfServe: true,
43
+ },
44
+ {
45
+ description:
46
+ 'Create, update, and delete organization resources across projects and environments.',
47
+ scope: 'management:write',
48
+ selfServe: true,
49
+ },
42
50
  {
43
51
  description: 'Fill and submit transactions through the relay.',
44
52
  scope: 'rpc-relay:read',
@@ -61,16 +69,44 @@ export const catalog = [
61
69
  },
62
70
  ] as const satisfies readonly Entry[]
63
71
 
64
- /** Issuable API-key scope identifier the union of every {@link catalog} entry. */
65
- export type Id = (typeof catalog)[number]['scope']
72
+ /** Issuable API-key scope identifier, including host-defined scopes. */
73
+ export type Id = (typeof catalog)[number]['scope'] | (string & {})
74
+
75
+ /** Scope catalog used by API-key issuance and discovery. */
76
+ export type Catalog = readonly Entry[]
66
77
 
67
78
  /**
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).
79
+ * Scopes an eligible session may self-mint through the developer platform,
80
+ * derived from the catalog's `selfServe` flag. Route-specific ownership rules
81
+ * may narrow issuance. Webhooks and {@link wildcard} stay super-admin-only.
73
82
  */
74
83
  export const selfServe: readonly Id[] = catalog
75
84
  .filter((entry) => entry.selfServe)
76
85
  .map((entry) => entry.scope)
86
+
87
+ /**
88
+ * Extends the built-in catalog with host-defined scopes.
89
+ *
90
+ * @param entries - Host-defined scope entries.
91
+ * @returns The merged scope catalog.
92
+ */
93
+ export function extend(entries: readonly Entry[] = []): Catalog {
94
+ const identifiers = new Set<string>()
95
+ const result = [...catalog, ...entries]
96
+ for (const entry of result) {
97
+ if (!entry.scope.trim()) throw new Error('Scope identifier must not be empty.')
98
+ if (identifiers.has(entry.scope)) throw new Error(`Duplicate scope \`${entry.scope}\`.`)
99
+ identifiers.add(entry.scope)
100
+ }
101
+ return result
102
+ }
103
+
104
+ /** Returns whether a catalog contains a scope identifier. */
105
+ export function includes(catalog: Catalog, scope: string): scope is Id {
106
+ return catalog.some((entry) => entry.scope === scope)
107
+ }
108
+
109
+ /** Returns the self-issuable identifiers in a scope catalog. */
110
+ export function selfServeFrom(catalog: Catalog): readonly Id[] {
111
+ return catalog.filter((entry) => entry.selfServe).map((entry) => entry.scope)
112
+ }
@@ -18,6 +18,10 @@ describe('create', () => {
18
18
  method: 'POST',
19
19
  }),
20
20
  new Request('http://admin/api-keys/key_x', { method: 'DELETE' }),
21
+ new Request('http://admin/organizations'),
22
+ new Request('http://admin/organizations/org_x'),
23
+ new Request('http://admin/organizations/org_x/members'),
24
+ new Request('http://admin/organizations/org_x/projects'),
21
25
  new Request('http://admin/verified-tokens?chainId=42431', {
22
26
  body: JSON.stringify({ tokens: [] }),
23
27
  headers: { 'content-type': 'application/json' },
@@ -67,13 +71,37 @@ describe('create', () => {
67
71
  describe('cache policy', () => {
68
72
  test('every response is no-store (admin data is per-principal and mutating)', async () => {
69
73
  const { app } = TestAdmin.setup()
70
- for (const path of ['/scopes', '/api-keys', '/config.json']) {
74
+ for (const path of ['/scopes', '/api-keys', '/organizations', '/config.json']) {
71
75
  const response = await app.request(`http://admin${path}`)
72
76
  expect(response.headers.get('cache-control')).toBe('no-store')
73
77
  }
74
78
  })
75
79
  })
76
80
 
81
+ describe('audit logging', () => {
82
+ test('records authenticated data access with actor, request id, route, and status', async () => {
83
+ const { app, db } = TestAdmin.setup({
84
+ identify: () => ({ email: 'ops@tempo.xyz' }),
85
+ })
86
+ const response = await app.request('http://admin/organizations?limit=5')
87
+ const records = await db.kysely
88
+ .selectFrom('admin_audit_logs')
89
+ .selectAll()
90
+ .orderBy('createdAt', 'desc')
91
+ .execute()
92
+
93
+ expect(response.status).toBe(200)
94
+ expect(records[0]).toMatchObject({
95
+ actor: 'ops@tempo.xyz',
96
+ method: 'GET',
97
+ path: '/organizations',
98
+ query: 'limit=5',
99
+ status: 200,
100
+ })
101
+ expect(records[0]?.requestId).toBeTruthy()
102
+ })
103
+ })
104
+
77
105
  describe('GET /config.json', () => {
78
106
  test('reports the signed-in identity and sign-in/out URLs', async () => {
79
107
  const { app } = TestAdmin.setup({
@@ -120,8 +148,10 @@ describe('create', () => {
120
148
  expect(response.headers.get('content-type')).toContain('text/html')
121
149
  const html = await response.text()
122
150
  expect(html).toContain('<div id="root">')
151
+ expect(html).not.toContain('data-theme="dark"')
152
+ expect(html).toContain("localStorage.getItem('tempo-api-admin.theme')")
123
153
  // Standalone mount → `<base href="/">` so relative asset/API URLs resolve.
124
- expect(html).toContain('<base href="/"/>')
154
+ expect(html).toContain('<base data-tempo-base="true" href="/"/>')
125
155
  })
126
156
 
127
157
  test('inlines the gated auth config into the shell (no bootstrap round trip)', async () => {
@@ -173,7 +203,7 @@ describe('create', () => {
173
203
  expect(response.status).toBe(200)
174
204
  // The base href is the mount root, not the deep link, so assets/API
175
205
  // resolve under `/admin/` from any in-app route.
176
- expect(await response.text()).toContain('<base href="/admin/"/>')
206
+ expect(await response.text()).toContain('<base data-tempo-base="true" href="/admin/"/>')
177
207
  })
178
208
 
179
209
  test('ui: false → no shell; `/` returns the JSON 404', async () => {
package/src/admin/App.ts CHANGED
@@ -4,11 +4,14 @@ import { requestId, type RequestIdVariables } from 'hono/request-id'
4
4
 
5
5
  import * as ApiKey from '../ApiKey.js'
6
6
  import type * as Assets from '../Assets.js'
7
- import type * as Db from '../db/Db.js'
7
+ import type * as Analytics from '../analytics/Analytics.js'
8
+ import * as Db from '../db/Db.js'
9
+ import * as AdminAuditLogs from '../db/tables/adminAuditLogs.js'
8
10
  import type * as Store from '../internal/Store.js'
9
11
  import * as Response from '../internal/Response.js'
10
12
 
11
13
  import { apiKeys } from './apps/api-keys.js'
14
+ import { organizations } from './apps/organizations.js'
12
15
  import { scopes } from './apps/scopes.js'
13
16
  import { verifiedTokens } from './apps/verified-tokens.js'
14
17
  import * as Ui from './ui.generated.js'
@@ -22,6 +25,8 @@ export type Identity = {
22
25
  /** Hono environment for the admin app. */
23
26
  export type Environment = {
24
27
  Variables: RequestIdVariables & {
28
+ /** ClickHouse analytics source used by operational usage reads. */
29
+ analytics: Analytics.Source
25
30
  /** Static-asset writer (e.g. the R2 icon bucket), when configured. */
26
31
  assets: create.Assets | undefined
27
32
  /** Authoritative Postgres store, or a per-request factory. Resolve at the leaf with `Db.get`. */
@@ -54,6 +59,7 @@ export function create(options: create.Options) {
54
59
  app.use('*', async (c, next) => {
55
60
  // Admin data is per-principal and mutating: never cache.
56
61
  c.header('Cache-Control', 'no-store')
62
+ c.set('analytics', options.analytics)
57
63
  c.set('assets', options.assets)
58
64
  c.set('db', options.db)
59
65
  c.set('kv', options.kv)
@@ -73,17 +79,18 @@ export function create(options: create.Options) {
73
79
  }
74
80
  }
75
81
 
76
- // Renders the public SPA shell: injects a `<base href>` of the mount root (so
77
- // relative asset/API URLs resolve) and the auth config (read synchronously,
78
- // `identity` is `null` when logged out). `<` is escaped to keep `<script>`.
82
+ // Renders the public SPA shell: stamps its hydration-safe base and auth
83
+ // placeholders with request values. `<` is escaped to keep the config script.
79
84
  const serveShell = (c: Context<Environment>) => {
80
85
  const mount = c.req.routePath.replace(/\*$/, '').replace(/\/$/, '')
81
86
  const config = JSON.stringify(authConfig(c.get('identity'))).replaceAll('<', '\\u003c')
82
87
  return c.html(
83
- Ui.shell.replace(
84
- '<head>',
85
- `<head><base href="${mount}/"/><script>window.__TEMPO_API_CONFIG__=${config}</script>`,
86
- ),
88
+ Ui.shell
89
+ .replace(
90
+ '<base data-tempo-base="true" href="/"/>',
91
+ `<base data-tempo-base="true" href="${mount}/"/>`,
92
+ )
93
+ .replace('window.__TEMPO_API_CONFIG__=undefined', `window.__TEMPO_API_CONFIG__=${config}`),
87
94
  )
88
95
  }
89
96
 
@@ -106,20 +113,46 @@ export function create(options: create.Options) {
106
113
  })
107
114
  return next()
108
115
  }
109
- for (const path of [
116
+ const protectedPaths = [
110
117
  '/config.json',
111
118
  '/api-keys',
112
119
  '/api-keys/*',
120
+ '/organizations',
121
+ '/organizations/*',
113
122
  '/scopes',
114
123
  '/scopes/*',
115
124
  '/verified-tokens',
116
125
  '/verified-tokens/*',
117
- ])
118
- app.use(path, requireAuth)
126
+ ] as const
127
+ for (const path of protectedPaths) app.use(path, requireAuth)
128
+
129
+ for (const path of protectedPaths)
130
+ app.use(path, async (c, next) => {
131
+ const identity = c.get('identity')
132
+ if (!identity) return
133
+ const search = new URL(c.req.url).searchParams
134
+ const record = (status: number) =>
135
+ AdminAuditLogs.insert(Db.get(c.get('db')), {
136
+ actor: identity.email,
137
+ method: c.req.method,
138
+ path: c.req.path,
139
+ query: search.size ? search.toString() : undefined,
140
+ requestId: c.get('requestId'),
141
+ status,
142
+ })
143
+ try {
144
+ await next()
145
+ } catch (cause) {
146
+ await record(500)
147
+ throw cause
148
+ }
149
+ await record(c.res.status)
150
+ })
119
151
 
120
152
  const routed = app
121
153
  .get('/config.json', (c) => c.json(authConfig(c.get('identity')), 200))
122
154
  .route('/api-keys', apiKeys())
155
+ .route('/organizations', organizations())
123
156
  .route('/scopes', scopes())
124
157
  .route('/verified-tokens', verifiedTokens())
125
158
 
@@ -172,15 +205,17 @@ export declare namespace create {
172
205
 
173
206
  /** Options for {@link create}. */
174
207
  type Options = {
208
+ /** ClickHouse analytics source used by operational usage reads. */
209
+ analytics: Analytics.Source
175
210
  /**
176
211
  * Static-asset writer for logo uploads (shared with the main API's `assets`
177
212
  * loader) to enable `PUT /verified-tokens/:address/logo`; omit for `404`.
178
213
  */
179
214
  assets?: Assets | undefined
180
215
  /**
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.
216
+ * Authoritative Postgres store shared with the main API. Pass a long-lived
217
+ * `Db.postgres(...)` singleton on Node, or a factory
218
+ * (`() => Db.postgres(...)`) on Workers.
184
219
  */
185
220
  db: Db.Source
186
221
  /**
@@ -5,6 +5,7 @@ import * as ApiKeys from '../../ApiKeys.js'
5
5
  import * as OpenApi from '../../internal/OpenApi.js'
6
6
  import * as Response from '../../internal/Response.js'
7
7
  import * as Schema from '../../internal/Schema.js'
8
+ import * as Scope from '../../Scope.js'
8
9
  import type * as App from '../App.js'
9
10
 
10
11
  /** Zod schemas owned by the admin API-keys handler. */
@@ -16,7 +17,11 @@ export namespace schema {
16
17
  * server sets from the verified identity so it can't be spoofed).
17
18
  */
18
19
  export const Body = Schema.describe(
19
- z.omit(ApiKeys.schema.MintInput, { createdBy: true }),
20
+ z.extend(z.omit(ApiKeys.schema.MintInput, { createdBy: true }), {
21
+ scopes: z
22
+ .readonly(z.array(z.enum([...Scope.catalog.map((entry) => entry.scope), Scope.wildcard])))
23
+ .check(z.describe("Granted scopes. '*' grants all scopes.")),
24
+ }),
20
25
  'Fields accepted when minting an API key.',
21
26
  )
22
27
 
@@ -59,7 +64,7 @@ export namespace schema {
59
64
  export const Body = Schema.describe(
60
65
  z.object({
61
66
  orgId: z.optional(z.string()).check(z.describe('New owning organization id (`org_…`).')),
62
- projectId: z.optional(z.string()).check(z.describe('New owning project id (`prj_…`).')),
67
+ projectId: z.optional(z.string()).check(z.describe('New attributed project id (`prj_…`).')),
63
68
  }),
64
69
  'Attribution to apply to an API key.',
65
70
  )
@@ -174,7 +179,9 @@ export function apiKeys() {
174
179
  }),
175
180
  async (c) => {
176
181
  const { orgId } = c.req.valid('query')
177
- const data = await ApiKeys.list(c.get('kv').store, orgId ? { orgId } : {})
182
+ const data = orgId
183
+ ? await ApiKeys.listByOrg(c.get('kv').store, orgId)
184
+ : await ApiKeys.list(c.get('kv').store)
178
185
  return c.json(Response.validated(schema.listApiKeys.Response, { data }))
179
186
  },
180
187
  )