tapimo 0.10.2 → 0.12.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.
- package/dist/App.d.ts +4896 -2166
- package/dist/App.d.ts.map +1 -1
- package/dist/App.js +2 -2
- package/dist/App.js.map +1 -1
- package/dist/Assets.d.ts +15 -0
- package/dist/Assets.d.ts.map +1 -1
- package/dist/Assets.js +7 -0
- package/dist/Assets.js.map +1 -1
- package/dist/Webhooks.d.ts +0 -75
- package/dist/Webhooks.d.ts.map +1 -1
- package/dist/Webhooks.js +1 -143
- package/dist/Webhooks.js.map +1 -1
- package/dist/admin/App.d.ts +127 -78
- package/dist/admin/App.d.ts.map +1 -1
- package/dist/admin/apps/earn-vaults.d.ts +115 -1
- package/dist/admin/apps/earn-vaults.d.ts.map +1 -1
- package/dist/admin/apps/earn-vaults.js +59 -6
- package/dist/admin/apps/earn-vaults.js.map +1 -1
- package/dist/admin/apps/organizations.d.ts +3 -3
- package/dist/admin/apps/organizations.d.ts.map +1 -1
- package/dist/admin/apps/organizations.js +4 -2
- package/dist/admin/apps/organizations.js.map +1 -1
- package/dist/admin/apps/verified-tokens.d.ts +83 -83
- package/dist/admin/ui.generated.d.ts +1 -1
- package/dist/admin/ui.generated.d.ts.map +1 -1
- package/dist/admin/ui.generated.js +151 -151
- package/dist/admin/ui.generated.js.map +1 -1
- package/dist/apps/data/App.d.ts +5924 -4310
- package/dist/apps/data/App.d.ts.map +1 -1
- package/dist/apps/data/App.js +61 -9
- package/dist/apps/data/App.js.map +1 -1
- package/dist/apps/data/ZoneSelection.d.ts +62 -0
- package/dist/apps/data/ZoneSelection.d.ts.map +1 -0
- package/dist/apps/data/ZoneSelection.js +44 -0
- package/dist/apps/data/ZoneSelection.js.map +1 -0
- package/dist/apps/data/routes/activities.d.ts +2368 -5
- package/dist/apps/data/routes/activities.d.ts.map +1 -1
- package/dist/apps/data/routes/activities.js +423 -114
- package/dist/apps/data/routes/activities.js.map +1 -1
- package/dist/apps/data/routes/addresses.d.ts +131 -0
- package/dist/apps/data/routes/addresses.d.ts.map +1 -1
- package/dist/apps/data/routes/balances.d.ts +25 -25
- package/dist/apps/data/routes/balances.js +1 -1
- package/dist/apps/data/routes/balances.js.map +1 -1
- package/dist/apps/data/routes/blocks.d.ts +1 -1
- package/dist/apps/data/routes/blocks.js +1 -1
- package/dist/apps/data/routes/blocks.js.map +1 -1
- package/dist/apps/data/routes/earn.d.ts +135 -10
- package/dist/apps/data/routes/earn.d.ts.map +1 -1
- package/dist/apps/data/routes/earn.js +157 -25
- package/dist/apps/data/routes/earn.js.map +1 -1
- package/dist/apps/data/routes/exchanges.d.ts +829 -123
- package/dist/apps/data/routes/exchanges.d.ts.map +1 -1
- package/dist/apps/data/routes/exchanges.js +16 -13
- package/dist/apps/data/routes/exchanges.js.map +1 -1
- package/dist/apps/data/routes/fee-amm.d.ts +26 -26
- package/dist/apps/data/routes/fee-amm.d.ts.map +1 -1
- package/dist/apps/data/routes/fee-amm.js +9 -11
- package/dist/apps/data/routes/fee-amm.js.map +1 -1
- package/dist/apps/data/routes/receipts.d.ts +54 -54
- package/dist/apps/data/routes/receipts.js +1 -1
- package/dist/apps/data/routes/receipts.js.map +1 -1
- package/dist/apps/data/routes/rpc.js +24 -10
- package/dist/apps/data/routes/rpc.js.map +1 -1
- package/dist/apps/data/routes/tokenlist.d.ts +12 -12
- package/dist/apps/data/routes/tokenlist.d.ts.map +1 -1
- package/dist/apps/data/routes/tokenlist.js +16 -15
- package/dist/apps/data/routes/tokenlist.js.map +1 -1
- package/dist/apps/data/routes/tokens.d.ts +17 -3
- package/dist/apps/data/routes/tokens.d.ts.map +1 -1
- package/dist/apps/data/routes/tokens.js +34 -15
- package/dist/apps/data/routes/tokens.js.map +1 -1
- package/dist/apps/data/routes/transactions.d.ts +1 -1
- package/dist/apps/data/routes/transactions.js +1 -1
- package/dist/apps/data/routes/transactions.js.map +1 -1
- package/dist/apps/data/routes/transfers.d.ts +13 -13
- package/dist/apps/data/routes/transfers.d.ts.map +1 -1
- package/dist/apps/data/routes/transfers.js +7 -9
- package/dist/apps/data/routes/transfers.js.map +1 -1
- package/dist/apps/data/routes/valuation.d.ts +21 -21
- package/dist/apps/data/routes/verified-tokens.d.ts +5 -5
- package/dist/apps/data/routes/webhooks.d.ts +1011 -151
- package/dist/apps/data/routes/webhooks.d.ts.map +1 -1
- package/dist/apps/data/routes/webhooks.js +126 -739
- package/dist/apps/data/routes/webhooks.js.map +1 -1
- package/dist/apps/data/routes/zones.d.ts +15 -15
- package/dist/apps/funding/App.d.ts +27 -27
- package/dist/apps/funding/routes/chains.d.ts.map +1 -1
- package/dist/apps/funding/routes/chains.js +5 -4
- package/dist/apps/funding/routes/chains.js.map +1 -1
- package/dist/apps/funding/routes/deposit-addresses.d.ts +18 -18
- package/dist/apps/funding/routes/deposit-addresses.d.ts.map +1 -1
- package/dist/apps/funding/routes/deposit-addresses.js +8 -8
- package/dist/apps/funding/routes/deposit-addresses.js.map +1 -1
- package/dist/apps/funding/routes/providers.d.ts.map +1 -1
- package/dist/apps/funding/routes/providers.js +3 -2
- package/dist/apps/funding/routes/providers.js.map +1 -1
- package/dist/apps/funding/routes/quotes.d.ts.map +1 -1
- package/dist/apps/funding/routes/quotes.js +2 -2
- package/dist/apps/funding/routes/quotes.js.map +1 -1
- package/dist/apps/funding/routes/transfers.d.ts +33 -33
- package/dist/apps/management/App.d.ts +1433 -322
- package/dist/apps/management/App.d.ts.map +1 -1
- package/dist/apps/management/Billing.d.ts.map +1 -1
- package/dist/apps/management/Billing.js +19 -6
- package/dist/apps/management/Billing.js.map +1 -1
- package/dist/apps/management/routes/api-keys.d.ts +154 -94
- package/dist/apps/management/routes/api-keys.d.ts.map +1 -1
- package/dist/apps/management/routes/api-keys.js +76 -30
- package/dist/apps/management/routes/api-keys.js.map +1 -1
- package/dist/apps/management/routes/billing.d.ts +103 -30
- package/dist/apps/management/routes/billing.d.ts.map +1 -1
- package/dist/apps/management/routes/billing.js +91 -32
- package/dist/apps/management/routes/billing.js.map +1 -1
- package/dist/apps/management/routes/faucet.d.ts +12 -12
- package/dist/apps/management/routes/faucet.d.ts.map +1 -1
- package/dist/apps/management/routes/faucet.js +23 -12
- package/dist/apps/management/routes/faucet.js.map +1 -1
- package/dist/apps/management/routes/invitations.d.ts +46 -13
- package/dist/apps/management/routes/invitations.d.ts.map +1 -1
- package/dist/apps/management/routes/invitations.js +79 -29
- package/dist/apps/management/routes/invitations.js.map +1 -1
- package/dist/apps/management/routes/invite-links.d.ts +128 -83
- package/dist/apps/management/routes/invite-links.d.ts.map +1 -1
- package/dist/apps/management/routes/invite-links.js +100 -41
- package/dist/apps/management/routes/invite-links.js.map +1 -1
- package/dist/apps/management/routes/me.d.ts +5 -5
- package/dist/apps/management/routes/me.d.ts.map +1 -1
- package/dist/apps/management/routes/me.js +14 -10
- package/dist/apps/management/routes/me.js.map +1 -1
- package/dist/apps/management/routes/members.d.ts +68 -47
- package/dist/apps/management/routes/members.d.ts.map +1 -1
- package/dist/apps/management/routes/members.js +37 -18
- package/dist/apps/management/routes/members.js.map +1 -1
- package/dist/apps/management/routes/orgs.d.ts +48 -8
- package/dist/apps/management/routes/orgs.d.ts.map +1 -1
- package/dist/apps/management/routes/orgs.js +48 -19
- package/dist/apps/management/routes/orgs.js.map +1 -1
- package/dist/apps/management/routes/projects.d.ts +130 -68
- package/dist/apps/management/routes/projects.d.ts.map +1 -1
- package/dist/apps/management/routes/projects.js +72 -21
- package/dist/apps/management/routes/projects.js.map +1 -1
- package/dist/apps/management/routes/scopes.d.ts.map +1 -1
- package/dist/apps/management/routes/scopes.js +8 -5
- package/dist/apps/management/routes/scopes.js.map +1 -1
- package/dist/apps/management/routes/usage.d.ts +38 -38
- package/dist/apps/management/routes/usage.d.ts.map +1 -1
- package/dist/apps/management/routes/usage.js +41 -31
- package/dist/apps/management/routes/usage.js.map +1 -1
- package/dist/apps/management/routes/verified-token-requests.d.ts +34 -6
- package/dist/apps/management/routes/verified-token-requests.d.ts.map +1 -1
- package/dist/apps/management/routes/verified-token-requests.js +36 -17
- package/dist/apps/management/routes/verified-token-requests.js.map +1 -1
- package/dist/apps/management/routes/webhooks.d.ts +1186 -197
- package/dist/apps/management/routes/webhooks.d.ts.map +1 -1
- package/dist/apps/management/routes/webhooks.js +106 -38
- package/dist/apps/management/routes/webhooks.js.map +1 -1
- package/dist/apps/mpp/internal/Adapter.d.ts +1 -1
- package/dist/apps/mpp/internal/Adapter.d.ts.map +1 -1
- package/dist/apps/mpp/internal/Adapter.js +12 -0
- package/dist/apps/mpp/internal/Adapter.js.map +1 -1
- package/dist/db/Db.d.ts +2 -0
- package/dist/db/Db.d.ts.map +1 -1
- package/dist/db/Db.js +61 -0
- package/dist/db/Db.js.map +1 -1
- package/dist/db/Schema.d.ts +30 -2
- package/dist/db/Schema.d.ts.map +1 -1
- package/dist/db/Schema.js +28 -3
- package/dist/db/Schema.js.map +1 -1
- package/dist/db/tables/earnVaults.d.ts +5 -1
- package/dist/db/tables/earnVaults.d.ts.map +1 -1
- package/dist/db/tables/earnVaults.js +5 -0
- package/dist/db/tables/earnVaults.js.map +1 -1
- package/dist/db/tables/webhookDeliveries.d.ts.map +1 -1
- package/dist/db/tables/webhookDeliveries.js +10 -0
- package/dist/db/tables/webhookDeliveries.js.map +1 -1
- package/dist/db/tables/webhookQueueCompletions.d.ts +15 -0
- package/dist/db/tables/webhookQueueCompletions.d.ts.map +1 -0
- package/dist/db/tables/webhookQueueCompletions.js +32 -0
- package/dist/db/tables/webhookQueueCompletions.js.map +1 -0
- package/dist/db/tables/webhookQueueEvents.d.ts +16 -20
- package/dist/db/tables/webhookQueueEvents.d.ts.map +1 -1
- package/dist/db/tables/webhookQueueEvents.js +68 -43
- package/dist/db/tables/webhookQueueEvents.js.map +1 -1
- package/dist/db/tables/webhookSubscriptions.d.ts +47 -72
- package/dist/db/tables/webhookSubscriptions.d.ts.map +1 -1
- package/dist/db/tables/webhookSubscriptions.js +92 -105
- package/dist/db/tables/webhookSubscriptions.js.map +1 -1
- package/dist/internal/Auth.d.ts +8 -1
- package/dist/internal/Auth.d.ts.map +1 -1
- package/dist/internal/Auth.js +13 -3
- package/dist/internal/Auth.js.map +1 -1
- package/dist/internal/Cache.d.ts.map +1 -1
- package/dist/internal/Cache.js +11 -7
- package/dist/internal/Cache.js.map +1 -1
- package/dist/internal/EarnVaults.d.ts +41 -2
- package/dist/internal/EarnVaults.d.ts.map +1 -1
- package/dist/internal/EarnVaults.js +167 -0
- package/dist/internal/EarnVaults.js.map +1 -1
- package/dist/internal/MetricSink.d.ts +35 -125
- package/dist/internal/MetricSink.d.ts.map +1 -1
- package/dist/internal/MetricSink.js +50 -121
- package/dist/internal/MetricSink.js.map +1 -1
- package/dist/internal/OpenApi.d.ts +26 -22
- package/dist/internal/OpenApi.d.ts.map +1 -1
- package/dist/internal/OpenApi.js +128 -39
- package/dist/internal/OpenApi.js.map +1 -1
- package/dist/internal/Response.d.ts +13 -8
- package/dist/internal/Response.d.ts.map +1 -1
- package/dist/internal/Response.js +16 -5
- package/dist/internal/Response.js.map +1 -1
- package/dist/internal/Schema.d.ts +8 -8
- package/dist/internal/Schema.d.ts.map +1 -1
- package/dist/internal/Schema.js +10 -9
- package/dist/internal/Schema.js.map +1 -1
- package/dist/internal/Store.d.ts +15 -6
- package/dist/internal/Store.d.ts.map +1 -1
- package/dist/internal/Store.js +45 -15
- package/dist/internal/Store.js.map +1 -1
- package/dist/internal/Tidx.d.ts +2 -0
- package/dist/internal/Tidx.d.ts.map +1 -1
- package/dist/internal/Tidx.js +16 -4
- package/dist/internal/Tidx.js.map +1 -1
- package/dist/internal/WebhookTransfer.d.ts +1 -55
- package/dist/internal/WebhookTransfer.d.ts.map +1 -1
- package/dist/internal/WebhookTransfer.js +1 -239
- package/dist/internal/WebhookTransfer.js.map +1 -1
- package/dist/internal/Webhooks.d.ts +112 -56
- package/dist/internal/Webhooks.d.ts.map +1 -1
- package/dist/internal/Webhooks.js +124 -93
- package/dist/internal/Webhooks.js.map +1 -1
- package/dist/internal/funding/Deposit.d.ts +2 -2
- package/dist/internal/funding/Deposit.d.ts.map +1 -1
- package/dist/internal/funding/Deposit.js +9 -8
- package/dist/internal/funding/Deposit.js.map +1 -1
- package/dist/internal/funding/DepositAddress.d.ts +4 -4
- package/dist/internal/funding/DepositAddress.d.ts.map +1 -1
- package/dist/internal/funding/DepositAddress.js +9 -8
- package/dist/internal/funding/DepositAddress.js.map +1 -1
- package/dist/internal/funding/DepositWebhook.d.ts +25 -0
- package/dist/internal/funding/DepositWebhook.d.ts.map +1 -0
- package/dist/internal/funding/DepositWebhook.js +74 -0
- package/dist/internal/funding/DepositWebhook.js.map +1 -0
- package/dist/internal/funding/Provider.d.ts.map +1 -1
- package/dist/internal/funding/Provider.js +13 -12
- package/dist/internal/funding/Provider.js.map +1 -1
- package/dist/internal/funding/Reconciliation.d.ts +6 -0
- package/dist/internal/funding/Reconciliation.d.ts.map +1 -1
- package/dist/internal/funding/Reconciliation.js +42 -10
- package/dist/internal/funding/Reconciliation.js.map +1 -1
- package/dist/internal/funding/index.d.ts +1 -0
- package/dist/internal/funding/index.d.ts.map +1 -1
- package/dist/internal/funding/index.js +1 -0
- package/dist/internal/funding/index.js.map +1 -1
- package/dist/internal/funding/providers/stargate.d.ts +19 -0
- package/dist/internal/funding/providers/stargate.d.ts.map +1 -0
- package/dist/internal/funding/providers/stargate.js +143 -0
- package/dist/internal/funding/providers/stargate.js.map +1 -0
- package/dist/internal/webhookDestinations/slack.d.ts.map +1 -1
- package/dist/internal/webhookDestinations/slack.js +16 -0
- package/dist/internal/webhookDestinations/slack.js.map +1 -1
- package/dist/providers/index.d.ts +7 -0
- package/dist/providers/index.d.ts.map +1 -1
- package/dist/providers/index.js +3 -0
- package/dist/providers/index.js.map +1 -1
- package/package.json +2 -2
- package/src/App.test.ts +48 -0
- package/src/App.ts +7 -2
- package/src/Assets.test.ts +18 -0
- package/src/Assets.ts +22 -0
- package/src/Client.test-d.ts +287 -17
- package/src/Webhooks.ts +1 -220
- package/src/admin/apps/earn-vaults.test.ts +143 -0
- package/src/admin/apps/earn-vaults.ts +83 -6
- package/src/admin/apps/organizations.ts +6 -2
- package/src/admin/ui/src/routes/api-keys.tsx +36 -0
- package/src/admin/ui.generated.ts +151 -189
- package/src/apps/data/App.test.ts +31 -0
- package/src/apps/data/App.ts +64 -10
- package/src/apps/data/ZoneSelection.test.ts +196 -0
- package/src/apps/data/ZoneSelection.ts +108 -0
- package/src/apps/data/routes/activities.test.ts +435 -12
- package/src/apps/data/routes/activities.ts +562 -125
- package/src/apps/data/routes/balances.ts +1 -1
- package/src/apps/data/routes/blocks.ts +1 -1
- package/src/apps/data/routes/earn.test.ts +183 -23
- package/src/apps/data/routes/earn.ts +225 -29
- package/src/apps/data/routes/earn.zone-access.test.ts +46 -0
- package/src/apps/data/routes/exchanges.test.ts +31 -1
- package/src/apps/data/routes/exchanges.ts +19 -15
- package/src/apps/data/routes/fee-amm.ts +18 -11
- package/src/apps/data/routes/receipts.ts +1 -1
- package/src/apps/data/routes/rpc.test.ts +75 -0
- package/src/apps/data/routes/rpc.ts +23 -9
- package/src/apps/data/routes/tokenlist.ts +21 -18
- package/src/apps/data/routes/tokens.test.ts +29 -0
- package/src/apps/data/routes/tokens.ts +38 -15
- package/src/apps/data/routes/transactions.ts +1 -1
- package/src/apps/data/routes/transfers.test.ts +3 -3
- package/src/apps/data/routes/transfers.ts +12 -9
- package/src/apps/data/routes/webhooks.test.ts +179 -1497
- package/src/apps/data/routes/webhooks.ts +156 -976
- package/src/apps/data/routes/zones.test.ts +19 -13
- package/src/apps/funding/routes/chains.test.ts +211 -7
- package/src/apps/funding/routes/chains.ts +19 -12
- package/src/apps/funding/routes/deposit-addresses.test.ts +394 -0
- package/src/apps/funding/routes/deposit-addresses.ts +84 -72
- package/src/apps/funding/routes/providers.ts +9 -5
- package/src/apps/funding/routes/quotes.test.ts +46 -0
- package/src/apps/funding/routes/quotes.ts +8 -5
- package/src/apps/funding/routes/transfers.test.ts +35 -32
- package/src/apps/management/Billing.test.ts +26 -0
- package/src/apps/management/Billing.ts +24 -7
- package/src/apps/management/routes/api-keys.test.ts +178 -0
- package/src/apps/management/routes/api-keys.ts +203 -144
- package/src/apps/management/routes/billing.test.ts +209 -0
- package/src/apps/management/routes/billing.ts +254 -177
- package/src/apps/management/routes/faucet.test.ts +39 -0
- package/src/apps/management/routes/faucet.ts +75 -41
- package/src/apps/management/routes/invitations.test.ts +216 -0
- package/src/apps/management/routes/invitations.ts +186 -109
- package/src/apps/management/routes/invite-links.test.ts +263 -1
- package/src/apps/management/routes/invite-links.ts +295 -214
- package/src/apps/management/routes/me.test.ts +49 -0
- package/src/apps/management/routes/me.ts +42 -32
- package/src/apps/management/routes/members.test.ts +124 -1
- package/src/apps/management/routes/members.ts +81 -51
- package/src/apps/management/routes/orgs.test.ts +176 -0
- package/src/apps/management/routes/orgs.ts +101 -59
- package/src/apps/management/routes/projects.test.ts +183 -0
- package/src/apps/management/routes/projects.ts +124 -60
- package/src/apps/management/routes/scopes.test.ts +44 -0
- package/src/apps/management/routes/scopes.ts +37 -28
- package/src/apps/management/routes/usage.test.ts +92 -0
- package/src/apps/management/routes/usage.ts +186 -151
- package/src/apps/management/routes/verified-token-requests.test.ts +114 -0
- package/src/apps/management/routes/verified-token-requests.ts +166 -133
- package/src/apps/management/routes/webhooks.test.ts +313 -1
- package/src/apps/management/routes/webhooks.ts +220 -71
- package/src/apps/mpp/internal/Adapter.ts +13 -1
- package/src/db/Db.test.ts +42 -5
- package/src/db/Db.ts +85 -0
- package/src/db/Schema.ts +41 -3
- package/src/db/tables/earnVaults.test.ts +22 -0
- package/src/db/tables/earnVaults.ts +11 -1
- package/src/db/tables/organizations.test.ts +5 -2
- package/src/db/tables/webhookDeliveries.ts +13 -0
- package/src/db/tables/webhookQueueCompletions.ts +55 -0
- package/src/db/tables/webhookQueueEvents.ts +97 -57
- package/src/db/tables/webhookSubscriptions.ts +134 -144
- package/src/internal/Auth.ts +23 -5
- package/src/internal/Cache.ts +14 -7
- package/src/internal/EarnVaults.capabilities.test.ts +33 -0
- package/src/internal/EarnVaults.test.ts +79 -0
- package/src/internal/EarnVaults.ts +277 -0
- package/src/internal/MetricSink.test.ts +41 -373
- package/src/internal/MetricSink.ts +92 -273
- package/src/internal/OpenApi.ts +165 -49
- package/src/internal/Response.test.ts +95 -0
- package/src/internal/Response.ts +40 -2
- package/src/internal/Schema.test.ts +34 -0
- package/src/internal/Schema.ts +20 -9
- package/src/internal/Store.test.ts +186 -0
- package/src/internal/Store.ts +57 -17
- package/src/internal/Tidx.test.ts +19 -0
- package/src/internal/Tidx.ts +16 -4
- package/src/internal/WebhookTransfer.ts +1 -351
- package/src/internal/Webhooks.test.ts +222 -145
- package/src/internal/Webhooks.ts +234 -135
- package/src/internal/funding/Deposit.ts +71 -64
- package/src/internal/funding/DepositAddress.ts +50 -43
- package/src/internal/funding/DepositWebhook.test.ts +131 -0
- package/src/internal/funding/DepositWebhook.ts +101 -0
- package/src/internal/funding/Provider.ts +164 -142
- package/src/internal/funding/Reconciliation.test.ts +14 -0
- package/src/internal/funding/Reconciliation.ts +161 -86
- package/src/internal/funding/index.test.ts +1 -0
- package/src/internal/funding/index.ts +1 -0
- package/src/internal/funding/providers/index.test.ts +185 -0
- package/src/internal/funding/providers/stargate.ts +184 -0
- package/src/internal/webhookDestinations/slack.ts +15 -0
- package/src/providers/index.test.ts +1 -0
- package/src/providers/index.ts +9 -0
- package/src/Webhooks.bench.ts +0 -714
- package/src/Webhooks.memory.bench.ts +0 -250
- package/src/Webhooks.test.ts +0 -146
package/src/db/Schema.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import * as z from 'zod/mini'
|
|
2
2
|
import type { Address } from 'viem'
|
|
3
3
|
|
|
4
|
+
import type * as EarnVaults from '../internal/EarnVaults.js'
|
|
4
5
|
import type * as funding_Catalog from '../internal/funding/Catalog.js'
|
|
5
6
|
import type * as funding_Deposit from '../internal/funding/Deposit.js'
|
|
6
7
|
import type * as funding_DepositAddress from '../internal/funding/DepositAddress.js'
|
|
@@ -106,6 +107,9 @@ export const EarnVault = z
|
|
|
106
107
|
slug: z.string().check(z.describe('Stable URL slug assigned when the vault is registered.')),
|
|
107
108
|
updatedAt: z.string().check(z.describe('When the vault registration last changed (ISO 8601).')),
|
|
108
109
|
vaultAddress: z.custom<Address>().check(z.describe('Earn vault address (lowercase).')),
|
|
110
|
+
zones: z
|
|
111
|
+
.custom<readonly EarnVaults.ZoneRoute[]>()
|
|
112
|
+
.check(z.describe('Curated private Zone routes (`jsonb`).')),
|
|
109
113
|
})
|
|
110
114
|
.check(z.describe('One curated Earn vault row.'))
|
|
111
115
|
|
|
@@ -792,6 +796,9 @@ export const WebhookSubscription = z.object({
|
|
|
792
796
|
destination: z
|
|
793
797
|
.custom<Webhooks.Destination>()
|
|
794
798
|
.check(z.describe('Delivery destination (`jsonb`).')),
|
|
799
|
+
environment: z
|
|
800
|
+
.nullable(z.enum(['production', 'sandbox']))
|
|
801
|
+
.check(z.describe('API-key environment for private resource events, or null.')),
|
|
795
802
|
eventType: z
|
|
796
803
|
.custom<Webhooks.EventType>()
|
|
797
804
|
.check(z.describe('Event type the subscription listens to.')),
|
|
@@ -820,6 +827,9 @@ export const WebhookSubscription = z.object({
|
|
|
820
827
|
pollerCursor: z
|
|
821
828
|
.nullable(z.string())
|
|
822
829
|
.check(z.describe('Poller keyset cursor, or null before the first tick.')),
|
|
830
|
+
projectId: z
|
|
831
|
+
.nullable(z.string())
|
|
832
|
+
.check(z.describe('API-key project attribution for private resource events, or null.')),
|
|
823
833
|
secret: z.string().check(z.describe('HMAC signing secret.')),
|
|
824
834
|
status: z.custom<Webhooks.Status>().check(z.describe('Lifecycle status.')),
|
|
825
835
|
updatedAt: z
|
|
@@ -875,6 +885,23 @@ export const WebhookDelivery = z.object({
|
|
|
875
885
|
/** Columns of the `webhook_deliveries` table. */
|
|
876
886
|
export type WebhookDelivery = z.output<typeof WebhookDelivery>
|
|
877
887
|
|
|
888
|
+
/** An insert-only dedupe record marking a delivery obligation terminal. */
|
|
889
|
+
export const WebhookQueueCompletion = z.object({
|
|
890
|
+
eventId: z.string().check(z.describe('Idempotent event id (`evt_...`).')),
|
|
891
|
+
expiresAt: z
|
|
892
|
+
.string()
|
|
893
|
+
.check(z.describe('ISO dedupe retention deadline; rows past it are invisible and pruned.')),
|
|
894
|
+
status: z
|
|
895
|
+
.enum(['failed', 'skipped', 'succeeded'])
|
|
896
|
+
.check(z.describe('Terminal delivery-obligation outcome.')),
|
|
897
|
+
subscriptionId: z
|
|
898
|
+
.string()
|
|
899
|
+
.check(z.describe('Owning subscription id (`wh_...`); FK with `ON DELETE CASCADE`.')),
|
|
900
|
+
})
|
|
901
|
+
|
|
902
|
+
/** Columns of the `webhook_queue_completions` table. */
|
|
903
|
+
export type WebhookQueueCompletion = z.output<typeof WebhookQueueCompletion>
|
|
904
|
+
|
|
878
905
|
/** A durable webhook envelope staged before its compact Queue reference is admitted. */
|
|
879
906
|
export const WebhookQueueEvent = z.object({
|
|
880
907
|
attemptCount: z.number().check(z.describe('Delivery attempts claimed so far.')),
|
|
@@ -887,18 +914,29 @@ export const WebhookQueueEvent = z.object({
|
|
|
887
914
|
.check(z.describe('The full webhook envelope retained outside Cloudflare Queues.')),
|
|
888
915
|
eventId: z.string().check(z.describe('Idempotent event id (`evt_...`).')),
|
|
889
916
|
expiresAt: z
|
|
890
|
-
.string()
|
|
917
|
+
.nullable(z.string())
|
|
891
918
|
.check(
|
|
892
919
|
z.describe(
|
|
893
|
-
'
|
|
920
|
+
'Unwritten since completions took over dedupe; non-null only on rows staged before the split.',
|
|
894
921
|
),
|
|
895
922
|
),
|
|
896
923
|
nextAttemptAt: z
|
|
897
924
|
.nullable(z.string())
|
|
898
925
|
.check(z.describe('ISO retry due time, or null when due immediately.')),
|
|
926
|
+
observedAt: z
|
|
927
|
+
.nullable(z.string())
|
|
928
|
+
.check(
|
|
929
|
+
z.describe(
|
|
930
|
+
'ISO timestamp the coordinator observed the head owing this event, or null on replays.',
|
|
931
|
+
),
|
|
932
|
+
),
|
|
899
933
|
status: z
|
|
900
934
|
.enum(['failed', 'pending', 'skipped', 'succeeded'])
|
|
901
|
-
.check(
|
|
935
|
+
.check(
|
|
936
|
+
z.describe(
|
|
937
|
+
'Always `pending` since completions took over terminal state; other values occur only on rows completed before the split.',
|
|
938
|
+
),
|
|
939
|
+
),
|
|
902
940
|
subscriptionId: z
|
|
903
941
|
.string()
|
|
904
942
|
.check(z.describe('Owning subscription id (`wh_...`); FK with `ON DELETE CASCADE`.')),
|
|
@@ -11,6 +11,13 @@ const input = {
|
|
|
11
11
|
],
|
|
12
12
|
privateOutputTokens: ['0x20c00000000000000000000057F242B21a53ae4D'],
|
|
13
13
|
vaultAddress: '0x2f05f4d3625B2eFFb383eFbEDAFd2D8F04371636',
|
|
14
|
+
zones: [
|
|
15
|
+
{
|
|
16
|
+
chainId: 1_424_310_001,
|
|
17
|
+
deploymentBlock: 31_039_238,
|
|
18
|
+
earnRouter: '0x9144086A79dea966d8bf1B52125eBED8919fC45c',
|
|
19
|
+
},
|
|
20
|
+
],
|
|
14
21
|
} as const satisfies EarnVaults.upsert.Options
|
|
15
22
|
|
|
16
23
|
describe('upsert', () => {
|
|
@@ -36,6 +43,13 @@ describe('upsert', () => {
|
|
|
36
43
|
],
|
|
37
44
|
"updatedAt": "2026-01-01T00:00:00.000Z",
|
|
38
45
|
"vaultAddress": "0x2f05f4d3625b2effb383efbedafd2d8f04371636",
|
|
46
|
+
"zones": [
|
|
47
|
+
{
|
|
48
|
+
"chainId": 1424310001,
|
|
49
|
+
"deploymentBlock": 31039238,
|
|
50
|
+
"earnRouter": "0x9144086a79dea966d8bf1b52125ebed8919fc45c",
|
|
51
|
+
},
|
|
52
|
+
],
|
|
39
53
|
}
|
|
40
54
|
`)
|
|
41
55
|
} finally {
|
|
@@ -68,6 +82,7 @@ describe('upsert', () => {
|
|
|
68
82
|
privateInputTokens: stored.privateInputTokens,
|
|
69
83
|
privateOutputTokens: stored.privateOutputTokens,
|
|
70
84
|
updatedAt: stored.updatedAt,
|
|
85
|
+
zones: stored.zones,
|
|
71
86
|
}).toMatchInlineSnapshot(`
|
|
72
87
|
{
|
|
73
88
|
"createdAt": "2026-01-01T00:00:00.000Z",
|
|
@@ -76,6 +91,13 @@ describe('upsert', () => {
|
|
|
76
91
|
"privateInputTokens": [],
|
|
77
92
|
"privateOutputTokens": [],
|
|
78
93
|
"updatedAt": "2026-01-02T00:00:00.000Z",
|
|
94
|
+
"zones": [
|
|
95
|
+
{
|
|
96
|
+
"chainId": 1424310001,
|
|
97
|
+
"deploymentBlock": 31039238,
|
|
98
|
+
"earnRouter": "0x9144086a79dea966d8bf1b52125ebed8919fc45c",
|
|
99
|
+
},
|
|
100
|
+
],
|
|
79
101
|
}
|
|
80
102
|
`)
|
|
81
103
|
|
|
@@ -10,7 +10,7 @@ const slugSuffix = customAlphabet('0123456789abcdefghijklmnopqrstuvwxyz', 8)
|
|
|
10
10
|
/** Columns of the `earn_vaults` table. */
|
|
11
11
|
export type Table = Omit<
|
|
12
12
|
db_Schema.EarnVault,
|
|
13
|
-
'chainId' | 'privateInputTokens' | 'privateOutputTokens'
|
|
13
|
+
'chainId' | 'privateInputTokens' | 'privateOutputTokens' | 'zones'
|
|
14
14
|
> & {
|
|
15
15
|
/** Bigint chain id, read as a string from pg and written as a number. */
|
|
16
16
|
chainId: ColumnType<string, number, number>
|
|
@@ -18,6 +18,8 @@ export type Table = Omit<
|
|
|
18
18
|
privateInputTokens: JSONColumnType<db_Schema.EarnVault['privateInputTokens'], string, string>
|
|
19
19
|
/** Tokens supported for private redemptions. */
|
|
20
20
|
privateOutputTokens: JSONColumnType<db_Schema.EarnVault['privateOutputTokens'], string, string>
|
|
21
|
+
/** Curated private Zone routes. */
|
|
22
|
+
zones: JSONColumnType<db_Schema.EarnVault['zones'], string, string>
|
|
21
23
|
}
|
|
22
24
|
|
|
23
25
|
/** A stored curated Earn vault. */
|
|
@@ -76,6 +78,7 @@ export async function upsert(db: Db.Db, options: upsert.Options): Promise<Record
|
|
|
76
78
|
slug,
|
|
77
79
|
updatedAt: now,
|
|
78
80
|
vaultAddress: normalize(options.vaultAddress),
|
|
81
|
+
zones: JSON.stringify(normalizeZones(options.zones)),
|
|
79
82
|
})
|
|
80
83
|
.onConflict((oc) =>
|
|
81
84
|
oc.columns(['chainId', 'vaultAddress']).doUpdateSet({
|
|
@@ -85,6 +88,7 @@ export async function upsert(db: Db.Db, options: upsert.Options): Promise<Record
|
|
|
85
88
|
privateOutputTokens: JSON.stringify(options.privateOutputTokens),
|
|
86
89
|
...(options.slug !== undefined ? { slug } : {}),
|
|
87
90
|
updatedAt: now,
|
|
91
|
+
zones: JSON.stringify(normalizeZones(options.zones)),
|
|
88
92
|
}),
|
|
89
93
|
)
|
|
90
94
|
.returningAll()
|
|
@@ -109,6 +113,8 @@ export declare namespace upsert {
|
|
|
109
113
|
slug?: string | undefined
|
|
110
114
|
/** Earn vault address. */
|
|
111
115
|
vaultAddress: Address
|
|
116
|
+
/** Curated private Zone routes. */
|
|
117
|
+
zones: db_Schema.EarnVault['zones']
|
|
112
118
|
}
|
|
113
119
|
}
|
|
114
120
|
|
|
@@ -149,3 +155,7 @@ function toRecord(row: Selectable<Table>): Record {
|
|
|
149
155
|
function normalize(address: string): Address {
|
|
150
156
|
return address.toLowerCase() as Address
|
|
151
157
|
}
|
|
158
|
+
|
|
159
|
+
function normalizeZones(zones: db_Schema.EarnVault['zones']): db_Schema.EarnVault['zones'] {
|
|
160
|
+
return zones.map((zone) => ({ ...zone, earnRouter: normalize(zone.earnRouter) }))
|
|
161
|
+
}
|
|
@@ -363,8 +363,11 @@ describe('deleteOrganization', () => {
|
|
|
363
363
|
expect(await Organizations.deleteOrganization(db, 'org_1')).toBe(true)
|
|
364
364
|
|
|
365
365
|
expect(await Webhooks.listSubscriptions(db, deletedOrg)).toEqual([])
|
|
366
|
-
const active = await Webhooks.
|
|
367
|
-
|
|
366
|
+
const active = await Webhooks.listActiveForChain(db, {
|
|
367
|
+
chainId: 4217,
|
|
368
|
+
eventTypes: ['token:transfer'],
|
|
369
|
+
})
|
|
370
|
+
expect(active.map(({ subscription }) => subscription.id).sort()).toEqual(
|
|
368
371
|
[keptApiKey.id, keptMppSubscription.id].sort(),
|
|
369
372
|
)
|
|
370
373
|
await db.close()
|
|
@@ -225,6 +225,10 @@ export async function get(
|
|
|
225
225
|
return row ? toDelivery(row) : null
|
|
226
226
|
}
|
|
227
227
|
|
|
228
|
+
// Per-statement delete bound: prune runs every minute, so a backlog drains in
|
|
229
|
+
// a few ticks instead of one unbounded DELETE churning the origin.
|
|
230
|
+
const pruneBatchLimit = 50_000
|
|
231
|
+
|
|
228
232
|
/**
|
|
229
233
|
* Deletes deliveries past their retention deadline. Called best-effort from
|
|
230
234
|
* the poller tick.
|
|
@@ -236,7 +240,16 @@ export async function get(
|
|
|
236
240
|
export async function pruneExpired(db: Db.Db, now: string): Promise<number> {
|
|
237
241
|
const result = await db.kysely
|
|
238
242
|
.deleteFrom('webhook_deliveries')
|
|
243
|
+
// Repeated on the outer delete: the row recheck under concurrency re-runs
|
|
244
|
+
// only this predicate, so a row renewed mid-statement survives.
|
|
239
245
|
.where('expiresAt', '<=', now)
|
|
246
|
+
.where('id', 'in', (eb) =>
|
|
247
|
+
eb
|
|
248
|
+
.selectFrom('webhook_deliveries')
|
|
249
|
+
.select('id')
|
|
250
|
+
.where('expiresAt', '<=', now)
|
|
251
|
+
.limit(pruneBatchLimit),
|
|
252
|
+
)
|
|
240
253
|
.executeTakeFirst()
|
|
241
254
|
return Number(result.numDeletedRows)
|
|
242
255
|
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import type * as Webhooks from '../../internal/Webhooks.js'
|
|
2
|
+
import type * as Db from '../Db.js'
|
|
3
|
+
import type * as db_Schema from '../Schema.js'
|
|
4
|
+
|
|
5
|
+
/** Columns of the `webhook_queue_completions` table, derived from `Schema.WebhookQueueCompletion`. */
|
|
6
|
+
export type Table = db_Schema.WebhookQueueCompletion
|
|
7
|
+
|
|
8
|
+
/** A terminal delivery-obligation outcome. */
|
|
9
|
+
export type TerminalStatus = db_Schema.WebhookQueueCompletion['status']
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Reads the unexpired completion for a reference, if any. Rows are written by
|
|
13
|
+
* the queue-event terminal transition and only ever read for dedupe.
|
|
14
|
+
*/
|
|
15
|
+
export async function get(
|
|
16
|
+
db: Db.Db,
|
|
17
|
+
reference: Webhooks.QueueReference,
|
|
18
|
+
now: string,
|
|
19
|
+
): Promise<Table | null> {
|
|
20
|
+
return (
|
|
21
|
+
(await db.kysely
|
|
22
|
+
.selectFrom('webhook_queue_completions')
|
|
23
|
+
.selectAll()
|
|
24
|
+
.where('eventId', '=', reference.eventId)
|
|
25
|
+
.where('subscriptionId', '=', reference.subscriptionId)
|
|
26
|
+
.where('expiresAt', '>', now)
|
|
27
|
+
.executeTakeFirst()) ?? null
|
|
28
|
+
)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Per-statement delete bound: prune runs every minute, so a backlog drains in
|
|
32
|
+
// a few ticks instead of one unbounded DELETE churning the origin.
|
|
33
|
+
const pruneBatchLimit = 50_000
|
|
34
|
+
|
|
35
|
+
/** Deletes expired completions, bounded per call; expiry is filtered at query time, so pruning is space-only. */
|
|
36
|
+
export async function pruneExpired(db: Db.Db, now: string): Promise<number> {
|
|
37
|
+
const result = await db.kysely
|
|
38
|
+
.deleteFrom('webhook_queue_completions')
|
|
39
|
+
// Repeated on the outer delete: the row recheck under concurrency re-runs
|
|
40
|
+
// only this predicate, so a completion renewed mid-statement survives.
|
|
41
|
+
.where('expiresAt', '<=', now)
|
|
42
|
+
.where(({ eb, refTuple, selectFrom }) =>
|
|
43
|
+
eb(
|
|
44
|
+
refTuple('subscriptionId', 'eventId'),
|
|
45
|
+
'in',
|
|
46
|
+
selectFrom('webhook_queue_completions')
|
|
47
|
+
.select(['subscriptionId', 'eventId'])
|
|
48
|
+
.where('expiresAt', '<=', now)
|
|
49
|
+
.limit(pruneBatchLimit)
|
|
50
|
+
.$asTuple('subscriptionId', 'eventId'),
|
|
51
|
+
),
|
|
52
|
+
)
|
|
53
|
+
.executeTakeFirst()
|
|
54
|
+
return Number(result.numDeletedRows)
|
|
55
|
+
}
|
|
@@ -2,19 +2,24 @@ import { type ExpressionBuilder, type Generated, type Insertable, type Selectabl
|
|
|
2
2
|
|
|
3
3
|
import type * as Webhooks from '../../internal/Webhooks.js'
|
|
4
4
|
import type * as Db from '../Db.js'
|
|
5
|
+
import * as WebhookQueueCompletions from './webhookQueueCompletions.js'
|
|
5
6
|
import type * as db_Schema from '../Schema.js'
|
|
6
7
|
|
|
7
8
|
/** Columns of the `webhook_queue_events` table, derived from `Schema.WebhookQueueEvent`. */
|
|
8
9
|
export type Table = Omit<
|
|
9
10
|
db_Schema.WebhookQueueEvent,
|
|
10
|
-
'attemptCount' | 'attemptingAt' | 'nextAttemptAt' | 'status'
|
|
11
|
+
'attemptCount' | 'attemptingAt' | 'expiresAt' | 'nextAttemptAt' | 'observedAt' | 'status'
|
|
11
12
|
> & {
|
|
12
13
|
/** Delivery attempts claimed so far; database default 0. */
|
|
13
14
|
attemptCount: Generated<number>
|
|
14
15
|
/** ISO timestamp of the current claim; database default null. */
|
|
15
16
|
attemptingAt: Generated<string | null>
|
|
17
|
+
/** Legacy dedupe deadline; unwritten since completions took over. */
|
|
18
|
+
expiresAt: Generated<string | null>
|
|
16
19
|
/** ISO retry due time; database default null (due immediately). */
|
|
17
20
|
nextAttemptAt: Generated<string | null>
|
|
21
|
+
/** ISO head-observation stamp; database default null (replays carry none). */
|
|
22
|
+
observedAt: Generated<string | null>
|
|
18
23
|
/** Delivery-obligation lifecycle status; database default `pending`. */
|
|
19
24
|
status: Generated<db_Schema.WebhookQueueEvent['status']>
|
|
20
25
|
}
|
|
@@ -22,12 +27,9 @@ export type Table = Omit<
|
|
|
22
27
|
/** A stored webhook Queue event. */
|
|
23
28
|
export type Record = Selectable<Table>
|
|
24
29
|
|
|
25
|
-
/** A terminal delivery-obligation outcome. */
|
|
26
|
-
export type TerminalStatus = Exclude<db_Schema.WebhookQueueEvent['status'], 'pending'>
|
|
27
|
-
|
|
28
30
|
/**
|
|
29
|
-
* Reads one pending envelope.
|
|
30
|
-
*
|
|
31
|
+
* Reads one pending envelope. The status filter only ever excludes rows
|
|
32
|
+
* completed before the completions split; completions now leave the table.
|
|
31
33
|
*/
|
|
32
34
|
export async function get(
|
|
33
35
|
db: Db.Db,
|
|
@@ -45,38 +47,56 @@ export async function get(
|
|
|
45
47
|
}
|
|
46
48
|
|
|
47
49
|
/**
|
|
48
|
-
* Stages envelopes as fresh obligations.
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
* dedupe row pruning has not yet reclaimed never blocks a replay.
|
|
50
|
+
* Stages envelopes as fresh obligations. Unexpired completions and any
|
|
51
|
+
* existing row suppress the replay; a replay stages only when neither exists,
|
|
52
|
+
* which a completion past its dedupe window guarantees for completed work.
|
|
52
53
|
*/
|
|
53
54
|
export async function ensure(db: Db.Db, rows: readonly Insertable<Table>[]): Promise<number> {
|
|
54
55
|
if (rows.length === 0) return 0
|
|
56
|
+
const values = sql.join(
|
|
57
|
+
rows.map(
|
|
58
|
+
(row) =>
|
|
59
|
+
sql`(${row.subscriptionId}::text, ${row.eventId}::text, ${JSON.stringify(row.envelope)}::jsonb, ${row.createdAt}::text, ${row.nextAttemptAt ?? null}::text, ${row.observedAt ?? null}::text)`,
|
|
60
|
+
),
|
|
61
|
+
)
|
|
62
|
+
// The VALUES source is raw because kysely has no table constructor for it,
|
|
63
|
+
// but both real tables stay in the builder so WithSchemaPlugin qualifies
|
|
64
|
+
// them (previews isolate by schema; raw identifiers would escape it). The
|
|
65
|
+
// completion check must ride the insert: a separate read would let a
|
|
66
|
+
// completion commit between the statements and re-stage just-finished work.
|
|
67
|
+
// `column1..6` are Postgres's default VALUES names, in the row order above;
|
|
68
|
+
// the window comparison uses each row's own staging time, not a shared clock.
|
|
69
|
+
// Conflicts only ever hit pending rows (or pre-split terminal rows during
|
|
70
|
+
// the drain), and both suppress the replay; a legitimate post-expiry replay
|
|
71
|
+
// meets no conflict because its completed row was deleted.
|
|
55
72
|
const result = await db.kysely
|
|
56
73
|
.insertInto('webhook_queue_events')
|
|
57
|
-
.
|
|
58
|
-
.
|
|
59
|
-
|
|
60
|
-
.
|
|
61
|
-
.
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
})
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
74
|
+
.columns(['subscriptionId', 'eventId', 'envelope', 'createdAt', 'nextAttemptAt', 'observedAt'])
|
|
75
|
+
.expression((eb) =>
|
|
76
|
+
eb
|
|
77
|
+
.selectFrom(sql`(VALUES ${values})`.as('v'))
|
|
78
|
+
.select([
|
|
79
|
+
sql`v.column1`.as('subscriptionId'),
|
|
80
|
+
sql`v.column2`.as('eventId'),
|
|
81
|
+
sql`v.column3`.as('envelope'),
|
|
82
|
+
sql`v.column4`.as('createdAt'),
|
|
83
|
+
sql`v.column5`.as('nextAttemptAt'),
|
|
84
|
+
sql`v.column6`.as('observedAt'),
|
|
85
|
+
])
|
|
86
|
+
.where(({ exists, not, selectFrom }) =>
|
|
87
|
+
not(
|
|
88
|
+
exists(
|
|
89
|
+
selectFrom('webhook_queue_completions')
|
|
90
|
+
.select('eventId')
|
|
91
|
+
.where('subscriptionId', '=', sql<string>`v.column1`)
|
|
92
|
+
.where('eventId', '=', sql<string>`v.column2`)
|
|
93
|
+
.where('expiresAt', '>', sql<string>`v.column4`),
|
|
94
|
+
),
|
|
95
|
+
),
|
|
75
96
|
),
|
|
76
97
|
)
|
|
98
|
+
.onConflict((oc) => oc.columns(['subscriptionId', 'eventId']).doNothing())
|
|
77
99
|
.executeTakeFirst()
|
|
78
|
-
// The command tag counts inserted rows plus replaced expired rows; live
|
|
79
|
-
// conflicts fall out of the conditional update and are not counted.
|
|
80
100
|
return Number(result.numInsertedOrUpdatedRows ?? 0n)
|
|
81
101
|
}
|
|
82
102
|
|
|
@@ -108,13 +128,23 @@ export async function claim(
|
|
|
108
128
|
.executeTakeFirst()
|
|
109
129
|
if (claimed) return { record: claimed, type: 'claimed' }
|
|
110
130
|
|
|
131
|
+
// Queue first, completions second: a complete() landing between the reads
|
|
132
|
+
// then still surfaces its completion, so a finished obligation never reads
|
|
133
|
+
// as missing, the one claim outcome alerts treat as a fault.
|
|
111
134
|
const existing = await db.kysely
|
|
112
135
|
.selectFrom('webhook_queue_events')
|
|
113
136
|
.select(['attemptingAt', 'nextAttemptAt', 'status'])
|
|
114
137
|
.where('subscriptionId', '=', reference.subscriptionId)
|
|
115
138
|
.where('eventId', '=', reference.eventId)
|
|
116
139
|
.executeTakeFirst()
|
|
117
|
-
if (!existing)
|
|
140
|
+
if (!existing) {
|
|
141
|
+
// An unexpired completion reads as terminal so claim-outcome metrics keep
|
|
142
|
+
// their meaning; an expired one reads as missing, like a pruned row.
|
|
143
|
+
const completed = await WebhookQueueCompletions.get(db, reference, options.now)
|
|
144
|
+
if (completed) return { status: completed.status, type: 'terminal' }
|
|
145
|
+
return { type: 'missing' }
|
|
146
|
+
}
|
|
147
|
+
// Reachable only for rows completed before the completions split.
|
|
118
148
|
if (existing.status !== 'pending') return { status: existing.status, type: 'terminal' }
|
|
119
149
|
if (existing.attemptingAt !== null && existing.attemptingAt > options.staleBefore)
|
|
120
150
|
return { type: 'attempting' }
|
|
@@ -136,12 +166,12 @@ export declare namespace claim {
|
|
|
136
166
|
| { record: Record; type: 'claimed' }
|
|
137
167
|
| { type: 'missing' }
|
|
138
168
|
| { nextAttemptAt: string | null; type: 'scheduled' }
|
|
139
|
-
| { status: TerminalStatus; type: 'terminal' }
|
|
169
|
+
| { status: WebhookQueueCompletions.TerminalStatus; type: 'terminal' }
|
|
140
170
|
}
|
|
141
171
|
|
|
142
172
|
/**
|
|
143
|
-
* Marks a claimed obligation terminal
|
|
144
|
-
*
|
|
173
|
+
* Marks a claimed obligation terminal: one atomic statement deletes the queue
|
|
174
|
+
* row and writes the insert-only completion that starts the dedupe window.
|
|
145
175
|
* The `attemptingAt` predicate fences stale claimants.
|
|
146
176
|
*/
|
|
147
177
|
export async function complete(
|
|
@@ -150,14 +180,38 @@ export async function complete(
|
|
|
150
180
|
options: complete.Options,
|
|
151
181
|
): Promise<boolean> {
|
|
152
182
|
const result = await db.kysely
|
|
153
|
-
.
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
183
|
+
.with('done', (qb) =>
|
|
184
|
+
qb
|
|
185
|
+
.deleteFrom('webhook_queue_events')
|
|
186
|
+
.where('subscriptionId', '=', reference.subscriptionId)
|
|
187
|
+
.where('eventId', '=', reference.eventId)
|
|
188
|
+
.where('status', '=', 'pending')
|
|
189
|
+
.where('attemptingAt', '=', options.claimedAt)
|
|
190
|
+
.returning(['subscriptionId', 'eventId']),
|
|
191
|
+
)
|
|
192
|
+
.insertInto('webhook_queue_completions')
|
|
193
|
+
.columns(['subscriptionId', 'eventId', 'status', 'expiresAt'])
|
|
194
|
+
.expression((eb) =>
|
|
195
|
+
eb
|
|
196
|
+
.selectFrom('done')
|
|
197
|
+
.select((s) => [
|
|
198
|
+
'subscriptionId',
|
|
199
|
+
'eventId',
|
|
200
|
+
s.val(options.status).as('status'),
|
|
201
|
+
s.val(options.expiresAt).as('expiresAt'),
|
|
202
|
+
]),
|
|
203
|
+
)
|
|
204
|
+
// Re-completing a replayed obligation: the latest delivery outcome wins
|
|
205
|
+
// and retention restarts, matching pre-split replay semantics; doNothing
|
|
206
|
+
// would also report zero rows and fail a fence that won.
|
|
207
|
+
.onConflict((oc) =>
|
|
208
|
+
oc.columns(['subscriptionId', 'eventId']).doUpdateSet((eb) => ({
|
|
209
|
+
expiresAt: eb.ref('excluded.expiresAt'),
|
|
210
|
+
status: eb.ref('excluded.status'),
|
|
211
|
+
})),
|
|
212
|
+
)
|
|
159
213
|
.executeTakeFirst()
|
|
160
|
-
return result.
|
|
214
|
+
return result.numInsertedOrUpdatedRows === 1n
|
|
161
215
|
}
|
|
162
216
|
|
|
163
217
|
export declare namespace complete {
|
|
@@ -165,10 +219,10 @@ export declare namespace complete {
|
|
|
165
219
|
type Options = {
|
|
166
220
|
/** Claim marker returned by {@link claim}; fences stale claimants. */
|
|
167
221
|
claimedAt: string
|
|
168
|
-
/**
|
|
222
|
+
/** ISO retention deadline for the dedupe completion. */
|
|
169
223
|
expiresAt: string
|
|
170
224
|
/** Terminal outcome. */
|
|
171
|
-
status: TerminalStatus
|
|
225
|
+
status: WebhookQueueCompletions.TerminalStatus
|
|
172
226
|
}
|
|
173
227
|
}
|
|
174
228
|
|
|
@@ -269,17 +323,3 @@ export declare namespace pending {
|
|
|
269
323
|
oldestDueAt: string | null
|
|
270
324
|
}
|
|
271
325
|
}
|
|
272
|
-
|
|
273
|
-
/**
|
|
274
|
-
* Deletes terminal dedupe rows past their retention deadline. Pending rows
|
|
275
|
-
* never expire: owed work drains only through terminal transitions
|
|
276
|
-
* (delivery, permanent failure, or the skip after auto-disable).
|
|
277
|
-
*/
|
|
278
|
-
export async function pruneExpired(db: Db.Db, now: string): Promise<number> {
|
|
279
|
-
const result = await db.kysely
|
|
280
|
-
.deleteFrom('webhook_queue_events')
|
|
281
|
-
.where('expiresAt', '<=', now)
|
|
282
|
-
.where('status', '!=', 'pending')
|
|
283
|
-
.executeTakeFirst()
|
|
284
|
-
return Number(result.numDeletedRows)
|
|
285
|
-
}
|