@secondlayer/shared 6.30.0 → 6.32.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.
@@ -116,6 +116,8 @@ interface SubgraphsTable {
116
116
  visibility: Generated<string>;
117
117
  /** Paid (wallet-ghost) deploys expire unless renewed or claimed; NULL = no expiry. */
118
118
  expires_at: Date | null;
119
+ /** (event type, contract) probe pairs persisted at deploy for weight classification. */
120
+ sparse_probe_targets: unknown | null;
119
121
  database_url_enc: ColumnType<Buffer | null, Buffer | null | undefined, Buffer | null>;
120
122
  created_at: Generated<Date>;
121
123
  updated_at: Generated<Date>;
@@ -162,6 +164,16 @@ interface SubgraphOperationsTable {
162
164
  error: string | null;
163
165
  created_at: Generated<Date>;
164
166
  updated_at: Generated<Date>;
167
+ /** 'light' (contract-scoped sparse) | 'heavy' (broad). Claim budgets heavy. */
168
+ weight: Generated<string>;
169
+ /** Candidate-event denominator computed at enqueue (sparse ops only). */
170
+ estimated_events: string | number | bigint | null;
171
+ /** Events processed so far — written by the progress flush. */
172
+ processed_events: string | number | bigint | null;
173
+ /** Backfill ops' own crash checkpoint — advanced conditionally in-tx with
174
+ * each written block; replays skip at/below it. NULL for reindex ops
175
+ * (those checkpoint on subgraphs.last_processed_block). */
176
+ cursor_block: string | number | bigint | null;
165
177
  }
166
178
  interface ApiKeysTable {
167
179
  id: Generated<string>;
@@ -336,22 +348,6 @@ interface TeamInvitationsTable {
336
348
  accepted_at: Date | null;
337
349
  created_at: Generated<Date>;
338
350
  }
339
- interface ChatSessionsTable {
340
- id: Generated<string>;
341
- account_id: string;
342
- title: string | null;
343
- summary: unknown | null;
344
- created_at: Generated<Date>;
345
- updated_at: Generated<Date>;
346
- }
347
- interface ChatMessagesTable {
348
- id: Generated<string>;
349
- chat_session_id: string;
350
- role: string;
351
- parts: unknown;
352
- metadata: unknown | null;
353
- created_at: Generated<Date>;
354
- }
355
351
  interface ProcessedStripeEventsTable {
356
352
  event_id: string;
357
353
  event_type: string;
@@ -649,8 +645,6 @@ interface Database {
649
645
  projects: ProjectsTable;
650
646
  team_members: TeamMembersTable;
651
647
  team_invitations: TeamInvitationsTable;
652
- chat_sessions: ChatSessionsTable;
653
- chat_messages: ChatMessagesTable;
654
648
  processed_stripe_events: ProcessedStripeEventsTable;
655
649
  tenants: TenantsTable;
656
650
  tenant_usage_monthly: TenantUsageMonthlyTable;
@@ -116,6 +116,8 @@ interface SubgraphsTable {
116
116
  visibility: Generated<string>;
117
117
  /** Paid (wallet-ghost) deploys expire unless renewed or claimed; NULL = no expiry. */
118
118
  expires_at: Date | null;
119
+ /** (event type, contract) probe pairs persisted at deploy for weight classification. */
120
+ sparse_probe_targets: unknown | null;
119
121
  database_url_enc: ColumnType<Buffer | null, Buffer | null | undefined, Buffer | null>;
120
122
  created_at: Generated<Date>;
121
123
  updated_at: Generated<Date>;
@@ -162,6 +164,16 @@ interface SubgraphOperationsTable {
162
164
  error: string | null;
163
165
  created_at: Generated<Date>;
164
166
  updated_at: Generated<Date>;
167
+ /** 'light' (contract-scoped sparse) | 'heavy' (broad). Claim budgets heavy. */
168
+ weight: Generated<string>;
169
+ /** Candidate-event denominator computed at enqueue (sparse ops only). */
170
+ estimated_events: string | number | bigint | null;
171
+ /** Events processed so far — written by the progress flush. */
172
+ processed_events: string | number | bigint | null;
173
+ /** Backfill ops' own crash checkpoint — advanced conditionally in-tx with
174
+ * each written block; replays skip at/below it. NULL for reindex ops
175
+ * (those checkpoint on subgraphs.last_processed_block). */
176
+ cursor_block: string | number | bigint | null;
165
177
  }
166
178
  interface ApiKeysTable {
167
179
  id: Generated<string>;
@@ -336,22 +348,6 @@ interface TeamInvitationsTable {
336
348
  accepted_at: Date | null;
337
349
  created_at: Generated<Date>;
338
350
  }
339
- interface ChatSessionsTable {
340
- id: Generated<string>;
341
- account_id: string;
342
- title: string | null;
343
- summary: unknown | null;
344
- created_at: Generated<Date>;
345
- updated_at: Generated<Date>;
346
- }
347
- interface ChatMessagesTable {
348
- id: Generated<string>;
349
- chat_session_id: string;
350
- role: string;
351
- parts: unknown;
352
- metadata: unknown | null;
353
- created_at: Generated<Date>;
354
- }
355
351
  interface ProcessedStripeEventsTable {
356
352
  event_id: string;
357
353
  event_type: string;
@@ -649,8 +645,6 @@ interface Database {
649
645
  projects: ProjectsTable;
650
646
  team_members: TeamMembersTable;
651
647
  team_invitations: TeamInvitationsTable;
652
- chat_sessions: ChatSessionsTable;
653
- chat_messages: ChatMessagesTable;
654
648
  processed_stripe_events: ProcessedStripeEventsTable;
655
649
  tenants: TenantsTable;
656
650
  tenant_usage_monthly: TenantUsageMonthlyTable;
@@ -116,6 +116,8 @@ interface SubgraphsTable {
116
116
  visibility: Generated<string>;
117
117
  /** Paid (wallet-ghost) deploys expire unless renewed or claimed; NULL = no expiry. */
118
118
  expires_at: Date | null;
119
+ /** (event type, contract) probe pairs persisted at deploy for weight classification. */
120
+ sparse_probe_targets: unknown | null;
119
121
  database_url_enc: ColumnType<Buffer | null, Buffer | null | undefined, Buffer | null>;
120
122
  created_at: Generated<Date>;
121
123
  updated_at: Generated<Date>;
@@ -162,6 +164,16 @@ interface SubgraphOperationsTable {
162
164
  error: string | null;
163
165
  created_at: Generated<Date>;
164
166
  updated_at: Generated<Date>;
167
+ /** 'light' (contract-scoped sparse) | 'heavy' (broad). Claim budgets heavy. */
168
+ weight: Generated<string>;
169
+ /** Candidate-event denominator computed at enqueue (sparse ops only). */
170
+ estimated_events: string | number | bigint | null;
171
+ /** Events processed so far — written by the progress flush. */
172
+ processed_events: string | number | bigint | null;
173
+ /** Backfill ops' own crash checkpoint — advanced conditionally in-tx with
174
+ * each written block; replays skip at/below it. NULL for reindex ops
175
+ * (those checkpoint on subgraphs.last_processed_block). */
176
+ cursor_block: string | number | bigint | null;
165
177
  }
166
178
  interface ApiKeysTable {
167
179
  id: Generated<string>;
@@ -336,22 +348,6 @@ interface TeamInvitationsTable {
336
348
  accepted_at: Date | null;
337
349
  created_at: Generated<Date>;
338
350
  }
339
- interface ChatSessionsTable {
340
- id: Generated<string>;
341
- account_id: string;
342
- title: string | null;
343
- summary: unknown | null;
344
- created_at: Generated<Date>;
345
- updated_at: Generated<Date>;
346
- }
347
- interface ChatMessagesTable {
348
- id: Generated<string>;
349
- chat_session_id: string;
350
- role: string;
351
- parts: unknown;
352
- metadata: unknown | null;
353
- created_at: Generated<Date>;
354
- }
355
351
  interface ProcessedStripeEventsTable {
356
352
  event_id: string;
357
353
  event_type: string;
@@ -649,8 +645,6 @@ interface Database {
649
645
  projects: ProjectsTable;
650
646
  team_members: TeamMembersTable;
651
647
  team_invitations: TeamInvitationsTable;
652
- chat_sessions: ChatSessionsTable;
653
- chat_messages: ChatMessagesTable;
654
648
  processed_stripe_events: ProcessedStripeEventsTable;
655
649
  tenants: TenantsTable;
656
650
  tenant_usage_monthly: TenantUsageMonthlyTable;
@@ -116,6 +116,8 @@ interface SubgraphsTable {
116
116
  visibility: Generated<string>;
117
117
  /** Paid (wallet-ghost) deploys expire unless renewed or claimed; NULL = no expiry. */
118
118
  expires_at: Date | null;
119
+ /** (event type, contract) probe pairs persisted at deploy for weight classification. */
120
+ sparse_probe_targets: unknown | null;
119
121
  database_url_enc: ColumnType<Buffer | null, Buffer | null | undefined, Buffer | null>;
120
122
  created_at: Generated<Date>;
121
123
  updated_at: Generated<Date>;
@@ -162,6 +164,16 @@ interface SubgraphOperationsTable {
162
164
  error: string | null;
163
165
  created_at: Generated<Date>;
164
166
  updated_at: Generated<Date>;
167
+ /** 'light' (contract-scoped sparse) | 'heavy' (broad). Claim budgets heavy. */
168
+ weight: Generated<string>;
169
+ /** Candidate-event denominator computed at enqueue (sparse ops only). */
170
+ estimated_events: string | number | bigint | null;
171
+ /** Events processed so far — written by the progress flush. */
172
+ processed_events: string | number | bigint | null;
173
+ /** Backfill ops' own crash checkpoint — advanced conditionally in-tx with
174
+ * each written block; replays skip at/below it. NULL for reindex ops
175
+ * (those checkpoint on subgraphs.last_processed_block). */
176
+ cursor_block: string | number | bigint | null;
165
177
  }
166
178
  interface ApiKeysTable {
167
179
  id: Generated<string>;
@@ -336,22 +348,6 @@ interface TeamInvitationsTable {
336
348
  accepted_at: Date | null;
337
349
  created_at: Generated<Date>;
338
350
  }
339
- interface ChatSessionsTable {
340
- id: Generated<string>;
341
- account_id: string;
342
- title: string | null;
343
- summary: unknown | null;
344
- created_at: Generated<Date>;
345
- updated_at: Generated<Date>;
346
- }
347
- interface ChatMessagesTable {
348
- id: Generated<string>;
349
- chat_session_id: string;
350
- role: string;
351
- parts: unknown;
352
- metadata: unknown | null;
353
- created_at: Generated<Date>;
354
- }
355
351
  interface ProcessedStripeEventsTable {
356
352
  event_id: string;
357
353
  event_type: string;
@@ -649,8 +645,6 @@ interface Database {
649
645
  projects: ProjectsTable;
650
646
  team_members: TeamMembersTable;
651
647
  team_invitations: TeamInvitationsTable;
652
- chat_sessions: ChatSessionsTable;
653
- chat_messages: ChatMessagesTable;
654
648
  processed_stripe_events: ProcessedStripeEventsTable;
655
649
  tenants: TenantsTable;
656
650
  tenant_usage_monthly: TenantUsageMonthlyTable;
@@ -882,6 +876,9 @@ declare function createSubgraphOperation(db: Kysely<Database>, data: {
882
876
  kind: SubgraphOperationKind
883
877
  fromBlock?: number
884
878
  toBlock?: number
879
+ /** 'light' | 'heavy' — claim budgets heavy ops. DB default 'heavy'. */
880
+ weight?: "light" | "heavy"
881
+ estimatedEvents?: number | null
885
882
  }): Promise<SubgraphOperation>;
886
883
  declare function findActiveSubgraphOperation(db: Kysely<Database>, subgraphId: string): Promise<SubgraphOperation | null>;
887
884
  declare function requestSubgraphOperationCancel(db: Kysely<Database>, subgraphId: string): Promise<SubgraphOperation | null>;
@@ -906,4 +903,18 @@ declare function listSubgraphOperations(db: Kysely<Database>, subgraphId: string
906
903
  declare function completeSubgraphOperation(db: Kysely<Database>, operationId: string, lockedBy: string, processedBlocks: number): Promise<void>;
907
904
  declare function cancelSubgraphOperation(db: Kysely<Database>, operationId: string, lockedBy: string, processedBlocks: number): Promise<void>;
908
905
  declare function failSubgraphOperation(db: Kysely<Database>, operationId: string, lockedBy: string, error: string, processedBlocks?: number): Promise<void>;
909
- export { waitForSubgraphOperationsClear, requestSubgraphOperationsCancelForDelete, requestSubgraphOperationCancel, listSubgraphOperations, isActiveSubgraphOperationConflict, heartbeatSubgraphOperation, getSubgraphOperation, findActiveSubgraphOperation, failSubgraphOperation, createSubgraphOperation, completeSubgraphOperation, claimSubgraphOperation, cancelSubgraphOperation };
906
+ /**\\n* 1-based position of a queued operation under the claim ordering (fairness →\\n* plan rank → queued-first → FIFO). The heavy-budget admission filter is NOT\\n* applied — a heavy op's eligibility depends on runtime budget state — so the\\n* position is approximate; render it as "~N". Returns null unless queued.\\n*/
907
+ declare function getOperationQueuePosition(db: Kysely<Database>, operationId: string): Promise<number | null>;
908
+ /** Median duration (seconds) of the last 20 completed ops of a weight class —
909
+ * the "est. start" multiplier for queued positions. Null with no history. */
910
+ declare function getRecentOperationMedianDuration(db: Kysely<Database>, weight: "light" | "heavy"): Promise<number | null>;
911
+ /** Progress-flush hook: events processed so far on a running operation. */
912
+ declare function updateOperationProcessedEvents(db: Kysely<Database>, operationId: string, processedEvents: number): Promise<void>;
913
+ /**
914
+ * Conditionally advance a backfill operation's crash checkpoint. Monotonic by
915
+ * construction: the WHERE clause makes concurrent writers serialize — exactly
916
+ * one advance wins per height, regardless of lease/zombie timing. Returns
917
+ * whether THIS caller advanced (false = a racing writer already covered h).
918
+ */
919
+ declare function advanceOperationCursor(db: Kysely<Database>, operationId: string, height: number): Promise<boolean>;
920
+ export { waitForSubgraphOperationsClear, updateOperationProcessedEvents, requestSubgraphOperationsCancelForDelete, requestSubgraphOperationCancel, listSubgraphOperations, isActiveSubgraphOperationConflict, heartbeatSubgraphOperation, getSubgraphOperation, getRecentOperationMedianDuration, getOperationQueuePosition, findActiveSubgraphOperation, failSubgraphOperation, createSubgraphOperation, completeSubgraphOperation, claimSubgraphOperation, cancelSubgraphOperation, advanceOperationCursor };
@@ -24,13 +24,21 @@ function isActiveSubgraphOperationConflict(err) {
24
24
  return candidate.code === "23505" && (candidate.constraint === "subgraph_operations_active_unique" || candidate.constraint_name === "subgraph_operations_active_unique");
25
25
  }
26
26
  async function createSubgraphOperation(db, data) {
27
+ let seededCursor = null;
28
+ if (data.kind === "backfill" && data.fromBlock != null && data.toBlock != null) {
29
+ const prior = await db.selectFrom("subgraph_operations").select((eb) => eb.fn.max("cursor_block").as("max_cursor")).where("subgraph_id", "=", data.subgraphId).where("kind", "=", "backfill").where("status", "in", ["failed", "cancelled"]).where("from_block", "<=", data.toBlock).where("to_block", ">=", data.fromBlock).executeTakeFirst();
30
+ seededCursor = prior?.max_cursor ?? null;
31
+ }
27
32
  return await db.insertInto("subgraph_operations").values({
28
33
  subgraph_id: data.subgraphId,
29
34
  subgraph_name: data.subgraphName,
30
35
  account_id: data.accountId ?? null,
31
36
  kind: data.kind,
32
37
  from_block: data.fromBlock ?? null,
33
- to_block: data.toBlock ?? null
38
+ to_block: data.toBlock ?? null,
39
+ ...data.weight ? { weight: data.weight } : {},
40
+ estimated_events: data.estimatedEvents ?? null,
41
+ cursor_block: seededCursor
34
42
  }).returningAll().executeTakeFirstOrThrow();
35
43
  }
36
44
  async function findActiveSubgraphOperation(db, subgraphId) {
@@ -54,7 +62,12 @@ async function waitForSubgraphOperationsClear(db, subgraphId, opts) {
54
62
  }
55
63
  return false;
56
64
  }
65
+ function resolveHeavyOpBudget() {
66
+ const parsed = Number.parseInt(process.env.SUBGRAPH_HEAVY_OP_BUDGET ?? "2", 10);
67
+ return Number.isFinite(parsed) && parsed > 0 ? parsed : 2;
68
+ }
57
69
  async function claimSubgraphOperation(db, lockedBy) {
70
+ const heavyBudget = resolveHeavyOpBudget();
58
71
  const result = await sql`
59
72
  UPDATE subgraph_operations
60
73
  SET
@@ -72,14 +85,40 @@ async function claimSubgraphOperation(db, lockedBy) {
72
85
  WHERE status = 'running'
73
86
  GROUP BY account_id
74
87
  ) rc ON so.account_id = rc.account_id
88
+ LEFT JOIN accounts a ON a.id::text = so.account_id
75
89
  WHERE
76
- so.status = 'queued'
77
- OR (
78
- so.status = 'running'
79
- AND (so.locked_until IS NULL OR so.locked_until < now())
90
+ (
91
+ so.status = 'queued'
92
+ OR (
93
+ so.status = 'running'
94
+ AND (so.locked_until IS NULL OR so.locked_until < now())
95
+ )
96
+ )
97
+ -- Heavy budget as an eligibility FILTER (not a post-claim refusal):
98
+ -- a budget-blocked heavy op at the head must not starve the light
99
+ -- ops behind it. Live-lock condition (locked_until > now()) keeps a
100
+ -- STALE heavy op from blocking its own reclaim. Soft across
101
+ -- concurrent claimers (can overshoot by one with multiple runners) —
102
+ -- acceptable for the single-runner deployment.
103
+ AND (
104
+ so.weight = 'light'
105
+ OR (
106
+ SELECT COUNT(*)
107
+ FROM subgraph_operations h
108
+ WHERE h.status = 'running'
109
+ AND h.weight = 'heavy'
110
+ AND h.locked_until > now()
111
+ AND h.id != so.id
112
+ ) < ${heavyBudget}
80
113
  )
81
114
  ORDER BY
82
115
  COALESCE(rc.cnt, 0) ASC,
116
+ CASE COALESCE(a.plan, 'none')
117
+ WHEN 'enterprise' THEN 0
118
+ WHEN 'scale' THEN 1
119
+ WHEN 'launch' THEN 2
120
+ ELSE 3
121
+ END,
83
122
  CASE WHEN so.status = 'queued' THEN 0 ELSE 1 END,
84
123
  so.created_at ASC
85
124
  FOR UPDATE OF so SKIP LOCKED
@@ -132,21 +171,89 @@ async function failSubgraphOperation(db, operationId, lockedBy, error, processed
132
171
  updated_at: new Date
133
172
  }).where("id", "=", operationId).where("locked_by", "=", lockedBy).execute();
134
173
  }
174
+ async function getOperationQueuePosition(db, operationId) {
175
+ const result = await sql`
176
+ WITH candidates AS (
177
+ SELECT
178
+ so.id,
179
+ ROW_NUMBER() OVER (
180
+ ORDER BY
181
+ COALESCE(rc.cnt, 0) ASC,
182
+ CASE COALESCE(a.plan, 'none')
183
+ WHEN 'enterprise' THEN 0
184
+ WHEN 'scale' THEN 1
185
+ WHEN 'launch' THEN 2
186
+ ELSE 3
187
+ END,
188
+ CASE WHEN so.status = 'queued' THEN 0 ELSE 1 END,
189
+ so.created_at ASC
190
+ ) AS rn
191
+ FROM subgraph_operations so
192
+ LEFT JOIN (
193
+ SELECT account_id, COUNT(*) AS cnt
194
+ FROM subgraph_operations
195
+ WHERE status = 'running'
196
+ GROUP BY account_id
197
+ ) rc ON so.account_id = rc.account_id
198
+ LEFT JOIN accounts a ON a.id::text = so.account_id
199
+ WHERE so.status = 'queued'
200
+ )
201
+ SELECT rn FROM candidates WHERE id = ${operationId}
202
+ `.execute(db);
203
+ const rn = result.rows[0]?.rn;
204
+ return rn == null ? null : Number(rn);
205
+ }
206
+ async function getRecentOperationMedianDuration(db, weight) {
207
+ const result = await sql`
208
+ SELECT percentile_cont(0.5) WITHIN GROUP (
209
+ ORDER BY EXTRACT(EPOCH FROM (finished_at - started_at))
210
+ ) AS median
211
+ FROM (
212
+ SELECT started_at, finished_at
213
+ FROM subgraph_operations
214
+ WHERE status = 'completed'
215
+ AND weight = ${weight}
216
+ AND started_at IS NOT NULL
217
+ AND finished_at IS NOT NULL
218
+ ORDER BY finished_at DESC
219
+ LIMIT 20
220
+ ) recent
221
+ `.execute(db);
222
+ const median = result.rows[0]?.median;
223
+ return median == null ? null : Number(median);
224
+ }
225
+ async function updateOperationProcessedEvents(db, operationId, processedEvents) {
226
+ await db.updateTable("subgraph_operations").set({
227
+ processed_events: sql`GREATEST(COALESCE(processed_events, 0), ${processedEvents})`,
228
+ updated_at: new Date
229
+ }).where("id", "=", operationId).execute();
230
+ }
231
+ async function advanceOperationCursor(db, operationId, height) {
232
+ const result = await db.updateTable("subgraph_operations").set({ cursor_block: height, updated_at: new Date }).where("id", "=", operationId).where((eb) => eb.or([
233
+ eb("cursor_block", "is", null),
234
+ eb("cursor_block", "<", String(height))
235
+ ])).executeTakeFirst();
236
+ return Number(result.numUpdatedRows ?? 0n) > 0;
237
+ }
135
238
  export {
136
239
  waitForSubgraphOperationsClear,
240
+ updateOperationProcessedEvents,
137
241
  requestSubgraphOperationsCancelForDelete,
138
242
  requestSubgraphOperationCancel,
139
243
  listSubgraphOperations,
140
244
  isActiveSubgraphOperationConflict,
141
245
  heartbeatSubgraphOperation,
142
246
  getSubgraphOperation,
247
+ getRecentOperationMedianDuration,
248
+ getOperationQueuePosition,
143
249
  findActiveSubgraphOperation,
144
250
  failSubgraphOperation,
145
251
  createSubgraphOperation,
146
252
  completeSubgraphOperation,
147
253
  claimSubgraphOperation,
148
- cancelSubgraphOperation
254
+ cancelSubgraphOperation,
255
+ advanceOperationCursor
149
256
  };
150
257
 
151
- //# debugId=7F3BE21002C861D864756E2164756E21
258
+ //# debugId=7C6D7169EDA84D0F64756E2164756E21
152
259
  //# sourceMappingURL=subgraph-operations.js.map
@@ -2,9 +2,9 @@
2
2
  "version": 3,
3
3
  "sources": ["../src/db/queries/subgraph-operations.ts"],
4
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 requestSubgraphOperationsCancelForDelete(\n\tdb: Kysely<Database>,\n\tsubgraphId: string,\n): Promise<SubgraphOperation[]> {\n\treturn await db\n\t\t.updateTable(\"subgraph_operations\")\n\t\t.set({ cancel_requested: true, updated_at: new Date() })\n\t\t.where(\"subgraph_id\", \"=\", subgraphId)\n\t\t.where(\"status\", \"in\", ACTIVE_STATUSES)\n\t\t.returningAll()\n\t\t.execute();\n}\n\n/**\n * Poll until no active subgraph operations remain for the subgraph or until\n * `timeoutMs` elapses. Returns true if all active operations cleared, false\n * if we timed out. Callers should use this before `DROP SCHEMA` so the active\n * processor has a chance to observe `cancel_requested` and release its row /\n * advisory locks. Without this, the DROP blocks behind the live transaction\n * and the API socket times out before the lock releases.\n */\nexport async function waitForSubgraphOperationsClear(\n\tdb: Kysely<Database>,\n\tsubgraphId: string,\n\topts?: { timeoutMs?: number; pollMs?: number },\n): Promise<boolean> {\n\tconst timeoutMs = opts?.timeoutMs ?? 30_000;\n\tconst pollMs = opts?.pollMs ?? 500;\n\tconst deadline = Date.now() + timeoutMs;\n\twhile (Date.now() < deadline) {\n\t\tconst active = await db\n\t\t\t.selectFrom(\"subgraph_operations\")\n\t\t\t.select(\"id\")\n\t\t\t.where(\"subgraph_id\", \"=\", subgraphId)\n\t\t\t.where(\"status\", \"in\", ACTIVE_STATUSES)\n\t\t\t.limit(1)\n\t\t\t.executeTakeFirst();\n\t\tif (!active) return true;\n\t\tawait new Promise((r) => setTimeout(r, pollMs));\n\t}\n\treturn false;\n}\n\nexport async function claimSubgraphOperation(\n\tdb: Kysely<Database>,\n\tlockedBy: string,\n): Promise<SubgraphOperation | null> {\n\t// Fair queue: accounts with fewer currently-running operations are served first,\n\t// breaking ties by creation time. Prevents one user's long reindex from\n\t// starving other accounts when SUBGRAPH_OPERATION_CONCURRENCY > 1.\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 so.id\n\t\t\tFROM subgraph_operations so\n\t\t\tLEFT JOIN (\n\t\t\t\tSELECT account_id, COUNT(*) AS cnt\n\t\t\t\tFROM subgraph_operations\n\t\t\t\tWHERE status = 'running'\n\t\t\t\tGROUP BY account_id\n\t\t\t) rc ON so.account_id = rc.account_id\n\t\t\tWHERE\n\t\t\t\tso.status = 'queued'\n\t\t\t\tOR (\n\t\t\t\t\tso.status = 'running'\n\t\t\t\t\tAND (so.locked_until IS NULL OR so.locked_until < now())\n\t\t\t\t)\n\t\t\tORDER BY\n\t\t\t\tCOALESCE(rc.cnt, 0) ASC,\n\t\t\t\tCASE WHEN so.status = 'queued' THEN 0 ELSE 1 END,\n\t\t\t\tso.created_at ASC\n\t\t\tFOR UPDATE OF so 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\n/** Recent operations for a subgraph, newest first (for the status read API). */\nexport async function listSubgraphOperations(\n\tdb: Kysely<Database>,\n\tsubgraphId: string,\n\tlimit = 20,\n): Promise<SubgraphOperation[]> {\n\treturn db\n\t\t.selectFrom(\"subgraph_operations\")\n\t\t.selectAll()\n\t\t.where(\"subgraph_id\", \"=\", subgraphId)\n\t\t.orderBy(\"created_at\", \"desc\")\n\t\t.limit(limit)\n\t\t.execute();\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"
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\t/** 'light' | 'heavy' — claim budgets heavy ops. DB default 'heavy'. */\n\t\tweight?: \"light\" | \"heavy\";\n\t\testimatedEvents?: number | null;\n\t},\n): Promise<SubgraphOperation> {\n\t// Requeued backfills inherit the committed prefix of prior attempts over an\n\t// overlapping range — cancel/fail + requeue can't replay applied deltas.\n\tlet seededCursor: string | number | bigint | null = null;\n\tif (\n\t\tdata.kind === \"backfill\" &&\n\t\tdata.fromBlock != null &&\n\t\tdata.toBlock != null\n\t) {\n\t\tconst prior = await db\n\t\t\t.selectFrom(\"subgraph_operations\")\n\t\t\t.select((eb) => eb.fn.max(\"cursor_block\").as(\"max_cursor\"))\n\t\t\t.where(\"subgraph_id\", \"=\", data.subgraphId)\n\t\t\t.where(\"kind\", \"=\", \"backfill\")\n\t\t\t.where(\"status\", \"in\", [\"failed\", \"cancelled\"])\n\t\t\t.where(\"from_block\", \"<=\", data.toBlock)\n\t\t\t.where(\"to_block\", \">=\", data.fromBlock)\n\t\t\t.executeTakeFirst();\n\t\tseededCursor = prior?.max_cursor ?? null;\n\t}\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\t...(data.weight ? { weight: data.weight } : {}),\n\t\t\testimated_events: data.estimatedEvents ?? null,\n\t\t\tcursor_block: seededCursor,\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 requestSubgraphOperationsCancelForDelete(\n\tdb: Kysely<Database>,\n\tsubgraphId: string,\n): Promise<SubgraphOperation[]> {\n\treturn await db\n\t\t.updateTable(\"subgraph_operations\")\n\t\t.set({ cancel_requested: true, updated_at: new Date() })\n\t\t.where(\"subgraph_id\", \"=\", subgraphId)\n\t\t.where(\"status\", \"in\", ACTIVE_STATUSES)\n\t\t.returningAll()\n\t\t.execute();\n}\n\n/**\n * Poll until no active subgraph operations remain for the subgraph or until\n * `timeoutMs` elapses. Returns true if all active operations cleared, false\n * if we timed out. Callers should use this before `DROP SCHEMA` so the active\n * processor has a chance to observe `cancel_requested` and release its row /\n * advisory locks. Without this, the DROP blocks behind the live transaction\n * and the API socket times out before the lock releases.\n */\nexport async function waitForSubgraphOperationsClear(\n\tdb: Kysely<Database>,\n\tsubgraphId: string,\n\topts?: { timeoutMs?: number; pollMs?: number },\n): Promise<boolean> {\n\tconst timeoutMs = opts?.timeoutMs ?? 30_000;\n\tconst pollMs = opts?.pollMs ?? 500;\n\tconst deadline = Date.now() + timeoutMs;\n\twhile (Date.now() < deadline) {\n\t\tconst active = await db\n\t\t\t.selectFrom(\"subgraph_operations\")\n\t\t\t.select(\"id\")\n\t\t\t.where(\"subgraph_id\", \"=\", subgraphId)\n\t\t\t.where(\"status\", \"in\", ACTIVE_STATUSES)\n\t\t\t.limit(1)\n\t\t\t.executeTakeFirst();\n\t\tif (!active) return true;\n\t\tawait new Promise((r) => setTimeout(r, pollMs));\n\t}\n\treturn false;\n}\n\n/** Max concurrently-running 'heavy' ops (broad/non-sparse syncs). */\nfunction resolveHeavyOpBudget(): number {\n\tconst parsed = Number.parseInt(\n\t\tprocess.env.SUBGRAPH_HEAVY_OP_BUDGET ?? \"2\",\n\t\t10,\n\t);\n\treturn Number.isFinite(parsed) && parsed > 0 ? parsed : 2;\n}\n\nexport async function claimSubgraphOperation(\n\tdb: Kysely<Database>,\n\tlockedBy: string,\n): Promise<SubgraphOperation | null> {\n\t// Fair queue: accounts with fewer currently-running operations are served first,\n\t// breaking ties by creation time. Prevents one user's long reindex from\n\t// starving other accounts when SUBGRAPH_OPERATION_CONCURRENCY > 1.\n\tconst heavyBudget = resolveHeavyOpBudget();\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 so.id\n\t\t\tFROM subgraph_operations so\n\t\t\tLEFT JOIN (\n\t\t\t\tSELECT account_id, COUNT(*) AS cnt\n\t\t\t\tFROM subgraph_operations\n\t\t\t\tWHERE status = 'running'\n\t\t\t\tGROUP BY account_id\n\t\t\t) rc ON so.account_id = rc.account_id\n\t\t\tLEFT JOIN accounts a ON a.id::text = so.account_id\n\t\t\tWHERE\n\t\t\t\t(\n\t\t\t\t\tso.status = 'queued'\n\t\t\t\t\tOR (\n\t\t\t\t\t\tso.status = 'running'\n\t\t\t\t\t\tAND (so.locked_until IS NULL OR so.locked_until < now())\n\t\t\t\t\t)\n\t\t\t\t)\n\t\t\t\t-- Heavy budget as an eligibility FILTER (not a post-claim refusal):\n\t\t\t\t-- a budget-blocked heavy op at the head must not starve the light\n\t\t\t\t-- ops behind it. Live-lock condition (locked_until > now()) keeps a\n\t\t\t\t-- STALE heavy op from blocking its own reclaim. Soft across\n\t\t\t\t-- concurrent claimers (can overshoot by one with multiple runners) —\n\t\t\t\t-- acceptable for the single-runner deployment.\n\t\t\t\tAND (\n\t\t\t\t\tso.weight = 'light'\n\t\t\t\t\tOR (\n\t\t\t\t\t\tSELECT COUNT(*)\n\t\t\t\t\t\tFROM subgraph_operations h\n\t\t\t\t\t\tWHERE h.status = 'running'\n\t\t\t\t\t\t\tAND h.weight = 'heavy'\n\t\t\t\t\t\t\tAND h.locked_until > now()\n\t\t\t\t\t\t\tAND h.id != so.id\n\t\t\t\t\t) < ${heavyBudget}\n\t\t\t\t)\n\t\t\tORDER BY\n\t\t\t\tCOALESCE(rc.cnt, 0) ASC,\n\t\t\t\tCASE COALESCE(a.plan, 'none')\n\t\t\t\t\tWHEN 'enterprise' THEN 0\n\t\t\t\t\tWHEN 'scale' THEN 1\n\t\t\t\t\tWHEN 'launch' THEN 2\n\t\t\t\t\tELSE 3\n\t\t\t\tEND,\n\t\t\t\tCASE WHEN so.status = 'queued' THEN 0 ELSE 1 END,\n\t\t\t\tso.created_at ASC\n\t\t\tFOR UPDATE OF so 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\n/** Recent operations for a subgraph, newest first (for the status read API). */\nexport async function listSubgraphOperations(\n\tdb: Kysely<Database>,\n\tsubgraphId: string,\n\tlimit = 20,\n): Promise<SubgraphOperation[]> {\n\treturn db\n\t\t.selectFrom(\"subgraph_operations\")\n\t\t.selectAll()\n\t\t.where(\"subgraph_id\", \"=\", subgraphId)\n\t\t.orderBy(\"created_at\", \"desc\")\n\t\t.limit(limit)\n\t\t.execute();\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\n/**\n * 1-based position of a queued operation under the claim ordering (fairness →\n * plan rank → queued-first → FIFO). The heavy-budget admission filter is NOT\n * applied — a heavy op's eligibility depends on runtime budget state — so the\n * position is approximate; render it as \"~N\". Returns null unless queued.\n */\nexport async function getOperationQueuePosition(\n\tdb: Kysely<Database>,\n\toperationId: string,\n): Promise<number | null> {\n\tconst result = await sql<{ rn: string | number }>`\n\t\tWITH candidates AS (\n\t\t\tSELECT\n\t\t\t\tso.id,\n\t\t\t\tROW_NUMBER() OVER (\n\t\t\t\t\tORDER BY\n\t\t\t\t\t\tCOALESCE(rc.cnt, 0) ASC,\n\t\t\t\t\t\tCASE COALESCE(a.plan, 'none')\n\t\t\t\t\t\t\tWHEN 'enterprise' THEN 0\n\t\t\t\t\t\t\tWHEN 'scale' THEN 1\n\t\t\t\t\t\t\tWHEN 'launch' THEN 2\n\t\t\t\t\t\t\tELSE 3\n\t\t\t\t\t\tEND,\n\t\t\t\t\t\tCASE WHEN so.status = 'queued' THEN 0 ELSE 1 END,\n\t\t\t\t\t\tso.created_at ASC\n\t\t\t\t) AS rn\n\t\t\tFROM subgraph_operations so\n\t\t\tLEFT JOIN (\n\t\t\t\tSELECT account_id, COUNT(*) AS cnt\n\t\t\t\tFROM subgraph_operations\n\t\t\t\tWHERE status = 'running'\n\t\t\t\tGROUP BY account_id\n\t\t\t) rc ON so.account_id = rc.account_id\n\t\t\tLEFT JOIN accounts a ON a.id::text = so.account_id\n\t\t\tWHERE so.status = 'queued'\n\t\t)\n\t\tSELECT rn FROM candidates WHERE id = ${operationId}\n\t`.execute(db);\n\tconst rn = result.rows[0]?.rn;\n\treturn rn == null ? null : Number(rn);\n}\n\n/** Median duration (seconds) of the last 20 completed ops of a weight class —\n * the \"est. start\" multiplier for queued positions. Null with no history. */\nexport async function getRecentOperationMedianDuration(\n\tdb: Kysely<Database>,\n\tweight: \"light\" | \"heavy\",\n): Promise<number | null> {\n\tconst result = await sql<{ median: string | number | null }>`\n\t\tSELECT percentile_cont(0.5) WITHIN GROUP (\n\t\t\tORDER BY EXTRACT(EPOCH FROM (finished_at - started_at))\n\t\t) AS median\n\t\tFROM (\n\t\t\tSELECT started_at, finished_at\n\t\t\tFROM subgraph_operations\n\t\t\tWHERE status = 'completed'\n\t\t\t\tAND weight = ${weight}\n\t\t\t\tAND started_at IS NOT NULL\n\t\t\t\tAND finished_at IS NOT NULL\n\t\t\tORDER BY finished_at DESC\n\t\t\tLIMIT 20\n\t\t) recent\n\t`.execute(db);\n\tconst median = result.rows[0]?.median;\n\treturn median == null ? null : Number(median);\n}\n\n/** Progress-flush hook: events processed so far on a running operation. */\nexport async function updateOperationProcessedEvents(\n\tdb: Kysely<Database>,\n\toperationId: string,\n\tprocessedEvents: number,\n): Promise<void> {\n\t// Monotonic: a crash-resumed run restarts its in-memory counter from 0 —\n\t// never let that regress the op's ETA surfaces.\n\tawait db\n\t\t.updateTable(\"subgraph_operations\")\n\t\t.set({\n\t\t\tprocessed_events: sql`GREATEST(COALESCE(processed_events, 0), ${processedEvents})`,\n\t\t\tupdated_at: new Date(),\n\t\t})\n\t\t.where(\"id\", \"=\", operationId)\n\t\t.execute();\n}\n\n/**\n * Conditionally advance a backfill operation's crash checkpoint. Monotonic by\n * construction: the WHERE clause makes concurrent writers serialize — exactly\n * one advance wins per height, regardless of lease/zombie timing. Returns\n * whether THIS caller advanced (false = a racing writer already covered h).\n */\nexport async function advanceOperationCursor(\n\tdb: Kysely<Database>,\n\toperationId: string,\n\theight: number,\n): Promise<boolean> {\n\tconst result = await db\n\t\t.updateTable(\"subgraph_operations\")\n\t\t.set({ cursor_block: height, updated_at: new Date() })\n\t\t.where(\"id\", \"=\", operationId)\n\t\t.where((eb) =>\n\t\t\teb.or([\n\t\t\t\teb(\"cursor_block\", \"is\", null),\n\t\t\t\teb(\"cursor_block\", \"<\", String(height)),\n\t\t\t]),\n\t\t)\n\t\t.executeTakeFirst();\n\treturn Number(result.numUpdatedRows ?? 0n) > 0;\n}\n"
6
6
  ],
7
- "mappings": "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",
8
- "debugId": "7F3BE21002C861D864756E2164756E21",
7
+ "mappings": "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",
8
+ "debugId": "7C6D7169EDA84D0F64756E2164756E21",
9
9
  "names": []
10
10
  }
@@ -117,6 +117,8 @@ interface SubgraphsTable {
117
117
  visibility: Generated<string>;
118
118
  /** Paid (wallet-ghost) deploys expire unless renewed or claimed; NULL = no expiry. */
119
119
  expires_at: Date | null;
120
+ /** (event type, contract) probe pairs persisted at deploy for weight classification. */
121
+ sparse_probe_targets: unknown | null;
120
122
  database_url_enc: ColumnType<Buffer | null, Buffer | null | undefined, Buffer | null>;
121
123
  created_at: Generated<Date>;
122
124
  updated_at: Generated<Date>;
@@ -163,6 +165,16 @@ interface SubgraphOperationsTable {
163
165
  error: string | null;
164
166
  created_at: Generated<Date>;
165
167
  updated_at: Generated<Date>;
168
+ /** 'light' (contract-scoped sparse) | 'heavy' (broad). Claim budgets heavy. */
169
+ weight: Generated<string>;
170
+ /** Candidate-event denominator computed at enqueue (sparse ops only). */
171
+ estimated_events: string | number | bigint | null;
172
+ /** Events processed so far — written by the progress flush. */
173
+ processed_events: string | number | bigint | null;
174
+ /** Backfill ops' own crash checkpoint — advanced conditionally in-tx with
175
+ * each written block; replays skip at/below it. NULL for reindex ops
176
+ * (those checkpoint on subgraphs.last_processed_block). */
177
+ cursor_block: string | number | bigint | null;
166
178
  }
167
179
  interface ApiKeysTable {
168
180
  id: Generated<string>;
@@ -337,22 +349,6 @@ interface TeamInvitationsTable {
337
349
  accepted_at: Date | null;
338
350
  created_at: Generated<Date>;
339
351
  }
340
- interface ChatSessionsTable {
341
- id: Generated<string>;
342
- account_id: string;
343
- title: string | null;
344
- summary: unknown | null;
345
- created_at: Generated<Date>;
346
- updated_at: Generated<Date>;
347
- }
348
- interface ChatMessagesTable {
349
- id: Generated<string>;
350
- chat_session_id: string;
351
- role: string;
352
- parts: unknown;
353
- metadata: unknown | null;
354
- created_at: Generated<Date>;
355
- }
356
352
  interface ProcessedStripeEventsTable {
357
353
  event_id: string;
358
354
  event_type: string;
@@ -650,8 +646,6 @@ interface Database {
650
646
  projects: ProjectsTable;
651
647
  team_members: TeamMembersTable;
652
648
  team_invitations: TeamInvitationsTable;
653
- chat_sessions: ChatSessionsTable;
654
- chat_messages: ChatMessagesTable;
655
649
  processed_stripe_events: ProcessedStripeEventsTable;
656
650
  tenants: TenantsTable;
657
651
  tenant_usage_monthly: TenantUsageMonthlyTable;
@@ -687,8 +687,6 @@ var TABLE_TO_DB = {
687
687
  projects: "target",
688
688
  team_members: "target",
689
689
  team_invitations: "target",
690
- chat_sessions: "target",
691
- chat_messages: "target",
692
690
  subscriptions: "target",
693
691
  subscription_outbox: "target",
694
692
  subscription_deliveries: "target",
@@ -1131,5 +1129,5 @@ export {
1131
1129
  deleteSubgraph
1132
1130
  };
1133
1131
 
1134
- //# debugId=E65B724DBDBCC17464756E2164756E21
1132
+ //# debugId=6E70C885DAD9B44864756E2164756E21
1135
1133
  //# sourceMappingURL=subgraphs.js.map