@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.
Files changed (76) hide show
  1. package/README.md +19 -0
  2. package/dist/src/crypto/hmac.d.ts +26 -0
  3. package/dist/src/crypto/hmac.js +75 -0
  4. package/dist/src/crypto/hmac.js.map +10 -0
  5. package/dist/src/db/index.d.ts +227 -0
  6. package/dist/src/db/index.js +75 -0
  7. package/dist/src/db/index.js.map +11 -0
  8. package/dist/src/db/jsonb.d.ts +13 -0
  9. package/dist/src/db/jsonb.js +35 -0
  10. package/dist/src/db/jsonb.js.map +10 -0
  11. package/dist/src/db/queries/accounts.d.ts +179 -0
  12. package/dist/src/db/queries/accounts.js +39 -0
  13. package/dist/src/db/queries/accounts.js.map +10 -0
  14. package/dist/src/db/queries/integrity.d.ts +178 -0
  15. package/dist/src/db/queries/integrity.js +68 -0
  16. package/dist/src/db/queries/integrity.js.map +10 -0
  17. package/dist/src/db/queries/metrics.d.ts +179 -0
  18. package/dist/src/db/queries/metrics.js +51 -0
  19. package/dist/src/db/queries/metrics.js.map +10 -0
  20. package/dist/src/db/queries/usage.d.ts +205 -0
  21. package/dist/src/db/queries/usage.js +117 -0
  22. package/dist/src/db/queries/usage.js.map +11 -0
  23. package/dist/src/db/queries/views.d.ts +191 -0
  24. package/dist/src/db/queries/views.js +111 -0
  25. package/dist/src/db/queries/views.js.map +11 -0
  26. package/dist/src/db/schema.d.ts +207 -0
  27. package/dist/src/db/schema.js +3 -0
  28. package/dist/src/db/schema.js.map +9 -0
  29. package/dist/src/env.d.ts +7 -0
  30. package/dist/src/env.js +60 -0
  31. package/dist/src/env.js.map +10 -0
  32. package/dist/src/errors.d.ts +51 -0
  33. package/dist/src/errors.js +103 -0
  34. package/dist/src/errors.js.map +10 -0
  35. package/dist/src/index.d.ts +464 -0
  36. package/dist/src/index.js +642 -0
  37. package/dist/src/index.js.map +19 -0
  38. package/dist/src/lib/plans.d.ts +10 -0
  39. package/dist/src/lib/plans.js +34 -0
  40. package/dist/src/lib/plans.js.map +10 -0
  41. package/dist/src/logger.d.ts +2 -0
  42. package/dist/src/logger.js +130 -0
  43. package/dist/src/logger.js.map +11 -0
  44. package/dist/src/node/client.d.ts +35 -0
  45. package/dist/src/node/client.js +56 -0
  46. package/dist/src/node/client.js.map +10 -0
  47. package/dist/src/node/hiro-client.d.ts +186 -0
  48. package/dist/src/node/hiro-client.js +410 -0
  49. package/dist/src/node/hiro-client.js.map +12 -0
  50. package/dist/src/queue/index.d.ts +50 -0
  51. package/dist/src/queue/index.js +176 -0
  52. package/dist/src/queue/index.js.map +12 -0
  53. package/dist/src/queue/listener.d.ts +20 -0
  54. package/dist/src/queue/listener.js +63 -0
  55. package/dist/src/queue/listener.js.map +10 -0
  56. package/dist/src/queue/recovery.d.ts +14 -0
  57. package/dist/src/queue/recovery.js +100 -0
  58. package/dist/src/queue/recovery.js.map +12 -0
  59. package/dist/src/schemas/filters.d.ts +30 -0
  60. package/dist/src/schemas/filters.js +133 -0
  61. package/dist/src/schemas/filters.js.map +10 -0
  62. package/dist/src/schemas/index.d.ts +109 -0
  63. package/dist/src/schemas/index.js +228 -0
  64. package/dist/src/schemas/index.js.map +12 -0
  65. package/dist/src/schemas/views.d.ts +51 -0
  66. package/dist/src/schemas/views.js +29 -0
  67. package/dist/src/schemas/views.js.map +10 -0
  68. package/dist/src/types.d.ts +102 -0
  69. package/dist/src/types.js +3 -0
  70. package/dist/src/types.js.map +9 -0
  71. package/migrations/0001_initial.ts +182 -0
  72. package/migrations/0002_api_keys.ts +38 -0
  73. package/migrations/0003_tenant_isolation.ts +114 -0
  74. package/migrations/0004_accounts_and_usage.ts +90 -0
  75. package/migrations/0005_sessions.ts +42 -0
  76. 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 };