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/internal/Webhooks.ts
CHANGED
|
@@ -3,6 +3,7 @@ import { Hash, Hex } from 'ox'
|
|
|
3
3
|
import type * as app_Webhooks from '../apps/data/routes/webhooks.js'
|
|
4
4
|
import type * as Db from '../db/Db.js'
|
|
5
5
|
import * as WebhookDeliveries from '../db/tables/webhookDeliveries.js'
|
|
6
|
+
import * as WebhookQueueCompletions from '../db/tables/webhookQueueCompletions.js'
|
|
6
7
|
import * as WebhookQueueEvents from '../db/tables/webhookQueueEvents.js'
|
|
7
8
|
import * as WebhookSubscriptions from '../db/tables/webhookSubscriptions.js'
|
|
8
9
|
import type * as Metrics from '../Metrics.js'
|
|
@@ -49,6 +50,16 @@ export type Owner =
|
|
|
49
50
|
type: 'mpp'
|
|
50
51
|
}
|
|
51
52
|
|
|
53
|
+
/** API-key scope applied when managing private-resource subscriptions. */
|
|
54
|
+
export type Access = {
|
|
55
|
+
/** API-key environment. */
|
|
56
|
+
environment: 'production' | 'sandbox'
|
|
57
|
+
/** Project attribution, when the key is project-scoped. */
|
|
58
|
+
projectId?: string | undefined
|
|
59
|
+
/** API-key scopes used to authorize private resource reads. */
|
|
60
|
+
scopes: readonly string[]
|
|
61
|
+
}
|
|
62
|
+
|
|
52
63
|
/**
|
|
53
64
|
* Optional human context describing what a subscription is for. Carried onto
|
|
54
65
|
* every delivered {@link Envelope} and surfaced by destinations (Slack header +
|
|
@@ -73,6 +84,8 @@ export type Subscription = {
|
|
|
73
84
|
createdAt: string
|
|
74
85
|
/** Where matched events are delivered (URL or Slack channel). */
|
|
75
86
|
destination: WebhookDestination.Destination
|
|
87
|
+
/** API-key environment captured when the subscription was created. */
|
|
88
|
+
environment?: 'production' | 'sandbox' | undefined
|
|
76
89
|
/** Event type the subscription listens to. */
|
|
77
90
|
eventType: EventType
|
|
78
91
|
/** ISO expiry timestamp; set for MPP-owned subscriptions (TTL-bounded). */
|
|
@@ -83,15 +96,19 @@ export type Subscription = {
|
|
|
83
96
|
filters: Record<string, unknown>
|
|
84
97
|
/** Subscription id (`wh_…`). */
|
|
85
98
|
id: string
|
|
86
|
-
/** ISO timestamp of the last successful delivery, if any.
|
|
99
|
+
/** ISO timestamp of the last successful delivery, if any. Display only:
|
|
100
|
+
* refreshed at most once per precision window. */
|
|
87
101
|
lastDeliveryAt?: string | undefined
|
|
88
102
|
/** Authenticated owner. */
|
|
89
103
|
owner: Owner
|
|
104
|
+
/** Project attribution captured from the creating API key. */
|
|
105
|
+
projectId?: string | undefined
|
|
90
106
|
/** HMAC signing secret. Returned once at creation; never exposed by reads. */
|
|
91
107
|
secret: string
|
|
92
108
|
/** Lifecycle status. */
|
|
93
109
|
status: Status
|
|
94
|
-
/** ISO timestamp of the last mutation.
|
|
110
|
+
/** ISO timestamp of the last mutation. May lag repeat deliveries; see
|
|
111
|
+
* `lastDeliveryAt`. */
|
|
95
112
|
updatedAt: string
|
|
96
113
|
}
|
|
97
114
|
|
|
@@ -103,12 +120,16 @@ export type CreateInput = {
|
|
|
103
120
|
context?: Context | undefined
|
|
104
121
|
/** Where matched events are delivered. `url` destinations are SSRF-validated. */
|
|
105
122
|
destination: WebhookDestination.Destination
|
|
123
|
+
/** API-key environment used to isolate private resource events. */
|
|
124
|
+
environment?: 'production' | 'sandbox' | undefined
|
|
106
125
|
/** Event type to listen to. */
|
|
107
126
|
eventType: EventType
|
|
108
127
|
/** Event-type-specific filter predicates. */
|
|
109
128
|
filters?: Record<string, unknown> | undefined
|
|
110
129
|
/** Owner derived from the request principal. */
|
|
111
130
|
owner: Owner
|
|
131
|
+
/** Project attribution used to isolate private resource events. */
|
|
132
|
+
projectId?: string | undefined
|
|
112
133
|
/** Time-to-live in milliseconds; set the subscription to expire (MPP TTL). */
|
|
113
134
|
ttl?: number | undefined
|
|
114
135
|
}
|
|
@@ -181,7 +202,7 @@ export async function createSubscription(
|
|
|
181
202
|
const now = (options.now ?? (() => new Date()))()
|
|
182
203
|
const iso = now.toISOString()
|
|
183
204
|
// A one-field checkpoint records the live creation head without claiming it
|
|
184
|
-
// was scanned
|
|
205
|
+
// was scanned; it anchors the fanout boundary for the new subscription.
|
|
185
206
|
const pollerCursor =
|
|
186
207
|
options.startBlockNumber === undefined ? undefined : Cursor.encode([options.startBlockNumber])
|
|
187
208
|
|
|
@@ -190,11 +211,13 @@ export async function createSubscription(
|
|
|
190
211
|
...(input.context === undefined ? {} : { context: input.context }),
|
|
191
212
|
createdAt: iso,
|
|
192
213
|
destination: input.destination,
|
|
214
|
+
...(input.environment === undefined ? {} : { environment: input.environment }),
|
|
193
215
|
eventType: input.eventType,
|
|
194
216
|
failureCount: 0,
|
|
195
217
|
filters: input.filters ?? {},
|
|
196
218
|
id: generateId(now),
|
|
197
219
|
owner: input.owner,
|
|
220
|
+
...(input.projectId === undefined ? {} : { projectId: input.projectId }),
|
|
198
221
|
secret: generateSecret(),
|
|
199
222
|
status: 'active',
|
|
200
223
|
updatedAt: iso,
|
|
@@ -236,8 +259,14 @@ export async function getSubscription(
|
|
|
236
259
|
db: Db.Db,
|
|
237
260
|
owner: Owner,
|
|
238
261
|
id: string,
|
|
262
|
+
options: getSubscription.Options = {},
|
|
239
263
|
): Promise<Subscription | null> {
|
|
240
|
-
return WebhookSubscriptions.get(db, owner, id, new Date().toISOString())
|
|
264
|
+
return WebhookSubscriptions.get(db, owner, id, new Date().toISOString(), options.access)
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
export declare namespace getSubscription {
|
|
268
|
+
/** Private-resource visibility options. */
|
|
269
|
+
type Options = { access?: Access | undefined }
|
|
241
270
|
}
|
|
242
271
|
|
|
243
272
|
/**
|
|
@@ -255,6 +284,8 @@ export async function listSubscriptions(
|
|
|
255
284
|
export declare namespace listSubscriptions {
|
|
256
285
|
/** Options for {@link listSubscriptions}. */
|
|
257
286
|
type Options = {
|
|
287
|
+
/** Private-resource visibility scope. */
|
|
288
|
+
access?: Access | undefined
|
|
258
289
|
/** Return subscriptions older than this id (keyset paging, newest first). */
|
|
259
290
|
cursor?: string | undefined
|
|
260
291
|
/** Maximum subscriptions to return. */
|
|
@@ -269,13 +300,22 @@ export declare namespace listSubscriptions {
|
|
|
269
300
|
* since the per-owner set is bounded by `maxPerOwner`. Feeds the opt-in
|
|
270
301
|
* `meta.totalCount` on `GET /webhooks`.
|
|
271
302
|
*/
|
|
272
|
-
export async function countSubscriptions(
|
|
273
|
-
|
|
303
|
+
export async function countSubscriptions(
|
|
304
|
+
db: Db.Db,
|
|
305
|
+
owner: Owner,
|
|
306
|
+
options: countSubscriptions.Options = {},
|
|
307
|
+
): Promise<number> {
|
|
308
|
+
return WebhookSubscriptions.count(db, owner, new Date().toISOString(), options.access)
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
export declare namespace countSubscriptions {
|
|
312
|
+
/** Private-resource visibility options. */
|
|
313
|
+
type Options = { access?: Access | undefined }
|
|
274
314
|
}
|
|
275
315
|
|
|
276
316
|
/**
|
|
277
317
|
* Patches an owner's subscription, re-validating the URL when changed.
|
|
278
|
-
* Returns null when absent. The status column drives the
|
|
318
|
+
* Returns null when absent. The status column drives the partial active
|
|
279
319
|
* index, so no secondary-index maintenance is needed.
|
|
280
320
|
*/
|
|
281
321
|
export async function updateSubscription(
|
|
@@ -285,7 +325,12 @@ export async function updateSubscription(
|
|
|
285
325
|
patch: PatchInput,
|
|
286
326
|
options: updateSubscription.Options = {},
|
|
287
327
|
): Promise<Subscription | null> {
|
|
288
|
-
const current = await getSubscription(
|
|
328
|
+
const current = await getSubscription(
|
|
329
|
+
db,
|
|
330
|
+
owner,
|
|
331
|
+
id,
|
|
332
|
+
options.access === undefined ? {} : { access: options.access },
|
|
333
|
+
)
|
|
289
334
|
if (!current) return null
|
|
290
335
|
|
|
291
336
|
if (current.destination.type !== 'url' && patch.destination?.type === 'url')
|
|
@@ -307,43 +352,29 @@ export async function updateSubscription(
|
|
|
307
352
|
export declare namespace updateSubscription {
|
|
308
353
|
/** Options for {@link updateSubscription}. */
|
|
309
354
|
type Options = {
|
|
355
|
+
/** Private-resource visibility scope. */
|
|
356
|
+
access?: Access | undefined
|
|
310
357
|
/** Clock used for timestamps (injectable for tests). */
|
|
311
358
|
now?: (() => Date) | undefined
|
|
312
359
|
}
|
|
313
360
|
}
|
|
314
361
|
|
|
315
362
|
/** Deletes an owner's subscription; its delivery rows cascade via the FK. */
|
|
316
|
-
export async function deleteSubscription(
|
|
317
|
-
return WebhookSubscriptions.remove(db, owner, id, new Date().toISOString())
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
/**
|
|
321
|
-
* Lists active subscriptions for a `(chainId, eventType)` stream. Used by the
|
|
322
|
-
* poller; one indexed query (the partial status index replaces the old
|
|
323
|
-
* secondary key space).
|
|
324
|
-
*/
|
|
325
|
-
export async function listActive(
|
|
363
|
+
export async function deleteSubscription(
|
|
326
364
|
db: Db.Db,
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
return
|
|
365
|
+
owner: Owner,
|
|
366
|
+
id: string,
|
|
367
|
+
options: deleteSubscription.Options = {},
|
|
368
|
+
): Promise<boolean> {
|
|
369
|
+
return WebhookSubscriptions.remove(db, owner, id, new Date().toISOString(), options.access)
|
|
332
370
|
}
|
|
333
371
|
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
*/
|
|
338
|
-
export async function listActiveWithCursors(
|
|
339
|
-
db: Db.Db,
|
|
340
|
-
chainId: number,
|
|
341
|
-
eventType: EventType,
|
|
342
|
-
): Promise<{ cursor: string | null; subscription: Subscription }[]> {
|
|
343
|
-
return WebhookSubscriptions.listActive(db, chainId, eventType, new Date().toISOString())
|
|
372
|
+
export declare namespace deleteSubscription {
|
|
373
|
+
/** Private-resource visibility options. */
|
|
374
|
+
type Options = { access?: Access | undefined }
|
|
344
375
|
}
|
|
345
376
|
|
|
346
|
-
/** Reads every live active subscription on a chain in one
|
|
377
|
+
/** Reads every live active subscription on a chain in one query. */
|
|
347
378
|
export async function listActiveForChain(
|
|
348
379
|
db: Db.Db,
|
|
349
380
|
options: listActiveForChain.Options,
|
|
@@ -356,29 +387,10 @@ export async function listActiveForChain(
|
|
|
356
387
|
}
|
|
357
388
|
|
|
358
389
|
export declare namespace listActiveForChain {
|
|
359
|
-
/** Filters for the
|
|
390
|
+
/** Filters for the chain-level subscription read. */
|
|
360
391
|
type Options = Pick<WebhookSubscriptions.listActiveForChain.Options, 'chainId' | 'eventTypes'>
|
|
361
392
|
}
|
|
362
393
|
|
|
363
|
-
/**
|
|
364
|
-
* Reads the freshest cursor block across a chain's active subscriptions, or
|
|
365
|
-
* null when none carry a cursor. Creation checkpoints count at their recorded
|
|
366
|
-
* head, so a brand-new subscription reads as fresh.
|
|
367
|
-
*/
|
|
368
|
-
export async function maxCursorBlock(db: Db.Db, chainId: number): Promise<number | null> {
|
|
369
|
-
const cursors = await WebhookSubscriptions.listActiveCursors(
|
|
370
|
-
db,
|
|
371
|
-
chainId,
|
|
372
|
-
new Date().toISOString(),
|
|
373
|
-
)
|
|
374
|
-
let max: number | null = null
|
|
375
|
-
for (const cursor of cursors) {
|
|
376
|
-
const block = cursorBlock(cursor)
|
|
377
|
-
if (block !== undefined && (max === null || block > max)) max = block
|
|
378
|
-
}
|
|
379
|
-
return max
|
|
380
|
-
}
|
|
381
|
-
|
|
382
394
|
/** Decodes the block component of a subscription cursor, if any. */
|
|
383
395
|
export function cursorBlock(cursor: string | null): number | undefined {
|
|
384
396
|
if (cursor === null) return undefined
|
|
@@ -392,47 +404,23 @@ export async function getCursor(db: Db.Db, subscriptionId: string): Promise<stri
|
|
|
392
404
|
return WebhookSubscriptions.getCursor(db, subscriptionId)
|
|
393
405
|
}
|
|
394
406
|
|
|
395
|
-
/** Writes a cursor only when its stored value matches the expected cursor. */
|
|
396
|
-
export async function compareAndSetCursor(
|
|
397
|
-
db: Db.Db,
|
|
398
|
-
options: compareAndSetCursor.Options,
|
|
399
|
-
): Promise<boolean> {
|
|
400
|
-
return WebhookSubscriptions.compareAndSetCursor(db, options)
|
|
401
|
-
}
|
|
402
|
-
|
|
403
|
-
export declare namespace compareAndSetCursor {
|
|
404
|
-
/** Options for {@link compareAndSetCursor}. */
|
|
405
|
-
type Options = WebhookSubscriptions.compareAndSetCursor.Options
|
|
406
|
-
}
|
|
407
|
-
|
|
408
|
-
/** Writes cursors whose stored values match their expected cursors. */
|
|
409
|
-
export async function compareAndSetCursors(
|
|
410
|
-
db: Db.Db,
|
|
411
|
-
options: compareAndSetCursors.Options,
|
|
412
|
-
): Promise<Set<string>> {
|
|
413
|
-
return WebhookSubscriptions.compareAndSetCursors(db, options)
|
|
414
|
-
}
|
|
415
|
-
|
|
416
|
-
export declare namespace compareAndSetCursors {
|
|
417
|
-
/** Options for {@link compareAndSetCursors}. */
|
|
418
|
-
type Options = WebhookSubscriptions.compareAndSetCursors.Options
|
|
419
|
-
}
|
|
420
|
-
|
|
421
|
-
/** Writes a subscription's keyset cursor. */
|
|
422
|
-
export async function setCursor(db: Db.Db, subscriptionId: string, cursor: string): Promise<void> {
|
|
423
|
-
await WebhookSubscriptions.setCursor(db, subscriptionId, cursor)
|
|
424
|
-
}
|
|
425
|
-
|
|
426
407
|
/**
|
|
427
408
|
* Deletes expired MPP-owned subscriptions (delivery rows cascade) and
|
|
428
|
-
* deliveries past retention. Reads already filter expiry at query time
|
|
429
|
-
* is the janitor reclaiming space,
|
|
409
|
+
* deliveries past retention. Reads already filter expiry at query time; this
|
|
410
|
+
* is the janitor reclaiming space, run best-effort on a schedule.
|
|
430
411
|
*/
|
|
431
|
-
export async function pruneExpired(
|
|
412
|
+
export async function pruneExpired(
|
|
413
|
+
db: Db.Db,
|
|
414
|
+
options: pruneExpired.Options = {},
|
|
415
|
+
): Promise<pruneExpired.Result> {
|
|
432
416
|
const iso = (options.now ?? (() => new Date()))().toISOString()
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
417
|
+
// Sequential on purpose: the subscription delete cascades into the other
|
|
418
|
+
// tables, so overlapping it with their prunes risks deadlocks.
|
|
419
|
+
return {
|
|
420
|
+
completions: await WebhookQueueCompletions.pruneExpired(db, iso),
|
|
421
|
+
deliveries: await WebhookDeliveries.pruneExpired(db, iso),
|
|
422
|
+
subscriptions: await WebhookSubscriptions.pruneExpired(db, iso),
|
|
423
|
+
}
|
|
436
424
|
}
|
|
437
425
|
|
|
438
426
|
export declare namespace pruneExpired {
|
|
@@ -441,6 +429,16 @@ export declare namespace pruneExpired {
|
|
|
441
429
|
/** Clock used for expiry evaluation; injectable for tests. */
|
|
442
430
|
now?: (() => Date) | undefined
|
|
443
431
|
}
|
|
432
|
+
|
|
433
|
+
/** Rows deleted per table; a count pinned at the batch bound means expiry is outpacing pruning. */
|
|
434
|
+
type Result = {
|
|
435
|
+
/** Deleted `webhook_queue_completions` rows. */
|
|
436
|
+
completions: number
|
|
437
|
+
/** Deleted `webhook_deliveries` rows. */
|
|
438
|
+
deliveries: number
|
|
439
|
+
/** Deleted `webhook_subscriptions` rows. */
|
|
440
|
+
subscriptions: number
|
|
441
|
+
}
|
|
444
442
|
}
|
|
445
443
|
|
|
446
444
|
/**
|
|
@@ -449,6 +447,13 @@ export declare namespace pruneExpired {
|
|
|
449
447
|
*/
|
|
450
448
|
export const deliveryRetentionMs = 7 * 24 * 60 * 60 * 1_000
|
|
451
449
|
|
|
450
|
+
/**
|
|
451
|
+
* Completions dedupe window: replays of a completed obligation are suppressed
|
|
452
|
+
* this long. Deliberately its own literal so per-deployment log-retention
|
|
453
|
+
* tuning cannot move it.
|
|
454
|
+
*/
|
|
455
|
+
export const dedupeRetentionMs = 7 * 24 * 60 * 60 * 1_000
|
|
456
|
+
|
|
452
457
|
/** A compact Cloudflare Queue message referencing a durable staged envelope. */
|
|
453
458
|
export type QueueReference = {
|
|
454
459
|
/** Idempotent event id of the staged envelope. */
|
|
@@ -463,8 +468,6 @@ export type QueueEvent = QueueReference & {
|
|
|
463
468
|
createdAt: string
|
|
464
469
|
/** Full envelope loaded by the Queue consumer. */
|
|
465
470
|
envelope: Envelope
|
|
466
|
-
/** ISO timestamp after which an unconsumed envelope may be pruned. */
|
|
467
|
-
expiresAt: string
|
|
468
471
|
}
|
|
469
472
|
|
|
470
473
|
/** A claim newer than this counts as an in-flight attempt; above the 10s destination timeout default. */
|
|
@@ -489,16 +492,17 @@ export async function ensureQueueEvents(
|
|
|
489
492
|
options: ensureQueueEvents.Options = {},
|
|
490
493
|
): Promise<ensureQueueEvents.Result> {
|
|
491
494
|
const now = (options.now ?? (() => new Date()))()
|
|
492
|
-
|
|
495
|
+
// Carried so the delivery worker can measure observation-to-attempt without
|
|
496
|
+
// re-reading the block; replays have no observation and stay null.
|
|
497
|
+
const observedAt =
|
|
498
|
+
options.observedAt === undefined ? null : new Date(options.observedAt).toISOString()
|
|
493
499
|
const events = dispatchables.map(({ envelope, subscription }) => ({
|
|
494
500
|
createdAt: now.toISOString(),
|
|
495
501
|
envelope,
|
|
496
502
|
eventId: envelope.id,
|
|
497
|
-
// Inert while pending; the meaningful dedupe deadline is stamped at
|
|
498
|
-
// terminal transitions.
|
|
499
|
-
expiresAt,
|
|
500
503
|
// Due immediately; the value orders the sweep so it is never null here.
|
|
501
504
|
nextAttemptAt: now.toISOString(),
|
|
505
|
+
observedAt,
|
|
502
506
|
subscriptionId: subscription.id,
|
|
503
507
|
}))
|
|
504
508
|
return {
|
|
@@ -516,12 +520,12 @@ export declare namespace ensureQueueEvents {
|
|
|
516
520
|
subscription: Subscription
|
|
517
521
|
}
|
|
518
522
|
|
|
519
|
-
/**
|
|
523
|
+
/** Staging options. */
|
|
520
524
|
type Options = {
|
|
521
525
|
/** Clock used for staging timestamps. */
|
|
522
526
|
now?: (() => Date) | undefined
|
|
523
|
-
/**
|
|
524
|
-
|
|
527
|
+
/** Epoch ms the head owing these events was observed; absent on replays. */
|
|
528
|
+
observedAt?: number | undefined
|
|
525
529
|
}
|
|
526
530
|
|
|
527
531
|
/** Created-row count plus references for every requested obligation. */
|
|
@@ -557,7 +561,7 @@ export function completeQueueEvent(
|
|
|
557
561
|
): Promise<boolean> {
|
|
558
562
|
return WebhookQueueEvents.complete(db, reference, {
|
|
559
563
|
...options,
|
|
560
|
-
expiresAt: new Date(Date.now() +
|
|
564
|
+
expiresAt: new Date(Date.now() + dedupeRetentionMs).toISOString(),
|
|
561
565
|
})
|
|
562
566
|
}
|
|
563
567
|
|
|
@@ -567,7 +571,7 @@ export declare namespace completeQueueEvent {
|
|
|
567
571
|
/** Claim marker from the claimed record's `attemptingAt`. */
|
|
568
572
|
claimedAt: string
|
|
569
573
|
/** Terminal obligation outcome. */
|
|
570
|
-
status:
|
|
574
|
+
status: WebhookQueueCompletions.TerminalStatus
|
|
571
575
|
}
|
|
572
576
|
}
|
|
573
577
|
|
|
@@ -652,10 +656,13 @@ export async function deliverClaimedAndRecord(
|
|
|
652
656
|
new Date().toISOString(),
|
|
653
657
|
)
|
|
654
658
|
if (!subscription || subscription.status !== 'active') return { status: 'skipped' }
|
|
659
|
+
// A staged row predating the observation column, or a replay, reads null.
|
|
660
|
+
const observedAt = claimed.observedAt ? Date.parse(claimed.observedAt) : Number.NaN
|
|
655
661
|
return {
|
|
656
662
|
result: await deliverAndRecord(db, subscription, claimed.envelope, {
|
|
657
663
|
...options,
|
|
658
664
|
dequeuedAt,
|
|
665
|
+
...(Number.isNaN(observedAt) ? {} : { observedAt }),
|
|
659
666
|
subscriptionReadMs: Date.now() - subscriptionReadStartedAt,
|
|
660
667
|
}),
|
|
661
668
|
status: 'delivered',
|
|
@@ -667,6 +674,8 @@ export declare namespace deliverClaimedAndRecord {
|
|
|
667
674
|
type Claimed = {
|
|
668
675
|
/** Immutable event envelope to deliver. */
|
|
669
676
|
envelope: Envelope
|
|
677
|
+
/** ISO head-observation stamp carried by the staged row; null on replays. */
|
|
678
|
+
observedAt?: string | null | undefined
|
|
670
679
|
/** Subscription re-resolved for current status and secret. */
|
|
671
680
|
subscriptionId: string
|
|
672
681
|
}
|
|
@@ -714,7 +723,7 @@ export async function recordDelivery(
|
|
|
714
723
|
): Promise<void> {
|
|
715
724
|
const ttl = options.ttl ?? deliveryRetentionMs
|
|
716
725
|
// The retention deadline replaces the old store TTL: reads filter on it at
|
|
717
|
-
// query time and
|
|
726
|
+
// query time and pruning reclaims past it. Anchored to write time (not the
|
|
718
727
|
// row's `createdAt`, which tests pin) — exactly like the old `put` TTL.
|
|
719
728
|
const expiresAt = new Date(Date.now() + ttl).toISOString()
|
|
720
729
|
await WebhookDeliveries.insert(db, delivery, expiresAt)
|
|
@@ -772,14 +781,21 @@ export async function getDelivery(
|
|
|
772
781
|
|
|
773
782
|
/**
|
|
774
783
|
* Default number of consecutive delivery failures after which a subscription
|
|
775
|
-
* auto-disables (and is dropped from the
|
|
784
|
+
* auto-disables (and is dropped from the partial active index).
|
|
776
785
|
*/
|
|
777
786
|
export const maxFailures = 10
|
|
778
787
|
|
|
788
|
+
/**
|
|
789
|
+
* How stale `lastDeliveryAt` may get before a success rewrites it. Read only
|
|
790
|
+
* for display, so exactness is traded for not rewriting the row per delivery.
|
|
791
|
+
*/
|
|
792
|
+
const lastDeliveryPrecisionMs = 60_000
|
|
793
|
+
|
|
779
794
|
/**
|
|
780
795
|
* Records a successful delivery: resets the consecutive-failure counter and
|
|
781
|
-
*
|
|
782
|
-
*
|
|
796
|
+
* refreshes `lastDeliveryAt` at most once per precision window. Returns the
|
|
797
|
+
* written row, or the caller's snapshot when the write was skipped or the row
|
|
798
|
+
* deleted.
|
|
783
799
|
*/
|
|
784
800
|
export async function recordSuccess(
|
|
785
801
|
db: Db.Db,
|
|
@@ -787,11 +803,13 @@ export async function recordSuccess(
|
|
|
787
803
|
options: recordSuccess.Options = {},
|
|
788
804
|
): Promise<Subscription> {
|
|
789
805
|
const now = (options.now ?? (() => new Date()))()
|
|
790
|
-
const
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
806
|
+
const next = await WebhookSubscriptions.recordSuccess(db, subscription.id, {
|
|
807
|
+
now: now.toISOString(),
|
|
808
|
+
staleBefore: new Date(now.getTime() - lastDeliveryPrecisionMs).toISOString(),
|
|
809
|
+
})
|
|
810
|
+
// No row means the write was skipped or the row was deleted; neither
|
|
811
|
+
// resurrects, so keep the caller's timestamps.
|
|
812
|
+
return next ?? { ...subscription, failureCount: 0 }
|
|
795
813
|
}
|
|
796
814
|
|
|
797
815
|
export declare namespace recordSuccess {
|
|
@@ -805,8 +823,10 @@ export declare namespace recordSuccess {
|
|
|
805
823
|
/**
|
|
806
824
|
* Records a failed delivery: atomically increments the consecutive-failure
|
|
807
825
|
* counter and, once it reaches `maxFailures`, sets `status: 'disabled'` so the
|
|
808
|
-
* subscription drops out of the
|
|
809
|
-
*
|
|
826
|
+
* subscription drops out of the partial active index (surfaced via the API).
|
|
827
|
+
* Once disabled at the cap the row is left alone, so an outage's in-flight
|
|
828
|
+
* failures stop contending on its lock. Returns the written row, or the
|
|
829
|
+
* caller's snapshot advanced optimistically.
|
|
810
830
|
*/
|
|
811
831
|
export async function recordFailure(
|
|
812
832
|
db: Db.Db,
|
|
@@ -818,14 +838,14 @@ export async function recordFailure(
|
|
|
818
838
|
const max = options.maxFailures ?? maxFailures
|
|
819
839
|
const next = await WebhookSubscriptions.recordFailure(db, subscription.id, max, iso)
|
|
820
840
|
if (next) return next
|
|
821
|
-
//
|
|
822
|
-
//
|
|
823
|
-
|
|
841
|
+
// No row means skipped (already disabled at the cap) or deleted; neither
|
|
842
|
+
// resurrects, so keep the caller's timestamps. Capped so chained skips
|
|
843
|
+
// cannot grow the counter past what the store would hold.
|
|
844
|
+
const failureCount = Math.min(subscription.failureCount + 1, max)
|
|
824
845
|
return {
|
|
825
846
|
...subscription,
|
|
826
847
|
failureCount,
|
|
827
848
|
...(failureCount >= max ? { status: 'disabled' as const } : {}),
|
|
828
|
-
updatedAt: iso,
|
|
829
849
|
}
|
|
830
850
|
}
|
|
831
851
|
|
|
@@ -868,23 +888,32 @@ export async function deliverAndRecord(
|
|
|
868
888
|
return { error: 'delivery failed before request', ok: false }
|
|
869
889
|
}
|
|
870
890
|
})()
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
const
|
|
875
|
-
|
|
891
|
+
// Overlapped: the two writes touch different rows and the log carries the
|
|
892
|
+
// immutable pre-attempt subscription, so it never reads the state write's
|
|
893
|
+
// result. Sequentially they cost two Postgres round trips per delivery.
|
|
894
|
+
const writeStartedAt = Date.now()
|
|
876
895
|
// Append a delivery-log row. Best-effort: a logging failure must never change
|
|
877
896
|
// the delivery outcome the caller (Queue consumer / inline path) acts on.
|
|
878
|
-
const
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
897
|
+
const deliveryLog = (async () => {
|
|
898
|
+
try {
|
|
899
|
+
await recordDelivery(
|
|
900
|
+
db,
|
|
901
|
+
buildDelivery({ attempt, envelope, result, subscription }, options),
|
|
902
|
+
options.deliveryRetentionMs === undefined ? {} : { ttl: options.deliveryRetentionMs },
|
|
903
|
+
)
|
|
904
|
+
} catch {
|
|
905
|
+
// delivery logging is best-effort
|
|
906
|
+
}
|
|
907
|
+
return Date.now() - writeStartedAt
|
|
908
|
+
})()
|
|
909
|
+
const stateWrite = (async () => {
|
|
910
|
+
if (result.ok) await recordSuccess(db, subscription, options)
|
|
911
|
+
else await recordFailure(db, subscription, options)
|
|
912
|
+
return Date.now() - writeStartedAt
|
|
913
|
+
})()
|
|
914
|
+
// A state-write rejection still propagates to the caller's retry path; the
|
|
915
|
+
// log promise never rejects, so it cannot mask it or go unhandled.
|
|
916
|
+
const [stateWriteMs, deliveryLogMs] = await Promise.all([stateWrite, deliveryLog])
|
|
888
917
|
const completedAt = Date.now()
|
|
889
918
|
|
|
890
919
|
// Emit after all delivery work so the timings cover every stage. The
|
|
@@ -901,19 +930,34 @@ export async function deliverAndRecord(
|
|
|
901
930
|
subscription,
|
|
902
931
|
attempt,
|
|
903
932
|
timings: {
|
|
904
|
-
deliveryLogMs
|
|
933
|
+
deliveryLogMs,
|
|
905
934
|
envelopeToQueueMs:
|
|
906
935
|
options.queuedAt === undefined || Number.isNaN(envelopeAt)
|
|
907
936
|
? undefined
|
|
908
937
|
: Math.max(0, options.queuedAt - envelopeAt),
|
|
909
938
|
endToEndMs: eventAt === undefined ? undefined : Math.max(0, completedAt - eventAt),
|
|
939
|
+
// The attempt leaving, not bookkeeping finishing: everything after
|
|
940
|
+
// `startedAt` is either the subscriber's time or our own recording,
|
|
941
|
+
// neither of which delays the event reaching them. Gated on a request
|
|
942
|
+
// actually being made, so a failure before send (formatting, invalid
|
|
943
|
+
// destination) does not report an attempt that never left.
|
|
944
|
+
eventToAttemptMs:
|
|
945
|
+
eventAt === undefined || result.durationMs === undefined
|
|
946
|
+
? undefined
|
|
947
|
+
: Math.max(0, startedAt - eventAt),
|
|
910
948
|
eventToEnvelopeMs:
|
|
911
949
|
eventAt === undefined || Number.isNaN(envelopeAt)
|
|
912
950
|
? undefined
|
|
913
951
|
: Math.max(0, envelopeAt - eventAt),
|
|
952
|
+
observedToAttemptMs:
|
|
953
|
+
options.observedAt === undefined || result.durationMs === undefined
|
|
954
|
+
? undefined
|
|
955
|
+
: Math.max(0, startedAt - options.observedAt),
|
|
956
|
+
preflightMs: Math.max(0, startedAt - dequeuedAt),
|
|
914
957
|
processingMs: Math.max(0, completedAt - dequeuedAt),
|
|
915
958
|
queueWaitMs:
|
|
916
959
|
options.queuedAt === undefined ? undefined : Math.max(0, dequeuedAt - options.queuedAt),
|
|
960
|
+
settleMs: Math.max(0, completedAt - writeStartedAt),
|
|
917
961
|
stateWriteMs,
|
|
918
962
|
subscriptionReadMs: options.subscriptionReadMs,
|
|
919
963
|
},
|
|
@@ -960,6 +1004,8 @@ export declare namespace deliverAndRecord {
|
|
|
960
1004
|
metrics?: Metrics.Metrics | undefined
|
|
961
1005
|
/** Clock used for timestamps; injectable for tests. */
|
|
962
1006
|
now?: (() => Date) | undefined
|
|
1007
|
+
/** Epoch ms the head owing this event was observed; absent on replays. */
|
|
1008
|
+
observedAt?: number | undefined
|
|
963
1009
|
/** Cloudflare Queue delivery attempt, starting at one; absent for inline delivery paths. */
|
|
964
1010
|
queueAttempt?: number | undefined
|
|
965
1011
|
/** Queue enqueue time as epoch milliseconds, used to isolate queue wait. */
|
|
@@ -1044,8 +1090,11 @@ function buildDelivery(
|
|
|
1044
1090
|
* re-deliveries are dedupable by the receiver.
|
|
1045
1091
|
*/
|
|
1046
1092
|
export function eventId(options: eventId.Options): string {
|
|
1047
|
-
|
|
1048
|
-
|
|
1093
|
+
return eventIdFromKey({
|
|
1094
|
+
chainId: options.chainId,
|
|
1095
|
+
eventType: options.eventType,
|
|
1096
|
+
key: `${options.blockNumber}:${options.logIndex}`,
|
|
1097
|
+
})
|
|
1049
1098
|
}
|
|
1050
1099
|
|
|
1051
1100
|
export declare namespace eventId {
|
|
@@ -1062,6 +1111,24 @@ export declare namespace eventId {
|
|
|
1062
1111
|
}
|
|
1063
1112
|
}
|
|
1064
1113
|
|
|
1114
|
+
/** Computes a stable event id from an event-type-specific identity key. */
|
|
1115
|
+
export function eventIdFromKey(options: eventIdFromKey.Options): string {
|
|
1116
|
+
const input = `${options.chainId}:${options.eventType}:${options.key}`
|
|
1117
|
+
return `evt_${Hash.sha256(Hex.fromString(input)).slice(2)}`
|
|
1118
|
+
}
|
|
1119
|
+
|
|
1120
|
+
export declare namespace eventIdFromKey {
|
|
1121
|
+
/** Stable identity inputs for a webhook event. */
|
|
1122
|
+
type Options = {
|
|
1123
|
+
/** Chain the event belongs to. */
|
|
1124
|
+
chainId: number
|
|
1125
|
+
/** Event type. */
|
|
1126
|
+
eventType: EventType
|
|
1127
|
+
/** Stable event-type-specific identity. */
|
|
1128
|
+
key: string
|
|
1129
|
+
}
|
|
1130
|
+
}
|
|
1131
|
+
|
|
1065
1132
|
/** The JSON envelope delivered to subscribers. */
|
|
1066
1133
|
export type Envelope = {
|
|
1067
1134
|
/** Chain the event occurred on. */
|
|
@@ -1115,6 +1182,38 @@ export declare namespace buildEnvelope {
|
|
|
1115
1182
|
}
|
|
1116
1183
|
}
|
|
1117
1184
|
|
|
1185
|
+
/** Builds a delivery envelope from a stable application-event identity. */
|
|
1186
|
+
export function buildKeyedEnvelope(options: buildKeyedEnvelope.Options): Envelope {
|
|
1187
|
+
const { subscription } = options
|
|
1188
|
+
return {
|
|
1189
|
+
chainId: subscription.chainId,
|
|
1190
|
+
...(subscription.context === undefined ? {} : { context: subscription.context }),
|
|
1191
|
+
createdAt: (options.createdAt ?? new Date()).toISOString(),
|
|
1192
|
+
data: options.data,
|
|
1193
|
+
id: eventIdFromKey({
|
|
1194
|
+
chainId: subscription.chainId,
|
|
1195
|
+
eventType: subscription.eventType,
|
|
1196
|
+
key: options.key,
|
|
1197
|
+
}),
|
|
1198
|
+
subscriptionId: subscription.id,
|
|
1199
|
+
type: subscription.eventType,
|
|
1200
|
+
}
|
|
1201
|
+
}
|
|
1202
|
+
|
|
1203
|
+
export declare namespace buildKeyedEnvelope {
|
|
1204
|
+
/** Application-event envelope fields. */
|
|
1205
|
+
type Options = {
|
|
1206
|
+
/** Timestamp to stamp on the envelope; defaults to now. */
|
|
1207
|
+
createdAt?: Date | undefined
|
|
1208
|
+
/** Public event payload. */
|
|
1209
|
+
data: unknown
|
|
1210
|
+
/** Stable event-type-specific identity. */
|
|
1211
|
+
key: string
|
|
1212
|
+
/** Subscription the event matched. */
|
|
1213
|
+
subscription: Subscription
|
|
1214
|
+
}
|
|
1215
|
+
}
|
|
1216
|
+
|
|
1118
1217
|
/**
|
|
1119
1218
|
* Builds a synthetic `ping` envelope so an owner can test a subscription's
|
|
1120
1219
|
* endpoint (connectivity, TLS, signature verification) without waiting for a real
|