tapimo 0.5.3 → 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 +526 -1
- 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 +872 -251
- 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/rpc.d.ts +31 -31
- 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 +49 -20
- 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 +44 -44
- 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/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 +1 -1
- 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/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,182 @@
|
|
|
1
|
+
import { type Selectable, sql } from 'kysely'
|
|
2
|
+
|
|
3
|
+
import * as Id from '../../internal/Id.js'
|
|
4
|
+
import type * as Db from '../Db.js'
|
|
5
|
+
import type * as db_Schema from '../Schema.js'
|
|
6
|
+
|
|
7
|
+
/** Columns of the `verified_token_requests` table. */
|
|
8
|
+
export type Table = db_Schema.VerifiedTokenRequest
|
|
9
|
+
/** A stored verification request. */
|
|
10
|
+
export type Record = Selectable<Table>
|
|
11
|
+
/** Review status of a request. */
|
|
12
|
+
export type Status = Record['status']
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Inserts a pending request; `undefined` when a pending request for
|
|
16
|
+
* (chainId, address) already exists.
|
|
17
|
+
*/
|
|
18
|
+
export function create(db: Db.Db, input: create.Input): Promise<Record | undefined> {
|
|
19
|
+
const now = new Date().toISOString()
|
|
20
|
+
return db.kysely
|
|
21
|
+
.insertInto('verified_token_requests')
|
|
22
|
+
.values({
|
|
23
|
+
address: input.address,
|
|
24
|
+
chainId: input.chainId,
|
|
25
|
+
createdAt: now,
|
|
26
|
+
currency: input.currency,
|
|
27
|
+
decimals: input.decimals,
|
|
28
|
+
id: Id.generate('vtr'),
|
|
29
|
+
logo: input.logo ?? null,
|
|
30
|
+
logoUri: input.logoUri ?? null,
|
|
31
|
+
name: input.name,
|
|
32
|
+
note: input.note ?? null,
|
|
33
|
+
orgId: input.orgId,
|
|
34
|
+
requestedBy: input.requestedBy,
|
|
35
|
+
reviewNote: null,
|
|
36
|
+
reviewedAt: null,
|
|
37
|
+
reviewedBy: null,
|
|
38
|
+
status: 'pending',
|
|
39
|
+
symbol: input.symbol,
|
|
40
|
+
updatedAt: now,
|
|
41
|
+
})
|
|
42
|
+
.onConflict((oc) =>
|
|
43
|
+
oc
|
|
44
|
+
.columns(['chainId', 'address'])
|
|
45
|
+
// `sql.lit` keeps the predicate literal; a bound parameter would
|
|
46
|
+
// defeat partial-index inference for the conflict target.
|
|
47
|
+
.where('status', '=', sql.lit('pending'))
|
|
48
|
+
.doNothing(),
|
|
49
|
+
)
|
|
50
|
+
.returningAll()
|
|
51
|
+
.executeTakeFirst()
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export declare namespace create {
|
|
55
|
+
/** Fields accepted when inserting a request. */
|
|
56
|
+
type Input = {
|
|
57
|
+
/** Requested token contract address (lowercase). */
|
|
58
|
+
address: string
|
|
59
|
+
/** Chain the token belongs to. */
|
|
60
|
+
chainId: number
|
|
61
|
+
/** Display currency, e.g. `USD`. */
|
|
62
|
+
currency: string
|
|
63
|
+
/** Decimal precision. */
|
|
64
|
+
decimals: number
|
|
65
|
+
/** Inline SVG logo markup uploaded by the requester. */
|
|
66
|
+
logo?: string | undefined
|
|
67
|
+
/** HTTPS logo URL from chain metadata. */
|
|
68
|
+
logoUri?: string | undefined
|
|
69
|
+
/** Display name. */
|
|
70
|
+
name: string
|
|
71
|
+
/** Requester note to reviewers. */
|
|
72
|
+
note?: string | undefined
|
|
73
|
+
/** Requesting organization id (`org_…`). */
|
|
74
|
+
orgId: string
|
|
75
|
+
/** User id that submitted the request, or `super_admin`. */
|
|
76
|
+
requestedBy: string
|
|
77
|
+
/** Ticker symbol. */
|
|
78
|
+
symbol: string
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/** Lists an organization's requests, newest first. */
|
|
83
|
+
export function list(db: Db.Db, orgId: string): Promise<Record[]> {
|
|
84
|
+
return db.kysely
|
|
85
|
+
.selectFrom('verified_token_requests')
|
|
86
|
+
.selectAll()
|
|
87
|
+
.where('orgId', '=', orgId)
|
|
88
|
+
.orderBy('createdAt', 'desc')
|
|
89
|
+
.execute()
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Lists requests in one status globally, oldest first (review-queue order),
|
|
94
|
+
* with the requesting organization's current name when it still exists.
|
|
95
|
+
*/
|
|
96
|
+
export function listByStatus(
|
|
97
|
+
db: Db.Db,
|
|
98
|
+
status: Status,
|
|
99
|
+
): Promise<(Record & { orgName: string | null })[]> {
|
|
100
|
+
return db.kysely
|
|
101
|
+
.selectFrom('verified_token_requests')
|
|
102
|
+
.leftJoin('organizations', 'organizations.id', 'verified_token_requests.orgId')
|
|
103
|
+
.selectAll('verified_token_requests')
|
|
104
|
+
.select('organizations.name as orgName')
|
|
105
|
+
.where('status', '=', status)
|
|
106
|
+
.orderBy('verified_token_requests.createdAt', 'asc')
|
|
107
|
+
.execute()
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/** Gets a request by id. */
|
|
111
|
+
export function get(db: Db.Db, id: string): Promise<Record | undefined> {
|
|
112
|
+
return db.kysely
|
|
113
|
+
.selectFrom('verified_token_requests')
|
|
114
|
+
.selectAll()
|
|
115
|
+
.where('id', '=', id)
|
|
116
|
+
.executeTakeFirst()
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/** Counts an organization's pending requests. */
|
|
120
|
+
export async function countPending(db: Db.Db, orgId: string): Promise<number> {
|
|
121
|
+
const row = await db.kysely
|
|
122
|
+
.selectFrom('verified_token_requests')
|
|
123
|
+
.select(({ fn }) => fn.countAll<string>().as('count'))
|
|
124
|
+
.where('orgId', '=', orgId)
|
|
125
|
+
.where('status', '=', 'pending')
|
|
126
|
+
.executeTakeFirstOrThrow()
|
|
127
|
+
return Number(row.count)
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/** Hard-deletes an org-scoped request while pending; `undefined` otherwise. */
|
|
131
|
+
export function remove(db: Db.Db, orgId: string, id: string): Promise<Record | undefined> {
|
|
132
|
+
return db.kysely
|
|
133
|
+
.deleteFrom('verified_token_requests')
|
|
134
|
+
.where('id', '=', id)
|
|
135
|
+
.where('orgId', '=', orgId)
|
|
136
|
+
.where('status', '=', 'pending')
|
|
137
|
+
.returningAll()
|
|
138
|
+
.executeTakeFirst()
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Marks a pending request approved or denied with reviewer identity;
|
|
143
|
+
* `undefined` when absent or already reviewed.
|
|
144
|
+
*/
|
|
145
|
+
export function review(db: Db.Db, id: string, input: review.Input): Promise<Record | undefined> {
|
|
146
|
+
const now = new Date().toISOString()
|
|
147
|
+
return db.kysely
|
|
148
|
+
.updateTable('verified_token_requests')
|
|
149
|
+
.set({
|
|
150
|
+
reviewNote: input.reviewNote ?? null,
|
|
151
|
+
reviewedAt: now,
|
|
152
|
+
reviewedBy: input.reviewedBy,
|
|
153
|
+
status: input.status,
|
|
154
|
+
updatedAt: now,
|
|
155
|
+
})
|
|
156
|
+
.where('id', '=', id)
|
|
157
|
+
.where('status', '=', 'pending')
|
|
158
|
+
.returningAll()
|
|
159
|
+
.executeTakeFirst()
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
export declare namespace review {
|
|
163
|
+
/** Review outcome fields. */
|
|
164
|
+
type Input = {
|
|
165
|
+
/** Reviewer note shown on denial. */
|
|
166
|
+
reviewNote?: string | undefined
|
|
167
|
+
/** Reviewing admin email. */
|
|
168
|
+
reviewedBy: string
|
|
169
|
+
/** Review outcome. */
|
|
170
|
+
status: 'approved' | 'denied'
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/** Maps a stored row to the wire shape (null `logo`/`logoUri` map to absent). */
|
|
175
|
+
export function toRequest(record: Record) {
|
|
176
|
+
const { logo, logoUri, ...rest } = record
|
|
177
|
+
return {
|
|
178
|
+
...rest,
|
|
179
|
+
...(logo === null ? {} : { logo }),
|
|
180
|
+
...(logoUri === null ? {} : { logoUri }),
|
|
181
|
+
}
|
|
182
|
+
}
|
|
@@ -5,6 +5,8 @@ import type * as Webhooks from '../../internal/Webhooks.js'
|
|
|
5
5
|
import type * as Db from '../Db.js'
|
|
6
6
|
import type * as db_Schema from '../Schema.js'
|
|
7
7
|
|
|
8
|
+
const subscriptionLimitLockClass = 1464353612
|
|
9
|
+
|
|
8
10
|
/** Columns of the `webhook_subscriptions` table, derived from `Schema.WebhookSubscription`. */
|
|
9
11
|
export type Table = db_Schema.WebhookSubscription
|
|
10
12
|
|
|
@@ -69,6 +71,35 @@ export async function insert(db: Db.Db, subscription: Webhooks.Subscription): Pr
|
|
|
69
71
|
.execute()
|
|
70
72
|
}
|
|
71
73
|
|
|
74
|
+
/**
|
|
75
|
+
* Inserts a subscription when its owner remains below the configured cap.
|
|
76
|
+
* An owner-scoped transaction lock serializes the count and insert.
|
|
77
|
+
*/
|
|
78
|
+
export async function insertWithinLimit(
|
|
79
|
+
db: Db.Db,
|
|
80
|
+
subscription: Webhooks.Subscription,
|
|
81
|
+
options: insertWithinLimit.Options,
|
|
82
|
+
): Promise<boolean> {
|
|
83
|
+
return db.transaction(async (tx) => {
|
|
84
|
+
const owner = `${subscription.owner.type}:${ownerId(subscription.owner)}`
|
|
85
|
+
await sql`select pg_advisory_xact_lock(${subscriptionLimitLockClass}, hashtext(${owner}))`.execute(tx.kysely) // prettier-ignore
|
|
86
|
+
const existing = await count(tx, subscription.owner, options.now)
|
|
87
|
+
if (existing >= options.maxPerOwner) return false
|
|
88
|
+
await insert(tx, subscription)
|
|
89
|
+
return true
|
|
90
|
+
})
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export declare namespace insertWithinLimit {
|
|
94
|
+
/** Options for an atomic capped insert. */
|
|
95
|
+
type Options = {
|
|
96
|
+
/** Maximum live subscriptions the owner may hold. */
|
|
97
|
+
maxPerOwner: number
|
|
98
|
+
/** ISO timestamp used to exclude expired rows from the count. */
|
|
99
|
+
now: string
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
72
103
|
/**
|
|
73
104
|
* Reads an owner's subscription by id; expired rows never match (query-time
|
|
74
105
|
* expiry, same read semantics as the old store TTL).
|
package/src/internal/Auth.ts
CHANGED
|
@@ -6,7 +6,6 @@ import { createMiddleware } from 'hono/factory'
|
|
|
6
6
|
import { matchedRoutes } from 'hono/route'
|
|
7
7
|
import type { MergePath, RouterRoute, Schema as core_Schema } from 'hono/types'
|
|
8
8
|
import { findTargetHandler } from 'hono/utils/handler'
|
|
9
|
-
import { decodeJwt } from 'jose'
|
|
10
9
|
import { Challenge, Credential, type Method } from 'mppx'
|
|
11
10
|
import { Mppx, tempo } from 'mppx/hono'
|
|
12
11
|
import { Addresses } from 'viem/tempo'
|
|
@@ -17,6 +16,7 @@ import * as ApiKey from '../ApiKey.js'
|
|
|
17
16
|
import * as ApiKeys from '../ApiKeys.js'
|
|
18
17
|
import * as Db from '../db/Db.js'
|
|
19
18
|
import type * as Store from './Store.js'
|
|
19
|
+
import * as EarlyAccess from '../db/tables/earlyAccess.js'
|
|
20
20
|
import * as Invitations from '../db/tables/invitations.js'
|
|
21
21
|
import * as Memberships from '../db/tables/memberships.js'
|
|
22
22
|
import * as Metadata from '../apps/metadata.js'
|
|
@@ -159,6 +159,8 @@ export type PolicyOverride = {
|
|
|
159
159
|
| undefined
|
|
160
160
|
/** Session lane: `true` allows signed-in wallet sessions, `false` disables. */
|
|
161
161
|
session?: boolean | undefined
|
|
162
|
+
/** Temporary early-access gate: `true` rejects sessions without early access ({@link tmp_earlyAccess}). TODO: remove after GA. */
|
|
163
|
+
tmp_earlyAccess?: boolean | undefined
|
|
162
164
|
}
|
|
163
165
|
|
|
164
166
|
/** Authenticated or anonymous caller. */
|
|
@@ -227,6 +229,8 @@ export type Variables = {
|
|
|
227
229
|
principal?: Principal | undefined
|
|
228
230
|
/** Project resolved by {@link ensureProject} for the current request. */
|
|
229
231
|
project?: Projects.Record | undefined
|
|
232
|
+
/** Temporary early-access gate toggle; unset or false grants everyone early access. TODO: remove after GA. */
|
|
233
|
+
tmp_earlyAccess?: boolean | undefined
|
|
230
234
|
}
|
|
231
235
|
|
|
232
236
|
/** Reads the authenticated principal from a Hono context. */
|
|
@@ -346,6 +350,9 @@ export function ensureProject(): MiddlewareHandler {
|
|
|
346
350
|
*/
|
|
347
351
|
export const narrowScope = false as boolean
|
|
348
352
|
|
|
353
|
+
/** Always false at runtime; lets handlers include organization role errors in inferred response unions. */
|
|
354
|
+
export const narrowOrgRole = false as boolean
|
|
355
|
+
|
|
349
356
|
/** Typed org-scope error response for Hono client inference. Never reached at runtime. */
|
|
350
357
|
export function ensureOrgError(c: hono_Context) {
|
|
351
358
|
return Response.error(c, {
|
|
@@ -355,6 +362,15 @@ export function ensureOrgError(c: hono_Context) {
|
|
|
355
362
|
})
|
|
356
363
|
}
|
|
357
364
|
|
|
365
|
+
/** Typed organization-role error response for Hono client inference. Never reached at runtime. */
|
|
366
|
+
export function ensureOrgRoleError(c: hono_Context) {
|
|
367
|
+
return Response.error(c, {
|
|
368
|
+
code: 'forbidden',
|
|
369
|
+
message: 'Insufficient organization role',
|
|
370
|
+
status: 403,
|
|
371
|
+
})
|
|
372
|
+
}
|
|
373
|
+
|
|
358
374
|
/** Typed project-scope error response for Hono client inference. Never reached at runtime. */
|
|
359
375
|
export function ensureProjectError(c: hono_Context) {
|
|
360
376
|
// Nested project routes run the org scope first, so either not-found can surface.
|
|
@@ -369,6 +385,42 @@ export function ensureProjectError(c: hono_Context) {
|
|
|
369
385
|
})
|
|
370
386
|
}
|
|
371
387
|
|
|
388
|
+
/** Whether the caller can grant early access, independent of the temporary route gate. TODO: remove after GA. */
|
|
389
|
+
export async function tmp_earlyAccessGrant(c: hono_Context): Promise<boolean> {
|
|
390
|
+
const principal = getPrincipal(c)
|
|
391
|
+
// Trust chain: `management:write` keys are only mintable by early-access owners.
|
|
392
|
+
if (principal?.type === 'api_key' || principal?.type === 'super_admin') return true
|
|
393
|
+
if (principal?.type !== 'session' || !principal.email) return false
|
|
394
|
+
const email = principal.email.trim().toLowerCase()
|
|
395
|
+
const db = Db.get(contextDb(c))
|
|
396
|
+
if (await EarlyAccess.matches(db, email)) return true
|
|
397
|
+
return Invitations.hasEarlyAccessGrant(db, email)
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
/** Whether the caller has effective early access, including a dormant temporary gate. TODO: remove after GA. */
|
|
401
|
+
export async function tmp_earlyAccess(c: hono_Context): Promise<boolean> {
|
|
402
|
+
const enabled = (c.get as (key: string) => unknown)('tmp_earlyAccess') as boolean | undefined
|
|
403
|
+
if (!enabled) return true
|
|
404
|
+
return tmp_earlyAccessGrant(c)
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
/**
|
|
408
|
+
* Always false at runtime, but typed as `boolean` so handlers can include a
|
|
409
|
+
* never-reached early-access-error branch in Hono's inferred response union
|
|
410
|
+
* (the `tmp_earlyAccess` policy flag produces this `403` before the handler
|
|
411
|
+
* runs). TODO: remove after GA.
|
|
412
|
+
*/
|
|
413
|
+
export const tmp_narrowEarlyAccess = false as boolean
|
|
414
|
+
|
|
415
|
+
/** Typed early-access error response for Hono client inference; also the live gate rejection. TODO: remove after GA. */
|
|
416
|
+
export function tmp_ensureEarlyAccessError(c: hono_Context) {
|
|
417
|
+
return Response.error(c, {
|
|
418
|
+
code: 'early_access_required',
|
|
419
|
+
message: 'Requires early access. Ask a teammate with access to invite you by email, or contact Tempo.', // prettier-ignore
|
|
420
|
+
status: 403,
|
|
421
|
+
})
|
|
422
|
+
}
|
|
423
|
+
|
|
372
424
|
/** Reads the caller's membership resolved by the organization or project guard; `undefined` for API keys and `super_admin`. */
|
|
373
425
|
export function membership<environment extends Environment>(
|
|
374
426
|
c: hono_Context<environment>,
|
|
@@ -577,6 +629,9 @@ export function middleware<
|
|
|
577
629
|
if (management && (!auth.mpp || !endpointPolicy.mpp)) return next()
|
|
578
630
|
|
|
579
631
|
c.set('auth', auth)
|
|
632
|
+
// Consumed by `tmp_earlyAccess`; absent (or false) leaves the gate dormant.
|
|
633
|
+
// TODO: remove after GA.
|
|
634
|
+
c.set('tmp_earlyAccess', options.tmp_earlyAccess)
|
|
580
635
|
return require<environment>(endpointPolicy)(c, next)
|
|
581
636
|
})
|
|
582
637
|
}
|
|
@@ -632,6 +687,12 @@ export declare namespace middleware {
|
|
|
632
687
|
* checks — before the key lookup runs. Omit to disable the entry entirely.
|
|
633
688
|
*/
|
|
634
689
|
superAdmin?: { secret: string } | undefined
|
|
690
|
+
/**
|
|
691
|
+
* Temporary invite-only gate for routes flagged via {@link policy}.
|
|
692
|
+
* Allowlist entries live in the `early_access` table (admin dash); unset
|
|
693
|
+
* grants everyone early access. TODO: remove after GA.
|
|
694
|
+
*/
|
|
695
|
+
tmp_earlyAccess?: boolean | undefined
|
|
635
696
|
}
|
|
636
697
|
|
|
637
698
|
/** Resolve-cache tuning for API-key resolution. */
|
|
@@ -764,12 +825,6 @@ export function install(options: install.Options) {
|
|
|
764
825
|
if (!options_session || !kv) return undefined
|
|
765
826
|
const { issuer, origin, requireEmail, ttl } =
|
|
766
827
|
(typeof options_session === 'object' ? options_session.wallet : undefined) ?? {}
|
|
767
|
-
// Case-insensitive exact-match email-domain allowlist for new-user sign-ups.
|
|
768
|
-
const emailAllowlist = (
|
|
769
|
-
typeof options_session === 'object' ? options_session.emailAllowlist : undefined
|
|
770
|
-
)
|
|
771
|
-
?.map((domain) => domain.trim().toLowerCase())
|
|
772
|
-
.filter(Boolean)
|
|
773
828
|
|
|
774
829
|
const handler = Accounts.Handler.auth({
|
|
775
830
|
...(issuer || requireEmail
|
|
@@ -834,38 +889,6 @@ export function install(options: install.Options) {
|
|
|
834
889
|
const policy_session = policy({ public: true, session: true })
|
|
835
890
|
const describe = OpenApi.describeRoute({ hide: true })
|
|
836
891
|
|
|
837
|
-
// Sign-up gate: new addresses need an allowlisted identity email domain
|
|
838
|
-
// or a pending invitation; existing users pass. The claim decodes
|
|
839
|
-
// unverified; the mounted handler verifies the token before any session
|
|
840
|
-
// or user is created, so a forged claim never authenticates.
|
|
841
|
-
const gate_signup = createMiddleware<App.Environment>(async (c, next) => {
|
|
842
|
-
if (!emailAllowlist?.length) return next()
|
|
843
|
-
const { address, idToken } = (await c.req.json()) as z.output<typeof schema.verify.Body>
|
|
844
|
-
const database = Db.get(db)
|
|
845
|
-
const user = await Users.getByAddress(database, address)
|
|
846
|
-
if (!user) {
|
|
847
|
-
const email = (() => {
|
|
848
|
-
if (!idToken) return undefined
|
|
849
|
-
try {
|
|
850
|
-
const { email } = decodeJwt<{ email?: string }>(idToken)
|
|
851
|
-
return typeof email === 'string' ? email.toLowerCase() : undefined
|
|
852
|
-
} catch {
|
|
853
|
-
return undefined
|
|
854
|
-
}
|
|
855
|
-
})()
|
|
856
|
-
const allowed = await (async () => {
|
|
857
|
-
if (!email) return false
|
|
858
|
-
const at = email.lastIndexOf('@')
|
|
859
|
-
if (at > 0 && emailAllowlist.includes(email.slice(at + 1))) return true
|
|
860
|
-
// A pending invitation admits the email regardless of domain.
|
|
861
|
-
return (await Invitations.listByEmail(database, email)).length > 0
|
|
862
|
-
})()
|
|
863
|
-
if (!allowed)
|
|
864
|
-
return c.json({ error: 'sign-ups are restricted to approved email domains or invited emails' }, 403) // prettier-ignore
|
|
865
|
-
}
|
|
866
|
-
return next()
|
|
867
|
-
})
|
|
868
|
-
|
|
869
892
|
const app = new Hono<App.Environment>()
|
|
870
893
|
.use('/v1/auth/siwe', policy_public, describe)
|
|
871
894
|
.use('/v1/auth/siwe/*', policy_public, describe)
|
|
@@ -906,7 +929,6 @@ export function install(options: install.Options) {
|
|
|
906
929
|
code: 'body_invalid',
|
|
907
930
|
message: 'Invalid request body',
|
|
908
931
|
}),
|
|
909
|
-
gate_signup,
|
|
910
932
|
OpenApi.describeRoute({
|
|
911
933
|
description:
|
|
912
934
|
'Verifies the signed challenge and establishes a session. Sets a session cookie by default; pass `returnToken` to receive a bearer token instead.',
|
|
@@ -926,9 +948,6 @@ export function install(options: install.Options) {
|
|
|
926
948
|
},
|
|
927
949
|
400: handlerError('Invalid, expired, or mismatched challenge message; or a required identity token is missing.'), // prettier-ignore
|
|
928
950
|
401: handlerError('Signature or identity verification failed.'),
|
|
929
|
-
...(emailAllowlist?.length
|
|
930
|
-
? { 403: handlerError('Sign-up rejected: the identity email is neither on an allowlisted domain nor pending an invitation.') } // prettier-ignore
|
|
931
|
-
: {}),
|
|
932
951
|
409: handlerError('Challenge nonce already used or unknown.'),
|
|
933
952
|
429: OpenApi.standardError(429, 'Rate limited.'),
|
|
934
953
|
500: OpenApi.standardError(500, 'Internal server error.'),
|
|
@@ -997,8 +1016,6 @@ export declare namespace install {
|
|
|
997
1016
|
type Session =
|
|
998
1017
|
| boolean
|
|
999
1018
|
| {
|
|
1000
|
-
/** Restrict new-user sign-ups to verified emails on these domains (case-insensitive exact match); invited emails and existing users sign in unaffected. */
|
|
1001
|
-
emailAllowlist?: readonly string[] | undefined
|
|
1002
1019
|
/** SIWE wallet sign-in configuration. */
|
|
1003
1020
|
wallet?: Wallet | undefined
|
|
1004
1021
|
}
|
|
@@ -1179,6 +1196,7 @@ function resolvePolicy(
|
|
|
1179
1196
|
let mpp = base.mpp
|
|
1180
1197
|
let public_ = base.public
|
|
1181
1198
|
let session = base.session
|
|
1199
|
+
let tmp_earlyAccess = base.tmp_earlyAccess
|
|
1182
1200
|
|
|
1183
1201
|
for (const override of overrides) {
|
|
1184
1202
|
if (override === false) return undefined
|
|
@@ -1187,6 +1205,7 @@ function resolvePolicy(
|
|
|
1187
1205
|
mpp = resolveMppLane(mpp, override.mpp, defaults.mpp)
|
|
1188
1206
|
public_ = resolvePublicLane(public_, override.public, defaults.public)
|
|
1189
1207
|
session = resolveSessionLane(session, override.session, defaults.session)
|
|
1208
|
+
tmp_earlyAccess = override.tmp_earlyAccess ?? tmp_earlyAccess
|
|
1190
1209
|
}
|
|
1191
1210
|
|
|
1192
1211
|
// The OpenAPI summary/description rides along as the payment-challenge
|
|
@@ -1202,6 +1221,7 @@ function resolvePolicy(
|
|
|
1202
1221
|
...(mpp ? { mpp } : {}),
|
|
1203
1222
|
...(public_ ? { public: public_ } : {}),
|
|
1204
1223
|
...(session ? { session } : {}),
|
|
1224
|
+
...(tmp_earlyAccess ? { tmp_earlyAccess } : {}),
|
|
1205
1225
|
}
|
|
1206
1226
|
}
|
|
1207
1227
|
|
|
@@ -1424,6 +1444,12 @@ export function require<environment extends Environment = Environment>(
|
|
|
1424
1444
|
)
|
|
1425
1445
|
return Response.unsupportedChainId(c, chainId, supportedChainIds)
|
|
1426
1446
|
const proceed = async () => {
|
|
1447
|
+
// Runs after principal resolution, so api_key and super_admin pass by
|
|
1448
|
+
// type; only gated sessions are rejected. TODO: remove after GA.
|
|
1449
|
+
if (policy.tmp_earlyAccess && !(await tmp_earlyAccess(c))) {
|
|
1450
|
+
c.res = tmp_ensureEarlyAccessError(c)
|
|
1451
|
+
return
|
|
1452
|
+
}
|
|
1427
1453
|
await next()
|
|
1428
1454
|
// Query credential responses must not enter shared caches.
|
|
1429
1455
|
const cacheControl = c.res.headers.get('Cache-Control')?.toLowerCase()
|
|
@@ -1659,6 +1685,8 @@ export declare namespace require {
|
|
|
1659
1685
|
| undefined
|
|
1660
1686
|
/** Session access policy: `true` allows signed-in wallet sessions. */
|
|
1661
1687
|
session?: boolean | undefined
|
|
1688
|
+
/** Temporary early-access gate for this route; see {@link tmp_earlyAccess}. TODO: remove after GA. */
|
|
1689
|
+
tmp_earlyAccess?: boolean | undefined
|
|
1662
1690
|
}
|
|
1663
1691
|
}
|
|
1664
1692
|
|