tapimo 0.9.0 → 0.10.1
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/ApiKey.d.ts +2 -1
- package/dist/ApiKey.d.ts.map +1 -1
- package/dist/ApiKeys.d.ts +4 -2
- package/dist/ApiKeys.d.ts.map +1 -1
- package/dist/ApiKeys.js +26 -1
- package/dist/ApiKeys.js.map +1 -1
- package/dist/App.d.ts +4292 -3708
- package/dist/App.d.ts.map +1 -1
- package/dist/App.js +1 -0
- package/dist/App.js.map +1 -1
- package/dist/Webhooks.d.ts +21 -20
- package/dist/Webhooks.d.ts.map +1 -1
- package/dist/Webhooks.js +66 -23
- package/dist/Webhooks.js.map +1 -1
- package/dist/admin/App.d.ts +91 -84
- package/dist/admin/App.d.ts.map +1 -1
- package/dist/admin/apps/api-keys.d.ts +26 -17
- package/dist/admin/apps/api-keys.d.ts.map +1 -1
- package/dist/admin/apps/api-keys.js +6 -0
- package/dist/admin/apps/api-keys.js.map +1 -1
- package/dist/admin/apps/organizations.d.ts +4 -2
- package/dist/admin/apps/organizations.d.ts.map +1 -1
- package/dist/admin/apps/verified-tokens.d.ts +168 -168
- package/dist/admin/ui.generated.d.ts +1 -1
- package/dist/admin/ui.generated.js +69 -69
- package/dist/admin/ui.generated.js.map +1 -1
- package/dist/analytics/Analytics.d.ts.map +1 -1
- package/dist/analytics/Analytics.js +26 -0
- package/dist/analytics/Analytics.js.map +1 -1
- package/dist/apps/Zones.d.ts +4280 -84
- package/dist/apps/Zones.d.ts.map +1 -1
- package/dist/apps/Zones.js +30 -10
- package/dist/apps/Zones.js.map +1 -1
- package/dist/apps/data/App.d.ts +5799 -4929
- package/dist/apps/data/App.d.ts.map +1 -1
- package/dist/apps/data/FxOracle.d.ts +4 -5
- package/dist/apps/data/FxOracle.d.ts.map +1 -1
- package/dist/apps/data/FxOracle.js +19 -4
- package/dist/apps/data/FxOracle.js.map +1 -1
- package/dist/apps/data/routes/activities.d.ts +3391 -492
- package/dist/apps/data/routes/activities.d.ts.map +1 -1
- package/dist/apps/data/routes/activities.js +372 -29
- package/dist/apps/data/routes/activities.js.map +1 -1
- package/dist/apps/data/routes/addresses.d.ts +853 -339
- package/dist/apps/data/routes/addresses.d.ts.map +1 -1
- package/dist/apps/data/routes/balances.d.ts +10 -10
- package/dist/apps/data/routes/balances.d.ts.map +1 -1
- package/dist/apps/data/routes/balances.js +145 -83
- package/dist/apps/data/routes/balances.js.map +1 -1
- package/dist/apps/data/routes/blocks.d.ts +123 -123
- package/dist/apps/data/routes/coingecko.d.ts +169 -169
- package/dist/apps/data/routes/earn.d.ts +455 -1
- package/dist/apps/data/routes/earn.d.ts.map +1 -1
- package/dist/apps/data/routes/earn.js +369 -45
- package/dist/apps/data/routes/earn.js.map +1 -1
- package/dist/apps/data/routes/exchanges.d.ts.map +1 -1
- package/dist/apps/data/routes/exchanges.js +114 -118
- 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/indexer.d.ts +35 -34
- package/dist/apps/data/routes/indexer.d.ts.map +1 -1
- package/dist/apps/data/routes/indexer.js +3 -2
- package/dist/apps/data/routes/indexer.js.map +1 -1
- package/dist/apps/data/routes/receipts.d.ts.map +1 -1
- package/dist/apps/data/routes/receipts.js +68 -47
- package/dist/apps/data/routes/receipts.js.map +1 -1
- package/dist/apps/data/routes/rpc.d.ts +31 -31
- package/dist/apps/data/routes/rpc.d.ts.map +1 -1
- package/dist/apps/data/routes/rpc.js +5 -1
- 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/tokens.d.ts +461 -459
- package/dist/apps/data/routes/tokens.d.ts.map +1 -1
- package/dist/apps/data/routes/transactions.d.ts +313 -305
- package/dist/apps/data/routes/transactions.d.ts.map +1 -1
- package/dist/apps/data/routes/transactions.js +25 -23
- package/dist/apps/data/routes/transactions.js.map +1 -1
- package/dist/apps/data/routes/transfers.d.ts +108 -86
- package/dist/apps/data/routes/transfers.d.ts.map +1 -1
- package/dist/apps/data/routes/transfers.js +618 -105
- 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/valuation.d.ts.map +1 -1
- package/dist/apps/data/routes/valuation.js +2 -2
- package/dist/apps/data/routes/valuation.js.map +1 -1
- package/dist/apps/data/routes/webhooks.d.ts +519 -868
- package/dist/apps/data/routes/webhooks.d.ts.map +1 -1
- package/dist/apps/data/routes/webhooks.js +642 -353
- package/dist/apps/data/routes/webhooks.js.map +1 -1
- package/dist/apps/data/routes/zones.d.ts +31 -31
- package/dist/apps/data/routes/zones.d.ts.map +1 -1
- package/dist/apps/data/routes/zones.js +4 -2
- package/dist/apps/data/routes/zones.js.map +1 -1
- package/dist/apps/funding/routes/deposit-addresses.js +1 -1
- package/dist/apps/funding/routes/deposit-addresses.js.map +1 -1
- package/dist/apps/funding/routes/providers.d.ts +13 -13
- package/dist/apps/funding/routes/quotes.d.ts +89 -89
- package/dist/apps/funding/routes/transfers.d.ts +36 -36
- package/dist/apps/funding/routes/transfers.js +1 -1
- package/dist/apps/funding/routes/transfers.js.map +1 -1
- package/dist/apps/management/App.d.ts +11 -309
- package/dist/apps/management/App.d.ts.map +1 -1
- package/dist/apps/management/routes/api-keys.d.ts +74 -74
- package/dist/apps/management/routes/billing.js +1 -1
- package/dist/apps/management/routes/billing.js.map +1 -1
- package/dist/apps/management/routes/faucet.d.ts +35 -35
- package/dist/apps/management/routes/faucet.js +2 -2
- package/dist/apps/management/routes/faucet.js.map +1 -1
- package/dist/apps/management/routes/invitations.d.ts +134 -134
- package/dist/apps/management/routes/invite-links.d.ts +80 -80
- package/dist/apps/management/routes/members.d.ts +69 -69
- package/dist/apps/management/routes/orgs.d.ts +98 -98
- package/dist/apps/management/routes/projects.d.ts +60 -60
- package/dist/apps/management/routes/usage.d.ts +72 -72
- package/dist/apps/management/routes/verified-token-requests.d.ts +83 -83
- package/dist/apps/management/routes/webhooks.d.ts +203 -618
- package/dist/apps/management/routes/webhooks.d.ts.map +1 -1
- package/dist/apps/mcp.d.ts.map +1 -1
- package/dist/apps/mcp.js +4 -1
- package/dist/apps/mcp.js.map +1 -1
- package/dist/apps/mpp/App.d.ts +56 -44
- package/dist/apps/mpp/App.d.ts.map +1 -1
- package/dist/apps/mpp/Mpp.d.ts +3 -0
- package/dist/apps/mpp/Mpp.d.ts.map +1 -1
- package/dist/apps/mpp/Mpp.js +3 -0
- package/dist/apps/mpp/Mpp.js.map +1 -1
- package/dist/apps/mpp/internal/Adapter.d.ts.map +1 -1
- package/dist/apps/mpp/internal/Adapter.js +9 -2
- package/dist/apps/mpp/internal/Adapter.js.map +1 -1
- package/dist/db/Db.d.ts +4 -2
- package/dist/db/Db.d.ts.map +1 -1
- package/dist/db/Db.js +75 -15
- package/dist/db/Db.js.map +1 -1
- package/dist/db/Schema.d.ts +19 -0
- package/dist/db/Schema.d.ts.map +1 -1
- package/dist/db/Schema.js +24 -0
- package/dist/db/Schema.js.map +1 -1
- package/dist/db/tables/sponsoredTransactions.d.ts.map +1 -1
- package/dist/db/tables/sponsoredTransactions.js +4 -1
- package/dist/db/tables/sponsoredTransactions.js.map +1 -1
- package/dist/db/tables/webhookQueueEvents.d.ts +122 -0
- package/dist/db/tables/webhookQueueEvents.d.ts.map +1 -0
- package/dist/db/tables/webhookQueueEvents.js +160 -0
- package/dist/db/tables/webhookQueueEvents.js.map +1 -0
- package/dist/db/tables/webhookSubscriptions.d.ts +58 -3
- package/dist/db/tables/webhookSubscriptions.d.ts.map +1 -1
- package/dist/db/tables/webhookSubscriptions.js +86 -3
- package/dist/db/tables/webhookSubscriptions.js.map +1 -1
- package/dist/internal/Auth.d.ts +3 -0
- package/dist/internal/Auth.d.ts.map +1 -1
- package/dist/internal/Auth.js +6 -4
- package/dist/internal/Auth.js.map +1 -1
- package/dist/internal/MetricSink.d.ts +139 -3
- package/dist/internal/MetricSink.d.ts.map +1 -1
- package/dist/internal/MetricSink.js +144 -4
- package/dist/internal/MetricSink.js.map +1 -1
- package/dist/internal/RateLimit.d.ts +4 -6
- package/dist/internal/RateLimit.d.ts.map +1 -1
- package/dist/internal/RateLimit.js +15 -14
- package/dist/internal/RateLimit.js.map +1 -1
- package/dist/internal/Schema.js +1 -1
- package/dist/internal/Schema.js.map +1 -1
- package/dist/internal/Store.d.ts +2 -1
- package/dist/internal/Store.d.ts.map +1 -1
- package/dist/internal/Store.js +57 -18
- package/dist/internal/Store.js.map +1 -1
- package/dist/internal/Tidx.d.ts +1 -3
- package/dist/internal/Tidx.d.ts.map +1 -1
- package/dist/internal/Tidx.js +1 -3
- package/dist/internal/Tidx.js.map +1 -1
- package/dist/internal/Value.d.ts +2 -0
- package/dist/internal/Value.d.ts.map +1 -1
- package/dist/internal/Value.js +25 -0
- package/dist/internal/Value.js.map +1 -1
- package/dist/internal/Viem.d.ts.map +1 -1
- package/dist/internal/Viem.js +2 -1
- package/dist/internal/Viem.js.map +1 -1
- package/dist/internal/WebhookDestination.d.ts +2 -0
- package/dist/internal/WebhookDestination.d.ts.map +1 -1
- package/dist/internal/WebhookDestination.js +11 -0
- package/dist/internal/WebhookDestination.js.map +1 -1
- package/dist/internal/WebhookTransfer.d.ts +75 -0
- package/dist/internal/WebhookTransfer.d.ts.map +1 -0
- package/dist/internal/WebhookTransfer.js +299 -0
- package/dist/internal/WebhookTransfer.js.map +1 -0
- package/dist/internal/Webhooks.d.ts +176 -0
- package/dist/internal/Webhooks.d.ts.map +1 -1
- package/dist/internal/Webhooks.js +216 -20
- package/dist/internal/Webhooks.js.map +1 -1
- package/dist/internal/funding/Provider.d.ts +4 -0
- package/dist/internal/funding/Provider.d.ts.map +1 -1
- package/dist/internal/funding/Provider.js +11 -7
- package/dist/internal/funding/Provider.js.map +1 -1
- package/dist/internal/funding/Reconciliation.d.ts.map +1 -1
- package/dist/internal/funding/Reconciliation.js +128 -0
- package/dist/internal/funding/Reconciliation.js.map +1 -1
- package/dist/internal/funding/providers/relay.d.ts +6 -4
- package/dist/internal/funding/providers/relay.d.ts.map +1 -1
- package/dist/internal/funding/providers/relay.js +71 -9
- package/dist/internal/funding/providers/relay.js.map +1 -1
- package/package.json +4 -4
- package/src/ApiKeys.test.ts +20 -0
- package/src/ApiKeys.ts +28 -1
- package/src/App.test.ts +10 -8
- package/src/App.ts +1 -0
- package/src/Client.test-d.ts +61 -5
- package/src/Webhooks.bench.ts +714 -0
- package/src/Webhooks.memory.bench.ts +250 -0
- package/src/Webhooks.test.ts +77 -6
- package/src/Webhooks.ts +85 -29
- package/src/admin/apps/api-keys.test.ts +9 -0
- package/src/admin/apps/api-keys.ts +15 -0
- package/src/admin/ui/src/lib/query-client.ts +3 -2
- package/src/admin/ui/src/routes/api-keys.tsx +31 -9
- package/src/admin/ui.generated.ts +69 -69
- package/src/analytics/Analytics.test.ts +8 -0
- package/src/analytics/Analytics.ts +26 -0
- package/src/apps/Zones.test.ts +23 -5
- package/src/apps/Zones.ts +31 -10
- package/src/apps/composition.test.ts +1 -0
- package/src/apps/data/App.test.ts +2 -2
- package/src/apps/data/FxOracle.test.ts +18 -0
- package/src/apps/data/FxOracle.ts +28 -5
- package/src/apps/data/routes/activities.bench.ts +41 -0
- package/src/apps/data/routes/activities.test.ts +122 -2
- package/src/apps/data/routes/activities.ts +435 -32
- package/src/apps/data/routes/balances.bench.ts +21 -0
- package/src/apps/data/routes/balances.test.ts +119 -10
- package/src/apps/data/routes/balances.ts +164 -97
- package/src/apps/data/routes/blocks.bench.ts +34 -0
- package/src/apps/data/routes/blocks.test.ts +12 -6
- package/src/apps/data/routes/coingecko.bench.ts +53 -0
- package/src/apps/data/routes/earn.bench.ts +71 -0
- package/src/apps/data/routes/earn.test.ts +197 -0
- package/src/apps/data/routes/earn.ts +487 -45
- package/src/apps/data/routes/exchanges.bench.ts +85 -0
- package/src/apps/data/routes/exchanges.test.ts +7 -6
- package/src/apps/data/routes/exchanges.ts +124 -117
- package/src/apps/data/routes/fee-amm.bench.ts +13 -0
- package/src/apps/data/routes/indexer.bench.ts +8 -0
- package/src/apps/data/routes/indexer.test.ts +2 -1
- package/src/apps/data/routes/indexer.ts +3 -2
- package/src/apps/data/routes/receipts.bench.ts +39 -0
- package/src/apps/data/routes/receipts.test.ts +24 -7
- package/src/apps/data/routes/receipts.ts +67 -50
- package/src/apps/data/routes/rpc.bench.ts +14 -0
- package/src/apps/data/routes/rpc.test.ts +27 -0
- package/src/apps/data/routes/rpc.ts +5 -1
- package/src/apps/data/routes/tokenlist.bench.ts +3 -0
- package/src/apps/data/routes/tokens.bench.ts +76 -0
- package/src/apps/data/routes/tokens.test.ts +51 -0
- package/src/apps/data/routes/transactions.bench.ts +40 -0
- package/src/apps/data/routes/transactions.test.ts +42 -0
- package/src/apps/data/routes/transactions.ts +44 -39
- package/src/apps/data/routes/transfers.bench.ts +28 -0
- package/src/apps/data/routes/transfers.test.ts +402 -24
- package/src/apps/data/routes/transfers.ts +708 -101
- package/src/apps/data/routes/valuation.bench.ts +14 -0
- package/src/apps/data/routes/valuation.test.ts +26 -0
- package/src/apps/data/routes/valuation.ts +6 -2
- package/src/apps/data/routes/verified-tokens.bench.ts +8 -0
- package/src/apps/data/routes/webhooks.bench.ts +26 -0
- package/src/apps/data/routes/webhooks.scan.test.ts +326 -0
- package/src/apps/data/routes/webhooks.test.ts +466 -135
- package/src/apps/data/routes/webhooks.ts +820 -429
- package/src/apps/data/routes/zones.bench.ts +32 -0
- package/src/apps/data/routes/zones.test.ts +1 -1
- package/src/apps/data/routes/zones.ts +7 -2
- package/src/apps/funding/routes/chains.test.ts +1 -1
- package/src/apps/funding/routes/deposit-addresses.test.ts +1 -1
- package/src/apps/funding/routes/deposit-addresses.ts +1 -1
- package/src/apps/funding/routes/providers.test.ts +1 -1
- package/src/apps/funding/routes/quotes.test.ts +2 -2
- package/src/apps/funding/routes/transfers.test.ts +3 -6
- package/src/apps/funding/routes/transfers.ts +1 -1
- package/src/apps/management/routes/billing.ts +1 -1
- package/src/apps/management/routes/faucet.ts +2 -2
- package/src/apps/management/routes/me.test.ts +31 -0
- package/src/apps/mcp.ts +4 -1
- package/src/apps/mpp/Mpp.test.ts +13 -1
- package/src/apps/mpp/Mpp.ts +8 -0
- package/src/apps/mpp/internal/Adapter.ts +10 -2
- package/src/db/Db.test.ts +40 -6
- package/src/db/Db.ts +114 -19
- package/src/db/Schema.ts +32 -0
- package/src/db/tables/sponsoredTransactions.ts +9 -2
- package/src/db/tables/webhookQueueEvents.ts +285 -0
- package/src/db/tables/webhookSubscriptions.ts +131 -4
- package/src/internal/Auth.test.ts +61 -50
- package/src/internal/Auth.ts +9 -4
- package/src/internal/EarnVaults.test.ts +2 -2
- package/src/internal/MetricSink.test.ts +575 -0
- package/src/internal/MetricSink.ts +325 -10
- package/src/internal/RateLimit.test.ts +23 -18
- package/src/internal/RateLimit.ts +23 -15
- package/src/internal/Schema.ts +1 -1
- package/src/internal/Store.test.ts +99 -13
- package/src/internal/Store.ts +52 -17
- package/src/internal/Tidx.test.ts +4 -4
- package/src/internal/Tidx.ts +1 -3
- package/src/internal/Value.test.ts +25 -0
- package/src/internal/Value.ts +29 -0
- package/src/internal/Viem.test.ts +8 -0
- package/src/internal/Viem.ts +2 -1
- package/src/internal/WebhookDestination.test.ts +20 -0
- package/src/internal/WebhookDestination.ts +10 -0
- package/src/internal/WebhookTransfer.test.ts +89 -0
- package/src/internal/WebhookTransfer.ts +430 -0
- package/src/internal/Webhooks.test.ts +616 -0
- package/src/internal/Webhooks.ts +412 -24
- package/src/internal/funding/Provider.test.ts +48 -8
- package/src/internal/funding/Provider.ts +12 -7
- package/src/internal/funding/Reconciliation.local.test.ts +3 -1
- package/src/internal/funding/Reconciliation.test.ts +1240 -0
- package/src/internal/funding/Reconciliation.ts +136 -0
- package/src/internal/funding/SourceObservation.test.ts +1 -0
- package/src/internal/funding/providers/index.test.ts +73 -3
- package/src/internal/funding/providers/relay.test.ts +2 -9
- package/src/internal/funding/providers/relay.ts +72 -13
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import * as Tidx from '../../../internal/Tidx.js'
|
|
2
|
+
import * as Viem from '../../../internal/Viem.js'
|
|
3
|
+
import * as TestApp from '../../../../test/App.js'
|
|
4
|
+
import * as QueryBench from '../../../../test/QueryBench.js'
|
|
5
|
+
|
|
6
|
+
const senderTag = `0x${'00'.repeat(32)}`
|
|
7
|
+
const zoneChainIds = {
|
|
8
|
+
4217: 421_700_002,
|
|
9
|
+
42431: 424_310_002,
|
|
10
|
+
} as const
|
|
11
|
+
|
|
12
|
+
QueryBench.bench({
|
|
13
|
+
chainId: false,
|
|
14
|
+
name: 'listZoneWithdrawals',
|
|
15
|
+
path: {
|
|
16
|
+
4217: `/v1/zones/withdrawals/${senderTag}?chainId=${zoneChainIds[4217]}`,
|
|
17
|
+
42431: `/v1/zones/withdrawals/${senderTag}?chainId=${zoneChainIds[42431]}`,
|
|
18
|
+
},
|
|
19
|
+
zones: ({ chainId }) => {
|
|
20
|
+
const rpcUrl = Viem.resolveUrl(process.env.RPC_URL, chainId) ?? Viem.url[chainId]
|
|
21
|
+
const tidxUrl = Viem.resolveUrl(process.env.TIDX_URL, chainId) ?? Tidx.url[chainId]
|
|
22
|
+
if (!rpcUrl || !tidxUrl) throw new Error(`Missing Zone upstream for chain ${chainId}.`)
|
|
23
|
+
return [
|
|
24
|
+
TestApp.zone({
|
|
25
|
+
chainId: zoneChainIds[chainId],
|
|
26
|
+
rpcUrl,
|
|
27
|
+
sourceChainId: chainId,
|
|
28
|
+
tidxUrl,
|
|
29
|
+
}),
|
|
30
|
+
]
|
|
31
|
+
},
|
|
32
|
+
})
|
|
@@ -43,7 +43,7 @@ export namespace schema {
|
|
|
43
43
|
.strictObject({
|
|
44
44
|
chainId: Schema.ChainId.check(
|
|
45
45
|
z.describe('The chain id of the Zone where the withdrawal originated.'),
|
|
46
|
-
z.meta({ examples: [
|
|
46
|
+
z.meta({ examples: [1_424_310_001] }),
|
|
47
47
|
),
|
|
48
48
|
})
|
|
49
49
|
.check(z.describe('Query parameters used to select the originating Zone.'))
|
|
@@ -176,7 +176,12 @@ export function zones() {
|
|
|
176
176
|
name: 'tempo-api:zone-withdrawals:v1',
|
|
177
177
|
key: (c) => {
|
|
178
178
|
const principal = Auth.getPrincipal(c)
|
|
179
|
-
|
|
179
|
+
const url = new URL(Cache.urlKey(c, schema.listZoneWithdrawals.Query))
|
|
180
|
+
url.searchParams.set(
|
|
181
|
+
'principal',
|
|
182
|
+
`${principal?.type ?? 'unknown'}:${principal?.id ?? 'unknown'}`,
|
|
183
|
+
)
|
|
184
|
+
return url.toString()
|
|
180
185
|
},
|
|
181
186
|
}),
|
|
182
187
|
async (c) => {
|
|
@@ -22,7 +22,7 @@ const providers = [
|
|
|
22
22
|
integratorId: '0xdead',
|
|
23
23
|
userAddress: '0x1111111111111111111111111111111111111111',
|
|
24
24
|
}),
|
|
25
|
-
Relay.relay({
|
|
25
|
+
Relay.relay({ userAddresses: TestFunding.relayQuoteUsers }),
|
|
26
26
|
Rhino.rhino(),
|
|
27
27
|
Squid.squid({
|
|
28
28
|
integratorId: 'squid-integrator-id',
|
|
@@ -45,7 +45,7 @@ const providers = [
|
|
|
45
45
|
integratorId: '0xdead',
|
|
46
46
|
userAddress: '0x1111111111111111111111111111111111111111',
|
|
47
47
|
}),
|
|
48
|
-
Relay.relay({
|
|
48
|
+
Relay.relay({ userAddresses: TestFunding.relayQuoteUsers }),
|
|
49
49
|
Rhino.rhino(),
|
|
50
50
|
Squid.squid({
|
|
51
51
|
integratorId: 'squid-integrator-id',
|
|
@@ -712,7 +712,7 @@ export function depositAddresses(options: depositAddresses.Options = {}) {
|
|
|
712
712
|
.post(
|
|
713
713
|
'/v1/funding/deposit-addresses/:id{fda_[A-Za-z0-9_-]+}/reconcile',
|
|
714
714
|
Auth.policy({
|
|
715
|
-
apiKey: { rateLimit: {
|
|
715
|
+
apiKey: { rateLimit: { limit: 10, period: 'minute' }, scopes: ['funding:write'] },
|
|
716
716
|
}),
|
|
717
717
|
OpenApi.validate('param', schema.reconcileFundingDepositAddress.Params, {
|
|
718
718
|
code: 'param_invalid',
|
|
@@ -28,7 +28,7 @@ const providers = [
|
|
|
28
28
|
integratorId: '0xdead',
|
|
29
29
|
userAddress: '0x1111111111111111111111111111111111111111',
|
|
30
30
|
}),
|
|
31
|
-
Relay.relay({
|
|
31
|
+
Relay.relay({ userAddresses: TestFunding.relayQuoteUsers }),
|
|
32
32
|
Rhino.rhino(),
|
|
33
33
|
Squid.squid({
|
|
34
34
|
integratorId: 'squid-integrator-id',
|
|
@@ -28,7 +28,7 @@ const providers = [
|
|
|
28
28
|
integratorId: '0xdead',
|
|
29
29
|
userAddress: '0x1111111111111111111111111111111111111111',
|
|
30
30
|
}),
|
|
31
|
-
Relay.relay({
|
|
31
|
+
Relay.relay({ userAddresses: TestFunding.relayQuoteUsers }),
|
|
32
32
|
Rhino.rhino(),
|
|
33
33
|
Squid.squid({
|
|
34
34
|
integratorId: 'squid-integrator-id',
|
|
@@ -271,7 +271,7 @@ describe('GET /funding/quotes', () => {
|
|
|
271
271
|
const response_promise = app.request(quoteUrl({ ...baseUsdcQuery, provider: 'across' }), {
|
|
272
272
|
headers: TestApp.auth.headers,
|
|
273
273
|
})
|
|
274
|
-
await vi.advanceTimersByTimeAsync(
|
|
274
|
+
await vi.advanceTimersByTimeAsync(5_000)
|
|
275
275
|
const response = await response_promise
|
|
276
276
|
|
|
277
277
|
expect(response.status).toBe(502)
|
|
@@ -27,11 +27,8 @@ const db = TestApp.database()
|
|
|
27
27
|
/** Providers exercised against executable calls on a forked source chain. */
|
|
28
28
|
const executableProviders = [
|
|
29
29
|
{
|
|
30
|
-
create: (
|
|
31
|
-
Relay.relay({
|
|
32
|
-
...(process.env.RELAY_API_KEY ? { apiKey: process.env.RELAY_API_KEY } : {}),
|
|
33
|
-
userAddress: sender,
|
|
34
|
-
}),
|
|
30
|
+
create: (_sender: string) =>
|
|
31
|
+
Relay.relay(process.env.RELAY_API_KEY ? { apiKey: process.env.RELAY_API_KEY } : {}),
|
|
35
32
|
id: 'relay',
|
|
36
33
|
},
|
|
37
34
|
] as const
|
|
@@ -282,7 +279,7 @@ describe('POST /funding/transfers', () => {
|
|
|
282
279
|
providerResponse(),
|
|
283
280
|
) {
|
|
284
281
|
return FundingProvider.from({
|
|
285
|
-
...Relay.relay(
|
|
282
|
+
...Relay.relay(),
|
|
286
283
|
prepareTransfer,
|
|
287
284
|
})
|
|
288
285
|
}
|
|
@@ -110,7 +110,7 @@ export namespace schema {
|
|
|
110
110
|
.check(
|
|
111
111
|
z.regex(/^eip155:\d+$/),
|
|
112
112
|
z.describe('Configured Zone CAIP-2 id.'),
|
|
113
|
-
z.meta({ examples: ['eip155:
|
|
113
|
+
z.meta({ examples: ['eip155:1424310001'] }),
|
|
114
114
|
),
|
|
115
115
|
destinationToken: FundingTransfer.schema.CreateRequest.shape.destinationToken,
|
|
116
116
|
mode: destinationCreateFields.mode,
|
|
@@ -699,7 +699,7 @@ export function billing() {
|
|
|
699
699
|
Auth.policy({
|
|
700
700
|
apiKey: false,
|
|
701
701
|
mpp: false,
|
|
702
|
-
public: { rateLimit: {
|
|
702
|
+
public: { rateLimit: { limit: 600, period: 'minute' } },
|
|
703
703
|
session: false,
|
|
704
704
|
}),
|
|
705
705
|
OpenApi.describeRoute({ hide: true, summary: 'Stripe webhook' }),
|
|
@@ -10,8 +10,8 @@ import * as Value from '../../../internal/Value.js'
|
|
|
10
10
|
import * as Viem from '../../../internal/Viem.js'
|
|
11
11
|
import type { Environment } from '../App.js'
|
|
12
12
|
|
|
13
|
-
const addressRateLimit = {
|
|
14
|
-
const userRateLimit = {
|
|
13
|
+
const addressRateLimit = { limit: 1, period: 'minute' } satisfies RateLimit.Limit
|
|
14
|
+
const userRateLimit = { limit: 3, period: 'minute' } satisfies RateLimit.Limit
|
|
15
15
|
|
|
16
16
|
/** OpenAPI schemas owned by the sandbox faucet. */
|
|
17
17
|
export namespace schema {
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import { serve } from '@hono/node-server'
|
|
2
2
|
import * as Accounts from 'accounts/server'
|
|
3
3
|
import { exportJWK, generateKeyPair } from 'jose'
|
|
4
|
+
import { P256 } from 'ox'
|
|
4
5
|
import { generatePrivateKey, privateKeyToAccount } from 'viem/accounts'
|
|
6
|
+
import { Account } from 'viem/tempo'
|
|
7
|
+
import { tempoTestnet } from 'viem/tempo/chains'
|
|
5
8
|
|
|
6
9
|
import * as TestApp from '../../../../test/App.js'
|
|
7
10
|
import * as Me from './me.js'
|
|
@@ -18,6 +21,34 @@ function createApp() {
|
|
|
18
21
|
})
|
|
19
22
|
}
|
|
20
23
|
|
|
24
|
+
describe('SIWE verification', () => {
|
|
25
|
+
test('surfaces configured RPC failures as dependency errors', async () => {
|
|
26
|
+
const account = Account.fromHeadlessWebAuthn(P256.randomPrivateKey(), {
|
|
27
|
+
origin,
|
|
28
|
+
rpId: 'localhost',
|
|
29
|
+
})
|
|
30
|
+
let chainId_seen: number | undefined
|
|
31
|
+
const app = TestApp.create({
|
|
32
|
+
rpc: ({ chainId }) => {
|
|
33
|
+
chainId_seen = chainId
|
|
34
|
+
return { url: 'http://127.0.0.1:1' }
|
|
35
|
+
},
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
const { verifyResponse } = await TestApp.signIn(app, account, {
|
|
39
|
+
chainId: tempoTestnet.id,
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
expect(verifyResponse.status).toBe(502)
|
|
43
|
+
expect(chainId_seen).toMatchInlineSnapshot(`42431`)
|
|
44
|
+
expect(await verifyResponse.json()).toMatchInlineSnapshot(`
|
|
45
|
+
{
|
|
46
|
+
"error": "signature verification unavailable",
|
|
47
|
+
}
|
|
48
|
+
`)
|
|
49
|
+
})
|
|
50
|
+
})
|
|
51
|
+
|
|
21
52
|
describe('GET /me', () => {
|
|
22
53
|
test('returns the signed-in user', async () => {
|
|
23
54
|
const app = createApp()
|
package/src/apps/mcp.ts
CHANGED
|
@@ -138,7 +138,10 @@ export function mcp(options: mcp.Options) {
|
|
|
138
138
|
const app = new Hono<App.Environment>()
|
|
139
139
|
.post(
|
|
140
140
|
'/mcp',
|
|
141
|
-
Auth.policy({
|
|
141
|
+
Auth.policy({
|
|
142
|
+
apiKey: { scopes: [] },
|
|
143
|
+
public: { rateLimit: { limit: 100, period: 'minute' } },
|
|
144
|
+
}),
|
|
142
145
|
OpenApi.describeRoute({
|
|
143
146
|
description:
|
|
144
147
|
"A stateless streamable-HTTP MCP server: send JSON-RPC 2.0 messages as `POST` with `Accept: application/json, text/event-stream`. Tools mirror the API's data domain, plus `docs_*` documentation search. Anonymous requests allow 100 requests per minute; send `Authorization: Bearer <api key>` for protected tools.",
|
package/src/apps/mpp/Mpp.test.ts
CHANGED
|
@@ -91,6 +91,17 @@ describe('MPP relay', () => {
|
|
|
91
91
|
expect(await response.json()).toEqual({ success: true })
|
|
92
92
|
})
|
|
93
93
|
|
|
94
|
+
test('verifies a zero-amount proof credential', async () => {
|
|
95
|
+
const app = TestApp.create({ auth: { keys: [key] } })
|
|
96
|
+
const input = await credential({ amount: '0' })
|
|
97
|
+
const response = await post(app, '/v1/mpp/validate', input)
|
|
98
|
+
|
|
99
|
+
expect(payloadType(input)).toBe('proof')
|
|
100
|
+
expect(input.source).toBeDefined()
|
|
101
|
+
expect(response.status).toBe(200)
|
|
102
|
+
expect(await response.json()).toEqual({ success: true })
|
|
103
|
+
})
|
|
104
|
+
|
|
94
105
|
test('validates and finalizes a pushed credential without a second submission', async () => {
|
|
95
106
|
const app = TestApp.create({ auth: { keys: [key] } })
|
|
96
107
|
const input = await credential({ mode: 'push', supportedModes: ['push'] })
|
|
@@ -619,7 +630,7 @@ type CredentialPayload = { hash?: string; type?: unknown }
|
|
|
619
630
|
|
|
620
631
|
async function credential(options: credential.Options = {}): Promise<Mpp.RelayInput> {
|
|
621
632
|
const issuer = createIssuer(options)
|
|
622
|
-
const challenge = await issuer.challenge.tempo.charge({ amount: '1' })
|
|
633
|
+
const challenge = await issuer.challenge.tempo.charge({ amount: options.amount ?? '1' })
|
|
623
634
|
const authorization = await createClient(options).createCredential(
|
|
624
635
|
new Response(null, {
|
|
625
636
|
headers: { 'www-authenticate': Mppx.Challenge.serialize(challenge) },
|
|
@@ -631,6 +642,7 @@ async function credential(options: credential.Options = {}): Promise<Mpp.RelayIn
|
|
|
631
642
|
|
|
632
643
|
declare namespace credential {
|
|
633
644
|
type Options = {
|
|
645
|
+
amount?: string | undefined
|
|
634
646
|
feePayer?: boolean | undefined
|
|
635
647
|
mode?: 'pull' | 'push' | undefined
|
|
636
648
|
supportedModes?: ('pull' | 'push')[] | undefined
|
package/src/apps/mpp/Mpp.ts
CHANGED
|
@@ -67,6 +67,8 @@ export type RelayInput = {
|
|
|
67
67
|
challenge: Challenge
|
|
68
68
|
/** Method-specific MPP credential payload. */
|
|
69
69
|
payload: unknown
|
|
70
|
+
/** Optional payer identifier supplied with the credential. */
|
|
71
|
+
source?: string | undefined
|
|
70
72
|
}
|
|
71
73
|
|
|
72
74
|
/** Broadcast MPP payment receipt. */
|
|
@@ -200,6 +202,12 @@ export namespace schema {
|
|
|
200
202
|
z.describe('The method-specific MPP credential payload.'),
|
|
201
203
|
z.meta({ examples: [{ signature: `0x${'aa'.repeat(65)}`, type: 'transaction' }] }),
|
|
202
204
|
),
|
|
205
|
+
source: z
|
|
206
|
+
.optional(z.string())
|
|
207
|
+
.check(
|
|
208
|
+
z.describe('An optional payer identifier supplied with the credential.'),
|
|
209
|
+
z.meta({ examples: ['did:pkh:eip155:4217:0x1234567890abcdef1234567890abcdef12345678'] }),
|
|
210
|
+
),
|
|
203
211
|
}),
|
|
204
212
|
'An MPP payment credential and its challenge.',
|
|
205
213
|
)
|
|
@@ -49,7 +49,7 @@ export function create(options: create.Options): Adapter {
|
|
|
49
49
|
/** Validates and screens a credential, rejecting when either check fails. */
|
|
50
50
|
async function validateOrThrow(input: Mpp.RelayInput) {
|
|
51
51
|
const result = await time('mpp_validate', () =>
|
|
52
|
-
Mppx.Method.validateCredential(methods,
|
|
52
|
+
Mppx.Method.validateCredential(methods, credential(input)),
|
|
53
53
|
)
|
|
54
54
|
await time('mpp_screen', () => screenManager.screen(Utils.addresses(result)))
|
|
55
55
|
}
|
|
@@ -65,7 +65,7 @@ export function create(options: create.Options): Adapter {
|
|
|
65
65
|
try {
|
|
66
66
|
await validateOrThrow(input)
|
|
67
67
|
const result = await time('mpp_broadcast', () =>
|
|
68
|
-
Mppx.Method.broadcastCredential(methods,
|
|
68
|
+
Mppx.Method.broadcastCredential(methods, credential(input)),
|
|
69
69
|
)
|
|
70
70
|
return {
|
|
71
71
|
receipt: {
|
|
@@ -91,6 +91,14 @@ export function create(options: create.Options): Adapter {
|
|
|
91
91
|
}
|
|
92
92
|
}
|
|
93
93
|
|
|
94
|
+
function credential(input: Mpp.RelayInput) {
|
|
95
|
+
return Mppx.Credential.from({
|
|
96
|
+
challenge: input.challenge,
|
|
97
|
+
payload: input.payload,
|
|
98
|
+
...(input.source === undefined ? {} : { source: input.source }),
|
|
99
|
+
})
|
|
100
|
+
}
|
|
101
|
+
|
|
94
102
|
export declare namespace create {
|
|
95
103
|
/** Dependencies used to validate and broadcast Tempo credentials. */
|
|
96
104
|
type Options = {
|
package/src/db/Db.test.ts
CHANGED
|
@@ -5,6 +5,7 @@ import * as Runtime from '../../test/runtime.js'
|
|
|
5
5
|
import * as Db from './Db.js'
|
|
6
6
|
import * as Invitations from './tables/invitations.js'
|
|
7
7
|
import * as Organizations from './tables/organizations.js'
|
|
8
|
+
import * as WebhookSubscriptions from './tables/webhookSubscriptions.js'
|
|
8
9
|
|
|
9
10
|
const create = () => Db.postgres({ connectionString: Runtime.postgresUrl, schema: `t_${nanoid()}` })
|
|
10
11
|
|
|
@@ -39,14 +40,47 @@ describe('postgres', () => {
|
|
|
39
40
|
await db.close()
|
|
40
41
|
})
|
|
41
42
|
|
|
42
|
-
test('behavior:
|
|
43
|
+
test('behavior: defers schema creation until migration', async () => {
|
|
43
44
|
const schema = `t_${nanoid()}`
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
45
|
+
const db = Db.postgres({ connectionString: Runtime.postgresUrl, schema })
|
|
46
|
+
const exists = async () =>
|
|
47
|
+
(
|
|
48
|
+
await sql<{
|
|
49
|
+
exists: boolean
|
|
50
|
+
}>`SELECT EXISTS (SELECT 1 FROM pg_namespace WHERE nspname = ${schema}) AS exists`.execute(
|
|
51
|
+
db.kysely,
|
|
52
|
+
)
|
|
53
|
+
).rows[0]!.exists
|
|
54
|
+
|
|
55
|
+
await sql`SELECT 1`.execute(db.kysely)
|
|
56
|
+
expect(await exists()).toBe(false)
|
|
57
|
+
await db.migrate()
|
|
58
|
+
expect(await exists()).toBe(true)
|
|
59
|
+
await db.close()
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
test('behavior: qualifies repository queries with the configured schema', async () => {
|
|
63
|
+
const db = Db.postgres({
|
|
64
|
+
connectionString: Runtime.postgresUrl,
|
|
65
|
+
schema: `t_MixedCase_${nanoid()}`,
|
|
66
|
+
})
|
|
67
|
+
await sql`SET search_path TO public`.execute(db.kysely)
|
|
68
|
+
await db.migrate()
|
|
69
|
+
await sql`SET search_path TO public`.execute(db.kysely)
|
|
70
|
+
await db.transaction(async (tx) => {
|
|
71
|
+
await Organizations.create(tx, { id: 'org_qualified', name: 'Qualified' })
|
|
72
|
+
})
|
|
73
|
+
expect((await Organizations.get(db, 'org_qualified'))?.name).toBe('Qualified')
|
|
74
|
+
expect(
|
|
75
|
+
await WebhookSubscriptions.compareAndSetCursors(db, {
|
|
76
|
+
cursors: [{ cursor: 'next', expectedCursor: null, subscriptionId: 'wh_missing' }],
|
|
77
|
+
}),
|
|
78
|
+
).toEqual(new Set())
|
|
79
|
+
await db.close()
|
|
80
|
+
})
|
|
49
81
|
|
|
82
|
+
test('behavior: concurrent migrate tolerates the ledger race', async () => {
|
|
83
|
+
const schema = `t_${nanoid()}`
|
|
50
84
|
// Several runners migrate the same fresh schema at once (mirrors parallel
|
|
51
85
|
// deploys). The session advisory lock serializes them; none throw.
|
|
52
86
|
const dbs = Array.from({ length: 5 }, () =>
|
package/src/db/Db.ts
CHANGED
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
PostgresDialect,
|
|
8
8
|
type RawBuilder,
|
|
9
9
|
sql,
|
|
10
|
+
WithSchemaPlugin,
|
|
10
11
|
} from 'kysely'
|
|
11
12
|
import * as pg from 'pg'
|
|
12
13
|
|
|
@@ -35,6 +36,7 @@ import type * as Users from './tables/users.js'
|
|
|
35
36
|
import type * as VerifiedTokenRequests from './tables/verifiedTokenRequests.js'
|
|
36
37
|
import type * as VerifiedTokens from './tables/verifiedTokens.js'
|
|
37
38
|
import type * as WebhookDeliveries from './tables/webhookDeliveries.js'
|
|
39
|
+
import type * as WebhookQueueEvents from './tables/webhookQueueEvents.js'
|
|
38
40
|
import type * as WebhookSubscriptions from './tables/webhookSubscriptions.js'
|
|
39
41
|
|
|
40
42
|
/**
|
|
@@ -81,6 +83,7 @@ export type Database = {
|
|
|
81
83
|
verified_token_requests: VerifiedTokenRequests.Table
|
|
82
84
|
verified_tokens: VerifiedTokens.Table
|
|
83
85
|
webhook_deliveries: WebhookDeliveries.Table
|
|
86
|
+
webhook_queue_events: WebhookQueueEvents.Table
|
|
84
87
|
webhook_subscriptions: WebhookSubscriptions.Table
|
|
85
88
|
}
|
|
86
89
|
|
|
@@ -1280,6 +1283,85 @@ export const migrations: readonly Migration[] = [
|
|
|
1280
1283
|
.on('fundingDepositAddresses')
|
|
1281
1284
|
.columns(['orgId', 'environment', 'address', 'projectId']),
|
|
1282
1285
|
},
|
|
1286
|
+
{
|
|
1287
|
+
name: '0115_webhook_queue_events',
|
|
1288
|
+
up: (db) =>
|
|
1289
|
+
db.schema
|
|
1290
|
+
.createTable('webhookQueueEvents')
|
|
1291
|
+
.ifNotExists()
|
|
1292
|
+
.addColumn('subscriptionId', 'text', (c) =>
|
|
1293
|
+
c.notNull().references('webhookSubscriptions.id').onDelete('cascade'),
|
|
1294
|
+
)
|
|
1295
|
+
.addColumn('eventId', 'text', (c) => c.notNull())
|
|
1296
|
+
.addColumn('envelope', 'jsonb', (c) => c.notNull())
|
|
1297
|
+
.addColumn('createdAt', 'text', (c) => c.notNull())
|
|
1298
|
+
.addColumn('expiresAt', 'text', (c) => c.notNull())
|
|
1299
|
+
.addPrimaryKeyConstraint('webhook_queue_events_pkey', ['subscriptionId', 'eventId']),
|
|
1300
|
+
},
|
|
1301
|
+
{
|
|
1302
|
+
name: '0116_webhook_queue_events_expires_index',
|
|
1303
|
+
up: (db) =>
|
|
1304
|
+
db.schema
|
|
1305
|
+
.createIndex('webhook_queue_events_expires_idx')
|
|
1306
|
+
.ifNotExists()
|
|
1307
|
+
.on('webhookQueueEvents')
|
|
1308
|
+
.column('expiresAt'),
|
|
1309
|
+
},
|
|
1310
|
+
{
|
|
1311
|
+
name: '0117_webhook_queue_events_ledger_columns',
|
|
1312
|
+
up: (db) =>
|
|
1313
|
+
raw(
|
|
1314
|
+
db,
|
|
1315
|
+
sql`ALTER TABLE webhook_queue_events
|
|
1316
|
+
ADD COLUMN IF NOT EXISTS status text NOT NULL DEFAULT 'pending',
|
|
1317
|
+
ADD COLUMN IF NOT EXISTS attempt_count integer NOT NULL DEFAULT 0,
|
|
1318
|
+
ADD COLUMN IF NOT EXISTS attempting_at text,
|
|
1319
|
+
ADD COLUMN IF NOT EXISTS next_attempt_at text`,
|
|
1320
|
+
),
|
|
1321
|
+
},
|
|
1322
|
+
{
|
|
1323
|
+
name: '0118_webhook_queue_events_status_check',
|
|
1324
|
+
up: (db) =>
|
|
1325
|
+
raw(
|
|
1326
|
+
db,
|
|
1327
|
+
sql`ALTER TABLE webhook_queue_events DROP CONSTRAINT IF EXISTS webhook_queue_events_status_check, ADD CONSTRAINT webhook_queue_events_status_check CHECK (status IN ('failed', 'pending', 'skipped', 'succeeded'))`,
|
|
1328
|
+
),
|
|
1329
|
+
},
|
|
1330
|
+
{
|
|
1331
|
+
name: '0119_webhook_queue_events_pending_index',
|
|
1332
|
+
up: (db) =>
|
|
1333
|
+
// NULLS FIRST matches the due-sweep order (null = due immediately);
|
|
1334
|
+
// INCLUDE lets the pending gauge take min(created_at) index-only.
|
|
1335
|
+
raw(
|
|
1336
|
+
db,
|
|
1337
|
+
sql`CREATE INDEX IF NOT EXISTS webhook_queue_events_pending_idx ON webhook_queue_events (next_attempt_at NULLS FIRST) INCLUDE (created_at) WHERE status = 'pending'`,
|
|
1338
|
+
),
|
|
1339
|
+
},
|
|
1340
|
+
{
|
|
1341
|
+
name: '0120_webhook_queue_events_pending_index_drop',
|
|
1342
|
+
up: (db) => raw(db, sql`DROP INDEX IF EXISTS webhook_queue_events_pending_idx`),
|
|
1343
|
+
},
|
|
1344
|
+
{
|
|
1345
|
+
name: '0121_webhook_queue_events_pending_index_expiry',
|
|
1346
|
+
up: (db) =>
|
|
1347
|
+
// Recreated with expires_at so claimable and gauge queries, which all
|
|
1348
|
+
// filter expiry at query time, stay index-only.
|
|
1349
|
+
raw(
|
|
1350
|
+
db,
|
|
1351
|
+
sql`CREATE INDEX IF NOT EXISTS webhook_queue_events_pending_idx ON webhook_queue_events (next_attempt_at NULLS FIRST) INCLUDE (created_at, expires_at) WHERE status = 'pending'`,
|
|
1352
|
+
),
|
|
1353
|
+
},
|
|
1354
|
+
{
|
|
1355
|
+
name: '0122_webhook_queue_events_expired_pending_cleanup',
|
|
1356
|
+
up: (db) =>
|
|
1357
|
+
// Pre-ledger rows already past their staging TTL were dead under the
|
|
1358
|
+
// old read path; pending rows no longer expire, so drop them once
|
|
1359
|
+
// instead of resurrecting them as eternal obligations.
|
|
1360
|
+
raw(
|
|
1361
|
+
db,
|
|
1362
|
+
sql`DELETE FROM webhook_queue_events WHERE status = 'pending' AND attempting_at IS NULL AND expires_at <= to_char(now() AT TIME ZONE 'utc', 'YYYY-MM-DD"T"HH24:MI:SS.MS"Z"')`,
|
|
1363
|
+
),
|
|
1364
|
+
},
|
|
1283
1365
|
]
|
|
1284
1366
|
|
|
1285
1367
|
/**
|
|
@@ -1301,6 +1383,15 @@ function raw(db: Kysely<Database>, statement: RawBuilder<unknown>) {
|
|
|
1301
1383
|
* @returns The database.
|
|
1302
1384
|
*/
|
|
1303
1385
|
export function from(dialect: Dialect): Db {
|
|
1386
|
+
return fromDialect(dialect)
|
|
1387
|
+
}
|
|
1388
|
+
|
|
1389
|
+
/** Builds a database with internal dialect-specific options. */
|
|
1390
|
+
function fromDialect(dialect: Dialect, options: fromDialect.Options = {}): Db {
|
|
1391
|
+
const root = new Kysely<Database>({ dialect, plugins: [new CamelCasePlugin()] })
|
|
1392
|
+
// Qualify repository queries instead of relying on session search_path;
|
|
1393
|
+
// transaction poolers reset session state between statements.
|
|
1394
|
+
const kysely = options.schema ? root.withPlugin(new WithSchemaPlugin(options.schema)) : root
|
|
1304
1395
|
// Builds Db over a top-level Kysely handle or transaction-scoped Kysely handle.
|
|
1305
1396
|
function build(kysely: Kysely<Database>): Db {
|
|
1306
1397
|
// Prevents same-handle nested transactions from deadlocking capped pools.
|
|
@@ -1314,9 +1405,19 @@ export function from(dialect: Dialect): Db {
|
|
|
1314
1405
|
async migrate() {
|
|
1315
1406
|
// A session advisory lock serializes the full run, including bootstrap
|
|
1316
1407
|
// DDL and ledger inserts. Pin one connection and unlock in `finally`.
|
|
1317
|
-
await
|
|
1408
|
+
await root.connection().execute(async (db) => {
|
|
1318
1409
|
await sql`SELECT pg_advisory_lock(${migrateLockKey})`.execute(db)
|
|
1319
1410
|
try {
|
|
1411
|
+
if (options.schema) {
|
|
1412
|
+
await db.executeQuery(
|
|
1413
|
+
CompiledQuery.raw(
|
|
1414
|
+
`CREATE SCHEMA IF NOT EXISTS ${pg.escapeIdentifier(options.schema)}`,
|
|
1415
|
+
),
|
|
1416
|
+
)
|
|
1417
|
+
await sql`SELECT set_config('search_path', quote_ident(${options.schema}), false)`.execute(
|
|
1418
|
+
db,
|
|
1419
|
+
)
|
|
1420
|
+
}
|
|
1320
1421
|
await sql`CREATE TABLE IF NOT EXISTS migrations (name TEXT PRIMARY KEY, checksum TEXT NOT NULL, applied_at TEXT NOT NULL)`.execute(
|
|
1321
1422
|
db,
|
|
1322
1423
|
)
|
|
@@ -1378,12 +1479,20 @@ export function from(dialect: Dialect): Db {
|
|
|
1378
1479
|
},
|
|
1379
1480
|
}
|
|
1380
1481
|
}
|
|
1381
|
-
return build(
|
|
1482
|
+
return build(kysely)
|
|
1483
|
+
}
|
|
1484
|
+
|
|
1485
|
+
declare namespace fromDialect {
|
|
1486
|
+
/** Internal database construction options. */
|
|
1487
|
+
type Options = {
|
|
1488
|
+
/** Schema that Kysely qualifies on every repository query. */
|
|
1489
|
+
schema?: string | undefined
|
|
1490
|
+
}
|
|
1382
1491
|
}
|
|
1383
1492
|
|
|
1384
1493
|
/**
|
|
1385
1494
|
* Creates a Postgres-backed {@link Db} via Kysely's `PostgresDialect`. Optional
|
|
1386
|
-
* `schema`
|
|
1495
|
+
* `schema` qualifies repository queries, scopes migrations, and caps the pool.
|
|
1387
1496
|
*
|
|
1388
1497
|
* @param options - Postgres options.
|
|
1389
1498
|
* @returns The database.
|
|
@@ -1391,21 +1500,7 @@ export function from(dialect: Dialect): Db {
|
|
|
1391
1500
|
export function postgres(options: postgres.Options): Db {
|
|
1392
1501
|
const { connectionString, schema } = options
|
|
1393
1502
|
const pool = new pg.Pool(schema ? { connectionString, max: 1 } : { connectionString })
|
|
1394
|
-
return
|
|
1395
|
-
new PostgresDialect({
|
|
1396
|
-
pool,
|
|
1397
|
-
...(schema
|
|
1398
|
-
? {
|
|
1399
|
-
onCreateConnection: async (connection) => {
|
|
1400
|
-
await connection.executeQuery(
|
|
1401
|
-
CompiledQuery.raw(`CREATE SCHEMA IF NOT EXISTS "${schema}"`),
|
|
1402
|
-
)
|
|
1403
|
-
await connection.executeQuery(CompiledQuery.raw(`SET search_path TO "${schema}"`))
|
|
1404
|
-
},
|
|
1405
|
-
}
|
|
1406
|
-
: {}),
|
|
1407
|
-
}),
|
|
1408
|
-
)
|
|
1503
|
+
return fromDialect(new PostgresDialect({ pool }), { schema })
|
|
1409
1504
|
}
|
|
1410
1505
|
|
|
1411
1506
|
export declare namespace postgres {
|
|
@@ -1413,7 +1508,7 @@ export declare namespace postgres {
|
|
|
1413
1508
|
type Options = {
|
|
1414
1509
|
/** Postgres connection string (`postgresql://…`). */
|
|
1415
1510
|
connectionString: string
|
|
1416
|
-
/** Schema to
|
|
1511
|
+
/** Schema to qualify and create during migration (isolates concurrent tests). */
|
|
1417
1512
|
schema?: string | undefined
|
|
1418
1513
|
}
|
|
1419
1514
|
}
|
package/src/db/Schema.ts
CHANGED
|
@@ -874,3 +874,35 @@ export const WebhookDelivery = z.object({
|
|
|
874
874
|
|
|
875
875
|
/** Columns of the `webhook_deliveries` table. */
|
|
876
876
|
export type WebhookDelivery = z.output<typeof WebhookDelivery>
|
|
877
|
+
|
|
878
|
+
/** A durable webhook envelope staged before its compact Queue reference is admitted. */
|
|
879
|
+
export const WebhookQueueEvent = z.object({
|
|
880
|
+
attemptCount: z.number().check(z.describe('Delivery attempts claimed so far.')),
|
|
881
|
+
attemptingAt: z
|
|
882
|
+
.nullable(z.string())
|
|
883
|
+
.check(z.describe('ISO timestamp of the current claim, or null when unclaimed.')),
|
|
884
|
+
createdAt: z.string().check(z.describe('ISO timestamp when the envelope was staged.')),
|
|
885
|
+
envelope: z
|
|
886
|
+
.custom<Webhooks.Envelope>()
|
|
887
|
+
.check(z.describe('The full webhook envelope retained outside Cloudflare Queues.')),
|
|
888
|
+
eventId: z.string().check(z.describe('Idempotent event id (`evt_...`).')),
|
|
889
|
+
expiresAt: z
|
|
890
|
+
.string()
|
|
891
|
+
.check(
|
|
892
|
+
z.describe(
|
|
893
|
+
'ISO dedupe retention deadline, restarted at terminal transitions; inert while pending.',
|
|
894
|
+
),
|
|
895
|
+
),
|
|
896
|
+
nextAttemptAt: z
|
|
897
|
+
.nullable(z.string())
|
|
898
|
+
.check(z.describe('ISO retry due time, or null when due immediately.')),
|
|
899
|
+
status: z
|
|
900
|
+
.enum(['failed', 'pending', 'skipped', 'succeeded'])
|
|
901
|
+
.check(z.describe('Delivery-obligation lifecycle status.')),
|
|
902
|
+
subscriptionId: z
|
|
903
|
+
.string()
|
|
904
|
+
.check(z.describe('Owning subscription id (`wh_...`); FK with `ON DELETE CASCADE`.')),
|
|
905
|
+
})
|
|
906
|
+
|
|
907
|
+
/** Columns of the `webhook_queue_events` table. */
|
|
908
|
+
export type WebhookQueueEvent = z.output<typeof WebhookQueueEvent>
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ExpressionBuilder, Selectable
|
|
1
|
+
import type { ExpressionBuilder, Selectable } from 'kysely'
|
|
2
2
|
import { sql } from 'kysely'
|
|
3
3
|
|
|
4
4
|
import * as Id from '../../internal/Id.js'
|
|
@@ -222,7 +222,14 @@ export async function assignTransactionHash(
|
|
|
222
222
|
.where('status', '=', record.status)
|
|
223
223
|
.where('transactionHash', 'is', null)
|
|
224
224
|
if (record.status === 'failed')
|
|
225
|
-
query = query.where(
|
|
225
|
+
query = query.where((eb) =>
|
|
226
|
+
eb.exists(
|
|
227
|
+
eb
|
|
228
|
+
.selectFrom('organizations')
|
|
229
|
+
.select('organizations.id')
|
|
230
|
+
.whereRef('organizations.id', '=', 'sponsored_transactions.orgId'),
|
|
231
|
+
),
|
|
232
|
+
)
|
|
226
233
|
const updated = await query.executeTakeFirst()
|
|
227
234
|
if (updated.numUpdatedRows === 0n) return
|
|
228
235
|
if (record.status === 'failed' && record.billable && limit) {
|