tapimo 0.0.2 → 0.1.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 (265) hide show
  1. package/README.md +11 -2
  2. package/dist/ApiKey.d.ts +12 -11
  3. package/dist/ApiKey.d.ts.map +1 -1
  4. package/dist/ApiKey.js +10 -12
  5. package/dist/ApiKey.js.map +1 -1
  6. package/dist/ApiKeys.d.ts +139 -0
  7. package/dist/ApiKeys.d.ts.map +1 -0
  8. package/dist/ApiKeys.js +166 -0
  9. package/dist/ApiKeys.js.map +1 -0
  10. package/dist/App.d.ts +920 -910
  11. package/dist/App.d.ts.map +1 -1
  12. package/dist/App.js +36 -12
  13. package/dist/App.js.map +1 -1
  14. package/dist/Handler.d.ts +86 -0
  15. package/dist/Handler.d.ts.map +1 -0
  16. package/dist/Handler.js +83 -0
  17. package/dist/Handler.js.map +1 -0
  18. package/dist/Scope.d.ts +4 -1
  19. package/dist/Scope.d.ts.map +1 -1
  20. package/dist/Scope.js +5 -1
  21. package/dist/Scope.js.map +1 -1
  22. package/dist/Webhooks.d.ts +3 -5
  23. package/dist/Webhooks.d.ts.map +1 -1
  24. package/dist/Webhooks.js +9 -2
  25. package/dist/Webhooks.js.map +1 -1
  26. package/dist/admin/Access.d.ts +1 -3
  27. package/dist/admin/Access.d.ts.map +1 -1
  28. package/dist/admin/Access.js +1 -3
  29. package/dist/admin/Access.js.map +1 -1
  30. package/dist/admin/App.d.ts +98 -209
  31. package/dist/admin/App.d.ts.map +1 -1
  32. package/dist/admin/App.js +2 -4
  33. package/dist/admin/App.js.map +1 -1
  34. package/dist/admin/apps/api-keys.d.ts +7 -5
  35. package/dist/admin/apps/api-keys.d.ts.map +1 -1
  36. package/dist/admin/apps/api-keys.js +8 -9
  37. package/dist/admin/apps/api-keys.js.map +1 -1
  38. package/dist/admin/apps/scopes.d.ts +3 -0
  39. package/dist/admin/apps/scopes.d.ts.map +1 -1
  40. package/dist/admin/apps/verified-tokens.d.ts +101 -215
  41. package/dist/admin/apps/verified-tokens.d.ts.map +1 -1
  42. package/dist/admin/apps/verified-tokens.js +17 -38
  43. package/dist/admin/apps/verified-tokens.js.map +1 -1
  44. package/dist/admin/ui.generated.d.ts +1 -1
  45. package/dist/admin/ui.generated.js +1 -1
  46. package/dist/apps/balances.d.ts +2 -2
  47. package/dist/apps/receipts.d.ts +43 -43
  48. package/dist/apps/tokens.d.ts +454 -454
  49. package/dist/apps/transactions.d.ts +43 -43
  50. package/dist/apps/transfers.d.ts +29 -29
  51. package/dist/apps/webhooks.d.ts +5 -2
  52. package/dist/apps/webhooks.d.ts.map +1 -1
  53. package/dist/apps/webhooks.js +38 -34
  54. package/dist/apps/webhooks.js.map +1 -1
  55. package/dist/{Cli.d.ts → cli/Cli.d.ts} +13 -2
  56. package/dist/cli/Cli.d.ts.map +1 -0
  57. package/dist/cli/Cli.js +19 -0
  58. package/dist/cli/Cli.js.map +1 -0
  59. package/dist/cli/bin.d.ts +3 -0
  60. package/dist/cli/bin.d.ts.map +1 -0
  61. package/dist/cli/bin.js +4 -0
  62. package/dist/cli/bin.js.map +1 -0
  63. package/dist/cli/commands/admin.d.ts +15 -0
  64. package/dist/cli/commands/admin.d.ts.map +1 -0
  65. package/dist/cli/commands/admin.js +60 -0
  66. package/dist/cli/commands/admin.js.map +1 -0
  67. package/dist/cloudflare.d.ts +7 -0
  68. package/dist/cloudflare.d.ts.map +1 -1
  69. package/dist/cloudflare.js +9 -0
  70. package/dist/cloudflare.js.map +1 -1
  71. package/dist/db/Db.d.ts +103 -0
  72. package/dist/db/Db.d.ts.map +1 -0
  73. package/dist/db/Db.js +274 -0
  74. package/dist/db/Db.js.map +1 -0
  75. package/dist/db/Schema.d.ts +88 -0
  76. package/dist/db/Schema.d.ts.map +1 -0
  77. package/dist/db/Schema.js +130 -0
  78. package/dist/db/Schema.js.map +1 -0
  79. package/dist/db/tables/organizations.d.ts +79 -0
  80. package/dist/db/tables/organizations.d.ts.map +1 -0
  81. package/dist/db/tables/organizations.js +81 -0
  82. package/dist/db/tables/organizations.js.map +1 -0
  83. package/dist/db/tables/verifiedTokens.d.ts +53 -0
  84. package/dist/db/tables/verifiedTokens.d.ts.map +1 -0
  85. package/dist/db/tables/verifiedTokens.js +74 -0
  86. package/dist/db/tables/verifiedTokens.js.map +1 -0
  87. package/dist/db/tables/webhookDeliveries.d.ts +71 -0
  88. package/dist/db/tables/webhookDeliveries.d.ts.map +1 -0
  89. package/dist/db/tables/webhookDeliveries.js +119 -0
  90. package/dist/db/tables/webhookDeliveries.js.map +1 -0
  91. package/dist/db/tables/webhookSubscriptions.d.ts +158 -0
  92. package/dist/db/tables/webhookSubscriptions.d.ts.map +1 -0
  93. package/dist/db/tables/webhookSubscriptions.js +272 -0
  94. package/dist/db/tables/webhookSubscriptions.js.map +1 -0
  95. package/dist/handlers/internal/executionError.d.ts +25 -0
  96. package/dist/handlers/internal/executionError.d.ts.map +1 -0
  97. package/dist/handlers/internal/executionError.js +184 -0
  98. package/dist/handlers/internal/executionError.js.map +1 -0
  99. package/dist/handlers/internal/multisig.d.ts +237 -0
  100. package/dist/handlers/internal/multisig.d.ts.map +1 -0
  101. package/dist/handlers/internal/multisig.js +567 -0
  102. package/dist/handlers/internal/multisig.js.map +1 -0
  103. package/dist/handlers/internal/requestListener.d.ts +124 -0
  104. package/dist/handlers/internal/requestListener.d.ts.map +1 -0
  105. package/dist/handlers/internal/requestListener.js +173 -0
  106. package/dist/handlers/internal/requestListener.js.map +1 -0
  107. package/dist/handlers/internal/sponsorship.d.ts +83 -0
  108. package/dist/handlers/internal/sponsorship.d.ts.map +1 -0
  109. package/dist/handlers/internal/sponsorship.js +103 -0
  110. package/dist/handlers/internal/sponsorship.js.map +1 -0
  111. package/dist/handlers/internal/types.d.ts +284 -0
  112. package/dist/handlers/internal/types.d.ts.map +1 -0
  113. package/dist/handlers/internal/types.js +2 -0
  114. package/dist/handlers/internal/types.js.map +1 -0
  115. package/dist/handlers/internal/utils.d.ts +34 -0
  116. package/dist/handlers/internal/utils.d.ts.map +1 -0
  117. package/dist/handlers/internal/utils.js +131 -0
  118. package/dist/handlers/internal/utils.js.map +1 -0
  119. package/dist/handlers/relay.d.ts +119 -0
  120. package/dist/handlers/relay.d.ts.map +1 -0
  121. package/dist/handlers/relay.js +1264 -0
  122. package/dist/handlers/relay.js.map +1 -0
  123. package/dist/index.d.ts +4 -2
  124. package/dist/index.d.ts.map +1 -1
  125. package/dist/index.js +4 -2
  126. package/dist/index.js.map +1 -1
  127. package/dist/internal/Auth.d.ts +16 -10
  128. package/dist/internal/Auth.d.ts.map +1 -1
  129. package/dist/internal/Auth.js +72 -12
  130. package/dist/internal/Auth.js.map +1 -1
  131. package/dist/internal/RpcSchema.d.ts +10 -0
  132. package/dist/internal/RpcSchema.d.ts.map +1 -0
  133. package/dist/internal/RpcSchema.js +2 -0
  134. package/dist/internal/RpcSchema.js.map +1 -0
  135. package/dist/internal/Store.d.ts +42 -3
  136. package/dist/internal/Store.d.ts.map +1 -1
  137. package/dist/internal/Store.js +65 -3
  138. package/dist/internal/Store.js.map +1 -1
  139. package/dist/internal/VerifiedTokens.d.ts +15 -34
  140. package/dist/internal/VerifiedTokens.d.ts.map +1 -1
  141. package/dist/internal/VerifiedTokens.js +115 -159
  142. package/dist/internal/VerifiedTokens.js.map +1 -1
  143. package/dist/internal/Webhooks.d.ts +49 -35
  144. package/dist/internal/Webhooks.d.ts.map +1 -1
  145. package/dist/internal/Webhooks.js +92 -205
  146. package/dist/internal/Webhooks.js.map +1 -1
  147. package/package.json +7 -2
  148. package/src/ApiKey.test.ts +3 -3
  149. package/src/ApiKey.ts +10 -12
  150. package/src/ApiKeys.test.ts +175 -0
  151. package/src/ApiKeys.ts +205 -0
  152. package/src/App.test-d.ts +1 -1
  153. package/src/App.test.ts +10768 -351
  154. package/src/App.ts +79 -36
  155. package/src/Client.test-d.ts +1 -1
  156. package/src/Handler.ts +154 -0
  157. package/src/Scope.ts +5 -1
  158. package/src/Webhooks.test.ts +8 -7
  159. package/src/Webhooks.ts +11 -5
  160. package/src/admin/Access.ts +1 -3
  161. package/src/admin/App.ts +19 -24
  162. package/src/admin/apps/api-keys.test.ts +19 -18
  163. package/src/admin/apps/api-keys.ts +8 -9
  164. package/src/admin/apps/scopes.test.ts +4 -0
  165. package/src/admin/apps/verified-tokens.test.ts +3 -30
  166. package/src/admin/apps/verified-tokens.ts +17 -36
  167. package/src/admin/ui.generated.ts +1 -1
  168. package/src/apps/balances.test.ts +4 -3
  169. package/src/apps/blocks.test.ts +1 -1
  170. package/src/apps/exchanges.test.ts +1 -1
  171. package/src/apps/indexer.test.ts +1 -1
  172. package/src/apps/receipts.test.ts +1 -1
  173. package/src/apps/rpc.test.ts +100 -10
  174. package/src/apps/tokenlist.test.ts +5 -7
  175. package/src/apps/tokens.test.ts +4 -3
  176. package/src/apps/transactions.test.ts +1 -1
  177. package/src/apps/transfers.test.ts +1 -1
  178. package/src/apps/verified-tokens.test.ts +24 -12
  179. package/src/apps/webhooks.test.ts +130 -143
  180. package/src/apps/webhooks.ts +45 -47
  181. package/src/{Cli.test.ts → cli/Cli.test.ts} +10 -9
  182. package/src/{Cli.ts → cli/Cli.ts} +16 -9
  183. package/src/cli/bin.ts +4 -0
  184. package/src/cli/commands/admin.test.ts +71 -0
  185. package/src/cli/commands/admin.ts +60 -0
  186. package/src/cloudflare.ts +10 -0
  187. package/src/db/Db.test.ts +84 -0
  188. package/src/db/Db.ts +383 -0
  189. package/src/db/Schema.ts +164 -0
  190. package/src/db/tables/organizations.test.ts +124 -0
  191. package/src/db/tables/organizations.ts +127 -0
  192. package/src/db/tables/verifiedTokens.ts +111 -0
  193. package/src/db/tables/webhookDeliveries.ts +159 -0
  194. package/src/db/tables/webhookSubscriptions.ts +358 -0
  195. package/src/handlers/internal/executionError.ts +205 -0
  196. package/src/handlers/internal/multisig.localnet.test.ts +146 -0
  197. package/src/handlers/internal/multisig.test.ts +744 -0
  198. package/src/handlers/internal/multisig.ts +858 -0
  199. package/src/handlers/internal/relay.localnet.test.ts +2035 -0
  200. package/src/handlers/internal/requestListener.ts +273 -0
  201. package/src/handlers/internal/sponsorship.ts +186 -0
  202. package/src/handlers/internal/types.ts +378 -0
  203. package/src/handlers/internal/utils.ts +153 -0
  204. package/src/handlers/relay.ts +1605 -0
  205. package/src/index.ts +4 -2
  206. package/src/internal/Auth.test.ts +21 -21
  207. package/src/internal/Auth.ts +92 -19
  208. package/src/internal/RateLimit.test.ts +1 -1
  209. package/src/internal/RpcSchema.ts +10 -0
  210. package/src/internal/Store.test.ts +98 -10
  211. package/src/internal/Store.ts +112 -6
  212. package/src/internal/VerifiedTokens.test.ts +99 -115
  213. package/src/internal/VerifiedTokens.ts +123 -175
  214. package/src/internal/Webhooks.test.ts +107 -107
  215. package/src/internal/Webhooks.ts +102 -220
  216. package/dist/ApiKeySource.d.ts +0 -166
  217. package/dist/ApiKeySource.d.ts.map +0 -1
  218. package/dist/ApiKeySource.js +0 -234
  219. package/dist/ApiKeySource.js.map +0 -1
  220. package/dist/Cli.d.ts.map +0 -1
  221. package/dist/Cli.js +0 -13
  222. package/dist/Cli.js.map +0 -1
  223. package/seeds/31318/icons/0x20c0000000000000000000000000000000000000.svg +0 -1
  224. package/seeds/31318/icons/0x20c0000000000000000000000000000000000001.svg +0 -1
  225. package/seeds/31318/icons/0x20c0000000000000000000000000000000000002.svg +0 -1
  226. package/seeds/31318/icons/0x20c0000000000000000000000000000000000003.svg +0 -1
  227. package/seeds/31318/verified-tokens.json +0 -30
  228. package/seeds/4217/icons/0x20c0000000000000000000000000000000000000.svg +0 -25
  229. package/seeds/4217/icons/0x20c000000000000000000000048c8f36df1c9a4a.svg +0 -1
  230. package/seeds/4217/icons/0x20c0000000000000000000000520792dcccccccc.svg +0 -1
  231. package/seeds/4217/icons/0x20c0000000000000000000000a6da882d075a4c3.svg +0 -4
  232. package/seeds/4217/icons/0x20c000000000000000000000111111111e910f0f.svg +0 -30
  233. package/seeds/4217/icons/0x20c00000000000000000000014f22ca97301eb73.svg +0 -1
  234. package/seeds/4217/icons/0x20c0000000000000000000001621e21f71cf12fb.svg +0 -1
  235. package/seeds/4217/icons/0x20c0000000000000000000002f52d5cc21a3207b.svg +0 -1
  236. package/seeds/4217/icons/0x20c0000000000000000000003158081efd85bfc2.svg +0 -11
  237. package/seeds/4217/icons/0x20c0000000000000000000003554d28269e0f3c2.svg +0 -1
  238. package/seeds/4217/icons/0x20c000000000000000000000383a23bacb546ab9.svg +0 -1
  239. package/seeds/4217/icons/0x20c00000000000000000000042109aef2f8b28e1.png +0 -0
  240. package/seeds/4217/icons/0x20c0000000000000000000005c0bac7cef389a11.svg +0 -1
  241. package/seeds/4217/icons/0x20c0000000000000000000006fd9a167923ba194.png +0 -0
  242. package/seeds/4217/icons/0x20c0000000000000000000007f7ba549dd0251b9.svg +0 -1
  243. package/seeds/4217/icons/0x20c0000000000000000000008191667423f70e67.svg +0 -5
  244. package/seeds/4217/icons/0x20c0000000000000000000008ee4fcff88888888.svg +0 -1
  245. package/seeds/4217/icons/0x20c0000000000000000000009a4a4b17e0dc6651.svg +0 -1
  246. package/seeds/4217/icons/0x20c000000000000000000000ab02d39df30bd17e.svg +0 -1
  247. package/seeds/4217/icons/0x20c000000000000000000000ae247a1130450f09.svg +0 -1
  248. package/seeds/4217/icons/0x20c000000000000000000000aeed2ec36a54d0e5.svg +0 -1
  249. package/seeds/4217/icons/0x20c000000000000000000000b9537d11c60e8b50.svg +0 -1
  250. package/seeds/4217/icons/0x20c000000000000000000000bd95bfb69fbe6ce3.svg +0 -1
  251. package/seeds/4217/icons/0x20c000000000000000000000c412ec89d0c08be5.svg +0 -1
  252. package/seeds/4217/icons/0x20c000000000000000000000f047dd7018e50367.png +0 -0
  253. package/seeds/4217/verified-tokens.json +0 -177
  254. package/seeds/42431/icons/0x20c0000000000000000000000000000000000000.svg +0 -25
  255. package/seeds/42431/icons/0x20c0000000000000000000000000000000000001.svg +0 -1
  256. package/seeds/42431/icons/0x20c0000000000000000000000000000000000002.svg +0 -1
  257. package/seeds/42431/icons/0x20c0000000000000000000000000000000000003.svg +0 -1
  258. package/seeds/42431/icons/0x20c0000000000000000000009e8d7eb59b783726.svg +0 -1
  259. package/seeds/42431/icons/0x20c000000000000000000000d72572838bbee59c.svg +0 -1
  260. package/seeds/42431/verified-tokens.json +0 -44
  261. package/seeds/openrpc/README.md +0 -27
  262. package/seeds/openrpc/execution-apis.json +0 -8989
  263. package/src/ApiKeySource.test-d.ts +0 -11
  264. package/src/ApiKeySource.test.ts +0 -295
  265. package/src/ApiKeySource.ts +0 -303
@@ -0,0 +1,127 @@
1
+ import type { Selectable } from 'kysely'
2
+ import { nanoid } from 'nanoid'
3
+
4
+ import type * as Db from '../Db.js'
5
+ import type * as db_Schema from '../Schema.js'
6
+
7
+ /** Columns of the `organizations` table, derived from `Schema.Organization`. */
8
+ export type Table = db_Schema.Organization
9
+
10
+ /** A stored organization row. */
11
+ export type Record = Selectable<Table>
12
+
13
+ /**
14
+ * Inserts an organization and returns the stored record. The `id` defaults to
15
+ * a generated `org_…` handle when omitted.
16
+ *
17
+ * @param db - The database.
18
+ * @param input - The organization to insert.
19
+ * @returns The stored record.
20
+ */
21
+ export function create(db: Db.Db, input: create.Input): Promise<Record> {
22
+ const now = new Date().toISOString()
23
+ return db.kysely
24
+ .insertInto('organizations')
25
+ .values({
26
+ createdAt: now,
27
+ createdBy: input.createdBy ?? null,
28
+ id: input.id ?? `org_${nanoid()}`,
29
+ name: input.name,
30
+ updatedAt: now,
31
+ })
32
+ .returningAll()
33
+ .executeTakeFirstOrThrow()
34
+ }
35
+
36
+ export declare namespace create {
37
+ /** Fields accepted when inserting an organization. */
38
+ type Input = {
39
+ /** Identity creating the organization (e.g. admin email), recorded for audit. */
40
+ createdBy?: string | undefined
41
+ /** Explicit id; a generated `org_…` handle is used when omitted. */
42
+ id?: string | undefined
43
+ /** Human-readable organization name. */
44
+ name: string
45
+ }
46
+ }
47
+
48
+ /**
49
+ * Reads an organization by id.
50
+ *
51
+ * @param db - The database.
52
+ * @param id - The organization id (`org_…`).
53
+ * @returns The record, or `undefined` when absent.
54
+ */
55
+ export function get(db: Db.Db, id: string): Promise<Record | undefined> {
56
+ return db.kysely.selectFrom('organizations').selectAll().where('id', '=', id).executeTakeFirst()
57
+ }
58
+
59
+ /**
60
+ * Lists organizations, newest first.
61
+ *
62
+ * @param db - The database.
63
+ * @returns The records.
64
+ */
65
+ export function list(db: Db.Db): Promise<Record[]> {
66
+ return db.kysely.selectFrom('organizations').selectAll().orderBy('createdAt', 'desc').execute()
67
+ }
68
+
69
+ /**
70
+ * Renames an organization, bumping `updatedAt`.
71
+ *
72
+ * @param db - The database.
73
+ * @param id - The organization id (`org_…`).
74
+ * @param input - The fields to update.
75
+ * @returns The updated record, or `undefined` when absent.
76
+ */
77
+ export function update(db: Db.Db, id: string, input: update.Input): Promise<Record | undefined> {
78
+ return db.kysely
79
+ .updateTable('organizations')
80
+ .set({ name: input.name, updatedAt: new Date().toISOString() })
81
+ .where('id', '=', id)
82
+ .returningAll()
83
+ .executeTakeFirst()
84
+ }
85
+
86
+ export declare namespace update {
87
+ /** Mutable organization fields. */
88
+ type Input = {
89
+ /** New human-readable organization name. */
90
+ name: string
91
+ }
92
+ }
93
+
94
+ /**
95
+ * Inserts an organization when absent (`ON CONFLICT DO NOTHING`) — the
96
+ * mint/backfill path, where a key's `orgId` must resolve to a real row. The
97
+ * `name` defaults to the id for degenerate orgs created by the mint fallback.
98
+ *
99
+ * @param db - The database.
100
+ * @param input - The organization to ensure.
101
+ */
102
+ export async function upsert(db: Db.Db, input: upsert.Input): Promise<void> {
103
+ const now = new Date().toISOString()
104
+ await db.kysely
105
+ .insertInto('organizations')
106
+ .values({
107
+ createdAt: now,
108
+ createdBy: input.createdBy ?? null,
109
+ id: input.id,
110
+ name: input.name ?? input.id,
111
+ updatedAt: now,
112
+ })
113
+ .onConflict((oc) => oc.column('id').doNothing())
114
+ .execute()
115
+ }
116
+
117
+ export declare namespace upsert {
118
+ /** Fields accepted when ensuring an organization exists. */
119
+ type Input = {
120
+ /** Identity creating the organization, recorded for audit. */
121
+ createdBy?: string | undefined
122
+ /** The organization id (`org_…`, or a mint-fallback key id). */
123
+ id: string
124
+ /** Human-readable organization name; defaults to the id. */
125
+ name?: string | undefined
126
+ }
127
+ }
@@ -0,0 +1,111 @@
1
+ import type { Selectable } from 'kysely'
2
+ import type { Hex } from 'ox'
3
+
4
+ import type * as VerifiedTokens from '../../internal/VerifiedTokens.js'
5
+ import type * as Db from '../Db.js'
6
+ import type * as db_Schema from '../Schema.js'
7
+
8
+ /** Columns of the `verified_tokens` table, derived from `Schema.VerifiedToken`. */
9
+ export type Table = db_Schema.VerifiedToken
10
+
11
+ /** Columns of the `verified_token_lists` table, derived from `Schema.VerifiedTokenList`. */
12
+ export type ListTable = db_Schema.VerifiedTokenList
13
+
14
+ /** A stored verified-token row. */
15
+ export type Row = Selectable<Table>
16
+
17
+ /** A chain's list-level metadata. */
18
+ export type Meta = Selectable<ListTable>
19
+
20
+ /**
21
+ * Reads a chain's list metadata — the "head" the soft-refresh cache compares
22
+ * versions against.
23
+ *
24
+ * @param db - The database.
25
+ * @param chainId - The chain id.
26
+ * @returns The metadata, or `undefined` when the chain has no list yet.
27
+ */
28
+ export function head(db: Db.Db, chainId: number): Promise<Meta | undefined> {
29
+ return db.kysely
30
+ .selectFrom('verified_token_lists')
31
+ .selectAll()
32
+ .where('chainId', '=', chainId)
33
+ .executeTakeFirst()
34
+ }
35
+
36
+ /**
37
+ * Reads a chain's tokens in canonical list order.
38
+ *
39
+ * @param db - The database.
40
+ * @param chainId - The chain id.
41
+ * @returns The rows.
42
+ */
43
+ export function list(db: Db.Db, chainId: number): Promise<Row[]> {
44
+ return db.kysely
45
+ .selectFrom('verified_tokens')
46
+ .selectAll()
47
+ .where('chainId', '=', chainId)
48
+ .orderBy('position')
49
+ .execute()
50
+ }
51
+
52
+ /**
53
+ * Publishes a chain's whole list: replaces its token rows (positions rewritten
54
+ * wholesale) and bumps the list metadata. Callers run this inside the
55
+ * per-chain publish lock/transaction, so the rewrite is atomic.
56
+ *
57
+ * @param db - The database (a transaction-scoped handle).
58
+ * @param chainId - The chain id.
59
+ * @param input - The validated list and its new version metadata.
60
+ */
61
+ export async function publish(db: Db.Db, chainId: number, input: publish.Input): Promise<void> {
62
+ await db.kysely.deleteFrom('verified_tokens').where('chainId', '=', chainId).execute()
63
+ if (input.tokens.length > 0)
64
+ await db.kysely
65
+ .insertInto('verified_tokens')
66
+ .values(
67
+ input.tokens.map((token, position) => ({
68
+ address: token.address,
69
+ chainId,
70
+ currency: token.currency,
71
+ decimals: token.decimals,
72
+ logoUri: token.logoUri ?? null,
73
+ name: token.name,
74
+ position,
75
+ symbol: token.symbol,
76
+ })),
77
+ )
78
+ .execute()
79
+ await db.kysely
80
+ .insertInto('verified_token_lists')
81
+ .values({ chainId, updatedAt: input.updatedAt, version: input.version })
82
+ .onConflict((oc) =>
83
+ oc.column('chainId').doUpdateSet({ updatedAt: input.updatedAt, version: input.version }),
84
+ )
85
+ .execute()
86
+ }
87
+
88
+ export declare namespace publish {
89
+ /** A validated list to publish. */
90
+ type Input = {
91
+ /** Tokens in canonical order (addresses lowercased by the domain layer). */
92
+ tokens: readonly VerifiedTokens.Token[]
93
+ /** ISO timestamp of the publish. */
94
+ updatedAt: string
95
+ /** New opaque list version. */
96
+ version: string
97
+ }
98
+ }
99
+
100
+ /** Maps a stored row to the domain token (null `logoUri` → absent field). */
101
+ export function toToken(row: Row): VerifiedTokens.Token {
102
+ return {
103
+ address: row.address as Hex.Hex,
104
+ currency: row.currency,
105
+ decimals: row.decimals,
106
+ id: row.address as Hex.Hex,
107
+ name: row.name,
108
+ symbol: row.symbol,
109
+ ...(row.logoUri === null ? {} : { logoUri: row.logoUri }),
110
+ }
111
+ }
@@ -0,0 +1,159 @@
1
+ import type { Selectable } from 'kysely'
2
+
3
+ import type * as Webhooks from '../../internal/Webhooks.js'
4
+ import type * as Db from '../Db.js'
5
+ import type * as db_Schema from '../Schema.js'
6
+
7
+ /** Columns of the `webhook_deliveries` table, derived from `Schema.WebhookDelivery`. */
8
+ export type Table = db_Schema.WebhookDelivery
9
+
10
+ /** A stored webhook-delivery row. */
11
+ export type Record = Selectable<Table>
12
+
13
+ /** Maps a stored row to the domain delivery (null columns → absent fields). */
14
+ export function toDelivery(row: Record): Webhooks.Delivery {
15
+ return {
16
+ attempt: row.attempt,
17
+ createdAt: row.createdAt,
18
+ envelope: row.envelope,
19
+ ...(row.error === null ? {} : { error: row.error }),
20
+ eventId: row.eventId,
21
+ id: row.id,
22
+ requestUrl: row.requestUrl,
23
+ ...(row.responseMs === null ? {} : { responseMs: row.responseMs }),
24
+ ...(row.responseStatus === null ? {} : { responseStatus: row.responseStatus }),
25
+ status: row.status,
26
+ subscriptionId: row.subscriptionId,
27
+ }
28
+ }
29
+
30
+ /**
31
+ * Inserts a delivery row from its domain value.
32
+ *
33
+ * @param db - The database.
34
+ * @param delivery - The delivery to insert.
35
+ * @param expiresAt - Retention deadline (ISO); the row is invisible past it.
36
+ */
37
+ export async function insert(
38
+ db: Db.Db,
39
+ delivery: Webhooks.Delivery,
40
+ expiresAt: string,
41
+ ): Promise<void> {
42
+ await db.kysely
43
+ .insertInto('webhook_deliveries')
44
+ .values({
45
+ attempt: delivery.attempt,
46
+ createdAt: delivery.createdAt,
47
+ envelope: delivery.envelope,
48
+ error: delivery.error ?? null,
49
+ eventId: delivery.eventId,
50
+ expiresAt,
51
+ id: delivery.id,
52
+ requestUrl: delivery.requestUrl,
53
+ responseMs: delivery.responseMs ?? null,
54
+ responseStatus: delivery.responseStatus ?? null,
55
+ status: delivery.status,
56
+ subscriptionId: delivery.subscriptionId,
57
+ })
58
+ .execute()
59
+ }
60
+
61
+ /**
62
+ * Lists a subscription's unexpired deliveries, newest first, with keyset
63
+ * paging (`id < cursor` — ids embed a zero-padded timestamp, so lexical order
64
+ * is chronological).
65
+ *
66
+ * @param db - The database.
67
+ * @param subscriptionId - The owning subscription id (`wh_…`).
68
+ * @param options - Paging options plus the retention timestamp.
69
+ * @returns The deliveries.
70
+ */
71
+ export async function list(
72
+ db: Db.Db,
73
+ subscriptionId: string,
74
+ options: list.Options,
75
+ ): Promise<Webhooks.Delivery[]> {
76
+ let query = db.kysely
77
+ .selectFrom('webhook_deliveries')
78
+ .selectAll()
79
+ .where('subscriptionId', '=', subscriptionId)
80
+ .where('expiresAt', '>', options.now)
81
+ .orderBy('id', 'desc')
82
+ if (options.cursor) query = query.where('id', '<', options.cursor)
83
+ if (options.offset) query = query.offset(options.offset)
84
+ if (options.limit !== undefined) query = query.limit(options.limit)
85
+ return (await query.execute()).map(toDelivery)
86
+ }
87
+
88
+ export declare namespace list {
89
+ /** Options for {@link list}. */
90
+ type Options = {
91
+ /** Return deliveries whose id sorts before this one (older entries). */
92
+ cursor?: string | undefined
93
+ /** Maximum deliveries to return. */
94
+ limit?: number | undefined
95
+ /** ISO timestamp retention is evaluated against. */
96
+ now: string
97
+ /** Rows to skip from the head (positional pagination; exclusive with `cursor`). */
98
+ offset?: number | undefined
99
+ }
100
+ }
101
+
102
+ /**
103
+ * Counts a subscription's unexpired deliveries.
104
+ *
105
+ * @param db - The database.
106
+ * @param subscriptionId - The owning subscription id (`wh_…`).
107
+ * @param now - ISO timestamp retention is evaluated against.
108
+ * @returns The count.
109
+ */
110
+ export async function count(db: Db.Db, subscriptionId: string, now: string): Promise<number> {
111
+ const row = await db.kysely
112
+ .selectFrom('webhook_deliveries')
113
+ .select((eb) => eb.fn.countAll().as('count'))
114
+ .where('subscriptionId', '=', subscriptionId)
115
+ .where('expiresAt', '>', now)
116
+ .executeTakeFirstOrThrow()
117
+ return Number(row.count)
118
+ }
119
+
120
+ /**
121
+ * Reads a single unexpired delivery scoped to its subscription.
122
+ *
123
+ * @param db - The database.
124
+ * @param subscriptionId - The owning subscription id (`wh_…`).
125
+ * @param deliveryId - The delivery id (`whd_…`).
126
+ * @param now - ISO timestamp retention is evaluated against.
127
+ * @returns The delivery, or `null` when absent.
128
+ */
129
+ export async function get(
130
+ db: Db.Db,
131
+ subscriptionId: string,
132
+ deliveryId: string,
133
+ now: string,
134
+ ): Promise<Webhooks.Delivery | null> {
135
+ const row = await db.kysely
136
+ .selectFrom('webhook_deliveries')
137
+ .selectAll()
138
+ .where('subscriptionId', '=', subscriptionId)
139
+ .where('id', '=', deliveryId)
140
+ .where('expiresAt', '>', now)
141
+ .executeTakeFirst()
142
+ return row ? toDelivery(row) : null
143
+ }
144
+
145
+ /**
146
+ * Deletes deliveries past their retention deadline. Called best-effort from
147
+ * the poller tick.
148
+ *
149
+ * @param db - The database.
150
+ * @param now - ISO timestamp retention is evaluated against.
151
+ * @returns The number of rows deleted.
152
+ */
153
+ export async function pruneExpired(db: Db.Db, now: string): Promise<number> {
154
+ const result = await db.kysely
155
+ .deleteFrom('webhook_deliveries')
156
+ .where('expiresAt', '<=', now)
157
+ .executeTakeFirst()
158
+ return Number(result.numDeletedRows)
159
+ }