tapimo 0.15.0 → 0.15.1

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 (91) hide show
  1. package/dist/App.d.ts +26 -26
  2. package/dist/admin/App.d.ts +3 -0
  3. package/dist/admin/App.d.ts.map +1 -1
  4. package/dist/admin/apps/api-keys.d.ts +41 -41
  5. package/dist/admin/apps/routes.d.ts +64 -64
  6. package/dist/admin/apps/verified-tokens.d.ts +161 -155
  7. package/dist/admin/apps/verified-tokens.d.ts.map +1 -1
  8. package/dist/admin/apps/verified-tokens.js +24 -4
  9. package/dist/admin/apps/verified-tokens.js.map +1 -1
  10. package/dist/admin/ui.generated.d.ts +1 -1
  11. package/dist/admin/ui.generated.d.ts.map +1 -1
  12. package/dist/admin/ui.generated.js +201 -209
  13. package/dist/admin/ui.generated.js.map +1 -1
  14. package/dist/apps/data/routes/activities.d.ts +357 -357
  15. package/dist/apps/data/routes/blocks.d.ts +125 -125
  16. package/dist/apps/data/routes/fee-amm.d.ts +108 -108
  17. package/dist/apps/data/routes/fee-amm.d.ts.map +1 -1
  18. package/dist/apps/data/routes/fee-amm.js +10 -2
  19. package/dist/apps/data/routes/fee-amm.js.map +1 -1
  20. package/dist/apps/data/routes/indexer.d.ts +43 -43
  21. package/dist/apps/data/routes/receipts.d.ts +235 -235
  22. package/dist/apps/data/routes/rpc.js +45 -11
  23. package/dist/apps/data/routes/rpc.js.map +1 -1
  24. package/dist/apps/data/routes/transfers.d.ts +81 -81
  25. package/dist/apps/data/routes/valuation.d.ts +32 -32
  26. package/dist/apps/data/routes/verified-tokens.d.ts +86 -86
  27. package/dist/apps/data/routes/webhooks.d.ts +1 -1
  28. package/dist/apps/data/routes/webhooks.d.ts.map +1 -1
  29. package/dist/apps/data/routes/webhooks.js +17 -10
  30. package/dist/apps/data/routes/webhooks.js.map +1 -1
  31. package/dist/apps/data/routes/zones.d.ts +31 -31
  32. package/dist/apps/management/App.d.ts +26 -26
  33. package/dist/apps/management/routes/api-keys.d.ts +246 -246
  34. package/dist/apps/management/routes/invitations.d.ts +138 -138
  35. package/dist/apps/management/routes/invite-links.d.ts +168 -168
  36. package/dist/apps/management/routes/orgs.d.ts +98 -98
  37. package/dist/apps/management/routes/projects.d.ts +125 -125
  38. package/dist/apps/management/routes/verified-token-requests.d.ts +95 -95
  39. package/dist/apps/management/routes/verified-token-requests.d.ts.map +1 -1
  40. package/dist/apps/management/routes/verified-token-requests.js +7 -9
  41. package/dist/apps/management/routes/verified-token-requests.js.map +1 -1
  42. package/dist/apps/management/routes/webhooks.d.ts +423 -423
  43. package/dist/apps/mcp.d.ts.map +1 -1
  44. package/dist/apps/mcp.js +16 -3
  45. package/dist/apps/mcp.js.map +1 -1
  46. package/dist/apps/mpp/App.d.ts +44 -44
  47. package/dist/apps/routes/routes/chains.d.ts +25 -25
  48. package/dist/apps/routes/routes/quotes.d.ts +87 -87
  49. package/dist/apps/routes/routes/transfers.d.ts +438 -438
  50. package/dist/db/Db.d.ts.map +1 -1
  51. package/dist/db/Db.js +5 -0
  52. package/dist/db/Db.js.map +1 -1
  53. package/dist/db/tables/verifiedTokenRequests.d.ts +21 -4
  54. package/dist/db/tables/verifiedTokenRequests.d.ts.map +1 -1
  55. package/dist/db/tables/verifiedTokenRequests.js +65 -44
  56. package/dist/db/tables/verifiedTokenRequests.js.map +1 -1
  57. package/dist/internal/Auth.d.ts +16 -0
  58. package/dist/internal/Auth.d.ts.map +1 -1
  59. package/dist/internal/Auth.js +31 -8
  60. package/dist/internal/Auth.js.map +1 -1
  61. package/dist/internal/routes/providers/relay.d.ts +1 -1
  62. package/dist/internal/routes/providers/relay.js +1 -1
  63. package/dist/internal/routes/providers/relay.js.map +1 -1
  64. package/package.json +1 -1
  65. package/src/admin/apps/verified-tokens.test.ts +32 -0
  66. package/src/admin/apps/verified-tokens.ts +30 -4
  67. package/src/admin/ui/src/lib/verified-tokens.ts +24 -9
  68. package/src/admin/ui/src/routes/__root.tsx +1 -1
  69. package/src/admin/ui/src/routes/organizations.tsx +34 -4
  70. package/src/admin/ui/src/routes/verified-tokens.tsx +64 -20
  71. package/src/admin/ui.generated.ts +202 -263
  72. package/src/apps/data/App.test.ts +26 -0
  73. package/src/apps/data/routes/fee-amm.test.ts +19 -0
  74. package/src/apps/data/routes/fee-amm.ts +10 -2
  75. package/src/apps/data/routes/rpc.test.ts +32 -0
  76. package/src/apps/data/routes/rpc.ts +47 -10
  77. package/src/apps/data/routes/webhooks.scan.test.ts +29 -0
  78. package/src/apps/data/routes/webhooks.test.ts +6 -0
  79. package/src/apps/data/routes/webhooks.ts +25 -12
  80. package/src/apps/management/routes/verified-token-requests.test.ts +16 -2
  81. package/src/apps/management/routes/verified-token-requests.ts +7 -10
  82. package/src/apps/mcp.test.ts +108 -25
  83. package/src/apps/mcp.ts +15 -3
  84. package/src/db/Db.ts +9 -0
  85. package/src/db/tables/routesDeposits.test.ts +72 -0
  86. package/src/db/tables/verifiedTokenRequests.test.ts +39 -20
  87. package/src/db/tables/verifiedTokenRequests.ts +80 -44
  88. package/src/internal/Auth.test.ts +92 -0
  89. package/src/internal/Auth.ts +51 -9
  90. package/src/internal/routes/providers/relay.test.ts +59 -0
  91. package/src/internal/routes/providers/relay.ts +1 -1
@@ -2,6 +2,7 @@ import { type Context, Hono } from 'hono'
2
2
  import * as z from 'zod/mini'
3
3
 
4
4
  import * as AssetResponse from '../../internal/AssetResponse.js'
5
+ import * as Cursor from '../../internal/Cursor.js'
5
6
  import * as OpenApi from '../../internal/OpenApi.js'
6
7
  import * as Response from '../../internal/Response.js'
7
8
  import * as Schema from '../../internal/Schema.js'
@@ -15,6 +16,8 @@ import type * as App from '../App.js'
15
16
 
16
17
  /** Required `chainId` query: admin mutations always address a chain explicitly. */
17
18
  const ChainIdQuery = Schema.ChainId
19
+ /** Maximum rows per review-queue response, bounding inline SVG payloads to 1.25 MiB. */
20
+ const maxRequestPage = 5
18
21
 
19
22
  /** Zod schemas owned by the admin verified-tokens handler. */
20
23
  export namespace schema {
@@ -157,6 +160,16 @@ export namespace schema {
157
160
  /** Query parameters for the request queue. */
158
161
  export const Query = z
159
162
  .strictObject({
163
+ cursor: Schema.Cursor,
164
+ limit: z
165
+ .pipe(
166
+ Schema.Limit,
167
+ z.transform((value) => Math.min(value, maxRequestPage)),
168
+ )
169
+ .check(
170
+ z.describe(`How many requests to return (5 maximum, default ${maxRequestPage}).`),
171
+ z.meta({ examples: [maxRequestPage] }),
172
+ ),
160
173
  status: z
161
174
  ._default(z.enum(['approved', 'denied', 'pending']), 'pending')
162
175
  .check(z.describe('Review status to filter by.')),
@@ -167,8 +180,9 @@ export namespace schema {
167
180
  export const Response = Schema.describe(
168
181
  z.object({
169
182
  data: z.array(Request).check(z.describe('Requests in the selected status, oldest first.')),
183
+ nextCursor: Schema.NextCursor,
170
184
  }),
171
- 'Verified-token requests in review-queue order.',
185
+ 'A page of verified-token requests in review-queue order.',
172
186
  )
173
187
  }
174
188
 
@@ -558,14 +572,26 @@ export function verifiedTokens() {
558
572
  }),
559
573
  async (c) => {
560
574
  const db = Db.get(c.get('db'))
561
- const { status } = c.req.valid('query')
562
- const records = await VerifiedTokenRequests.listByStatus(db, status)
575
+ const { cursor, limit, status } = c.req.valid('query')
576
+ const decoded = cursor ? Cursor.decode(cursor, ['timestamp', 'id']) : undefined
577
+ const records = await VerifiedTokenRequests.listByStatus(db, status, {
578
+ ...(decoded
579
+ ? { cursor: { createdAt: String(decoded[0]), id: String(decoded[1]) } }
580
+ : {}),
581
+ limit: limit + 1,
582
+ })
583
+ const page = Cursor.paginate({
584
+ key: (record) => [record.createdAt, record.id],
585
+ limit,
586
+ rows: records,
587
+ })
563
588
  return c.json(
564
589
  Response.validated(schema.listVerifiedTokenRequests.Response, {
565
- data: records.map((record) => ({
590
+ data: page.rows.map((record) => ({
566
591
  ...VerifiedTokenRequests.toRequest(record),
567
592
  orgName: record.orgName,
568
593
  })),
594
+ nextCursor: page.nextCursor,
569
595
  }),
570
596
  )
571
597
  },
@@ -76,26 +76,41 @@ export namespace create {
76
76
 
77
77
  /** Review queue over organization verification requests. */
78
78
  export namespace requests {
79
+ /** One page returned by the queue endpoint. */
80
+ export type Data = InferResponseType<(typeof client)['verified-tokens']['requests']['$get'], 200>
81
+
79
82
  /** A verification request record from the queue endpoint. */
80
- export type VerifiedTokenRequest = InferResponseType<
81
- (typeof client)['verified-tokens']['requests']['$get'],
82
- 200
83
- >['data'][number]
83
+ export type VerifiedTokenRequest = Data['data'][number]
84
84
 
85
85
  export namespace list {
86
86
  export function key() {
87
87
  return ['verified-token-requests'] as const
88
88
  }
89
- export async function fn() {
90
- const response = await client['verified-tokens'].requests.$get({ query: {} })
89
+ export async function fn(options: fn.Options) {
90
+ const response = await client['verified-tokens'].requests.$get({
91
+ query: { ...options, limit: '5' },
92
+ })
91
93
  if (!response.ok) throw new Error('Failed to load requests.')
92
- return response.json()
94
+ return response.json() as Promise<Data>
95
+ }
96
+ export declare namespace fn {
97
+ /** Review-queue page selection. */
98
+ type Options = {
99
+ /** Cursor after the previous loaded page. */
100
+ cursor?: string | undefined
101
+ }
93
102
  }
94
103
  export function options() {
95
- return ReactQuery.queryOptions({ queryFn: fn, queryKey: key(), staleTime: 30_000 })
104
+ return ReactQuery.infiniteQueryOptions({
105
+ getNextPageParam: (page: Data) => page.nextCursor ?? undefined,
106
+ initialPageParam: null as string | null,
107
+ queryFn: ({ pageParam }): Promise<Data> => fn({ cursor: pageParam ?? undefined }),
108
+ queryKey: key(),
109
+ staleTime: 30_000,
110
+ })
96
111
  }
97
112
  export function useQuery() {
98
- return ReactQuery.useQuery(options())
113
+ return ReactQuery.useInfiniteQuery(options())
99
114
  }
100
115
  }
101
116
 
@@ -111,7 +111,7 @@ function Admin() {
111
111
  <div className="relative flex min-h-dvh min-w-[360px] bg-background max-md:h-dvh max-md:flex-col max-md:overflow-hidden">
112
112
  <div className="flex-1 bg-background max-md:hidden" />
113
113
  <Sidebar email={email} logoutUrl={logoutUrl} />
114
- <main className="flex w-full max-w-[1024px] flex-col px-4 pt-5 pb-10 max-md:min-h-0 max-md:flex-1 max-md:overflow-y-auto sm:px-8">
114
+ <main className="flex min-w-0 w-full max-w-[1024px] flex-col px-4 pt-5 pb-10 max-md:min-h-0 max-md:flex-1 max-md:overflow-y-auto sm:px-8">
115
115
  <Outlet />
116
116
  </main>
117
117
  <div className="flex-1 max-md:hidden" />
@@ -6,7 +6,7 @@ import {
6
6
  useNavigate,
7
7
  useRouter,
8
8
  } from '@tanstack/react-router'
9
- import { useEffect, useState } from 'react'
9
+ import { useEffect, useRef, useState } from 'react'
10
10
  import { Button, Input, Rows } from 'regen-ui'
11
11
  import { ChevronRight, Search } from 'lucide-react'
12
12
 
@@ -27,20 +27,39 @@ function PageComponent() {
27
27
  const { query } = Route.useLoaderDeps()
28
28
  const navigate = useNavigate({ from: Route.fullPath })
29
29
  const [value, setValue] = useState(query ?? '')
30
+ const debouncedValue = useDebouncedValue(value, 300)
31
+ const submittedValue = useRef(query ?? '')
30
32
  const organizations = useSuspenseInfiniteQuery(Organizations.list.options({ query }))
31
33
  const records = organizations.data.pages.flatMap((page) => page.data)
32
34
  const invalid = value.trim().length > 0 && value.trim().length < 3
33
35
 
34
- useEffect(() => setValue(query ?? ''), [query])
36
+ useEffect(() => {
37
+ if (value.trim() !== submittedValue.current) return
38
+ const next = query ?? ''
39
+ submittedValue.current = next
40
+ setValue(next)
41
+ }, [query, value])
42
+ useEffect(() => {
43
+ const next = debouncedValue.trim()
44
+ if (value.trim() !== next || (next.length > 0 && next.length < 3) || next === (query ?? ''))
45
+ return
46
+ submittedValue.current = next
47
+ void navigate({
48
+ replace: true,
49
+ search: Organizations.searchParameters({ query: next }),
50
+ to: '/organizations',
51
+ })
52
+ }, [debouncedValue, navigate, query, value])
35
53
 
36
54
  return (
37
55
  <Page title="Organizations" description="Look up organizations by id, name, or member email">
38
56
  <form
39
- className="flex gap-2"
57
+ className="flex min-w-0 gap-2"
40
58
  onSubmit={(event) => {
41
59
  event.preventDefault()
42
60
  const next = value.trim()
43
61
  if (invalid) return
62
+ submittedValue.current = next
44
63
  void navigate({
45
64
  search: Organizations.searchParameters({ query: next }),
46
65
  to: '/organizations',
@@ -56,7 +75,7 @@ function PageComponent() {
56
75
  value={value}
57
76
  />
58
77
  </div>
59
- <Button disabled={invalid} type="submit">
78
+ <Button className="shrink-0" disabled={invalid} type="submit">
60
79
  <Search className="size-4" />
61
80
  Search
62
81
  </Button>
@@ -127,6 +146,17 @@ function PageComponent() {
127
146
  )
128
147
  }
129
148
 
149
+ function useDebouncedValue(value: string, delay: number) {
150
+ const [debouncedValue, setDebouncedValue] = useState(value)
151
+
152
+ useEffect(() => {
153
+ const timeout = setTimeout(() => setDebouncedValue(value), delay)
154
+ return () => clearTimeout(timeout)
155
+ }, [delay, value])
156
+
157
+ return debouncedValue
158
+ }
159
+
130
160
  function LookupError(options: LookupError.Props) {
131
161
  const router = useRouter()
132
162
  return (
@@ -22,7 +22,7 @@ export const Route = createFileRoute('/verified-tokens')({
22
22
  loader: ({ context }) =>
23
23
  Promise.all([
24
24
  context.queryClient.prefetchQuery(VerifiedTokens.get.options({ chainId: defaultChain })),
25
- context.queryClient.prefetchQuery(VerifiedTokens.requests.list.options()),
25
+ context.queryClient.prefetchInfiniteQuery(VerifiedTokens.requests.list.options()),
26
26
  ]),
27
27
  component: RouteComponent,
28
28
  })
@@ -47,7 +47,10 @@ function RouteComponent() {
47
47
  const tokens = VerifiedTokens.get.useQuery({ chainId: chain })
48
48
  const requests = VerifiedTokens.requests.list.useQuery()
49
49
  const create = VerifiedTokens.create.useMutation()
50
- const pendingCount = requests.data?.data.length ?? 0
50
+ const pending =
51
+ requests.data?.pages.flatMap((page: VerifiedTokens.requests.Data) => page.data) ?? []
52
+ const pendingCount = pending.length
53
+ const pendingLabel = requests.hasNextPage ? `${pendingCount}+` : String(pendingCount)
51
54
 
52
55
  return (
53
56
  <Dialog.Root onOpenChange={setOpen} open={open}>
@@ -75,7 +78,7 @@ function RouteComponent() {
75
78
  onChange={(id) => void navigate({ search: id === 'verified' ? {} : { tab: id } })}
76
79
  tabs={[
77
80
  { id: 'verified', label: 'Verified' },
78
- { id: 'pending', label: pendingCount > 0 ? `Pending (${pendingCount})` : 'Pending' },
81
+ { id: 'pending', label: pendingCount > 0 ? `Pending (${pendingLabel})` : 'Pending' },
79
82
  ]}
80
83
  />
81
84
  {tab === 'verified' ? (
@@ -87,9 +90,13 @@ function RouteComponent() {
87
90
  />
88
91
  ) : (
89
92
  <RequestQueue
90
- error={requests.error}
93
+ error={requests.isLoadingError ? requests.error : null}
94
+ hasNextPage={requests.hasNextPage}
95
+ loadMoreError={requests.isFetchNextPageError ? requests.error : null}
91
96
  loading={requests.isPending}
92
- requests={requests.data?.data ?? []}
97
+ loadingMore={requests.isFetchingNextPage}
98
+ onLoadMore={() => requests.fetchNextPage()}
99
+ requests={pending}
93
100
  />
94
101
  )}
95
102
  <Dialog.Popup>
@@ -115,11 +122,7 @@ function chainLabel(chainId: number) {
115
122
  }
116
123
 
117
124
  /** Pending verification requests, oldest first, reviewed in bulk via selection. */
118
- function RequestQueue(props: {
119
- error: Error | null
120
- loading: boolean
121
- requests: readonly VerifiedTokens.requests.VerifiedTokenRequest[]
122
- }) {
125
+ function RequestQueue(props: RequestQueue.Props) {
123
126
  const approve = VerifiedTokens.requests.approve.useMutation()
124
127
  const deny = VerifiedTokens.requests.deny.useMutation()
125
128
  const [selected, setSelected] = useState<ReadonlySet<string>>(new Set())
@@ -197,21 +200,62 @@ function RequestQueue(props: {
197
200
  ) : props.requests.length === 0 ? (
198
201
  <Notice>No pending requests.</Notice>
199
202
  ) : (
200
- <ul className="divide-y divide-border">
201
- {props.requests.map((request) => (
202
- <RequestRow
203
- key={request.id}
204
- onToggle={() => toggle(request.id)}
205
- request={request}
206
- selected={selected.has(request.id)}
207
- />
208
- ))}
209
- </ul>
203
+ <div className="flex flex-col gap-4 pb-4">
204
+ <ul className="divide-y divide-border">
205
+ {props.requests.map((request) => (
206
+ <RequestRow
207
+ key={request.id}
208
+ onToggle={() => toggle(request.id)}
209
+ request={request}
210
+ selected={selected.has(request.id)}
211
+ />
212
+ ))}
213
+ </ul>
214
+ {props.loadMoreError ? (
215
+ <div
216
+ className="flex items-center justify-center gap-3 text-copy-14 text-negative"
217
+ role="alert"
218
+ >
219
+ <span>{props.loadMoreError.message}</span>
220
+ <Button loading={props.loadingMore} onClick={() => void props.onLoadMore()}>
221
+ Retry
222
+ </Button>
223
+ </div>
224
+ ) : props.hasNextPage ? (
225
+ <Button
226
+ className="self-center"
227
+ loading={props.loadingMore}
228
+ onClick={() => void props.onLoadMore()}
229
+ >
230
+ Load more
231
+ </Button>
232
+ ) : null}
233
+ </div>
210
234
  )}
211
235
  </Card>
212
236
  )
213
237
  }
214
238
 
239
+ declare namespace RequestQueue {
240
+ /** Pending queue display state and pagination controls. */
241
+ type Props = {
242
+ /** Queue load error. */
243
+ error: Error | null
244
+ /** Whether another queue page is available. */
245
+ hasNextPage: boolean
246
+ /** Whether the first page is loading. */
247
+ loading: boolean
248
+ /** Error returned while loading another page. */
249
+ loadMoreError: Error | null
250
+ /** Whether another page is loading. */
251
+ loadingMore: boolean
252
+ /** Loads the next queue page. */
253
+ onLoadMore: () => Promise<unknown>
254
+ /** Loaded requests in review order. */
255
+ requests: readonly VerifiedTokens.requests.VerifiedTokenRequest[]
256
+ }
257
+ }
258
+
215
259
  /** Confirmation label: one token by name, several by count. */
216
260
  function describeSelection(requests: readonly VerifiedTokens.requests.VerifiedTokenRequest[]) {
217
261
  const [first] = requests