@secondlayer/shared 7.11.1 → 8.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/dist/src/archive/range-digest.d.ts +0 -169
  2. package/dist/src/archive/semantic-digest-builder.d.ts +0 -169
  3. package/dist/src/coverage/index.d.ts +0 -169
  4. package/dist/src/crypto/secrets.js +22 -3
  5. package/dist/src/crypto/secrets.js.map +3 -3
  6. package/dist/src/db/index.d.ts +1 -211
  7. package/dist/src/db/index.js +22 -16
  8. package/dist/src/db/index.js.map +4 -4
  9. package/dist/src/db/queries/chain-reorgs.d.ts +0 -169
  10. package/dist/src/db/queries/chain-reorgs.js +22 -16
  11. package/dist/src/db/queries/chain-reorgs.js.map +4 -4
  12. package/dist/src/db/queries/contracts.d.ts +0 -169
  13. package/dist/src/db/queries/instance.d.ts +0 -169
  14. package/dist/src/db/queries/integrity.d.ts +0 -169
  15. package/dist/src/db/queries/subgraph-gaps.d.ts +0 -169
  16. package/dist/src/db/queries/subgraph-operations.d.ts +1 -170
  17. package/dist/src/db/queries/subgraph-operations.js +1 -15
  18. package/dist/src/db/queries/subgraph-operations.js.map +3 -3
  19. package/dist/src/db/queries/subgraphs.d.ts +1 -191
  20. package/dist/src/db/queries/subgraphs.js +30 -1068
  21. package/dist/src/db/queries/subgraphs.js.map +5 -12
  22. package/dist/src/db/queries/subscriptions.d.ts +0 -169
  23. package/dist/src/db/queries/subscriptions.js +22 -3
  24. package/dist/src/db/queries/subscriptions.js.map +3 -3
  25. package/dist/src/db/schema.d.ts +1 -198
  26. package/dist/src/errors.d.ts +1 -22
  27. package/dist/src/errors.js +2 -4
  28. package/dist/src/errors.js.map +3 -3
  29. package/dist/src/index.d.ts +5 -256
  30. package/dist/src/index.js +23 -20
  31. package/dist/src/index.js.map +6 -6
  32. package/dist/src/mode.d.ts +16 -2
  33. package/dist/src/mode.js +25 -4
  34. package/dist/src/mode.js.map +3 -3
  35. package/dist/src/node/local-client.d.ts +0 -169
  36. package/dist/src/runtime/index.js +26 -3
  37. package/dist/src/runtime/index.js.map +3 -3
  38. package/dist/src/schemas/index.d.ts +5 -25
  39. package/dist/src/schemas/index.js +1 -2
  40. package/dist/src/schemas/index.js.map +3 -3
  41. package/dist/src/schemas/subgraphs.d.ts +5 -25
  42. package/dist/src/schemas/subgraphs.js +1 -2
  43. package/dist/src/schemas/subgraphs.js.map +3 -3
  44. package/dist/src/streams-rows/index.d.ts +399 -0
  45. package/dist/src/streams-rows/index.js +336 -0
  46. package/dist/src/streams-rows/index.js.map +17 -0
  47. package/dist/src/subgraphs/spec.d.ts +0 -9
  48. package/migrations/0118_drop_retired_control_tables.ts +53 -0
  49. package/migrations/0119_drop_accounts_plan.ts +29 -0
  50. package/package.json +5 -1
@@ -223,11 +223,9 @@ interface AccountsTable {
223
223
  ghost: Generated<boolean>;
224
224
  /** Stacks principal owning a wallet-ghost account (x402-paid deploys). */
225
225
  wallet_principal: string | null;
226
- plan: Generated<string>;
227
226
  display_name: string | null;
228
227
  bio: string | null;
229
228
  avatar_url: string | null;
230
- slug: string | null;
231
229
  stripe_customer_id: string | null;
232
230
  /** Opt-out toggle for the subgraph reindex-completion email. Defaults true. */
233
231
  notify_reindex_complete: Generated<boolean>;
@@ -244,19 +242,6 @@ interface SessionsTable {
244
242
  last_used_at: Date | null;
245
243
  created_at: Generated<Date>;
246
244
  }
247
- /**
248
- * One-time claim tokens for ghost accounts. The raw token only ever appears in
249
- * the claim URL returned at mint time; we store its sha256. A used token marks
250
- * the account as claimed (or merged into an existing account).
251
- */
252
- interface ClaimTokensTable {
253
- id: Generated<string>;
254
- account_id: string;
255
- token_hash: string;
256
- created_at: Generated<Date>;
257
- expires_at: Date;
258
- used_at: Date | null;
259
- }
260
245
  interface MagicLinksTable {
261
246
  id: Generated<string>;
262
247
  email: string;
@@ -267,47 +252,6 @@ interface MagicLinksTable {
267
252
  failed_attempts: Generated<number>;
268
253
  created_at: Generated<Date>;
269
254
  }
270
- interface UsageDailyTable {
271
- account_id: string;
272
- tenant_id: string | null;
273
- date: string;
274
- api_requests: Generated<number>;
275
- deliveries: Generated<number>;
276
- streams_events_returned: Generated<number>;
277
- index_decoded_events_returned: Generated<number>;
278
- }
279
- interface UsageSnapshotsTable {
280
- id: Generated<string>;
281
- account_id: string;
282
- measured_at: Generated<Date>;
283
- storage_bytes: Generated<number>;
284
- }
285
- interface AccountInsightsTable {
286
- id: Generated<string>;
287
- account_id: string;
288
- category: string;
289
- insight_type: string;
290
- resource_id: string | null;
291
- severity: string;
292
- title: string;
293
- body: string;
294
- data: unknown;
295
- dismissed_at: Date | null;
296
- expires_at: Date | null;
297
- created_at: Generated<Date>;
298
- }
299
- interface AccountAgentRunsTable {
300
- id: Generated<string>;
301
- account_id: string;
302
- started_at: Generated<Date>;
303
- completed_at: Date | null;
304
- status: Generated<string>;
305
- input_tokens: Generated<number>;
306
- output_tokens: Generated<number>;
307
- cost_usd: Generated<number>;
308
- insights_created: Generated<number>;
309
- error: string | null;
310
- }
311
255
  interface SubgraphProcessingStatsTable {
312
256
  id: Generated<string>;
313
257
  subgraph_name: string;
@@ -340,41 +284,6 @@ interface SubgraphHealthSnapshotsTable {
340
284
  last_processed_block: number | null;
341
285
  captured_at: Generated<Date>;
342
286
  }
343
- interface SubgraphUsageDailyTable {
344
- subgraph_id: string;
345
- date: string;
346
- query_count: Generated<number>;
347
- }
348
- interface ProjectsTable {
349
- id: Generated<string>;
350
- name: string;
351
- slug: string;
352
- account_id: string;
353
- settings: Generated<Record<string, unknown>>;
354
- network: Generated<string>;
355
- node_rpc: string | null;
356
- created_at: Generated<Date>;
357
- updated_at: Generated<Date>;
358
- }
359
- interface TeamMembersTable {
360
- id: Generated<string>;
361
- project_id: string;
362
- account_id: string;
363
- role: Generated<string>;
364
- invited_by: string | null;
365
- created_at: Generated<Date>;
366
- }
367
- interface TeamInvitationsTable {
368
- id: Generated<string>;
369
- project_id: string;
370
- email: string;
371
- role: Generated<string>;
372
- token: string;
373
- invited_by: string | null;
374
- expires_at: Date;
375
- accepted_at: Date | null;
376
- created_at: Generated<Date>;
377
- }
378
287
  interface ProcessedStripeEventsTable {
379
288
  event_id: string;
380
289
  event_type: string;
@@ -827,27 +736,14 @@ interface Database {
827
736
  accounts: AccountsTable;
828
737
  sessions: SessionsTable;
829
738
  magic_links: MagicLinksTable;
830
- claim_tokens: ClaimTokensTable;
831
- usage_daily: UsageDailyTable;
832
- usage_snapshots: UsageSnapshotsTable;
833
- account_insights: AccountInsightsTable;
834
- account_agent_runs: AccountAgentRunsTable;
835
739
  subgraph_health_snapshots: SubgraphHealthSnapshotsTable;
836
740
  subgraph_processing_stats: SubgraphProcessingStatsTable;
837
741
  subgraph_table_snapshots: SubgraphTableSnapshotsTable;
838
742
  subgraph_gaps: SubgraphGapsTable;
839
743
  subgraph_operations: SubgraphOperationsTable;
840
- subgraph_usage_daily: SubgraphUsageDailyTable;
841
- projects: ProjectsTable;
842
- team_members: TeamMembersTable;
843
- team_invitations: TeamInvitationsTable;
844
744
  processed_stripe_events: ProcessedStripeEventsTable;
845
- tenants: TenantsTable;
846
- tenant_usage_monthly: TenantUsageMonthlyTable;
847
- tenant_compute_addons: TenantComputeAddonsTable;
848
745
  account_spend_caps: AccountSpendCapsTable;
849
746
  account_credits: AccountCreditsTable;
850
- provisioning_audit_log: ProvisioningAuditLogTable;
851
747
  subscriptions: SubscriptionsTable;
852
748
  subscription_outbox: SubscriptionOutboxTable;
853
749
  subscription_deliveries: SubscriptionDeliveriesTable;
@@ -922,57 +818,6 @@ interface X402PaymentsTable {
922
818
  * reconciler's heal path can never double-credit them. */
923
819
  credited_at: Generated<Date> | null;
924
820
  }
925
- type TenantStatus = "provisioning" | "active" | "limit_warning" | "paused_limit" | "suspended" | "error" | "deleted";
926
- interface TenantsTable {
927
- id: Generated<string>;
928
- account_id: string;
929
- slug: string;
930
- status: ColumnType<TenantStatus, TenantStatus | undefined, TenantStatus>;
931
- plan: string;
932
- cpus: ColumnType<number, number | string, number | string>;
933
- memory_mb: number;
934
- storage_limit_mb: number;
935
- storage_used_mb: number | null;
936
- pg_container_id: string | null;
937
- api_container_id: string | null;
938
- processor_container_id: string | null;
939
- target_database_url_enc: Buffer;
940
- tenant_jwt_secret_enc: Buffer;
941
- anon_key_enc: Buffer;
942
- service_key_enc: Buffer;
943
- api_url_internal: string;
944
- api_url_public: string;
945
- suspended_at: Date | null;
946
- last_health_check_at: Date | null;
947
- last_active_at: Generated<Date>;
948
- service_gen: Generated<number>;
949
- anon_gen: Generated<number>;
950
- project_id: string | null;
951
- created_at: Generated<Date>;
952
- updated_at: Generated<Date>;
953
- }
954
- interface TenantUsageMonthlyTable {
955
- id: Generated<string>;
956
- tenant_id: string;
957
- period_month: Date;
958
- storage_peak_mb: Generated<number>;
959
- storage_avg_mb: Generated<number>;
960
- storage_last_mb: Generated<number>;
961
- measurements: Generated<number>;
962
- first_at: Generated<Date>;
963
- last_at: Generated<Date>;
964
- }
965
- interface TenantComputeAddonsTable {
966
- id: Generated<string>;
967
- tenant_id: string;
968
- memory_mb_delta: Generated<number>;
969
- cpu_delta: Generated<number | string>;
970
- storage_mb_delta: Generated<number>;
971
- effective_from: Generated<Date>;
972
- effective_until: Date | null;
973
- stripe_subscription_item_id: string | null;
974
- created_at: Generated<Date>;
975
- }
976
821
  interface AccountSpendCapsTable {
977
822
  account_id: string;
978
823
  monthly_cap_cents: number | null;
@@ -996,20 +841,6 @@ interface AccountCreditsTable {
996
841
  refill_last_at: Date | null;
997
842
  updated_at: Generated<Date>;
998
843
  }
999
- type ProvisioningAuditEvent = "provision.start" | "provision.success" | "provision.failure" | "suspend" | "resume" | "resize" | "keys.rotate" | "bastion.key.upload" | "bastion.key.revoke" | "teardown";
1000
- type ProvisioningAuditStatus = "ok" | "error";
1001
- interface ProvisioningAuditLogTable {
1002
- id: Generated<string>;
1003
- tenant_id: string | null;
1004
- tenant_slug: string | null;
1005
- account_id: string | null;
1006
- actor: string;
1007
- event: ProvisioningAuditEvent;
1008
- status: ProvisioningAuditStatus;
1009
- detail: unknown | null;
1010
- error: string | null;
1011
- created_at: Generated<Date>;
1012
- }
1013
844
  type SubgraphOperation = Selectable<SubgraphOperationsTable>;
1014
845
  type SubscriptionStatus = "active" | "paused" | "error";
1015
846
  /** Polymorphic subscription mode: `subgraph` reacts to processed table rows;
@@ -1134,7 +965,7 @@ declare function listSubgraphOperations(db: Kysely<Database>, subgraphId: string
1134
965
  declare function completeSubgraphOperation(db: Kysely<Database>, operationId: string, lockedBy: string, processedBlocks: number): Promise<void>;
1135
966
  declare function cancelSubgraphOperation(db: Kysely<Database>, operationId: string, lockedBy: string, processedBlocks: number): Promise<void>;
1136
967
  declare function failSubgraphOperation(db: Kysely<Database>, operationId: string, lockedBy: string, error: string, processedBlocks?: number): Promise<void>;
1137
- /**\\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*/
968
+ /**\\n* 1-based position of a queued operation under the claim ordering (fairness →\\n* queued-first → FIFO). The heavy-budget admission filter is NOT applied — a\\n* heavy op's eligibility depends on runtime budget state — so the position is\\n* approximate; render it as "~N". Returns null unless queued.\\n*/
1138
969
  declare function getOperationQueuePosition(db: Kysely<Database>, operationId: string): Promise<number | null>;
1139
970
  /** Median duration (seconds) of the last 20 completed ops of a weight class —
1140
971
  * the "est. start" multiplier for queued positions. Null with no history. */
@@ -85,7 +85,6 @@ async function claimSubgraphOperation(db, lockedBy) {
85
85
  WHERE status = 'running'
86
86
  GROUP BY account_id
87
87
  ) rc ON so.account_id = rc.account_id
88
- LEFT JOIN accounts a ON a.id::text = so.account_id
89
88
  WHERE
90
89
  (
91
90
  so.status = 'queued'
@@ -113,12 +112,6 @@ async function claimSubgraphOperation(db, lockedBy) {
113
112
  )
114
113
  ORDER BY
115
114
  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,
122
115
  CASE WHEN so.status = 'queued' THEN 0 ELSE 1 END,
123
116
  so.created_at ASC
124
117
  FOR UPDATE OF so SKIP LOCKED
@@ -179,12 +172,6 @@ async function getOperationQueuePosition(db, operationId) {
179
172
  ROW_NUMBER() OVER (
180
173
  ORDER BY
181
174
  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
175
  CASE WHEN so.status = 'queued' THEN 0 ELSE 1 END,
189
176
  so.created_at ASC
190
177
  ) AS rn
@@ -195,7 +182,6 @@ async function getOperationQueuePosition(db, operationId) {
195
182
  WHERE status = 'running'
196
183
  GROUP BY account_id
197
184
  ) rc ON so.account_id = rc.account_id
198
- LEFT JOIN accounts a ON a.id::text = so.account_id
199
185
  WHERE so.status = 'queued'
200
186
  )
201
187
  SELECT rn FROM candidates WHERE id = ${operationId}
@@ -255,5 +241,5 @@ export {
255
241
  advanceOperationCursor
256
242
  };
257
243
 
258
- //# debugId=7C6D7169EDA84D0F64756E2164756E21
244
+ //# debugId=774B93C0E098748E64756E2164756E21
259
245
  //# 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\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"
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\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 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 * queued-first → FIFO). The heavy-budget admission filter is NOT applied — a\n * heavy op's eligibility depends on runtime budget state — so the position is\n * 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 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\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": "7C6D7169EDA84D0F64756E2164756E21",
7
+ "mappings": "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",
8
+ "debugId": "774B93C0E098748E64756E2164756E21",
9
9
  "names": []
10
10
  }
@@ -1,5 +1,4 @@
1
1
  import { Kysely } from "kysely";
2
- import postgres from "postgres";
3
2
  import { ColumnType, Generated, Selectable } from "kysely";
4
3
  /**
5
4
  * Coverage-kernel constraints. SQL CHECKs in migration 0116 must stay
@@ -224,11 +223,9 @@ interface AccountsTable {
224
223
  ghost: Generated<boolean>;
225
224
  /** Stacks principal owning a wallet-ghost account (x402-paid deploys). */
226
225
  wallet_principal: string | null;
227
- plan: Generated<string>;
228
226
  display_name: string | null;
229
227
  bio: string | null;
230
228
  avatar_url: string | null;
231
- slug: string | null;
232
229
  stripe_customer_id: string | null;
233
230
  /** Opt-out toggle for the subgraph reindex-completion email. Defaults true. */
234
231
  notify_reindex_complete: Generated<boolean>;
@@ -245,19 +242,6 @@ interface SessionsTable {
245
242
  last_used_at: Date | null;
246
243
  created_at: Generated<Date>;
247
244
  }
248
- /**
249
- * One-time claim tokens for ghost accounts. The raw token only ever appears in
250
- * the claim URL returned at mint time; we store its sha256. A used token marks
251
- * the account as claimed (or merged into an existing account).
252
- */
253
- interface ClaimTokensTable {
254
- id: Generated<string>;
255
- account_id: string;
256
- token_hash: string;
257
- created_at: Generated<Date>;
258
- expires_at: Date;
259
- used_at: Date | null;
260
- }
261
245
  interface MagicLinksTable {
262
246
  id: Generated<string>;
263
247
  email: string;
@@ -268,47 +252,6 @@ interface MagicLinksTable {
268
252
  failed_attempts: Generated<number>;
269
253
  created_at: Generated<Date>;
270
254
  }
271
- interface UsageDailyTable {
272
- account_id: string;
273
- tenant_id: string | null;
274
- date: string;
275
- api_requests: Generated<number>;
276
- deliveries: Generated<number>;
277
- streams_events_returned: Generated<number>;
278
- index_decoded_events_returned: Generated<number>;
279
- }
280
- interface UsageSnapshotsTable {
281
- id: Generated<string>;
282
- account_id: string;
283
- measured_at: Generated<Date>;
284
- storage_bytes: Generated<number>;
285
- }
286
- interface AccountInsightsTable {
287
- id: Generated<string>;
288
- account_id: string;
289
- category: string;
290
- insight_type: string;
291
- resource_id: string | null;
292
- severity: string;
293
- title: string;
294
- body: string;
295
- data: unknown;
296
- dismissed_at: Date | null;
297
- expires_at: Date | null;
298
- created_at: Generated<Date>;
299
- }
300
- interface AccountAgentRunsTable {
301
- id: Generated<string>;
302
- account_id: string;
303
- started_at: Generated<Date>;
304
- completed_at: Date | null;
305
- status: Generated<string>;
306
- input_tokens: Generated<number>;
307
- output_tokens: Generated<number>;
308
- cost_usd: Generated<number>;
309
- insights_created: Generated<number>;
310
- error: string | null;
311
- }
312
255
  interface SubgraphProcessingStatsTable {
313
256
  id: Generated<string>;
314
257
  subgraph_name: string;
@@ -341,41 +284,6 @@ interface SubgraphHealthSnapshotsTable {
341
284
  last_processed_block: number | null;
342
285
  captured_at: Generated<Date>;
343
286
  }
344
- interface SubgraphUsageDailyTable {
345
- subgraph_id: string;
346
- date: string;
347
- query_count: Generated<number>;
348
- }
349
- interface ProjectsTable {
350
- id: Generated<string>;
351
- name: string;
352
- slug: string;
353
- account_id: string;
354
- settings: Generated<Record<string, unknown>>;
355
- network: Generated<string>;
356
- node_rpc: string | null;
357
- created_at: Generated<Date>;
358
- updated_at: Generated<Date>;
359
- }
360
- interface TeamMembersTable {
361
- id: Generated<string>;
362
- project_id: string;
363
- account_id: string;
364
- role: Generated<string>;
365
- invited_by: string | null;
366
- created_at: Generated<Date>;
367
- }
368
- interface TeamInvitationsTable {
369
- id: Generated<string>;
370
- project_id: string;
371
- email: string;
372
- role: Generated<string>;
373
- token: string;
374
- invited_by: string | null;
375
- expires_at: Date;
376
- accepted_at: Date | null;
377
- created_at: Generated<Date>;
378
- }
379
287
  interface ProcessedStripeEventsTable {
380
288
  event_id: string;
381
289
  event_type: string;
@@ -828,27 +736,14 @@ interface Database {
828
736
  accounts: AccountsTable;
829
737
  sessions: SessionsTable;
830
738
  magic_links: MagicLinksTable;
831
- claim_tokens: ClaimTokensTable;
832
- usage_daily: UsageDailyTable;
833
- usage_snapshots: UsageSnapshotsTable;
834
- account_insights: AccountInsightsTable;
835
- account_agent_runs: AccountAgentRunsTable;
836
739
  subgraph_health_snapshots: SubgraphHealthSnapshotsTable;
837
740
  subgraph_processing_stats: SubgraphProcessingStatsTable;
838
741
  subgraph_table_snapshots: SubgraphTableSnapshotsTable;
839
742
  subgraph_gaps: SubgraphGapsTable;
840
743
  subgraph_operations: SubgraphOperationsTable;
841
- subgraph_usage_daily: SubgraphUsageDailyTable;
842
- projects: ProjectsTable;
843
- team_members: TeamMembersTable;
844
- team_invitations: TeamInvitationsTable;
845
744
  processed_stripe_events: ProcessedStripeEventsTable;
846
- tenants: TenantsTable;
847
- tenant_usage_monthly: TenantUsageMonthlyTable;
848
- tenant_compute_addons: TenantComputeAddonsTable;
849
745
  account_spend_caps: AccountSpendCapsTable;
850
746
  account_credits: AccountCreditsTable;
851
- provisioning_audit_log: ProvisioningAuditLogTable;
852
747
  subscriptions: SubscriptionsTable;
853
748
  subscription_outbox: SubscriptionOutboxTable;
854
749
  subscription_deliveries: SubscriptionDeliveriesTable;
@@ -923,57 +818,6 @@ interface X402PaymentsTable {
923
818
  * reconciler's heal path can never double-credit them. */
924
819
  credited_at: Generated<Date> | null;
925
820
  }
926
- type TenantStatus = "provisioning" | "active" | "limit_warning" | "paused_limit" | "suspended" | "error" | "deleted";
927
- interface TenantsTable {
928
- id: Generated<string>;
929
- account_id: string;
930
- slug: string;
931
- status: ColumnType<TenantStatus, TenantStatus | undefined, TenantStatus>;
932
- plan: string;
933
- cpus: ColumnType<number, number | string, number | string>;
934
- memory_mb: number;
935
- storage_limit_mb: number;
936
- storage_used_mb: number | null;
937
- pg_container_id: string | null;
938
- api_container_id: string | null;
939
- processor_container_id: string | null;
940
- target_database_url_enc: Buffer;
941
- tenant_jwt_secret_enc: Buffer;
942
- anon_key_enc: Buffer;
943
- service_key_enc: Buffer;
944
- api_url_internal: string;
945
- api_url_public: string;
946
- suspended_at: Date | null;
947
- last_health_check_at: Date | null;
948
- last_active_at: Generated<Date>;
949
- service_gen: Generated<number>;
950
- anon_gen: Generated<number>;
951
- project_id: string | null;
952
- created_at: Generated<Date>;
953
- updated_at: Generated<Date>;
954
- }
955
- interface TenantUsageMonthlyTable {
956
- id: Generated<string>;
957
- tenant_id: string;
958
- period_month: Date;
959
- storage_peak_mb: Generated<number>;
960
- storage_avg_mb: Generated<number>;
961
- storage_last_mb: Generated<number>;
962
- measurements: Generated<number>;
963
- first_at: Generated<Date>;
964
- last_at: Generated<Date>;
965
- }
966
- interface TenantComputeAddonsTable {
967
- id: Generated<string>;
968
- tenant_id: string;
969
- memory_mb_delta: Generated<number>;
970
- cpu_delta: Generated<number | string>;
971
- storage_mb_delta: Generated<number>;
972
- effective_from: Generated<Date>;
973
- effective_until: Date | null;
974
- stripe_subscription_item_id: string | null;
975
- created_at: Generated<Date>;
976
- }
977
821
  interface AccountSpendCapsTable {
978
822
  account_id: string;
979
823
  monthly_cap_cents: number | null;
@@ -997,20 +841,6 @@ interface AccountCreditsTable {
997
841
  refill_last_at: Date | null;
998
842
  updated_at: Generated<Date>;
999
843
  }
1000
- type ProvisioningAuditEvent = "provision.start" | "provision.success" | "provision.failure" | "suspend" | "resume" | "resize" | "keys.rotate" | "bastion.key.upload" | "bastion.key.revoke" | "teardown";
1001
- type ProvisioningAuditStatus = "ok" | "error";
1002
- interface ProvisioningAuditLogTable {
1003
- id: Generated<string>;
1004
- tenant_id: string | null;
1005
- tenant_slug: string | null;
1006
- account_id: string | null;
1007
- actor: string;
1008
- event: ProvisioningAuditEvent;
1009
- status: ProvisioningAuditStatus;
1010
- detail: unknown | null;
1011
- error: string | null;
1012
- created_at: Generated<Date>;
1013
- }
1014
844
  type Subgraph = Selectable<SubgraphsTable>;
1015
845
  type SubscriptionStatus = "active" | "paused" | "error";
1016
846
  /** Polymorphic subscription mode: `subgraph` reacts to processed table rows;
@@ -1101,24 +931,6 @@ interface TriggerEvaluatorStateTable {
1101
931
  updated_at: Generated<Date>;
1102
932
  }
1103
933
  /**
1104
- * BYO data plane helpers. A subgraph's user-owned Postgres connection string is
1105
- * stored encrypted at rest in `database_url_enc` (AES-GCM envelope). Plaintext
1106
- * only exists transiently — at deploy (to encrypt) and at pool construction (to
1107
- * connect). Never serialize it into API responses.
1108
- */
1109
- declare function encryptDatabaseUrl(url: string): Buffer;
1110
- /** Decrypt a subgraph's BYO connection string, or null when managed. */
1111
- declare function subgraphDatabaseUrl(subgraph: Subgraph): string | null;
1112
- /** True when the subgraph writes/serves from a user-owned DB. */
1113
- declare function isByoSubgraph(subgraph: Subgraph): boolean;
1114
- /**
1115
- * Resolve the Kysely instance a subgraph's data plane lives on: the user's DB
1116
- * when BYO, else the managed target DB. Pools are cached by URL in db/index.ts.
1117
- */
1118
- declare function resolveSubgraphDb(subgraph: Subgraph): Kysely<Database>;
1119
- /** Raw postgres.js client for a subgraph's data plane (DDL / serving queries). */
1120
- declare function resolveSubgraphRawClient(subgraph: Subgraph): ReturnType<typeof postgres>;
1121
- /**
1122
934
  * Convert a subgraph name to its PostgreSQL schema name (legacy form).
1123
935
  * Pre shared-rip every tenant DB had its own schema namespace so disambiguation
1124
936
  * was implicit. Kept for oss mode (single-tenant) and legacy-row fallback.
@@ -1145,8 +957,6 @@ declare function registerSubgraph(db: Kysely<Database>, data: {
1145
957
  startBlock?: number
1146
958
  handlerCode?: string
1147
959
  sourceCode?: string
1148
- /** BYO data plane: encrypted user-DB connection string, or null = managed. */
1149
- databaseUrlEnc?: Buffer | null
1150
960
  }): Promise<Subgraph>;
1151
961
  declare function getSubgraph(db: Kysely<Database>, name: string, accountId?: string): Promise<Subgraph | null>;
1152
962
  declare function listSubgraphs(db: Kysely<Database>, accountId?: string): Promise<Subgraph[]>;
@@ -1233,4 +1043,4 @@ declare function recordLiveProgress(db: Kysely<Database>, name: string, lastProc
1233
1043
  * forward write between the rewind and the fork block's reprocess. Do not
1234
1044
  * call this from the live catch-up path. */
1235
1045
  declare function rewindLiveProgress(db: Kysely<Database>, name: string, lastProcessedBlock: number): Promise<void>;
1236
- export { updateSubgraphVisibility, updateSubgraphStatus, updateSubgraphHandlerPath, updateSubgraphExpiry, subgraphDatabaseUrl, rewindLiveProgress, resolveSubgraphRawClient, resolveSubgraphDb, registerSubgraph, recordSubgraphProcessed, recordLiveProgress, recordLiveError, pgSchemaNameFor, pgSchemaName, localSubgraphAccountId, listSubgraphs, isByoSubgraph, getSubgraph, findPublicSubgraphByName, encryptDatabaseUrl, deleteSubgraph };
1046
+ export { updateSubgraphVisibility, updateSubgraphStatus, updateSubgraphHandlerPath, updateSubgraphExpiry, rewindLiveProgress, registerSubgraph, recordSubgraphProcessed, recordLiveProgress, recordLiveError, pgSchemaNameFor, pgSchemaName, localSubgraphAccountId, listSubgraphs, getSubgraph, findPublicSubgraphByName, deleteSubgraph };