tapimo 0.5.0 → 0.5.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (197) hide show
  1. package/dist/App.d.ts +4854 -4829
  2. package/dist/App.d.ts.map +1 -1
  3. package/dist/App.js +20 -2
  4. package/dist/App.js.map +1 -1
  5. package/dist/Assets.js +3 -3
  6. package/dist/Assets.js.map +1 -1
  7. package/dist/Scope.d.ts.map +1 -1
  8. package/dist/Scope.js +1 -2
  9. package/dist/Scope.js.map +1 -1
  10. package/dist/Webhooks.d.ts +1 -1
  11. package/dist/Webhooks.d.ts.map +1 -1
  12. package/dist/Webhooks.js +5 -10
  13. package/dist/Webhooks.js.map +1 -1
  14. package/dist/admin/App.d.ts +6 -34
  15. package/dist/admin/App.d.ts.map +1 -1
  16. package/dist/admin/apps/api-keys.d.ts +3 -3
  17. package/dist/admin/apps/api-keys.d.ts.map +1 -1
  18. package/dist/admin/apps/api-keys.js +4 -1
  19. package/dist/admin/apps/api-keys.js.map +1 -1
  20. package/dist/admin/apps/scopes.d.ts +6 -34
  21. package/dist/admin/apps/scopes.d.ts.map +1 -1
  22. package/dist/admin/apps/scopes.js +2 -2
  23. package/dist/admin/apps/scopes.js.map +1 -1
  24. package/dist/admin/apps/verified-tokens.d.ts +2 -2
  25. package/dist/admin/ui.generated.d.ts +1 -1
  26. package/dist/admin/ui.generated.js +33 -33
  27. package/dist/admin/ui.generated.js.map +1 -1
  28. package/dist/apps/data/App.d.ts +1398 -1405
  29. package/dist/apps/data/App.d.ts.map +1 -1
  30. package/dist/apps/data/App.js +25 -38
  31. package/dist/apps/data/App.js.map +1 -1
  32. package/dist/apps/data/FundingQuoteProviders.d.ts +108 -3
  33. package/dist/apps/data/FundingQuoteProviders.d.ts.map +1 -1
  34. package/dist/apps/data/FundingQuoteProviders.js +409 -2
  35. package/dist/apps/data/FundingQuoteProviders.js.map +1 -1
  36. package/dist/apps/data/routes/activities.d.ts +245 -245
  37. package/dist/apps/data/routes/blocks.d.ts +123 -123
  38. package/dist/apps/data/routes/funding.d.ts +236 -146
  39. package/dist/apps/data/routes/funding.d.ts.map +1 -1
  40. package/dist/apps/data/routes/funding.js +23 -12
  41. package/dist/apps/data/routes/funding.js.map +1 -1
  42. package/dist/apps/data/routes/indexer.d.ts.map +1 -1
  43. package/dist/apps/data/routes/indexer.js +9 -2
  44. package/dist/apps/data/routes/indexer.js.map +1 -1
  45. package/dist/apps/data/routes/rpc.d.ts +31 -31
  46. package/dist/apps/data/routes/tokenlist.d.ts +36 -36
  47. package/dist/apps/data/routes/transactions.d.ts +281 -281
  48. package/dist/apps/data/routes/transfers.d.ts +66 -73
  49. package/dist/apps/data/routes/transfers.d.ts.map +1 -1
  50. package/dist/apps/data/routes/transfers.js +21 -27
  51. package/dist/apps/data/routes/transfers.js.map +1 -1
  52. package/dist/apps/data/routes/verified-tokens.d.ts +86 -86
  53. package/dist/apps/data/routes/webhooks.d.ts +35 -35
  54. package/dist/apps/data/routes/webhooks.d.ts.map +1 -1
  55. package/dist/apps/data/routes/webhooks.js +176 -42
  56. package/dist/apps/data/routes/webhooks.js.map +1 -1
  57. package/dist/apps/management/App.d.ts +520 -504
  58. package/dist/apps/management/App.d.ts.map +1 -1
  59. package/dist/apps/management/Billing.d.ts.map +1 -1
  60. package/dist/apps/management/Billing.js +5 -4
  61. package/dist/apps/management/Billing.js.map +1 -1
  62. package/dist/apps/management/routes/api-keys.d.ts +120 -120
  63. package/dist/apps/management/routes/billing.d.ts +211 -211
  64. package/dist/apps/management/routes/billing.d.ts.map +1 -1
  65. package/dist/apps/management/routes/billing.js +1 -1
  66. package/dist/apps/management/routes/billing.js.map +1 -1
  67. package/dist/apps/management/routes/invitations.d.ts +72 -72
  68. package/dist/apps/management/routes/invite-links.d.ts +159 -159
  69. package/dist/apps/management/routes/me.d.ts +17 -17
  70. package/dist/apps/management/routes/members.d.ts +80 -80
  71. package/dist/apps/management/routes/orgs.d.ts +59 -43
  72. package/dist/apps/management/routes/orgs.d.ts.map +1 -1
  73. package/dist/apps/management/routes/orgs.js +18 -1
  74. package/dist/apps/management/routes/orgs.js.map +1 -1
  75. package/dist/apps/management/routes/projects.d.ts +109 -109
  76. package/dist/apps/management/routes/scopes.d.ts +11 -11
  77. package/dist/apps/management/routes/usage.d.ts +79 -79
  78. package/dist/apps/mcp.js +2 -2
  79. package/dist/apps/mcp.js.map +1 -1
  80. package/dist/apps/relay/App.d.ts +2 -2
  81. package/dist/apps/relay/App.js +2 -2
  82. package/dist/apps/relay/Sponsorships.d.ts +3 -0
  83. package/dist/apps/relay/Sponsorships.d.ts.map +1 -1
  84. package/dist/apps/relay/Sponsorships.js +15 -1
  85. package/dist/apps/relay/Sponsorships.js.map +1 -1
  86. package/dist/db/Db.d.ts.map +1 -1
  87. package/dist/db/Db.js +4 -0
  88. package/dist/db/Db.js.map +1 -1
  89. package/dist/db/Schema.js +1 -1
  90. package/dist/db/Schema.js.map +1 -1
  91. package/dist/db/tables/organizations.d.ts +6 -0
  92. package/dist/db/tables/organizations.d.ts.map +1 -1
  93. package/dist/db/tables/organizations.js +26 -0
  94. package/dist/db/tables/organizations.js.map +1 -1
  95. package/dist/db/tables/requestUsage.d.ts +2 -11
  96. package/dist/db/tables/requestUsage.d.ts.map +1 -1
  97. package/dist/db/tables/requestUsage.js +1 -20
  98. package/dist/db/tables/requestUsage.js.map +1 -1
  99. package/dist/db/tables/sponsoredTransactions.d.ts +2 -1
  100. package/dist/db/tables/sponsoredTransactions.d.ts.map +1 -1
  101. package/dist/db/tables/sponsoredTransactions.js +52 -10
  102. package/dist/db/tables/sponsoredTransactions.js.map +1 -1
  103. package/dist/internal/Auth.d.ts +4 -0
  104. package/dist/internal/Auth.d.ts.map +1 -1
  105. package/dist/internal/Auth.js +15 -8
  106. package/dist/internal/Auth.js.map +1 -1
  107. package/dist/internal/Cache.d.ts.map +1 -1
  108. package/dist/internal/Cache.js +9 -1
  109. package/dist/internal/Cache.js.map +1 -1
  110. package/dist/internal/EdgeCache.d.ts.map +1 -1
  111. package/dist/internal/EdgeCache.js +12 -3
  112. package/dist/internal/EdgeCache.js.map +1 -1
  113. package/dist/internal/FundingQuotes.d.ts +176 -56
  114. package/dist/internal/FundingQuotes.d.ts.map +1 -1
  115. package/dist/internal/FundingQuotes.js +100 -5
  116. package/dist/internal/FundingQuotes.js.map +1 -1
  117. package/dist/internal/Log.d.ts +18 -12
  118. package/dist/internal/Log.d.ts.map +1 -1
  119. package/dist/internal/Log.js +2 -0
  120. package/dist/internal/Log.js.map +1 -1
  121. package/dist/internal/RateLimit.d.ts +2 -0
  122. package/dist/internal/RateLimit.d.ts.map +1 -1
  123. package/dist/internal/RateLimit.js +4 -4
  124. package/dist/internal/RateLimit.js.map +1 -1
  125. package/dist/internal/Tidx.d.ts +9 -3
  126. package/dist/internal/Tidx.d.ts.map +1 -1
  127. package/dist/internal/Tidx.js +13 -7
  128. package/dist/internal/Tidx.js.map +1 -1
  129. package/dist/internal/Viem.d.ts +14 -1
  130. package/dist/internal/Viem.d.ts.map +1 -1
  131. package/dist/internal/Viem.js +19 -1
  132. package/dist/internal/Viem.js.map +1 -1
  133. package/package.json +7 -2
  134. package/src/ApiKeys.test.ts +2 -0
  135. package/src/App.test.ts +15 -4
  136. package/src/App.ts +31 -5
  137. package/src/Assets.ts +3 -3
  138. package/src/Client.test-d.ts +30 -3
  139. package/src/Scope.ts +4 -2
  140. package/src/Webhooks.ts +5 -10
  141. package/src/admin/apps/api-keys.test.ts +27 -0
  142. package/src/admin/apps/api-keys.ts +8 -1
  143. package/src/admin/apps/scopes.test.ts +7 -2
  144. package/src/admin/apps/scopes.ts +2 -2
  145. package/src/admin/ui/src/components/Dialog.tsx +8 -2
  146. package/src/admin/ui/src/lib/scopes.test.ts +19 -0
  147. package/src/admin/ui/src/lib/scopes.ts +12 -0
  148. package/src/admin/ui/src/routes/api-keys.tsx +128 -27
  149. package/src/admin/ui.generated.ts +33 -33
  150. package/src/apps/composition.test.ts +77 -0
  151. package/src/apps/data/App.ts +25 -45
  152. package/src/apps/data/FundingQuoteProviders.test.ts +615 -5
  153. package/src/apps/data/FundingQuoteProviders.ts +570 -4
  154. package/src/apps/data/routes/funding.test.ts +181 -7
  155. package/src/apps/data/routes/funding.ts +27 -12
  156. package/src/apps/data/routes/indexer.test.ts +82 -20
  157. package/src/apps/data/routes/indexer.ts +8 -2
  158. package/src/apps/data/routes/tokens.test.ts +54 -0
  159. package/src/apps/data/routes/transfers.test.ts +73 -1
  160. package/src/apps/data/routes/transfers.ts +24 -30
  161. package/src/apps/data/routes/webhooks.test.ts +203 -0
  162. package/src/apps/data/routes/webhooks.ts +191 -54
  163. package/src/apps/management/Billing.test.ts +52 -3
  164. package/src/apps/management/Billing.ts +5 -4
  165. package/src/apps/management/routes/billing.ts +6 -1
  166. package/src/apps/management/routes/orgs.ts +19 -1
  167. package/src/apps/mcp.test.ts +18 -0
  168. package/src/apps/mcp.ts +2 -2
  169. package/src/apps/relay/App.test.ts +6 -5
  170. package/src/apps/relay/App.ts +2 -2
  171. package/src/apps/relay/Sponsorships.test.ts +2 -2
  172. package/src/apps/relay/Sponsorships.ts +24 -1
  173. package/src/db/Db.test.ts +5 -5
  174. package/src/db/Db.ts +4 -0
  175. package/src/db/Schema.ts +1 -1
  176. package/src/db/tables/billingSettings.test.ts +2 -2
  177. package/src/db/tables/organizations.test.ts +32 -2
  178. package/src/db/tables/organizations.ts +28 -0
  179. package/src/db/tables/requestUsage.ts +3 -23
  180. package/src/db/tables/sponsoredTransactions.test.ts +60 -2
  181. package/src/db/tables/sponsoredTransactions.ts +67 -21
  182. package/src/db/tables/stripeCustomers.test.ts +2 -2
  183. package/src/internal/Auth.test.ts +63 -10
  184. package/src/internal/Auth.ts +21 -9
  185. package/src/internal/Cache.test.ts +19 -0
  186. package/src/internal/Cache.ts +13 -1
  187. package/src/internal/EdgeCache.test.ts +40 -0
  188. package/src/internal/EdgeCache.ts +11 -3
  189. package/src/internal/FundingQuotes.test.ts +130 -3
  190. package/src/internal/FundingQuotes.ts +113 -6
  191. package/src/internal/Log.ts +21 -14
  192. package/src/internal/RateLimit.test.ts +12 -0
  193. package/src/internal/RateLimit.ts +4 -4
  194. package/src/internal/Tidx.test.ts +48 -0
  195. package/src/internal/Tidx.ts +21 -7
  196. package/src/internal/Viem.test.ts +46 -0
  197. package/src/internal/Viem.ts +34 -1
@@ -130,6 +130,18 @@ describe('edge', () => {
130
130
  expect(ip.limit).toHaveBeenCalledWith({ key: '2001:db8:abcd:12' })
131
131
  })
132
132
 
133
+ test('normalizes equivalent IPv6 addresses to the same /64 prefix', () => {
134
+ expect([
135
+ RateLimit.normalizeIp('2001:0db8:abcd:0012::1'),
136
+ RateLimit.normalizeIp('2001:db8:abcd:12:ffff::2'),
137
+ ]).toMatchInlineSnapshot(`
138
+ [
139
+ "2001:db8:abcd:12",
140
+ "2001:db8:abcd:12",
141
+ ]
142
+ `)
143
+ })
144
+
133
145
  test('returns 429 when any layer denies', async () => {
134
146
  const response = await RateLimit.edge(request({ asn: 64512, ip: '203.0.113.7' }), {
135
147
  asn: binding(false),
@@ -139,12 +139,12 @@ type CfProperties = { asn?: number | undefined }
139
139
  function clientKey(request: Request): string {
140
140
  const ip = request.headers.get('cf-connecting-ip')
141
141
  if (!ip) return 'unknown'
142
- if (ip.includes('.')) return ip
143
- return ipv6Prefix(ip)
142
+ return normalizeIp(ip)
144
143
  }
145
144
 
146
- /** First four hextets of an IPv6 address, with `::` expanded. */
147
- function ipv6Prefix(ip: string): string {
145
+ /** Normalizes an IPv4 address or IPv6 /64 prefix for per-client quotas. */
146
+ export function normalizeIp(ip: string): string {
147
+ if (ip.includes('.')) return ip
148
148
  const [address = ''] = ip.split('%')
149
149
  const [head = '', tail = ''] = address.split('::')
150
150
  const left = head ? head.split(':') : []
@@ -203,6 +203,21 @@ describe('resolve', () => {
203
203
  `)
204
204
  })
205
205
 
206
+ test('resolves Bearer auth', () => {
207
+ expect(
208
+ Tidx.resolve(
209
+ { baseUrl: 'https://internal.indexer', bearerAuth: 'zone-secret' },
210
+ { chainId: 4217, principal: apiKeyPrincipal },
211
+ ),
212
+ ).toMatchInlineSnapshot(`
213
+ {
214
+ "baseUrl": "https://internal.indexer",
215
+ "basicAuth": undefined,
216
+ "bearerAuth": "zone-secret",
217
+ }
218
+ `)
219
+ })
220
+
206
221
  test('the resolver form routes anonymous callers to a separate upstream', () => {
207
222
  const tidx = ({ principal }: Viem.resolve.Context) =>
208
223
  principal?.type === 'public'
@@ -243,6 +258,28 @@ describe('resolve', () => {
243
258
  })
244
259
  })
245
260
 
261
+ describe('getClient', () => {
262
+ test('sends Bearer auth', async () => {
263
+ const fetch = vi.spyOn(globalThis, 'fetch').mockResolvedValue(
264
+ new Response(JSON.stringify({ columns: [], ok: true, row_count: 0, rows: [] }), {
265
+ headers: { 'content-type': 'application/json' },
266
+ }),
267
+ )
268
+ try {
269
+ const client = Tidx.getClient({
270
+ chainId: 4217,
271
+ tidx: { baseUrl: 'https://internal.indexer', bearerAuth: 'zone-secret' },
272
+ })
273
+ await client.fetch({ query: 'select 1' })
274
+
275
+ const upstream = fetch.mock.calls[0]![0] as Request
276
+ expect(upstream.headers.get('authorization')).toBe('Bearer zone-secret')
277
+ } finally {
278
+ fetch.mockRestore()
279
+ }
280
+ })
281
+ })
282
+
246
283
  describe('createGetClient', () => {
247
284
  test('memoizes one client per resolved upstream', () => {
248
285
  const getTidx = Tidx.createGetClient({
@@ -267,6 +304,17 @@ describe('createGetClient', () => {
267
304
  expect(getTidx(4217, null)).toBe(getTidx(4217, publicPrincipal))
268
305
  })
269
306
 
307
+ test('does not share clients across Bearer credentials', () => {
308
+ const getTidx = Tidx.createGetClient({
309
+ tidx: ({ principal }) => ({
310
+ baseUrl: 'https://internal.indexer',
311
+ bearerAuth: principal?.type === 'public' ? 'public-secret' : 'private-secret',
312
+ }),
313
+ })
314
+
315
+ expect(getTidx(4217, publicPrincipal)).not.toBe(getTidx(4217, apiKeyPrincipal))
316
+ })
317
+
270
318
  test('consults the resolver on every call but builds one client per upstream', () => {
271
319
  const seen: (string | undefined)[] = []
272
320
  const getTidx = Tidx.createGetClient({
@@ -29,8 +29,8 @@ export type UrlResolver = (chainId: Viem.ChainId) => string | undefined
29
29
  * receives a {@link Viem.resolve.Context} (`chainId` plus the request
30
30
  * `principal`), so callers can route traffic per caller — e.g. send anonymous
31
31
  * (`principal.type === 'public'`) traffic to a separate, no-auth indexer. A
32
- * missing `baseUrl` falls back to the built-in public host map ({@link url}); a
33
- * missing `basicAuth` means no Basic auth is sent.
32
+ * missing `baseUrl` falls back to the built-in public host map ({@link url});
33
+ * missing auth fields send no Authorization header.
34
34
  */
35
35
  export function resolve(
36
36
  tidx: getClient.Tidx | undefined,
@@ -42,6 +42,7 @@ export function resolve(
42
42
  (typeof config?.baseUrl === 'function' ? config.baseUrl(context.chainId) : config?.baseUrl) ??
43
43
  url[context.chainId],
44
44
  basicAuth: config?.basicAuth,
45
+ ...(config?.bearerAuth ? { bearerAuth: config.bearerAuth } : {}),
45
46
  }
46
47
  }
47
48
 
@@ -56,13 +57,22 @@ export declare namespace resolve {
56
57
  baseUrl: string | undefined
57
58
  /** Basic auth credentials, or undefined to send no Basic auth. */
58
59
  basicAuth?: string | undefined
60
+ /** Bearer auth credential, or undefined to send no Bearer auth. */
61
+ bearerAuth?: string | undefined
59
62
  }
60
63
  }
61
64
 
62
65
  export function getClient(options: getClient.Options): Client {
63
66
  const { chainId = Viem.defaultChainId, principal = null, tidx } = options
64
- const { baseUrl, basicAuth } = resolve(tidx, { chainId, principal })
65
- return withTransientRetry(Tidx.create({ baseUrl, basicAuth, chainId }))
67
+ const { baseUrl, basicAuth, bearerAuth } = resolve(tidx, { chainId, principal })
68
+ return withTransientRetry(
69
+ Tidx.create({
70
+ baseUrl,
71
+ basicAuth,
72
+ bearerAuth,
73
+ chainId,
74
+ }),
75
+ )
66
76
  }
67
77
 
68
78
  /**
@@ -72,7 +82,7 @@ export function getClient(options: getClient.Options): Client {
72
82
  * output (`baseUrl` + `basicAuth` + `chainId`), not the principal, so a resolver
73
83
  * `tidx` may branch on any principal field while client cardinality stays
74
84
  * bounded by the number of distinct upstreams — callers that resolve to the same
75
- * upstream share a client. Shared by `App.create` (per-request reads) and
85
+ * upstream share a client. Shared by `data()` (per-request reads) and
76
86
  * `Webhooks.createPoller` (the scheduled poller) so the resolver logic lives in
77
87
  * one place.
78
88
  */
@@ -83,8 +93,8 @@ export function createGetClient(options: createGetClient.Options = {}): GetClien
83
93
  return (chainId = defaultChainId, principal = null) => {
84
94
  // Resolve to derive the cache key; `getClient` re-resolves to build the
85
95
  // client on a miss. `resolve` is pure and cheap, so the extra call is free.
86
- const { baseUrl, basicAuth } = resolve(tidx, { chainId, principal })
87
- const cacheKey = `${baseUrl}|${basicAuth ?? ''}|${chainId}`
96
+ const { baseUrl, basicAuth, bearerAuth } = resolve(tidx, { chainId, principal })
97
+ const cacheKey = `${baseUrl}|${basicAuth ?? ''}|${bearerAuth ?? ''}|${chainId}`
88
98
  const existing = clients.get(cacheKey)
89
99
  if (existing) return existing
90
100
 
@@ -218,12 +228,16 @@ export declare namespace getClient {
218
228
  baseUrl?: UrlResolver | string | undefined
219
229
  /** Basic auth credentials for TIDX requests. */
220
230
  basicAuth?: string | undefined
231
+ /** Bearer auth credential for TIDX requests. */
232
+ bearerAuth?: string | undefined
221
233
  }
222
234
  | ((context: Viem.resolve.Context) => {
223
235
  /** TIDX base URL to use; falls back to the built-in public host when omitted. */
224
236
  baseUrl?: string | undefined
225
237
  /** Basic auth credentials for TIDX requests, or undefined for none. */
226
238
  basicAuth?: string | undefined
239
+ /** Bearer auth credential for TIDX requests, or undefined for none. */
240
+ bearerAuth?: string | undefined
227
241
  })
228
242
  }
229
243
 
@@ -113,3 +113,49 @@ describe('resolve', () => {
113
113
  `)
114
114
  })
115
115
  })
116
+
117
+ describe('createGetClient', () => {
118
+ test('memoizes one client per resolved upstream', () => {
119
+ const getClient = Viem.createGetClient({
120
+ rpc: ({ principal }) =>
121
+ principal?.type === 'public'
122
+ ? { url: 'https://rpc.tempo.xyz' }
123
+ : { basicAuth: 'user:pass', url: 'https://internal.rpc' },
124
+ })
125
+
126
+ expect(getClient(4217, apiKeyPrincipal)).toBe(getClient(4217, apiKeyPrincipal))
127
+ expect(getClient(4217, publicPrincipal)).toBe(getClient(4217, publicPrincipal))
128
+ expect(getClient(4217, publicPrincipal)).not.toBe(getClient(4217, apiKeyPrincipal))
129
+ expect(getClient(4217, apiKeyPrincipal)).not.toBe(getClient(42431, apiKeyPrincipal))
130
+ })
131
+
132
+ test('shares a client across principals when they resolve identically', () => {
133
+ const getClient = Viem.createGetClient()
134
+
135
+ expect(getClient(4217, publicPrincipal)).toBe(getClient(4217, apiKeyPrincipal))
136
+ expect(getClient(4217, null)).toBe(getClient(4217, publicPrincipal))
137
+ })
138
+
139
+ test('consults the resolver on cache hits', () => {
140
+ const seen: (string | undefined)[] = []
141
+ const getClient = Viem.createGetClient({
142
+ rpc: ({ principal }) => {
143
+ seen.push(principal?.type)
144
+ return { url: 'https://internal.rpc' }
145
+ },
146
+ })
147
+
148
+ const first = getClient(4217, apiKeyPrincipal)
149
+ const second = getClient(4217, apiKeyPrincipal)
150
+ getClient(4217, publicPrincipal)
151
+
152
+ expect(seen).toMatchInlineSnapshot(`
153
+ [
154
+ "api_key",
155
+ "api_key",
156
+ "public",
157
+ ]
158
+ `)
159
+ expect(first).toBe(second)
160
+ })
161
+ })
@@ -58,7 +58,7 @@ export type UrlResolver = (chainId: ChainId) => string | undefined
58
58
  * webhook poller). A missing `url` falls back to the built-in public host map
59
59
  * ({@link url}); a missing `basicAuth` means no Basic auth is sent.
60
60
  *
61
- * Memoization is keyed on the resolved output (see `App.create` and
61
+ * Memoization is keyed on the resolved output (see {@link createGetClient} and
62
62
  * {@link Tidx.createGetClient}), not the context, so a resolver may branch on
63
63
  * any principal field without unbounded client cardinality: callers that
64
64
  * resolve to the same upstream share a client. Pass the result to
@@ -130,6 +130,39 @@ export function getClient(options: getClient.Options): getClient.ReturnType {
130
130
  .extend(tempoActions())
131
131
  }
132
132
 
133
+ /** Builds a memoized Tempo client resolver. */
134
+ export function createGetClient(options: createGetClient.Options = {}): createGetClient.ReturnType {
135
+ const { defaultChainId: defaultChainId_option = defaultChainId, rpc } = options
136
+ const clients = new Map<string, getClient.ReturnType>()
137
+
138
+ return (chainId = defaultChainId_option, principal = null) => {
139
+ const { basicAuth, url } = resolve(rpc, { chainId, principal })
140
+ const cacheKey = JSON.stringify([url, basicAuth, chainId])
141
+ const existing = clients.get(cacheKey)
142
+ if (existing) return existing
143
+
144
+ const client = getClient({
145
+ chainId,
146
+ rpc: { basicAuth, url: () => url },
147
+ })
148
+ clients.set(cacheKey, client)
149
+ return client
150
+ }
151
+ }
152
+
153
+ export declare namespace createGetClient {
154
+ /** Options for building a memoized Tempo client resolver. */
155
+ type Options = {
156
+ /** Tempo chain id used when a caller omits one. */
157
+ defaultChainId?: ChainId | undefined
158
+ /** Tempo RPC options applied to every constructed client. */
159
+ rpc?: getClient.Rpc | undefined
160
+ }
161
+
162
+ /** Memoized Tempo client resolver. */
163
+ type ReturnType = (chainId?: ChainId, principal?: Auth.Principal | null) => getClient.ReturnType
164
+ }
165
+
133
166
  export declare namespace getClient {
134
167
  /** Tempo viem client with public and Tempo action helpers. */
135
168
  type ReturnType = core_Client<