@secondlayer/shared 0.7.1 → 0.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. package/dist/src/crypto/hmac.js +2 -2
  2. package/dist/src/crypto/hmac.js.map +3 -3
  3. package/dist/src/db/index.d.ts +15 -1
  4. package/dist/src/db/index.js +5 -3
  5. package/dist/src/db/index.js.map +4 -4
  6. package/dist/src/db/jsonb.js.map +2 -2
  7. package/dist/src/db/queries/accounts.d.ts +12 -0
  8. package/dist/src/db/queries/accounts.js.map +2 -2
  9. package/dist/src/db/queries/integrity.d.ts +12 -0
  10. package/dist/src/db/queries/integrity.js.map +2 -2
  11. package/dist/src/db/queries/metrics.d.ts +12 -0
  12. package/dist/src/db/queries/metrics.js.map +2 -2
  13. package/dist/src/db/queries/subgraph-gaps.d.ts +305 -0
  14. package/dist/src/db/queries/subgraph-gaps.js +103 -0
  15. package/dist/src/db/queries/subgraph-gaps.js.map +10 -0
  16. package/dist/src/db/queries/subgraphs.d.ts +13 -0
  17. package/dist/src/db/queries/subgraphs.js +4 -2
  18. package/dist/src/db/queries/subgraphs.js.map +4 -4
  19. package/dist/src/db/queries/usage.d.ts +12 -0
  20. package/dist/src/db/queries/usage.js +13 -3
  21. package/dist/src/db/queries/usage.js.map +4 -4
  22. package/dist/src/db/schema.d.ts +15 -1
  23. package/dist/src/env.js.map +2 -2
  24. package/dist/src/errors.js.map +2 -2
  25. package/dist/src/index.d.ts +59 -1
  26. package/dist/src/index.js +12 -8
  27. package/dist/src/index.js.map +12 -12
  28. package/dist/src/lib/plans.js.map +2 -2
  29. package/dist/src/logger.js.map +3 -3
  30. package/dist/src/node/archive-client.js +22 -6
  31. package/dist/src/node/archive-client.js.map +5 -5
  32. package/dist/src/node/client.js.map +2 -2
  33. package/dist/src/node/hiro-client.js +47 -11
  34. package/dist/src/node/hiro-client.js.map +5 -5
  35. package/dist/src/node/hiro-pg-client.js +131 -26
  36. package/dist/src/node/hiro-pg-client.js.map +3 -3
  37. package/dist/src/node/local-client.d.ts +12 -0
  38. package/dist/src/node/local-client.js.map +2 -2
  39. package/dist/src/queue/index.js +7 -5
  40. package/dist/src/queue/index.js.map +5 -5
  41. package/dist/src/queue/listener.js.map +2 -2
  42. package/dist/src/queue/recovery.js +5 -3
  43. package/dist/src/queue/recovery.js.map +5 -5
  44. package/dist/src/schemas/filters.js +2 -2
  45. package/dist/src/schemas/filters.js.map +3 -3
  46. package/dist/src/schemas/index.d.ts +45 -1
  47. package/dist/src/schemas/index.js +5 -3
  48. package/dist/src/schemas/index.js.map +5 -5
  49. package/dist/src/schemas/subgraphs.d.ts +45 -1
  50. package/dist/src/schemas/subgraphs.js.map +2 -2
  51. package/migrations/0001_initial.ts +295 -159
  52. package/migrations/0002_api_keys.ts +44 -28
  53. package/migrations/0003_tenant_isolation.ts +116 -107
  54. package/migrations/0004_accounts_and_usage.ts +81 -75
  55. package/migrations/0005_sessions.ts +33 -33
  56. package/migrations/0006_tx_index.ts +6 -2
  57. package/migrations/0007_contracts.ts +38 -24
  58. package/migrations/0008_drop_contracts.ts +33 -19
  59. package/migrations/0009_waitlist.ts +12 -12
  60. package/migrations/0010_waitlist_status.ts +5 -5
  61. package/migrations/0011_account_insights.ts +52 -52
  62. package/migrations/0012_view_health_snapshots.ts +21 -21
  63. package/migrations/0013_view_processing_stats.ts +32 -32
  64. package/migrations/0014_view_table_snapshots.ts +24 -24
  65. package/migrations/0015_rename_views_to_subgraphs.ts +137 -75
  66. package/migrations/0016_rename_webhook_to_endpoint.ts +12 -4
  67. package/migrations/0017_security_hardening.ts +23 -11
  68. package/migrations/0018_subgraph_gaps.ts +39 -0
  69. package/package.json +147 -143
@@ -1,159 +1,285 @@
1
1
  import { type Kysely, sql } from "kysely";
2
2
 
3
3
  export async function up(db: Kysely<any>): Promise<void> {
4
- // ── blocks ──────────────────────────────────────────────────────────
5
- await db.schema
6
- .createTable("blocks")
7
- .addColumn("height", "bigint", (c) => c.primaryKey())
8
- .addColumn("hash", "text", (c) => c.notNull())
9
- .addColumn("parent_hash", "text", (c) => c.notNull())
10
- .addColumn("burn_block_height", "bigint", (c) => c.notNull())
11
- .addColumn("timestamp", "bigint", (c) => c.notNull())
12
- .addColumn("canonical", "boolean", (c) => c.notNull().defaultTo(true))
13
- .addColumn("created_at", "timestamp", (c) => c.notNull().defaultTo(sql`now()`))
14
- .execute();
15
-
16
- await db.schema.createIndex("blocks_hash_idx").on("blocks").column("hash").execute();
17
- await db.schema.createIndex("blocks_canonical_height_idx").on("blocks").columns(["canonical", "height"]).execute();
18
-
19
- // ── transactions ────────────────────────────────────────────────────
20
- await db.schema
21
- .createTable("transactions")
22
- .addColumn("tx_id", "text", (c) => c.primaryKey())
23
- .addColumn("block_height", "bigint", (c) => c.notNull().references("blocks.height"))
24
- .addColumn("type", "text", (c) => c.notNull())
25
- .addColumn("sender", "text", (c) => c.notNull())
26
- .addColumn("status", "text", (c) => c.notNull())
27
- .addColumn("contract_id", "text")
28
- .addColumn("function_name", "text")
29
- .addColumn("raw_tx", "text", (c) => c.notNull())
30
- .addColumn("created_at", "timestamp", (c) => c.notNull().defaultTo(sql`now()`))
31
- .execute();
32
-
33
- await db.schema.createIndex("transactions_block_height_idx").on("transactions").column("block_height").execute();
34
- await db.schema.createIndex("transactions_sender_idx").on("transactions").column("sender").execute();
35
- await db.schema.createIndex("transactions_contract_id_idx").on("transactions").column("contract_id").execute();
36
-
37
- // ── events ──────────────────────────────────────────────────────────
38
- await db.schema
39
- .createTable("events")
40
- .addColumn("id", "uuid", (c) => c.primaryKey().defaultTo(sql`gen_random_uuid()`))
41
- .addColumn("tx_id", "text", (c) => c.notNull().references("transactions.tx_id"))
42
- .addColumn("block_height", "bigint", (c) => c.notNull().references("blocks.height"))
43
- .addColumn("event_index", "integer", (c) => c.notNull())
44
- .addColumn("type", "text", (c) => c.notNull())
45
- .addColumn("data", "jsonb", (c) => c.notNull())
46
- .addColumn("created_at", "timestamp", (c) => c.notNull().defaultTo(sql`now()`))
47
- .execute();
48
-
49
- await db.schema.createIndex("events_tx_id_idx").on("events").column("tx_id").execute();
50
- await db.schema.createIndex("events_block_height_idx").on("events").column("block_height").execute();
51
- await db.schema.createIndex("events_type_idx").on("events").column("type").execute();
52
-
53
- // ── streams ─────────────────────────────────────────────────────────
54
- await db.schema
55
- .createTable("streams")
56
- .addColumn("id", "uuid", (c) => c.primaryKey().defaultTo(sql`gen_random_uuid()`))
57
- .addColumn("name", "text", (c) => c.notNull())
58
- .addColumn("status", "text", (c) => c.notNull().defaultTo("active"))
59
- .addColumn("filters", "jsonb", (c) => c.notNull())
60
- .addColumn("options", "jsonb", (c) => c.notNull().defaultTo(sql`'{}'::jsonb`))
61
- .addColumn("webhook_url", "text", (c) => c.notNull())
62
- .addColumn("webhook_secret", "text")
63
- .addColumn("created_at", "timestamp", (c) => c.notNull().defaultTo(sql`now()`))
64
- .addColumn("updated_at", "timestamp", (c) => c.notNull().defaultTo(sql`now()`))
65
- .execute();
66
-
67
- await db.schema.createIndex("streams_status_idx").on("streams").column("status").execute();
68
-
69
- // ── stream_metrics ──────────────────────────────────────────────────
70
- await db.schema
71
- .createTable("stream_metrics")
72
- .addColumn("stream_id", "uuid", (c) => c.primaryKey().references("streams.id").onDelete("cascade"))
73
- .addColumn("last_triggered_at", "timestamp")
74
- .addColumn("last_triggered_block", "bigint")
75
- .addColumn("total_deliveries", "integer", (c) => c.notNull().defaultTo(0))
76
- .addColumn("failed_deliveries", "integer", (c) => c.notNull().defaultTo(0))
77
- .addColumn("error_message", "text")
78
- .execute();
79
-
80
- await db.schema.createIndex("stream_metrics_last_triggered_at_idx").on("stream_metrics").column("last_triggered_at").execute();
81
-
82
- // ── jobs ────────────────────────────────────────────────────────────
83
- await db.schema
84
- .createTable("jobs")
85
- .addColumn("id", "uuid", (c) => c.primaryKey().defaultTo(sql`gen_random_uuid()`))
86
- .addColumn("stream_id", "uuid", (c) => c.notNull().references("streams.id").onDelete("cascade"))
87
- .addColumn("block_height", "bigint", (c) => c.notNull())
88
- .addColumn("status", "text", (c) => c.notNull().defaultTo("pending"))
89
- .addColumn("attempts", "integer", (c) => c.notNull().defaultTo(0))
90
- .addColumn("locked_at", "timestamp")
91
- .addColumn("locked_by", "text")
92
- .addColumn("error", "text")
93
- .addColumn("backfill", "boolean", (c) => c.notNull().defaultTo(false))
94
- .addColumn("created_at", "timestamp", (c) => c.notNull().defaultTo(sql`now()`))
95
- .addColumn("completed_at", "timestamp")
96
- .execute();
97
-
98
- await db.schema.createIndex("jobs_stream_id_idx").on("jobs").column("stream_id").execute();
99
- await db.schema.createIndex("jobs_status_idx").on("jobs").column("status").execute();
100
- await db.schema.createIndex("jobs_block_height_idx").on("jobs").column("block_height").execute();
101
- await db.schema.createIndex("jobs_locked_at_idx").on("jobs").column("locked_at").execute();
102
-
103
- // ── index_progress ──────────────────────────────────────────────────
104
- await db.schema
105
- .createTable("index_progress")
106
- .addColumn("network", "text", (c) => c.primaryKey())
107
- .addColumn("last_indexed_block", "bigint", (c) => c.notNull().defaultTo(0))
108
- .addColumn("last_contiguous_block", "bigint", (c) => c.notNull().defaultTo(0))
109
- .addColumn("highest_seen_block", "bigint", (c) => c.notNull().defaultTo(0))
110
- .addColumn("updated_at", "timestamp", (c) => c.notNull().defaultTo(sql`now()`))
111
- .execute();
112
-
113
- // ── deliveries ──────────────────────────────────────────────────────
114
- await db.schema
115
- .createTable("deliveries")
116
- .addColumn("id", "uuid", (c) => c.primaryKey().defaultTo(sql`gen_random_uuid()`))
117
- .addColumn("stream_id", "uuid", (c) => c.notNull().references("streams.id").onDelete("cascade"))
118
- .addColumn("job_id", "uuid", (c) => c.references("jobs.id").onDelete("set null"))
119
- .addColumn("block_height", "bigint", (c) => c.notNull())
120
- .addColumn("status", "text", (c) => c.notNull())
121
- .addColumn("status_code", "integer")
122
- .addColumn("response_time_ms", "integer")
123
- .addColumn("attempts", "integer", (c) => c.notNull().defaultTo(1))
124
- .addColumn("error", "text")
125
- .addColumn("payload", "jsonb", (c) => c.notNull())
126
- .addColumn("created_at", "timestamp", (c) => c.notNull().defaultTo(sql`now()`))
127
- .execute();
128
-
129
- await db.schema.createIndex("deliveries_stream_id_idx").on("deliveries").column("stream_id").execute();
130
- await db.schema.createIndex("deliveries_status_idx").on("deliveries").column("status").execute();
131
- await db.schema.createIndex("deliveries_block_height_idx").on("deliveries").column("block_height").execute();
132
-
133
- // ── views ───────────────────────────────────────────────────────────
134
- await db.schema
135
- .createTable("views")
136
- .addColumn("id", "uuid", (c) => c.primaryKey().defaultTo(sql`gen_random_uuid()`))
137
- .addColumn("name", "text", (c) => c.notNull().unique())
138
- .addColumn("version", "text", (c) => c.notNull().defaultTo("1.0.0"))
139
- .addColumn("status", "text", (c) => c.notNull().defaultTo("active"))
140
- .addColumn("definition", "jsonb", (c) => c.notNull())
141
- .addColumn("schema_hash", "text", (c) => c.notNull())
142
- .addColumn("handler_path", "text", (c) => c.notNull())
143
- .addColumn("last_processed_block", "bigint", (c) => c.notNull().defaultTo(0))
144
- .addColumn("last_error", "text")
145
- .addColumn("last_error_at", "timestamp")
146
- .addColumn("total_processed", "bigint", (c) => c.notNull().defaultTo(0))
147
- .addColumn("total_errors", "bigint", (c) => c.notNull().defaultTo(0))
148
- .addColumn("created_at", "timestamp", (c) => c.notNull().defaultTo(sql`now()`))
149
- .addColumn("updated_at", "timestamp", (c) => c.notNull().defaultTo(sql`now()`))
150
- .execute();
151
-
152
- await db.schema.createIndex("views_name_idx").on("views").column("name").execute();
153
- await db.schema.createIndex("views_status_idx").on("views").column("status").execute();
154
-
155
- // Notify trigger for view changes (used by API hot-reload)
156
- await sql`
4
+ // ── blocks ──────────────────────────────────────────────────────────
5
+ await db.schema
6
+ .createTable("blocks")
7
+ .addColumn("height", "bigint", (c) => c.primaryKey())
8
+ .addColumn("hash", "text", (c) => c.notNull())
9
+ .addColumn("parent_hash", "text", (c) => c.notNull())
10
+ .addColumn("burn_block_height", "bigint", (c) => c.notNull())
11
+ .addColumn("timestamp", "bigint", (c) => c.notNull())
12
+ .addColumn("canonical", "boolean", (c) => c.notNull().defaultTo(true))
13
+ .addColumn("created_at", "timestamp", (c) =>
14
+ c.notNull().defaultTo(sql`now()`),
15
+ )
16
+ .execute();
17
+
18
+ await db.schema
19
+ .createIndex("blocks_hash_idx")
20
+ .on("blocks")
21
+ .column("hash")
22
+ .execute();
23
+ await db.schema
24
+ .createIndex("blocks_canonical_height_idx")
25
+ .on("blocks")
26
+ .columns(["canonical", "height"])
27
+ .execute();
28
+
29
+ // ── transactions ────────────────────────────────────────────────────
30
+ await db.schema
31
+ .createTable("transactions")
32
+ .addColumn("tx_id", "text", (c) => c.primaryKey())
33
+ .addColumn("block_height", "bigint", (c) =>
34
+ c.notNull().references("blocks.height"),
35
+ )
36
+ .addColumn("type", "text", (c) => c.notNull())
37
+ .addColumn("sender", "text", (c) => c.notNull())
38
+ .addColumn("status", "text", (c) => c.notNull())
39
+ .addColumn("contract_id", "text")
40
+ .addColumn("function_name", "text")
41
+ .addColumn("raw_tx", "text", (c) => c.notNull())
42
+ .addColumn("created_at", "timestamp", (c) =>
43
+ c.notNull().defaultTo(sql`now()`),
44
+ )
45
+ .execute();
46
+
47
+ await db.schema
48
+ .createIndex("transactions_block_height_idx")
49
+ .on("transactions")
50
+ .column("block_height")
51
+ .execute();
52
+ await db.schema
53
+ .createIndex("transactions_sender_idx")
54
+ .on("transactions")
55
+ .column("sender")
56
+ .execute();
57
+ await db.schema
58
+ .createIndex("transactions_contract_id_idx")
59
+ .on("transactions")
60
+ .column("contract_id")
61
+ .execute();
62
+
63
+ // ── events ──────────────────────────────────────────────────────────
64
+ await db.schema
65
+ .createTable("events")
66
+ .addColumn("id", "uuid", (c) =>
67
+ c.primaryKey().defaultTo(sql`gen_random_uuid()`),
68
+ )
69
+ .addColumn("tx_id", "text", (c) =>
70
+ c.notNull().references("transactions.tx_id"),
71
+ )
72
+ .addColumn("block_height", "bigint", (c) =>
73
+ c.notNull().references("blocks.height"),
74
+ )
75
+ .addColumn("event_index", "integer", (c) => c.notNull())
76
+ .addColumn("type", "text", (c) => c.notNull())
77
+ .addColumn("data", "jsonb", (c) => c.notNull())
78
+ .addColumn("created_at", "timestamp", (c) =>
79
+ c.notNull().defaultTo(sql`now()`),
80
+ )
81
+ .execute();
82
+
83
+ await db.schema
84
+ .createIndex("events_tx_id_idx")
85
+ .on("events")
86
+ .column("tx_id")
87
+ .execute();
88
+ await db.schema
89
+ .createIndex("events_block_height_idx")
90
+ .on("events")
91
+ .column("block_height")
92
+ .execute();
93
+ await db.schema
94
+ .createIndex("events_type_idx")
95
+ .on("events")
96
+ .column("type")
97
+ .execute();
98
+
99
+ // ── streams ─────────────────────────────────────────────────────────
100
+ await db.schema
101
+ .createTable("streams")
102
+ .addColumn("id", "uuid", (c) =>
103
+ c.primaryKey().defaultTo(sql`gen_random_uuid()`),
104
+ )
105
+ .addColumn("name", "text", (c) => c.notNull())
106
+ .addColumn("status", "text", (c) => c.notNull().defaultTo("active"))
107
+ .addColumn("filters", "jsonb", (c) => c.notNull())
108
+ .addColumn("options", "jsonb", (c) =>
109
+ c.notNull().defaultTo(sql`'{}'::jsonb`),
110
+ )
111
+ .addColumn("webhook_url", "text", (c) => c.notNull())
112
+ .addColumn("webhook_secret", "text")
113
+ .addColumn("created_at", "timestamp", (c) =>
114
+ c.notNull().defaultTo(sql`now()`),
115
+ )
116
+ .addColumn("updated_at", "timestamp", (c) =>
117
+ c.notNull().defaultTo(sql`now()`),
118
+ )
119
+ .execute();
120
+
121
+ await db.schema
122
+ .createIndex("streams_status_idx")
123
+ .on("streams")
124
+ .column("status")
125
+ .execute();
126
+
127
+ // ── stream_metrics ──────────────────────────────────────────────────
128
+ await db.schema
129
+ .createTable("stream_metrics")
130
+ .addColumn("stream_id", "uuid", (c) =>
131
+ c.primaryKey().references("streams.id").onDelete("cascade"),
132
+ )
133
+ .addColumn("last_triggered_at", "timestamp")
134
+ .addColumn("last_triggered_block", "bigint")
135
+ .addColumn("total_deliveries", "integer", (c) => c.notNull().defaultTo(0))
136
+ .addColumn("failed_deliveries", "integer", (c) => c.notNull().defaultTo(0))
137
+ .addColumn("error_message", "text")
138
+ .execute();
139
+
140
+ await db.schema
141
+ .createIndex("stream_metrics_last_triggered_at_idx")
142
+ .on("stream_metrics")
143
+ .column("last_triggered_at")
144
+ .execute();
145
+
146
+ // ── jobs ────────────────────────────────────────────────────────────
147
+ await db.schema
148
+ .createTable("jobs")
149
+ .addColumn("id", "uuid", (c) =>
150
+ c.primaryKey().defaultTo(sql`gen_random_uuid()`),
151
+ )
152
+ .addColumn("stream_id", "uuid", (c) =>
153
+ c.notNull().references("streams.id").onDelete("cascade"),
154
+ )
155
+ .addColumn("block_height", "bigint", (c) => c.notNull())
156
+ .addColumn("status", "text", (c) => c.notNull().defaultTo("pending"))
157
+ .addColumn("attempts", "integer", (c) => c.notNull().defaultTo(0))
158
+ .addColumn("locked_at", "timestamp")
159
+ .addColumn("locked_by", "text")
160
+ .addColumn("error", "text")
161
+ .addColumn("backfill", "boolean", (c) => c.notNull().defaultTo(false))
162
+ .addColumn("created_at", "timestamp", (c) =>
163
+ c.notNull().defaultTo(sql`now()`),
164
+ )
165
+ .addColumn("completed_at", "timestamp")
166
+ .execute();
167
+
168
+ await db.schema
169
+ .createIndex("jobs_stream_id_idx")
170
+ .on("jobs")
171
+ .column("stream_id")
172
+ .execute();
173
+ await db.schema
174
+ .createIndex("jobs_status_idx")
175
+ .on("jobs")
176
+ .column("status")
177
+ .execute();
178
+ await db.schema
179
+ .createIndex("jobs_block_height_idx")
180
+ .on("jobs")
181
+ .column("block_height")
182
+ .execute();
183
+ await db.schema
184
+ .createIndex("jobs_locked_at_idx")
185
+ .on("jobs")
186
+ .column("locked_at")
187
+ .execute();
188
+
189
+ // ── index_progress ──────────────────────────────────────────────────
190
+ await db.schema
191
+ .createTable("index_progress")
192
+ .addColumn("network", "text", (c) => c.primaryKey())
193
+ .addColumn("last_indexed_block", "bigint", (c) => c.notNull().defaultTo(0))
194
+ .addColumn("last_contiguous_block", "bigint", (c) =>
195
+ c.notNull().defaultTo(0),
196
+ )
197
+ .addColumn("highest_seen_block", "bigint", (c) => c.notNull().defaultTo(0))
198
+ .addColumn("updated_at", "timestamp", (c) =>
199
+ c.notNull().defaultTo(sql`now()`),
200
+ )
201
+ .execute();
202
+
203
+ // ── deliveries ──────────────────────────────────────────────────────
204
+ await db.schema
205
+ .createTable("deliveries")
206
+ .addColumn("id", "uuid", (c) =>
207
+ c.primaryKey().defaultTo(sql`gen_random_uuid()`),
208
+ )
209
+ .addColumn("stream_id", "uuid", (c) =>
210
+ c.notNull().references("streams.id").onDelete("cascade"),
211
+ )
212
+ .addColumn("job_id", "uuid", (c) =>
213
+ c.references("jobs.id").onDelete("set null"),
214
+ )
215
+ .addColumn("block_height", "bigint", (c) => c.notNull())
216
+ .addColumn("status", "text", (c) => c.notNull())
217
+ .addColumn("status_code", "integer")
218
+ .addColumn("response_time_ms", "integer")
219
+ .addColumn("attempts", "integer", (c) => c.notNull().defaultTo(1))
220
+ .addColumn("error", "text")
221
+ .addColumn("payload", "jsonb", (c) => c.notNull())
222
+ .addColumn("created_at", "timestamp", (c) =>
223
+ c.notNull().defaultTo(sql`now()`),
224
+ )
225
+ .execute();
226
+
227
+ await db.schema
228
+ .createIndex("deliveries_stream_id_idx")
229
+ .on("deliveries")
230
+ .column("stream_id")
231
+ .execute();
232
+ await db.schema
233
+ .createIndex("deliveries_status_idx")
234
+ .on("deliveries")
235
+ .column("status")
236
+ .execute();
237
+ await db.schema
238
+ .createIndex("deliveries_block_height_idx")
239
+ .on("deliveries")
240
+ .column("block_height")
241
+ .execute();
242
+
243
+ // ── views ───────────────────────────────────────────────────────────
244
+ await db.schema
245
+ .createTable("views")
246
+ .addColumn("id", "uuid", (c) =>
247
+ c.primaryKey().defaultTo(sql`gen_random_uuid()`),
248
+ )
249
+ .addColumn("name", "text", (c) => c.notNull().unique())
250
+ .addColumn("version", "text", (c) => c.notNull().defaultTo("1.0.0"))
251
+ .addColumn("status", "text", (c) => c.notNull().defaultTo("active"))
252
+ .addColumn("definition", "jsonb", (c) => c.notNull())
253
+ .addColumn("schema_hash", "text", (c) => c.notNull())
254
+ .addColumn("handler_path", "text", (c) => c.notNull())
255
+ .addColumn("last_processed_block", "bigint", (c) =>
256
+ c.notNull().defaultTo(0),
257
+ )
258
+ .addColumn("last_error", "text")
259
+ .addColumn("last_error_at", "timestamp")
260
+ .addColumn("total_processed", "bigint", (c) => c.notNull().defaultTo(0))
261
+ .addColumn("total_errors", "bigint", (c) => c.notNull().defaultTo(0))
262
+ .addColumn("created_at", "timestamp", (c) =>
263
+ c.notNull().defaultTo(sql`now()`),
264
+ )
265
+ .addColumn("updated_at", "timestamp", (c) =>
266
+ c.notNull().defaultTo(sql`now()`),
267
+ )
268
+ .execute();
269
+
270
+ await db.schema
271
+ .createIndex("views_name_idx")
272
+ .on("views")
273
+ .column("name")
274
+ .execute();
275
+ await db.schema
276
+ .createIndex("views_status_idx")
277
+ .on("views")
278
+ .column("status")
279
+ .execute();
280
+
281
+ // Notify trigger for view changes (used by API hot-reload)
282
+ await sql`
157
283
  CREATE OR REPLACE FUNCTION notify_view_changes() RETURNS trigger AS $$
158
284
  BEGIN
159
285
  PERFORM pg_notify('view_changes', json_build_object(
@@ -165,7 +291,7 @@ export async function up(db: Kysely<any>): Promise<void> {
165
291
  $$ LANGUAGE plpgsql
166
292
  `.execute(db);
167
293
 
168
- await sql`
294
+ await sql`
169
295
  CREATE TRIGGER views_notify_trigger
170
296
  AFTER INSERT OR UPDATE OR DELETE ON "views"
171
297
  FOR EACH ROW EXECUTE FUNCTION notify_view_changes()
@@ -173,10 +299,20 @@ export async function up(db: Kysely<any>): Promise<void> {
173
299
  }
174
300
 
175
301
  export async function down(db: Kysely<any>): Promise<void> {
176
- await sql`DROP TRIGGER IF EXISTS views_notify_trigger ON "views"`.execute(db);
177
- await sql`DROP FUNCTION IF EXISTS notify_view_changes()`.execute(db);
302
+ await sql`DROP TRIGGER IF EXISTS views_notify_trigger ON "views"`.execute(db);
303
+ await sql`DROP FUNCTION IF EXISTS notify_view_changes()`.execute(db);
178
304
 
179
- for (const table of ["views", "deliveries", "index_progress", "jobs", "stream_metrics", "streams", "events", "transactions", "blocks"]) {
180
- await db.schema.dropTable(table).ifExists().cascade().execute();
181
- }
305
+ for (const table of [
306
+ "views",
307
+ "deliveries",
308
+ "index_progress",
309
+ "jobs",
310
+ "stream_metrics",
311
+ "streams",
312
+ "events",
313
+ "transactions",
314
+ "blocks",
315
+ ]) {
316
+ await db.schema.dropTable(table).ifExists().cascade().execute();
317
+ }
182
318
  }
@@ -1,38 +1,54 @@
1
1
  import { type Kysely, sql } from "kysely";
2
2
 
3
3
  export async function up(db: Kysely<any>): Promise<void> {
4
- await db.schema
5
- .createTable("api_keys")
6
- .addColumn("id", "uuid", (c) => c.primaryKey().defaultTo(sql`gen_random_uuid()`))
7
- .addColumn("key_hash", "text", (c) => c.notNull().unique())
8
- .addColumn("key_prefix", "text", (c) => c.notNull())
9
- .addColumn("name", "text")
10
- .addColumn("status", "text", (c) => c.notNull().defaultTo("active"))
11
- .addColumn("rate_limit", "integer", (c) => c.notNull().defaultTo(120))
12
- .addColumn("ip_address", "text", (c) => c.notNull())
13
- .addColumn("last_used_at", "timestamp")
14
- .addColumn("revoked_at", "timestamp")
15
- .addColumn("created_at", "timestamp", (c) => c.notNull().defaultTo(sql`now()`))
16
- .execute();
4
+ await db.schema
5
+ .createTable("api_keys")
6
+ .addColumn("id", "uuid", (c) =>
7
+ c.primaryKey().defaultTo(sql`gen_random_uuid()`),
8
+ )
9
+ .addColumn("key_hash", "text", (c) => c.notNull().unique())
10
+ .addColumn("key_prefix", "text", (c) => c.notNull())
11
+ .addColumn("name", "text")
12
+ .addColumn("status", "text", (c) => c.notNull().defaultTo("active"))
13
+ .addColumn("rate_limit", "integer", (c) => c.notNull().defaultTo(120))
14
+ .addColumn("ip_address", "text", (c) => c.notNull())
15
+ .addColumn("last_used_at", "timestamp")
16
+ .addColumn("revoked_at", "timestamp")
17
+ .addColumn("created_at", "timestamp", (c) =>
18
+ c.notNull().defaultTo(sql`now()`),
19
+ )
20
+ .execute();
17
21
 
18
- await db.schema.createIndex("api_keys_key_hash_idx").on("api_keys").column("key_hash").execute();
19
- await db.schema.createIndex("api_keys_status_idx").on("api_keys").column("status").execute();
20
- await db.schema.createIndex("api_keys_ip_address_idx").on("api_keys").column("ip_address").execute();
22
+ await db.schema
23
+ .createIndex("api_keys_key_hash_idx")
24
+ .on("api_keys")
25
+ .column("key_hash")
26
+ .execute();
27
+ await db.schema
28
+ .createIndex("api_keys_status_idx")
29
+ .on("api_keys")
30
+ .column("status")
31
+ .execute();
32
+ await db.schema
33
+ .createIndex("api_keys_ip_address_idx")
34
+ .on("api_keys")
35
+ .column("ip_address")
36
+ .execute();
21
37
 
22
- // Add api_key_id to streams and views
23
- await db.schema
24
- .alterTable("streams")
25
- .addColumn("api_key_id", "uuid", (c) => c.references("api_keys.id"))
26
- .execute();
38
+ // Add api_key_id to streams and views
39
+ await db.schema
40
+ .alterTable("streams")
41
+ .addColumn("api_key_id", "uuid", (c) => c.references("api_keys.id"))
42
+ .execute();
27
43
 
28
- await db.schema
29
- .alterTable("views")
30
- .addColumn("api_key_id", "uuid", (c) => c.references("api_keys.id"))
31
- .execute();
44
+ await db.schema
45
+ .alterTable("views")
46
+ .addColumn("api_key_id", "uuid", (c) => c.references("api_keys.id"))
47
+ .execute();
32
48
  }
33
49
 
34
50
  export async function down(db: Kysely<any>): Promise<void> {
35
- await db.schema.alterTable("views").dropColumn("api_key_id").execute();
36
- await db.schema.alterTable("streams").dropColumn("api_key_id").execute();
37
- await db.schema.dropTable("api_keys").ifExists().cascade().execute();
51
+ await db.schema.alterTable("views").dropColumn("api_key_id").execute();
52
+ await db.schema.alterTable("streams").dropColumn("api_key_id").execute();
53
+ await db.schema.dropTable("api_keys").ifExists().cascade().execute();
38
54
  }