@secondlayer/shared 4.2.0 → 4.3.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.
@@ -0,0 +1,461 @@
1
+ import { Kysely } from "kysely";
2
+ import { ColumnType, 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
+ tx_index: Generated<number>;
16
+ type: string;
17
+ sender: string;
18
+ status: string;
19
+ contract_id: string | null;
20
+ function_name: string | null;
21
+ function_args: Generated<unknown | null>;
22
+ raw_result: Generated<string | null>;
23
+ raw_tx: string;
24
+ created_at: Generated<Date>;
25
+ }
26
+ interface EventsTable {
27
+ id: Generated<string>;
28
+ tx_id: string;
29
+ block_height: number;
30
+ event_index: number;
31
+ type: string;
32
+ data: unknown;
33
+ created_at: Generated<Date>;
34
+ }
35
+ interface IndexProgressTable {
36
+ network: string;
37
+ last_indexed_block: Generated<number>;
38
+ last_contiguous_block: Generated<number>;
39
+ highest_seen_block: Generated<number>;
40
+ updated_at: Generated<Date>;
41
+ }
42
+ interface SubgraphsTable {
43
+ id: Generated<string>;
44
+ name: string;
45
+ version: Generated<string>;
46
+ status: Generated<string>;
47
+ definition: Record<string, unknown>;
48
+ schema_hash: string;
49
+ handler_path: string;
50
+ schema_name: string | null;
51
+ start_block: Generated<number>;
52
+ last_processed_block: Generated<number>;
53
+ reindex_from_block: number | null;
54
+ reindex_to_block: number | null;
55
+ last_error: string | null;
56
+ last_error_at: Date | null;
57
+ total_processed: Generated<number>;
58
+ total_errors: Generated<number>;
59
+ account_id: string;
60
+ handler_code: string | null;
61
+ source_code: string | null;
62
+ project_id: string | null;
63
+ created_at: Generated<Date>;
64
+ updated_at: Generated<Date>;
65
+ }
66
+ interface SubgraphGapsTable {
67
+ id: Generated<string>;
68
+ subgraph_id: string;
69
+ subgraph_name: string;
70
+ gap_start: number;
71
+ gap_end: number;
72
+ reason: string;
73
+ detected_at: Generated<Date>;
74
+ resolved_at: Date | null;
75
+ }
76
+ type SubgraphOperationKind = "reindex" | "backfill";
77
+ type SubgraphOperationStatus = "queued" | "running" | "completed" | "failed" | "cancelled";
78
+ interface SubgraphOperationsTable {
79
+ id: Generated<string>;
80
+ subgraph_id: string;
81
+ subgraph_name: string;
82
+ account_id: string | null;
83
+ kind: ColumnType<SubgraphOperationKind, SubgraphOperationKind, SubgraphOperationKind>;
84
+ status: ColumnType<SubgraphOperationStatus, SubgraphOperationStatus | undefined, SubgraphOperationStatus>;
85
+ from_block: number | null;
86
+ to_block: number | null;
87
+ cancel_requested: Generated<boolean>;
88
+ locked_by: string | null;
89
+ locked_until: Date | null;
90
+ started_at: Date | null;
91
+ finished_at: Date | null;
92
+ processed_blocks: number | null;
93
+ error: string | null;
94
+ created_at: Generated<Date>;
95
+ updated_at: Generated<Date>;
96
+ }
97
+ interface ApiKeysTable {
98
+ id: Generated<string>;
99
+ key_hash: string;
100
+ key_prefix: string;
101
+ name: string | null;
102
+ status: Generated<string>;
103
+ rate_limit: Generated<number>;
104
+ ip_address: string;
105
+ account_id: string;
106
+ last_used_at: Date | null;
107
+ revoked_at: Date | null;
108
+ created_at: Generated<Date>;
109
+ }
110
+ interface AccountsTable {
111
+ id: Generated<string>;
112
+ email: string;
113
+ plan: Generated<string>;
114
+ display_name: string | null;
115
+ bio: string | null;
116
+ avatar_url: string | null;
117
+ slug: string | null;
118
+ stripe_customer_id: string | null;
119
+ created_at: Generated<Date>;
120
+ }
121
+ interface SessionsTable {
122
+ id: Generated<string>;
123
+ token_hash: string;
124
+ token_prefix: string;
125
+ account_id: string;
126
+ ip_address: string;
127
+ expires_at: Generated<Date>;
128
+ revoked_at: Date | null;
129
+ last_used_at: Date | null;
130
+ created_at: Generated<Date>;
131
+ }
132
+ interface MagicLinksTable {
133
+ id: Generated<string>;
134
+ email: string;
135
+ token: string;
136
+ code: string | null;
137
+ expires_at: Date;
138
+ used_at: Date | null;
139
+ failed_attempts: Generated<number>;
140
+ created_at: Generated<Date>;
141
+ }
142
+ interface UsageDailyTable {
143
+ account_id: string;
144
+ tenant_id: string | null;
145
+ date: string;
146
+ api_requests: Generated<number>;
147
+ deliveries: Generated<number>;
148
+ }
149
+ interface UsageSnapshotsTable {
150
+ id: Generated<string>;
151
+ account_id: string;
152
+ measured_at: Generated<Date>;
153
+ storage_bytes: Generated<number>;
154
+ }
155
+ interface WaitlistTable {
156
+ id: Generated<string>;
157
+ email: string;
158
+ source: Generated<string>;
159
+ status: Generated<string>;
160
+ created_at: Generated<Date>;
161
+ }
162
+ interface AccountInsightsTable {
163
+ id: Generated<string>;
164
+ account_id: string;
165
+ category: string;
166
+ insight_type: string;
167
+ resource_id: string | null;
168
+ severity: string;
169
+ title: string;
170
+ body: string;
171
+ data: unknown;
172
+ dismissed_at: Date | null;
173
+ expires_at: Date | null;
174
+ created_at: Generated<Date>;
175
+ }
176
+ interface AccountAgentRunsTable {
177
+ id: Generated<string>;
178
+ account_id: string;
179
+ started_at: Generated<Date>;
180
+ completed_at: Date | null;
181
+ status: Generated<string>;
182
+ input_tokens: Generated<number>;
183
+ output_tokens: Generated<number>;
184
+ cost_usd: Generated<number>;
185
+ insights_created: Generated<number>;
186
+ error: string | null;
187
+ }
188
+ interface SubgraphProcessingStatsTable {
189
+ id: Generated<string>;
190
+ subgraph_name: string;
191
+ api_key_id: string | null;
192
+ bucket_start: Date | null;
193
+ bucket_end: Date | null;
194
+ blocks_processed: number | null;
195
+ total_time_ms: number | null;
196
+ handler_time_ms: number | null;
197
+ flush_time_ms: number | null;
198
+ max_block_time_ms: number | null;
199
+ max_handler_time_ms: number | null;
200
+ avg_ops_per_block: number | null;
201
+ is_catchup: Generated<boolean>;
202
+ created_at: Generated<Date>;
203
+ }
204
+ interface SubgraphTableSnapshotsTable {
205
+ id: Generated<string>;
206
+ subgraph_name: string;
207
+ api_key_id: string | null;
208
+ table_name: string;
209
+ row_count: number | null;
210
+ created_at: Generated<Date>;
211
+ }
212
+ interface SubgraphHealthSnapshotsTable {
213
+ id: Generated<string>;
214
+ subgraph_id: string;
215
+ total_processed: number;
216
+ total_errors: number;
217
+ last_processed_block: number | null;
218
+ captured_at: Generated<Date>;
219
+ }
220
+ interface SubgraphUsageDailyTable {
221
+ subgraph_id: string;
222
+ date: string;
223
+ query_count: Generated<number>;
224
+ }
225
+ interface ProjectsTable {
226
+ id: Generated<string>;
227
+ name: string;
228
+ slug: string;
229
+ account_id: string;
230
+ settings: Generated<Record<string, unknown>>;
231
+ network: Generated<string>;
232
+ node_rpc: string | null;
233
+ created_at: Generated<Date>;
234
+ updated_at: Generated<Date>;
235
+ }
236
+ interface TeamMembersTable {
237
+ id: Generated<string>;
238
+ project_id: string;
239
+ account_id: string;
240
+ role: Generated<string>;
241
+ invited_by: string | null;
242
+ created_at: Generated<Date>;
243
+ }
244
+ interface TeamInvitationsTable {
245
+ id: Generated<string>;
246
+ project_id: string;
247
+ email: string;
248
+ role: Generated<string>;
249
+ token: string;
250
+ invited_by: string | null;
251
+ expires_at: Date;
252
+ accepted_at: Date | null;
253
+ created_at: Generated<Date>;
254
+ }
255
+ interface ChatSessionsTable {
256
+ id: Generated<string>;
257
+ account_id: string;
258
+ title: string | null;
259
+ summary: unknown | null;
260
+ created_at: Generated<Date>;
261
+ updated_at: Generated<Date>;
262
+ }
263
+ interface ChatMessagesTable {
264
+ id: Generated<string>;
265
+ chat_session_id: string;
266
+ role: string;
267
+ parts: unknown;
268
+ metadata: unknown | null;
269
+ created_at: Generated<Date>;
270
+ }
271
+ interface Database {
272
+ blocks: BlocksTable;
273
+ transactions: TransactionsTable;
274
+ events: EventsTable;
275
+ index_progress: IndexProgressTable;
276
+ subgraphs: SubgraphsTable;
277
+ api_keys: ApiKeysTable;
278
+ accounts: AccountsTable;
279
+ sessions: SessionsTable;
280
+ magic_links: MagicLinksTable;
281
+ usage_daily: UsageDailyTable;
282
+ usage_snapshots: UsageSnapshotsTable;
283
+ waitlist: WaitlistTable;
284
+ account_insights: AccountInsightsTable;
285
+ account_agent_runs: AccountAgentRunsTable;
286
+ subgraph_health_snapshots: SubgraphHealthSnapshotsTable;
287
+ subgraph_processing_stats: SubgraphProcessingStatsTable;
288
+ subgraph_table_snapshots: SubgraphTableSnapshotsTable;
289
+ subgraph_gaps: SubgraphGapsTable;
290
+ subgraph_operations: SubgraphOperationsTable;
291
+ subgraph_usage_daily: SubgraphUsageDailyTable;
292
+ projects: ProjectsTable;
293
+ team_members: TeamMembersTable;
294
+ team_invitations: TeamInvitationsTable;
295
+ chat_sessions: ChatSessionsTable;
296
+ chat_messages: ChatMessagesTable;
297
+ tenants: TenantsTable;
298
+ tenant_usage_monthly: TenantUsageMonthlyTable;
299
+ tenant_compute_addons: TenantComputeAddonsTable;
300
+ account_spend_caps: AccountSpendCapsTable;
301
+ provisioning_audit_log: ProvisioningAuditLogTable;
302
+ subscriptions: SubscriptionsTable;
303
+ subscription_outbox: SubscriptionOutboxTable;
304
+ subscription_deliveries: SubscriptionDeliveriesTable;
305
+ }
306
+ type TenantStatus = "provisioning" | "active" | "suspended" | "error" | "deleted";
307
+ interface TenantsTable {
308
+ id: Generated<string>;
309
+ account_id: string;
310
+ slug: string;
311
+ status: ColumnType<TenantStatus, TenantStatus | undefined, TenantStatus>;
312
+ plan: string;
313
+ cpus: ColumnType<number, number | string, number | string>;
314
+ memory_mb: number;
315
+ storage_limit_mb: number;
316
+ storage_used_mb: number | null;
317
+ pg_container_id: string | null;
318
+ api_container_id: string | null;
319
+ processor_container_id: string | null;
320
+ target_database_url_enc: Buffer;
321
+ tenant_jwt_secret_enc: Buffer;
322
+ anon_key_enc: Buffer;
323
+ service_key_enc: Buffer;
324
+ api_url_internal: string;
325
+ api_url_public: string;
326
+ suspended_at: Date | null;
327
+ last_health_check_at: Date | null;
328
+ last_active_at: Generated<Date>;
329
+ service_gen: Generated<number>;
330
+ anon_gen: Generated<number>;
331
+ project_id: string | null;
332
+ created_at: Generated<Date>;
333
+ updated_at: Generated<Date>;
334
+ }
335
+ interface TenantUsageMonthlyTable {
336
+ id: Generated<string>;
337
+ tenant_id: string;
338
+ period_month: Date;
339
+ storage_peak_mb: Generated<number>;
340
+ storage_avg_mb: Generated<number>;
341
+ storage_last_mb: Generated<number>;
342
+ measurements: Generated<number>;
343
+ first_at: Generated<Date>;
344
+ last_at: Generated<Date>;
345
+ }
346
+ interface TenantComputeAddonsTable {
347
+ id: Generated<string>;
348
+ tenant_id: string;
349
+ memory_mb_delta: Generated<number>;
350
+ cpu_delta: Generated<number | string>;
351
+ storage_mb_delta: Generated<number>;
352
+ effective_from: Generated<Date>;
353
+ effective_until: Date | null;
354
+ stripe_subscription_item_id: string | null;
355
+ created_at: Generated<Date>;
356
+ }
357
+ interface AccountSpendCapsTable {
358
+ account_id: string;
359
+ monthly_cap_cents: number | null;
360
+ compute_cap_cents: number | null;
361
+ storage_cap_cents: number | null;
362
+ alert_threshold_pct: Generated<number>;
363
+ alert_sent_at: Date | null;
364
+ frozen_at: Date | null;
365
+ updated_at: Generated<Date>;
366
+ }
367
+ type ProvisioningAuditEvent = "provision.start" | "provision.success" | "provision.failure" | "suspend" | "resume" | "resize" | "keys.rotate" | "bastion.key.upload" | "bastion.key.revoke" | "teardown";
368
+ type ProvisioningAuditStatus = "ok" | "error";
369
+ interface ProvisioningAuditLogTable {
370
+ id: Generated<string>;
371
+ tenant_id: string | null;
372
+ tenant_slug: string | null;
373
+ account_id: string | null;
374
+ actor: string;
375
+ event: ProvisioningAuditEvent;
376
+ status: ProvisioningAuditStatus;
377
+ detail: unknown | null;
378
+ error: string | null;
379
+ created_at: Generated<Date>;
380
+ }
381
+ type SubgraphOperation = Selectable<SubgraphOperationsTable>;
382
+ type SubscriptionStatus = "active" | "paused" | "error";
383
+ type SubscriptionFormat = "standard-webhooks" | "inngest" | "trigger" | "cloudflare" | "cloudevents" | "raw";
384
+ type SubscriptionRuntime = "inngest" | "trigger" | "cloudflare" | "node";
385
+ interface SubscriptionsTable {
386
+ id: Generated<string>;
387
+ account_id: string;
388
+ project_id: string | null;
389
+ name: string;
390
+ status: ColumnType<SubscriptionStatus, SubscriptionStatus | undefined, SubscriptionStatus>;
391
+ subgraph_name: string;
392
+ table_name: string;
393
+ filter: Generated<unknown>;
394
+ format: ColumnType<SubscriptionFormat, SubscriptionFormat | undefined, SubscriptionFormat>;
395
+ runtime: SubscriptionRuntime | null;
396
+ url: string;
397
+ signing_secret_enc: Buffer;
398
+ auth_config: Generated<unknown>;
399
+ max_retries: Generated<number>;
400
+ timeout_ms: Generated<number>;
401
+ concurrency: Generated<number>;
402
+ circuit_failures: Generated<number>;
403
+ circuit_opened_at: Date | null;
404
+ last_delivery_at: Date | null;
405
+ last_success_at: Date | null;
406
+ last_error: string | null;
407
+ created_at: Generated<Date>;
408
+ updated_at: Generated<Date>;
409
+ }
410
+ type OutboxStatus = "pending" | "delivered" | "dead";
411
+ interface SubscriptionOutboxTable {
412
+ id: Generated<string>;
413
+ subscription_id: string;
414
+ subgraph_name: string;
415
+ table_name: string;
416
+ block_height: number | bigint;
417
+ tx_id: string | null;
418
+ row_pk: unknown;
419
+ event_type: string;
420
+ payload: unknown;
421
+ dedup_key: string;
422
+ attempt: Generated<number>;
423
+ next_attempt_at: Generated<Date>;
424
+ status: ColumnType<OutboxStatus, OutboxStatus | undefined, OutboxStatus>;
425
+ is_replay: Generated<boolean>;
426
+ delivered_at: Date | null;
427
+ failed_at: Date | null;
428
+ locked_by: string | null;
429
+ locked_until: Date | null;
430
+ created_at: Generated<Date>;
431
+ }
432
+ interface SubscriptionDeliveriesTable {
433
+ id: Generated<string>;
434
+ outbox_id: string;
435
+ subscription_id: string;
436
+ attempt: number;
437
+ status_code: number | null;
438
+ response_headers: unknown | null;
439
+ response_body: string | null;
440
+ error_message: string | null;
441
+ duration_ms: number | null;
442
+ dispatched_at: Generated<Date>;
443
+ }
444
+ declare function isActiveSubgraphOperationConflict(err: unknown): boolean;
445
+ declare function createSubgraphOperation(db: Kysely<Database>, data: {
446
+ subgraphId: string
447
+ subgraphName: string
448
+ accountId?: string | null
449
+ kind: SubgraphOperationKind
450
+ fromBlock?: number
451
+ toBlock?: number
452
+ }): Promise<SubgraphOperation>;
453
+ declare function findActiveSubgraphOperation(db: Kysely<Database>, subgraphId: string): Promise<SubgraphOperation | null>;
454
+ declare function requestSubgraphOperationCancel(db: Kysely<Database>, subgraphId: string): Promise<SubgraphOperation | null>;
455
+ declare function claimSubgraphOperation(db: Kysely<Database>, lockedBy: string): Promise<SubgraphOperation | null>;
456
+ declare function heartbeatSubgraphOperation(db: Kysely<Database>, operationId: string, lockedBy: string): Promise<void>;
457
+ declare function getSubgraphOperation(db: Kysely<Database>, operationId: string): Promise<SubgraphOperation | null>;
458
+ declare function completeSubgraphOperation(db: Kysely<Database>, operationId: string, lockedBy: string, processedBlocks: number): Promise<void>;
459
+ declare function cancelSubgraphOperation(db: Kysely<Database>, operationId: string, lockedBy: string, processedBlocks: number): Promise<void>;
460
+ declare function failSubgraphOperation(db: Kysely<Database>, operationId: string, lockedBy: string, error: string, processedBlocks?: number): Promise<void>;
461
+ export { requestSubgraphOperationCancel, isActiveSubgraphOperationConflict, heartbeatSubgraphOperation, getSubgraphOperation, findActiveSubgraphOperation, failSubgraphOperation, createSubgraphOperation, completeSubgraphOperation, claimSubgraphOperation, cancelSubgraphOperation };
@@ -0,0 +1,124 @@
1
+ import { createRequire } from "node:module";
2
+ var __defProp = Object.defineProperty;
3
+ var __returnValue = (v) => v;
4
+ function __exportSetter(name, newValue) {
5
+ this[name] = __returnValue.bind(null, newValue);
6
+ }
7
+ var __export = (target, all) => {
8
+ for (var name in all)
9
+ __defProp(target, name, {
10
+ get: all[name],
11
+ enumerable: true,
12
+ configurable: true,
13
+ set: __exportSetter.bind(all, name)
14
+ });
15
+ };
16
+
17
+ // src/db/queries/subgraph-operations.ts
18
+ import { sql } from "kysely";
19
+ var ACTIVE_STATUSES = ["queued", "running"];
20
+ function isActiveSubgraphOperationConflict(err) {
21
+ if (!(err instanceof Error))
22
+ return false;
23
+ const candidate = err;
24
+ return candidate.code === "23505" && (candidate.constraint === "subgraph_operations_active_unique" || candidate.constraint_name === "subgraph_operations_active_unique");
25
+ }
26
+ async function createSubgraphOperation(db, data) {
27
+ return await db.insertInto("subgraph_operations").values({
28
+ subgraph_id: data.subgraphId,
29
+ subgraph_name: data.subgraphName,
30
+ account_id: data.accountId ?? null,
31
+ kind: data.kind,
32
+ from_block: data.fromBlock ?? null,
33
+ to_block: data.toBlock ?? null
34
+ }).returningAll().executeTakeFirstOrThrow();
35
+ }
36
+ async function findActiveSubgraphOperation(db, subgraphId) {
37
+ return await db.selectFrom("subgraph_operations").selectAll().where("subgraph_id", "=", subgraphId).where("status", "in", ACTIVE_STATUSES).orderBy("created_at", "asc").executeTakeFirst() ?? null;
38
+ }
39
+ async function requestSubgraphOperationCancel(db, subgraphId) {
40
+ return await db.updateTable("subgraph_operations").set({ cancel_requested: true, updated_at: new Date }).where("subgraph_id", "=", subgraphId).where("status", "in", ACTIVE_STATUSES).returningAll().executeTakeFirst() ?? null;
41
+ }
42
+ async function claimSubgraphOperation(db, lockedBy) {
43
+ const result = await sql`
44
+ UPDATE subgraph_operations
45
+ SET
46
+ status = 'running',
47
+ locked_by = ${lockedBy},
48
+ locked_until = now() + interval '60 seconds',
49
+ started_at = COALESCE(started_at, now()),
50
+ updated_at = now()
51
+ WHERE id = (
52
+ SELECT id
53
+ FROM subgraph_operations
54
+ WHERE
55
+ status = 'queued'
56
+ OR (
57
+ status = 'running'
58
+ AND (locked_until IS NULL OR locked_until < now())
59
+ )
60
+ ORDER BY
61
+ CASE WHEN status = 'queued' THEN 0 ELSE 1 END,
62
+ created_at
63
+ FOR UPDATE SKIP LOCKED
64
+ LIMIT 1
65
+ )
66
+ RETURNING *
67
+ `.execute(db);
68
+ return result.rows[0] ?? null;
69
+ }
70
+ async function heartbeatSubgraphOperation(db, operationId, lockedBy) {
71
+ await db.updateTable("subgraph_operations").set({
72
+ locked_until: sql`now() + interval '60 seconds'`,
73
+ updated_at: new Date
74
+ }).where("id", "=", operationId).where("status", "=", "running").where("locked_by", "=", lockedBy).execute();
75
+ }
76
+ async function getSubgraphOperation(db, operationId) {
77
+ return await db.selectFrom("subgraph_operations").selectAll().where("id", "=", operationId).executeTakeFirst() ?? null;
78
+ }
79
+ async function completeSubgraphOperation(db, operationId, lockedBy, processedBlocks) {
80
+ await db.updateTable("subgraph_operations").set({
81
+ status: "completed",
82
+ finished_at: new Date,
83
+ processed_blocks: processedBlocks,
84
+ locked_by: null,
85
+ locked_until: null,
86
+ updated_at: new Date
87
+ }).where("id", "=", operationId).where("locked_by", "=", lockedBy).execute();
88
+ }
89
+ async function cancelSubgraphOperation(db, operationId, lockedBy, processedBlocks) {
90
+ await db.updateTable("subgraph_operations").set({
91
+ status: "cancelled",
92
+ finished_at: new Date,
93
+ processed_blocks: processedBlocks,
94
+ locked_by: null,
95
+ locked_until: null,
96
+ updated_at: new Date
97
+ }).where("id", "=", operationId).where("locked_by", "=", lockedBy).execute();
98
+ }
99
+ async function failSubgraphOperation(db, operationId, lockedBy, error, processedBlocks) {
100
+ await db.updateTable("subgraph_operations").set({
101
+ status: "failed",
102
+ finished_at: new Date,
103
+ processed_blocks: processedBlocks ?? null,
104
+ error,
105
+ locked_by: null,
106
+ locked_until: null,
107
+ updated_at: new Date
108
+ }).where("id", "=", operationId).where("locked_by", "=", lockedBy).execute();
109
+ }
110
+ export {
111
+ requestSubgraphOperationCancel,
112
+ isActiveSubgraphOperationConflict,
113
+ heartbeatSubgraphOperation,
114
+ getSubgraphOperation,
115
+ findActiveSubgraphOperation,
116
+ failSubgraphOperation,
117
+ createSubgraphOperation,
118
+ completeSubgraphOperation,
119
+ claimSubgraphOperation,
120
+ cancelSubgraphOperation
121
+ };
122
+
123
+ //# debugId=7F3BF8DBD323E7A664756E2164756E21
124
+ //# sourceMappingURL=subgraph-operations.js.map
@@ -0,0 +1,10 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/db/queries/subgraph-operations.ts"],
4
+ "sourcesContent": [
5
+ "import { type Kysely, sql } from \"kysely\";\nimport type {\n\tDatabase,\n\tSubgraphOperation,\n\tSubgraphOperationKind,\n\tSubgraphOperationStatus,\n} from \"../types.ts\";\n\nconst ACTIVE_STATUSES: SubgraphOperationStatus[] = [\"queued\", \"running\"];\n\nexport function isActiveSubgraphOperationConflict(err: unknown): boolean {\n\tif (!(err instanceof Error)) return false;\n\tconst candidate = err as Error & {\n\t\tcode?: string;\n\t\tconstraint?: string;\n\t\tconstraint_name?: string;\n\t};\n\treturn (\n\t\tcandidate.code === \"23505\" &&\n\t\t(candidate.constraint === \"subgraph_operations_active_unique\" ||\n\t\t\tcandidate.constraint_name === \"subgraph_operations_active_unique\")\n\t);\n}\n\nexport async function createSubgraphOperation(\n\tdb: Kysely<Database>,\n\tdata: {\n\t\tsubgraphId: string;\n\t\tsubgraphName: string;\n\t\taccountId?: string | null;\n\t\tkind: SubgraphOperationKind;\n\t\tfromBlock?: number;\n\t\ttoBlock?: number;\n\t},\n): Promise<SubgraphOperation> {\n\treturn await db\n\t\t.insertInto(\"subgraph_operations\")\n\t\t.values({\n\t\t\tsubgraph_id: data.subgraphId,\n\t\t\tsubgraph_name: data.subgraphName,\n\t\t\taccount_id: data.accountId ?? null,\n\t\t\tkind: data.kind,\n\t\t\tfrom_block: data.fromBlock ?? null,\n\t\t\tto_block: data.toBlock ?? null,\n\t\t})\n\t\t.returningAll()\n\t\t.executeTakeFirstOrThrow();\n}\n\nexport async function findActiveSubgraphOperation(\n\tdb: Kysely<Database>,\n\tsubgraphId: string,\n): Promise<SubgraphOperation | null> {\n\treturn (\n\t\t(await db\n\t\t\t.selectFrom(\"subgraph_operations\")\n\t\t\t.selectAll()\n\t\t\t.where(\"subgraph_id\", \"=\", subgraphId)\n\t\t\t.where(\"status\", \"in\", ACTIVE_STATUSES)\n\t\t\t.orderBy(\"created_at\", \"asc\")\n\t\t\t.executeTakeFirst()) ?? null\n\t);\n}\n\nexport async function requestSubgraphOperationCancel(\n\tdb: Kysely<Database>,\n\tsubgraphId: string,\n): Promise<SubgraphOperation | null> {\n\treturn (\n\t\t(await db\n\t\t\t.updateTable(\"subgraph_operations\")\n\t\t\t.set({ cancel_requested: true, updated_at: new Date() })\n\t\t\t.where(\"subgraph_id\", \"=\", subgraphId)\n\t\t\t.where(\"status\", \"in\", ACTIVE_STATUSES)\n\t\t\t.returningAll()\n\t\t\t.executeTakeFirst()) ?? null\n\t);\n}\n\nexport async function claimSubgraphOperation(\n\tdb: Kysely<Database>,\n\tlockedBy: string,\n): Promise<SubgraphOperation | null> {\n\tconst result = await sql<SubgraphOperation>`\n\t\tUPDATE subgraph_operations\n\t\tSET\n\t\t\tstatus = 'running',\n\t\t\tlocked_by = ${lockedBy},\n\t\t\tlocked_until = now() + interval '60 seconds',\n\t\t\tstarted_at = COALESCE(started_at, now()),\n\t\t\tupdated_at = now()\n\t\tWHERE id = (\n\t\t\tSELECT id\n\t\t\tFROM subgraph_operations\n\t\t\tWHERE\n\t\t\t\tstatus = 'queued'\n\t\t\t\tOR (\n\t\t\t\t\tstatus = 'running'\n\t\t\t\t\tAND (locked_until IS NULL OR locked_until < now())\n\t\t\t\t)\n\t\t\tORDER BY\n\t\t\t\tCASE WHEN status = 'queued' THEN 0 ELSE 1 END,\n\t\t\t\tcreated_at\n\t\t\tFOR UPDATE SKIP LOCKED\n\t\t\tLIMIT 1\n\t\t)\n\t\tRETURNING *\n\t`.execute(db);\n\treturn result.rows[0] ?? null;\n}\n\nexport async function heartbeatSubgraphOperation(\n\tdb: Kysely<Database>,\n\toperationId: string,\n\tlockedBy: string,\n): Promise<void> {\n\tawait db\n\t\t.updateTable(\"subgraph_operations\")\n\t\t.set({\n\t\t\tlocked_until: sql<Date>`now() + interval '60 seconds'`,\n\t\t\tupdated_at: new Date(),\n\t\t})\n\t\t.where(\"id\", \"=\", operationId)\n\t\t.where(\"status\", \"=\", \"running\")\n\t\t.where(\"locked_by\", \"=\", lockedBy)\n\t\t.execute();\n}\n\nexport async function getSubgraphOperation(\n\tdb: Kysely<Database>,\n\toperationId: string,\n): Promise<SubgraphOperation | null> {\n\treturn (\n\t\t(await db\n\t\t\t.selectFrom(\"subgraph_operations\")\n\t\t\t.selectAll()\n\t\t\t.where(\"id\", \"=\", operationId)\n\t\t\t.executeTakeFirst()) ?? null\n\t);\n}\n\nexport async function completeSubgraphOperation(\n\tdb: Kysely<Database>,\n\toperationId: string,\n\tlockedBy: string,\n\tprocessedBlocks: number,\n): Promise<void> {\n\tawait db\n\t\t.updateTable(\"subgraph_operations\")\n\t\t.set({\n\t\t\tstatus: \"completed\",\n\t\t\tfinished_at: new Date(),\n\t\t\tprocessed_blocks: processedBlocks,\n\t\t\tlocked_by: null,\n\t\t\tlocked_until: null,\n\t\t\tupdated_at: new Date(),\n\t\t})\n\t\t.where(\"id\", \"=\", operationId)\n\t\t.where(\"locked_by\", \"=\", lockedBy)\n\t\t.execute();\n}\n\nexport async function cancelSubgraphOperation(\n\tdb: Kysely<Database>,\n\toperationId: string,\n\tlockedBy: string,\n\tprocessedBlocks: number,\n): Promise<void> {\n\tawait db\n\t\t.updateTable(\"subgraph_operations\")\n\t\t.set({\n\t\t\tstatus: \"cancelled\",\n\t\t\tfinished_at: new Date(),\n\t\t\tprocessed_blocks: processedBlocks,\n\t\t\tlocked_by: null,\n\t\t\tlocked_until: null,\n\t\t\tupdated_at: new Date(),\n\t\t})\n\t\t.where(\"id\", \"=\", operationId)\n\t\t.where(\"locked_by\", \"=\", lockedBy)\n\t\t.execute();\n}\n\nexport async function failSubgraphOperation(\n\tdb: Kysely<Database>,\n\toperationId: string,\n\tlockedBy: string,\n\terror: string,\n\tprocessedBlocks?: number,\n): Promise<void> {\n\tawait db\n\t\t.updateTable(\"subgraph_operations\")\n\t\t.set({\n\t\t\tstatus: \"failed\",\n\t\t\tfinished_at: new Date(),\n\t\t\tprocessed_blocks: processedBlocks ?? null,\n\t\t\terror,\n\t\t\tlocked_by: null,\n\t\t\tlocked_until: null,\n\t\t\tupdated_at: new Date(),\n\t\t})\n\t\t.where(\"id\", \"=\", operationId)\n\t\t.where(\"locked_by\", \"=\", lockedBy)\n\t\t.execute();\n}\n"
6
+ ],
7
+ "mappings": "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",
8
+ "debugId": "7F3BF8DBD323E7A664756E2164756E21",
9
+ "names": []
10
+ }
@@ -73,6 +73,27 @@ interface SubgraphGapsTable {
73
73
  detected_at: Generated<Date>;
74
74
  resolved_at: Date | null;
75
75
  }
76
+ type SubgraphOperationKind = "reindex" | "backfill";
77
+ type SubgraphOperationStatus = "queued" | "running" | "completed" | "failed" | "cancelled";
78
+ interface SubgraphOperationsTable {
79
+ id: Generated<string>;
80
+ subgraph_id: string;
81
+ subgraph_name: string;
82
+ account_id: string | null;
83
+ kind: ColumnType<SubgraphOperationKind, SubgraphOperationKind, SubgraphOperationKind>;
84
+ status: ColumnType<SubgraphOperationStatus, SubgraphOperationStatus | undefined, SubgraphOperationStatus>;
85
+ from_block: number | null;
86
+ to_block: number | null;
87
+ cancel_requested: Generated<boolean>;
88
+ locked_by: string | null;
89
+ locked_until: Date | null;
90
+ started_at: Date | null;
91
+ finished_at: Date | null;
92
+ processed_blocks: number | null;
93
+ error: string | null;
94
+ created_at: Generated<Date>;
95
+ updated_at: Generated<Date>;
96
+ }
76
97
  interface ApiKeysTable {
77
98
  id: Generated<string>;
78
99
  key_hash: string;
@@ -266,6 +287,7 @@ interface Database {
266
287
  subgraph_processing_stats: SubgraphProcessingStatsTable;
267
288
  subgraph_table_snapshots: SubgraphTableSnapshotsTable;
268
289
  subgraph_gaps: SubgraphGapsTable;
290
+ subgraph_operations: SubgraphOperationsTable;
269
291
  subgraph_usage_daily: SubgraphUsageDailyTable;
270
292
  projects: ProjectsTable;
271
293
  team_members: TeamMembersTable;
@@ -73,6 +73,27 @@ interface SubgraphGapsTable {
73
73
  detected_at: Generated<Date>;
74
74
  resolved_at: Date | null;
75
75
  }
76
+ type SubgraphOperationKind = "reindex" | "backfill";
77
+ type SubgraphOperationStatus = "queued" | "running" | "completed" | "failed" | "cancelled";
78
+ interface SubgraphOperationsTable {
79
+ id: Generated<string>;
80
+ subgraph_id: string;
81
+ subgraph_name: string;
82
+ account_id: string | null;
83
+ kind: ColumnType<SubgraphOperationKind, SubgraphOperationKind, SubgraphOperationKind>;
84
+ status: ColumnType<SubgraphOperationStatus, SubgraphOperationStatus | undefined, SubgraphOperationStatus>;
85
+ from_block: number | null;
86
+ to_block: number | null;
87
+ cancel_requested: Generated<boolean>;
88
+ locked_by: string | null;
89
+ locked_until: Date | null;
90
+ started_at: Date | null;
91
+ finished_at: Date | null;
92
+ processed_blocks: number | null;
93
+ error: string | null;
94
+ created_at: Generated<Date>;
95
+ updated_at: Generated<Date>;
96
+ }
76
97
  interface ApiKeysTable {
77
98
  id: Generated<string>;
78
99
  key_hash: string;
@@ -266,6 +287,7 @@ interface Database {
266
287
  subgraph_processing_stats: SubgraphProcessingStatsTable;
267
288
  subgraph_table_snapshots: SubgraphTableSnapshotsTable;
268
289
  subgraph_gaps: SubgraphGapsTable;
290
+ subgraph_operations: SubgraphOperationsTable;
269
291
  subgraph_usage_daily: SubgraphUsageDailyTable;
270
292
  projects: ProjectsTable;
271
293
  team_members: TeamMembersTable;