tapimo 0.5.2 → 0.6.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 +13987 -5755
- package/dist/App.d.ts.map +1 -1
- package/dist/App.js.map +1 -1
- package/dist/admin/App.d.ts +910 -412
- package/dist/admin/App.d.ts.map +1 -1
- package/dist/admin/App.js +4 -0
- package/dist/admin/App.js.map +1 -1
- package/dist/admin/apps/early-access.d.ts +148 -0
- package/dist/admin/apps/early-access.d.ts.map +1 -0
- package/dist/admin/apps/early-access.js +133 -0
- package/dist/admin/apps/early-access.js.map +1 -0
- package/dist/admin/apps/verified-tokens.d.ts +643 -118
- package/dist/admin/apps/verified-tokens.d.ts.map +1 -1
- package/dist/admin/apps/verified-tokens.js +214 -1
- package/dist/admin/apps/verified-tokens.js.map +1 -1
- 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 +45 -33
- package/dist/admin/ui.generated.js.map +1 -1
- package/dist/apps/data/App.d.ts +3733 -2428
- package/dist/apps/data/App.d.ts.map +1 -1
- package/dist/apps/data/App.js +11 -6
- package/dist/apps/data/App.js.map +1 -1
- package/dist/apps/data/FundingQuoteProviders.d.ts +34 -1
- package/dist/apps/data/FundingQuoteProviders.d.ts.map +1 -1
- package/dist/apps/data/FundingQuoteProviders.js +193 -2
- package/dist/apps/data/FundingQuoteProviders.js.map +1 -1
- package/dist/apps/data/FxOracle.d.ts +71 -0
- package/dist/apps/data/FxOracle.d.ts.map +1 -0
- package/dist/apps/data/FxOracle.js +97 -0
- package/dist/apps/data/FxOracle.js.map +1 -0
- package/dist/apps/data/index.d.ts +1 -0
- package/dist/apps/data/index.d.ts.map +1 -1
- package/dist/apps/data/index.js +1 -0
- package/dist/apps/data/index.js.map +1 -1
- package/dist/apps/data/routes/activities.d.ts +3575 -1358
- package/dist/apps/data/routes/activities.d.ts.map +1 -1
- package/dist/apps/data/routes/activities.js +230 -88
- package/dist/apps/data/routes/activities.js.map +1 -1
- package/dist/apps/data/routes/addresses.d.ts +963 -342
- package/dist/apps/data/routes/addresses.d.ts.map +1 -1
- package/dist/apps/data/routes/addresses.js +11 -9
- package/dist/apps/data/routes/addresses.js.map +1 -1
- package/dist/apps/data/routes/balances.d.ts +108 -57
- package/dist/apps/data/routes/balances.d.ts.map +1 -1
- package/dist/apps/data/routes/balances.js +88 -45
- package/dist/apps/data/routes/balances.js.map +1 -1
- package/dist/apps/data/routes/coingecko.d.ts +169 -169
- package/dist/apps/data/routes/exchanges.d.ts +952 -201
- package/dist/apps/data/routes/exchanges.d.ts.map +1 -1
- package/dist/apps/data/routes/exchanges.js +518 -76
- package/dist/apps/data/routes/exchanges.js.map +1 -1
- package/dist/apps/data/routes/fee-amm.d.ts +24 -24
- package/dist/apps/data/routes/funding.d.ts +8 -2
- package/dist/apps/data/routes/funding.d.ts.map +1 -1
- package/dist/apps/data/routes/funding.js +32 -6
- package/dist/apps/data/routes/funding.js.map +1 -1
- package/dist/apps/data/routes/indexer.d.ts +5 -5
- package/dist/apps/data/routes/receipts.d.ts +392 -315
- package/dist/apps/data/routes/receipts.d.ts.map +1 -1
- package/dist/apps/data/routes/receipts.js +152 -77
- package/dist/apps/data/routes/receipts.js.map +1 -1
- package/dist/apps/data/routes/tokens.d.ts +166 -173
- package/dist/apps/data/routes/tokens.d.ts.map +1 -1
- package/dist/apps/data/routes/tokens.js +14 -12
- package/dist/apps/data/routes/tokens.js.map +1 -1
- package/dist/apps/data/routes/transactions.d.ts +277 -536
- package/dist/apps/data/routes/transactions.d.ts.map +1 -1
- package/dist/apps/data/routes/transactions.js +96 -77
- package/dist/apps/data/routes/transactions.js.map +1 -1
- package/dist/apps/data/routes/transfers.d.ts +82 -17
- package/dist/apps/data/routes/transfers.d.ts.map +1 -1
- package/dist/apps/data/routes/transfers.js +70 -13
- package/dist/apps/data/routes/transfers.js.map +1 -1
- package/dist/apps/data/routes/valuation.d.ts +307 -0
- package/dist/apps/data/routes/valuation.d.ts.map +1 -0
- package/dist/apps/data/routes/valuation.js +292 -0
- package/dist/apps/data/routes/valuation.js.map +1 -0
- package/dist/apps/data/routes/verified-tokens.d.ts.map +1 -1
- package/dist/apps/data/routes/verified-tokens.js +7 -5
- package/dist/apps/data/routes/verified-tokens.js.map +1 -1
- package/dist/apps/data/routes/webhooks.d.ts +459 -479
- package/dist/apps/data/routes/webhooks.d.ts.map +1 -1
- package/dist/apps/data/routes/webhooks.js +124 -25
- package/dist/apps/data/routes/webhooks.js.map +1 -1
- package/dist/apps/index.d.ts +1 -1
- package/dist/apps/index.d.ts.map +1 -1
- package/dist/apps/index.js +1 -1
- package/dist/apps/index.js.map +1 -1
- package/dist/apps/management/App.d.ts +8606 -1681
- package/dist/apps/management/App.d.ts.map +1 -1
- package/dist/apps/management/App.js +12 -1
- package/dist/apps/management/App.js.map +1 -1
- package/dist/apps/management/routes/api-keys.d.ts +23 -0
- package/dist/apps/management/routes/api-keys.d.ts.map +1 -1
- package/dist/apps/management/routes/api-keys.js +5 -3
- package/dist/apps/management/routes/api-keys.js.map +1 -1
- package/dist/apps/management/routes/billing.d.ts +88 -48
- package/dist/apps/management/routes/billing.d.ts.map +1 -1
- package/dist/apps/management/routes/billing.js +10 -2
- package/dist/apps/management/routes/billing.js.map +1 -1
- package/dist/apps/management/routes/faucet.d.ts +218 -0
- package/dist/apps/management/routes/faucet.d.ts.map +1 -0
- package/dist/apps/management/routes/faucet.js +139 -0
- package/dist/apps/management/routes/faucet.js.map +1 -0
- package/dist/apps/management/routes/invitations.d.ts +121 -121
- package/dist/apps/management/routes/invitations.d.ts.map +1 -1
- package/dist/apps/management/routes/invitations.js +3 -0
- package/dist/apps/management/routes/invitations.js.map +1 -1
- package/dist/apps/management/routes/invite-links.d.ts +197 -168
- package/dist/apps/management/routes/invite-links.d.ts.map +1 -1
- package/dist/apps/management/routes/invite-links.js +26 -1
- package/dist/apps/management/routes/invite-links.js.map +1 -1
- package/dist/apps/management/routes/me.d.ts +2 -0
- package/dist/apps/management/routes/me.d.ts.map +1 -1
- package/dist/apps/management/routes/me.js +4 -0
- package/dist/apps/management/routes/me.js.map +1 -1
- package/dist/apps/management/routes/members.d.ts +49 -49
- package/dist/apps/management/routes/orgs.d.ts +91 -91
- package/dist/apps/management/routes/projects.d.ts +115 -115
- package/dist/apps/management/routes/usage.d.ts +35 -32
- package/dist/apps/management/routes/usage.d.ts.map +1 -1
- package/dist/apps/management/routes/usage.js +4 -0
- package/dist/apps/management/routes/usage.js.map +1 -1
- package/dist/apps/management/routes/verified-token-requests.d.ts +697 -0
- package/dist/apps/management/routes/verified-token-requests.d.ts.map +1 -0
- package/dist/apps/management/routes/verified-token-requests.js +271 -0
- package/dist/apps/management/routes/verified-token-requests.js.map +1 -0
- package/dist/apps/management/routes/webhooks.d.ts +6977 -0
- package/dist/apps/management/routes/webhooks.d.ts.map +1 -0
- package/dist/apps/management/routes/webhooks.js +573 -0
- package/dist/apps/management/routes/webhooks.js.map +1 -0
- package/dist/apps/mcp.d.ts +9 -2
- package/dist/apps/mcp.d.ts.map +1 -1
- package/dist/apps/mcp.js +9 -4
- package/dist/apps/mcp.js.map +1 -1
- package/dist/cli/Cli.d.ts.map +1 -1
- package/dist/cli/Cli.js +7 -2
- package/dist/cli/Cli.js.map +1 -1
- package/dist/db/Db.d.ts +4 -0
- package/dist/db/Db.d.ts.map +1 -1
- package/dist/db/Db.js +83 -0
- package/dist/db/Db.js.map +1 -1
- package/dist/db/Schema.d.ts +41 -0
- package/dist/db/Schema.d.ts.map +1 -1
- package/dist/db/Schema.js +45 -0
- package/dist/db/Schema.js.map +1 -1
- package/dist/db/tables/earlyAccess.d.ts +50 -0
- package/dist/db/tables/earlyAccess.d.ts.map +1 -0
- package/dist/db/tables/earlyAccess.js +63 -0
- package/dist/db/tables/earlyAccess.js.map +1 -0
- package/dist/db/tables/invitations.d.ts +12 -1
- package/dist/db/tables/invitations.d.ts.map +1 -1
- package/dist/db/tables/invitations.js +23 -0
- package/dist/db/tables/invitations.js.map +1 -1
- package/dist/db/tables/inviteLinks.d.ts +10 -2
- package/dist/db/tables/inviteLinks.d.ts.map +1 -1
- package/dist/db/tables/inviteLinks.js +10 -1
- package/dist/db/tables/inviteLinks.js.map +1 -1
- package/dist/db/tables/sponsoredTransactions.d.ts +2 -0
- package/dist/db/tables/sponsoredTransactions.d.ts.map +1 -1
- package/dist/db/tables/sponsoredTransactions.js +2 -0
- package/dist/db/tables/sponsoredTransactions.js.map +1 -1
- package/dist/db/tables/verifiedTokenRequests.d.ts +94 -0
- package/dist/db/tables/verifiedTokenRequests.d.ts.map +1 -0
- package/dist/db/tables/verifiedTokenRequests.js +120 -0
- package/dist/db/tables/verifiedTokenRequests.js.map +1 -0
- package/dist/db/tables/webhookSubscriptions.d.ts +14 -0
- package/dist/db/tables/webhookSubscriptions.d.ts.map +1 -1
- package/dist/db/tables/webhookSubscriptions.js +16 -0
- package/dist/db/tables/webhookSubscriptions.js.map +1 -1
- package/dist/internal/Auth.d.ts +43 -2
- package/dist/internal/Auth.d.ts.map +1 -1
- package/dist/internal/Auth.js +60 -45
- package/dist/internal/Auth.js.map +1 -1
- package/dist/internal/FundingQuotes.d.ts +69 -15
- package/dist/internal/FundingQuotes.d.ts.map +1 -1
- package/dist/internal/FundingQuotes.js +74 -25
- package/dist/internal/FundingQuotes.js.map +1 -1
- package/dist/internal/Log.d.ts +19 -1
- package/dist/internal/Log.d.ts.map +1 -1
- package/dist/internal/Log.js +2 -0
- package/dist/internal/Log.js.map +1 -1
- package/dist/internal/MetricSink.d.ts.map +1 -1
- package/dist/internal/MetricSink.js +9 -0
- package/dist/internal/MetricSink.js.map +1 -1
- package/dist/internal/OpenApi.d.ts +2 -2
- package/dist/internal/OpenApi.d.ts.map +1 -1
- package/dist/internal/OpenApi.js +3 -0
- package/dist/internal/OpenApi.js.map +1 -1
- package/dist/internal/Schema.d.ts +14 -5
- package/dist/internal/Schema.d.ts.map +1 -1
- package/dist/internal/Schema.js +23 -5
- package/dist/internal/Schema.js.map +1 -1
- package/dist/internal/Value.d.ts +18 -0
- package/dist/internal/Value.d.ts.map +1 -1
- package/dist/internal/Value.js +11 -1
- package/dist/internal/Value.js.map +1 -1
- package/dist/internal/VerifiedTokens.d.ts.map +1 -1
- package/dist/internal/VerifiedTokens.js +10 -1
- package/dist/internal/VerifiedTokens.js.map +1 -1
- package/dist/internal/Viem.d.ts +1 -1
- package/dist/internal/Webhooks.d.ts +24 -3
- package/dist/internal/Webhooks.d.ts.map +1 -1
- package/dist/internal/Webhooks.js +34 -8
- package/dist/internal/Webhooks.js.map +1 -1
- package/package.json +2 -2
- package/src/App.ts +9 -1
- package/src/Client.test-d.ts +638 -6
- package/src/admin/App.ts +4 -0
- package/src/admin/apps/early-access.test.ts +125 -0
- package/src/admin/apps/early-access.ts +175 -0
- package/src/admin/apps/verified-tokens.test.ts +201 -0
- package/src/admin/apps/verified-tokens.ts +532 -286
- package/src/admin/ui/plugin.ts +2 -1
- package/src/admin/ui/src/lib/early-access.ts +63 -0
- package/src/admin/ui/src/lib/verified-tokens.ts +79 -0
- package/src/admin/ui/src/routeTree.gen.ts +33 -2
- package/src/admin/ui/src/routes/__root.tsx +2 -1
- package/src/admin/ui/src/routes/early-access.tsx +157 -0
- package/src/admin/ui/src/routes/verified-tokens.tsx +230 -19
- package/src/admin/ui.generated.ts +48 -33
- package/src/apps/data/App.ts +22 -14
- package/src/apps/data/FundingQuoteProviders.test.ts +298 -0
- package/src/apps/data/FundingQuoteProviders.ts +257 -4
- package/src/apps/data/FxOracle.test.ts +118 -0
- package/src/apps/data/FxOracle.ts +153 -0
- package/src/apps/data/index.ts +1 -0
- package/src/apps/data/routes/activities.test.ts +173 -3
- package/src/apps/data/routes/activities.ts +403 -132
- package/src/apps/data/routes/addresses.ts +19 -9
- package/src/apps/data/routes/balances.test.ts +144 -11
- package/src/apps/data/routes/balances.ts +140 -51
- package/src/apps/data/routes/exchanges.test.ts +540 -9
- package/src/apps/data/routes/exchanges.ts +690 -103
- package/src/apps/data/routes/funding.test.ts +84 -0
- package/src/apps/data/routes/funding.ts +38 -6
- package/src/apps/data/routes/receipts.test.ts +125 -23
- package/src/apps/data/routes/receipts.ts +205 -100
- package/src/apps/data/routes/tokens.test.ts +43 -0
- package/src/apps/data/routes/tokens.ts +27 -12
- package/src/apps/data/routes/transactions.test.ts +72 -19
- package/src/apps/data/routes/transactions.ts +162 -92
- package/src/apps/data/routes/transfers.test.ts +146 -4
- package/src/apps/data/routes/transfers.ts +91 -13
- package/src/apps/data/routes/valuation.test.ts +422 -0
- package/src/apps/data/routes/valuation.ts +423 -0
- package/src/apps/data/routes/verified-tokens.ts +14 -2
- package/src/apps/data/routes/webhooks.test.ts +56 -8
- package/src/apps/data/routes/webhooks.ts +144 -27
- package/src/apps/index.ts +1 -1
- package/src/apps/management/App.ts +13 -0
- package/src/apps/management/routes/api-keys.test.ts +87 -0
- package/src/apps/management/routes/api-keys.ts +4 -3
- package/src/apps/management/routes/billing.test.ts +112 -1
- package/src/apps/management/routes/billing.ts +7 -2
- package/src/apps/management/routes/faucet.test.ts +79 -0
- package/src/apps/management/routes/faucet.ts +185 -0
- package/src/apps/management/routes/invitations.test.ts +172 -0
- package/src/apps/management/routes/invitations.ts +3 -0
- package/src/apps/management/routes/invite-links.test.ts +117 -0
- package/src/apps/management/routes/invite-links.ts +43 -1
- package/src/apps/management/routes/me.test.ts +140 -88
- package/src/apps/management/routes/me.ts +6 -0
- package/src/apps/management/routes/usage.test.ts +6 -0
- package/src/apps/management/routes/usage.ts +7 -0
- package/src/apps/management/routes/verified-token-requests.test.ts +377 -0
- package/src/apps/management/routes/verified-token-requests.ts +397 -0
- package/src/apps/management/routes/webhooks.test.ts +426 -0
- package/src/apps/management/routes/webhooks.ts +672 -0
- package/src/apps/mcp.test.ts +13 -0
- package/src/apps/mcp.ts +18 -5
- package/src/cli/Cli.test.ts +36 -0
- package/src/cli/Cli.ts +7 -2
- package/src/db/Db.test.ts +19 -0
- package/src/db/Db.ts +95 -0
- package/src/db/Schema.ts +53 -0
- package/src/db/tables/earlyAccess.ts +86 -0
- package/src/db/tables/invitations.ts +26 -1
- package/src/db/tables/inviteLinks.ts +22 -3
- package/src/db/tables/sponsoredTransactions.ts +4 -0
- package/src/db/tables/verifiedTokenRequests.test.ts +200 -0
- package/src/db/tables/verifiedTokenRequests.ts +182 -0
- package/src/db/tables/webhookSubscriptions.ts +31 -0
- package/src/internal/Auth.ts +73 -45
- package/src/internal/FundingQuotes.test.ts +143 -1
- package/src/internal/FundingQuotes.ts +95 -28
- package/src/internal/Log.test-d.ts +21 -0
- package/src/internal/Log.test.ts +50 -0
- package/src/internal/Log.ts +24 -1
- package/src/internal/MetricSink.test.ts +160 -0
- package/src/internal/MetricSink.ts +10 -0
- package/src/internal/OpenApi.ts +3 -0
- package/src/internal/Schema.ts +50 -5
- package/src/internal/Value.test.ts +20 -0
- package/src/internal/Value.ts +24 -1
- package/src/internal/VerifiedTokens.ts +9 -1
- package/src/internal/Webhooks.test.ts +140 -0
- package/src/internal/Webhooks.ts +53 -9
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import * as Metrics from '../Metrics.js'
|
|
2
|
+
import type * as Log from './Log.js'
|
|
3
|
+
import * as MetricSink from './MetricSink.js'
|
|
4
|
+
|
|
5
|
+
type MetricEvent = {
|
|
6
|
+
name: string
|
|
7
|
+
tags: Metrics.Tags | undefined
|
|
8
|
+
type: 'count' | 'gauge' | 'histogram'
|
|
9
|
+
value: number
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
describe('requests', () => {
|
|
13
|
+
test('records bounded funding provider attempts without caller data', () => {
|
|
14
|
+
const events: MetricEvent[] = []
|
|
15
|
+
let flushes = 0
|
|
16
|
+
const metrics = Metrics.from({
|
|
17
|
+
count(name, value, tags) {
|
|
18
|
+
events.push({ name, tags, type: 'count', value })
|
|
19
|
+
},
|
|
20
|
+
flush() {
|
|
21
|
+
flushes += 1
|
|
22
|
+
},
|
|
23
|
+
gauge(name, value, tags) {
|
|
24
|
+
events.push({ name, tags, type: 'gauge', value })
|
|
25
|
+
},
|
|
26
|
+
histogram(name, value, tags) {
|
|
27
|
+
events.push({ name, tags, type: 'histogram', value })
|
|
28
|
+
},
|
|
29
|
+
})
|
|
30
|
+
const record = MetricSink.requests(metrics)
|
|
31
|
+
|
|
32
|
+
record({
|
|
33
|
+
duration: 100,
|
|
34
|
+
fundingProviderAttempts: [
|
|
35
|
+
{
|
|
36
|
+
durationMs: 10,
|
|
37
|
+
id: 'relay',
|
|
38
|
+
operation: 'quote/v2',
|
|
39
|
+
outcome: 'available',
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
durationMs: 20,
|
|
43
|
+
id: 'rhino',
|
|
44
|
+
operation: 'quote/bridge-swap/public',
|
|
45
|
+
outcome: 'thin',
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
durationMs: 30,
|
|
49
|
+
id: 'squid',
|
|
50
|
+
operation: 'v2/route',
|
|
51
|
+
outcome: 'unavailable',
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
durationMs: 40,
|
|
55
|
+
failure: 'timeout',
|
|
56
|
+
id: 'across',
|
|
57
|
+
operation: 'swap/approval',
|
|
58
|
+
outcome: 'failed',
|
|
59
|
+
},
|
|
60
|
+
],
|
|
61
|
+
level: 'info',
|
|
62
|
+
method: 'GET',
|
|
63
|
+
path: '/v1/funding/quotes',
|
|
64
|
+
principal: {
|
|
65
|
+
id: 'key_private',
|
|
66
|
+
orgId: 'org_private',
|
|
67
|
+
projectId: 'project_private',
|
|
68
|
+
type: 'api_key',
|
|
69
|
+
},
|
|
70
|
+
query: 'sourceAmount=secret',
|
|
71
|
+
requestId: 'request_private',
|
|
72
|
+
route: '/v1/funding/quotes',
|
|
73
|
+
status: 200,
|
|
74
|
+
} satisfies Log.Entry)
|
|
75
|
+
|
|
76
|
+
const funding = events.filter((event) => event.name.startsWith('funding_quote_provider_'))
|
|
77
|
+
expect(funding).toMatchInlineSnapshot(`
|
|
78
|
+
[
|
|
79
|
+
{
|
|
80
|
+
"name": "funding_quote_provider_attempt_count",
|
|
81
|
+
"tags": {
|
|
82
|
+
"outcome": "available",
|
|
83
|
+
"provider": "relay",
|
|
84
|
+
},
|
|
85
|
+
"type": "count",
|
|
86
|
+
"value": 1,
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
"name": "funding_quote_provider_duration_ms",
|
|
90
|
+
"tags": {
|
|
91
|
+
"outcome": "available",
|
|
92
|
+
"provider": "relay",
|
|
93
|
+
},
|
|
94
|
+
"type": "histogram",
|
|
95
|
+
"value": 10,
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
"name": "funding_quote_provider_attempt_count",
|
|
99
|
+
"tags": {
|
|
100
|
+
"outcome": "thin",
|
|
101
|
+
"provider": "rhino",
|
|
102
|
+
},
|
|
103
|
+
"type": "count",
|
|
104
|
+
"value": 1,
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
"name": "funding_quote_provider_duration_ms",
|
|
108
|
+
"tags": {
|
|
109
|
+
"outcome": "thin",
|
|
110
|
+
"provider": "rhino",
|
|
111
|
+
},
|
|
112
|
+
"type": "histogram",
|
|
113
|
+
"value": 20,
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
"name": "funding_quote_provider_attempt_count",
|
|
117
|
+
"tags": {
|
|
118
|
+
"outcome": "unavailable",
|
|
119
|
+
"provider": "squid",
|
|
120
|
+
},
|
|
121
|
+
"type": "count",
|
|
122
|
+
"value": 1,
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
"name": "funding_quote_provider_duration_ms",
|
|
126
|
+
"tags": {
|
|
127
|
+
"outcome": "unavailable",
|
|
128
|
+
"provider": "squid",
|
|
129
|
+
},
|
|
130
|
+
"type": "histogram",
|
|
131
|
+
"value": 30,
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
"name": "funding_quote_provider_attempt_count",
|
|
135
|
+
"tags": {
|
|
136
|
+
"failure": "timeout",
|
|
137
|
+
"outcome": "failed",
|
|
138
|
+
"provider": "across",
|
|
139
|
+
},
|
|
140
|
+
"type": "count",
|
|
141
|
+
"value": 1,
|
|
142
|
+
},
|
|
143
|
+
{
|
|
144
|
+
"name": "funding_quote_provider_duration_ms",
|
|
145
|
+
"tags": {
|
|
146
|
+
"failure": "timeout",
|
|
147
|
+
"outcome": "failed",
|
|
148
|
+
"provider": "across",
|
|
149
|
+
},
|
|
150
|
+
"type": "histogram",
|
|
151
|
+
"value": 40,
|
|
152
|
+
},
|
|
153
|
+
]
|
|
154
|
+
`)
|
|
155
|
+
expect(JSON.stringify(funding)).not.toMatch(
|
|
156
|
+
/key_private|org_private|project_private|request_private|secret/,
|
|
157
|
+
)
|
|
158
|
+
expect(flushes).toBe(1)
|
|
159
|
+
})
|
|
160
|
+
})
|
|
@@ -95,6 +95,16 @@ export function requests(backend: Metrics): RequestSink {
|
|
|
95
95
|
for (const [operation, duration] of Object.entries(entry.timings ?? {}))
|
|
96
96
|
backend.histogram('upstream_duration_ms', duration, { ...tags, operation })
|
|
97
97
|
|
|
98
|
+
for (const attempt of entry.fundingProviderAttempts ?? []) {
|
|
99
|
+
const tags_attempt = {
|
|
100
|
+
...(attempt.failure === undefined ? {} : { failure: attempt.failure }),
|
|
101
|
+
outcome: attempt.outcome,
|
|
102
|
+
provider: attempt.id,
|
|
103
|
+
}
|
|
104
|
+
backend.count('funding_quote_provider_attempt_count', 1, tags_attempt)
|
|
105
|
+
backend.histogram('funding_quote_provider_duration_ms', attempt.durationMs, tags_attempt)
|
|
106
|
+
}
|
|
107
|
+
|
|
98
108
|
backend.flush()
|
|
99
109
|
}
|
|
100
110
|
}
|
package/src/internal/OpenApi.ts
CHANGED
|
@@ -64,6 +64,7 @@ export const errorCodes = {
|
|
|
64
64
|
'address_invalid',
|
|
65
65
|
'token_invalid',
|
|
66
66
|
'symbol_invalid',
|
|
67
|
+
'quote_amount_out_of_range',
|
|
67
68
|
'pair_invalid',
|
|
68
69
|
'pair_id_invalid',
|
|
69
70
|
'transaction_invalid',
|
|
@@ -71,6 +72,7 @@ export const errorCodes = {
|
|
|
71
72
|
'block_invalid',
|
|
72
73
|
'chain_id_invalid',
|
|
73
74
|
'chain_id_unsupported',
|
|
75
|
+
'destination_transition_invalid',
|
|
74
76
|
'url_invalid',
|
|
75
77
|
'api_key_malformed',
|
|
76
78
|
],
|
|
@@ -85,6 +87,7 @@ export const errorCodes = {
|
|
|
85
87
|
'receipt_not_found',
|
|
86
88
|
'order_not_found',
|
|
87
89
|
'pair_not_found',
|
|
90
|
+
'quote_not_available',
|
|
88
91
|
'verified_token_not_found',
|
|
89
92
|
'webhook_not_found',
|
|
90
93
|
'delivery_not_found',
|
package/src/internal/Schema.ts
CHANGED
|
@@ -290,6 +290,35 @@ export const DecimalString = z
|
|
|
290
290
|
z.meta({ examples: ['1000000'] }),
|
|
291
291
|
)
|
|
292
292
|
|
|
293
|
+
/** A self-contained token quantity in base units and human-readable form. */
|
|
294
|
+
export const TokenAmount = describe(
|
|
295
|
+
z.object({
|
|
296
|
+
baseUnits: DecimalString.check(z.describe("The quantity in the token's smallest unit.")),
|
|
297
|
+
currency: z
|
|
298
|
+
.string()
|
|
299
|
+
.check(
|
|
300
|
+
z.describe('The monetary denomination of this quantity.'),
|
|
301
|
+
z.meta({ examples: ['USD'] }),
|
|
302
|
+
),
|
|
303
|
+
decimals: z
|
|
304
|
+
.number()
|
|
305
|
+
.check(
|
|
306
|
+
z.int(),
|
|
307
|
+
z.nonnegative(),
|
|
308
|
+
z.describe('Decimal places used to convert `baseUnits` into `formatted`.'),
|
|
309
|
+
z.meta({ examples: [6] }),
|
|
310
|
+
),
|
|
311
|
+
formatted: z
|
|
312
|
+
.string()
|
|
313
|
+
.check(
|
|
314
|
+
z.regex(/^\d+(\.\d+)?$/),
|
|
315
|
+
z.describe('The quantity rendered in whole token units.'),
|
|
316
|
+
z.meta({ examples: ['1'] }),
|
|
317
|
+
),
|
|
318
|
+
}),
|
|
319
|
+
'A token quantity with its denomination and decimal representation.',
|
|
320
|
+
)
|
|
321
|
+
|
|
293
322
|
/**
|
|
294
323
|
* Minimum page size accepted across list endpoints. Floored at 5 because the
|
|
295
324
|
* indexer rejects signature-decoded `Transfer` queries with `LIMIT < 5`
|
|
@@ -306,6 +335,22 @@ export const Limit = z
|
|
|
306
335
|
z.meta({ examples: [10] }),
|
|
307
336
|
)
|
|
308
337
|
|
|
338
|
+
/** Valuation denomination query parameter shared across valuation-bearing endpoints. */
|
|
339
|
+
export const Denomination = z
|
|
340
|
+
.optional(
|
|
341
|
+
z.pipe(
|
|
342
|
+
z.string().check(z.regex(/^[a-zA-Z]{3}$/)),
|
|
343
|
+
z.transform((value) => value.toUpperCase()),
|
|
344
|
+
),
|
|
345
|
+
)
|
|
346
|
+
.check(
|
|
347
|
+
z.describe(
|
|
348
|
+
'Currency to denominate values in (case-insensitive, e.g. `AUD`). ' +
|
|
349
|
+
'Must be priced by the configured FX oracle. Defaults to `USD`.',
|
|
350
|
+
),
|
|
351
|
+
z.meta({ examples: ['AUD'] }),
|
|
352
|
+
)
|
|
353
|
+
|
|
309
354
|
/** Opaque keyset-pagination cursor query parameter shared across list endpoints. */
|
|
310
355
|
export const Cursor = z
|
|
311
356
|
.optional(z.string())
|
|
@@ -464,17 +509,17 @@ export function timestampBound(noun: string, bound: 'from' | 'to') {
|
|
|
464
509
|
|
|
465
510
|
/**
|
|
466
511
|
* Builds the schema for a comma-separated opt-in query parameter (e.g.
|
|
467
|
-
* `?include=token,
|
|
512
|
+
* `?include=token,totalCount`), parsed into a validated array of the given enum's
|
|
468
513
|
* members. Absent or empty values yield `[]`. Centralizes the splitting,
|
|
469
514
|
* trimming, and OpenAPI array-enum metadata that every list endpoint otherwise
|
|
470
515
|
* repeats.
|
|
471
516
|
*
|
|
472
|
-
* A repeated parameter (`?include=token&include=
|
|
517
|
+
* A repeated parameter (`?include=token&include=totalCount`) arrives from the
|
|
473
518
|
* validator as a `string[]`, so the input accepts both forms and each entry is
|
|
474
|
-
* still comma-split
|
|
519
|
+
* still comma-split, so the two styles compose freely.
|
|
475
520
|
*
|
|
476
|
-
* Pass the endpoint's `Include` enum
|
|
477
|
-
*
|
|
521
|
+
* Pass the endpoint's local `Include` enum so its members drive the parsed type
|
|
522
|
+
* and documentation, plus the parameter description.
|
|
478
523
|
*/
|
|
479
524
|
export function includeQuery<const T extends Readonly<Record<string, string>>>(
|
|
480
525
|
member: z.ZodMiniEnum<T>,
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import * as Value from './Value.js'
|
|
2
|
+
|
|
3
|
+
describe('tokenAmount', () => {
|
|
4
|
+
test('renders a self-contained token quantity', () => {
|
|
5
|
+
expect(
|
|
6
|
+
Value.tokenAmount({
|
|
7
|
+
baseUnits: '1234567',
|
|
8
|
+
currency: 'USD',
|
|
9
|
+
decimals: 6,
|
|
10
|
+
}),
|
|
11
|
+
).toMatchInlineSnapshot(`
|
|
12
|
+
{
|
|
13
|
+
"baseUnits": "1234567",
|
|
14
|
+
"currency": "USD",
|
|
15
|
+
"decimals": 6,
|
|
16
|
+
"formatted": "1.234567",
|
|
17
|
+
}
|
|
18
|
+
`)
|
|
19
|
+
})
|
|
20
|
+
})
|
package/src/internal/Value.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Hex } from 'ox'
|
|
1
|
+
import { Hex, Value as core_Value } from 'ox'
|
|
2
2
|
|
|
3
3
|
/** Coerce a TIDX numeric column (bigint / number / string) to a JS number. */
|
|
4
4
|
export function toNumber(value: unknown) {
|
|
@@ -39,6 +39,29 @@ export function toIntegerString(value: unknown) {
|
|
|
39
39
|
return undefined
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
+
/** Builds a token amount from its base-unit quantity and immutable token metadata. */
|
|
43
|
+
export function tokenAmount(options: tokenAmount.Options) {
|
|
44
|
+
const baseUnits = options.baseUnits.toString()
|
|
45
|
+
return {
|
|
46
|
+
baseUnits,
|
|
47
|
+
currency: options.currency,
|
|
48
|
+
decimals: options.decimals,
|
|
49
|
+
formatted: core_Value.format(BigInt(baseUnits), options.decimals),
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export declare namespace tokenAmount {
|
|
54
|
+
/** Inputs needed to render a self-contained token amount. */
|
|
55
|
+
type Options = {
|
|
56
|
+
/** Quantity in the token's smallest unit. */
|
|
57
|
+
baseUnits: bigint | string
|
|
58
|
+
/** Monetary denomination reported by the token. */
|
|
59
|
+
currency: string
|
|
60
|
+
/** Decimal places used by the token. */
|
|
61
|
+
decimals: number
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
42
65
|
/**
|
|
43
66
|
* Coerce a TIDX numeric column (bigint / number / decimal string) to a minimal
|
|
44
67
|
* `0x`-prefixed hex quantity (`0x0` for zero), matching the JSON-RPC wire
|
|
@@ -535,7 +535,15 @@ export async function snapshot(c: Context<App.Environment>, chainId: ChainId): P
|
|
|
535
535
|
|
|
536
536
|
// Soft-stale: serve immediately, refresh in the background. Never blocks.
|
|
537
537
|
if (cell) {
|
|
538
|
-
|
|
538
|
+
const inflight = refresh(c, Db.get(c.get('dbCached')), chainId, cell)
|
|
539
|
+
// Workers cancel post-response I/O unless kept alive via `waitUntil`;
|
|
540
|
+
// without it the refresh dies mid-query and publishes never propagate.
|
|
541
|
+
// Node has no execution context, and fire-and-forget survives there.
|
|
542
|
+
try {
|
|
543
|
+
c.executionCtx.waitUntil(inflight)
|
|
544
|
+
} catch {
|
|
545
|
+
void inflight
|
|
546
|
+
}
|
|
539
547
|
return cell.snapshot
|
|
540
548
|
}
|
|
541
549
|
|
|
@@ -78,6 +78,45 @@ describe('createSubscription', () => {
|
|
|
78
78
|
)
|
|
79
79
|
})
|
|
80
80
|
|
|
81
|
+
test('counts paused subscriptions toward the live subscription cap', async () => {
|
|
82
|
+
const db = TestApp.database()
|
|
83
|
+
const created = await Webhooks.createSubscription(db, createInput(), { maxPerOwner: 1 })
|
|
84
|
+
await Webhooks.updateSubscription(db, apiKeyOwner, created.id, { status: 'paused' })
|
|
85
|
+
|
|
86
|
+
await expect(
|
|
87
|
+
Webhooks.createSubscription(db, createInput(), { maxPerOwner: 1 }),
|
|
88
|
+
).rejects.toBeInstanceOf(Webhooks.LimitExceededError)
|
|
89
|
+
await Webhooks.deleteSubscription(db, apiKeyOwner, created.id)
|
|
90
|
+
await expect(
|
|
91
|
+
Webhooks.createSubscription(db, createInput(), { maxPerOwner: 1 }),
|
|
92
|
+
).resolves.toMatchObject({ status: 'active' })
|
|
93
|
+
})
|
|
94
|
+
|
|
95
|
+
test('enforces the per-owner cap across concurrent creates', async () => {
|
|
96
|
+
const getDb = TestApp.databaseFactory()
|
|
97
|
+
const results = await Promise.allSettled(
|
|
98
|
+
Array.from({ length: 8 }, () =>
|
|
99
|
+
Webhooks.createSubscription(getDb(), createInput(), { maxPerOwner: 3 }),
|
|
100
|
+
),
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
expect(results.map((result) => result.status).sort()).toMatchInlineSnapshot(`
|
|
104
|
+
[
|
|
105
|
+
"fulfilled",
|
|
106
|
+
"fulfilled",
|
|
107
|
+
"fulfilled",
|
|
108
|
+
"rejected",
|
|
109
|
+
"rejected",
|
|
110
|
+
"rejected",
|
|
111
|
+
"rejected",
|
|
112
|
+
"rejected",
|
|
113
|
+
]
|
|
114
|
+
`)
|
|
115
|
+
for (const result of results)
|
|
116
|
+
if (result.status === 'rejected') expect(result.reason).toBeInstanceOf(Webhooks.LimitExceededError) // prettier-ignore
|
|
117
|
+
expect(await Webhooks.listSubscriptions(getDb(), apiKeyOwner)).toHaveLength(3)
|
|
118
|
+
})
|
|
119
|
+
|
|
81
120
|
test('rejects an undeliverable URL before persisting', async () => {
|
|
82
121
|
const db = TestApp.database()
|
|
83
122
|
await expect(
|
|
@@ -204,6 +243,27 @@ describe('updateSubscription', () => {
|
|
|
204
243
|
`[Webhooks.InvalidUrlError: Webhook URL rejected (protocol): http://10.0.0.1]`,
|
|
205
244
|
)
|
|
206
245
|
})
|
|
246
|
+
|
|
247
|
+
test('requires recreation before activating URL signing for a provider destination', async () => {
|
|
248
|
+
const db = TestApp.database()
|
|
249
|
+
const created = await Webhooks.createSubscription(
|
|
250
|
+
db,
|
|
251
|
+
createInput({
|
|
252
|
+
destination: {
|
|
253
|
+
type: 'slack',
|
|
254
|
+
url: 'https://hooks.slack.com/services/T000/B000/xxxx',
|
|
255
|
+
},
|
|
256
|
+
}),
|
|
257
|
+
)
|
|
258
|
+
|
|
259
|
+
await expect(
|
|
260
|
+
Webhooks.updateSubscription(db, apiKeyOwner, created.id, {
|
|
261
|
+
destination: { type: 'url', url: 'https://hooks.example.com/v2' },
|
|
262
|
+
}),
|
|
263
|
+
).rejects.toThrowErrorMatchingInlineSnapshot(
|
|
264
|
+
`[Webhooks.InvalidDestinationTransitionError: Create a new webhook to change a provider-managed destination to an HTTPS endpoint.]`,
|
|
265
|
+
)
|
|
266
|
+
})
|
|
207
267
|
})
|
|
208
268
|
|
|
209
269
|
describe('listActive', () => {
|
|
@@ -548,6 +608,86 @@ describe('delivery log', () => {
|
|
|
548
608
|
`)
|
|
549
609
|
})
|
|
550
610
|
|
|
611
|
+
test('deliverCurrentAndRecord skips paused and deleted queued subscriptions', async () => {
|
|
612
|
+
const db = TestApp.database()
|
|
613
|
+
const paused = await Webhooks.createSubscription(db, createInput())
|
|
614
|
+
const removed = await Webhooks.createSubscription(db, createInput())
|
|
615
|
+
const pausedEnvelope = Webhooks.buildEnvelope({
|
|
616
|
+
blockNumber: 1,
|
|
617
|
+
data: { amount: '1' },
|
|
618
|
+
logIndex: 0,
|
|
619
|
+
subscription: paused,
|
|
620
|
+
})
|
|
621
|
+
const removedEnvelope = Webhooks.buildEnvelope({
|
|
622
|
+
blockNumber: 1,
|
|
623
|
+
data: { amount: '1' },
|
|
624
|
+
logIndex: 1,
|
|
625
|
+
subscription: removed,
|
|
626
|
+
})
|
|
627
|
+
await Webhooks.updateSubscription(db, apiKeyOwner, paused.id, { status: 'paused' })
|
|
628
|
+
await Webhooks.deleteSubscription(db, apiKeyOwner, removed.id)
|
|
629
|
+
let requests = 0
|
|
630
|
+
const fetch = (async () => {
|
|
631
|
+
requests++
|
|
632
|
+
return new Response(null, { status: 200 })
|
|
633
|
+
}) satisfies typeof globalThis.fetch
|
|
634
|
+
|
|
635
|
+
await expect(
|
|
636
|
+
Webhooks.deliverCurrentAndRecord(db, paused, pausedEnvelope, { fetch }),
|
|
637
|
+
).resolves.toEqual({ status: 'skipped' })
|
|
638
|
+
await expect(
|
|
639
|
+
Webhooks.deliverCurrentAndRecord(db, removed, removedEnvelope, { fetch }),
|
|
640
|
+
).resolves.toEqual({ status: 'skipped' })
|
|
641
|
+
expect(requests).toBe(0)
|
|
642
|
+
})
|
|
643
|
+
|
|
644
|
+
test('deliverCurrentAndRecord uses the current destination and secret', async () => {
|
|
645
|
+
const db = TestApp.database()
|
|
646
|
+
const created = await Webhooks.createSubscription(db, createInput())
|
|
647
|
+
const envelope = Webhooks.buildEnvelope({
|
|
648
|
+
blockNumber: 1,
|
|
649
|
+
data: { amount: '1' },
|
|
650
|
+
logIndex: 0,
|
|
651
|
+
subscription: created,
|
|
652
|
+
})
|
|
653
|
+
const current = await Webhooks.updateSubscription(db, apiKeyOwner, created.id, {
|
|
654
|
+
destination: { type: 'url', url: 'https://hooks.example.com/current' },
|
|
655
|
+
})
|
|
656
|
+
const queued = { ...created, secret: `whsec_${'ff'.repeat(32)}` }
|
|
657
|
+
const captured: { body: string; signature: string | null; url: string } = {
|
|
658
|
+
body: '',
|
|
659
|
+
signature: null,
|
|
660
|
+
url: '',
|
|
661
|
+
}
|
|
662
|
+
const fetch = (async (input, init) => {
|
|
663
|
+
captured.body = typeof init?.body === 'string' ? init.body : ''
|
|
664
|
+
captured.signature = new Headers(init?.headers).get(Webhooks.signatureHeader)
|
|
665
|
+
captured.url = input instanceof Request ? input.url : input.toString()
|
|
666
|
+
return new Response(null, { status: 200 })
|
|
667
|
+
}) satisfies typeof globalThis.fetch
|
|
668
|
+
|
|
669
|
+
const delivery = await Webhooks.deliverCurrentAndRecord(db, queued, envelope, { fetch })
|
|
670
|
+
|
|
671
|
+
expect(delivery.status).toBe('delivered')
|
|
672
|
+
expect(captured.url).toBe('https://hooks.example.com/current')
|
|
673
|
+
expect(
|
|
674
|
+
Webhooks.verify({
|
|
675
|
+
body: captured.body,
|
|
676
|
+
secret: current!.secret,
|
|
677
|
+
signature: captured.signature ?? '',
|
|
678
|
+
tolerance: Number.POSITIVE_INFINITY,
|
|
679
|
+
}),
|
|
680
|
+
).toBe(true)
|
|
681
|
+
expect(
|
|
682
|
+
Webhooks.verify({
|
|
683
|
+
body: captured.body,
|
|
684
|
+
secret: queued.secret,
|
|
685
|
+
signature: captured.signature ?? '',
|
|
686
|
+
tolerance: Number.POSITIVE_INFINITY,
|
|
687
|
+
}),
|
|
688
|
+
).toBe(false)
|
|
689
|
+
})
|
|
690
|
+
|
|
551
691
|
test('deleteSubscription removes the delivery log', async () => {
|
|
552
692
|
const db = TestApp.database()
|
|
553
693
|
const created = await Webhooks.createSubscription(db, createInput())
|
package/src/internal/Webhooks.ts
CHANGED
|
@@ -161,8 +161,8 @@ export type Delivery = {
|
|
|
161
161
|
|
|
162
162
|
/**
|
|
163
163
|
* Creates a subscription, validating its URL and (optionally) enforcing a
|
|
164
|
-
* per-owner cap, then persists it.
|
|
165
|
-
*
|
|
164
|
+
* per-owner cap, then persists it. Capped writes serialize per owner so
|
|
165
|
+
* concurrent requests cannot exceed the limit.
|
|
166
166
|
*/
|
|
167
167
|
export async function createSubscription(
|
|
168
168
|
db: Db.Db,
|
|
@@ -174,11 +174,6 @@ export async function createSubscription(
|
|
|
174
174
|
const now = (options.now ?? (() => new Date()))()
|
|
175
175
|
const iso = now.toISOString()
|
|
176
176
|
|
|
177
|
-
if (options.maxPerOwner !== undefined) {
|
|
178
|
-
const existing = await WebhookSubscriptions.count(db, input.owner, iso)
|
|
179
|
-
if (existing >= options.maxPerOwner) throw new LimitExceededError(options.maxPerOwner)
|
|
180
|
-
}
|
|
181
|
-
|
|
182
177
|
const subscription: Subscription = {
|
|
183
178
|
chainId: input.chainId,
|
|
184
179
|
...(input.context === undefined ? {} : { context: input.context }),
|
|
@@ -197,14 +192,20 @@ export async function createSubscription(
|
|
|
197
192
|
: { expiresAt: new Date(now.getTime() + input.ttl).toISOString() }),
|
|
198
193
|
}
|
|
199
194
|
|
|
200
|
-
|
|
195
|
+
if (options.maxPerOwner !== undefined) {
|
|
196
|
+
const inserted = await WebhookSubscriptions.insertWithinLimit(db, subscription, {
|
|
197
|
+
maxPerOwner: options.maxPerOwner,
|
|
198
|
+
now: iso,
|
|
199
|
+
})
|
|
200
|
+
if (!inserted) throw new LimitExceededError(options.maxPerOwner)
|
|
201
|
+
} else await WebhookSubscriptions.insert(db, subscription)
|
|
201
202
|
return subscription
|
|
202
203
|
}
|
|
203
204
|
|
|
204
205
|
export declare namespace createSubscription {
|
|
205
206
|
/** Options for {@link createSubscription}. */
|
|
206
207
|
type Options = {
|
|
207
|
-
/** Reject creation once the owner has this many subscriptions. */
|
|
208
|
+
/** Reject creation once the owner has this many live subscriptions. */
|
|
208
209
|
maxPerOwner?: number | undefined
|
|
209
210
|
/** Clock used for timestamps (injectable for tests). */
|
|
210
211
|
now?: (() => Date) | undefined
|
|
@@ -268,6 +269,8 @@ export async function updateSubscription(
|
|
|
268
269
|
const current = await getSubscription(db, owner, id)
|
|
269
270
|
if (!current) return null
|
|
270
271
|
|
|
272
|
+
if (current.destination.type !== 'url' && patch.destination?.type === 'url')
|
|
273
|
+
throw new InvalidDestinationTransitionError()
|
|
271
274
|
if (patch.destination !== undefined) WebhookDestination.assertDestination(patch.destination)
|
|
272
275
|
|
|
273
276
|
const now = (options.now ?? (() => new Date()))()
|
|
@@ -550,6 +553,39 @@ export declare namespace deliverAndRecord {
|
|
|
550
553
|
}
|
|
551
554
|
}
|
|
552
555
|
|
|
556
|
+
/**
|
|
557
|
+
* Re-resolves an enqueued subscription before delivery. Missing or inactive
|
|
558
|
+
* subscriptions are skipped; delivery uses the current destination and secret.
|
|
559
|
+
*/
|
|
560
|
+
export async function deliverCurrentAndRecord(
|
|
561
|
+
db: Db.Db,
|
|
562
|
+
queued: Subscription,
|
|
563
|
+
envelope: Envelope,
|
|
564
|
+
options: deliverAndRecord.Options = {},
|
|
565
|
+
): Promise<deliverCurrentAndRecord.Result> {
|
|
566
|
+
const subscription = await getSubscription(db, queued.owner, queued.id)
|
|
567
|
+
if (!subscription || subscription.status !== 'active') return { status: 'skipped' }
|
|
568
|
+
return {
|
|
569
|
+
result: await deliverAndRecord(db, subscription, envelope, options),
|
|
570
|
+
status: 'delivered',
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
export declare namespace deliverCurrentAndRecord {
|
|
575
|
+
/** Authoritative delivery outcome, or a skipped stale queue message. */
|
|
576
|
+
type Result =
|
|
577
|
+
| {
|
|
578
|
+
/** Transport result from the current subscription state. */
|
|
579
|
+
result: WebhookDestination.Result
|
|
580
|
+
/** The current subscription was delivered. */
|
|
581
|
+
status: 'delivered'
|
|
582
|
+
}
|
|
583
|
+
| {
|
|
584
|
+
/** The queued subscription is absent or inactive. */
|
|
585
|
+
status: 'skipped'
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
|
|
553
589
|
/** Builds a delivery-log row from an attempt's envelope and classified result. */
|
|
554
590
|
function buildDelivery(
|
|
555
591
|
input: {
|
|
@@ -783,6 +819,14 @@ export class LimitExceededError extends Error {
|
|
|
783
819
|
}
|
|
784
820
|
}
|
|
785
821
|
|
|
822
|
+
/** Thrown when an update would activate URL signing with an unrecoverable secret. */
|
|
823
|
+
export class InvalidDestinationTransitionError extends Error {
|
|
824
|
+
constructor() {
|
|
825
|
+
super('Create a new webhook to change a provider-managed destination to an HTTPS endpoint.')
|
|
826
|
+
this.name = 'Webhooks.InvalidDestinationTransitionError'
|
|
827
|
+
}
|
|
828
|
+
}
|
|
829
|
+
|
|
786
830
|
/**
|
|
787
831
|
* Thrown when a subscription's `filters` fail to validate against the schema for
|
|
788
832
|
* its `eventType`. The detection path treats stored filters as **fail-closed**:
|