@secondlayer/shared 5.0.0 → 5.1.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 (32) hide show
  1. package/dist/src/db/index.d.ts +7 -1
  2. package/dist/src/db/queries/account-spend-caps.d.ts +6 -0
  3. package/dist/src/db/queries/account-usage.d.ts +6 -0
  4. package/dist/src/db/queries/account-usage.js +4 -2
  5. package/dist/src/db/queries/account-usage.js.map +3 -3
  6. package/dist/src/db/queries/accounts.d.ts +6 -0
  7. package/dist/src/db/queries/integrity.d.ts +6 -0
  8. package/dist/src/db/queries/projects.d.ts +6 -0
  9. package/dist/src/db/queries/provisioning-audit.d.ts +6 -0
  10. package/dist/src/db/queries/subgraph-gaps.d.ts +6 -0
  11. package/dist/src/db/queries/subgraph-operations.d.ts +6 -0
  12. package/dist/src/db/queries/subgraphs.d.ts +6 -0
  13. package/dist/src/db/queries/subgraphs.js +7 -1
  14. package/dist/src/db/queries/subgraphs.js.map +3 -3
  15. package/dist/src/db/queries/subscriptions.d.ts +6 -0
  16. package/dist/src/db/queries/tenant-compute-addons.d.ts +6 -0
  17. package/dist/src/db/queries/tenants.d.ts +6 -0
  18. package/dist/src/db/queries/usage.d.ts +6 -0
  19. package/dist/src/db/schema.d.ts +7 -1
  20. package/dist/src/index.d.ts +17 -1
  21. package/dist/src/index.js.map +1 -1
  22. package/dist/src/node/local-client.d.ts +6 -0
  23. package/dist/src/pricing.d.ts +1 -1
  24. package/dist/src/pricing.js +4 -2
  25. package/dist/src/pricing.js.map +3 -3
  26. package/dist/src/schemas/index.d.ts +11 -1
  27. package/dist/src/schemas/index.js.map +1 -1
  28. package/dist/src/schemas/subgraphs.d.ts +11 -1
  29. package/dist/src/schemas/subgraphs.js.map +1 -1
  30. package/dist/src/subgraphs/spec.d.ts +9 -0
  31. package/migrations/0063_processed_stripe_events.ts +33 -0
  32. package/package.json +1 -1
@@ -286,6 +286,11 @@ interface ChatMessagesTable {
286
286
  metadata: unknown | null;
287
287
  created_at: Generated<Date>;
288
288
  }
289
+ interface ProcessedStripeEventsTable {
290
+ event_id: string;
291
+ event_type: string;
292
+ processed_at: Generated<Date>;
293
+ }
289
294
  interface Database {
290
295
  blocks: BlocksTable;
291
296
  transactions: TransactionsTable;
@@ -312,6 +317,7 @@ interface Database {
312
317
  team_invitations: TeamInvitationsTable;
313
318
  chat_sessions: ChatSessionsTable;
314
319
  chat_messages: ChatMessagesTable;
320
+ processed_stripe_events: ProcessedStripeEventsTable;
315
321
  tenants: TenantsTable;
316
322
  tenant_usage_monthly: TenantUsageMonthlyTable;
317
323
  tenant_compute_addons: TenantComputeAddonsTable;
@@ -556,4 +562,4 @@ declare function getDb(connectionString?: string): Kysely<Database>;
556
562
  declare function getRawClient(role?: "source" | "target"): ReturnType<typeof postgres>;
557
563
  /** Close all DB connection pools. Call in CLI commands to allow process exit. */
558
564
  declare function closeDb(): Promise<void>;
559
- export { sql, parseJsonb, jsonb, getTargetDb, getSourceDb, getRawClient, getDb, closeDb, WaitlistTable, UsageSnapshotsTable, UsageSnapshot, UsageDailyTable, UsageDaily, UpdateTransaction, UpdateTenantUsageMonthly, UpdateTenantComputeAddon, UpdateTenant, UpdateSubscriptionOutbox, UpdateSubscription, UpdateSubgraphOperation, UpdateSubgraph, UpdateProject, UpdateIndexProgress, UpdateEvent, UpdateChatSession, UpdateBlock, UpdateApiKey, UpdateAccountSpendCap, TransactionsTable, Transaction, TenantsTable, TenantUsageMonthlyTable, TenantUsageMonthly, TenantStatus, TenantComputeAddonsTable, TenantComputeAddon, Tenant, TeamMembersTable, TeamMember, TeamInvitationsTable, TeamInvitation, SubscriptionsTable, SubscriptionStatus, SubscriptionRuntime, SubscriptionOutboxTable, SubscriptionOutbox, SubscriptionFormat, SubscriptionDelivery, SubscriptionDeliveriesTable, Subscription, SubgraphsTable, SubgraphUsageDailyTable, SubgraphUsageDaily, SubgraphTableSnapshotsTable, SubgraphProcessingStatsTable, SubgraphOperationsTable, SubgraphOperationStatus, SubgraphOperationKind, SubgraphOperation, SubgraphHealthSnapshotsTable, SubgraphHealthSnapshot, SubgraphGapsTable, SubgraphGap, Subgraph, SessionsTable, Session, ProvisioningAuditStatus, ProvisioningAuditLogTable, ProvisioningAuditLog, ProvisioningAuditEvent, ProjectsTable, Project, OutboxStatus, MagicLinksTable, MagicLink, InsertTransaction, InsertTenantUsageMonthly, InsertTenantComputeAddon, InsertTenant, InsertTeamMember, InsertTeamInvitation, InsertSubscriptionOutbox, InsertSubscriptionDelivery, InsertSubscription, InsertSubgraphUsageDaily, InsertSubgraphOperation, InsertSubgraphHealthSnapshot, InsertSubgraphGap, InsertSubgraph, InsertSession, InsertProvisioningAuditLog, InsertProject, InsertMagicLink, InsertIndexProgress, InsertEvent, InsertChatSession, InsertChatMessage, InsertBlock, InsertApiKey, InsertAccountSpendCap, InsertAccountInsight, InsertAccountAgentRun, InsertAccount, IndexProgressTable, IndexProgress, EventsTable, Event, Database, ChatSessionsTable, ChatSession, ChatMessagesTable, ChatMessage, BlocksTable, Block, ApiKeysTable, ApiKey, AccountsTable, AccountSpendCapsTable, AccountSpendCap, AccountInsightsTable, AccountInsight, AccountAgentRunsTable, AccountAgentRun, Account };
565
+ export { sql, parseJsonb, jsonb, getTargetDb, getSourceDb, getRawClient, getDb, closeDb, WaitlistTable, UsageSnapshotsTable, UsageSnapshot, UsageDailyTable, UsageDaily, UpdateTransaction, UpdateTenantUsageMonthly, UpdateTenantComputeAddon, UpdateTenant, UpdateSubscriptionOutbox, UpdateSubscription, UpdateSubgraphOperation, UpdateSubgraph, UpdateProject, UpdateIndexProgress, UpdateEvent, UpdateChatSession, UpdateBlock, UpdateApiKey, UpdateAccountSpendCap, TransactionsTable, Transaction, TenantsTable, TenantUsageMonthlyTable, TenantUsageMonthly, TenantStatus, TenantComputeAddonsTable, TenantComputeAddon, Tenant, TeamMembersTable, TeamMember, TeamInvitationsTable, TeamInvitation, SubscriptionsTable, SubscriptionStatus, SubscriptionRuntime, SubscriptionOutboxTable, SubscriptionOutbox, SubscriptionFormat, SubscriptionDelivery, SubscriptionDeliveriesTable, Subscription, SubgraphsTable, SubgraphUsageDailyTable, SubgraphUsageDaily, SubgraphTableSnapshotsTable, SubgraphProcessingStatsTable, SubgraphOperationsTable, SubgraphOperationStatus, SubgraphOperationKind, SubgraphOperation, SubgraphHealthSnapshotsTable, SubgraphHealthSnapshot, SubgraphGapsTable, SubgraphGap, Subgraph, SessionsTable, Session, ProvisioningAuditStatus, ProvisioningAuditLogTable, ProvisioningAuditLog, ProvisioningAuditEvent, ProjectsTable, Project, ProcessedStripeEventsTable, OutboxStatus, MagicLinksTable, MagicLink, InsertTransaction, InsertTenantUsageMonthly, InsertTenantComputeAddon, InsertTenant, InsertTeamMember, InsertTeamInvitation, InsertSubscriptionOutbox, InsertSubscriptionDelivery, InsertSubscription, InsertSubgraphUsageDaily, InsertSubgraphOperation, InsertSubgraphHealthSnapshot, InsertSubgraphGap, InsertSubgraph, InsertSession, InsertProvisioningAuditLog, InsertProject, InsertMagicLink, InsertIndexProgress, InsertEvent, InsertChatSession, InsertChatMessage, InsertBlock, InsertApiKey, InsertAccountSpendCap, InsertAccountInsight, InsertAccountAgentRun, InsertAccount, IndexProgressTable, IndexProgress, EventsTable, Event, Database, ChatSessionsTable, ChatSession, ChatMessagesTable, ChatMessage, BlocksTable, Block, ApiKeysTable, ApiKey, AccountsTable, AccountSpendCapsTable, AccountSpendCap, AccountInsightsTable, AccountInsight, AccountAgentRunsTable, AccountAgentRun, Account };
@@ -268,6 +268,11 @@ interface ChatMessagesTable {
268
268
  metadata: unknown | null;
269
269
  created_at: Generated<Date>;
270
270
  }
271
+ interface ProcessedStripeEventsTable {
272
+ event_id: string;
273
+ event_type: string;
274
+ processed_at: Generated<Date>;
275
+ }
271
276
  interface Database {
272
277
  blocks: BlocksTable;
273
278
  transactions: TransactionsTable;
@@ -294,6 +299,7 @@ interface Database {
294
299
  team_invitations: TeamInvitationsTable;
295
300
  chat_sessions: ChatSessionsTable;
296
301
  chat_messages: ChatMessagesTable;
302
+ processed_stripe_events: ProcessedStripeEventsTable;
297
303
  tenants: TenantsTable;
298
304
  tenant_usage_monthly: TenantUsageMonthlyTable;
299
305
  tenant_compute_addons: TenantComputeAddonsTable;
@@ -268,6 +268,11 @@ interface ChatMessagesTable {
268
268
  metadata: unknown | null;
269
269
  created_at: Generated<Date>;
270
270
  }
271
+ interface ProcessedStripeEventsTable {
272
+ event_id: string;
273
+ event_type: string;
274
+ processed_at: Generated<Date>;
275
+ }
271
276
  interface Database {
272
277
  blocks: BlocksTable;
273
278
  transactions: TransactionsTable;
@@ -294,6 +299,7 @@ interface Database {
294
299
  team_invitations: TeamInvitationsTable;
295
300
  chat_sessions: ChatSessionsTable;
296
301
  chat_messages: ChatMessagesTable;
302
+ processed_stripe_events: ProcessedStripeEventsTable;
297
303
  tenants: TenantsTable;
298
304
  tenant_usage_monthly: TenantUsageMonthlyTable;
299
305
  tenant_compute_addons: TenantComputeAddonsTable;
@@ -67,7 +67,8 @@ var PLANS = {
67
67
  features: [
68
68
  "0.5 vCPU · 512 MB RAM",
69
69
  "5 GB storage · auto-pause 7d",
70
- "Subgraphs + subscriptions",
70
+ "Starter subgraphs + subscriptions",
71
+ "Recent-range reindexing",
71
72
  "Community support"
72
73
  ],
73
74
  stripeLookupKey: null
@@ -85,6 +86,7 @@ var PLANS = {
85
86
  "1 vCPU · 2 GB RAM",
86
87
  "25 GB storage · always-on",
87
88
  "Unlimited subgraphs + subscriptions",
89
+ "Larger reindex windows",
88
90
  "Spend caps + alerts",
89
91
  "Email support"
90
92
  ],
@@ -277,5 +279,5 @@ export {
277
279
  getComputeUsage
278
280
  };
279
281
 
280
- //# debugId=7A0FEDB720F0E4A864756E2164756E21
282
+ //# debugId=7CE87B0F0526DA2864756E2164756E21
281
283
  //# sourceMappingURL=account-usage.js.map
@@ -2,10 +2,10 @@
2
2
  "version": 3,
3
3
  "sources": ["../src/pricing.ts", "../src/db/queries/account-usage.ts"],
4
4
  "sourcesContent": [
5
- "/**\n * Single source of truth for plan tiers — capacity, price, display copy,\n * Stripe binding, and container allocations.\n *\n * Consumed by:\n * - Provisioner (`packages/provisioner/src/plans.ts` re-exports)\n * - API (`/api/accounts/usage` for allowance math + display)\n * - Web app (`/billing` page renders plan cards from this)\n *\n * Adding a tier? Add an entry to PLANS. Removing one? Drop here, run\n * the Stripe-side cleanup (archive lookup_key), and update env vars.\n */\n\nconst BYTES_PER_GB = 1024 ** 3;\n\nexport type PlanId = \"hobby\" | \"launch\" | \"scale\" | \"enterprise\";\n\nexport interface ContainerAlloc {\n\tmemoryMb: number;\n\tcpus: number;\n}\n\nexport interface Plan {\n\tid: PlanId;\n\tdisplayName: string;\n\t/** Monthly subscription price in cents. null = custom (Enterprise). */\n\tmonthlyPriceCents: number | null;\n\ttotalCpus: number;\n\ttotalMemoryMb: number;\n\t/** Hard cap. -1 = unlimited (Enterprise). Storage overage bills past this. */\n\tstorageLimitMb: number;\n\tcontainers: {\n\t\tpostgres: ContainerAlloc;\n\t\tapi: ContainerAlloc;\n\t\tprocessor: ContainerAlloc;\n\t};\n\t/** Display-only. Marketing/short pitch. */\n\ttagline: string;\n\t/** Display-only. Bullet list on the plan card. */\n\tfeatures: string[];\n\t/** Stripe `lookup_key` for the recurring tier price. null for hobby/enterprise. */\n\tstripeLookupKey: string | null;\n}\n\n// ── Allocation helpers ──────────────────────────────────────────────\n//\n// Allocation within a plan (3 containers per tenant):\n// Default split (paid tiers) — PG 50% / proc 30% / api 20%\n// Sub-1GB total (Hobby) — PG 60% / proc 25% / api 15%\n//\n// Biased toward PG on Hobby because PG 17's default `shared_buffers` is\n// 128MB — a naive 50/30/20 split on 512MB leaves PG with 256MB RAM, which\n// is technically workable but crashes if `shared_buffers` isn't also\n// shrunk. The 60/25/15 split gives PG 307MB, more headroom.\n//\n// Docker memory limit is a hard cap (OOM kill on overage). CPU is a soft\n// cap via `--cpus` (throttling, not killing). Storage is monitored\n// separately and billed as overage — PG crashes if we hard-cap it.\n\nfunction alloc(totalMb: number, totalCpus: number): Plan[\"containers\"] {\n\treturn {\n\t\tpostgres: {\n\t\t\tmemoryMb: Math.floor(totalMb * 0.5),\n\t\t\tcpus: round2(totalCpus * 0.5),\n\t\t},\n\t\tprocessor: {\n\t\t\tmemoryMb: Math.floor(totalMb * 0.3),\n\t\t\tcpus: round2(totalCpus * 0.3),\n\t\t},\n\t\tapi: {\n\t\t\tmemoryMb: Math.floor(totalMb * 0.2),\n\t\t\tcpus: round2(totalCpus * 0.2),\n\t\t},\n\t};\n}\n\nfunction allocTight(totalMb: number, totalCpus: number): Plan[\"containers\"] {\n\treturn {\n\t\tpostgres: {\n\t\t\tmemoryMb: Math.floor(totalMb * 0.6),\n\t\t\tcpus: round2(totalCpus * 0.6),\n\t\t},\n\t\tprocessor: {\n\t\t\tmemoryMb: Math.floor(totalMb * 0.25),\n\t\t\tcpus: round2(totalCpus * 0.25),\n\t\t},\n\t\tapi: {\n\t\t\tmemoryMb: Math.floor(totalMb * 0.15),\n\t\t\tcpus: round2(totalCpus * 0.15),\n\t\t},\n\t};\n}\n\nfunction round2(n: number): number {\n\treturn Math.round(n * 100) / 100;\n}\n\n/**\n * Split a compute envelope across (postgres, processor, api) containers.\n * Auto-biases PG-heavy (60/25/15) for sub-1GB totals.\n */\nexport function allocForTotals(\n\ttotalMemoryMb: number,\n\ttotalCpus: number,\n): Plan[\"containers\"] {\n\treturn totalMemoryMb < 1024\n\t\t? allocTight(totalMemoryMb, totalCpus)\n\t\t: alloc(totalMemoryMb, totalCpus);\n}\n\n// ── Canonical plan data ─────────────────────────────────────────────\n\nexport const PLANS: Record<PlanId, Plan> = {\n\thobby: {\n\t\tid: \"hobby\",\n\t\tdisplayName: \"Hobby\",\n\t\tmonthlyPriceCents: 0,\n\t\ttotalCpus: 0.5,\n\t\ttotalMemoryMb: 512,\n\t\tstorageLimitMb: 5_120,\n\t\tcontainers: allocTight(512, 0.5),\n\t\ttagline: \"Free, forever\",\n\t\tfeatures: [\n\t\t\t\"0.5 vCPU · 512 MB RAM\",\n\t\t\t\"5 GB storage · auto-pause 7d\",\n\t\t\t\"Subgraphs + subscriptions\",\n\t\t\t\"Community support\",\n\t\t],\n\t\tstripeLookupKey: null,\n\t},\n\tlaunch: {\n\t\tid: \"launch\",\n\t\tdisplayName: \"Launch\",\n\t\tmonthlyPriceCents: 5_000, // $50\n\t\ttotalCpus: 1,\n\t\ttotalMemoryMb: 2_048,\n\t\tstorageLimitMb: 25_600, // 25 GB\n\t\tcontainers: alloc(2_048, 1),\n\t\ttagline: \"Production-ready\",\n\t\tfeatures: [\n\t\t\t\"1 vCPU · 2 GB RAM\",\n\t\t\t\"25 GB storage · always-on\",\n\t\t\t\"Unlimited subgraphs + subscriptions\",\n\t\t\t\"Spend caps + alerts\",\n\t\t\t\"Email support\",\n\t\t],\n\t\tstripeLookupKey: \"secondlayer_launch_monthly\",\n\t},\n\tscale: {\n\t\tid: \"scale\",\n\t\tdisplayName: \"Scale\",\n\t\tmonthlyPriceCents: 20_000, // $200\n\t\ttotalCpus: 4,\n\t\ttotalMemoryMb: 8_192,\n\t\tstorageLimitMb: 102_400, // 100 GB\n\t\tcontainers: alloc(8_192, 4),\n\t\ttagline: \"Scale with confidence\",\n\t\tfeatures: [\n\t\t\t\"4 vCPU · 8 GB RAM\",\n\t\t\t\"100 GB storage · always-on\",\n\t\t\t\"Higher throughput + replay\",\n\t\t\t\"24h SLA · priority support\",\n\t\t],\n\t\tstripeLookupKey: \"secondlayer_scale_monthly\",\n\t},\n\tenterprise: {\n\t\tid: \"enterprise\",\n\t\tdisplayName: \"Enterprise\",\n\t\tmonthlyPriceCents: null,\n\t\ttotalCpus: 8,\n\t\ttotalMemoryMb: 32_768,\n\t\tstorageLimitMb: -1,\n\t\tcontainers: alloc(32_768, 8),\n\t\ttagline: \"Custom workloads\",\n\t\tfeatures: [\n\t\t\t\"Custom compute + storage\",\n\t\t\t\"SLAs · regions · SSO\",\n\t\t\t\"Dedicated success engineer\",\n\t\t],\n\t\tstripeLookupKey: null,\n\t},\n};\n\nexport const PLAN_IDS: readonly PlanId[] = [\n\t\"hobby\",\n\t\"launch\",\n\t\"scale\",\n\t\"enterprise\",\n];\n\nexport function getPlan(id: string): Plan {\n\tconst plan = (PLANS as Record<string, Plan | undefined>)[id];\n\tif (!plan) throw new Error(`Unknown plan: ${id}`);\n\treturn plan;\n}\n\nexport function isValidPlanId(id: string): id is PlanId {\n\treturn id in PLANS;\n}\n\n// ── Allowance helpers (used by /api/accounts/usage display) ─────────\n//\n// Compute is hard-capped by Docker `--cpus`, so there's no compute\n// overage billing. The function below returns ∞ for paid plans (display-\n// only — no metering).\n//\n// Storage IS metered and billed past the plan's allowance via the\n// `storage_gb_months` Stripe meter at $2/GB-mo.\n\nexport function getComputeAllowanceHours(_plan: string): number {\n\t// Compute overage was killed when we removed the `compute_hours` meter.\n\t// All plans are now hard-capped by Docker `--cpus`. ∞ here means \"no\n\t// overage tracked\" for display purposes.\n\treturn Number.POSITIVE_INFINITY;\n}\n\nexport function getStorageAllowanceBytes(plan: string): number {\n\tconst planDef = (PLANS as Record<string, Plan | undefined>)[plan];\n\tif (!planDef) return PLANS.hobby.storageLimitMb * 1024 * 1024;\n\tif (planDef.storageLimitMb < 0) return Number.POSITIVE_INFINITY;\n\treturn planDef.storageLimitMb * 1024 * 1024;\n}\n\n/** Hobby has a hard cap (no overage billing); paid tiers bill $2/GB over allowance. */\nexport function hasStorageOverage(plan: string): boolean {\n\treturn plan !== \"hobby\" && plan !== \"enterprise\";\n}\n\nexport function getBasePriceCents(plan: string): number {\n\tconst planDef = (PLANS as Record<string, Plan | undefined>)[plan];\n\treturn planDef?.monthlyPriceCents ?? 0;\n}\n\nexport function getPlanDisplayName(plan: string): string {\n\tconst planDef = (PLANS as Record<string, Plan | undefined>)[plan];\n\treturn planDef?.displayName ?? plan.charAt(0).toUpperCase() + plan.slice(1);\n}\n\n// Re-export bytes-per-GB constant for callers that compute display values.\nexport { BYTES_PER_GB };\n",
5
+ "/**\n * Single source of truth for plan tiers — capacity, price, display copy,\n * Stripe binding, and container allocations.\n *\n * Consumed by:\n * - Provisioner (`packages/provisioner/src/plans.ts` re-exports)\n * - API (`/api/accounts/usage` for allowance math + display)\n * - Web app (`/billing` page renders plan cards from this)\n *\n * Adding a tier? Add an entry to PLANS. Removing one? Drop here, run\n * the Stripe-side cleanup (archive lookup_key), and update env vars.\n */\n\nconst BYTES_PER_GB: number = 1024 ** 3;\n\nexport type PlanId = \"hobby\" | \"launch\" | \"scale\" | \"enterprise\";\n\nexport interface ContainerAlloc {\n\tmemoryMb: number;\n\tcpus: number;\n}\n\nexport interface Plan {\n\tid: PlanId;\n\tdisplayName: string;\n\t/** Monthly subscription price in cents. null = custom (Enterprise). */\n\tmonthlyPriceCents: number | null;\n\ttotalCpus: number;\n\ttotalMemoryMb: number;\n\t/** Hard cap. -1 = unlimited (Enterprise). Storage overage bills past this. */\n\tstorageLimitMb: number;\n\tcontainers: {\n\t\tpostgres: ContainerAlloc;\n\t\tapi: ContainerAlloc;\n\t\tprocessor: ContainerAlloc;\n\t};\n\t/** Display-only. Marketing/short pitch. */\n\ttagline: string;\n\t/** Display-only. Bullet list on the plan card. */\n\tfeatures: string[];\n\t/** Stripe `lookup_key` for the recurring tier price. null for hobby/enterprise. */\n\tstripeLookupKey: string | null;\n}\n\n// ── Allocation helpers ──────────────────────────────────────────────\n//\n// Allocation within a plan (3 containers per tenant):\n// Default split (paid tiers) — PG 50% / proc 30% / api 20%\n// Sub-1GB total (Hobby) — PG 60% / proc 25% / api 15%\n//\n// Biased toward PG on Hobby because PG 17's default `shared_buffers` is\n// 128MB — a naive 50/30/20 split on 512MB leaves PG with 256MB RAM, which\n// is technically workable but crashes if `shared_buffers` isn't also\n// shrunk. The 60/25/15 split gives PG 307MB, more headroom.\n//\n// Docker memory limit is a hard cap (OOM kill on overage). CPU is a soft\n// cap via `--cpus` (throttling, not killing). Storage is monitored\n// separately and billed as overage — PG crashes if we hard-cap it.\n\nfunction alloc(totalMb: number, totalCpus: number): Plan[\"containers\"] {\n\treturn {\n\t\tpostgres: {\n\t\t\tmemoryMb: Math.floor(totalMb * 0.5),\n\t\t\tcpus: round2(totalCpus * 0.5),\n\t\t},\n\t\tprocessor: {\n\t\t\tmemoryMb: Math.floor(totalMb * 0.3),\n\t\t\tcpus: round2(totalCpus * 0.3),\n\t\t},\n\t\tapi: {\n\t\t\tmemoryMb: Math.floor(totalMb * 0.2),\n\t\t\tcpus: round2(totalCpus * 0.2),\n\t\t},\n\t};\n}\n\nfunction allocTight(totalMb: number, totalCpus: number): Plan[\"containers\"] {\n\treturn {\n\t\tpostgres: {\n\t\t\tmemoryMb: Math.floor(totalMb * 0.6),\n\t\t\tcpus: round2(totalCpus * 0.6),\n\t\t},\n\t\tprocessor: {\n\t\t\tmemoryMb: Math.floor(totalMb * 0.25),\n\t\t\tcpus: round2(totalCpus * 0.25),\n\t\t},\n\t\tapi: {\n\t\t\tmemoryMb: Math.floor(totalMb * 0.15),\n\t\t\tcpus: round2(totalCpus * 0.15),\n\t\t},\n\t};\n}\n\nfunction round2(n: number): number {\n\treturn Math.round(n * 100) / 100;\n}\n\n/**\n * Split a compute envelope across (postgres, processor, api) containers.\n * Auto-biases PG-heavy (60/25/15) for sub-1GB totals.\n */\nexport function allocForTotals(\n\ttotalMemoryMb: number,\n\ttotalCpus: number,\n): Plan[\"containers\"] {\n\treturn totalMemoryMb < 1024\n\t\t? allocTight(totalMemoryMb, totalCpus)\n\t\t: alloc(totalMemoryMb, totalCpus);\n}\n\n// ── Canonical plan data ─────────────────────────────────────────────\n\nexport const PLANS: Record<PlanId, Plan> = {\n\thobby: {\n\t\tid: \"hobby\",\n\t\tdisplayName: \"Hobby\",\n\t\tmonthlyPriceCents: 0,\n\t\ttotalCpus: 0.5,\n\t\ttotalMemoryMb: 512,\n\t\tstorageLimitMb: 5_120,\n\t\tcontainers: allocTight(512, 0.5),\n\t\ttagline: \"Free, forever\",\n\t\tfeatures: [\n\t\t\t\"0.5 vCPU · 512 MB RAM\",\n\t\t\t\"5 GB storage · auto-pause 7d\",\n\t\t\t\"Starter subgraphs + subscriptions\",\n\t\t\t\"Recent-range reindexing\",\n\t\t\t\"Community support\",\n\t\t],\n\t\tstripeLookupKey: null,\n\t},\n\tlaunch: {\n\t\tid: \"launch\",\n\t\tdisplayName: \"Launch\",\n\t\tmonthlyPriceCents: 5_000, // $50\n\t\ttotalCpus: 1,\n\t\ttotalMemoryMb: 2_048,\n\t\tstorageLimitMb: 25_600, // 25 GB\n\t\tcontainers: alloc(2_048, 1),\n\t\ttagline: \"Production-ready\",\n\t\tfeatures: [\n\t\t\t\"1 vCPU · 2 GB RAM\",\n\t\t\t\"25 GB storage · always-on\",\n\t\t\t\"Unlimited subgraphs + subscriptions\",\n\t\t\t\"Larger reindex windows\",\n\t\t\t\"Spend caps + alerts\",\n\t\t\t\"Email support\",\n\t\t],\n\t\tstripeLookupKey: \"secondlayer_launch_monthly\",\n\t},\n\tscale: {\n\t\tid: \"scale\",\n\t\tdisplayName: \"Scale\",\n\t\tmonthlyPriceCents: 20_000, // $200\n\t\ttotalCpus: 4,\n\t\ttotalMemoryMb: 8_192,\n\t\tstorageLimitMb: 102_400, // 100 GB\n\t\tcontainers: alloc(8_192, 4),\n\t\ttagline: \"Scale with confidence\",\n\t\tfeatures: [\n\t\t\t\"4 vCPU · 8 GB RAM\",\n\t\t\t\"100 GB storage · always-on\",\n\t\t\t\"Higher throughput + replay\",\n\t\t\t\"24h SLA · priority support\",\n\t\t],\n\t\tstripeLookupKey: \"secondlayer_scale_monthly\",\n\t},\n\tenterprise: {\n\t\tid: \"enterprise\",\n\t\tdisplayName: \"Enterprise\",\n\t\tmonthlyPriceCents: null,\n\t\ttotalCpus: 8,\n\t\ttotalMemoryMb: 32_768,\n\t\tstorageLimitMb: -1,\n\t\tcontainers: alloc(32_768, 8),\n\t\ttagline: \"Custom workloads\",\n\t\tfeatures: [\n\t\t\t\"Custom compute + storage\",\n\t\t\t\"SLAs · regions · SSO\",\n\t\t\t\"Dedicated success engineer\",\n\t\t],\n\t\tstripeLookupKey: null,\n\t},\n};\n\nexport const PLAN_IDS: readonly PlanId[] = [\n\t\"hobby\",\n\t\"launch\",\n\t\"scale\",\n\t\"enterprise\",\n];\n\nexport function getPlan(id: string): Plan {\n\tconst plan = (PLANS as Record<string, Plan | undefined>)[id];\n\tif (!plan) throw new Error(`Unknown plan: ${id}`);\n\treturn plan;\n}\n\nexport function isValidPlanId(id: string): id is PlanId {\n\treturn id in PLANS;\n}\n\n// ── Allowance helpers (used by /api/accounts/usage display) ─────────\n//\n// Compute is hard-capped by Docker `--cpus`, so there's no compute\n// overage billing. The function below returns ∞ for paid plans (display-\n// only — no metering).\n//\n// Storage IS metered and billed past the plan's allowance via the\n// `storage_gb_months` Stripe meter at $2/GB-mo.\n\nexport function getComputeAllowanceHours(_plan: string): number {\n\t// Compute overage was killed when we removed the `compute_hours` meter.\n\t// All plans are now hard-capped by Docker `--cpus`. ∞ here means \"no\n\t// overage tracked\" for display purposes.\n\treturn Number.POSITIVE_INFINITY;\n}\n\nexport function getStorageAllowanceBytes(plan: string): number {\n\tconst planDef = (PLANS as Record<string, Plan | undefined>)[plan];\n\tif (!planDef) return PLANS.hobby.storageLimitMb * 1024 * 1024;\n\tif (planDef.storageLimitMb < 0) return Number.POSITIVE_INFINITY;\n\treturn planDef.storageLimitMb * 1024 * 1024;\n}\n\n/** Hobby has a hard cap (no overage billing); paid tiers bill $2/GB over allowance. */\nexport function hasStorageOverage(plan: string): boolean {\n\treturn plan !== \"hobby\" && plan !== \"enterprise\";\n}\n\nexport function getBasePriceCents(plan: string): number {\n\tconst planDef = (PLANS as Record<string, Plan | undefined>)[plan];\n\treturn planDef?.monthlyPriceCents ?? 0;\n}\n\nexport function getPlanDisplayName(plan: string): string {\n\tconst planDef = (PLANS as Record<string, Plan | undefined>)[plan];\n\treturn planDef?.displayName ?? plan.charAt(0).toUpperCase() + plan.slice(1);\n}\n\n// Re-export bytes-per-GB constant for callers that compute display values.\nexport { BYTES_PER_GB };\n",
6
6
  "import { type Kysely, sql } from \"kysely\";\nimport {\n\tgetComputeAllowanceHours,\n\tgetStorageAllowanceBytes,\n} from \"../../pricing.ts\";\nimport type { Database } from \"../types.ts\";\n\n/**\n * Rollup queries that power the `/platform/usage` page.\n *\n * Compute-hours approximation: each active tenant contributes\n * cpus × hours-in-period-while-active\n * where \"active\" is approximated from `last_active_at`. This undercounts\n * tenants that went idle between cron ticks and overcounts nothing.\n *\n * Actual Stripe billing happens in `packages/worker/src/jobs/compute-metering.ts`\n * — these numbers are for display only. Follow-up work: write-through\n * compute ledger so this query reads truth instead of estimating.\n */\n\nconst IDLE_GRACE_MS = 2 * 60 * 60 * 1000; // 2h\nconst BYTES_PER_MB = 1024 * 1024;\n\n// ── Types ────────────────────────────────────────────────────────────\n\nexport interface SparklinePoint {\n\tday: string; // YYYY-MM-DD\n\tvalue: number;\n}\n\nexport interface ComputeUsage {\n\tusedHours: number;\n\tallowanceHours: number;\n\tpct: number;\n\tsparkline: SparklinePoint[];\n}\n\nexport interface StorageUsage {\n\tusedBytes: number;\n\tallowanceBytes: number;\n\tpct: number;\n\tsparkline: SparklinePoint[];\n}\n\nexport interface ProjectRow {\n\tid: string;\n\tslug: string;\n\tname: string;\n\tstatus: string;\n\tsubgraphCount: number;\n\tcompute: { hours: number; pct: number };\n\tstorage: { bytes: number; pct: number };\n}\n\n// ── Helpers ──────────────────────────────────────────────────────────\n\nfunction toDayKey(d: Date): string {\n\treturn d.toISOString().slice(0, 10);\n}\n\nfunction* lastNDays(n: number, endInclusive: Date): Generator<string> {\n\tconst end = new Date(endInclusive);\n\tfor (let i = n - 1; i >= 0; i--) {\n\t\tconst d = new Date(end);\n\t\td.setUTCDate(d.getUTCDate() - i);\n\t\tyield toDayKey(d);\n\t}\n}\n\nfunction computeActiveHours(\n\tperiodStart: Date,\n\tnow: Date,\n\ttenant: {\n\t\tcreated_at: Date;\n\t\tlast_active_at: Date;\n\t\tstatus: string;\n\t},\n): number {\n\tif (tenant.status !== \"active\") return 0;\n\tconst rangeStart = Math.max(\n\t\tperiodStart.getTime(),\n\t\ttenant.created_at.getTime(),\n\t);\n\tconst rangeEnd = Math.min(\n\t\tnow.getTime(),\n\t\ttenant.last_active_at.getTime() + IDLE_GRACE_MS,\n\t);\n\tif (rangeEnd <= rangeStart) return 0;\n\treturn (rangeEnd - rangeStart) / (1000 * 60 * 60);\n}\n\nfunction pct(used: number, allowance: number): number {\n\tif (!Number.isFinite(allowance) || allowance <= 0) return 0;\n\treturn Math.min((used / allowance) * 100, 100);\n}\n\n// ── Queries ──────────────────────────────────────────────────────────\n\nexport async function getComputeUsage(\n\tdb: Kysely<Database>,\n\taccountId: string,\n\tplan: string,\n\tperiodStart: Date,\n\tnow: Date = new Date(),\n): Promise<ComputeUsage> {\n\tconst tenants = await db\n\t\t.selectFrom(\"tenants\")\n\t\t.select([\"id\", \"cpus\", \"status\", \"created_at\", \"last_active_at\"])\n\t\t.where(\"account_id\", \"=\", accountId)\n\t\t.where(\"status\", \"!=\", \"deleted\")\n\t\t.execute();\n\n\tlet totalHours = 0;\n\tfor (const t of tenants) {\n\t\tconst hours = computeActiveHours(periodStart, now, {\n\t\t\tcreated_at: t.created_at,\n\t\t\tlast_active_at: t.last_active_at,\n\t\t\tstatus: String(t.status),\n\t\t});\n\t\ttotalHours += hours * Number(t.cpus);\n\t}\n\n\tconst allowance = getComputeAllowanceHours(plan);\n\n\t// 14-day sparkline: bucket the same formula per-day.\n\tconst sparkline: SparklinePoint[] = [];\n\tfor (const day of lastNDays(14, now)) {\n\t\tconst dayStart = new Date(`${day}T00:00:00.000Z`);\n\t\tconst dayEnd = new Date(`${day}T23:59:59.999Z`);\n\t\tlet dayHours = 0;\n\t\tfor (const t of tenants) {\n\t\t\tconst hours = computeActiveHours(dayStart, dayEnd, {\n\t\t\t\tcreated_at: t.created_at,\n\t\t\t\tlast_active_at: t.last_active_at,\n\t\t\t\tstatus: String(t.status),\n\t\t\t});\n\t\t\tdayHours += Math.min(hours, 24) * Number(t.cpus);\n\t\t}\n\t\tsparkline.push({ day, value: dayHours });\n\t}\n\n\treturn {\n\t\tusedHours: totalHours,\n\t\tallowanceHours: allowance,\n\t\tpct: pct(totalHours, allowance),\n\t\tsparkline,\n\t};\n}\n\nexport async function getStorageUsage(\n\tdb: Kysely<Database>,\n\taccountId: string,\n\tplan: string,\n\tnow: Date = new Date(),\n): Promise<StorageUsage> {\n\t// Current usage: sum of tenants.storage_used_mb for this account.\n\tconst current = await db\n\t\t.selectFrom(\"tenants\")\n\t\t.select(sql<string>`COALESCE(SUM(storage_used_mb), 0)`.as(\"mb\"))\n\t\t.where(\"account_id\", \"=\", accountId)\n\t\t.where(\"status\", \"!=\", \"deleted\")\n\t\t.executeTakeFirst();\n\n\tconst usedBytes = Number(current?.mb ?? 0) * BYTES_PER_MB;\n\tconst allowance = getStorageAllowanceBytes(plan);\n\n\t// 14-day sparkline: per-month snapshots only — fall back to a flat\n\t// line at the current value. When per-day storage history lands, swap\n\t// this for a real bucket query.\n\tconst sparkline: SparklinePoint[] = [];\n\tfor (const day of lastNDays(14, now)) {\n\t\tsparkline.push({ day, value: Number(current?.mb ?? 0) });\n\t}\n\n\treturn {\n\t\tusedBytes,\n\t\tallowanceBytes: allowance,\n\t\tpct: pct(usedBytes, allowance),\n\t\tsparkline,\n\t};\n}\n\nexport async function getProjectBreakdown(\n\tdb: Kysely<Database>,\n\taccountId: string,\n\tplan: string,\n\tperiodStart: Date,\n\tnow: Date = new Date(),\n): Promise<ProjectRow[]> {\n\tconst tenants = await db\n\t\t.selectFrom(\"tenants\")\n\t\t.select([\n\t\t\t\"id\",\n\t\t\t\"slug\",\n\t\t\t\"status\",\n\t\t\t\"cpus\",\n\t\t\t\"storage_used_mb\",\n\t\t\t\"created_at\",\n\t\t\t\"last_active_at\",\n\t\t])\n\t\t.where(\"account_id\", \"=\", accountId)\n\t\t.where(\"status\", \"!=\", \"deleted\")\n\t\t.orderBy(\"created_at\", \"desc\")\n\t\t.execute();\n\n\tif (tenants.length === 0) return [];\n\n\tconst computeAllowance = getComputeAllowanceHours(plan);\n\tconst storageAllowance = getStorageAllowanceBytes(plan);\n\n\treturn tenants.map((t) => {\n\t\tconst hours =\n\t\t\tcomputeActiveHours(periodStart, now, {\n\t\t\t\tcreated_at: t.created_at,\n\t\t\t\tlast_active_at: t.last_active_at,\n\t\t\t\tstatus: String(t.status),\n\t\t\t}) * Number(t.cpus);\n\t\tconst bytes = Number(t.storage_used_mb ?? 0) * BYTES_PER_MB;\n\n\t\treturn {\n\t\t\tid: t.id,\n\t\t\tslug: t.slug,\n\t\t\tname: t.slug,\n\t\t\tstatus: String(t.status),\n\t\t\t// subgraphCount not tracked at account-DB level (subgraphs live on\n\t\t\t// per-tenant DBs). Left at 0; later pass can ping each tenant API.\n\t\t\tsubgraphCount: 0,\n\t\t\tcompute: { hours, pct: pct(hours, computeAllowance) },\n\t\t\tstorage: { bytes, pct: pct(bytes, storageAllowance) },\n\t\t};\n\t});\n}\n"
7
7
  ],
8
- "mappings": "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",
9
- "debugId": "7A0FEDB720F0E4A864756E2164756E21",
8
+ "mappings": ";;;;;;;;;;;;;;;;;AAaA,IAAM,eAAuB,QAAQ;AA8CrC,SAAS,KAAK,CAAC,SAAiB,WAAuC;AAAA,EACtE,OAAO;AAAA,IACN,UAAU;AAAA,MACT,UAAU,KAAK,MAAM,UAAU,GAAG;AAAA,MAClC,MAAM,OAAO,YAAY,GAAG;AAAA,IAC7B;AAAA,IACA,WAAW;AAAA,MACV,UAAU,KAAK,MAAM,UAAU,GAAG;AAAA,MAClC,MAAM,OAAO,YAAY,GAAG;AAAA,IAC7B;AAAA,IACA,KAAK;AAAA,MACJ,UAAU,KAAK,MAAM,UAAU,GAAG;AAAA,MAClC,MAAM,OAAO,YAAY,GAAG;AAAA,IAC7B;AAAA,EACD;AAAA;AAGD,SAAS,UAAU,CAAC,SAAiB,WAAuC;AAAA,EAC3E,OAAO;AAAA,IACN,UAAU;AAAA,MACT,UAAU,KAAK,MAAM,UAAU,GAAG;AAAA,MAClC,MAAM,OAAO,YAAY,GAAG;AAAA,IAC7B;AAAA,IACA,WAAW;AAAA,MACV,UAAU,KAAK,MAAM,UAAU,IAAI;AAAA,MACnC,MAAM,OAAO,YAAY,IAAI;AAAA,IAC9B;AAAA,IACA,KAAK;AAAA,MACJ,UAAU,KAAK,MAAM,UAAU,IAAI;AAAA,MACnC,MAAM,OAAO,YAAY,IAAI;AAAA,IAC9B;AAAA,EACD;AAAA;AAGD,SAAS,MAAM,CAAC,GAAmB;AAAA,EAClC,OAAO,KAAK,MAAM,IAAI,GAAG,IAAI;AAAA;AAOvB,SAAS,cAAc,CAC7B,eACA,WACqB;AAAA,EACrB,OAAO,gBAAgB,OACpB,WAAW,eAAe,SAAS,IACnC,MAAM,eAAe,SAAS;AAAA;AAK3B,IAAM,QAA8B;AAAA,EAC1C,OAAO;AAAA,IACN,IAAI;AAAA,IACJ,aAAa;AAAA,IACb,mBAAmB;AAAA,IACnB,WAAW;AAAA,IACX,eAAe;AAAA,IACf,gBAAgB;AAAA,IAChB,YAAY,WAAW,KAAK,GAAG;AAAA,IAC/B,SAAS;AAAA,IACT,UAAU;AAAA,MACT;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACD;AAAA,IACA,iBAAiB;AAAA,EAClB;AAAA,EACA,QAAQ;AAAA,IACP,IAAI;AAAA,IACJ,aAAa;AAAA,IACb,mBAAmB;AAAA,IACnB,WAAW;AAAA,IACX,eAAe;AAAA,IACf,gBAAgB;AAAA,IAChB,YAAY,MAAM,MAAO,CAAC;AAAA,IAC1B,SAAS;AAAA,IACT,UAAU;AAAA,MACT;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACD;AAAA,IACA,iBAAiB;AAAA,EAClB;AAAA,EACA,OAAO;AAAA,IACN,IAAI;AAAA,IACJ,aAAa;AAAA,IACb,mBAAmB;AAAA,IACnB,WAAW;AAAA,IACX,eAAe;AAAA,IACf,gBAAgB;AAAA,IAChB,YAAY,MAAM,MAAO,CAAC;AAAA,IAC1B,SAAS;AAAA,IACT,UAAU;AAAA,MACT;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACD;AAAA,IACA,iBAAiB;AAAA,EAClB;AAAA,EACA,YAAY;AAAA,IACX,IAAI;AAAA,IACJ,aAAa;AAAA,IACb,mBAAmB;AAAA,IACnB,WAAW;AAAA,IACX,eAAe;AAAA,IACf,gBAAgB;AAAA,IAChB,YAAY,MAAM,OAAQ,CAAC;AAAA,IAC3B,SAAS;AAAA,IACT,UAAU;AAAA,MACT;AAAA,MACA;AAAA,MACA;AAAA,IACD;AAAA,IACA,iBAAiB;AAAA,EAClB;AACD;AAEO,IAAM,WAA8B;AAAA,EAC1C;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACD;AAEO,SAAS,OAAO,CAAC,IAAkB;AAAA,EACzC,MAAM,OAAQ,MAA2C;AAAA,EACzD,IAAI,CAAC;AAAA,IAAM,MAAM,IAAI,MAAM,iBAAiB,IAAI;AAAA,EAChD,OAAO;AAAA;AAGD,SAAS,aAAa,CAAC,IAA0B;AAAA,EACvD,OAAO,MAAM;AAAA;AAYP,SAAS,wBAAwB,CAAC,OAAuB;AAAA,EAI/D,OAAO,OAAO;AAAA;AAGR,SAAS,wBAAwB,CAAC,MAAsB;AAAA,EAC9D,MAAM,UAAW,MAA2C;AAAA,EAC5D,IAAI,CAAC;AAAA,IAAS,OAAO,MAAM,MAAM,iBAAiB,OAAO;AAAA,EACzD,IAAI,QAAQ,iBAAiB;AAAA,IAAG,OAAO,OAAO;AAAA,EAC9C,OAAO,QAAQ,iBAAiB,OAAO;AAAA;AAIjC,SAAS,iBAAiB,CAAC,MAAuB;AAAA,EACxD,OAAO,SAAS,WAAW,SAAS;AAAA;AAG9B,SAAS,iBAAiB,CAAC,MAAsB;AAAA,EACvD,MAAM,UAAW,MAA2C;AAAA,EAC5D,OAAO,SAAS,qBAAqB;AAAA;AAG/B,SAAS,kBAAkB,CAAC,MAAsB;AAAA,EACxD,MAAM,UAAW,MAA2C;AAAA,EAC5D,OAAO,SAAS,eAAe,KAAK,OAAO,CAAC,EAAE,YAAY,IAAI,KAAK,MAAM,CAAC;AAAA;;AC7O3E;AAoBA,IAAM,gBAAgB,IAAI,KAAK,KAAK;AACpC,IAAM,eAAe,OAAO;AAmC5B,SAAS,QAAQ,CAAC,GAAiB;AAAA,EAClC,OAAO,EAAE,YAAY,EAAE,MAAM,GAAG,EAAE;AAAA;AAGnC,UAAU,SAAS,CAAC,GAAW,cAAuC;AAAA,EACrE,MAAM,MAAM,IAAI,KAAK,YAAY;AAAA,EACjC,SAAS,IAAI,IAAI,EAAG,KAAK,GAAG,KAAK;AAAA,IAChC,MAAM,IAAI,IAAI,KAAK,GAAG;AAAA,IACtB,EAAE,WAAW,EAAE,WAAW,IAAI,CAAC;AAAA,IAC/B,MAAM,SAAS,CAAC;AAAA,EACjB;AAAA;AAGD,SAAS,kBAAkB,CAC1B,aACA,KACA,QAKS;AAAA,EACT,IAAI,OAAO,WAAW;AAAA,IAAU,OAAO;AAAA,EACvC,MAAM,aAAa,KAAK,IACvB,YAAY,QAAQ,GACpB,OAAO,WAAW,QAAQ,CAC3B;AAAA,EACA,MAAM,WAAW,KAAK,IACrB,IAAI,QAAQ,GACZ,OAAO,eAAe,QAAQ,IAAI,aACnC;AAAA,EACA,IAAI,YAAY;AAAA,IAAY,OAAO;AAAA,EACnC,QAAQ,WAAW,eAAe,OAAO,KAAK;AAAA;AAG/C,SAAS,GAAG,CAAC,MAAc,WAA2B;AAAA,EACrD,IAAI,CAAC,OAAO,SAAS,SAAS,KAAK,aAAa;AAAA,IAAG,OAAO;AAAA,EAC1D,OAAO,KAAK,IAAK,OAAO,YAAa,KAAK,GAAG;AAAA;AAK9C,eAAsB,eAAe,CACpC,IACA,WACA,MACA,aACA,MAAY,IAAI,MACQ;AAAA,EACxB,MAAM,UAAU,MAAM,GACpB,WAAW,SAAS,EACpB,OAAO,CAAC,MAAM,QAAQ,UAAU,cAAc,gBAAgB,CAAC,EAC/D,MAAM,cAAc,KAAK,SAAS,EAClC,MAAM,UAAU,MAAM,SAAS,EAC/B,QAAQ;AAAA,EAEV,IAAI,aAAa;AAAA,EACjB,WAAW,KAAK,SAAS;AAAA,IACxB,MAAM,QAAQ,mBAAmB,aAAa,KAAK;AAAA,MAClD,YAAY,EAAE;AAAA,MACd,gBAAgB,EAAE;AAAA,MAClB,QAAQ,OAAO,EAAE,MAAM;AAAA,IACxB,CAAC;AAAA,IACD,cAAc,QAAQ,OAAO,EAAE,IAAI;AAAA,EACpC;AAAA,EAEA,MAAM,YAAY,yBAAyB,IAAI;AAAA,EAG/C,MAAM,YAA8B,CAAC;AAAA,EACrC,WAAW,OAAO,UAAU,IAAI,GAAG,GAAG;AAAA,IACrC,MAAM,WAAW,IAAI,KAAK,GAAG,mBAAmB;AAAA,IAChD,MAAM,SAAS,IAAI,KAAK,GAAG,mBAAmB;AAAA,IAC9C,IAAI,WAAW;AAAA,IACf,WAAW,KAAK,SAAS;AAAA,MACxB,MAAM,QAAQ,mBAAmB,UAAU,QAAQ;AAAA,QAClD,YAAY,EAAE;AAAA,QACd,gBAAgB,EAAE;AAAA,QAClB,QAAQ,OAAO,EAAE,MAAM;AAAA,MACxB,CAAC;AAAA,MACD,YAAY,KAAK,IAAI,OAAO,EAAE,IAAI,OAAO,EAAE,IAAI;AAAA,IAChD;AAAA,IACA,UAAU,KAAK,EAAE,KAAK,OAAO,SAAS,CAAC;AAAA,EACxC;AAAA,EAEA,OAAO;AAAA,IACN,WAAW;AAAA,IACX,gBAAgB;AAAA,IAChB,KAAK,IAAI,YAAY,SAAS;AAAA,IAC9B;AAAA,EACD;AAAA;AAGD,eAAsB,eAAe,CACpC,IACA,WACA,MACA,MAAY,IAAI,MACQ;AAAA,EAExB,MAAM,UAAU,MAAM,GACpB,WAAW,SAAS,EACpB,OAAO,uCAA+C,GAAG,IAAI,CAAC,EAC9D,MAAM,cAAc,KAAK,SAAS,EAClC,MAAM,UAAU,MAAM,SAAS,EAC/B,iBAAiB;AAAA,EAEnB,MAAM,YAAY,OAAO,SAAS,MAAM,CAAC,IAAI;AAAA,EAC7C,MAAM,YAAY,yBAAyB,IAAI;AAAA,EAK/C,MAAM,YAA8B,CAAC;AAAA,EACrC,WAAW,OAAO,UAAU,IAAI,GAAG,GAAG;AAAA,IACrC,UAAU,KAAK,EAAE,KAAK,OAAO,OAAO,SAAS,MAAM,CAAC,EAAE,CAAC;AAAA,EACxD;AAAA,EAEA,OAAO;AAAA,IACN;AAAA,IACA,gBAAgB;AAAA,IAChB,KAAK,IAAI,WAAW,SAAS;AAAA,IAC7B;AAAA,EACD;AAAA;AAGD,eAAsB,mBAAmB,CACxC,IACA,WACA,MACA,aACA,MAAY,IAAI,MACQ;AAAA,EACxB,MAAM,UAAU,MAAM,GACpB,WAAW,SAAS,EACpB,OAAO;AAAA,IACP;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACD,CAAC,EACA,MAAM,cAAc,KAAK,SAAS,EAClC,MAAM,UAAU,MAAM,SAAS,EAC/B,QAAQ,cAAc,MAAM,EAC5B,QAAQ;AAAA,EAEV,IAAI,QAAQ,WAAW;AAAA,IAAG,OAAO,CAAC;AAAA,EAElC,MAAM,mBAAmB,yBAAyB,IAAI;AAAA,EACtD,MAAM,mBAAmB,yBAAyB,IAAI;AAAA,EAEtD,OAAO,QAAQ,IAAI,CAAC,MAAM;AAAA,IACzB,MAAM,QACL,mBAAmB,aAAa,KAAK;AAAA,MACpC,YAAY,EAAE;AAAA,MACd,gBAAgB,EAAE;AAAA,MAClB,QAAQ,OAAO,EAAE,MAAM;AAAA,IACxB,CAAC,IAAI,OAAO,EAAE,IAAI;AAAA,IACnB,MAAM,QAAQ,OAAO,EAAE,mBAAmB,CAAC,IAAI;AAAA,IAE/C,OAAO;AAAA,MACN,IAAI,EAAE;AAAA,MACN,MAAM,EAAE;AAAA,MACR,MAAM,EAAE;AAAA,MACR,QAAQ,OAAO,EAAE,MAAM;AAAA,MAGvB,eAAe;AAAA,MACf,SAAS,EAAE,OAAO,KAAK,IAAI,OAAO,gBAAgB,EAAE;AAAA,MACpD,SAAS,EAAE,OAAO,KAAK,IAAI,OAAO,gBAAgB,EAAE;AAAA,IACrD;AAAA,GACA;AAAA;",
9
+ "debugId": "7CE87B0F0526DA2864756E2164756E21",
10
10
  "names": []
11
11
  }
@@ -269,6 +269,11 @@ interface ChatMessagesTable {
269
269
  metadata: unknown | null;
270
270
  created_at: Generated<Date>;
271
271
  }
272
+ interface ProcessedStripeEventsTable {
273
+ event_id: string;
274
+ event_type: string;
275
+ processed_at: Generated<Date>;
276
+ }
272
277
  interface Database {
273
278
  blocks: BlocksTable;
274
279
  transactions: TransactionsTable;
@@ -295,6 +300,7 @@ interface Database {
295
300
  team_invitations: TeamInvitationsTable;
296
301
  chat_sessions: ChatSessionsTable;
297
302
  chat_messages: ChatMessagesTable;
303
+ processed_stripe_events: ProcessedStripeEventsTable;
298
304
  tenants: TenantsTable;
299
305
  tenant_usage_monthly: TenantUsageMonthlyTable;
300
306
  tenant_compute_addons: TenantComputeAddonsTable;
@@ -268,6 +268,11 @@ interface ChatMessagesTable {
268
268
  metadata: unknown | null;
269
269
  created_at: Generated<Date>;
270
270
  }
271
+ interface ProcessedStripeEventsTable {
272
+ event_id: string;
273
+ event_type: string;
274
+ processed_at: Generated<Date>;
275
+ }
271
276
  interface Database {
272
277
  blocks: BlocksTable;
273
278
  transactions: TransactionsTable;
@@ -294,6 +299,7 @@ interface Database {
294
299
  team_invitations: TeamInvitationsTable;
295
300
  chat_sessions: ChatSessionsTable;
296
301
  chat_messages: ChatMessagesTable;
302
+ processed_stripe_events: ProcessedStripeEventsTable;
297
303
  tenants: TenantsTable;
298
304
  tenant_usage_monthly: TenantUsageMonthlyTable;
299
305
  tenant_compute_addons: TenantComputeAddonsTable;
@@ -268,6 +268,11 @@ interface ChatMessagesTable {
268
268
  metadata: unknown | null;
269
269
  created_at: Generated<Date>;
270
270
  }
271
+ interface ProcessedStripeEventsTable {
272
+ event_id: string;
273
+ event_type: string;
274
+ processed_at: Generated<Date>;
275
+ }
271
276
  interface Database {
272
277
  blocks: BlocksTable;
273
278
  transactions: TransactionsTable;
@@ -294,6 +299,7 @@ interface Database {
294
299
  team_invitations: TeamInvitationsTable;
295
300
  chat_sessions: ChatSessionsTable;
296
301
  chat_messages: ChatMessagesTable;
302
+ processed_stripe_events: ProcessedStripeEventsTable;
297
303
  tenants: TenantsTable;
298
304
  tenant_usage_monthly: TenantUsageMonthlyTable;
299
305
  tenant_compute_addons: TenantComputeAddonsTable;
@@ -268,6 +268,11 @@ interface ChatMessagesTable {
268
268
  metadata: unknown | null;
269
269
  created_at: Generated<Date>;
270
270
  }
271
+ interface ProcessedStripeEventsTable {
272
+ event_id: string;
273
+ event_type: string;
274
+ processed_at: Generated<Date>;
275
+ }
271
276
  interface Database {
272
277
  blocks: BlocksTable;
273
278
  transactions: TransactionsTable;
@@ -294,6 +299,7 @@ interface Database {
294
299
  team_invitations: TeamInvitationsTable;
295
300
  chat_sessions: ChatSessionsTable;
296
301
  chat_messages: ChatMessagesTable;
302
+ processed_stripe_events: ProcessedStripeEventsTable;
297
303
  tenants: TenantsTable;
298
304
  tenant_usage_monthly: TenantUsageMonthlyTable;
299
305
  tenant_compute_addons: TenantComputeAddonsTable;
@@ -268,6 +268,11 @@ interface ChatMessagesTable {
268
268
  metadata: unknown | null;
269
269
  created_at: Generated<Date>;
270
270
  }
271
+ interface ProcessedStripeEventsTable {
272
+ event_id: string;
273
+ event_type: string;
274
+ processed_at: Generated<Date>;
275
+ }
271
276
  interface Database {
272
277
  blocks: BlocksTable;
273
278
  transactions: TransactionsTable;
@@ -294,6 +299,7 @@ interface Database {
294
299
  team_invitations: TeamInvitationsTable;
295
300
  chat_sessions: ChatSessionsTable;
296
301
  chat_messages: ChatMessagesTable;
302
+ processed_stripe_events: ProcessedStripeEventsTable;
297
303
  tenants: TenantsTable;
298
304
  tenant_usage_monthly: TenantUsageMonthlyTable;
299
305
  tenant_compute_addons: TenantComputeAddonsTable;
@@ -268,6 +268,11 @@ interface ChatMessagesTable {
268
268
  metadata: unknown | null;
269
269
  created_at: Generated<Date>;
270
270
  }
271
+ interface ProcessedStripeEventsTable {
272
+ event_id: string;
273
+ event_type: string;
274
+ processed_at: Generated<Date>;
275
+ }
271
276
  interface Database {
272
277
  blocks: BlocksTable;
273
278
  transactions: TransactionsTable;
@@ -294,6 +299,7 @@ interface Database {
294
299
  team_invitations: TeamInvitationsTable;
295
300
  chat_sessions: ChatSessionsTable;
296
301
  chat_messages: ChatMessagesTable;
302
+ processed_stripe_events: ProcessedStripeEventsTable;
297
303
  tenants: TenantsTable;
298
304
  tenant_usage_monthly: TenantUsageMonthlyTable;
299
305
  tenant_compute_addons: TenantComputeAddonsTable;
@@ -268,6 +268,11 @@ interface ChatMessagesTable {
268
268
  metadata: unknown | null;
269
269
  created_at: Generated<Date>;
270
270
  }
271
+ interface ProcessedStripeEventsTable {
272
+ event_id: string;
273
+ event_type: string;
274
+ processed_at: Generated<Date>;
275
+ }
271
276
  interface Database {
272
277
  blocks: BlocksTable;
273
278
  transactions: TransactionsTable;
@@ -294,6 +299,7 @@ interface Database {
294
299
  team_invitations: TeamInvitationsTable;
295
300
  chat_sessions: ChatSessionsTable;
296
301
  chat_messages: ChatMessagesTable;
302
+ processed_stripe_events: ProcessedStripeEventsTable;
297
303
  tenants: TenantsTable;
298
304
  tenant_usage_monthly: TenantUsageMonthlyTable;
299
305
  tenant_compute_addons: TenantComputeAddonsTable;
@@ -98,6 +98,12 @@ async function deleteSubgraph(db, name, accountId) {
98
98
  if (!subgraph)
99
99
  return null;
100
100
  const schemaName = subgraph.schema_name ?? pgSchemaName(name);
101
+ await db.updateTable("subscriptions").set({
102
+ status: "paused",
103
+ last_error: `Subgraph "${name}" deleted; subscription auto-paused.`,
104
+ updated_at: new Date
105
+ }).where("subgraph_name", "=", name).execute();
106
+ await db.deleteFrom("subscription_outbox").where("status", "=", "pending").where("subscription_id", "in", (qb) => qb.selectFrom("subscriptions").select("id").where("subgraph_name", "=", name)).execute();
101
107
  await sql2`DROP SCHEMA IF EXISTS ${sql2.raw(`"${schemaName}"`)} CASCADE`.execute(db);
102
108
  await db.deleteFrom("subgraphs").where("id", "=", subgraph.id).execute();
103
109
  return subgraph;
@@ -113,5 +119,5 @@ export {
113
119
  deleteSubgraph
114
120
  };
115
121
 
116
- //# debugId=50F07B32C2BC0A0B64756E2164756E21
122
+ //# debugId=756EF511BC34387464756E2164756E21
117
123
  //# sourceMappingURL=subgraphs.js.map
@@ -3,9 +3,9 @@
3
3
  "sources": ["../src/db/jsonb.ts", "../src/db/queries/subgraphs.ts"],
4
4
  "sourcesContent": [
5
5
  "import { type RawBuilder, sql } from \"kysely\";\n\n/**\n * Safely encode a JS value as a JSONB literal for Kysely inserts/updates.\n * Kysely + postgres.js double-encodes JSON when using parameterized queries\n * with ::jsonb casts. This uses sql.raw to inline a properly escaped literal.\n *\n * Generic parameter lets callers set the RawBuilder's output type so they\n * don't need to cast at the insert site. Default is `unknown` — widen at\n * the call site when the column type is narrower, e.g. `jsonb<MyShape>(...)`.\n */\nexport function jsonb<T = unknown>(value: T): RawBuilder<T> {\n\tconst escaped = JSON.stringify(value, (_k, v) =>\n\t\ttypeof v === \"bigint\" ? v.toString() : v,\n\t).replace(/'/g, \"''\");\n\treturn sql`${sql.raw(`'${escaped}'::jsonb`)}`;\n}\n\n/**\n * Safely parse a JSONB value from the database.\n * Handles double-encoded strings where postgres.js returns a JSON string\n * instead of a parsed object.\n */\nexport function parseJsonb<T = unknown>(value: unknown): T {\n\tif (typeof value === \"string\") {\n\t\ttry {\n\t\t\treturn JSON.parse(value) as T;\n\t\t} catch {\n\t\t\treturn value as T;\n\t\t}\n\t}\n\treturn (value ?? {}) as T;\n}\n",
6
- "import { type Kysely, sql } from \"kysely\";\nimport { jsonb } from \"../jsonb.ts\";\nimport type { Database, Subgraph } from \"../types.ts\";\n\n/**\n * Convert a subgraph name to its PostgreSQL schema name.\n * Every tenant DB has its own isolated schema namespace, so there's no\n * account-prefix disambiguation needed — every subgraph within a tenant\n * just maps to `subgraph_{safeName}`.\n */\nexport function pgSchemaName(subgraphName: string): string {\n\tconst safeName = subgraphName.replace(/-/g, \"_\");\n\treturn `subgraph_${safeName}`;\n}\n\nexport async function registerSubgraph(\n\tdb: Kysely<Database>,\n\tdata: {\n\t\tname: string;\n\t\tversion: string;\n\t\tdefinition: Record<string, unknown>;\n\t\tschemaHash: string;\n\t\thandlerPath: string;\n\t\tapiKeyId?: string;\n\t\taccountId?: string;\n\t\tschemaName?: string;\n\t\tstartBlock?: number;\n\t\thandlerCode?: string;\n\t\tsourceCode?: string;\n\t},\n): Promise<Subgraph> {\n\treturn await db\n\t\t.insertInto(\"subgraphs\")\n\t\t.values({\n\t\t\tname: data.name,\n\t\t\tversion: data.version,\n\t\t\tdefinition: jsonb<Record<string, unknown>>(data.definition),\n\t\t\tschema_hash: data.schemaHash,\n\t\t\thandler_path: data.handlerPath,\n\t\t\taccount_id: data.accountId ?? \"\",\n\t\t\thandler_code: data.handlerCode ?? null,\n\t\t\tsource_code: data.sourceCode ?? null,\n\t\t\tschema_name: data.schemaName ?? null,\n\t\t\tstart_block: data.startBlock ?? 0,\n\t\t})\n\t\t.onConflict((oc) =>\n\t\t\toc.columns([\"name\", \"account_id\"]).doUpdateSet({\n\t\t\t\tversion: data.version,\n\t\t\t\tdefinition: jsonb<Record<string, unknown>>(data.definition),\n\t\t\t\tschema_hash: data.schemaHash,\n\t\t\t\thandler_path: data.handlerPath,\n\t\t\t\thandler_code: data.handlerCode ?? null,\n\t\t\t\tsource_code: data.sourceCode ?? null,\n\t\t\t\tschema_name: data.schemaName ?? null,\n\t\t\t\tstart_block: data.startBlock ?? 0,\n\t\t\t\tupdated_at: new Date(),\n\t\t\t}),\n\t\t)\n\t\t.returningAll()\n\t\t.executeTakeFirstOrThrow();\n}\n\nexport async function getSubgraph(\n\tdb: Kysely<Database>,\n\tname: string,\n\taccountId?: string,\n): Promise<Subgraph | null> {\n\tlet query = db.selectFrom(\"subgraphs\").selectAll().where(\"name\", \"=\", name);\n\n\tif (accountId !== undefined) {\n\t\tquery = query.where(\"account_id\", \"=\", accountId);\n\t}\n\n\treturn (await query.executeTakeFirst()) ?? null;\n}\n\nexport async function listSubgraphs(\n\tdb: Kysely<Database>,\n\taccountId?: string,\n): Promise<Subgraph[]> {\n\tlet query = db.selectFrom(\"subgraphs\").selectAll();\n\tif (accountId !== undefined) {\n\t\tquery = query.where(\"account_id\", \"=\", accountId);\n\t}\n\treturn query.execute();\n}\n\nexport async function updateSubgraphStatus(\n\tdb: Kysely<Database>,\n\tname: string,\n\tstatus: string,\n\tlastProcessedBlock?: number,\n): Promise<void> {\n\tawait db\n\t\t.updateTable(\"subgraphs\")\n\t\t.set({\n\t\t\tstatus,\n\t\t\t...(lastProcessedBlock !== undefined\n\t\t\t\t? { last_processed_block: lastProcessedBlock }\n\t\t\t\t: {}),\n\t\t\tupdated_at: new Date(),\n\t\t})\n\t\t.where(\"name\", \"=\", name)\n\t\t.execute();\n}\n\nexport async function recordSubgraphProcessed(\n\tdb: Kysely<Database>,\n\tname: string,\n\tprocessed: number,\n\terrors: number,\n\tlastError?: string,\n): Promise<void> {\n\tawait db\n\t\t.updateTable(\"subgraphs\")\n\t\t.set({\n\t\t\ttotal_processed: sql`total_processed + ${processed}`,\n\t\t\ttotal_errors: sql`total_errors + ${errors}`,\n\t\t\t...(lastError\n\t\t\t\t? { last_error: lastError, last_error_at: new Date() }\n\t\t\t\t: {}),\n\t\t\tupdated_at: new Date(),\n\t\t})\n\t\t.where(\"name\", \"=\", name)\n\t\t.execute();\n}\n\nexport async function updateSubgraphHandlerPath(\n\tdb: Kysely<Database>,\n\tname: string,\n\thandlerPath: string,\n): Promise<void> {\n\tawait db\n\t\t.updateTable(\"subgraphs\")\n\t\t.set({ handler_path: handlerPath, updated_at: new Date() })\n\t\t.where(\"name\", \"=\", name)\n\t\t.execute();\n}\n\nexport async function deleteSubgraph(\n\tdb: Kysely<Database>,\n\tname: string,\n\taccountId?: string,\n): Promise<Subgraph | null> {\n\tconst subgraph = await getSubgraph(db, name, accountId);\n\tif (!subgraph) return null;\n\n\t// Use stored schema_name if available, otherwise compute\n\tconst schemaName = subgraph.schema_name ?? pgSchemaName(name);\n\n\t// Drop the subgraph's schema (CASCADE drops all tables within)\n\tawait sql`DROP SCHEMA IF EXISTS ${sql.raw(`\"${schemaName}\"`)} CASCADE`.execute(\n\t\tdb,\n\t);\n\n\t// Remove from registry\n\tawait db.deleteFrom(\"subgraphs\").where(\"id\", \"=\", subgraph.id).execute();\n\n\treturn subgraph;\n}\n"
6
+ "import { type Kysely, sql } from \"kysely\";\nimport { jsonb } from \"../jsonb.ts\";\nimport type { Database, Subgraph } from \"../types.ts\";\n\n/**\n * Convert a subgraph name to its PostgreSQL schema name.\n * Every tenant DB has its own isolated schema namespace, so there's no\n * account-prefix disambiguation needed — every subgraph within a tenant\n * just maps to `subgraph_{safeName}`.\n */\nexport function pgSchemaName(subgraphName: string): string {\n\tconst safeName = subgraphName.replace(/-/g, \"_\");\n\treturn `subgraph_${safeName}`;\n}\n\nexport async function registerSubgraph(\n\tdb: Kysely<Database>,\n\tdata: {\n\t\tname: string;\n\t\tversion: string;\n\t\tdefinition: Record<string, unknown>;\n\t\tschemaHash: string;\n\t\thandlerPath: string;\n\t\tapiKeyId?: string;\n\t\taccountId?: string;\n\t\tschemaName?: string;\n\t\tstartBlock?: number;\n\t\thandlerCode?: string;\n\t\tsourceCode?: string;\n\t},\n): Promise<Subgraph> {\n\treturn await db\n\t\t.insertInto(\"subgraphs\")\n\t\t.values({\n\t\t\tname: data.name,\n\t\t\tversion: data.version,\n\t\t\tdefinition: jsonb<Record<string, unknown>>(data.definition),\n\t\t\tschema_hash: data.schemaHash,\n\t\t\thandler_path: data.handlerPath,\n\t\t\taccount_id: data.accountId ?? \"\",\n\t\t\thandler_code: data.handlerCode ?? null,\n\t\t\tsource_code: data.sourceCode ?? null,\n\t\t\tschema_name: data.schemaName ?? null,\n\t\t\tstart_block: data.startBlock ?? 0,\n\t\t})\n\t\t.onConflict((oc) =>\n\t\t\toc.columns([\"name\", \"account_id\"]).doUpdateSet({\n\t\t\t\tversion: data.version,\n\t\t\t\tdefinition: jsonb<Record<string, unknown>>(data.definition),\n\t\t\t\tschema_hash: data.schemaHash,\n\t\t\t\thandler_path: data.handlerPath,\n\t\t\t\thandler_code: data.handlerCode ?? null,\n\t\t\t\tsource_code: data.sourceCode ?? null,\n\t\t\t\tschema_name: data.schemaName ?? null,\n\t\t\t\tstart_block: data.startBlock ?? 0,\n\t\t\t\tupdated_at: new Date(),\n\t\t\t}),\n\t\t)\n\t\t.returningAll()\n\t\t.executeTakeFirstOrThrow();\n}\n\nexport async function getSubgraph(\n\tdb: Kysely<Database>,\n\tname: string,\n\taccountId?: string,\n): Promise<Subgraph | null> {\n\tlet query = db.selectFrom(\"subgraphs\").selectAll().where(\"name\", \"=\", name);\n\n\tif (accountId !== undefined) {\n\t\tquery = query.where(\"account_id\", \"=\", accountId);\n\t}\n\n\treturn (await query.executeTakeFirst()) ?? null;\n}\n\nexport async function listSubgraphs(\n\tdb: Kysely<Database>,\n\taccountId?: string,\n): Promise<Subgraph[]> {\n\tlet query = db.selectFrom(\"subgraphs\").selectAll();\n\tif (accountId !== undefined) {\n\t\tquery = query.where(\"account_id\", \"=\", accountId);\n\t}\n\treturn query.execute();\n}\n\nexport async function updateSubgraphStatus(\n\tdb: Kysely<Database>,\n\tname: string,\n\tstatus: string,\n\tlastProcessedBlock?: number,\n): Promise<void> {\n\tawait db\n\t\t.updateTable(\"subgraphs\")\n\t\t.set({\n\t\t\tstatus,\n\t\t\t...(lastProcessedBlock !== undefined\n\t\t\t\t? { last_processed_block: lastProcessedBlock }\n\t\t\t\t: {}),\n\t\t\tupdated_at: new Date(),\n\t\t})\n\t\t.where(\"name\", \"=\", name)\n\t\t.execute();\n}\n\nexport async function recordSubgraphProcessed(\n\tdb: Kysely<Database>,\n\tname: string,\n\tprocessed: number,\n\terrors: number,\n\tlastError?: string,\n): Promise<void> {\n\tawait db\n\t\t.updateTable(\"subgraphs\")\n\t\t.set({\n\t\t\ttotal_processed: sql`total_processed + ${processed}`,\n\t\t\ttotal_errors: sql`total_errors + ${errors}`,\n\t\t\t...(lastError\n\t\t\t\t? { last_error: lastError, last_error_at: new Date() }\n\t\t\t\t: {}),\n\t\t\tupdated_at: new Date(),\n\t\t})\n\t\t.where(\"name\", \"=\", name)\n\t\t.execute();\n}\n\nexport async function updateSubgraphHandlerPath(\n\tdb: Kysely<Database>,\n\tname: string,\n\thandlerPath: string,\n): Promise<void> {\n\tawait db\n\t\t.updateTable(\"subgraphs\")\n\t\t.set({ handler_path: handlerPath, updated_at: new Date() })\n\t\t.where(\"name\", \"=\", name)\n\t\t.execute();\n}\n\nexport async function deleteSubgraph(\n\tdb: Kysely<Database>,\n\tname: string,\n\taccountId?: string,\n): Promise<Subgraph | null> {\n\tconst subgraph = await getSubgraph(db, name, accountId);\n\tif (!subgraph) return null;\n\n\t// Use stored schema_name if available, otherwise compute\n\tconst schemaName = subgraph.schema_name ?? pgSchemaName(name);\n\n\t// Cascade to subscriptions: a subscription pointing at a deleted\n\t// subgraph + table will throw `relation does not exist` on every\n\t// subsequent emission. Pause active subs and purge any pending outbox\n\t// rows so receivers don't get phantom replays. We don't delete the\n\t// subscriptions themselves — operators may want to repoint them at a\n\t// resurrected subgraph; we just stop them firing.\n\tawait db\n\t\t.updateTable(\"subscriptions\")\n\t\t.set({\n\t\t\tstatus: \"paused\",\n\t\t\tlast_error: `Subgraph \"${name}\" deleted; subscription auto-paused.`,\n\t\t\tupdated_at: new Date(),\n\t\t})\n\t\t.where(\"subgraph_name\", \"=\", name)\n\t\t.execute();\n\tawait db\n\t\t.deleteFrom(\"subscription_outbox\")\n\t\t.where(\"status\", \"=\", \"pending\")\n\t\t.where(\"subscription_id\", \"in\", (qb) =>\n\t\t\tqb\n\t\t\t\t.selectFrom(\"subscriptions\")\n\t\t\t\t.select(\"id\")\n\t\t\t\t.where(\"subgraph_name\", \"=\", name),\n\t\t)\n\t\t.execute();\n\n\t// Drop the subgraph's schema (CASCADE drops all tables within)\n\tawait sql`DROP SCHEMA IF EXISTS ${sql.raw(`\"${schemaName}\"`)} CASCADE`.execute(\n\t\tdb,\n\t);\n\n\t// Remove from registry\n\tawait db.deleteFrom(\"subgraphs\").where(\"id\", \"=\", subgraph.id).execute();\n\n\treturn subgraph;\n}\n"
7
7
  ],
8
- "mappings": "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",
9
- "debugId": "50F07B32C2BC0A0B64756E2164756E21",
8
+ "mappings": "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",
9
+ "debugId": "756EF511BC34387464756E2164756E21",
10
10
  "names": []
11
11
  }
@@ -268,6 +268,11 @@ interface ChatMessagesTable {
268
268
  metadata: unknown | null;
269
269
  created_at: Generated<Date>;
270
270
  }
271
+ interface ProcessedStripeEventsTable {
272
+ event_id: string;
273
+ event_type: string;
274
+ processed_at: Generated<Date>;
275
+ }
271
276
  interface Database {
272
277
  blocks: BlocksTable;
273
278
  transactions: TransactionsTable;
@@ -294,6 +299,7 @@ interface Database {
294
299
  team_invitations: TeamInvitationsTable;
295
300
  chat_sessions: ChatSessionsTable;
296
301
  chat_messages: ChatMessagesTable;
302
+ processed_stripe_events: ProcessedStripeEventsTable;
297
303
  tenants: TenantsTable;
298
304
  tenant_usage_monthly: TenantUsageMonthlyTable;
299
305
  tenant_compute_addons: TenantComputeAddonsTable;
@@ -268,6 +268,11 @@ interface ChatMessagesTable {
268
268
  metadata: unknown | null;
269
269
  created_at: Generated<Date>;
270
270
  }
271
+ interface ProcessedStripeEventsTable {
272
+ event_id: string;
273
+ event_type: string;
274
+ processed_at: Generated<Date>;
275
+ }
271
276
  interface Database {
272
277
  blocks: BlocksTable;
273
278
  transactions: TransactionsTable;
@@ -294,6 +299,7 @@ interface Database {
294
299
  team_invitations: TeamInvitationsTable;
295
300
  chat_sessions: ChatSessionsTable;
296
301
  chat_messages: ChatMessagesTable;
302
+ processed_stripe_events: ProcessedStripeEventsTable;
297
303
  tenants: TenantsTable;
298
304
  tenant_usage_monthly: TenantUsageMonthlyTable;
299
305
  tenant_compute_addons: TenantComputeAddonsTable;
@@ -268,6 +268,11 @@ interface ChatMessagesTable {
268
268
  metadata: unknown | null;
269
269
  created_at: Generated<Date>;
270
270
  }
271
+ interface ProcessedStripeEventsTable {
272
+ event_id: string;
273
+ event_type: string;
274
+ processed_at: Generated<Date>;
275
+ }
271
276
  interface Database {
272
277
  blocks: BlocksTable;
273
278
  transactions: TransactionsTable;
@@ -294,6 +299,7 @@ interface Database {
294
299
  team_invitations: TeamInvitationsTable;
295
300
  chat_sessions: ChatSessionsTable;
296
301
  chat_messages: ChatMessagesTable;
302
+ processed_stripe_events: ProcessedStripeEventsTable;
297
303
  tenants: TenantsTable;
298
304
  tenant_usage_monthly: TenantUsageMonthlyTable;
299
305
  tenant_compute_addons: TenantComputeAddonsTable;
@@ -268,6 +268,11 @@ interface ChatMessagesTable {
268
268
  metadata: unknown | null;
269
269
  created_at: Generated<Date>;
270
270
  }
271
+ interface ProcessedStripeEventsTable {
272
+ event_id: string;
273
+ event_type: string;
274
+ processed_at: Generated<Date>;
275
+ }
271
276
  interface Database {
272
277
  blocks: BlocksTable;
273
278
  transactions: TransactionsTable;
@@ -294,6 +299,7 @@ interface Database {
294
299
  team_invitations: TeamInvitationsTable;
295
300
  chat_sessions: ChatSessionsTable;
296
301
  chat_messages: ChatMessagesTable;
302
+ processed_stripe_events: ProcessedStripeEventsTable;
297
303
  tenants: TenantsTable;
298
304
  tenant_usage_monthly: TenantUsageMonthlyTable;
299
305
  tenant_compute_addons: TenantComputeAddonsTable;
@@ -267,6 +267,11 @@ interface ChatMessagesTable {
267
267
  metadata: unknown | null;
268
268
  created_at: Generated<Date>;
269
269
  }
270
+ interface ProcessedStripeEventsTable {
271
+ event_id: string;
272
+ event_type: string;
273
+ processed_at: Generated<Date>;
274
+ }
270
275
  interface Database {
271
276
  blocks: BlocksTable;
272
277
  transactions: TransactionsTable;
@@ -293,6 +298,7 @@ interface Database {
293
298
  team_invitations: TeamInvitationsTable;
294
299
  chat_sessions: ChatSessionsTable;
295
300
  chat_messages: ChatMessagesTable;
301
+ processed_stripe_events: ProcessedStripeEventsTable;
296
302
  tenants: TenantsTable;
297
303
  tenant_usage_monthly: TenantUsageMonthlyTable;
298
304
  tenant_compute_addons: TenantComputeAddonsTable;
@@ -512,4 +518,4 @@ interface SubscriptionDeliveriesTable {
512
518
  }
513
519
  type SubscriptionDelivery = Selectable<SubscriptionDeliveriesTable>;
514
520
  type InsertSubscriptionDelivery = Insertable<SubscriptionDeliveriesTable>;
515
- export { WaitlistTable, UsageSnapshotsTable, UsageSnapshot, UsageDailyTable, UsageDaily, UpdateTransaction, UpdateTenantUsageMonthly, UpdateTenantComputeAddon, UpdateTenant, UpdateSubscriptionOutbox, UpdateSubscription, UpdateSubgraphOperation, UpdateSubgraph, UpdateProject, UpdateIndexProgress, UpdateEvent, UpdateChatSession, UpdateBlock, UpdateApiKey, UpdateAccountSpendCap, TransactionsTable, Transaction, TenantsTable, TenantUsageMonthlyTable, TenantUsageMonthly, TenantStatus, TenantComputeAddonsTable, TenantComputeAddon, Tenant, TeamMembersTable, TeamMember, TeamInvitationsTable, TeamInvitation, SubscriptionsTable, SubscriptionStatus, SubscriptionRuntime, SubscriptionOutboxTable, SubscriptionOutbox, SubscriptionFormat, SubscriptionDelivery, SubscriptionDeliveriesTable, Subscription, SubgraphsTable, SubgraphUsageDailyTable, SubgraphUsageDaily, SubgraphTableSnapshotsTable, SubgraphProcessingStatsTable, SubgraphOperationsTable, SubgraphOperationStatus, SubgraphOperationKind, SubgraphOperation, SubgraphHealthSnapshotsTable, SubgraphHealthSnapshot, SubgraphGapsTable, SubgraphGap, Subgraph, SessionsTable, Session, ProvisioningAuditStatus, ProvisioningAuditLogTable, ProvisioningAuditLog, ProvisioningAuditEvent, ProjectsTable, Project, OutboxStatus, MagicLinksTable, MagicLink, InsertTransaction, InsertTenantUsageMonthly, InsertTenantComputeAddon, InsertTenant, InsertTeamMember, InsertTeamInvitation, InsertSubscriptionOutbox, InsertSubscriptionDelivery, InsertSubscription, InsertSubgraphUsageDaily, InsertSubgraphOperation, InsertSubgraphHealthSnapshot, InsertSubgraphGap, InsertSubgraph, InsertSession, InsertProvisioningAuditLog, InsertProject, InsertMagicLink, InsertIndexProgress, InsertEvent, InsertChatSession, InsertChatMessage, InsertBlock, InsertApiKey, InsertAccountSpendCap, InsertAccountInsight, InsertAccountAgentRun, InsertAccount, IndexProgressTable, IndexProgress, EventsTable, Event, Database, ChatSessionsTable, ChatSession, ChatMessagesTable, ChatMessage, BlocksTable, Block, ApiKeysTable, ApiKey, AccountsTable, AccountSpendCapsTable, AccountSpendCap, AccountInsightsTable, AccountInsight, AccountAgentRunsTable, AccountAgentRun, Account };
521
+ export { WaitlistTable, UsageSnapshotsTable, UsageSnapshot, UsageDailyTable, UsageDaily, UpdateTransaction, UpdateTenantUsageMonthly, UpdateTenantComputeAddon, UpdateTenant, UpdateSubscriptionOutbox, UpdateSubscription, UpdateSubgraphOperation, UpdateSubgraph, UpdateProject, UpdateIndexProgress, UpdateEvent, UpdateChatSession, UpdateBlock, UpdateApiKey, UpdateAccountSpendCap, TransactionsTable, Transaction, TenantsTable, TenantUsageMonthlyTable, TenantUsageMonthly, TenantStatus, TenantComputeAddonsTable, TenantComputeAddon, Tenant, TeamMembersTable, TeamMember, TeamInvitationsTable, TeamInvitation, SubscriptionsTable, SubscriptionStatus, SubscriptionRuntime, SubscriptionOutboxTable, SubscriptionOutbox, SubscriptionFormat, SubscriptionDelivery, SubscriptionDeliveriesTable, Subscription, SubgraphsTable, SubgraphUsageDailyTable, SubgraphUsageDaily, SubgraphTableSnapshotsTable, SubgraphProcessingStatsTable, SubgraphOperationsTable, SubgraphOperationStatus, SubgraphOperationKind, SubgraphOperation, SubgraphHealthSnapshotsTable, SubgraphHealthSnapshot, SubgraphGapsTable, SubgraphGap, Subgraph, SessionsTable, Session, ProvisioningAuditStatus, ProvisioningAuditLogTable, ProvisioningAuditLog, ProvisioningAuditEvent, ProjectsTable, Project, ProcessedStripeEventsTable, OutboxStatus, MagicLinksTable, MagicLink, InsertTransaction, InsertTenantUsageMonthly, InsertTenantComputeAddon, InsertTenant, InsertTeamMember, InsertTeamInvitation, InsertSubscriptionOutbox, InsertSubscriptionDelivery, InsertSubscription, InsertSubgraphUsageDaily, InsertSubgraphOperation, InsertSubgraphHealthSnapshot, InsertSubgraphGap, InsertSubgraph, InsertSession, InsertProvisioningAuditLog, InsertProject, InsertMagicLink, InsertIndexProgress, InsertEvent, InsertChatSession, InsertChatMessage, InsertBlock, InsertApiKey, InsertAccountSpendCap, InsertAccountInsight, InsertAccountAgentRun, InsertAccount, IndexProgressTable, IndexProgress, EventsTable, Event, Database, ChatSessionsTable, ChatSession, ChatMessagesTable, ChatMessage, BlocksTable, Block, ApiKeysTable, ApiKey, AccountsTable, AccountSpendCapsTable, AccountSpendCap, AccountInsightsTable, AccountInsight, AccountAgentRunsTable, AccountAgentRun, Account };
@@ -267,6 +267,11 @@ interface ChatMessagesTable {
267
267
  metadata: unknown | null;
268
268
  created_at: Generated<Date>;
269
269
  }
270
+ interface ProcessedStripeEventsTable {
271
+ event_id: string;
272
+ event_type: string;
273
+ processed_at: Generated<Date>;
274
+ }
270
275
  interface Database {
271
276
  blocks: BlocksTable;
272
277
  transactions: TransactionsTable;
@@ -293,6 +298,7 @@ interface Database {
293
298
  team_invitations: TeamInvitationsTable;
294
299
  chat_sessions: ChatSessionsTable;
295
300
  chat_messages: ChatMessagesTable;
301
+ processed_stripe_events: ProcessedStripeEventsTable;
296
302
  tenants: TenantsTable;
297
303
  tenant_usage_monthly: TenantUsageMonthlyTable;
298
304
  tenant_compute_addons: TenantComputeAddonsTable;
@@ -804,6 +810,7 @@ interface SubgraphSummary {
804
810
  progress: number;
805
811
  blocksRemaining?: number;
806
812
  syncMode?: "sync" | "reindex";
813
+ resourceWarning?: SubgraphResourceWarning;
807
814
  gapCount: number;
808
815
  integrity: "complete" | "gaps_detected";
809
816
  createdAt: string;
@@ -830,6 +837,7 @@ interface SubgraphSyncInfo {
830
837
  processedBlocks?: number;
831
838
  totalBlocks?: number;
832
839
  progress: number;
840
+ resourceWarning?: SubgraphResourceWarning;
833
841
  gaps: {
834
842
  count: number
835
843
  totalMissingBlocks: number
@@ -837,6 +845,14 @@ interface SubgraphSyncInfo {
837
845
  };
838
846
  integrity: "complete" | "gaps_detected";
839
847
  }
848
+ interface SubgraphResourceWarning {
849
+ code: "HOBBY_LARGE_REINDEX";
850
+ message: string;
851
+ plan: "hobby";
852
+ blockRange: number;
853
+ processorMemoryMb: number;
854
+ recommendedPlan: "launch";
855
+ }
840
856
  interface SubgraphDetail {
841
857
  name: string;
842
858
  version: string;
@@ -1112,4 +1128,4 @@ declare function createSignatureHeader(payload: string, secret: string, timestam
1112
1128
  * Returns true if valid, false otherwise
1113
1129
  */
1114
1130
  declare function verifySignatureHeader(payload: string, header: string, secret: string, toleranceSeconds?: number): boolean;
1115
- export { validateSubscriptionFilterForTable, sql, parseJsonb, logger, jsonb, getTargetDb, getSourceDb, getRawClient, getErrorMessage, getEnv, getDb, generateSubgraphSpec, generateSubgraphOpenApi, generateSubgraphMarkdown, generateSubgraphAgentSchema, formatSubscriptionSchemaErrors, exports_hmac as crypto, closeDb, WaitlistTable, VersionConflictError, ValidationError, UsageSnapshotsTable, UsageSnapshot, UsageDailyTable, UsageDaily, UpdateTransaction, UpdateTenantUsageMonthly, UpdateTenantComputeAddon, UpdateTenant, UpdateSubscriptionRequestSchema, UpdateSubscriptionRequest, UpdateSubscriptionOutbox, UpdateSubscription, UpdateSubgraphOperation, UpdateSubgraph, UpdateProject, UpdateProfileRequestSchema, UpdateProfileRequest, UpdateIndexProgress, UpdateEvent, UpdateChatSession, UpdateBlock, UpdateApiKey, UpdateAccountSpendCap, TransactionsTable, Transaction, TenantsTable, TenantUsageMonthlyTable, TenantUsageMonthly, TenantSuspendedError, TenantStatus, TenantComputeAddonsTable, TenantComputeAddon, Tenant, TeamMembersTable, TeamMember, TeamInvitationsTable, TeamInvitation, SubscriptionsTable, SubscriptionSummary, SubscriptionStatusSchema, SubscriptionStatus, SubscriptionSchemaTables, SubscriptionSchemaTable, SubscriptionSchemaColumn, SubscriptionRuntimeSchema, SubscriptionRuntime, SubscriptionOutboxTable, SubscriptionOutbox, SubscriptionFormatSchema, SubscriptionFormat, SubscriptionFilterSchema, SubscriptionFilterPrimitiveSchema, SubscriptionFilterPrimitive, SubscriptionFilterOperatorSchema, SubscriptionFilterOperator, SubscriptionFilterClauseSchema, SubscriptionFilterClause, SubscriptionFilter, SubscriptionDetail, SubscriptionDelivery, SubscriptionDeliveriesTable, Subscription, SubgraphsTable, SubgraphUsageDailyTable, SubgraphUsageDaily, SubgraphTableSnapshotsTable, SubgraphSyncInfo, SubgraphSummary, SubgraphSpecOptions, SubgraphSpecFormat, SubgraphQueryParams, SubgraphProcessingStatsTable, SubgraphOperationsTable, SubgraphOperationStatus, SubgraphOperationKind, SubgraphOperation, SubgraphHealthSnapshotsTable, SubgraphHealthSnapshot, SubgraphGapsTable, SubgraphGapsResponse, SubgraphGapRange, SubgraphGapEntry, SubgraphGap, SubgraphDetail, SubgraphAgentSchema, Subgraph, StxTransferFilterSchema, StxTransferFilter, StxMintFilterSchema, StxMintFilter, StxLockFilterSchema, StxLockFilter, StxBurnFilterSchema, StxBurnFilter, SessionsTable, Session, SecondLayerError, SUBSCRIPTION_STATUSES, SUBSCRIPTION_RUNTIMES, SUBSCRIPTION_FORMATS, SUBSCRIPTION_FILTER_OPERATORS, RotateSecretResponse, ReplaySubscriptionRequestSchema, ReplaySubscriptionRequest, ReplayResult, ReindexResponse, RateLimitError, ProvisioningAuditStatus, ProvisioningAuditLogTable, ProvisioningAuditLog, ProvisioningAuditEvent, ProjectsTable, Project, PrintEventFilterSchema, PrintEventFilter, ParsedUpdateSubscriptionRequest, ParsedReplaySubscriptionRequest, ParsedCreateSubscriptionRequest, OutboxStatus, NotFoundError, NftTransferFilterSchema, NftTransferFilter, NftMintFilterSchema, NftMintFilter, NftBurnFilterSchema, NftBurnFilter, MagicLinksTable, MagicLink, KeyRotatedError, InsertTransaction, InsertTenantUsageMonthly, InsertTenantComputeAddon, InsertTenant, InsertTeamMember, InsertTeamInvitation, InsertSubscriptionOutbox, InsertSubscriptionDelivery, InsertSubscription, InsertSubgraphUsageDaily, InsertSubgraphOperation, InsertSubgraphHealthSnapshot, InsertSubgraphGap, InsertSubgraph, InsertSession, InsertProvisioningAuditLog, InsertProject, InsertMagicLink, InsertIndexProgress, InsertEvent, InsertChatSession, InsertChatMessage, InsertBlock, InsertApiKey, InsertAccountSpendCap, InsertAccountInsight, InsertAccountAgentRun, InsertAccount, IndexProgressTable, IndexProgress, FtTransferFilterSchema, FtTransferFilter, FtMintFilterSchema, FtMintFilter, FtBurnFilterSchema, FtBurnFilter, ForbiddenError, EventsTable, EventFilterSchema, EventFilter, Event, ErrorCodes, ErrorCode, Env, DeploySubgraphResponse, DeploySubgraphRequestSchema, DeploySubgraphRequest, DeliveryRow, DeadRow, DatabaseError, Database, CreateSubscriptionResponse, CreateSubscriptionRequestSchema, CreateSubscriptionRequest, ContractDeployFilterSchema, ContractDeployFilter, ContractCallFilterSchema, ContractCallFilter, ChatSessionsTable, ChatSession, ChatMessagesTable, ChatMessage, CODE_TO_STATUS, BlocksTable, Block, AuthorizationError, AuthenticationError, ApiKeysTable, ApiKey, AccountsTable, AccountSpendCapsTable, AccountSpendCap, AccountInsightsTable, AccountInsight, AccountAgentRunsTable, AccountAgentRun, Account };
1131
+ export { validateSubscriptionFilterForTable, sql, parseJsonb, logger, jsonb, getTargetDb, getSourceDb, getRawClient, getErrorMessage, getEnv, getDb, generateSubgraphSpec, generateSubgraphOpenApi, generateSubgraphMarkdown, generateSubgraphAgentSchema, formatSubscriptionSchemaErrors, exports_hmac as crypto, closeDb, WaitlistTable, VersionConflictError, ValidationError, UsageSnapshotsTable, UsageSnapshot, UsageDailyTable, UsageDaily, UpdateTransaction, UpdateTenantUsageMonthly, UpdateTenantComputeAddon, UpdateTenant, UpdateSubscriptionRequestSchema, UpdateSubscriptionRequest, UpdateSubscriptionOutbox, UpdateSubscription, UpdateSubgraphOperation, UpdateSubgraph, UpdateProject, UpdateProfileRequestSchema, UpdateProfileRequest, UpdateIndexProgress, UpdateEvent, UpdateChatSession, UpdateBlock, UpdateApiKey, UpdateAccountSpendCap, TransactionsTable, Transaction, TenantsTable, TenantUsageMonthlyTable, TenantUsageMonthly, TenantSuspendedError, TenantStatus, TenantComputeAddonsTable, TenantComputeAddon, Tenant, TeamMembersTable, TeamMember, TeamInvitationsTable, TeamInvitation, SubscriptionsTable, SubscriptionSummary, SubscriptionStatusSchema, SubscriptionStatus, SubscriptionSchemaTables, SubscriptionSchemaTable, SubscriptionSchemaColumn, SubscriptionRuntimeSchema, SubscriptionRuntime, SubscriptionOutboxTable, SubscriptionOutbox, SubscriptionFormatSchema, SubscriptionFormat, SubscriptionFilterSchema, SubscriptionFilterPrimitiveSchema, SubscriptionFilterPrimitive, SubscriptionFilterOperatorSchema, SubscriptionFilterOperator, SubscriptionFilterClauseSchema, SubscriptionFilterClause, SubscriptionFilter, SubscriptionDetail, SubscriptionDelivery, SubscriptionDeliveriesTable, Subscription, SubgraphsTable, SubgraphUsageDailyTable, SubgraphUsageDaily, SubgraphTableSnapshotsTable, SubgraphSyncInfo, SubgraphSummary, SubgraphSpecOptions, SubgraphSpecFormat, SubgraphResourceWarning, SubgraphQueryParams, SubgraphProcessingStatsTable, SubgraphOperationsTable, SubgraphOperationStatus, SubgraphOperationKind, SubgraphOperation, SubgraphHealthSnapshotsTable, SubgraphHealthSnapshot, SubgraphGapsTable, SubgraphGapsResponse, SubgraphGapRange, SubgraphGapEntry, SubgraphGap, SubgraphDetail, SubgraphAgentSchema, Subgraph, StxTransferFilterSchema, StxTransferFilter, StxMintFilterSchema, StxMintFilter, StxLockFilterSchema, StxLockFilter, StxBurnFilterSchema, StxBurnFilter, SessionsTable, Session, SecondLayerError, SUBSCRIPTION_STATUSES, SUBSCRIPTION_RUNTIMES, SUBSCRIPTION_FORMATS, SUBSCRIPTION_FILTER_OPERATORS, RotateSecretResponse, ReplaySubscriptionRequestSchema, ReplaySubscriptionRequest, ReplayResult, ReindexResponse, RateLimitError, ProvisioningAuditStatus, ProvisioningAuditLogTable, ProvisioningAuditLog, ProvisioningAuditEvent, ProjectsTable, Project, ProcessedStripeEventsTable, PrintEventFilterSchema, PrintEventFilter, ParsedUpdateSubscriptionRequest, ParsedReplaySubscriptionRequest, ParsedCreateSubscriptionRequest, OutboxStatus, NotFoundError, NftTransferFilterSchema, NftTransferFilter, NftMintFilterSchema, NftMintFilter, NftBurnFilterSchema, NftBurnFilter, MagicLinksTable, MagicLink, KeyRotatedError, InsertTransaction, InsertTenantUsageMonthly, InsertTenantComputeAddon, InsertTenant, InsertTeamMember, InsertTeamInvitation, InsertSubscriptionOutbox, InsertSubscriptionDelivery, InsertSubscription, InsertSubgraphUsageDaily, InsertSubgraphOperation, InsertSubgraphHealthSnapshot, InsertSubgraphGap, InsertSubgraph, InsertSession, InsertProvisioningAuditLog, InsertProject, InsertMagicLink, InsertIndexProgress, InsertEvent, InsertChatSession, InsertChatMessage, InsertBlock, InsertApiKey, InsertAccountSpendCap, InsertAccountInsight, InsertAccountAgentRun, InsertAccount, IndexProgressTable, IndexProgress, FtTransferFilterSchema, FtTransferFilter, FtMintFilterSchema, FtMintFilter, FtBurnFilterSchema, FtBurnFilter, ForbiddenError, EventsTable, EventFilterSchema, EventFilter, Event, ErrorCodes, ErrorCode, Env, DeploySubgraphResponse, DeploySubgraphRequestSchema, DeploySubgraphRequest, DeliveryRow, DeadRow, DatabaseError, Database, CreateSubscriptionResponse, CreateSubscriptionRequestSchema, CreateSubscriptionRequest, ContractDeployFilterSchema, ContractDeployFilter, ContractCallFilterSchema, ContractCallFilter, ChatSessionsTable, ChatSession, ChatMessagesTable, ChatMessage, CODE_TO_STATUS, BlocksTable, Block, AuthorizationError, AuthenticationError, ApiKeysTable, ApiKey, AccountsTable, AccountSpendCapsTable, AccountSpendCap, AccountInsightsTable, AccountInsight, AccountAgentRunsTable, AccountAgentRun, Account };
@@ -9,7 +9,7 @@
9
9
  "export const ErrorCodes = {\n\tVALIDATION_ERROR: \"VALIDATION_ERROR\",\n\tDATABASE_ERROR: \"DATABASE_ERROR\",\n\tAUTHENTICATION_ERROR: \"AUTHENTICATION_ERROR\",\n\tAUTHORIZATION_ERROR: \"AUTHORIZATION_ERROR\",\n\tRATE_LIMIT_ERROR: \"RATE_LIMIT_ERROR\",\n\tFORBIDDEN: \"FORBIDDEN\",\n\tVERSION_CONFLICT: \"VERSION_CONFLICT\",\n\tNOT_FOUND: \"NOT_FOUND\",\n\t// Tenant lifecycle (CLI surfaces these verbatim)\n\tKEY_ROTATED: \"KEY_ROTATED\",\n\tTENANT_SUSPENDED: \"TENANT_SUSPENDED\",\n\tNO_TENANT_FOR_PROJECT: \"NO_TENANT_FOR_PROJECT\",\n\tINSTANCE_EXISTS: \"INSTANCE_EXISTS\",\n} as const;\n\nexport type ErrorCode = (typeof ErrorCodes)[keyof typeof ErrorCodes];\n\n/** Base error class for all Secondlayer errors. */\nexport class SecondLayerError extends Error {\n\tpublic code: ErrorCode;\n\tpublic override cause?: unknown;\n\n\tconstructor(code: ErrorCode, message: string, cause?: unknown) {\n\t\tsuper(message);\n\t\tthis.code = code;\n\t\tthis.cause = cause;\n\t\tthis.name = this.constructor.name;\n\t\tError.captureStackTrace?.(this, this.constructor);\n\t}\n\n\ttoJSON(): {\n\t\tname: string;\n\t\tcode: string;\n\t\tmessage: string;\n\t\tstack: string | undefined;\n\t\tcause: unknown;\n\t} {\n\t\treturn {\n\t\t\tname: this.name,\n\t\t\tcode: this.code,\n\t\t\tmessage: this.message,\n\t\t\tstack: this.stack,\n\t\t\tcause: this.cause,\n\t\t};\n\t}\n}\n\nexport class NotFoundError extends SecondLayerError {\n\tconstructor(message: string) {\n\t\tsuper(\"NOT_FOUND\", message);\n\t}\n}\n\nexport class ValidationError extends SecondLayerError {\n\tconstructor(message: string, cause?: unknown) {\n\t\tsuper(\"VALIDATION_ERROR\", message, cause);\n\t}\n}\n\nexport class DatabaseError extends SecondLayerError {\n\tconstructor(message: string, cause?: unknown) {\n\t\tsuper(\"DATABASE_ERROR\", message, cause);\n\t}\n}\n\nexport class AuthenticationError extends SecondLayerError {\n\tconstructor(message: string) {\n\t\tsuper(\"AUTHENTICATION_ERROR\", message);\n\t}\n}\n\nexport class AuthorizationError extends SecondLayerError {\n\tconstructor(message: string) {\n\t\tsuper(\"AUTHORIZATION_ERROR\", message);\n\t}\n}\n\nexport class RateLimitError extends SecondLayerError {\n\tconstructor(message: string) {\n\t\tsuper(\"RATE_LIMIT_ERROR\", message);\n\t}\n}\n\nexport class ForbiddenError extends SecondLayerError {\n\tconstructor(message = \"Forbidden\") {\n\t\tsuper(\"FORBIDDEN\", message);\n\t}\n}\n\nexport class VersionConflictError extends SecondLayerError {\n\tpublic currentVersion: string;\n\tpublic expectedVersion: string;\n\n\tconstructor(currentVersion: string, expectedVersion: string) {\n\t\tsuper(\n\t\t\t\"VERSION_CONFLICT\",\n\t\t\t`Version conflict: expected ${expectedVersion}, current ${currentVersion}`,\n\t\t);\n\t\tthis.currentVersion = currentVersion;\n\t\tthis.expectedVersion = expectedVersion;\n\t}\n}\n\nexport class KeyRotatedError extends SecondLayerError {\n\tconstructor(message = \"Token has been rotated\") {\n\t\tsuper(\"KEY_ROTATED\", message);\n\t}\n}\n\nexport class TenantSuspendedError extends SecondLayerError {\n\tconstructor(message = \"Instance is suspended\") {\n\t\tsuper(\"TENANT_SUSPENDED\", message);\n\t}\n}\n\n/** Error code → HTTP status. Used by API middleware for code-based matching\n * (avoids cross-bundle instanceof failures from bunup class duplication). */\n// String literal map — codes don't have to be in the central ErrorCode\n// enum (route-local error classes can supply any code; we just map the\n// HTTP status here). This keeps cross-bundle instanceof failures out of\n// the equation.\nexport const CODE_TO_STATUS: Record<\n\tstring,\n\t400 | 401 | 403 | 404 | 409 | 423 | 429\n> = {\n\tAUTHENTICATION_ERROR: 401,\n\tAUTHORIZATION_ERROR: 403,\n\tRATE_LIMIT_ERROR: 429,\n\tFORBIDDEN: 403,\n\tNOT_FOUND: 404,\n\tVALIDATION_ERROR: 400,\n\tKEY_ROTATED: 401,\n\tTENANT_SUSPENDED: 423,\n\tNO_TENANT_FOR_PROJECT: 404,\n\tINSTANCE_EXISTS: 409,\n\tSUBGRAPH_NOT_FOUND: 404,\n} as const;\n\nexport function getErrorMessage(err: unknown): string {\n\treturn err instanceof Error ? err.message : String(err);\n}\n",
10
10
  "import { z } from \"zod/v4\";\n\n/**\n * Account profile shapes. Unrelated to marketplace — previously lived in\n * schemas/marketplace.ts alongside public-directory types. Kept here now\n * that marketplace is gone so the profile fields (display_name, bio, slug)\n * have a stable home.\n */\n\nexport interface UpdateProfileRequest {\n\tdisplay_name?: string;\n\tbio?: string;\n\tslug?: string;\n}\n\nexport const UpdateProfileRequestSchema: z.ZodType<UpdateProfileRequest> =\n\tz.object({\n\t\tdisplay_name: z.string().max(50).optional(),\n\t\tbio: z.string().max(300).optional(),\n\t\tslug: z\n\t\t\t.string()\n\t\t\t.regex(/^[a-z0-9-]+$/, \"lowercase alphanumeric + hyphens only\")\n\t\t\t.min(3)\n\t\t\t.max(30)\n\t\t\t.optional(),\n\t});\n",
11
11
  "import { isValidAddress as _isValidAddress } from \"@secondlayer/stacks\";\nimport { z } from \"zod/v4\";\n\nconst isValidAddress = _isValidAddress as (addr: string) => boolean;\n\n/** Validate a Stacks principal (standard or contract, e.g. SP2J...ABC or SP2J...ABC.contract-name) */\nconst stacksPrincipal = z.string().refine((val) => {\n\tconst parts = val.split(\".\");\n\tif (parts.length > 2) return false;\n\t// biome-ignore lint/style/noNonNullAssertion: value is non-null after preceding check or by construction; TS narrowing limitation\n\treturn isValidAddress(parts[0]!);\n}, \"Invalid Stacks principal address\");\n\n// Base filter with common fields\nconst baseFilter = {\n\t// Optional: filter by sender\n\tsender: stacksPrincipal.optional(),\n\t// Optional: filter by recipient\n\trecipient: stacksPrincipal.optional(),\n};\n\n// Type exports — defined first so they can annotate schemas\nexport interface StxTransferFilter {\n\ttype: \"stx_transfer\";\n\tsender?: string;\n\trecipient?: string;\n\tminAmount?: number;\n\tmaxAmount?: number;\n}\n\nexport interface StxMintFilter {\n\ttype: \"stx_mint\";\n\trecipient?: string;\n\tminAmount?: number;\n}\n\nexport interface StxBurnFilter {\n\ttype: \"stx_burn\";\n\tsender?: string;\n\tminAmount?: number;\n}\n\nexport interface StxLockFilter {\n\ttype: \"stx_lock\";\n\tlockedAddress?: string;\n\tminAmount?: number;\n}\n\nexport interface FtTransferFilter {\n\ttype: \"ft_transfer\";\n\tsender?: string;\n\trecipient?: string;\n\tassetIdentifier?: string;\n\tminAmount?: number;\n}\n\nexport interface FtMintFilter {\n\ttype: \"ft_mint\";\n\trecipient?: string;\n\tassetIdentifier?: string;\n\tminAmount?: number;\n}\n\nexport interface FtBurnFilter {\n\ttype: \"ft_burn\";\n\tsender?: string;\n\tassetIdentifier?: string;\n\tminAmount?: number;\n}\n\nexport interface NftTransferFilter {\n\ttype: \"nft_transfer\";\n\tsender?: string;\n\trecipient?: string;\n\tassetIdentifier?: string;\n\ttokenId?: string;\n}\n\nexport interface NftMintFilter {\n\ttype: \"nft_mint\";\n\trecipient?: string;\n\tassetIdentifier?: string;\n\ttokenId?: string;\n}\n\nexport interface NftBurnFilter {\n\ttype: \"nft_burn\";\n\tsender?: string;\n\tassetIdentifier?: string;\n\ttokenId?: string;\n}\n\nexport interface ContractCallFilter {\n\ttype: \"contract_call\";\n\tcontractId?: string;\n\tfunctionName?: string;\n\tcaller?: string;\n}\n\nexport interface ContractDeployFilter {\n\ttype: \"contract_deploy\";\n\tdeployer?: string;\n\tcontractName?: string;\n}\n\nexport interface PrintEventFilter {\n\ttype: \"print_event\";\n\tcontractId?: string;\n\ttopic?: string;\n\tcontains?: string;\n}\n\nexport type EventFilter =\n\t| StxTransferFilter\n\t| StxMintFilter\n\t| StxBurnFilter\n\t| StxLockFilter\n\t| FtTransferFilter\n\t| FtMintFilter\n\t| FtBurnFilter\n\t| NftTransferFilter\n\t| NftMintFilter\n\t| NftBurnFilter\n\t| ContractCallFilter\n\t| ContractDeployFilter\n\t| PrintEventFilter;\n\n// STX Transfer Filter\nexport const StxTransferFilterSchema: z.ZodType<StxTransferFilter> = z.object({\n\ttype: z.literal(\"stx_transfer\"),\n\t...baseFilter,\n\t// Optional: minimum amount in microSTX\n\tminAmount: z.coerce.number().int().positive().optional(),\n\t// Optional: maximum amount in microSTX\n\tmaxAmount: z.coerce.number().int().positive().optional(),\n});\n\n// STX Mint Filter\nexport const StxMintFilterSchema: z.ZodType<StxMintFilter> = z.object({\n\ttype: z.literal(\"stx_mint\"),\n\trecipient: stacksPrincipal.optional(),\n\tminAmount: z.coerce.number().int().positive().optional(),\n});\n\n// STX Burn Filter\nexport const StxBurnFilterSchema: z.ZodType<StxBurnFilter> = z.object({\n\ttype: z.literal(\"stx_burn\"),\n\tsender: stacksPrincipal.optional(),\n\tminAmount: z.coerce.number().int().positive().optional(),\n});\n\n// STX Lock Filter\nexport const StxLockFilterSchema: z.ZodType<StxLockFilter> = z.object({\n\ttype: z.literal(\"stx_lock\"),\n\tlockedAddress: stacksPrincipal.optional(),\n\tminAmount: z.coerce.number().int().positive().optional(),\n});\n\n// FT Transfer Filter\nexport const FtTransferFilterSchema: z.ZodType<FtTransferFilter> = z.object({\n\ttype: z.literal(\"ft_transfer\"),\n\t...baseFilter,\n\t// Contract that defines the token (e.g., SP3K8BC0PPEVCV7NZ6QSRWPQ2JE9E5B6N3PA0KBR9.token-wstx)\n\tassetIdentifier: z.string().optional(),\n\tminAmount: z.coerce.number().int().positive().optional(),\n});\n\n// FT Mint Filter\nexport const FtMintFilterSchema: z.ZodType<FtMintFilter> = z.object({\n\ttype: z.literal(\"ft_mint\"),\n\trecipient: stacksPrincipal.optional(),\n\tassetIdentifier: z.string().optional(),\n\tminAmount: z.coerce.number().int().positive().optional(),\n});\n\n// FT Burn Filter\nexport const FtBurnFilterSchema: z.ZodType<FtBurnFilter> = z.object({\n\ttype: z.literal(\"ft_burn\"),\n\tsender: stacksPrincipal.optional(),\n\tassetIdentifier: z.string().optional(),\n\tminAmount: z.coerce.number().int().positive().optional(),\n});\n\n// NFT Transfer Filter\nexport const NftTransferFilterSchema: z.ZodType<NftTransferFilter> = z.object({\n\ttype: z.literal(\"nft_transfer\"),\n\t...baseFilter,\n\tassetIdentifier: z.string().optional(),\n\t// Optional: filter by specific token ID (Clarity value as hex)\n\ttokenId: z.string().optional(),\n});\n\n// NFT Mint Filter\nexport const NftMintFilterSchema: z.ZodType<NftMintFilter> = z.object({\n\ttype: z.literal(\"nft_mint\"),\n\trecipient: stacksPrincipal.optional(),\n\tassetIdentifier: z.string().optional(),\n\ttokenId: z.string().optional(),\n});\n\n// NFT Burn Filter\nexport const NftBurnFilterSchema: z.ZodType<NftBurnFilter> = z.object({\n\ttype: z.literal(\"nft_burn\"),\n\tsender: stacksPrincipal.optional(),\n\tassetIdentifier: z.string().optional(),\n\ttokenId: z.string().optional(),\n});\n\n// Contract Call Filter\nexport const ContractCallFilterSchema: z.ZodType<ContractCallFilter> = z.object(\n\t{\n\t\ttype: z.literal(\"contract_call\"),\n\t\t// Contract being called\n\t\tcontractId: stacksPrincipal.optional(),\n\t\t// Function name (supports wildcards with *)\n\t\tfunctionName: z.string().optional(),\n\t\t// Caller address\n\t\tcaller: stacksPrincipal.optional(),\n\t},\n);\n\n// Contract Deploy Filter\nexport const ContractDeployFilterSchema: z.ZodType<ContractDeployFilter> =\n\tz.object({\n\t\ttype: z.literal(\"contract_deploy\"),\n\t\t// Deployer address\n\t\tdeployer: stacksPrincipal.optional(),\n\t\t// Contract name pattern (supports wildcards)\n\t\tcontractName: z.string().optional(),\n\t});\n\n// Print Event Filter (smart contract events)\nexport const PrintEventFilterSchema: z.ZodType<PrintEventFilter> = z.object({\n\ttype: z.literal(\"print_event\"),\n\t// Contract emitting the event\n\tcontractId: stacksPrincipal.optional(),\n\t// Topic/name of the event\n\ttopic: z.string().optional(),\n\t// Search for substring in event data\n\tcontains: z.string().optional(),\n});\n\n// Union of all filter types\nexport const EventFilterSchema: z.ZodType<EventFilter> = z.discriminatedUnion(\n\t\"type\",\n\t[\n\t\t// biome-ignore lint/suspicious/noExplicitAny: interop boundary or dynamic-shape value where typing adds friction without runtime safety\n\t\tStxTransferFilterSchema as any,\n\t\t// biome-ignore lint/suspicious/noExplicitAny: interop boundary or dynamic-shape value where typing adds friction without runtime safety\n\t\tStxMintFilterSchema as any,\n\t\t// biome-ignore lint/suspicious/noExplicitAny: interop boundary or dynamic-shape value where typing adds friction without runtime safety\n\t\tStxBurnFilterSchema as any,\n\t\t// biome-ignore lint/suspicious/noExplicitAny: interop boundary or dynamic-shape value where typing adds friction without runtime safety\n\t\tStxLockFilterSchema as any,\n\t\t// biome-ignore lint/suspicious/noExplicitAny: interop boundary or dynamic-shape value where typing adds friction without runtime safety\n\t\tFtTransferFilterSchema as any,\n\t\t// biome-ignore lint/suspicious/noExplicitAny: interop boundary or dynamic-shape value where typing adds friction without runtime safety\n\t\tFtMintFilterSchema as any,\n\t\t// biome-ignore lint/suspicious/noExplicitAny: interop boundary or dynamic-shape value where typing adds friction without runtime safety\n\t\tFtBurnFilterSchema as any,\n\t\t// biome-ignore lint/suspicious/noExplicitAny: interop boundary or dynamic-shape value where typing adds friction without runtime safety\n\t\tNftTransferFilterSchema as any,\n\t\t// biome-ignore lint/suspicious/noExplicitAny: interop boundary or dynamic-shape value where typing adds friction without runtime safety\n\t\tNftMintFilterSchema as any,\n\t\t// biome-ignore lint/suspicious/noExplicitAny: interop boundary or dynamic-shape value where typing adds friction without runtime safety\n\t\tNftBurnFilterSchema as any,\n\t\t// biome-ignore lint/suspicious/noExplicitAny: interop boundary or dynamic-shape value where typing adds friction without runtime safety\n\t\tContractCallFilterSchema as any,\n\t\t// biome-ignore lint/suspicious/noExplicitAny: interop boundary or dynamic-shape value where typing adds friction without runtime safety\n\t\tContractDeployFilterSchema as any,\n\t\t// biome-ignore lint/suspicious/noExplicitAny: interop boundary or dynamic-shape value where typing adds friction without runtime safety\n\t\tPrintEventFilterSchema as any,\n\t],\n);\n",
12
- "import { z } from \"zod/v4\";\n\n// ── Deploy Subgraph Request ─────────────────────────────────────────────────\n\nexport interface DeploySubgraphRequest {\n\tname: string;\n\tversion?: string;\n\tdescription?: string;\n\tsources: Record<string, Record<string, unknown>>;\n\tschema: Record<string, unknown>;\n\thandlerCode: string;\n\t/** Override the definition's startBlock for this deploy only. */\n\tstartBlock?: number;\n\t/** Original TypeScript source, persisted so chat can read/diff/edit later. */\n\tsourceCode?: string;\n}\n\nexport const DeploySubgraphRequestSchema: z.ZodType<DeploySubgraphRequest> =\n\tz.object({\n\t\tname: z\n\t\t\t.string()\n\t\t\t.regex(/^[a-z0-9-]+$/, \"lowercase alphanumeric + hyphens only\")\n\t\t\t.max(63),\n\t\tversion: z.string().optional(),\n\t\tdescription: z.string().optional(),\n\t\tsources: z\n\t\t\t.record(z.string(), z.record(z.string(), z.unknown()))\n\t\t\t.refine(\n\t\t\t\t(s) => Object.keys(s).length > 0,\n\t\t\t\t\"Must have at least one source\",\n\t\t\t),\n\t\tschema: z.record(z.string(), z.unknown()),\n\t\thandlerCode: z.string().max(1_048_576, \"handler code exceeds 1MB limit\"),\n\t\tstartBlock: z.number().int().nonnegative().optional(),\n\t\tsourceCode: z\n\t\t\t.string()\n\t\t\t.max(1_048_576, \"source code exceeds 1MB limit\")\n\t\t\t.optional(),\n\t});\n\nexport interface DeploySubgraphResponse {\n\taction: \"created\" | \"unchanged\" | \"updated\" | \"reindexed\";\n\tsubgraphId: string;\n\tversion: string;\n\tmessage: string;\n\toperationId?: string;\n\treindexStarted?: boolean;\n\tdiff?: {\n\t\taddedTables: string[];\n\t\tremovedTables: string[];\n\t\taddedColumns: Record<string, string[]>;\n\t\tbreakingChanges: string[];\n\t};\n}\n\n// Subgraph API response types\n\nexport interface SubgraphSummary {\n\tname: string;\n\tversion: string;\n\tstatus: string;\n\tlastProcessedBlock: number;\n\ttotalProcessed: number;\n\ttotalErrors: number;\n\ttables: string[];\n\tchainTip: number;\n\tsourceChainTip?: number;\n\ttargetBlock?: number;\n\tprogress: number;\n\tblocksRemaining?: number;\n\tsyncMode?: \"sync\" | \"reindex\";\n\tgapCount: number;\n\tintegrity: \"complete\" | \"gaps_detected\";\n\tcreatedAt: string;\n}\n\nexport interface SubgraphGapRange {\n\tstart: number;\n\tend: number;\n\tsize: number;\n\treason: string;\n}\n\nexport interface SubgraphSyncInfo {\n\tstatus: \"synced\" | \"catching_up\" | \"reindexing\" | \"error\";\n\tmode?: \"sync\" | \"reindex\";\n\tstartBlock: number;\n\tlastProcessedBlock: number;\n\t/**\n\t * Backward-compatible denominator for progress displays. During reindexing,\n\t * this is the reindex target block rather than the live source chain tip.\n\t */\n\tchainTip: number;\n\tsourceChainTip?: number;\n\ttargetBlock?: number;\n\tblocksRemaining: number;\n\tprocessedBlocks?: number;\n\ttotalBlocks?: number;\n\tprogress: number;\n\tgaps: {\n\t\tcount: number;\n\t\ttotalMissingBlocks: number;\n\t\tranges: SubgraphGapRange[];\n\t};\n\tintegrity: \"complete\" | \"gaps_detected\";\n}\n\nexport interface SubgraphDetail {\n\tname: string;\n\tversion: string;\n\tschemaHash?: string;\n\tstatus: string;\n\tlastProcessedBlock: number;\n\tdescription?: string;\n\tsources?: Record<string, unknown>;\n\tdefinition?: Record<string, unknown>;\n\thealth: {\n\t\ttotalProcessed: number;\n\t\ttotalErrors: number;\n\t\terrorRate: number;\n\t\tlastError: string | null;\n\t\tlastErrorAt: string | null;\n\t};\n\tsync: SubgraphSyncInfo;\n\ttables: Record<\n\t\tstring,\n\t\t{\n\t\t\tendpoint: string;\n\t\t\tcolumns: Record<\n\t\t\t\tstring,\n\t\t\t\t{\n\t\t\t\t\ttype: string;\n\t\t\t\t\tnullable?: boolean;\n\t\t\t\t\tindexed?: boolean;\n\t\t\t\t\tsearchable?: boolean;\n\t\t\t\t\tdefault?: string | number | boolean;\n\t\t\t\t}\n\t\t\t>;\n\t\t\trowCount: number;\n\t\t\texample: string;\n\t\t\tindexes?: string[][];\n\t\t\tuniqueKeys?: string[][];\n\t\t}\n\t>;\n\tcreatedAt: string;\n\tupdatedAt: string;\n}\n\nexport interface SubgraphGapEntry {\n\tstart: number;\n\tend: number;\n\tsize: number;\n\treason: string;\n\tdetectedAt: string;\n\tresolvedAt: string | null;\n}\n\nexport interface SubgraphGapsResponse {\n\tdata: SubgraphGapEntry[];\n\tmeta: {\n\t\ttotal: number;\n\t\ttotalMissingBlocks: number;\n\t\tlimit: number;\n\t\toffset: number;\n\t};\n}\n\nexport interface ReindexResponse {\n\tmessage: string;\n\tfromBlock: number;\n\ttoBlock: number | string;\n\toperationId?: string;\n\tstatus?: \"queued\" | \"running\" | \"cancel_requested\";\n}\n\nexport interface SubgraphQueryParams {\n\tsort?: string;\n\torder?: string;\n\tlimit?: number;\n\toffset?: number;\n\tfields?: string;\n\tfilters?: Record<string, string>;\n}\n",
12
+ "import { z } from \"zod/v4\";\n\n// ── Deploy Subgraph Request ─────────────────────────────────────────────────\n\nexport interface DeploySubgraphRequest {\n\tname: string;\n\tversion?: string;\n\tdescription?: string;\n\tsources: Record<string, Record<string, unknown>>;\n\tschema: Record<string, unknown>;\n\thandlerCode: string;\n\t/** Override the definition's startBlock for this deploy only. */\n\tstartBlock?: number;\n\t/** Original TypeScript source, persisted so chat can read/diff/edit later. */\n\tsourceCode?: string;\n}\n\nexport const DeploySubgraphRequestSchema: z.ZodType<DeploySubgraphRequest> =\n\tz.object({\n\t\tname: z\n\t\t\t.string()\n\t\t\t.regex(/^[a-z0-9-]+$/, \"lowercase alphanumeric + hyphens only\")\n\t\t\t.max(63),\n\t\tversion: z.string().optional(),\n\t\tdescription: z.string().optional(),\n\t\tsources: z\n\t\t\t.record(z.string(), z.record(z.string(), z.unknown()))\n\t\t\t.refine(\n\t\t\t\t(s) => Object.keys(s).length > 0,\n\t\t\t\t\"Must have at least one source\",\n\t\t\t),\n\t\tschema: z.record(z.string(), z.unknown()),\n\t\thandlerCode: z.string().max(1_048_576, \"handler code exceeds 1MB limit\"),\n\t\tstartBlock: z.number().int().nonnegative().optional(),\n\t\tsourceCode: z\n\t\t\t.string()\n\t\t\t.max(1_048_576, \"source code exceeds 1MB limit\")\n\t\t\t.optional(),\n\t});\n\nexport interface DeploySubgraphResponse {\n\taction: \"created\" | \"unchanged\" | \"updated\" | \"reindexed\";\n\tsubgraphId: string;\n\tversion: string;\n\tmessage: string;\n\toperationId?: string;\n\treindexStarted?: boolean;\n\tdiff?: {\n\t\taddedTables: string[];\n\t\tremovedTables: string[];\n\t\taddedColumns: Record<string, string[]>;\n\t\tbreakingChanges: string[];\n\t};\n}\n\n// Subgraph API response types\n\nexport interface SubgraphSummary {\n\tname: string;\n\tversion: string;\n\tstatus: string;\n\tlastProcessedBlock: number;\n\ttotalProcessed: number;\n\ttotalErrors: number;\n\ttables: string[];\n\tchainTip: number;\n\tsourceChainTip?: number;\n\ttargetBlock?: number;\n\tprogress: number;\n\tblocksRemaining?: number;\n\tsyncMode?: \"sync\" | \"reindex\";\n\tresourceWarning?: SubgraphResourceWarning;\n\tgapCount: number;\n\tintegrity: \"complete\" | \"gaps_detected\";\n\tcreatedAt: string;\n}\n\nexport interface SubgraphGapRange {\n\tstart: number;\n\tend: number;\n\tsize: number;\n\treason: string;\n}\n\nexport interface SubgraphSyncInfo {\n\tstatus: \"synced\" | \"catching_up\" | \"reindexing\" | \"error\";\n\tmode?: \"sync\" | \"reindex\";\n\tstartBlock: number;\n\tlastProcessedBlock: number;\n\t/**\n\t * Backward-compatible denominator for progress displays. During reindexing,\n\t * this is the reindex target block rather than the live source chain tip.\n\t */\n\tchainTip: number;\n\tsourceChainTip?: number;\n\ttargetBlock?: number;\n\tblocksRemaining: number;\n\tprocessedBlocks?: number;\n\ttotalBlocks?: number;\n\tprogress: number;\n\tresourceWarning?: SubgraphResourceWarning;\n\tgaps: {\n\t\tcount: number;\n\t\ttotalMissingBlocks: number;\n\t\tranges: SubgraphGapRange[];\n\t};\n\tintegrity: \"complete\" | \"gaps_detected\";\n}\n\nexport interface SubgraphResourceWarning {\n\tcode: \"HOBBY_LARGE_REINDEX\";\n\tmessage: string;\n\tplan: \"hobby\";\n\tblockRange: number;\n\tprocessorMemoryMb: number;\n\trecommendedPlan: \"launch\";\n}\n\nexport interface SubgraphDetail {\n\tname: string;\n\tversion: string;\n\tschemaHash?: string;\n\tstatus: string;\n\tlastProcessedBlock: number;\n\tdescription?: string;\n\tsources?: Record<string, unknown>;\n\tdefinition?: Record<string, unknown>;\n\thealth: {\n\t\ttotalProcessed: number;\n\t\ttotalErrors: number;\n\t\terrorRate: number;\n\t\tlastError: string | null;\n\t\tlastErrorAt: string | null;\n\t};\n\tsync: SubgraphSyncInfo;\n\ttables: Record<\n\t\tstring,\n\t\t{\n\t\t\tendpoint: string;\n\t\t\tcolumns: Record<\n\t\t\t\tstring,\n\t\t\t\t{\n\t\t\t\t\ttype: string;\n\t\t\t\t\tnullable?: boolean;\n\t\t\t\t\tindexed?: boolean;\n\t\t\t\t\tsearchable?: boolean;\n\t\t\t\t\tdefault?: string | number | boolean;\n\t\t\t\t}\n\t\t\t>;\n\t\t\trowCount: number;\n\t\t\texample: string;\n\t\t\tindexes?: string[][];\n\t\t\tuniqueKeys?: string[][];\n\t\t}\n\t>;\n\tcreatedAt: string;\n\tupdatedAt: string;\n}\n\nexport interface SubgraphGapEntry {\n\tstart: number;\n\tend: number;\n\tsize: number;\n\treason: string;\n\tdetectedAt: string;\n\tresolvedAt: string | null;\n}\n\nexport interface SubgraphGapsResponse {\n\tdata: SubgraphGapEntry[];\n\tmeta: {\n\t\ttotal: number;\n\t\ttotalMissingBlocks: number;\n\t\tlimit: number;\n\t\toffset: number;\n\t};\n}\n\nexport interface ReindexResponse {\n\tmessage: string;\n\tfromBlock: number;\n\ttoBlock: number | string;\n\toperationId?: string;\n\tstatus?: \"queued\" | \"running\" | \"cancel_requested\";\n}\n\nexport interface SubgraphQueryParams {\n\tsort?: string;\n\torder?: string;\n\tlimit?: number;\n\toffset?: number;\n\tfields?: string;\n\tfilters?: Record<string, string>;\n}\n",
13
13
  "import { z } from \"zod/v4\";\n\nexport const SUBSCRIPTION_FORMATS = [\n\t\"standard-webhooks\",\n\t\"inngest\",\n\t\"trigger\",\n\t\"cloudflare\",\n\t\"cloudevents\",\n\t\"raw\",\n] as const;\n\nexport const SUBSCRIPTION_RUNTIMES = [\n\t\"inngest\",\n\t\"trigger\",\n\t\"cloudflare\",\n\t\"node\",\n] as const;\n\nexport const SUBSCRIPTION_STATUSES = [\"active\", \"paused\", \"error\"] as const;\n\nexport const SUBSCRIPTION_FILTER_OPERATORS = [\n\t\"eq\",\n\t\"neq\",\n\t\"gt\",\n\t\"gte\",\n\t\"lt\",\n\t\"lte\",\n\t\"in\",\n] as const;\n\nconst webhookUrl = z\n\t.string()\n\t.trim()\n\t.min(1)\n\t.refine(\n\t\t(value) => value.startsWith(\"http://\") || value.startsWith(\"https://\"),\n\t\t\"must be an http(s) URL\",\n\t);\n\nconst name = z.string().trim().min(1).max(128);\nconst resourceName = z.string().trim().min(1).max(128);\n\nexport const SubscriptionStatusSchema: z.ZodType<SubscriptionStatus> = z.enum(\n\tSUBSCRIPTION_STATUSES,\n);\nexport const SubscriptionFormatSchema: z.ZodType<SubscriptionFormat> =\n\tz.enum(SUBSCRIPTION_FORMATS);\nexport const SubscriptionRuntimeSchema: z.ZodType<SubscriptionRuntime> = z.enum(\n\tSUBSCRIPTION_RUNTIMES,\n);\n\nexport const SubscriptionFilterPrimitiveSchema: z.ZodType<SubscriptionFilterPrimitive> =\n\tz.union([z.string(), z.number().finite(), z.boolean()]);\n\nexport const SubscriptionFilterOperatorSchema: z.ZodType<SubscriptionFilterOperator> =\n\tz.union([\n\t\tz.object({ eq: SubscriptionFilterPrimitiveSchema }).strict(),\n\t\tz.object({ neq: SubscriptionFilterPrimitiveSchema }).strict(),\n\t\tz.object({ gt: z.union([z.string(), z.number().finite()]) }).strict(),\n\t\tz.object({ gte: z.union([z.string(), z.number().finite()]) }).strict(),\n\t\tz.object({ lt: z.union([z.string(), z.number().finite()]) }).strict(),\n\t\tz.object({ lte: z.union([z.string(), z.number().finite()]) }).strict(),\n\t\tz\n\t\t\t.object({\n\t\t\t\tin: z.array(SubscriptionFilterPrimitiveSchema).min(1),\n\t\t\t})\n\t\t\t.strict(),\n\t]);\n\nexport const SubscriptionFilterClauseSchema: z.ZodType<SubscriptionFilterClause> =\n\tz.union([\n\t\tSubscriptionFilterPrimitiveSchema,\n\t\tSubscriptionFilterOperatorSchema,\n\t]);\n\nexport const SubscriptionFilterSchema: z.ZodType<SubscriptionFilter> = z.record(\n\tz.string().min(1),\n\tSubscriptionFilterClauseSchema,\n);\n\nexport const CreateSubscriptionRequestSchema: z.ZodType<ParsedCreateSubscriptionRequest> =\n\tz.object({\n\t\tname,\n\t\tsubgraphName: resourceName,\n\t\ttableName: resourceName,\n\t\turl: webhookUrl,\n\t\tfilter: SubscriptionFilterSchema.optional(),\n\t\tformat: SubscriptionFormatSchema.default(\"standard-webhooks\"),\n\t\truntime: SubscriptionRuntimeSchema.nullable().optional(),\n\t\tauthConfig: z.record(z.string(), z.unknown()).optional(),\n\t\tmaxRetries: z.number().int().min(0).max(100).optional(),\n\t\ttimeoutMs: z.number().int().min(100).max(300_000).optional(),\n\t\tconcurrency: z.number().int().min(1).max(100).optional(),\n\t});\n\nexport const UpdateSubscriptionRequestSchema: z.ZodType<UpdateSubscriptionRequest> =\n\tz\n\t\t.object({\n\t\t\tname: name.optional(),\n\t\t\turl: webhookUrl.optional(),\n\t\t\tfilter: SubscriptionFilterSchema.optional(),\n\t\t\tformat: SubscriptionFormatSchema.optional(),\n\t\t\truntime: SubscriptionRuntimeSchema.nullable().optional(),\n\t\t\tauthConfig: z.record(z.string(), z.unknown()).optional(),\n\t\t\tmaxRetries: z.number().int().min(0).max(100).optional(),\n\t\t\ttimeoutMs: z.number().int().min(100).max(300_000).optional(),\n\t\t\tconcurrency: z.number().int().min(1).max(100).optional(),\n\t\t})\n\t\t.refine((value) => Object.keys(value).length > 0, {\n\t\t\tmessage: \"At least one field must be provided\",\n\t\t});\n\nexport const ReplaySubscriptionRequestSchema: z.ZodType<ReplaySubscriptionRequest> =\n\tz\n\t\t.object({\n\t\t\tfromBlock: z.number().int().nonnegative(),\n\t\t\ttoBlock: z.number().int().nonnegative(),\n\t\t\tforce: z.string().trim().min(1).max(64).optional(),\n\t\t})\n\t\t.refine((value) => value.fromBlock <= value.toBlock, {\n\t\t\tmessage: \"fromBlock must be less than or equal to toBlock\",\n\t\t\tpath: [\"toBlock\"],\n\t\t});\n\nexport type SubscriptionStatus = (typeof SUBSCRIPTION_STATUSES)[number];\nexport type SubscriptionFormat = (typeof SUBSCRIPTION_FORMATS)[number];\nexport type SubscriptionRuntime = (typeof SUBSCRIPTION_RUNTIMES)[number];\nexport type SubscriptionFilterPrimitive = string | number | boolean;\nexport type SubscriptionFilterOperator =\n\t| { eq: SubscriptionFilterPrimitive }\n\t| { neq: SubscriptionFilterPrimitive }\n\t| { gt: string | number }\n\t| { gte: string | number }\n\t| { lt: string | number }\n\t| { lte: string | number }\n\t| { in: SubscriptionFilterPrimitive[] };\nexport type SubscriptionFilterClause =\n\t| SubscriptionFilterPrimitive\n\t| SubscriptionFilterOperator;\nexport type SubscriptionFilter = Record<string, SubscriptionFilterClause>;\n\nexport interface CreateSubscriptionRequest {\n\tname: string;\n\tsubgraphName: string;\n\ttableName: string;\n\turl: string;\n\tfilter?: SubscriptionFilter;\n\tformat?: SubscriptionFormat;\n\truntime?: SubscriptionRuntime | null;\n\tauthConfig?: Record<string, unknown>;\n\tmaxRetries?: number;\n\ttimeoutMs?: number;\n\tconcurrency?: number;\n}\n\nexport interface ParsedCreateSubscriptionRequest\n\textends Omit<CreateSubscriptionRequest, \"format\"> {\n\tformat: SubscriptionFormat;\n}\n\nexport interface UpdateSubscriptionRequest {\n\tname?: string;\n\turl?: string;\n\tfilter?: SubscriptionFilter;\n\tformat?: SubscriptionFormat;\n\truntime?: SubscriptionRuntime | null;\n\tauthConfig?: Record<string, unknown>;\n\tmaxRetries?: number;\n\ttimeoutMs?: number;\n\tconcurrency?: number;\n}\n\nexport type ParsedUpdateSubscriptionRequest = UpdateSubscriptionRequest;\n\nexport interface ReplaySubscriptionRequest {\n\tfromBlock: number;\n\ttoBlock: number;\n\tforce?: string;\n}\n\nexport type ParsedReplaySubscriptionRequest = ReplaySubscriptionRequest;\n\nexport interface SubscriptionSummary {\n\tid: string;\n\tname: string;\n\tstatus: SubscriptionStatus;\n\tsubgraphName: string;\n\ttableName: string;\n\tformat: SubscriptionFormat;\n\truntime: SubscriptionRuntime | null;\n\turl: string;\n\tlastDeliveryAt: string | null;\n\tlastSuccessAt: string | null;\n\tcreatedAt: string;\n\tupdatedAt: string;\n}\n\nexport interface SubscriptionDetail extends SubscriptionSummary {\n\tfilter: Record<string, unknown>;\n\tauthConfig: Record<string, unknown>;\n\tmaxRetries: number;\n\ttimeoutMs: number;\n\tconcurrency: number;\n\tcircuitFailures: number;\n\tcircuitOpenedAt: string | null;\n\tlastError: string | null;\n}\n\nexport interface CreateSubscriptionResponse {\n\tsubscription: SubscriptionDetail;\n\t/** Plaintext signing secret — surfaced ONCE. Store it server-side. */\n\tsigningSecret: string;\n}\n\nexport interface RotateSecretResponse {\n\tsubscription: SubscriptionDetail;\n\tsigningSecret: string;\n}\n\nexport interface DeliveryRow {\n\tid: string;\n\tattempt: number;\n\tstatusCode: number | null;\n\terrorMessage: string | null;\n\tdurationMs: number | null;\n\tresponseBody: string | null;\n\tdispatchedAt: string;\n}\n\nexport interface ReplayResult {\n\treplayId: string;\n\tenqueuedCount: number;\n\tscannedCount: number;\n}\n\nexport interface DeadRow {\n\tid: string;\n\teventType: string;\n\tattempt: number;\n\tblockHeight: number;\n\ttxId: string | null;\n\tpayload: Record<string, unknown>;\n\tfailedAt: string | null;\n\tcreatedAt: string;\n}\n\nexport interface SubscriptionSchemaColumn {\n\ttype?: unknown;\n}\n\nexport interface SubscriptionSchemaTable {\n\tcolumns: Record<string, SubscriptionSchemaColumn>;\n}\n\nexport type SubscriptionSchemaTables = Record<string, SubscriptionSchemaTable>;\n\nconst SCALAR_COLUMN_TYPES = new Set([\n\t\"text\",\n\t\"uint\",\n\t\"int\",\n\t\"principal\",\n\t\"boolean\",\n\t\"timestamp\",\n]);\n\nconst COMPARISON_COLUMN_TYPES = new Set([\"uint\", \"int\", \"timestamp\"]);\n\nfunction formatIssuePath(path: PropertyKey[]): string {\n\treturn path.length > 0 ? `${path.map(String).join(\".\")}: ` : \"\";\n}\n\nexport function formatSubscriptionSchemaErrors(error: z.ZodError): string[] {\n\treturn error.issues.map(\n\t\t(issue) => `${formatIssuePath(issue.path)}${issue.message}`,\n\t);\n}\n\nfunction operatorForClause(clause: SubscriptionFilterClause): string {\n\tif (clause === null || typeof clause !== \"object\" || Array.isArray(clause)) {\n\t\treturn \"eq\";\n\t}\n\treturn Object.keys(clause)[0] ?? \"eq\";\n}\n\nexport function validateSubscriptionFilterForTable(input: {\n\tsubgraphName?: string;\n\ttableName: string;\n\tfilter?: unknown;\n\ttables: SubscriptionSchemaTables;\n}): string[] {\n\tconst errors: string[] = [];\n\tconst table = input.tables[input.tableName];\n\tif (!table) {\n\t\tconst names = Object.keys(input.tables);\n\t\terrors.push(\n\t\t\t`Unknown table \"${input.tableName}\"${\n\t\t\t\tinput.subgraphName ? ` in subgraph \"${input.subgraphName}\"` : \"\"\n\t\t\t}.${names.length > 0 ? ` Available tables: ${names.join(\", \")}.` : \"\"}`,\n\t\t);\n\t\treturn errors;\n\t}\n\n\tif (input.filter === undefined) return errors;\n\n\tconst parsed = SubscriptionFilterSchema.safeParse(input.filter);\n\tif (!parsed.success) {\n\t\treturn formatSubscriptionSchemaErrors(parsed.error);\n\t}\n\n\tfor (const [field, clause] of Object.entries(parsed.data)) {\n\t\tconst column = table.columns[field];\n\t\tif (!column) {\n\t\t\terrors.push(\n\t\t\t\t`Unknown filter field \"${field}\" on table \"${input.tableName}\".`,\n\t\t\t);\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst columnType =\n\t\t\ttypeof column.type === \"string\" ? column.type.toLowerCase() : \"\";\n\t\tif (!SCALAR_COLUMN_TYPES.has(columnType)) {\n\t\t\terrors.push(\n\t\t\t\t`Filter field \"${field}\" has unsupported type \"${columnType || \"unknown\"}\"; subscription filters require scalar columns.`,\n\t\t\t);\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst operator = operatorForClause(clause);\n\t\tif (\n\t\t\t(operator === \"gt\" ||\n\t\t\t\toperator === \"gte\" ||\n\t\t\t\toperator === \"lt\" ||\n\t\t\t\toperator === \"lte\") &&\n\t\t\t!COMPARISON_COLUMN_TYPES.has(columnType)\n\t\t) {\n\t\t\terrors.push(\n\t\t\t\t`Operator \"${operator}\" is not supported for ${columnType} field \"${field}\".`,\n\t\t\t);\n\t\t}\n\t}\n\n\treturn errors;\n}\n",
14
14
  "import type { SubgraphDetail } from \"../schemas/subgraphs.ts\";\n\nexport type SubgraphSpecFormat = \"openapi\" | \"agent\" | \"markdown\";\n\nexport interface SubgraphSpecOptions {\n\tserverUrl?: string;\n\tgeneratedAt?: string;\n}\n\nexport interface SubgraphAgentSchema {\n\tname: string;\n\tversion: string;\n\tdescription?: string;\n\tschemaHash?: string;\n\tgeneratedAt: string;\n\tserverUrl: string;\n\tsources?: Record<string, unknown>;\n\ttables: Record<\n\t\tstring,\n\t\t{\n\t\t\tendpoint: string;\n\t\t\tcountEndpoint: string;\n\t\t\trowCount: number;\n\t\t\tcolumns: SubgraphDetail[\"tables\"][string][\"columns\"];\n\t\t\tindexes?: string[][];\n\t\t\tuniqueKeys?: string[][];\n\t\t\tquery: {\n\t\t\t\tparameters: string[];\n\t\t\t\tsortable: string[];\n\t\t\t\tselectable: string[];\n\t\t\t\tsearchable: string[];\n\t\t\t\tfilters: string[];\n\t\t\t};\n\t\t\texamples: {\n\t\t\t\tlist: Record<string, unknown>;\n\t\t\t\tcount: { count: number };\n\t\t\t\tcurl: string;\n\t\t\t};\n\t\t}\n\t>;\n}\n\ntype ColumnMeta = SubgraphDetail[\"tables\"][string][\"columns\"][string];\n\nconst SYSTEM_COLUMNS = [\"_id\", \"_block_height\", \"_tx_id\", \"_created_at\"];\nconst BASE_QUERY_PARAMS = [\"_limit\", \"_offset\", \"_sort\", \"_order\", \"_fields\"];\nconst COMPARISON_OPS = [\"neq\", \"gt\", \"gte\", \"lt\", \"lte\"];\n\nfunction generatedAt(options: SubgraphSpecOptions): string {\n\treturn options.generatedAt ?? new Date().toISOString();\n}\n\nfunction normalizeServerUrl(serverUrl?: string): string {\n\treturn (serverUrl ?? \"https://api.secondlayer.tools\").replace(/\\/+$/, \"\");\n}\n\nfunction tablePath(subgraphName: string, tableName: string): string {\n\treturn `/api/subgraphs/${subgraphName}/${tableName}`;\n}\n\nfunction countPath(subgraphName: string, tableName: string): string {\n\treturn `${tablePath(subgraphName, tableName)}/count`;\n}\n\nfunction isTextLike(type: string): boolean {\n\treturn type === \"text\" || type === \"principal\" || type === \"timestamp\";\n}\n\nfunction isComparable(type: string): boolean {\n\treturn (\n\t\ttype === \"uint\" ||\n\t\ttype === \"int\" ||\n\t\ttype === \"bigint\" ||\n\t\ttype === \"serial\" ||\n\t\ttype === \"timestamp\"\n\t);\n}\n\nfunction exampleForColumn(type: string): unknown {\n\tswitch (type) {\n\t\tcase \"uint\":\n\t\tcase \"int\":\n\t\tcase \"bigint\":\n\t\t\treturn \"1000\";\n\t\tcase \"serial\":\n\t\t\treturn 1;\n\t\tcase \"principal\":\n\t\t\treturn \"SP000000000000000000002Q6VF78\";\n\t\tcase \"timestamp\":\n\t\t\treturn \"2026-01-01T00:00:00.000Z\";\n\t\tcase \"boolean\":\n\t\t\treturn true;\n\t\tcase \"jsonb\":\n\t\t\treturn { example: true };\n\t\tdefault:\n\t\t\treturn \"example\";\n\t}\n}\n\nfunction openApiSchemaForColumn(col: ColumnMeta): Record<string, unknown> {\n\tlet schema: Record<string, unknown>;\n\tswitch (col.type) {\n\t\tcase \"uint\":\n\t\tcase \"int\":\n\t\tcase \"bigint\":\n\t\t\tschema = { type: \"string\", pattern: \"^-?\\\\d+(\\\\.\\\\d+)?$\" };\n\t\t\tbreak;\n\t\tcase \"serial\":\n\t\t\tschema = { type: \"integer\" };\n\t\t\tbreak;\n\t\tcase \"principal\":\n\t\tcase \"text\":\n\t\t\tschema = { type: \"string\" };\n\t\t\tbreak;\n\t\tcase \"timestamp\":\n\t\t\tschema = { type: \"string\", format: \"date-time\" };\n\t\t\tbreak;\n\t\tcase \"boolean\":\n\t\t\tschema = { type: \"boolean\" };\n\t\t\tbreak;\n\t\tcase \"jsonb\":\n\t\t\tschema = { type: \"object\", additionalProperties: true };\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tschema = {};\n\t\t\tbreak;\n\t}\n\tif (col.nullable) {\n\t\tconst type = schema.type;\n\t\tif (typeof type === \"string\") schema.type = [type, \"null\"];\n\t}\n\treturn schema;\n}\n\nfunction columnEntries(table: SubgraphDetail[\"tables\"][string]) {\n\treturn Object.entries(table.columns);\n}\n\nfunction selectableColumns(table: SubgraphDetail[\"tables\"][string]): string[] {\n\treturn columnEntries(table).map(([name]) => name);\n}\n\nfunction searchableColumns(table: SubgraphDetail[\"tables\"][string]): string[] {\n\treturn columnEntries(table)\n\t\t.filter(([, col]) => col.searchable)\n\t\t.map(([name]) => name);\n}\n\nfunction filterNames(table: SubgraphDetail[\"tables\"][string]): string[] {\n\tconst result: string[] = [];\n\tfor (const [name, col] of columnEntries(table)) {\n\t\tresult.push(name);\n\t\tresult.push(`${name}.neq`);\n\t\tif (isComparable(col.type)) {\n\t\t\tfor (const op of COMPARISON_OPS.filter((op) => op !== \"neq\")) {\n\t\t\t\tresult.push(`${name}.${op}`);\n\t\t\t}\n\t\t}\n\t\tif (isTextLike(col.type)) result.push(`${name}.like`);\n\t}\n\treturn result;\n}\n\nfunction queryParameters(table: SubgraphDetail[\"tables\"][string]): string[] {\n\tconst params = [...BASE_QUERY_PARAMS];\n\tif (searchableColumns(table).length > 0) params.push(\"_search\");\n\treturn params;\n}\n\nfunction rowExample(table: SubgraphDetail[\"tables\"][string]) {\n\tconst row: Record<string, unknown> = {};\n\tfor (const [name, col] of columnEntries(table)) {\n\t\trow[name] = exampleForColumn(col.type);\n\t}\n\treturn row;\n}\n\nfunction openApiParameter(\n\tname: string,\n\tdescription: string,\n\tschema: Record<string, unknown> = { type: \"string\" },\n) {\n\treturn {\n\t\tname,\n\t\tin: \"query\",\n\t\trequired: false,\n\t\tdescription,\n\t\tschema,\n\t};\n}\n\nfunction tableParameters(table: SubgraphDetail[\"tables\"][string]) {\n\tconst parameters = [\n\t\topenApiParameter(\"_limit\", \"Maximum rows to return.\", {\n\t\t\ttype: \"integer\",\n\t\t\tdefault: 50,\n\t\t\tminimum: 1,\n\t\t\tmaximum: 1000,\n\t\t}),\n\t\topenApiParameter(\"_offset\", \"Rows to skip for pagination.\", {\n\t\t\ttype: \"integer\",\n\t\t\tdefault: 0,\n\t\t\tminimum: 0,\n\t\t}),\n\t\topenApiParameter(\"_sort\", \"Column to sort by.\", {\n\t\t\ttype: \"string\",\n\t\t\tenum: selectableColumns(table),\n\t\t}),\n\t\topenApiParameter(\"_order\", \"Sort direction.\", {\n\t\t\ttype: \"string\",\n\t\t\tenum: [\"asc\", \"desc\"],\n\t\t\tdefault: \"asc\",\n\t\t}),\n\t\topenApiParameter(\"_fields\", \"Comma-separated columns to include.\", {\n\t\t\ttype: \"string\",\n\t\t}),\n\t];\n\tif (searchableColumns(table).length > 0) {\n\t\tparameters.push(\n\t\t\topenApiParameter(\"_search\", \"Search across searchable columns.\", {\n\t\t\t\ttype: \"string\",\n\t\t\t}),\n\t\t);\n\t}\n\tfor (const [name, col] of columnEntries(table)) {\n\t\tparameters.push(\n\t\t\topenApiParameter(name, `Filter ${name} by equality.`, {\n\t\t\t\ttype: \"string\",\n\t\t\t}),\n\t\t);\n\t\tparameters.push(\n\t\t\topenApiParameter(`${name}.neq`, `Filter ${name} by inequality.`, {\n\t\t\t\ttype: \"string\",\n\t\t\t}),\n\t\t);\n\t\tif (isComparable(col.type)) {\n\t\t\tfor (const op of [\"gt\", \"gte\", \"lt\", \"lte\"]) {\n\t\t\t\tparameters.push(\n\t\t\t\t\topenApiParameter(`${name}.${op}`, `Filter ${name} with ${op}.`, {\n\t\t\t\t\t\ttype: \"string\",\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t\tif (isTextLike(col.type)) {\n\t\t\tparameters.push(\n\t\t\t\topenApiParameter(\n\t\t\t\t\t`${name}.like`,\n\t\t\t\t\t`Case-insensitive contains filter for ${name}.`,\n\t\t\t\t\t{\n\t\t\t\t\t\ttype: \"string\",\n\t\t\t\t\t},\n\t\t\t\t),\n\t\t\t);\n\t\t}\n\t}\n\treturn parameters;\n}\n\nexport function generateSubgraphAgentSchema(\n\tdetail: SubgraphDetail,\n\toptions: SubgraphSpecOptions = {},\n): SubgraphAgentSchema {\n\tconst serverUrl = normalizeServerUrl(options.serverUrl);\n\tconst tables: SubgraphAgentSchema[\"tables\"] = {};\n\tfor (const [tableName, table] of Object.entries(detail.tables)) {\n\t\tconst path = tablePath(detail.name, tableName);\n\t\ttables[tableName] = {\n\t\t\tendpoint: `${serverUrl}${path}`,\n\t\t\tcountEndpoint: `${serverUrl}${countPath(detail.name, tableName)}`,\n\t\t\trowCount: table.rowCount,\n\t\t\tcolumns: table.columns,\n\t\t\t...(table.indexes ? { indexes: table.indexes } : {}),\n\t\t\t...(table.uniqueKeys ? { uniqueKeys: table.uniqueKeys } : {}),\n\t\t\tquery: {\n\t\t\t\tparameters: queryParameters(table),\n\t\t\t\tsortable: selectableColumns(table),\n\t\t\t\tselectable: selectableColumns(table),\n\t\t\t\tsearchable: searchableColumns(table),\n\t\t\t\tfilters: filterNames(table),\n\t\t\t},\n\t\t\texamples: {\n\t\t\t\tlist: rowExample(table),\n\t\t\t\tcount: { count: table.rowCount },\n\t\t\t\tcurl: `curl '${serverUrl}${path}?_limit=10&_sort=_block_height&_order=desc'`,\n\t\t\t},\n\t\t};\n\t}\n\treturn {\n\t\tname: detail.name,\n\t\tversion: detail.version,\n\t\t...(detail.description ? { description: detail.description } : {}),\n\t\t...(detail.schemaHash ? { schemaHash: detail.schemaHash } : {}),\n\t\tgeneratedAt: generatedAt(options),\n\t\tserverUrl,\n\t\t...(detail.sources ? { sources: detail.sources } : {}),\n\t\ttables,\n\t};\n}\n\nexport function generateSubgraphOpenApi(\n\tdetail: SubgraphDetail,\n\toptions: SubgraphSpecOptions = {},\n): Record<string, unknown> {\n\tconst serverUrl = normalizeServerUrl(options.serverUrl);\n\tconst paths: Record<string, unknown> = {};\n\tconst schemas: Record<string, unknown> = {};\n\n\tfor (const [tableName, table] of Object.entries(detail.tables)) {\n\t\tconst schemaName = `${tableName}Row`;\n\t\tconst properties: Record<string, unknown> = {};\n\t\tconst required: string[] = [];\n\t\tfor (const [columnName, column] of columnEntries(table)) {\n\t\t\tproperties[columnName] = openApiSchemaForColumn(column);\n\t\t\tif (!column.nullable) required.push(columnName);\n\t\t}\n\t\tschemas[schemaName] = {\n\t\t\ttype: \"object\",\n\t\t\tproperties,\n\t\t\trequired,\n\t\t\texample: rowExample(table),\n\t\t};\n\n\t\tpaths[tablePath(detail.name, tableName)] = {\n\t\t\tget: {\n\t\t\t\tsummary: `Query ${detail.name}.${tableName}`,\n\t\t\t\toperationId: `query_${detail.name.replace(/-/g, \"_\")}_${tableName}`,\n\t\t\t\tparameters: tableParameters(table),\n\t\t\t\tresponses: {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\tdescription: \"Rows returned from the subgraph table.\",\n\t\t\t\t\t\tcontent: {\n\t\t\t\t\t\t\t\"application/json\": {\n\t\t\t\t\t\t\t\tschema: {\n\t\t\t\t\t\t\t\t\ttype: \"object\",\n\t\t\t\t\t\t\t\t\tproperties: {\n\t\t\t\t\t\t\t\t\t\tdata: {\n\t\t\t\t\t\t\t\t\t\t\ttype: \"array\",\n\t\t\t\t\t\t\t\t\t\t\titems: { $ref: `#/components/schemas/${schemaName}` },\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\tmeta: {\n\t\t\t\t\t\t\t\t\t\t\ttype: \"object\",\n\t\t\t\t\t\t\t\t\t\t\tproperties: {\n\t\t\t\t\t\t\t\t\t\t\t\ttotal: { type: \"integer\" },\n\t\t\t\t\t\t\t\t\t\t\t\tlimit: { type: \"integer\" },\n\t\t\t\t\t\t\t\t\t\t\t\toffset: { type: \"integer\" },\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t};\n\n\t\tpaths[countPath(detail.name, tableName)] = {\n\t\t\tget: {\n\t\t\t\tsummary: `Count ${detail.name}.${tableName}`,\n\t\t\t\toperationId: `count_${detail.name.replace(/-/g, \"_\")}_${tableName}`,\n\t\t\t\tparameters: tableParameters(table),\n\t\t\t\tresponses: {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\tdescription: \"Row count for the filtered table query.\",\n\t\t\t\t\t\tcontent: {\n\t\t\t\t\t\t\t\"application/json\": {\n\t\t\t\t\t\t\t\tschema: {\n\t\t\t\t\t\t\t\t\ttype: \"object\",\n\t\t\t\t\t\t\t\t\tproperties: { count: { type: \"integer\" } },\n\t\t\t\t\t\t\t\t\trequired: [\"count\"],\n\t\t\t\t\t\t\t\t\texample: { count: table.rowCount },\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t};\n\t}\n\n\treturn {\n\t\topenapi: \"3.1.0\",\n\t\tinfo: {\n\t\t\ttitle: `${detail.name} Subgraph API`,\n\t\t\tversion: detail.version,\n\t\t\t...(detail.description ? { description: detail.description } : {}),\n\t\t},\n\t\tservers: [{ url: serverUrl }],\n\t\tpaths,\n\t\tcomponents: { schemas },\n\t\t\"x-secondlayer-subgraph\": detail.name,\n\t\t\"x-secondlayer-version\": detail.version,\n\t\t\"x-secondlayer-schema-hash\": detail.schemaHash,\n\t\t\"x-secondlayer-generated-at\": generatedAt(options),\n\t\t\"x-secondlayer-sources\": detail.sources ?? {},\n\t\t\"x-secondlayer-tables\": Object.keys(detail.tables),\n\t};\n}\n\nexport function generateSubgraphMarkdown(\n\tdetail: SubgraphDetail,\n\toptions: SubgraphSpecOptions = {},\n): string {\n\tconst agent = generateSubgraphAgentSchema(detail, options);\n\tconst lines = [\n\t\t`# ${detail.name} Subgraph API`,\n\t\t\"\",\n\t\t`Version: ${detail.version}`,\n\t\tdetail.schemaHash ? `Schema hash: ${detail.schemaHash}` : undefined,\n\t\t`Server: ${agent.serverUrl}`,\n\t\t\"\",\n\t\tdetail.description,\n\t].filter((line): line is string => line !== undefined && line !== \"\");\n\n\tfor (const [tableName, table] of Object.entries(agent.tables)) {\n\t\tlines.push(\n\t\t\t\"\",\n\t\t\t`## ${tableName}`,\n\t\t\t\"\",\n\t\t\t`GET ${table.endpoint}`,\n\t\t\t`GET ${table.countEndpoint}`,\n\t\t\t\"\",\n\t\t\t`Rows: ${table.rowCount}`,\n\t\t\t\"\",\n\t\t\t\"### Columns\",\n\t\t\t\"\",\n\t\t\t\"| Column | Type | Attributes |\",\n\t\t\t\"| --- | --- | --- |\",\n\t\t);\n\t\tfor (const [columnName, col] of Object.entries(table.columns)) {\n\t\t\tconst attrs = [\n\t\t\t\tSYSTEM_COLUMNS.includes(columnName) ? \"system\" : undefined,\n\t\t\t\tcol.nullable ? \"nullable\" : undefined,\n\t\t\t\tcol.indexed ? \"indexed\" : undefined,\n\t\t\t\tcol.searchable ? \"searchable\" : undefined,\n\t\t\t]\n\t\t\t\t.filter(Boolean)\n\t\t\t\t.join(\", \");\n\t\t\tlines.push(`| \\`${columnName}\\` | \\`${col.type}\\` | ${attrs || \"-\"} |`);\n\t\t}\n\t\tlines.push(\n\t\t\t\"\",\n\t\t\t\"### Query\",\n\t\t\t\"\",\n\t\t\t`Parameters: ${table.query.parameters.map((p) => `\\`${p}\\``).join(\", \")}`,\n\t\t\t`Filters: ${table.query.filters.map((p) => `\\`${p}\\``).join(\", \")}`,\n\t\t\t\"\",\n\t\t\t\"### Example\",\n\t\t\t\"\",\n\t\t\t\"```bash\",\n\t\t\ttable.examples.curl,\n\t\t\t\"```\",\n\t\t);\n\t}\n\treturn `${lines.join(\"\\n\")}\\n`;\n}\n\nexport function generateSubgraphSpec(\n\tdetail: SubgraphDetail,\n\tformat: SubgraphSpecFormat,\n\toptions: SubgraphSpecOptions = {},\n): Record<string, unknown> | SubgraphAgentSchema | string {\n\tif (format === \"openapi\") return generateSubgraphOpenApi(detail, options);\n\tif (format === \"agent\") return generateSubgraphAgentSchema(detail, options);\n\treturn generateSubgraphMarkdown(detail, options);\n}\n",
15
15
  "import { createHmac, randomBytes } from \"node:crypto\";\n\n/**\n * Generate a random secret for delivery signing\n * Returns 32 bytes as a 64-character hex string\n */\nexport function generateSecret(): string {\n\treturn randomBytes(32).toString(\"hex\");\n}\n\n/**\n * Sign a payload with HMAC-SHA256\n * Returns the signature as a hex string\n */\nexport function signPayload(payload: string, secret: string): string {\n\tconst hmac = createHmac(\"sha256\", secret);\n\thmac.update(payload);\n\treturn hmac.digest(\"hex\");\n}\n\n/**\n * Verify an HMAC signature\n * Uses constant-time comparison to prevent timing attacks\n */\nexport function verifySignature(\n\tpayload: string,\n\tsignature: string,\n\tsecret: string,\n): boolean {\n\tconst expectedSignature = signPayload(payload, secret);\n\n\t// Constant-time comparison\n\tif (signature.length !== expectedSignature.length) {\n\t\treturn false;\n\t}\n\n\tlet result = 0;\n\tfor (let i = 0; i < signature.length; i++) {\n\t\tresult |= signature.charCodeAt(i) ^ expectedSignature.charCodeAt(i);\n\t}\n\n\treturn result === 0;\n}\n\n/**\n * Create a Stripe-style signature header\n * Format: t=timestamp,v1=signature\n */\nexport function createSignatureHeader(\n\tpayload: string,\n\tsecret: string,\n\ttimestamp?: number,\n): string {\n\tconst ts = timestamp ?? Math.floor(Date.now() / 1000);\n\tconst signedPayload = `${ts}.${payload}`;\n\tconst signature = signPayload(signedPayload, secret);\n\n\treturn `t=${ts},v1=${signature}`;\n}\n\n/**\n * Parse and verify a Stripe-style signature header\n * Returns true if valid, false otherwise\n */\nexport function verifySignatureHeader(\n\tpayload: string,\n\theader: string,\n\tsecret: string,\n\ttoleranceSeconds = 300, // 5 minutes\n): boolean {\n\t// Parse header\n\tconst parts = header.split(\",\");\n\tconst timestamp = parts.find((p) => p.startsWith(\"t=\"))?.slice(2);\n\tconst signature = parts.find((p) => p.startsWith(\"v1=\"))?.slice(3);\n\n\tif (!timestamp || !signature) {\n\t\treturn false;\n\t}\n\n\tconst ts = Number.parseInt(timestamp, 10);\n\tif (Number.isNaN(ts)) {\n\t\treturn false;\n\t}\n\n\t// Check timestamp is within tolerance\n\tconst now = Math.floor(Date.now() / 1000);\n\tif (Math.abs(now - ts) > toleranceSeconds) {\n\t\treturn false;\n\t}\n\n\t// Verify signature\n\tconst signedPayload = `${ts}.${payload}`;\n\treturn verifySignature(signedPayload, signature, secret);\n}\n"
@@ -268,6 +268,11 @@ interface ChatMessagesTable {
268
268
  metadata: unknown | null;
269
269
  created_at: Generated<Date>;
270
270
  }
271
+ interface ProcessedStripeEventsTable {
272
+ event_id: string;
273
+ event_type: string;
274
+ processed_at: Generated<Date>;
275
+ }
271
276
  interface Database {
272
277
  blocks: BlocksTable;
273
278
  transactions: TransactionsTable;
@@ -294,6 +299,7 @@ interface Database {
294
299
  team_invitations: TeamInvitationsTable;
295
300
  chat_sessions: ChatSessionsTable;
296
301
  chat_messages: ChatMessagesTable;
302
+ processed_stripe_events: ProcessedStripeEventsTable;
297
303
  tenants: TenantsTable;
298
304
  tenant_usage_monthly: TenantUsageMonthlyTable;
299
305
  tenant_compute_addons: TenantComputeAddonsTable;
@@ -10,7 +10,7 @@
10
10
  * Adding a tier? Add an entry to PLANS. Removing one? Drop here, run
11
11
  * the Stripe-side cleanup (archive lookup_key), and update env vars.
12
12
  */
13
- declare const BYTES_PER_GB: unknown;
13
+ declare const BYTES_PER_GB: number;
14
14
  type PlanId = "hobby" | "launch" | "scale" | "enterprise";
15
15
  interface ContainerAlloc {
16
16
  memoryMb: number;
@@ -67,7 +67,8 @@ var PLANS = {
67
67
  features: [
68
68
  "0.5 vCPU · 512 MB RAM",
69
69
  "5 GB storage · auto-pause 7d",
70
- "Subgraphs + subscriptions",
70
+ "Starter subgraphs + subscriptions",
71
+ "Recent-range reindexing",
71
72
  "Community support"
72
73
  ],
73
74
  stripeLookupKey: null
@@ -85,6 +86,7 @@ var PLANS = {
85
86
  "1 vCPU · 2 GB RAM",
86
87
  "25 GB storage · always-on",
87
88
  "Unlimited subgraphs + subscriptions",
89
+ "Larger reindex windows",
88
90
  "Spend caps + alerts",
89
91
  "Email support"
90
92
  ],
@@ -175,5 +177,5 @@ export {
175
177
  BYTES_PER_GB
176
178
  };
177
179
 
178
- //# debugId=79B3993D9A501BD864756E2164756E21
180
+ //# debugId=33E32BD4F4A5ECD864756E2164756E21
179
181
  //# sourceMappingURL=pricing.js.map
@@ -2,9 +2,9 @@
2
2
  "version": 3,
3
3
  "sources": ["../src/pricing.ts"],
4
4
  "sourcesContent": [
5
- "/**\n * Single source of truth for plan tiers — capacity, price, display copy,\n * Stripe binding, and container allocations.\n *\n * Consumed by:\n * - Provisioner (`packages/provisioner/src/plans.ts` re-exports)\n * - API (`/api/accounts/usage` for allowance math + display)\n * - Web app (`/billing` page renders plan cards from this)\n *\n * Adding a tier? Add an entry to PLANS. Removing one? Drop here, run\n * the Stripe-side cleanup (archive lookup_key), and update env vars.\n */\n\nconst BYTES_PER_GB = 1024 ** 3;\n\nexport type PlanId = \"hobby\" | \"launch\" | \"scale\" | \"enterprise\";\n\nexport interface ContainerAlloc {\n\tmemoryMb: number;\n\tcpus: number;\n}\n\nexport interface Plan {\n\tid: PlanId;\n\tdisplayName: string;\n\t/** Monthly subscription price in cents. null = custom (Enterprise). */\n\tmonthlyPriceCents: number | null;\n\ttotalCpus: number;\n\ttotalMemoryMb: number;\n\t/** Hard cap. -1 = unlimited (Enterprise). Storage overage bills past this. */\n\tstorageLimitMb: number;\n\tcontainers: {\n\t\tpostgres: ContainerAlloc;\n\t\tapi: ContainerAlloc;\n\t\tprocessor: ContainerAlloc;\n\t};\n\t/** Display-only. Marketing/short pitch. */\n\ttagline: string;\n\t/** Display-only. Bullet list on the plan card. */\n\tfeatures: string[];\n\t/** Stripe `lookup_key` for the recurring tier price. null for hobby/enterprise. */\n\tstripeLookupKey: string | null;\n}\n\n// ── Allocation helpers ──────────────────────────────────────────────\n//\n// Allocation within a plan (3 containers per tenant):\n// Default split (paid tiers) — PG 50% / proc 30% / api 20%\n// Sub-1GB total (Hobby) — PG 60% / proc 25% / api 15%\n//\n// Biased toward PG on Hobby because PG 17's default `shared_buffers` is\n// 128MB — a naive 50/30/20 split on 512MB leaves PG with 256MB RAM, which\n// is technically workable but crashes if `shared_buffers` isn't also\n// shrunk. The 60/25/15 split gives PG 307MB, more headroom.\n//\n// Docker memory limit is a hard cap (OOM kill on overage). CPU is a soft\n// cap via `--cpus` (throttling, not killing). Storage is monitored\n// separately and billed as overage — PG crashes if we hard-cap it.\n\nfunction alloc(totalMb: number, totalCpus: number): Plan[\"containers\"] {\n\treturn {\n\t\tpostgres: {\n\t\t\tmemoryMb: Math.floor(totalMb * 0.5),\n\t\t\tcpus: round2(totalCpus * 0.5),\n\t\t},\n\t\tprocessor: {\n\t\t\tmemoryMb: Math.floor(totalMb * 0.3),\n\t\t\tcpus: round2(totalCpus * 0.3),\n\t\t},\n\t\tapi: {\n\t\t\tmemoryMb: Math.floor(totalMb * 0.2),\n\t\t\tcpus: round2(totalCpus * 0.2),\n\t\t},\n\t};\n}\n\nfunction allocTight(totalMb: number, totalCpus: number): Plan[\"containers\"] {\n\treturn {\n\t\tpostgres: {\n\t\t\tmemoryMb: Math.floor(totalMb * 0.6),\n\t\t\tcpus: round2(totalCpus * 0.6),\n\t\t},\n\t\tprocessor: {\n\t\t\tmemoryMb: Math.floor(totalMb * 0.25),\n\t\t\tcpus: round2(totalCpus * 0.25),\n\t\t},\n\t\tapi: {\n\t\t\tmemoryMb: Math.floor(totalMb * 0.15),\n\t\t\tcpus: round2(totalCpus * 0.15),\n\t\t},\n\t};\n}\n\nfunction round2(n: number): number {\n\treturn Math.round(n * 100) / 100;\n}\n\n/**\n * Split a compute envelope across (postgres, processor, api) containers.\n * Auto-biases PG-heavy (60/25/15) for sub-1GB totals.\n */\nexport function allocForTotals(\n\ttotalMemoryMb: number,\n\ttotalCpus: number,\n): Plan[\"containers\"] {\n\treturn totalMemoryMb < 1024\n\t\t? allocTight(totalMemoryMb, totalCpus)\n\t\t: alloc(totalMemoryMb, totalCpus);\n}\n\n// ── Canonical plan data ─────────────────────────────────────────────\n\nexport const PLANS: Record<PlanId, Plan> = {\n\thobby: {\n\t\tid: \"hobby\",\n\t\tdisplayName: \"Hobby\",\n\t\tmonthlyPriceCents: 0,\n\t\ttotalCpus: 0.5,\n\t\ttotalMemoryMb: 512,\n\t\tstorageLimitMb: 5_120,\n\t\tcontainers: allocTight(512, 0.5),\n\t\ttagline: \"Free, forever\",\n\t\tfeatures: [\n\t\t\t\"0.5 vCPU · 512 MB RAM\",\n\t\t\t\"5 GB storage · auto-pause 7d\",\n\t\t\t\"Subgraphs + subscriptions\",\n\t\t\t\"Community support\",\n\t\t],\n\t\tstripeLookupKey: null,\n\t},\n\tlaunch: {\n\t\tid: \"launch\",\n\t\tdisplayName: \"Launch\",\n\t\tmonthlyPriceCents: 5_000, // $50\n\t\ttotalCpus: 1,\n\t\ttotalMemoryMb: 2_048,\n\t\tstorageLimitMb: 25_600, // 25 GB\n\t\tcontainers: alloc(2_048, 1),\n\t\ttagline: \"Production-ready\",\n\t\tfeatures: [\n\t\t\t\"1 vCPU · 2 GB RAM\",\n\t\t\t\"25 GB storage · always-on\",\n\t\t\t\"Unlimited subgraphs + subscriptions\",\n\t\t\t\"Spend caps + alerts\",\n\t\t\t\"Email support\",\n\t\t],\n\t\tstripeLookupKey: \"secondlayer_launch_monthly\",\n\t},\n\tscale: {\n\t\tid: \"scale\",\n\t\tdisplayName: \"Scale\",\n\t\tmonthlyPriceCents: 20_000, // $200\n\t\ttotalCpus: 4,\n\t\ttotalMemoryMb: 8_192,\n\t\tstorageLimitMb: 102_400, // 100 GB\n\t\tcontainers: alloc(8_192, 4),\n\t\ttagline: \"Scale with confidence\",\n\t\tfeatures: [\n\t\t\t\"4 vCPU · 8 GB RAM\",\n\t\t\t\"100 GB storage · always-on\",\n\t\t\t\"Higher throughput + replay\",\n\t\t\t\"24h SLA · priority support\",\n\t\t],\n\t\tstripeLookupKey: \"secondlayer_scale_monthly\",\n\t},\n\tenterprise: {\n\t\tid: \"enterprise\",\n\t\tdisplayName: \"Enterprise\",\n\t\tmonthlyPriceCents: null,\n\t\ttotalCpus: 8,\n\t\ttotalMemoryMb: 32_768,\n\t\tstorageLimitMb: -1,\n\t\tcontainers: alloc(32_768, 8),\n\t\ttagline: \"Custom workloads\",\n\t\tfeatures: [\n\t\t\t\"Custom compute + storage\",\n\t\t\t\"SLAs · regions · SSO\",\n\t\t\t\"Dedicated success engineer\",\n\t\t],\n\t\tstripeLookupKey: null,\n\t},\n};\n\nexport const PLAN_IDS: readonly PlanId[] = [\n\t\"hobby\",\n\t\"launch\",\n\t\"scale\",\n\t\"enterprise\",\n];\n\nexport function getPlan(id: string): Plan {\n\tconst plan = (PLANS as Record<string, Plan | undefined>)[id];\n\tif (!plan) throw new Error(`Unknown plan: ${id}`);\n\treturn plan;\n}\n\nexport function isValidPlanId(id: string): id is PlanId {\n\treturn id in PLANS;\n}\n\n// ── Allowance helpers (used by /api/accounts/usage display) ─────────\n//\n// Compute is hard-capped by Docker `--cpus`, so there's no compute\n// overage billing. The function below returns ∞ for paid plans (display-\n// only — no metering).\n//\n// Storage IS metered and billed past the plan's allowance via the\n// `storage_gb_months` Stripe meter at $2/GB-mo.\n\nexport function getComputeAllowanceHours(_plan: string): number {\n\t// Compute overage was killed when we removed the `compute_hours` meter.\n\t// All plans are now hard-capped by Docker `--cpus`. ∞ here means \"no\n\t// overage tracked\" for display purposes.\n\treturn Number.POSITIVE_INFINITY;\n}\n\nexport function getStorageAllowanceBytes(plan: string): number {\n\tconst planDef = (PLANS as Record<string, Plan | undefined>)[plan];\n\tif (!planDef) return PLANS.hobby.storageLimitMb * 1024 * 1024;\n\tif (planDef.storageLimitMb < 0) return Number.POSITIVE_INFINITY;\n\treturn planDef.storageLimitMb * 1024 * 1024;\n}\n\n/** Hobby has a hard cap (no overage billing); paid tiers bill $2/GB over allowance. */\nexport function hasStorageOverage(plan: string): boolean {\n\treturn plan !== \"hobby\" && plan !== \"enterprise\";\n}\n\nexport function getBasePriceCents(plan: string): number {\n\tconst planDef = (PLANS as Record<string, Plan | undefined>)[plan];\n\treturn planDef?.monthlyPriceCents ?? 0;\n}\n\nexport function getPlanDisplayName(plan: string): string {\n\tconst planDef = (PLANS as Record<string, Plan | undefined>)[plan];\n\treturn planDef?.displayName ?? plan.charAt(0).toUpperCase() + plan.slice(1);\n}\n\n// Re-export bytes-per-GB constant for callers that compute display values.\nexport { BYTES_PER_GB };\n"
5
+ "/**\n * Single source of truth for plan tiers — capacity, price, display copy,\n * Stripe binding, and container allocations.\n *\n * Consumed by:\n * - Provisioner (`packages/provisioner/src/plans.ts` re-exports)\n * - API (`/api/accounts/usage` for allowance math + display)\n * - Web app (`/billing` page renders plan cards from this)\n *\n * Adding a tier? Add an entry to PLANS. Removing one? Drop here, run\n * the Stripe-side cleanup (archive lookup_key), and update env vars.\n */\n\nconst BYTES_PER_GB: number = 1024 ** 3;\n\nexport type PlanId = \"hobby\" | \"launch\" | \"scale\" | \"enterprise\";\n\nexport interface ContainerAlloc {\n\tmemoryMb: number;\n\tcpus: number;\n}\n\nexport interface Plan {\n\tid: PlanId;\n\tdisplayName: string;\n\t/** Monthly subscription price in cents. null = custom (Enterprise). */\n\tmonthlyPriceCents: number | null;\n\ttotalCpus: number;\n\ttotalMemoryMb: number;\n\t/** Hard cap. -1 = unlimited (Enterprise). Storage overage bills past this. */\n\tstorageLimitMb: number;\n\tcontainers: {\n\t\tpostgres: ContainerAlloc;\n\t\tapi: ContainerAlloc;\n\t\tprocessor: ContainerAlloc;\n\t};\n\t/** Display-only. Marketing/short pitch. */\n\ttagline: string;\n\t/** Display-only. Bullet list on the plan card. */\n\tfeatures: string[];\n\t/** Stripe `lookup_key` for the recurring tier price. null for hobby/enterprise. */\n\tstripeLookupKey: string | null;\n}\n\n// ── Allocation helpers ──────────────────────────────────────────────\n//\n// Allocation within a plan (3 containers per tenant):\n// Default split (paid tiers) — PG 50% / proc 30% / api 20%\n// Sub-1GB total (Hobby) — PG 60% / proc 25% / api 15%\n//\n// Biased toward PG on Hobby because PG 17's default `shared_buffers` is\n// 128MB — a naive 50/30/20 split on 512MB leaves PG with 256MB RAM, which\n// is technically workable but crashes if `shared_buffers` isn't also\n// shrunk. The 60/25/15 split gives PG 307MB, more headroom.\n//\n// Docker memory limit is a hard cap (OOM kill on overage). CPU is a soft\n// cap via `--cpus` (throttling, not killing). Storage is monitored\n// separately and billed as overage — PG crashes if we hard-cap it.\n\nfunction alloc(totalMb: number, totalCpus: number): Plan[\"containers\"] {\n\treturn {\n\t\tpostgres: {\n\t\t\tmemoryMb: Math.floor(totalMb * 0.5),\n\t\t\tcpus: round2(totalCpus * 0.5),\n\t\t},\n\t\tprocessor: {\n\t\t\tmemoryMb: Math.floor(totalMb * 0.3),\n\t\t\tcpus: round2(totalCpus * 0.3),\n\t\t},\n\t\tapi: {\n\t\t\tmemoryMb: Math.floor(totalMb * 0.2),\n\t\t\tcpus: round2(totalCpus * 0.2),\n\t\t},\n\t};\n}\n\nfunction allocTight(totalMb: number, totalCpus: number): Plan[\"containers\"] {\n\treturn {\n\t\tpostgres: {\n\t\t\tmemoryMb: Math.floor(totalMb * 0.6),\n\t\t\tcpus: round2(totalCpus * 0.6),\n\t\t},\n\t\tprocessor: {\n\t\t\tmemoryMb: Math.floor(totalMb * 0.25),\n\t\t\tcpus: round2(totalCpus * 0.25),\n\t\t},\n\t\tapi: {\n\t\t\tmemoryMb: Math.floor(totalMb * 0.15),\n\t\t\tcpus: round2(totalCpus * 0.15),\n\t\t},\n\t};\n}\n\nfunction round2(n: number): number {\n\treturn Math.round(n * 100) / 100;\n}\n\n/**\n * Split a compute envelope across (postgres, processor, api) containers.\n * Auto-biases PG-heavy (60/25/15) for sub-1GB totals.\n */\nexport function allocForTotals(\n\ttotalMemoryMb: number,\n\ttotalCpus: number,\n): Plan[\"containers\"] {\n\treturn totalMemoryMb < 1024\n\t\t? allocTight(totalMemoryMb, totalCpus)\n\t\t: alloc(totalMemoryMb, totalCpus);\n}\n\n// ── Canonical plan data ─────────────────────────────────────────────\n\nexport const PLANS: Record<PlanId, Plan> = {\n\thobby: {\n\t\tid: \"hobby\",\n\t\tdisplayName: \"Hobby\",\n\t\tmonthlyPriceCents: 0,\n\t\ttotalCpus: 0.5,\n\t\ttotalMemoryMb: 512,\n\t\tstorageLimitMb: 5_120,\n\t\tcontainers: allocTight(512, 0.5),\n\t\ttagline: \"Free, forever\",\n\t\tfeatures: [\n\t\t\t\"0.5 vCPU · 512 MB RAM\",\n\t\t\t\"5 GB storage · auto-pause 7d\",\n\t\t\t\"Starter subgraphs + subscriptions\",\n\t\t\t\"Recent-range reindexing\",\n\t\t\t\"Community support\",\n\t\t],\n\t\tstripeLookupKey: null,\n\t},\n\tlaunch: {\n\t\tid: \"launch\",\n\t\tdisplayName: \"Launch\",\n\t\tmonthlyPriceCents: 5_000, // $50\n\t\ttotalCpus: 1,\n\t\ttotalMemoryMb: 2_048,\n\t\tstorageLimitMb: 25_600, // 25 GB\n\t\tcontainers: alloc(2_048, 1),\n\t\ttagline: \"Production-ready\",\n\t\tfeatures: [\n\t\t\t\"1 vCPU · 2 GB RAM\",\n\t\t\t\"25 GB storage · always-on\",\n\t\t\t\"Unlimited subgraphs + subscriptions\",\n\t\t\t\"Larger reindex windows\",\n\t\t\t\"Spend caps + alerts\",\n\t\t\t\"Email support\",\n\t\t],\n\t\tstripeLookupKey: \"secondlayer_launch_monthly\",\n\t},\n\tscale: {\n\t\tid: \"scale\",\n\t\tdisplayName: \"Scale\",\n\t\tmonthlyPriceCents: 20_000, // $200\n\t\ttotalCpus: 4,\n\t\ttotalMemoryMb: 8_192,\n\t\tstorageLimitMb: 102_400, // 100 GB\n\t\tcontainers: alloc(8_192, 4),\n\t\ttagline: \"Scale with confidence\",\n\t\tfeatures: [\n\t\t\t\"4 vCPU · 8 GB RAM\",\n\t\t\t\"100 GB storage · always-on\",\n\t\t\t\"Higher throughput + replay\",\n\t\t\t\"24h SLA · priority support\",\n\t\t],\n\t\tstripeLookupKey: \"secondlayer_scale_monthly\",\n\t},\n\tenterprise: {\n\t\tid: \"enterprise\",\n\t\tdisplayName: \"Enterprise\",\n\t\tmonthlyPriceCents: null,\n\t\ttotalCpus: 8,\n\t\ttotalMemoryMb: 32_768,\n\t\tstorageLimitMb: -1,\n\t\tcontainers: alloc(32_768, 8),\n\t\ttagline: \"Custom workloads\",\n\t\tfeatures: [\n\t\t\t\"Custom compute + storage\",\n\t\t\t\"SLAs · regions · SSO\",\n\t\t\t\"Dedicated success engineer\",\n\t\t],\n\t\tstripeLookupKey: null,\n\t},\n};\n\nexport const PLAN_IDS: readonly PlanId[] = [\n\t\"hobby\",\n\t\"launch\",\n\t\"scale\",\n\t\"enterprise\",\n];\n\nexport function getPlan(id: string): Plan {\n\tconst plan = (PLANS as Record<string, Plan | undefined>)[id];\n\tif (!plan) throw new Error(`Unknown plan: ${id}`);\n\treturn plan;\n}\n\nexport function isValidPlanId(id: string): id is PlanId {\n\treturn id in PLANS;\n}\n\n// ── Allowance helpers (used by /api/accounts/usage display) ─────────\n//\n// Compute is hard-capped by Docker `--cpus`, so there's no compute\n// overage billing. The function below returns ∞ for paid plans (display-\n// only — no metering).\n//\n// Storage IS metered and billed past the plan's allowance via the\n// `storage_gb_months` Stripe meter at $2/GB-mo.\n\nexport function getComputeAllowanceHours(_plan: string): number {\n\t// Compute overage was killed when we removed the `compute_hours` meter.\n\t// All plans are now hard-capped by Docker `--cpus`. ∞ here means \"no\n\t// overage tracked\" for display purposes.\n\treturn Number.POSITIVE_INFINITY;\n}\n\nexport function getStorageAllowanceBytes(plan: string): number {\n\tconst planDef = (PLANS as Record<string, Plan | undefined>)[plan];\n\tif (!planDef) return PLANS.hobby.storageLimitMb * 1024 * 1024;\n\tif (planDef.storageLimitMb < 0) return Number.POSITIVE_INFINITY;\n\treturn planDef.storageLimitMb * 1024 * 1024;\n}\n\n/** Hobby has a hard cap (no overage billing); paid tiers bill $2/GB over allowance. */\nexport function hasStorageOverage(plan: string): boolean {\n\treturn plan !== \"hobby\" && plan !== \"enterprise\";\n}\n\nexport function getBasePriceCents(plan: string): number {\n\tconst planDef = (PLANS as Record<string, Plan | undefined>)[plan];\n\treturn planDef?.monthlyPriceCents ?? 0;\n}\n\nexport function getPlanDisplayName(plan: string): string {\n\tconst planDef = (PLANS as Record<string, Plan | undefined>)[plan];\n\treturn planDef?.displayName ?? plan.charAt(0).toUpperCase() + plan.slice(1);\n}\n\n// Re-export bytes-per-GB constant for callers that compute display values.\nexport { BYTES_PER_GB };\n"
6
6
  ],
7
- "mappings": "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",
8
- "debugId": "79B3993D9A501BD864756E2164756E21",
7
+ "mappings": "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",
8
+ "debugId": "33E32BD4F4A5ECD864756E2164756E21",
9
9
  "names": []
10
10
  }
@@ -146,6 +146,7 @@ interface SubgraphSummary {
146
146
  progress: number;
147
147
  blocksRemaining?: number;
148
148
  syncMode?: "sync" | "reindex";
149
+ resourceWarning?: SubgraphResourceWarning;
149
150
  gapCount: number;
150
151
  integrity: "complete" | "gaps_detected";
151
152
  createdAt: string;
@@ -172,6 +173,7 @@ interface SubgraphSyncInfo {
172
173
  processedBlocks?: number;
173
174
  totalBlocks?: number;
174
175
  progress: number;
176
+ resourceWarning?: SubgraphResourceWarning;
175
177
  gaps: {
176
178
  count: number
177
179
  totalMissingBlocks: number
@@ -179,6 +181,14 @@ interface SubgraphSyncInfo {
179
181
  };
180
182
  integrity: "complete" | "gaps_detected";
181
183
  }
184
+ interface SubgraphResourceWarning {
185
+ code: "HOBBY_LARGE_REINDEX";
186
+ message: string;
187
+ plan: "hobby";
188
+ blockRange: number;
189
+ processorMemoryMb: number;
190
+ recommendedPlan: "launch";
191
+ }
182
192
  interface SubgraphDetail {
183
193
  name: string;
184
194
  version: string;
@@ -386,4 +396,4 @@ declare function validateSubscriptionFilterForTable(input: {
386
396
  filter?: unknown
387
397
  tables: SubscriptionSchemaTables
388
398
  }): string[];
389
- export { validateSubscriptionFilterForTable, formatSubscriptionSchemaErrors, UpdateSubscriptionRequestSchema, UpdateSubscriptionRequest, UpdateProfileRequestSchema, UpdateProfileRequest, SubscriptionSummary, SubscriptionStatusSchema, SubscriptionSchemaTables, SubscriptionSchemaTable, SubscriptionSchemaColumn, SubscriptionRuntimeSchema, SubscriptionFormatSchema, SubscriptionFilterSchema, SubscriptionFilterPrimitiveSchema, SubscriptionFilterPrimitive, SubscriptionFilterOperatorSchema, SubscriptionFilterOperator, SubscriptionFilterClauseSchema, SubscriptionFilterClause, SubscriptionFilter, SubscriptionDetail, SubgraphSyncInfo, SubgraphSummary, SubgraphQueryParams, SubgraphGapsResponse, SubgraphGapRange, SubgraphGapEntry, SubgraphDetail, StxTransferFilterSchema, StxTransferFilter, StxMintFilterSchema, StxMintFilter, StxLockFilterSchema, StxLockFilter, StxBurnFilterSchema, StxBurnFilter, SUBSCRIPTION_STATUSES, SUBSCRIPTION_RUNTIMES, SUBSCRIPTION_FORMATS, SUBSCRIPTION_FILTER_OPERATORS, RotateSecretResponse, ReplaySubscriptionRequestSchema, ReplaySubscriptionRequest, ReplayResult, ReindexResponse, PrintEventFilterSchema, PrintEventFilter, ParsedUpdateSubscriptionRequest, ParsedReplaySubscriptionRequest, ParsedCreateSubscriptionRequest, NftTransferFilterSchema, NftTransferFilter, NftMintFilterSchema, NftMintFilter, NftBurnFilterSchema, NftBurnFilter, FtTransferFilterSchema, FtTransferFilter, FtMintFilterSchema, FtMintFilter, FtBurnFilterSchema, FtBurnFilter, EventFilterSchema, EventFilter, DeploySubgraphResponse, DeploySubgraphRequestSchema, DeploySubgraphRequest, DeliveryRow, DeadRow, CreateSubscriptionResponse, CreateSubscriptionRequestSchema, CreateSubscriptionRequest, ContractDeployFilterSchema, ContractDeployFilter, ContractCallFilterSchema, ContractCallFilter };
399
+ export { validateSubscriptionFilterForTable, formatSubscriptionSchemaErrors, UpdateSubscriptionRequestSchema, UpdateSubscriptionRequest, UpdateProfileRequestSchema, UpdateProfileRequest, SubscriptionSummary, SubscriptionStatusSchema, SubscriptionSchemaTables, SubscriptionSchemaTable, SubscriptionSchemaColumn, SubscriptionRuntimeSchema, SubscriptionFormatSchema, SubscriptionFilterSchema, SubscriptionFilterPrimitiveSchema, SubscriptionFilterPrimitive, SubscriptionFilterOperatorSchema, SubscriptionFilterOperator, SubscriptionFilterClauseSchema, SubscriptionFilterClause, SubscriptionFilter, SubscriptionDetail, SubgraphSyncInfo, SubgraphSummary, SubgraphResourceWarning, SubgraphQueryParams, SubgraphGapsResponse, SubgraphGapRange, SubgraphGapEntry, SubgraphDetail, StxTransferFilterSchema, StxTransferFilter, StxMintFilterSchema, StxMintFilter, StxLockFilterSchema, StxLockFilter, StxBurnFilterSchema, StxBurnFilter, SUBSCRIPTION_STATUSES, SUBSCRIPTION_RUNTIMES, SUBSCRIPTION_FORMATS, SUBSCRIPTION_FILTER_OPERATORS, RotateSecretResponse, ReplaySubscriptionRequestSchema, ReplaySubscriptionRequest, ReplayResult, ReindexResponse, PrintEventFilterSchema, PrintEventFilter, ParsedUpdateSubscriptionRequest, ParsedReplaySubscriptionRequest, ParsedCreateSubscriptionRequest, NftTransferFilterSchema, NftTransferFilter, NftMintFilterSchema, NftMintFilter, NftBurnFilterSchema, NftBurnFilter, FtTransferFilterSchema, FtTransferFilter, FtMintFilterSchema, FtMintFilter, FtBurnFilterSchema, FtBurnFilter, EventFilterSchema, EventFilter, DeploySubgraphResponse, DeploySubgraphRequestSchema, DeploySubgraphRequest, DeliveryRow, DeadRow, CreateSubscriptionResponse, CreateSubscriptionRequestSchema, CreateSubscriptionRequest, ContractDeployFilterSchema, ContractDeployFilter, ContractCallFilterSchema, ContractCallFilter };
@@ -4,7 +4,7 @@
4
4
  "sourcesContent": [
5
5
  "import { z } from \"zod/v4\";\n\n/**\n * Account profile shapes. Unrelated to marketplace — previously lived in\n * schemas/marketplace.ts alongside public-directory types. Kept here now\n * that marketplace is gone so the profile fields (display_name, bio, slug)\n * have a stable home.\n */\n\nexport interface UpdateProfileRequest {\n\tdisplay_name?: string;\n\tbio?: string;\n\tslug?: string;\n}\n\nexport const UpdateProfileRequestSchema: z.ZodType<UpdateProfileRequest> =\n\tz.object({\n\t\tdisplay_name: z.string().max(50).optional(),\n\t\tbio: z.string().max(300).optional(),\n\t\tslug: z\n\t\t\t.string()\n\t\t\t.regex(/^[a-z0-9-]+$/, \"lowercase alphanumeric + hyphens only\")\n\t\t\t.min(3)\n\t\t\t.max(30)\n\t\t\t.optional(),\n\t});\n",
6
6
  "import { isValidAddress as _isValidAddress } from \"@secondlayer/stacks\";\nimport { z } from \"zod/v4\";\n\nconst isValidAddress = _isValidAddress as (addr: string) => boolean;\n\n/** Validate a Stacks principal (standard or contract, e.g. SP2J...ABC or SP2J...ABC.contract-name) */\nconst stacksPrincipal = z.string().refine((val) => {\n\tconst parts = val.split(\".\");\n\tif (parts.length > 2) return false;\n\t// biome-ignore lint/style/noNonNullAssertion: value is non-null after preceding check or by construction; TS narrowing limitation\n\treturn isValidAddress(parts[0]!);\n}, \"Invalid Stacks principal address\");\n\n// Base filter with common fields\nconst baseFilter = {\n\t// Optional: filter by sender\n\tsender: stacksPrincipal.optional(),\n\t// Optional: filter by recipient\n\trecipient: stacksPrincipal.optional(),\n};\n\n// Type exports — defined first so they can annotate schemas\nexport interface StxTransferFilter {\n\ttype: \"stx_transfer\";\n\tsender?: string;\n\trecipient?: string;\n\tminAmount?: number;\n\tmaxAmount?: number;\n}\n\nexport interface StxMintFilter {\n\ttype: \"stx_mint\";\n\trecipient?: string;\n\tminAmount?: number;\n}\n\nexport interface StxBurnFilter {\n\ttype: \"stx_burn\";\n\tsender?: string;\n\tminAmount?: number;\n}\n\nexport interface StxLockFilter {\n\ttype: \"stx_lock\";\n\tlockedAddress?: string;\n\tminAmount?: number;\n}\n\nexport interface FtTransferFilter {\n\ttype: \"ft_transfer\";\n\tsender?: string;\n\trecipient?: string;\n\tassetIdentifier?: string;\n\tminAmount?: number;\n}\n\nexport interface FtMintFilter {\n\ttype: \"ft_mint\";\n\trecipient?: string;\n\tassetIdentifier?: string;\n\tminAmount?: number;\n}\n\nexport interface FtBurnFilter {\n\ttype: \"ft_burn\";\n\tsender?: string;\n\tassetIdentifier?: string;\n\tminAmount?: number;\n}\n\nexport interface NftTransferFilter {\n\ttype: \"nft_transfer\";\n\tsender?: string;\n\trecipient?: string;\n\tassetIdentifier?: string;\n\ttokenId?: string;\n}\n\nexport interface NftMintFilter {\n\ttype: \"nft_mint\";\n\trecipient?: string;\n\tassetIdentifier?: string;\n\ttokenId?: string;\n}\n\nexport interface NftBurnFilter {\n\ttype: \"nft_burn\";\n\tsender?: string;\n\tassetIdentifier?: string;\n\ttokenId?: string;\n}\n\nexport interface ContractCallFilter {\n\ttype: \"contract_call\";\n\tcontractId?: string;\n\tfunctionName?: string;\n\tcaller?: string;\n}\n\nexport interface ContractDeployFilter {\n\ttype: \"contract_deploy\";\n\tdeployer?: string;\n\tcontractName?: string;\n}\n\nexport interface PrintEventFilter {\n\ttype: \"print_event\";\n\tcontractId?: string;\n\ttopic?: string;\n\tcontains?: string;\n}\n\nexport type EventFilter =\n\t| StxTransferFilter\n\t| StxMintFilter\n\t| StxBurnFilter\n\t| StxLockFilter\n\t| FtTransferFilter\n\t| FtMintFilter\n\t| FtBurnFilter\n\t| NftTransferFilter\n\t| NftMintFilter\n\t| NftBurnFilter\n\t| ContractCallFilter\n\t| ContractDeployFilter\n\t| PrintEventFilter;\n\n// STX Transfer Filter\nexport const StxTransferFilterSchema: z.ZodType<StxTransferFilter> = z.object({\n\ttype: z.literal(\"stx_transfer\"),\n\t...baseFilter,\n\t// Optional: minimum amount in microSTX\n\tminAmount: z.coerce.number().int().positive().optional(),\n\t// Optional: maximum amount in microSTX\n\tmaxAmount: z.coerce.number().int().positive().optional(),\n});\n\n// STX Mint Filter\nexport const StxMintFilterSchema: z.ZodType<StxMintFilter> = z.object({\n\ttype: z.literal(\"stx_mint\"),\n\trecipient: stacksPrincipal.optional(),\n\tminAmount: z.coerce.number().int().positive().optional(),\n});\n\n// STX Burn Filter\nexport const StxBurnFilterSchema: z.ZodType<StxBurnFilter> = z.object({\n\ttype: z.literal(\"stx_burn\"),\n\tsender: stacksPrincipal.optional(),\n\tminAmount: z.coerce.number().int().positive().optional(),\n});\n\n// STX Lock Filter\nexport const StxLockFilterSchema: z.ZodType<StxLockFilter> = z.object({\n\ttype: z.literal(\"stx_lock\"),\n\tlockedAddress: stacksPrincipal.optional(),\n\tminAmount: z.coerce.number().int().positive().optional(),\n});\n\n// FT Transfer Filter\nexport const FtTransferFilterSchema: z.ZodType<FtTransferFilter> = z.object({\n\ttype: z.literal(\"ft_transfer\"),\n\t...baseFilter,\n\t// Contract that defines the token (e.g., SP3K8BC0PPEVCV7NZ6QSRWPQ2JE9E5B6N3PA0KBR9.token-wstx)\n\tassetIdentifier: z.string().optional(),\n\tminAmount: z.coerce.number().int().positive().optional(),\n});\n\n// FT Mint Filter\nexport const FtMintFilterSchema: z.ZodType<FtMintFilter> = z.object({\n\ttype: z.literal(\"ft_mint\"),\n\trecipient: stacksPrincipal.optional(),\n\tassetIdentifier: z.string().optional(),\n\tminAmount: z.coerce.number().int().positive().optional(),\n});\n\n// FT Burn Filter\nexport const FtBurnFilterSchema: z.ZodType<FtBurnFilter> = z.object({\n\ttype: z.literal(\"ft_burn\"),\n\tsender: stacksPrincipal.optional(),\n\tassetIdentifier: z.string().optional(),\n\tminAmount: z.coerce.number().int().positive().optional(),\n});\n\n// NFT Transfer Filter\nexport const NftTransferFilterSchema: z.ZodType<NftTransferFilter> = z.object({\n\ttype: z.literal(\"nft_transfer\"),\n\t...baseFilter,\n\tassetIdentifier: z.string().optional(),\n\t// Optional: filter by specific token ID (Clarity value as hex)\n\ttokenId: z.string().optional(),\n});\n\n// NFT Mint Filter\nexport const NftMintFilterSchema: z.ZodType<NftMintFilter> = z.object({\n\ttype: z.literal(\"nft_mint\"),\n\trecipient: stacksPrincipal.optional(),\n\tassetIdentifier: z.string().optional(),\n\ttokenId: z.string().optional(),\n});\n\n// NFT Burn Filter\nexport const NftBurnFilterSchema: z.ZodType<NftBurnFilter> = z.object({\n\ttype: z.literal(\"nft_burn\"),\n\tsender: stacksPrincipal.optional(),\n\tassetIdentifier: z.string().optional(),\n\ttokenId: z.string().optional(),\n});\n\n// Contract Call Filter\nexport const ContractCallFilterSchema: z.ZodType<ContractCallFilter> = z.object(\n\t{\n\t\ttype: z.literal(\"contract_call\"),\n\t\t// Contract being called\n\t\tcontractId: stacksPrincipal.optional(),\n\t\t// Function name (supports wildcards with *)\n\t\tfunctionName: z.string().optional(),\n\t\t// Caller address\n\t\tcaller: stacksPrincipal.optional(),\n\t},\n);\n\n// Contract Deploy Filter\nexport const ContractDeployFilterSchema: z.ZodType<ContractDeployFilter> =\n\tz.object({\n\t\ttype: z.literal(\"contract_deploy\"),\n\t\t// Deployer address\n\t\tdeployer: stacksPrincipal.optional(),\n\t\t// Contract name pattern (supports wildcards)\n\t\tcontractName: z.string().optional(),\n\t});\n\n// Print Event Filter (smart contract events)\nexport const PrintEventFilterSchema: z.ZodType<PrintEventFilter> = z.object({\n\ttype: z.literal(\"print_event\"),\n\t// Contract emitting the event\n\tcontractId: stacksPrincipal.optional(),\n\t// Topic/name of the event\n\ttopic: z.string().optional(),\n\t// Search for substring in event data\n\tcontains: z.string().optional(),\n});\n\n// Union of all filter types\nexport const EventFilterSchema: z.ZodType<EventFilter> = z.discriminatedUnion(\n\t\"type\",\n\t[\n\t\t// biome-ignore lint/suspicious/noExplicitAny: interop boundary or dynamic-shape value where typing adds friction without runtime safety\n\t\tStxTransferFilterSchema as any,\n\t\t// biome-ignore lint/suspicious/noExplicitAny: interop boundary or dynamic-shape value where typing adds friction without runtime safety\n\t\tStxMintFilterSchema as any,\n\t\t// biome-ignore lint/suspicious/noExplicitAny: interop boundary or dynamic-shape value where typing adds friction without runtime safety\n\t\tStxBurnFilterSchema as any,\n\t\t// biome-ignore lint/suspicious/noExplicitAny: interop boundary or dynamic-shape value where typing adds friction without runtime safety\n\t\tStxLockFilterSchema as any,\n\t\t// biome-ignore lint/suspicious/noExplicitAny: interop boundary or dynamic-shape value where typing adds friction without runtime safety\n\t\tFtTransferFilterSchema as any,\n\t\t// biome-ignore lint/suspicious/noExplicitAny: interop boundary or dynamic-shape value where typing adds friction without runtime safety\n\t\tFtMintFilterSchema as any,\n\t\t// biome-ignore lint/suspicious/noExplicitAny: interop boundary or dynamic-shape value where typing adds friction without runtime safety\n\t\tFtBurnFilterSchema as any,\n\t\t// biome-ignore lint/suspicious/noExplicitAny: interop boundary or dynamic-shape value where typing adds friction without runtime safety\n\t\tNftTransferFilterSchema as any,\n\t\t// biome-ignore lint/suspicious/noExplicitAny: interop boundary or dynamic-shape value where typing adds friction without runtime safety\n\t\tNftMintFilterSchema as any,\n\t\t// biome-ignore lint/suspicious/noExplicitAny: interop boundary or dynamic-shape value where typing adds friction without runtime safety\n\t\tNftBurnFilterSchema as any,\n\t\t// biome-ignore lint/suspicious/noExplicitAny: interop boundary or dynamic-shape value where typing adds friction without runtime safety\n\t\tContractCallFilterSchema as any,\n\t\t// biome-ignore lint/suspicious/noExplicitAny: interop boundary or dynamic-shape value where typing adds friction without runtime safety\n\t\tContractDeployFilterSchema as any,\n\t\t// biome-ignore lint/suspicious/noExplicitAny: interop boundary or dynamic-shape value where typing adds friction without runtime safety\n\t\tPrintEventFilterSchema as any,\n\t],\n);\n",
7
- "import { z } from \"zod/v4\";\n\n// ── Deploy Subgraph Request ─────────────────────────────────────────────────\n\nexport interface DeploySubgraphRequest {\n\tname: string;\n\tversion?: string;\n\tdescription?: string;\n\tsources: Record<string, Record<string, unknown>>;\n\tschema: Record<string, unknown>;\n\thandlerCode: string;\n\t/** Override the definition's startBlock for this deploy only. */\n\tstartBlock?: number;\n\t/** Original TypeScript source, persisted so chat can read/diff/edit later. */\n\tsourceCode?: string;\n}\n\nexport const DeploySubgraphRequestSchema: z.ZodType<DeploySubgraphRequest> =\n\tz.object({\n\t\tname: z\n\t\t\t.string()\n\t\t\t.regex(/^[a-z0-9-]+$/, \"lowercase alphanumeric + hyphens only\")\n\t\t\t.max(63),\n\t\tversion: z.string().optional(),\n\t\tdescription: z.string().optional(),\n\t\tsources: z\n\t\t\t.record(z.string(), z.record(z.string(), z.unknown()))\n\t\t\t.refine(\n\t\t\t\t(s) => Object.keys(s).length > 0,\n\t\t\t\t\"Must have at least one source\",\n\t\t\t),\n\t\tschema: z.record(z.string(), z.unknown()),\n\t\thandlerCode: z.string().max(1_048_576, \"handler code exceeds 1MB limit\"),\n\t\tstartBlock: z.number().int().nonnegative().optional(),\n\t\tsourceCode: z\n\t\t\t.string()\n\t\t\t.max(1_048_576, \"source code exceeds 1MB limit\")\n\t\t\t.optional(),\n\t});\n\nexport interface DeploySubgraphResponse {\n\taction: \"created\" | \"unchanged\" | \"updated\" | \"reindexed\";\n\tsubgraphId: string;\n\tversion: string;\n\tmessage: string;\n\toperationId?: string;\n\treindexStarted?: boolean;\n\tdiff?: {\n\t\taddedTables: string[];\n\t\tremovedTables: string[];\n\t\taddedColumns: Record<string, string[]>;\n\t\tbreakingChanges: string[];\n\t};\n}\n\n// Subgraph API response types\n\nexport interface SubgraphSummary {\n\tname: string;\n\tversion: string;\n\tstatus: string;\n\tlastProcessedBlock: number;\n\ttotalProcessed: number;\n\ttotalErrors: number;\n\ttables: string[];\n\tchainTip: number;\n\tsourceChainTip?: number;\n\ttargetBlock?: number;\n\tprogress: number;\n\tblocksRemaining?: number;\n\tsyncMode?: \"sync\" | \"reindex\";\n\tgapCount: number;\n\tintegrity: \"complete\" | \"gaps_detected\";\n\tcreatedAt: string;\n}\n\nexport interface SubgraphGapRange {\n\tstart: number;\n\tend: number;\n\tsize: number;\n\treason: string;\n}\n\nexport interface SubgraphSyncInfo {\n\tstatus: \"synced\" | \"catching_up\" | \"reindexing\" | \"error\";\n\tmode?: \"sync\" | \"reindex\";\n\tstartBlock: number;\n\tlastProcessedBlock: number;\n\t/**\n\t * Backward-compatible denominator for progress displays. During reindexing,\n\t * this is the reindex target block rather than the live source chain tip.\n\t */\n\tchainTip: number;\n\tsourceChainTip?: number;\n\ttargetBlock?: number;\n\tblocksRemaining: number;\n\tprocessedBlocks?: number;\n\ttotalBlocks?: number;\n\tprogress: number;\n\tgaps: {\n\t\tcount: number;\n\t\ttotalMissingBlocks: number;\n\t\tranges: SubgraphGapRange[];\n\t};\n\tintegrity: \"complete\" | \"gaps_detected\";\n}\n\nexport interface SubgraphDetail {\n\tname: string;\n\tversion: string;\n\tschemaHash?: string;\n\tstatus: string;\n\tlastProcessedBlock: number;\n\tdescription?: string;\n\tsources?: Record<string, unknown>;\n\tdefinition?: Record<string, unknown>;\n\thealth: {\n\t\ttotalProcessed: number;\n\t\ttotalErrors: number;\n\t\terrorRate: number;\n\t\tlastError: string | null;\n\t\tlastErrorAt: string | null;\n\t};\n\tsync: SubgraphSyncInfo;\n\ttables: Record<\n\t\tstring,\n\t\t{\n\t\t\tendpoint: string;\n\t\t\tcolumns: Record<\n\t\t\t\tstring,\n\t\t\t\t{\n\t\t\t\t\ttype: string;\n\t\t\t\t\tnullable?: boolean;\n\t\t\t\t\tindexed?: boolean;\n\t\t\t\t\tsearchable?: boolean;\n\t\t\t\t\tdefault?: string | number | boolean;\n\t\t\t\t}\n\t\t\t>;\n\t\t\trowCount: number;\n\t\t\texample: string;\n\t\t\tindexes?: string[][];\n\t\t\tuniqueKeys?: string[][];\n\t\t}\n\t>;\n\tcreatedAt: string;\n\tupdatedAt: string;\n}\n\nexport interface SubgraphGapEntry {\n\tstart: number;\n\tend: number;\n\tsize: number;\n\treason: string;\n\tdetectedAt: string;\n\tresolvedAt: string | null;\n}\n\nexport interface SubgraphGapsResponse {\n\tdata: SubgraphGapEntry[];\n\tmeta: {\n\t\ttotal: number;\n\t\ttotalMissingBlocks: number;\n\t\tlimit: number;\n\t\toffset: number;\n\t};\n}\n\nexport interface ReindexResponse {\n\tmessage: string;\n\tfromBlock: number;\n\ttoBlock: number | string;\n\toperationId?: string;\n\tstatus?: \"queued\" | \"running\" | \"cancel_requested\";\n}\n\nexport interface SubgraphQueryParams {\n\tsort?: string;\n\torder?: string;\n\tlimit?: number;\n\toffset?: number;\n\tfields?: string;\n\tfilters?: Record<string, string>;\n}\n",
7
+ "import { z } from \"zod/v4\";\n\n// ── Deploy Subgraph Request ─────────────────────────────────────────────────\n\nexport interface DeploySubgraphRequest {\n\tname: string;\n\tversion?: string;\n\tdescription?: string;\n\tsources: Record<string, Record<string, unknown>>;\n\tschema: Record<string, unknown>;\n\thandlerCode: string;\n\t/** Override the definition's startBlock for this deploy only. */\n\tstartBlock?: number;\n\t/** Original TypeScript source, persisted so chat can read/diff/edit later. */\n\tsourceCode?: string;\n}\n\nexport const DeploySubgraphRequestSchema: z.ZodType<DeploySubgraphRequest> =\n\tz.object({\n\t\tname: z\n\t\t\t.string()\n\t\t\t.regex(/^[a-z0-9-]+$/, \"lowercase alphanumeric + hyphens only\")\n\t\t\t.max(63),\n\t\tversion: z.string().optional(),\n\t\tdescription: z.string().optional(),\n\t\tsources: z\n\t\t\t.record(z.string(), z.record(z.string(), z.unknown()))\n\t\t\t.refine(\n\t\t\t\t(s) => Object.keys(s).length > 0,\n\t\t\t\t\"Must have at least one source\",\n\t\t\t),\n\t\tschema: z.record(z.string(), z.unknown()),\n\t\thandlerCode: z.string().max(1_048_576, \"handler code exceeds 1MB limit\"),\n\t\tstartBlock: z.number().int().nonnegative().optional(),\n\t\tsourceCode: z\n\t\t\t.string()\n\t\t\t.max(1_048_576, \"source code exceeds 1MB limit\")\n\t\t\t.optional(),\n\t});\n\nexport interface DeploySubgraphResponse {\n\taction: \"created\" | \"unchanged\" | \"updated\" | \"reindexed\";\n\tsubgraphId: string;\n\tversion: string;\n\tmessage: string;\n\toperationId?: string;\n\treindexStarted?: boolean;\n\tdiff?: {\n\t\taddedTables: string[];\n\t\tremovedTables: string[];\n\t\taddedColumns: Record<string, string[]>;\n\t\tbreakingChanges: string[];\n\t};\n}\n\n// Subgraph API response types\n\nexport interface SubgraphSummary {\n\tname: string;\n\tversion: string;\n\tstatus: string;\n\tlastProcessedBlock: number;\n\ttotalProcessed: number;\n\ttotalErrors: number;\n\ttables: string[];\n\tchainTip: number;\n\tsourceChainTip?: number;\n\ttargetBlock?: number;\n\tprogress: number;\n\tblocksRemaining?: number;\n\tsyncMode?: \"sync\" | \"reindex\";\n\tresourceWarning?: SubgraphResourceWarning;\n\tgapCount: number;\n\tintegrity: \"complete\" | \"gaps_detected\";\n\tcreatedAt: string;\n}\n\nexport interface SubgraphGapRange {\n\tstart: number;\n\tend: number;\n\tsize: number;\n\treason: string;\n}\n\nexport interface SubgraphSyncInfo {\n\tstatus: \"synced\" | \"catching_up\" | \"reindexing\" | \"error\";\n\tmode?: \"sync\" | \"reindex\";\n\tstartBlock: number;\n\tlastProcessedBlock: number;\n\t/**\n\t * Backward-compatible denominator for progress displays. During reindexing,\n\t * this is the reindex target block rather than the live source chain tip.\n\t */\n\tchainTip: number;\n\tsourceChainTip?: number;\n\ttargetBlock?: number;\n\tblocksRemaining: number;\n\tprocessedBlocks?: number;\n\ttotalBlocks?: number;\n\tprogress: number;\n\tresourceWarning?: SubgraphResourceWarning;\n\tgaps: {\n\t\tcount: number;\n\t\ttotalMissingBlocks: number;\n\t\tranges: SubgraphGapRange[];\n\t};\n\tintegrity: \"complete\" | \"gaps_detected\";\n}\n\nexport interface SubgraphResourceWarning {\n\tcode: \"HOBBY_LARGE_REINDEX\";\n\tmessage: string;\n\tplan: \"hobby\";\n\tblockRange: number;\n\tprocessorMemoryMb: number;\n\trecommendedPlan: \"launch\";\n}\n\nexport interface SubgraphDetail {\n\tname: string;\n\tversion: string;\n\tschemaHash?: string;\n\tstatus: string;\n\tlastProcessedBlock: number;\n\tdescription?: string;\n\tsources?: Record<string, unknown>;\n\tdefinition?: Record<string, unknown>;\n\thealth: {\n\t\ttotalProcessed: number;\n\t\ttotalErrors: number;\n\t\terrorRate: number;\n\t\tlastError: string | null;\n\t\tlastErrorAt: string | null;\n\t};\n\tsync: SubgraphSyncInfo;\n\ttables: Record<\n\t\tstring,\n\t\t{\n\t\t\tendpoint: string;\n\t\t\tcolumns: Record<\n\t\t\t\tstring,\n\t\t\t\t{\n\t\t\t\t\ttype: string;\n\t\t\t\t\tnullable?: boolean;\n\t\t\t\t\tindexed?: boolean;\n\t\t\t\t\tsearchable?: boolean;\n\t\t\t\t\tdefault?: string | number | boolean;\n\t\t\t\t}\n\t\t\t>;\n\t\t\trowCount: number;\n\t\t\texample: string;\n\t\t\tindexes?: string[][];\n\t\t\tuniqueKeys?: string[][];\n\t\t}\n\t>;\n\tcreatedAt: string;\n\tupdatedAt: string;\n}\n\nexport interface SubgraphGapEntry {\n\tstart: number;\n\tend: number;\n\tsize: number;\n\treason: string;\n\tdetectedAt: string;\n\tresolvedAt: string | null;\n}\n\nexport interface SubgraphGapsResponse {\n\tdata: SubgraphGapEntry[];\n\tmeta: {\n\t\ttotal: number;\n\t\ttotalMissingBlocks: number;\n\t\tlimit: number;\n\t\toffset: number;\n\t};\n}\n\nexport interface ReindexResponse {\n\tmessage: string;\n\tfromBlock: number;\n\ttoBlock: number | string;\n\toperationId?: string;\n\tstatus?: \"queued\" | \"running\" | \"cancel_requested\";\n}\n\nexport interface SubgraphQueryParams {\n\tsort?: string;\n\torder?: string;\n\tlimit?: number;\n\toffset?: number;\n\tfields?: string;\n\tfilters?: Record<string, string>;\n}\n",
8
8
  "import { z } from \"zod/v4\";\n\nexport const SUBSCRIPTION_FORMATS = [\n\t\"standard-webhooks\",\n\t\"inngest\",\n\t\"trigger\",\n\t\"cloudflare\",\n\t\"cloudevents\",\n\t\"raw\",\n] as const;\n\nexport const SUBSCRIPTION_RUNTIMES = [\n\t\"inngest\",\n\t\"trigger\",\n\t\"cloudflare\",\n\t\"node\",\n] as const;\n\nexport const SUBSCRIPTION_STATUSES = [\"active\", \"paused\", \"error\"] as const;\n\nexport const SUBSCRIPTION_FILTER_OPERATORS = [\n\t\"eq\",\n\t\"neq\",\n\t\"gt\",\n\t\"gte\",\n\t\"lt\",\n\t\"lte\",\n\t\"in\",\n] as const;\n\nconst webhookUrl = z\n\t.string()\n\t.trim()\n\t.min(1)\n\t.refine(\n\t\t(value) => value.startsWith(\"http://\") || value.startsWith(\"https://\"),\n\t\t\"must be an http(s) URL\",\n\t);\n\nconst name = z.string().trim().min(1).max(128);\nconst resourceName = z.string().trim().min(1).max(128);\n\nexport const SubscriptionStatusSchema: z.ZodType<SubscriptionStatus> = z.enum(\n\tSUBSCRIPTION_STATUSES,\n);\nexport const SubscriptionFormatSchema: z.ZodType<SubscriptionFormat> =\n\tz.enum(SUBSCRIPTION_FORMATS);\nexport const SubscriptionRuntimeSchema: z.ZodType<SubscriptionRuntime> = z.enum(\n\tSUBSCRIPTION_RUNTIMES,\n);\n\nexport const SubscriptionFilterPrimitiveSchema: z.ZodType<SubscriptionFilterPrimitive> =\n\tz.union([z.string(), z.number().finite(), z.boolean()]);\n\nexport const SubscriptionFilterOperatorSchema: z.ZodType<SubscriptionFilterOperator> =\n\tz.union([\n\t\tz.object({ eq: SubscriptionFilterPrimitiveSchema }).strict(),\n\t\tz.object({ neq: SubscriptionFilterPrimitiveSchema }).strict(),\n\t\tz.object({ gt: z.union([z.string(), z.number().finite()]) }).strict(),\n\t\tz.object({ gte: z.union([z.string(), z.number().finite()]) }).strict(),\n\t\tz.object({ lt: z.union([z.string(), z.number().finite()]) }).strict(),\n\t\tz.object({ lte: z.union([z.string(), z.number().finite()]) }).strict(),\n\t\tz\n\t\t\t.object({\n\t\t\t\tin: z.array(SubscriptionFilterPrimitiveSchema).min(1),\n\t\t\t})\n\t\t\t.strict(),\n\t]);\n\nexport const SubscriptionFilterClauseSchema: z.ZodType<SubscriptionFilterClause> =\n\tz.union([\n\t\tSubscriptionFilterPrimitiveSchema,\n\t\tSubscriptionFilterOperatorSchema,\n\t]);\n\nexport const SubscriptionFilterSchema: z.ZodType<SubscriptionFilter> = z.record(\n\tz.string().min(1),\n\tSubscriptionFilterClauseSchema,\n);\n\nexport const CreateSubscriptionRequestSchema: z.ZodType<ParsedCreateSubscriptionRequest> =\n\tz.object({\n\t\tname,\n\t\tsubgraphName: resourceName,\n\t\ttableName: resourceName,\n\t\turl: webhookUrl,\n\t\tfilter: SubscriptionFilterSchema.optional(),\n\t\tformat: SubscriptionFormatSchema.default(\"standard-webhooks\"),\n\t\truntime: SubscriptionRuntimeSchema.nullable().optional(),\n\t\tauthConfig: z.record(z.string(), z.unknown()).optional(),\n\t\tmaxRetries: z.number().int().min(0).max(100).optional(),\n\t\ttimeoutMs: z.number().int().min(100).max(300_000).optional(),\n\t\tconcurrency: z.number().int().min(1).max(100).optional(),\n\t});\n\nexport const UpdateSubscriptionRequestSchema: z.ZodType<UpdateSubscriptionRequest> =\n\tz\n\t\t.object({\n\t\t\tname: name.optional(),\n\t\t\turl: webhookUrl.optional(),\n\t\t\tfilter: SubscriptionFilterSchema.optional(),\n\t\t\tformat: SubscriptionFormatSchema.optional(),\n\t\t\truntime: SubscriptionRuntimeSchema.nullable().optional(),\n\t\t\tauthConfig: z.record(z.string(), z.unknown()).optional(),\n\t\t\tmaxRetries: z.number().int().min(0).max(100).optional(),\n\t\t\ttimeoutMs: z.number().int().min(100).max(300_000).optional(),\n\t\t\tconcurrency: z.number().int().min(1).max(100).optional(),\n\t\t})\n\t\t.refine((value) => Object.keys(value).length > 0, {\n\t\t\tmessage: \"At least one field must be provided\",\n\t\t});\n\nexport const ReplaySubscriptionRequestSchema: z.ZodType<ReplaySubscriptionRequest> =\n\tz\n\t\t.object({\n\t\t\tfromBlock: z.number().int().nonnegative(),\n\t\t\ttoBlock: z.number().int().nonnegative(),\n\t\t\tforce: z.string().trim().min(1).max(64).optional(),\n\t\t})\n\t\t.refine((value) => value.fromBlock <= value.toBlock, {\n\t\t\tmessage: \"fromBlock must be less than or equal to toBlock\",\n\t\t\tpath: [\"toBlock\"],\n\t\t});\n\nexport type SubscriptionStatus = (typeof SUBSCRIPTION_STATUSES)[number];\nexport type SubscriptionFormat = (typeof SUBSCRIPTION_FORMATS)[number];\nexport type SubscriptionRuntime = (typeof SUBSCRIPTION_RUNTIMES)[number];\nexport type SubscriptionFilterPrimitive = string | number | boolean;\nexport type SubscriptionFilterOperator =\n\t| { eq: SubscriptionFilterPrimitive }\n\t| { neq: SubscriptionFilterPrimitive }\n\t| { gt: string | number }\n\t| { gte: string | number }\n\t| { lt: string | number }\n\t| { lte: string | number }\n\t| { in: SubscriptionFilterPrimitive[] };\nexport type SubscriptionFilterClause =\n\t| SubscriptionFilterPrimitive\n\t| SubscriptionFilterOperator;\nexport type SubscriptionFilter = Record<string, SubscriptionFilterClause>;\n\nexport interface CreateSubscriptionRequest {\n\tname: string;\n\tsubgraphName: string;\n\ttableName: string;\n\turl: string;\n\tfilter?: SubscriptionFilter;\n\tformat?: SubscriptionFormat;\n\truntime?: SubscriptionRuntime | null;\n\tauthConfig?: Record<string, unknown>;\n\tmaxRetries?: number;\n\ttimeoutMs?: number;\n\tconcurrency?: number;\n}\n\nexport interface ParsedCreateSubscriptionRequest\n\textends Omit<CreateSubscriptionRequest, \"format\"> {\n\tformat: SubscriptionFormat;\n}\n\nexport interface UpdateSubscriptionRequest {\n\tname?: string;\n\turl?: string;\n\tfilter?: SubscriptionFilter;\n\tformat?: SubscriptionFormat;\n\truntime?: SubscriptionRuntime | null;\n\tauthConfig?: Record<string, unknown>;\n\tmaxRetries?: number;\n\ttimeoutMs?: number;\n\tconcurrency?: number;\n}\n\nexport type ParsedUpdateSubscriptionRequest = UpdateSubscriptionRequest;\n\nexport interface ReplaySubscriptionRequest {\n\tfromBlock: number;\n\ttoBlock: number;\n\tforce?: string;\n}\n\nexport type ParsedReplaySubscriptionRequest = ReplaySubscriptionRequest;\n\nexport interface SubscriptionSummary {\n\tid: string;\n\tname: string;\n\tstatus: SubscriptionStatus;\n\tsubgraphName: string;\n\ttableName: string;\n\tformat: SubscriptionFormat;\n\truntime: SubscriptionRuntime | null;\n\turl: string;\n\tlastDeliveryAt: string | null;\n\tlastSuccessAt: string | null;\n\tcreatedAt: string;\n\tupdatedAt: string;\n}\n\nexport interface SubscriptionDetail extends SubscriptionSummary {\n\tfilter: Record<string, unknown>;\n\tauthConfig: Record<string, unknown>;\n\tmaxRetries: number;\n\ttimeoutMs: number;\n\tconcurrency: number;\n\tcircuitFailures: number;\n\tcircuitOpenedAt: string | null;\n\tlastError: string | null;\n}\n\nexport interface CreateSubscriptionResponse {\n\tsubscription: SubscriptionDetail;\n\t/** Plaintext signing secret — surfaced ONCE. Store it server-side. */\n\tsigningSecret: string;\n}\n\nexport interface RotateSecretResponse {\n\tsubscription: SubscriptionDetail;\n\tsigningSecret: string;\n}\n\nexport interface DeliveryRow {\n\tid: string;\n\tattempt: number;\n\tstatusCode: number | null;\n\terrorMessage: string | null;\n\tdurationMs: number | null;\n\tresponseBody: string | null;\n\tdispatchedAt: string;\n}\n\nexport interface ReplayResult {\n\treplayId: string;\n\tenqueuedCount: number;\n\tscannedCount: number;\n}\n\nexport interface DeadRow {\n\tid: string;\n\teventType: string;\n\tattempt: number;\n\tblockHeight: number;\n\ttxId: string | null;\n\tpayload: Record<string, unknown>;\n\tfailedAt: string | null;\n\tcreatedAt: string;\n}\n\nexport interface SubscriptionSchemaColumn {\n\ttype?: unknown;\n}\n\nexport interface SubscriptionSchemaTable {\n\tcolumns: Record<string, SubscriptionSchemaColumn>;\n}\n\nexport type SubscriptionSchemaTables = Record<string, SubscriptionSchemaTable>;\n\nconst SCALAR_COLUMN_TYPES = new Set([\n\t\"text\",\n\t\"uint\",\n\t\"int\",\n\t\"principal\",\n\t\"boolean\",\n\t\"timestamp\",\n]);\n\nconst COMPARISON_COLUMN_TYPES = new Set([\"uint\", \"int\", \"timestamp\"]);\n\nfunction formatIssuePath(path: PropertyKey[]): string {\n\treturn path.length > 0 ? `${path.map(String).join(\".\")}: ` : \"\";\n}\n\nexport function formatSubscriptionSchemaErrors(error: z.ZodError): string[] {\n\treturn error.issues.map(\n\t\t(issue) => `${formatIssuePath(issue.path)}${issue.message}`,\n\t);\n}\n\nfunction operatorForClause(clause: SubscriptionFilterClause): string {\n\tif (clause === null || typeof clause !== \"object\" || Array.isArray(clause)) {\n\t\treturn \"eq\";\n\t}\n\treturn Object.keys(clause)[0] ?? \"eq\";\n}\n\nexport function validateSubscriptionFilterForTable(input: {\n\tsubgraphName?: string;\n\ttableName: string;\n\tfilter?: unknown;\n\ttables: SubscriptionSchemaTables;\n}): string[] {\n\tconst errors: string[] = [];\n\tconst table = input.tables[input.tableName];\n\tif (!table) {\n\t\tconst names = Object.keys(input.tables);\n\t\terrors.push(\n\t\t\t`Unknown table \"${input.tableName}\"${\n\t\t\t\tinput.subgraphName ? ` in subgraph \"${input.subgraphName}\"` : \"\"\n\t\t\t}.${names.length > 0 ? ` Available tables: ${names.join(\", \")}.` : \"\"}`,\n\t\t);\n\t\treturn errors;\n\t}\n\n\tif (input.filter === undefined) return errors;\n\n\tconst parsed = SubscriptionFilterSchema.safeParse(input.filter);\n\tif (!parsed.success) {\n\t\treturn formatSubscriptionSchemaErrors(parsed.error);\n\t}\n\n\tfor (const [field, clause] of Object.entries(parsed.data)) {\n\t\tconst column = table.columns[field];\n\t\tif (!column) {\n\t\t\terrors.push(\n\t\t\t\t`Unknown filter field \"${field}\" on table \"${input.tableName}\".`,\n\t\t\t);\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst columnType =\n\t\t\ttypeof column.type === \"string\" ? column.type.toLowerCase() : \"\";\n\t\tif (!SCALAR_COLUMN_TYPES.has(columnType)) {\n\t\t\terrors.push(\n\t\t\t\t`Filter field \"${field}\" has unsupported type \"${columnType || \"unknown\"}\"; subscription filters require scalar columns.`,\n\t\t\t);\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst operator = operatorForClause(clause);\n\t\tif (\n\t\t\t(operator === \"gt\" ||\n\t\t\t\toperator === \"gte\" ||\n\t\t\t\toperator === \"lt\" ||\n\t\t\t\toperator === \"lte\") &&\n\t\t\t!COMPARISON_COLUMN_TYPES.has(columnType)\n\t\t) {\n\t\t\terrors.push(\n\t\t\t\t`Operator \"${operator}\" is not supported for ${columnType} field \"${field}\".`,\n\t\t\t);\n\t\t}\n\t}\n\n\treturn errors;\n}\n"
9
9
  ],
10
10
  "mappings": "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",
@@ -40,6 +40,7 @@ interface SubgraphSummary {
40
40
  progress: number;
41
41
  blocksRemaining?: number;
42
42
  syncMode?: "sync" | "reindex";
43
+ resourceWarning?: SubgraphResourceWarning;
43
44
  gapCount: number;
44
45
  integrity: "complete" | "gaps_detected";
45
46
  createdAt: string;
@@ -66,6 +67,7 @@ interface SubgraphSyncInfo {
66
67
  processedBlocks?: number;
67
68
  totalBlocks?: number;
68
69
  progress: number;
70
+ resourceWarning?: SubgraphResourceWarning;
69
71
  gaps: {
70
72
  count: number
71
73
  totalMissingBlocks: number
@@ -73,6 +75,14 @@ interface SubgraphSyncInfo {
73
75
  };
74
76
  integrity: "complete" | "gaps_detected";
75
77
  }
78
+ interface SubgraphResourceWarning {
79
+ code: "HOBBY_LARGE_REINDEX";
80
+ message: string;
81
+ plan: "hobby";
82
+ blockRange: number;
83
+ processorMemoryMb: number;
84
+ recommendedPlan: "launch";
85
+ }
76
86
  interface SubgraphDetail {
77
87
  name: string;
78
88
  version: string;
@@ -139,4 +149,4 @@ interface SubgraphQueryParams {
139
149
  fields?: string;
140
150
  filters?: Record<string, string>;
141
151
  }
142
- export { SubgraphSyncInfo, SubgraphSummary, SubgraphQueryParams, SubgraphGapsResponse, SubgraphGapRange, SubgraphGapEntry, SubgraphDetail, ReindexResponse, DeploySubgraphResponse, DeploySubgraphRequestSchema, DeploySubgraphRequest };
152
+ export { SubgraphSyncInfo, SubgraphSummary, SubgraphResourceWarning, SubgraphQueryParams, SubgraphGapsResponse, SubgraphGapRange, SubgraphGapEntry, SubgraphDetail, ReindexResponse, DeploySubgraphResponse, DeploySubgraphRequestSchema, DeploySubgraphRequest };
@@ -2,7 +2,7 @@
2
2
  "version": 3,
3
3
  "sources": ["../src/schemas/subgraphs.ts"],
4
4
  "sourcesContent": [
5
- "import { z } from \"zod/v4\";\n\n// ── Deploy Subgraph Request ─────────────────────────────────────────────────\n\nexport interface DeploySubgraphRequest {\n\tname: string;\n\tversion?: string;\n\tdescription?: string;\n\tsources: Record<string, Record<string, unknown>>;\n\tschema: Record<string, unknown>;\n\thandlerCode: string;\n\t/** Override the definition's startBlock for this deploy only. */\n\tstartBlock?: number;\n\t/** Original TypeScript source, persisted so chat can read/diff/edit later. */\n\tsourceCode?: string;\n}\n\nexport const DeploySubgraphRequestSchema: z.ZodType<DeploySubgraphRequest> =\n\tz.object({\n\t\tname: z\n\t\t\t.string()\n\t\t\t.regex(/^[a-z0-9-]+$/, \"lowercase alphanumeric + hyphens only\")\n\t\t\t.max(63),\n\t\tversion: z.string().optional(),\n\t\tdescription: z.string().optional(),\n\t\tsources: z\n\t\t\t.record(z.string(), z.record(z.string(), z.unknown()))\n\t\t\t.refine(\n\t\t\t\t(s) => Object.keys(s).length > 0,\n\t\t\t\t\"Must have at least one source\",\n\t\t\t),\n\t\tschema: z.record(z.string(), z.unknown()),\n\t\thandlerCode: z.string().max(1_048_576, \"handler code exceeds 1MB limit\"),\n\t\tstartBlock: z.number().int().nonnegative().optional(),\n\t\tsourceCode: z\n\t\t\t.string()\n\t\t\t.max(1_048_576, \"source code exceeds 1MB limit\")\n\t\t\t.optional(),\n\t});\n\nexport interface DeploySubgraphResponse {\n\taction: \"created\" | \"unchanged\" | \"updated\" | \"reindexed\";\n\tsubgraphId: string;\n\tversion: string;\n\tmessage: string;\n\toperationId?: string;\n\treindexStarted?: boolean;\n\tdiff?: {\n\t\taddedTables: string[];\n\t\tremovedTables: string[];\n\t\taddedColumns: Record<string, string[]>;\n\t\tbreakingChanges: string[];\n\t};\n}\n\n// Subgraph API response types\n\nexport interface SubgraphSummary {\n\tname: string;\n\tversion: string;\n\tstatus: string;\n\tlastProcessedBlock: number;\n\ttotalProcessed: number;\n\ttotalErrors: number;\n\ttables: string[];\n\tchainTip: number;\n\tsourceChainTip?: number;\n\ttargetBlock?: number;\n\tprogress: number;\n\tblocksRemaining?: number;\n\tsyncMode?: \"sync\" | \"reindex\";\n\tgapCount: number;\n\tintegrity: \"complete\" | \"gaps_detected\";\n\tcreatedAt: string;\n}\n\nexport interface SubgraphGapRange {\n\tstart: number;\n\tend: number;\n\tsize: number;\n\treason: string;\n}\n\nexport interface SubgraphSyncInfo {\n\tstatus: \"synced\" | \"catching_up\" | \"reindexing\" | \"error\";\n\tmode?: \"sync\" | \"reindex\";\n\tstartBlock: number;\n\tlastProcessedBlock: number;\n\t/**\n\t * Backward-compatible denominator for progress displays. During reindexing,\n\t * this is the reindex target block rather than the live source chain tip.\n\t */\n\tchainTip: number;\n\tsourceChainTip?: number;\n\ttargetBlock?: number;\n\tblocksRemaining: number;\n\tprocessedBlocks?: number;\n\ttotalBlocks?: number;\n\tprogress: number;\n\tgaps: {\n\t\tcount: number;\n\t\ttotalMissingBlocks: number;\n\t\tranges: SubgraphGapRange[];\n\t};\n\tintegrity: \"complete\" | \"gaps_detected\";\n}\n\nexport interface SubgraphDetail {\n\tname: string;\n\tversion: string;\n\tschemaHash?: string;\n\tstatus: string;\n\tlastProcessedBlock: number;\n\tdescription?: string;\n\tsources?: Record<string, unknown>;\n\tdefinition?: Record<string, unknown>;\n\thealth: {\n\t\ttotalProcessed: number;\n\t\ttotalErrors: number;\n\t\terrorRate: number;\n\t\tlastError: string | null;\n\t\tlastErrorAt: string | null;\n\t};\n\tsync: SubgraphSyncInfo;\n\ttables: Record<\n\t\tstring,\n\t\t{\n\t\t\tendpoint: string;\n\t\t\tcolumns: Record<\n\t\t\t\tstring,\n\t\t\t\t{\n\t\t\t\t\ttype: string;\n\t\t\t\t\tnullable?: boolean;\n\t\t\t\t\tindexed?: boolean;\n\t\t\t\t\tsearchable?: boolean;\n\t\t\t\t\tdefault?: string | number | boolean;\n\t\t\t\t}\n\t\t\t>;\n\t\t\trowCount: number;\n\t\t\texample: string;\n\t\t\tindexes?: string[][];\n\t\t\tuniqueKeys?: string[][];\n\t\t}\n\t>;\n\tcreatedAt: string;\n\tupdatedAt: string;\n}\n\nexport interface SubgraphGapEntry {\n\tstart: number;\n\tend: number;\n\tsize: number;\n\treason: string;\n\tdetectedAt: string;\n\tresolvedAt: string | null;\n}\n\nexport interface SubgraphGapsResponse {\n\tdata: SubgraphGapEntry[];\n\tmeta: {\n\t\ttotal: number;\n\t\ttotalMissingBlocks: number;\n\t\tlimit: number;\n\t\toffset: number;\n\t};\n}\n\nexport interface ReindexResponse {\n\tmessage: string;\n\tfromBlock: number;\n\ttoBlock: number | string;\n\toperationId?: string;\n\tstatus?: \"queued\" | \"running\" | \"cancel_requested\";\n}\n\nexport interface SubgraphQueryParams {\n\tsort?: string;\n\torder?: string;\n\tlimit?: number;\n\toffset?: number;\n\tfields?: string;\n\tfilters?: Record<string, string>;\n}\n"
5
+ "import { z } from \"zod/v4\";\n\n// ── Deploy Subgraph Request ─────────────────────────────────────────────────\n\nexport interface DeploySubgraphRequest {\n\tname: string;\n\tversion?: string;\n\tdescription?: string;\n\tsources: Record<string, Record<string, unknown>>;\n\tschema: Record<string, unknown>;\n\thandlerCode: string;\n\t/** Override the definition's startBlock for this deploy only. */\n\tstartBlock?: number;\n\t/** Original TypeScript source, persisted so chat can read/diff/edit later. */\n\tsourceCode?: string;\n}\n\nexport const DeploySubgraphRequestSchema: z.ZodType<DeploySubgraphRequest> =\n\tz.object({\n\t\tname: z\n\t\t\t.string()\n\t\t\t.regex(/^[a-z0-9-]+$/, \"lowercase alphanumeric + hyphens only\")\n\t\t\t.max(63),\n\t\tversion: z.string().optional(),\n\t\tdescription: z.string().optional(),\n\t\tsources: z\n\t\t\t.record(z.string(), z.record(z.string(), z.unknown()))\n\t\t\t.refine(\n\t\t\t\t(s) => Object.keys(s).length > 0,\n\t\t\t\t\"Must have at least one source\",\n\t\t\t),\n\t\tschema: z.record(z.string(), z.unknown()),\n\t\thandlerCode: z.string().max(1_048_576, \"handler code exceeds 1MB limit\"),\n\t\tstartBlock: z.number().int().nonnegative().optional(),\n\t\tsourceCode: z\n\t\t\t.string()\n\t\t\t.max(1_048_576, \"source code exceeds 1MB limit\")\n\t\t\t.optional(),\n\t});\n\nexport interface DeploySubgraphResponse {\n\taction: \"created\" | \"unchanged\" | \"updated\" | \"reindexed\";\n\tsubgraphId: string;\n\tversion: string;\n\tmessage: string;\n\toperationId?: string;\n\treindexStarted?: boolean;\n\tdiff?: {\n\t\taddedTables: string[];\n\t\tremovedTables: string[];\n\t\taddedColumns: Record<string, string[]>;\n\t\tbreakingChanges: string[];\n\t};\n}\n\n// Subgraph API response types\n\nexport interface SubgraphSummary {\n\tname: string;\n\tversion: string;\n\tstatus: string;\n\tlastProcessedBlock: number;\n\ttotalProcessed: number;\n\ttotalErrors: number;\n\ttables: string[];\n\tchainTip: number;\n\tsourceChainTip?: number;\n\ttargetBlock?: number;\n\tprogress: number;\n\tblocksRemaining?: number;\n\tsyncMode?: \"sync\" | \"reindex\";\n\tresourceWarning?: SubgraphResourceWarning;\n\tgapCount: number;\n\tintegrity: \"complete\" | \"gaps_detected\";\n\tcreatedAt: string;\n}\n\nexport interface SubgraphGapRange {\n\tstart: number;\n\tend: number;\n\tsize: number;\n\treason: string;\n}\n\nexport interface SubgraphSyncInfo {\n\tstatus: \"synced\" | \"catching_up\" | \"reindexing\" | \"error\";\n\tmode?: \"sync\" | \"reindex\";\n\tstartBlock: number;\n\tlastProcessedBlock: number;\n\t/**\n\t * Backward-compatible denominator for progress displays. During reindexing,\n\t * this is the reindex target block rather than the live source chain tip.\n\t */\n\tchainTip: number;\n\tsourceChainTip?: number;\n\ttargetBlock?: number;\n\tblocksRemaining: number;\n\tprocessedBlocks?: number;\n\ttotalBlocks?: number;\n\tprogress: number;\n\tresourceWarning?: SubgraphResourceWarning;\n\tgaps: {\n\t\tcount: number;\n\t\ttotalMissingBlocks: number;\n\t\tranges: SubgraphGapRange[];\n\t};\n\tintegrity: \"complete\" | \"gaps_detected\";\n}\n\nexport interface SubgraphResourceWarning {\n\tcode: \"HOBBY_LARGE_REINDEX\";\n\tmessage: string;\n\tplan: \"hobby\";\n\tblockRange: number;\n\tprocessorMemoryMb: number;\n\trecommendedPlan: \"launch\";\n}\n\nexport interface SubgraphDetail {\n\tname: string;\n\tversion: string;\n\tschemaHash?: string;\n\tstatus: string;\n\tlastProcessedBlock: number;\n\tdescription?: string;\n\tsources?: Record<string, unknown>;\n\tdefinition?: Record<string, unknown>;\n\thealth: {\n\t\ttotalProcessed: number;\n\t\ttotalErrors: number;\n\t\terrorRate: number;\n\t\tlastError: string | null;\n\t\tlastErrorAt: string | null;\n\t};\n\tsync: SubgraphSyncInfo;\n\ttables: Record<\n\t\tstring,\n\t\t{\n\t\t\tendpoint: string;\n\t\t\tcolumns: Record<\n\t\t\t\tstring,\n\t\t\t\t{\n\t\t\t\t\ttype: string;\n\t\t\t\t\tnullable?: boolean;\n\t\t\t\t\tindexed?: boolean;\n\t\t\t\t\tsearchable?: boolean;\n\t\t\t\t\tdefault?: string | number | boolean;\n\t\t\t\t}\n\t\t\t>;\n\t\t\trowCount: number;\n\t\t\texample: string;\n\t\t\tindexes?: string[][];\n\t\t\tuniqueKeys?: string[][];\n\t\t}\n\t>;\n\tcreatedAt: string;\n\tupdatedAt: string;\n}\n\nexport interface SubgraphGapEntry {\n\tstart: number;\n\tend: number;\n\tsize: number;\n\treason: string;\n\tdetectedAt: string;\n\tresolvedAt: string | null;\n}\n\nexport interface SubgraphGapsResponse {\n\tdata: SubgraphGapEntry[];\n\tmeta: {\n\t\ttotal: number;\n\t\ttotalMissingBlocks: number;\n\t\tlimit: number;\n\t\toffset: number;\n\t};\n}\n\nexport interface ReindexResponse {\n\tmessage: string;\n\tfromBlock: number;\n\ttoBlock: number | string;\n\toperationId?: string;\n\tstatus?: \"queued\" | \"running\" | \"cancel_requested\";\n}\n\nexport interface SubgraphQueryParams {\n\tsort?: string;\n\torder?: string;\n\tlimit?: number;\n\toffset?: number;\n\tfields?: string;\n\tfilters?: Record<string, string>;\n}\n"
6
6
  ],
7
7
  "mappings": ";;;;;;;;;;;;;;;;;AAAA;AAiBO,IAAM,8BACZ,EAAE,OAAO;AAAA,EACR,MAAM,EACJ,OAAO,EACP,MAAM,gBAAgB,uCAAuC,EAC7D,IAAI,EAAE;AAAA,EACR,SAAS,EAAE,OAAO,EAAE,SAAS;AAAA,EAC7B,aAAa,EAAE,OAAO,EAAE,SAAS;AAAA,EACjC,SAAS,EACP,OAAO,EAAE,OAAO,GAAG,EAAE,OAAO,EAAE,OAAO,GAAG,EAAE,QAAQ,CAAC,CAAC,EACpD,OACA,CAAC,MAAM,OAAO,KAAK,CAAC,EAAE,SAAS,GAC/B,+BACD;AAAA,EACD,QAAQ,EAAE,OAAO,EAAE,OAAO,GAAG,EAAE,QAAQ,CAAC;AAAA,EACxC,aAAa,EAAE,OAAO,EAAE,IAAI,SAAW,gCAAgC;AAAA,EACvE,YAAY,EAAE,OAAO,EAAE,IAAI,EAAE,YAAY,EAAE,SAAS;AAAA,EACpD,YAAY,EACV,OAAO,EACP,IAAI,SAAW,+BAA+B,EAC9C,SAAS;AACZ,CAAC;",
8
8
  "debugId": "AA56811DA75420EF64756E2164756E21",
@@ -20,6 +20,7 @@ interface SubgraphSyncInfo {
20
20
  processedBlocks?: number;
21
21
  totalBlocks?: number;
22
22
  progress: number;
23
+ resourceWarning?: SubgraphResourceWarning;
23
24
  gaps: {
24
25
  count: number
25
26
  totalMissingBlocks: number
@@ -27,6 +28,14 @@ interface SubgraphSyncInfo {
27
28
  };
28
29
  integrity: "complete" | "gaps_detected";
29
30
  }
31
+ interface SubgraphResourceWarning {
32
+ code: "HOBBY_LARGE_REINDEX";
33
+ message: string;
34
+ plan: "hobby";
35
+ blockRange: number;
36
+ processorMemoryMb: number;
37
+ recommendedPlan: "launch";
38
+ }
30
39
  interface SubgraphDetail {
31
40
  name: string;
32
41
  version: string;
@@ -0,0 +1,33 @@
1
+ import { type Kysely, sql } from "kysely";
2
+
3
+ /**
4
+ * `processed_stripe_events` — webhook idempotency table.
5
+ *
6
+ * Stripe redelivers events on any non-2xx and on its own retry policy.
7
+ * Without dedup, a duplicate `customer.subscription.updated` could
8
+ * double-apply state writes; a late replay of an old `invoice.paid`
9
+ * could clear a current-cycle freeze. We INSERT ON CONFLICT DO NOTHING
10
+ * keyed on `event_id` and bail early if the event has already been
11
+ * processed.
12
+ *
13
+ * Retention: 90 days. Stripe's retry window is shorter, but we keep
14
+ * extra runway for forensics.
15
+ */
16
+
17
+ export async function up(db: Kysely<unknown>): Promise<void> {
18
+ await sql`
19
+ CREATE TABLE processed_stripe_events (
20
+ event_id TEXT PRIMARY KEY,
21
+ event_type TEXT NOT NULL,
22
+ processed_at TIMESTAMPTZ NOT NULL DEFAULT now()
23
+ )
24
+ `.execute(db);
25
+ await sql`
26
+ CREATE INDEX processed_stripe_events_processed_at_idx
27
+ ON processed_stripe_events (processed_at)
28
+ `.execute(db);
29
+ }
30
+
31
+ export async function down(db: Kysely<unknown>): Promise<void> {
32
+ await sql`DROP TABLE IF EXISTS processed_stripe_events`.execute(db);
33
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@secondlayer/shared",
3
- "version": "5.0.0",
3
+ "version": "5.1.0",
4
4
  "type": "module",
5
5
  "main": "./dist/src/index.js",
6
6
  "types": "./dist/src/index.d.ts",