@secondlayer/shared 0.2.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/README.md +19 -0
- package/dist/src/crypto/hmac.d.ts +26 -0
- package/dist/src/crypto/hmac.js +75 -0
- package/dist/src/crypto/hmac.js.map +10 -0
- package/dist/src/db/index.d.ts +227 -0
- package/dist/src/db/index.js +75 -0
- package/dist/src/db/index.js.map +11 -0
- package/dist/src/db/jsonb.d.ts +13 -0
- package/dist/src/db/jsonb.js +35 -0
- package/dist/src/db/jsonb.js.map +10 -0
- package/dist/src/db/queries/accounts.d.ts +179 -0
- package/dist/src/db/queries/accounts.js +39 -0
- package/dist/src/db/queries/accounts.js.map +10 -0
- package/dist/src/db/queries/integrity.d.ts +178 -0
- package/dist/src/db/queries/integrity.js +68 -0
- package/dist/src/db/queries/integrity.js.map +10 -0
- package/dist/src/db/queries/metrics.d.ts +179 -0
- package/dist/src/db/queries/metrics.js +51 -0
- package/dist/src/db/queries/metrics.js.map +10 -0
- package/dist/src/db/queries/usage.d.ts +205 -0
- package/dist/src/db/queries/usage.js +117 -0
- package/dist/src/db/queries/usage.js.map +11 -0
- package/dist/src/db/queries/views.d.ts +191 -0
- package/dist/src/db/queries/views.js +111 -0
- package/dist/src/db/queries/views.js.map +11 -0
- package/dist/src/db/schema.d.ts +207 -0
- package/dist/src/db/schema.js +3 -0
- package/dist/src/db/schema.js.map +9 -0
- package/dist/src/env.d.ts +7 -0
- package/dist/src/env.js +60 -0
- package/dist/src/env.js.map +10 -0
- package/dist/src/errors.d.ts +51 -0
- package/dist/src/errors.js +103 -0
- package/dist/src/errors.js.map +10 -0
- package/dist/src/index.d.ts +464 -0
- package/dist/src/index.js +642 -0
- package/dist/src/index.js.map +19 -0
- package/dist/src/lib/plans.d.ts +10 -0
- package/dist/src/lib/plans.js +34 -0
- package/dist/src/lib/plans.js.map +10 -0
- package/dist/src/logger.d.ts +2 -0
- package/dist/src/logger.js +130 -0
- package/dist/src/logger.js.map +11 -0
- package/dist/src/node/client.d.ts +35 -0
- package/dist/src/node/client.js +56 -0
- package/dist/src/node/client.js.map +10 -0
- package/dist/src/node/hiro-client.d.ts +186 -0
- package/dist/src/node/hiro-client.js +410 -0
- package/dist/src/node/hiro-client.js.map +12 -0
- package/dist/src/queue/index.d.ts +50 -0
- package/dist/src/queue/index.js +176 -0
- package/dist/src/queue/index.js.map +12 -0
- package/dist/src/queue/listener.d.ts +20 -0
- package/dist/src/queue/listener.js +63 -0
- package/dist/src/queue/listener.js.map +10 -0
- package/dist/src/queue/recovery.d.ts +14 -0
- package/dist/src/queue/recovery.js +100 -0
- package/dist/src/queue/recovery.js.map +12 -0
- package/dist/src/schemas/filters.d.ts +30 -0
- package/dist/src/schemas/filters.js +133 -0
- package/dist/src/schemas/filters.js.map +10 -0
- package/dist/src/schemas/index.d.ts +109 -0
- package/dist/src/schemas/index.js +228 -0
- package/dist/src/schemas/index.js.map +12 -0
- package/dist/src/schemas/views.d.ts +51 -0
- package/dist/src/schemas/views.js +29 -0
- package/dist/src/schemas/views.js.map +10 -0
- package/dist/src/types.d.ts +102 -0
- package/dist/src/types.js +3 -0
- package/dist/src/types.js.map +9 -0
- package/migrations/0001_initial.ts +182 -0
- package/migrations/0002_api_keys.ts +38 -0
- package/migrations/0003_tenant_isolation.ts +114 -0
- package/migrations/0004_accounts_and_usage.ts +90 -0
- package/migrations/0005_sessions.ts +42 -0
- package/package.json +128 -0
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
import { Kysely } from "kysely";
|
|
2
|
+
import { Generated, Selectable } from "kysely";
|
|
3
|
+
interface BlocksTable {
|
|
4
|
+
height: number;
|
|
5
|
+
hash: string;
|
|
6
|
+
parent_hash: string;
|
|
7
|
+
burn_block_height: number;
|
|
8
|
+
timestamp: number;
|
|
9
|
+
canonical: Generated<boolean>;
|
|
10
|
+
created_at: Generated<Date>;
|
|
11
|
+
}
|
|
12
|
+
interface TransactionsTable {
|
|
13
|
+
tx_id: string;
|
|
14
|
+
block_height: number;
|
|
15
|
+
type: string;
|
|
16
|
+
sender: string;
|
|
17
|
+
status: string;
|
|
18
|
+
contract_id: string | null;
|
|
19
|
+
function_name: string | null;
|
|
20
|
+
raw_tx: string;
|
|
21
|
+
created_at: Generated<Date>;
|
|
22
|
+
}
|
|
23
|
+
interface EventsTable {
|
|
24
|
+
id: Generated<string>;
|
|
25
|
+
tx_id: string;
|
|
26
|
+
block_height: number;
|
|
27
|
+
event_index: number;
|
|
28
|
+
type: string;
|
|
29
|
+
data: unknown;
|
|
30
|
+
created_at: Generated<Date>;
|
|
31
|
+
}
|
|
32
|
+
interface StreamsTable {
|
|
33
|
+
id: Generated<string>;
|
|
34
|
+
name: string;
|
|
35
|
+
status: Generated<string>;
|
|
36
|
+
filters: unknown;
|
|
37
|
+
options: Generated<unknown>;
|
|
38
|
+
webhook_url: string;
|
|
39
|
+
webhook_secret: string | null;
|
|
40
|
+
api_key_id: string | null;
|
|
41
|
+
created_at: Generated<Date>;
|
|
42
|
+
updated_at: Generated<Date>;
|
|
43
|
+
}
|
|
44
|
+
interface StreamMetricsTable {
|
|
45
|
+
stream_id: string;
|
|
46
|
+
last_triggered_at: Date | null;
|
|
47
|
+
last_triggered_block: number | null;
|
|
48
|
+
total_deliveries: Generated<number>;
|
|
49
|
+
failed_deliveries: Generated<number>;
|
|
50
|
+
error_message: string | null;
|
|
51
|
+
}
|
|
52
|
+
interface JobsTable {
|
|
53
|
+
id: Generated<string>;
|
|
54
|
+
stream_id: string;
|
|
55
|
+
block_height: number;
|
|
56
|
+
status: Generated<string>;
|
|
57
|
+
attempts: Generated<number>;
|
|
58
|
+
locked_at: Date | null;
|
|
59
|
+
locked_by: string | null;
|
|
60
|
+
error: string | null;
|
|
61
|
+
backfill: Generated<boolean>;
|
|
62
|
+
created_at: Generated<Date>;
|
|
63
|
+
completed_at: Date | null;
|
|
64
|
+
}
|
|
65
|
+
interface IndexProgressTable {
|
|
66
|
+
network: string;
|
|
67
|
+
last_indexed_block: Generated<number>;
|
|
68
|
+
last_contiguous_block: Generated<number>;
|
|
69
|
+
highest_seen_block: Generated<number>;
|
|
70
|
+
updated_at: Generated<Date>;
|
|
71
|
+
}
|
|
72
|
+
interface DeliveriesTable {
|
|
73
|
+
id: Generated<string>;
|
|
74
|
+
stream_id: string;
|
|
75
|
+
job_id: string | null;
|
|
76
|
+
block_height: number;
|
|
77
|
+
status: string;
|
|
78
|
+
status_code: number | null;
|
|
79
|
+
response_time_ms: number | null;
|
|
80
|
+
attempts: Generated<number>;
|
|
81
|
+
error: string | null;
|
|
82
|
+
payload: unknown;
|
|
83
|
+
created_at: Generated<Date>;
|
|
84
|
+
}
|
|
85
|
+
interface ViewsTable {
|
|
86
|
+
id: Generated<string>;
|
|
87
|
+
name: string;
|
|
88
|
+
version: Generated<string>;
|
|
89
|
+
status: Generated<string>;
|
|
90
|
+
definition: unknown;
|
|
91
|
+
schema_hash: string;
|
|
92
|
+
handler_path: string;
|
|
93
|
+
schema_name: string | null;
|
|
94
|
+
last_processed_block: Generated<number>;
|
|
95
|
+
last_error: string | null;
|
|
96
|
+
last_error_at: Date | null;
|
|
97
|
+
total_processed: Generated<number>;
|
|
98
|
+
total_errors: Generated<number>;
|
|
99
|
+
api_key_id: string | null;
|
|
100
|
+
created_at: Generated<Date>;
|
|
101
|
+
updated_at: Generated<Date>;
|
|
102
|
+
}
|
|
103
|
+
interface ApiKeysTable {
|
|
104
|
+
id: Generated<string>;
|
|
105
|
+
key_hash: string;
|
|
106
|
+
key_prefix: string;
|
|
107
|
+
name: string | null;
|
|
108
|
+
status: Generated<string>;
|
|
109
|
+
rate_limit: Generated<number>;
|
|
110
|
+
ip_address: string;
|
|
111
|
+
account_id: string;
|
|
112
|
+
last_used_at: Date | null;
|
|
113
|
+
revoked_at: Date | null;
|
|
114
|
+
created_at: Generated<Date>;
|
|
115
|
+
}
|
|
116
|
+
interface AccountsTable {
|
|
117
|
+
id: Generated<string>;
|
|
118
|
+
email: string;
|
|
119
|
+
plan: Generated<string>;
|
|
120
|
+
created_at: Generated<Date>;
|
|
121
|
+
}
|
|
122
|
+
interface SessionsTable {
|
|
123
|
+
id: Generated<string>;
|
|
124
|
+
token_hash: string;
|
|
125
|
+
token_prefix: string;
|
|
126
|
+
account_id: string;
|
|
127
|
+
ip_address: string;
|
|
128
|
+
expires_at: Generated<Date>;
|
|
129
|
+
revoked_at: Date | null;
|
|
130
|
+
last_used_at: Date | null;
|
|
131
|
+
created_at: Generated<Date>;
|
|
132
|
+
}
|
|
133
|
+
interface MagicLinksTable {
|
|
134
|
+
id: Generated<string>;
|
|
135
|
+
email: string;
|
|
136
|
+
token: string;
|
|
137
|
+
expires_at: Date;
|
|
138
|
+
used_at: Date | null;
|
|
139
|
+
created_at: Generated<Date>;
|
|
140
|
+
}
|
|
141
|
+
interface UsageDailyTable {
|
|
142
|
+
account_id: string;
|
|
143
|
+
date: string;
|
|
144
|
+
api_requests: Generated<number>;
|
|
145
|
+
deliveries: Generated<number>;
|
|
146
|
+
}
|
|
147
|
+
interface UsageSnapshotsTable {
|
|
148
|
+
id: Generated<string>;
|
|
149
|
+
account_id: string;
|
|
150
|
+
measured_at: Generated<Date>;
|
|
151
|
+
storage_bytes: Generated<number>;
|
|
152
|
+
}
|
|
153
|
+
interface Database {
|
|
154
|
+
blocks: BlocksTable;
|
|
155
|
+
transactions: TransactionsTable;
|
|
156
|
+
events: EventsTable;
|
|
157
|
+
streams: StreamsTable;
|
|
158
|
+
stream_metrics: StreamMetricsTable;
|
|
159
|
+
jobs: JobsTable;
|
|
160
|
+
index_progress: IndexProgressTable;
|
|
161
|
+
deliveries: DeliveriesTable;
|
|
162
|
+
views: ViewsTable;
|
|
163
|
+
api_keys: ApiKeysTable;
|
|
164
|
+
accounts: AccountsTable;
|
|
165
|
+
sessions: SessionsTable;
|
|
166
|
+
magic_links: MagicLinksTable;
|
|
167
|
+
usage_daily: UsageDailyTable;
|
|
168
|
+
usage_snapshots: UsageSnapshotsTable;
|
|
169
|
+
}
|
|
170
|
+
type Account = Selectable<AccountsTable>;
|
|
171
|
+
declare function upsertAccount(db: Kysely<Database>, email: string): Promise<Account>;
|
|
172
|
+
declare function getAccountById(db: Kysely<Database>, id: string): Promise<Account | null>;
|
|
173
|
+
declare function createMagicLink(db: Kysely<Database>, email: string, token: string, expiresInMs?): Promise<void>;
|
|
174
|
+
/**
|
|
175
|
+
* Verify a magic link token. Returns the email if valid, null otherwise.
|
|
176
|
+
* Marks the token as used atomically.
|
|
177
|
+
*/
|
|
178
|
+
declare function verifyMagicLink(db: Kysely<Database>, token: string): Promise<string | null>;
|
|
179
|
+
export { verifyMagicLink, upsertAccount, getAccountById, createMagicLink };
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { createRequire } from "node:module";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __export = (target, all) => {
|
|
4
|
+
for (var name in all)
|
|
5
|
+
__defProp(target, name, {
|
|
6
|
+
get: all[name],
|
|
7
|
+
enumerable: true,
|
|
8
|
+
configurable: true,
|
|
9
|
+
set: (newValue) => all[name] = () => newValue
|
|
10
|
+
});
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
// src/db/queries/accounts.ts
|
|
14
|
+
async function upsertAccount(db, email) {
|
|
15
|
+
return await db.insertInto("accounts").values({ email }).onConflict((oc) => oc.column("email").doUpdateSet({ email })).returningAll().executeTakeFirstOrThrow();
|
|
16
|
+
}
|
|
17
|
+
async function getAccountById(db, id) {
|
|
18
|
+
return await db.selectFrom("accounts").selectAll().where("id", "=", id).executeTakeFirst() ?? null;
|
|
19
|
+
}
|
|
20
|
+
async function createMagicLink(db, email, token, expiresInMs = 15 * 60 * 1000) {
|
|
21
|
+
await db.insertInto("magic_links").values({
|
|
22
|
+
email,
|
|
23
|
+
token,
|
|
24
|
+
expires_at: new Date(Date.now() + expiresInMs)
|
|
25
|
+
}).execute();
|
|
26
|
+
}
|
|
27
|
+
async function verifyMagicLink(db, token) {
|
|
28
|
+
const result = await db.updateTable("magic_links").set({ used_at: new Date }).where("token", "=", token).where("used_at", "is", null).where("expires_at", ">", new Date).returning("email").executeTakeFirst();
|
|
29
|
+
return result?.email ?? null;
|
|
30
|
+
}
|
|
31
|
+
export {
|
|
32
|
+
verifyMagicLink,
|
|
33
|
+
upsertAccount,
|
|
34
|
+
getAccountById,
|
|
35
|
+
createMagicLink
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
//# debugId=BF026A17168292E864756E2164756E21
|
|
39
|
+
//# sourceMappingURL=accounts.js.map
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/db/queries/accounts.ts"],
|
|
4
|
+
"sourcesContent": [
|
|
5
|
+
"import type { Kysely } from \"kysely\";\nimport type { Database, Account } from \"../types.ts\";\n\nexport async function upsertAccount(\n db: Kysely<Database>,\n email: string,\n): Promise<Account> {\n return await db\n .insertInto(\"accounts\")\n .values({ email })\n .onConflict((oc) =>\n oc.column(\"email\").doUpdateSet({ email }), // no-op update to return existing\n )\n .returningAll()\n .executeTakeFirstOrThrow();\n}\n\nexport async function getAccountById(\n db: Kysely<Database>,\n id: string,\n): Promise<Account | null> {\n return (\n (await db\n .selectFrom(\"accounts\")\n .selectAll()\n .where(\"id\", \"=\", id)\n .executeTakeFirst()) ?? null\n );\n}\n\nexport async function createMagicLink(\n db: Kysely<Database>,\n email: string,\n token: string,\n expiresInMs = 15 * 60 * 1000,\n): Promise<void> {\n await db\n .insertInto(\"magic_links\")\n .values({\n email,\n token,\n expires_at: new Date(Date.now() + expiresInMs),\n })\n .execute();\n}\n\n/**\n * Verify a magic link token. Returns the email if valid, null otherwise.\n * Marks the token as used atomically.\n */\nexport async function verifyMagicLink(\n db: Kysely<Database>,\n token: string,\n): Promise<string | null> {\n const result = await db\n .updateTable(\"magic_links\")\n .set({ used_at: new Date() })\n .where(\"token\", \"=\", token)\n .where(\"used_at\", \"is\", null)\n .where(\"expires_at\", \">\", new Date())\n .returning(\"email\")\n .executeTakeFirst();\n\n return result?.email ?? null;\n}\n"
|
|
6
|
+
],
|
|
7
|
+
"mappings": ";;;;;;;;;;;;;AAGA,eAAsB,aAAa,CACjC,IACA,OACkB;AAAA,EAClB,OAAO,MAAM,GACV,WAAW,UAAU,EACrB,OAAO,EAAE,MAAM,CAAC,EAChB,WAAW,CAAC,OACX,GAAG,OAAO,OAAO,EAAE,YAAY,EAAE,MAAM,CAAC,CAC1C,EACC,aAAa,EACb,wBAAwB;AAAA;AAG7B,eAAsB,cAAc,CAClC,IACA,IACyB;AAAA,EACzB,OACG,MAAM,GACJ,WAAW,UAAU,EACrB,UAAU,EACV,MAAM,MAAM,KAAK,EAAE,EACnB,iBAAiB,KAAM;AAAA;AAI9B,eAAsB,eAAe,CACnC,IACA,OACA,OACA,cAAc,KAAK,KAAK,MACT;AAAA,EACf,MAAM,GACH,WAAW,aAAa,EACxB,OAAO;AAAA,IACN;AAAA,IACA;AAAA,IACA,YAAY,IAAI,KAAK,KAAK,IAAI,IAAI,WAAW;AAAA,EAC/C,CAAC,EACA,QAAQ;AAAA;AAOb,eAAsB,eAAe,CACnC,IACA,OACwB;AAAA,EACxB,MAAM,SAAS,MAAM,GAClB,YAAY,aAAa,EACzB,IAAI,EAAE,SAAS,IAAI,KAAO,CAAC,EAC3B,MAAM,SAAS,KAAK,KAAK,EACzB,MAAM,WAAW,MAAM,IAAI,EAC3B,MAAM,cAAc,KAAK,IAAI,IAAM,EACnC,UAAU,OAAO,EACjB,iBAAiB;AAAA,EAEpB,OAAO,QAAQ,SAAS;AAAA;",
|
|
8
|
+
"debugId": "BF026A17168292E864756E2164756E21",
|
|
9
|
+
"names": []
|
|
10
|
+
}
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import { Kysely } from "kysely";
|
|
2
|
+
import { Generated } from "kysely";
|
|
3
|
+
interface BlocksTable {
|
|
4
|
+
height: number;
|
|
5
|
+
hash: string;
|
|
6
|
+
parent_hash: string;
|
|
7
|
+
burn_block_height: number;
|
|
8
|
+
timestamp: number;
|
|
9
|
+
canonical: Generated<boolean>;
|
|
10
|
+
created_at: Generated<Date>;
|
|
11
|
+
}
|
|
12
|
+
interface TransactionsTable {
|
|
13
|
+
tx_id: string;
|
|
14
|
+
block_height: number;
|
|
15
|
+
type: string;
|
|
16
|
+
sender: string;
|
|
17
|
+
status: string;
|
|
18
|
+
contract_id: string | null;
|
|
19
|
+
function_name: string | null;
|
|
20
|
+
raw_tx: string;
|
|
21
|
+
created_at: Generated<Date>;
|
|
22
|
+
}
|
|
23
|
+
interface EventsTable {
|
|
24
|
+
id: Generated<string>;
|
|
25
|
+
tx_id: string;
|
|
26
|
+
block_height: number;
|
|
27
|
+
event_index: number;
|
|
28
|
+
type: string;
|
|
29
|
+
data: unknown;
|
|
30
|
+
created_at: Generated<Date>;
|
|
31
|
+
}
|
|
32
|
+
interface StreamsTable {
|
|
33
|
+
id: Generated<string>;
|
|
34
|
+
name: string;
|
|
35
|
+
status: Generated<string>;
|
|
36
|
+
filters: unknown;
|
|
37
|
+
options: Generated<unknown>;
|
|
38
|
+
webhook_url: string;
|
|
39
|
+
webhook_secret: string | null;
|
|
40
|
+
api_key_id: string | null;
|
|
41
|
+
created_at: Generated<Date>;
|
|
42
|
+
updated_at: Generated<Date>;
|
|
43
|
+
}
|
|
44
|
+
interface StreamMetricsTable {
|
|
45
|
+
stream_id: string;
|
|
46
|
+
last_triggered_at: Date | null;
|
|
47
|
+
last_triggered_block: number | null;
|
|
48
|
+
total_deliveries: Generated<number>;
|
|
49
|
+
failed_deliveries: Generated<number>;
|
|
50
|
+
error_message: string | null;
|
|
51
|
+
}
|
|
52
|
+
interface JobsTable {
|
|
53
|
+
id: Generated<string>;
|
|
54
|
+
stream_id: string;
|
|
55
|
+
block_height: number;
|
|
56
|
+
status: Generated<string>;
|
|
57
|
+
attempts: Generated<number>;
|
|
58
|
+
locked_at: Date | null;
|
|
59
|
+
locked_by: string | null;
|
|
60
|
+
error: string | null;
|
|
61
|
+
backfill: Generated<boolean>;
|
|
62
|
+
created_at: Generated<Date>;
|
|
63
|
+
completed_at: Date | null;
|
|
64
|
+
}
|
|
65
|
+
interface IndexProgressTable {
|
|
66
|
+
network: string;
|
|
67
|
+
last_indexed_block: Generated<number>;
|
|
68
|
+
last_contiguous_block: Generated<number>;
|
|
69
|
+
highest_seen_block: Generated<number>;
|
|
70
|
+
updated_at: Generated<Date>;
|
|
71
|
+
}
|
|
72
|
+
interface DeliveriesTable {
|
|
73
|
+
id: Generated<string>;
|
|
74
|
+
stream_id: string;
|
|
75
|
+
job_id: string | null;
|
|
76
|
+
block_height: number;
|
|
77
|
+
status: string;
|
|
78
|
+
status_code: number | null;
|
|
79
|
+
response_time_ms: number | null;
|
|
80
|
+
attempts: Generated<number>;
|
|
81
|
+
error: string | null;
|
|
82
|
+
payload: unknown;
|
|
83
|
+
created_at: Generated<Date>;
|
|
84
|
+
}
|
|
85
|
+
interface ViewsTable {
|
|
86
|
+
id: Generated<string>;
|
|
87
|
+
name: string;
|
|
88
|
+
version: Generated<string>;
|
|
89
|
+
status: Generated<string>;
|
|
90
|
+
definition: unknown;
|
|
91
|
+
schema_hash: string;
|
|
92
|
+
handler_path: string;
|
|
93
|
+
schema_name: string | null;
|
|
94
|
+
last_processed_block: Generated<number>;
|
|
95
|
+
last_error: string | null;
|
|
96
|
+
last_error_at: Date | null;
|
|
97
|
+
total_processed: Generated<number>;
|
|
98
|
+
total_errors: Generated<number>;
|
|
99
|
+
api_key_id: string | null;
|
|
100
|
+
created_at: Generated<Date>;
|
|
101
|
+
updated_at: Generated<Date>;
|
|
102
|
+
}
|
|
103
|
+
interface ApiKeysTable {
|
|
104
|
+
id: Generated<string>;
|
|
105
|
+
key_hash: string;
|
|
106
|
+
key_prefix: string;
|
|
107
|
+
name: string | null;
|
|
108
|
+
status: Generated<string>;
|
|
109
|
+
rate_limit: Generated<number>;
|
|
110
|
+
ip_address: string;
|
|
111
|
+
account_id: string;
|
|
112
|
+
last_used_at: Date | null;
|
|
113
|
+
revoked_at: Date | null;
|
|
114
|
+
created_at: Generated<Date>;
|
|
115
|
+
}
|
|
116
|
+
interface AccountsTable {
|
|
117
|
+
id: Generated<string>;
|
|
118
|
+
email: string;
|
|
119
|
+
plan: Generated<string>;
|
|
120
|
+
created_at: Generated<Date>;
|
|
121
|
+
}
|
|
122
|
+
interface SessionsTable {
|
|
123
|
+
id: Generated<string>;
|
|
124
|
+
token_hash: string;
|
|
125
|
+
token_prefix: string;
|
|
126
|
+
account_id: string;
|
|
127
|
+
ip_address: string;
|
|
128
|
+
expires_at: Generated<Date>;
|
|
129
|
+
revoked_at: Date | null;
|
|
130
|
+
last_used_at: Date | null;
|
|
131
|
+
created_at: Generated<Date>;
|
|
132
|
+
}
|
|
133
|
+
interface MagicLinksTable {
|
|
134
|
+
id: Generated<string>;
|
|
135
|
+
email: string;
|
|
136
|
+
token: string;
|
|
137
|
+
expires_at: Date;
|
|
138
|
+
used_at: Date | null;
|
|
139
|
+
created_at: Generated<Date>;
|
|
140
|
+
}
|
|
141
|
+
interface UsageDailyTable {
|
|
142
|
+
account_id: string;
|
|
143
|
+
date: string;
|
|
144
|
+
api_requests: Generated<number>;
|
|
145
|
+
deliveries: Generated<number>;
|
|
146
|
+
}
|
|
147
|
+
interface UsageSnapshotsTable {
|
|
148
|
+
id: Generated<string>;
|
|
149
|
+
account_id: string;
|
|
150
|
+
measured_at: Generated<Date>;
|
|
151
|
+
storage_bytes: Generated<number>;
|
|
152
|
+
}
|
|
153
|
+
interface Database {
|
|
154
|
+
blocks: BlocksTable;
|
|
155
|
+
transactions: TransactionsTable;
|
|
156
|
+
events: EventsTable;
|
|
157
|
+
streams: StreamsTable;
|
|
158
|
+
stream_metrics: StreamMetricsTable;
|
|
159
|
+
jobs: JobsTable;
|
|
160
|
+
index_progress: IndexProgressTable;
|
|
161
|
+
deliveries: DeliveriesTable;
|
|
162
|
+
views: ViewsTable;
|
|
163
|
+
api_keys: ApiKeysTable;
|
|
164
|
+
accounts: AccountsTable;
|
|
165
|
+
sessions: SessionsTable;
|
|
166
|
+
magic_links: MagicLinksTable;
|
|
167
|
+
usage_daily: UsageDailyTable;
|
|
168
|
+
usage_snapshots: UsageSnapshotsTable;
|
|
169
|
+
}
|
|
170
|
+
interface Gap {
|
|
171
|
+
gapStart: number;
|
|
172
|
+
gapEnd: number;
|
|
173
|
+
size: number;
|
|
174
|
+
}
|
|
175
|
+
declare function findGaps(db: Kysely<Database>, limit?: number): Promise<Gap[]>;
|
|
176
|
+
declare function countMissingBlocks(db: Kysely<Database>): Promise<number>;
|
|
177
|
+
declare function computeContiguousTip(db: Kysely<Database>, fromHeight: number): Promise<number>;
|
|
178
|
+
export { findGaps, countMissingBlocks, computeContiguousTip, Gap };
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { createRequire } from "node:module";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __export = (target, all) => {
|
|
4
|
+
for (var name in all)
|
|
5
|
+
__defProp(target, name, {
|
|
6
|
+
get: all[name],
|
|
7
|
+
enumerable: true,
|
|
8
|
+
configurable: true,
|
|
9
|
+
set: (newValue) => all[name] = () => newValue
|
|
10
|
+
});
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
// src/db/queries/integrity.ts
|
|
14
|
+
import { sql } from "kysely";
|
|
15
|
+
async function findGaps(db, limit) {
|
|
16
|
+
const limitClause = limit ? sql`LIMIT ${limit}` : sql``;
|
|
17
|
+
const { rows } = await sql`
|
|
18
|
+
SELECT gap_start, gap_end, gap_end - gap_start + 1 AS size
|
|
19
|
+
FROM (
|
|
20
|
+
SELECT height + 1 AS gap_start, next_height - 1 AS gap_end
|
|
21
|
+
FROM (
|
|
22
|
+
SELECT height, LEAD(height) OVER (ORDER BY height) AS next_height
|
|
23
|
+
FROM blocks WHERE canonical = true
|
|
24
|
+
) sub
|
|
25
|
+
WHERE next_height - height > 1
|
|
26
|
+
) gaps
|
|
27
|
+
ORDER BY gap_start
|
|
28
|
+
${limitClause}
|
|
29
|
+
`.execute(db);
|
|
30
|
+
return rows.map((r) => ({
|
|
31
|
+
gapStart: Number(r.gap_start),
|
|
32
|
+
gapEnd: Number(r.gap_end),
|
|
33
|
+
size: Number(r.size)
|
|
34
|
+
}));
|
|
35
|
+
}
|
|
36
|
+
async function countMissingBlocks(db) {
|
|
37
|
+
const { rows } = await sql`
|
|
38
|
+
SELECT COALESCE(SUM(next_height - height - 1), 0) AS total
|
|
39
|
+
FROM (
|
|
40
|
+
SELECT height, LEAD(height) OVER (ORDER BY height) AS next_height
|
|
41
|
+
FROM blocks WHERE canonical = true
|
|
42
|
+
) sub
|
|
43
|
+
WHERE next_height - height > 1
|
|
44
|
+
`.execute(db);
|
|
45
|
+
return Number(rows[0]?.total ?? 0);
|
|
46
|
+
}
|
|
47
|
+
async function computeContiguousTip(db, fromHeight) {
|
|
48
|
+
const { rows } = await sql`
|
|
49
|
+
SELECT COALESCE(MAX(height), ${fromHeight}) AS tip
|
|
50
|
+
FROM (
|
|
51
|
+
SELECT height, height - ROW_NUMBER() OVER (ORDER BY height) AS grp
|
|
52
|
+
FROM blocks WHERE canonical = true AND height >= ${fromHeight}
|
|
53
|
+
) sub
|
|
54
|
+
WHERE grp = (
|
|
55
|
+
SELECT height - ROW_NUMBER() OVER (ORDER BY height)
|
|
56
|
+
FROM blocks WHERE canonical = true AND height = ${fromHeight}
|
|
57
|
+
)
|
|
58
|
+
`.execute(db);
|
|
59
|
+
return Number(rows[0]?.tip ?? fromHeight);
|
|
60
|
+
}
|
|
61
|
+
export {
|
|
62
|
+
findGaps,
|
|
63
|
+
countMissingBlocks,
|
|
64
|
+
computeContiguousTip
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
//# debugId=370F0F5D6FDDF61064756E2164756E21
|
|
68
|
+
//# sourceMappingURL=integrity.js.map
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/db/queries/integrity.ts"],
|
|
4
|
+
"sourcesContent": [
|
|
5
|
+
"import { sql, type Kysely } from \"kysely\";\nimport type { Database } from \"../types.ts\";\n\nexport interface Gap {\n gapStart: number;\n gapEnd: number;\n size: number;\n}\n\nexport async function findGaps(db: Kysely<Database>, limit?: number): Promise<Gap[]> {\n const limitClause = limit ? sql`LIMIT ${limit}` : sql``;\n const { rows } = await sql<{ gap_start: string; gap_end: string; size: string }>`\n SELECT gap_start, gap_end, gap_end - gap_start + 1 AS size\n FROM (\n SELECT height + 1 AS gap_start, next_height - 1 AS gap_end\n FROM (\n SELECT height, LEAD(height) OVER (ORDER BY height) AS next_height\n FROM blocks WHERE canonical = true\n ) sub\n WHERE next_height - height > 1\n ) gaps\n ORDER BY gap_start\n ${limitClause}\n `.execute(db);\n\n return rows.map((r) => ({\n gapStart: Number(r.gap_start),\n gapEnd: Number(r.gap_end),\n size: Number(r.size),\n }));\n}\n\nexport async function countMissingBlocks(db: Kysely<Database>): Promise<number> {\n const { rows } = await sql<{ total: string }>`\n SELECT COALESCE(SUM(next_height - height - 1), 0) AS total\n FROM (\n SELECT height, LEAD(height) OVER (ORDER BY height) AS next_height\n FROM blocks WHERE canonical = true\n ) sub\n WHERE next_height - height > 1\n `.execute(db);\n\n return Number(rows[0]?.total ?? 0);\n}\n\nexport async function computeContiguousTip(db: Kysely<Database>, fromHeight: number): Promise<number> {\n const { rows } = await sql<{ tip: string }>`\n SELECT COALESCE(MAX(height), ${fromHeight}) AS tip\n FROM (\n SELECT height, height - ROW_NUMBER() OVER (ORDER BY height) AS grp\n FROM blocks WHERE canonical = true AND height >= ${fromHeight}\n ) sub\n WHERE grp = (\n SELECT height - ROW_NUMBER() OVER (ORDER BY height)\n FROM blocks WHERE canonical = true AND height = ${fromHeight}\n )\n `.execute(db);\n\n return Number(rows[0]?.tip ?? fromHeight);\n}\n"
|
|
6
|
+
],
|
|
7
|
+
"mappings": ";;;;;;;;;;;;;AAAA;AASA,eAAsB,QAAQ,CAAC,IAAsB,OAAgC;AAAA,EACnF,MAAM,cAAc,QAAQ,YAAY,UAAU;AAAA,EAClD,QAAQ,SAAS,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAWnB;AAAA,IACF,QAAQ,EAAE;AAAA,EAEZ,OAAO,KAAK,IAAI,CAAC,OAAO;AAAA,IACtB,UAAU,OAAO,EAAE,SAAS;AAAA,IAC5B,QAAQ,OAAO,EAAE,OAAO;AAAA,IACxB,MAAM,OAAO,EAAE,IAAI;AAAA,EACrB,EAAE;AAAA;AAGJ,eAAsB,kBAAkB,CAAC,IAAuC;AAAA,EAC9E,QAAQ,SAAS,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAOrB,QAAQ,EAAE;AAAA,EAEZ,OAAO,OAAO,KAAK,IAAI,SAAS,CAAC;AAAA;AAGnC,eAAsB,oBAAoB,CAAC,IAAsB,YAAqC;AAAA,EACpG,QAAQ,SAAS,MAAM;AAAA,mCACU;AAAA;AAAA;AAAA,yDAGsB;AAAA;AAAA;AAAA;AAAA,wDAID;AAAA;AAAA,IAEpD,QAAQ,EAAE;AAAA,EAEZ,OAAO,OAAO,KAAK,IAAI,OAAO,UAAU;AAAA;",
|
|
8
|
+
"debugId": "370F0F5D6FDDF61064756E2164756E21",
|
|
9
|
+
"names": []
|
|
10
|
+
}
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
import { Kysely } from "kysely";
|
|
2
|
+
import { Generated } from "kysely";
|
|
3
|
+
interface BlocksTable {
|
|
4
|
+
height: number;
|
|
5
|
+
hash: string;
|
|
6
|
+
parent_hash: string;
|
|
7
|
+
burn_block_height: number;
|
|
8
|
+
timestamp: number;
|
|
9
|
+
canonical: Generated<boolean>;
|
|
10
|
+
created_at: Generated<Date>;
|
|
11
|
+
}
|
|
12
|
+
interface TransactionsTable {
|
|
13
|
+
tx_id: string;
|
|
14
|
+
block_height: number;
|
|
15
|
+
type: string;
|
|
16
|
+
sender: string;
|
|
17
|
+
status: string;
|
|
18
|
+
contract_id: string | null;
|
|
19
|
+
function_name: string | null;
|
|
20
|
+
raw_tx: string;
|
|
21
|
+
created_at: Generated<Date>;
|
|
22
|
+
}
|
|
23
|
+
interface EventsTable {
|
|
24
|
+
id: Generated<string>;
|
|
25
|
+
tx_id: string;
|
|
26
|
+
block_height: number;
|
|
27
|
+
event_index: number;
|
|
28
|
+
type: string;
|
|
29
|
+
data: unknown;
|
|
30
|
+
created_at: Generated<Date>;
|
|
31
|
+
}
|
|
32
|
+
interface StreamsTable {
|
|
33
|
+
id: Generated<string>;
|
|
34
|
+
name: string;
|
|
35
|
+
status: Generated<string>;
|
|
36
|
+
filters: unknown;
|
|
37
|
+
options: Generated<unknown>;
|
|
38
|
+
webhook_url: string;
|
|
39
|
+
webhook_secret: string | null;
|
|
40
|
+
api_key_id: string | null;
|
|
41
|
+
created_at: Generated<Date>;
|
|
42
|
+
updated_at: Generated<Date>;
|
|
43
|
+
}
|
|
44
|
+
interface StreamMetricsTable {
|
|
45
|
+
stream_id: string;
|
|
46
|
+
last_triggered_at: Date | null;
|
|
47
|
+
last_triggered_block: number | null;
|
|
48
|
+
total_deliveries: Generated<number>;
|
|
49
|
+
failed_deliveries: Generated<number>;
|
|
50
|
+
error_message: string | null;
|
|
51
|
+
}
|
|
52
|
+
interface JobsTable {
|
|
53
|
+
id: Generated<string>;
|
|
54
|
+
stream_id: string;
|
|
55
|
+
block_height: number;
|
|
56
|
+
status: Generated<string>;
|
|
57
|
+
attempts: Generated<number>;
|
|
58
|
+
locked_at: Date | null;
|
|
59
|
+
locked_by: string | null;
|
|
60
|
+
error: string | null;
|
|
61
|
+
backfill: Generated<boolean>;
|
|
62
|
+
created_at: Generated<Date>;
|
|
63
|
+
completed_at: Date | null;
|
|
64
|
+
}
|
|
65
|
+
interface IndexProgressTable {
|
|
66
|
+
network: string;
|
|
67
|
+
last_indexed_block: Generated<number>;
|
|
68
|
+
last_contiguous_block: Generated<number>;
|
|
69
|
+
highest_seen_block: Generated<number>;
|
|
70
|
+
updated_at: Generated<Date>;
|
|
71
|
+
}
|
|
72
|
+
interface DeliveriesTable {
|
|
73
|
+
id: Generated<string>;
|
|
74
|
+
stream_id: string;
|
|
75
|
+
job_id: string | null;
|
|
76
|
+
block_height: number;
|
|
77
|
+
status: string;
|
|
78
|
+
status_code: number | null;
|
|
79
|
+
response_time_ms: number | null;
|
|
80
|
+
attempts: Generated<number>;
|
|
81
|
+
error: string | null;
|
|
82
|
+
payload: unknown;
|
|
83
|
+
created_at: Generated<Date>;
|
|
84
|
+
}
|
|
85
|
+
interface ViewsTable {
|
|
86
|
+
id: Generated<string>;
|
|
87
|
+
name: string;
|
|
88
|
+
version: Generated<string>;
|
|
89
|
+
status: Generated<string>;
|
|
90
|
+
definition: unknown;
|
|
91
|
+
schema_hash: string;
|
|
92
|
+
handler_path: string;
|
|
93
|
+
schema_name: string | null;
|
|
94
|
+
last_processed_block: Generated<number>;
|
|
95
|
+
last_error: string | null;
|
|
96
|
+
last_error_at: Date | null;
|
|
97
|
+
total_processed: Generated<number>;
|
|
98
|
+
total_errors: Generated<number>;
|
|
99
|
+
api_key_id: string | null;
|
|
100
|
+
created_at: Generated<Date>;
|
|
101
|
+
updated_at: Generated<Date>;
|
|
102
|
+
}
|
|
103
|
+
interface ApiKeysTable {
|
|
104
|
+
id: Generated<string>;
|
|
105
|
+
key_hash: string;
|
|
106
|
+
key_prefix: string;
|
|
107
|
+
name: string | null;
|
|
108
|
+
status: Generated<string>;
|
|
109
|
+
rate_limit: Generated<number>;
|
|
110
|
+
ip_address: string;
|
|
111
|
+
account_id: string;
|
|
112
|
+
last_used_at: Date | null;
|
|
113
|
+
revoked_at: Date | null;
|
|
114
|
+
created_at: Generated<Date>;
|
|
115
|
+
}
|
|
116
|
+
interface AccountsTable {
|
|
117
|
+
id: Generated<string>;
|
|
118
|
+
email: string;
|
|
119
|
+
plan: Generated<string>;
|
|
120
|
+
created_at: Generated<Date>;
|
|
121
|
+
}
|
|
122
|
+
interface SessionsTable {
|
|
123
|
+
id: Generated<string>;
|
|
124
|
+
token_hash: string;
|
|
125
|
+
token_prefix: string;
|
|
126
|
+
account_id: string;
|
|
127
|
+
ip_address: string;
|
|
128
|
+
expires_at: Generated<Date>;
|
|
129
|
+
revoked_at: Date | null;
|
|
130
|
+
last_used_at: Date | null;
|
|
131
|
+
created_at: Generated<Date>;
|
|
132
|
+
}
|
|
133
|
+
interface MagicLinksTable {
|
|
134
|
+
id: Generated<string>;
|
|
135
|
+
email: string;
|
|
136
|
+
token: string;
|
|
137
|
+
expires_at: Date;
|
|
138
|
+
used_at: Date | null;
|
|
139
|
+
created_at: Generated<Date>;
|
|
140
|
+
}
|
|
141
|
+
interface UsageDailyTable {
|
|
142
|
+
account_id: string;
|
|
143
|
+
date: string;
|
|
144
|
+
api_requests: Generated<number>;
|
|
145
|
+
deliveries: Generated<number>;
|
|
146
|
+
}
|
|
147
|
+
interface UsageSnapshotsTable {
|
|
148
|
+
id: Generated<string>;
|
|
149
|
+
account_id: string;
|
|
150
|
+
measured_at: Generated<Date>;
|
|
151
|
+
storage_bytes: Generated<number>;
|
|
152
|
+
}
|
|
153
|
+
interface Database {
|
|
154
|
+
blocks: BlocksTable;
|
|
155
|
+
transactions: TransactionsTable;
|
|
156
|
+
events: EventsTable;
|
|
157
|
+
streams: StreamsTable;
|
|
158
|
+
stream_metrics: StreamMetricsTable;
|
|
159
|
+
jobs: JobsTable;
|
|
160
|
+
index_progress: IndexProgressTable;
|
|
161
|
+
deliveries: DeliveriesTable;
|
|
162
|
+
views: ViewsTable;
|
|
163
|
+
api_keys: ApiKeysTable;
|
|
164
|
+
accounts: AccountsTable;
|
|
165
|
+
sessions: SessionsTable;
|
|
166
|
+
magic_links: MagicLinksTable;
|
|
167
|
+
usage_daily: UsageDailyTable;
|
|
168
|
+
usage_snapshots: UsageSnapshotsTable;
|
|
169
|
+
}
|
|
170
|
+
declare function getStreamMetrics(db: Kysely<Database>, streamId: string);
|
|
171
|
+
declare function updateStreamMetrics(db: Kysely<Database>, streamId: string, updates: Partial<{
|
|
172
|
+
lastTriggeredAt: Date
|
|
173
|
+
lastTriggeredBlock: number
|
|
174
|
+
totalDeliveries: number
|
|
175
|
+
failedDeliveries: number
|
|
176
|
+
errorMessage: string | null
|
|
177
|
+
}>);
|
|
178
|
+
declare function incrementDeliveryCount(db: Kysely<Database>, streamId: string, failed: boolean);
|
|
179
|
+
export { updateStreamMetrics, incrementDeliveryCount, getStreamMetrics };
|