@secondlayer/shared 6.3.1 → 6.3.3

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.
@@ -19,12 +19,12 @@ var BYTES_PER_GB = 1024 ** 3;
19
19
  function alloc(totalMb, totalCpus) {
20
20
  return {
21
21
  postgres: {
22
- memoryMb: Math.floor(totalMb * 0.5),
23
- cpus: round2(totalCpus * 0.5)
22
+ memoryMb: Math.floor(totalMb * 0.25),
23
+ cpus: round2(totalCpus * 0.25)
24
24
  },
25
25
  processor: {
26
- memoryMb: Math.floor(totalMb * 0.3),
27
- cpus: round2(totalCpus * 0.3)
26
+ memoryMb: Math.floor(totalMb * 0.55),
27
+ cpus: round2(totalCpus * 0.55)
28
28
  },
29
29
  api: {
30
30
  memoryMb: Math.floor(totalMb * 0.2),
@@ -262,5 +262,5 @@ export {
262
262
  getComputeUsage
263
263
  };
264
264
 
265
- //# debugId=7B39A65B4DBA7F2D64756E2164756E21
265
+ //# debugId=EDCFAE397F4E86AB64756E2164756E21
266
266
  //# 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: number = 1024 ** 3;\n\nexport type AccountPlanId = \"none\" | PlanId;\nexport type PlanId = \"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\t/** Annual subscription price in cents. null = no self-serve annual price. */\n\tannualPriceCents: 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 monthly recurring tier price. null for enterprise. */\n\tstripeLookupKey: string | null;\n\t/** Stripe `lookup_key` for annual recurring tier price. null for enterprise. */\n\tstripeAnnualLookupKey: 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 — PG 60% / proc 25% / api 15%\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\tlaunch: {\n\t\tid: \"launch\",\n\t\tdisplayName: \"Launch\",\n\t\tmonthlyPriceCents: 9_900, // $99\n\t\tannualPriceCents: 99_000, // 2 months free\n\t\ttotalCpus: 2,\n\t\ttotalMemoryMb: 6_144,\n\t\tstorageLimitMb: 102_400, // 100 GB\n\t\tcontainers: alloc(6_144, 2),\n\t\ttagline: \"Real product\",\n\t\tfeatures: [\n\t\t\t\"2 vCPU · 6 GB RAM\",\n\t\t\t\"100 GB storage · always-on\",\n\t\t\t\"3-5 contracts\",\n\t\t\t\"Production 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\tstripeAnnualLookupKey: \"secondlayer_launch_yearly\",\n\t},\n\tscale: {\n\t\tid: \"scale\",\n\t\tdisplayName: \"Scale\",\n\t\tmonthlyPriceCents: 29_900, // $299\n\t\tannualPriceCents: 299_000, // 2 months free\n\t\ttotalCpus: 8,\n\t\ttotalMemoryMb: 24_576,\n\t\tstorageLimitMb: 512_000, // 500 GB\n\t\tcontainers: alloc(24_576, 8),\n\t\ttagline: \"Full indexing\",\n\t\tfeatures: [\n\t\t\t\"8 vCPU · 24 GB RAM\",\n\t\t\t\"500 GB storage · always-on\",\n\t\t\t\"Heavy history + replay\",\n\t\t\t\"24h SLA · priority support\",\n\t\t],\n\t\tstripeLookupKey: \"secondlayer_scale_monthly\",\n\t\tstripeAnnualLookupKey: \"secondlayer_scale_yearly\",\n\t},\n\tenterprise: {\n\t\tid: \"enterprise\",\n\t\tdisplayName: \"Enterprise\",\n\t\tmonthlyPriceCents: null,\n\t\tannualPriceCents: null,\n\t\ttotalCpus: 16,\n\t\ttotalMemoryMb: 65_536,\n\t\tstorageLimitMb: -1,\n\t\tcontainers: alloc(65_536, 16),\n\t\ttagline: \"Whatever needed\",\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\tstripeAnnualLookupKey: null,\n\t},\n};\n\nexport const PLAN_IDS: readonly PlanId[] = [\"launch\", \"scale\", \"enterprise\"];\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 0;\n\tif (planDef.storageLimitMb < 0) return Number.POSITIVE_INFINITY;\n\treturn planDef.storageLimitMb * 1024 * 1024;\n}\n\n/** Paid tiers bill $2/GB over allowance. Accounts with no plan do not accrue overage. */\nexport function hasStorageOverage(plan: string): boolean {\n\treturn plan !== \"none\" && 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 AccountPlanId = \"none\" | PlanId;\nexport type PlanId = \"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\t/** Annual subscription price in cents. null = no self-serve annual price. */\n\tannualPriceCents: 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 monthly recurring tier price. null for enterprise. */\n\tstripeLookupKey: string | null;\n\t/** Stripe `lookup_key` for annual recurring tier price. null for enterprise. */\n\tstripeAnnualLookupKey: string | null;\n}\n\n// ── Allocation helpers ──────────────────────────────────────────────\n//\n// Allocation within a plan (3 containers per tenant):\n// Default split (paid tiers) — PG 25% / proc 55% / api 20%\n// Sub-1GB total — PG 60% / proc 25% / api 15%\n//\n// Why proc-heavy: the subgraph processor is CPU-bound during backfill\n// (event decode + handler exec + DB writes), while PG idles at <1% CPU\n// observed during steady-state indexing on Launch tier. The 2026-05-13\n// rebalance shifts CPU from idle PG to active proc to recover backfill\n// throughput regressed by the move from shared infra to per-tenant\n// containers — Launch went from 5 blocks/min → ~100 blocks/min just by\n// raising the proc's `--cpus` allotment.\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.25),\n\t\t\tcpus: round2(totalCpus * 0.25),\n\t\t},\n\t\tprocessor: {\n\t\t\tmemoryMb: Math.floor(totalMb * 0.55),\n\t\t\tcpus: round2(totalCpus * 0.55),\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\tlaunch: {\n\t\tid: \"launch\",\n\t\tdisplayName: \"Launch\",\n\t\tmonthlyPriceCents: 9_900, // $99\n\t\tannualPriceCents: 99_000, // 2 months free\n\t\ttotalCpus: 2,\n\t\ttotalMemoryMb: 6_144,\n\t\tstorageLimitMb: 102_400, // 100 GB\n\t\tcontainers: alloc(6_144, 2),\n\t\ttagline: \"Real product\",\n\t\tfeatures: [\n\t\t\t\"2 vCPU · 6 GB RAM\",\n\t\t\t\"100 GB storage · always-on\",\n\t\t\t\"3-5 contracts\",\n\t\t\t\"Production 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\tstripeAnnualLookupKey: \"secondlayer_launch_yearly\",\n\t},\n\tscale: {\n\t\tid: \"scale\",\n\t\tdisplayName: \"Scale\",\n\t\tmonthlyPriceCents: 29_900, // $299\n\t\tannualPriceCents: 299_000, // 2 months free\n\t\ttotalCpus: 8,\n\t\ttotalMemoryMb: 24_576,\n\t\tstorageLimitMb: 512_000, // 500 GB\n\t\tcontainers: alloc(24_576, 8),\n\t\ttagline: \"Full indexing\",\n\t\tfeatures: [\n\t\t\t\"8 vCPU · 24 GB RAM\",\n\t\t\t\"500 GB storage · always-on\",\n\t\t\t\"Heavy history + replay\",\n\t\t\t\"24h SLA · priority support\",\n\t\t],\n\t\tstripeLookupKey: \"secondlayer_scale_monthly\",\n\t\tstripeAnnualLookupKey: \"secondlayer_scale_yearly\",\n\t},\n\tenterprise: {\n\t\tid: \"enterprise\",\n\t\tdisplayName: \"Enterprise\",\n\t\tmonthlyPriceCents: null,\n\t\tannualPriceCents: null,\n\t\ttotalCpus: 16,\n\t\ttotalMemoryMb: 65_536,\n\t\tstorageLimitMb: -1,\n\t\tcontainers: alloc(65_536, 16),\n\t\ttagline: \"Whatever needed\",\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\tstripeAnnualLookupKey: null,\n\t},\n};\n\nexport const PLAN_IDS: readonly PlanId[] = [\"launch\", \"scale\", \"enterprise\"];\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 0;\n\tif (planDef.storageLimitMb < 0) return Number.POSITIVE_INFINITY;\n\treturn planDef.storageLimitMb * 1024 * 1024;\n}\n\n/** Paid tiers bill $2/GB over allowance. Accounts with no plan do not accrue overage. */\nexport function hasStorageOverage(plan: string): boolean {\n\treturn plan !== \"none\" && 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\" && tenant.status !== \"limit_warning\") 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": "7B39A65B4DBA7F2D64756E2164756E21",
8
+ "mappings": "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",
9
+ "debugId": "EDCFAE397F4E86AB64756E2164756E21",
10
10
  "names": []
11
11
  }
@@ -718,10 +718,22 @@ declare function createSubgraphOperation(db: Kysely<Database>, data: {
718
718
  declare function findActiveSubgraphOperation(db: Kysely<Database>, subgraphId: string): Promise<SubgraphOperation | null>;
719
719
  declare function requestSubgraphOperationCancel(db: Kysely<Database>, subgraphId: string): Promise<SubgraphOperation | null>;
720
720
  declare function requestSubgraphOperationsCancelForDelete(db: Kysely<Database>, subgraphId: string): Promise<SubgraphOperation[]>;
721
+ /**
722
+ * Poll until no active subgraph operations remain for the subgraph or until
723
+ * `timeoutMs` elapses. Returns true if all active operations cleared, false
724
+ * if we timed out. Callers should use this before `DROP SCHEMA` so the active
725
+ * processor has a chance to observe `cancel_requested` and release its row /
726
+ * advisory locks. Without this, the DROP blocks behind the live transaction
727
+ * and the API socket times out before the lock releases.
728
+ */
729
+ declare function waitForSubgraphOperationsClear(db: Kysely<Database>, subgraphId: string, opts?: {
730
+ timeoutMs?: number
731
+ pollMs?: number
732
+ }): Promise<boolean>;
721
733
  declare function claimSubgraphOperation(db: Kysely<Database>, lockedBy: string): Promise<SubgraphOperation | null>;
722
734
  declare function heartbeatSubgraphOperation(db: Kysely<Database>, operationId: string, lockedBy: string): Promise<void>;
723
735
  declare function getSubgraphOperation(db: Kysely<Database>, operationId: string): Promise<SubgraphOperation | null>;
724
736
  declare function completeSubgraphOperation(db: Kysely<Database>, operationId: string, lockedBy: string, processedBlocks: number): Promise<void>;
725
737
  declare function cancelSubgraphOperation(db: Kysely<Database>, operationId: string, lockedBy: string, processedBlocks: number): Promise<void>;
726
738
  declare function failSubgraphOperation(db: Kysely<Database>, operationId: string, lockedBy: string, error: string, processedBlocks?: number): Promise<void>;
727
- export { requestSubgraphOperationsCancelForDelete, requestSubgraphOperationCancel, isActiveSubgraphOperationConflict, heartbeatSubgraphOperation, getSubgraphOperation, findActiveSubgraphOperation, failSubgraphOperation, createSubgraphOperation, completeSubgraphOperation, claimSubgraphOperation, cancelSubgraphOperation };
739
+ export { waitForSubgraphOperationsClear, requestSubgraphOperationsCancelForDelete, requestSubgraphOperationCancel, isActiveSubgraphOperationConflict, heartbeatSubgraphOperation, getSubgraphOperation, findActiveSubgraphOperation, failSubgraphOperation, createSubgraphOperation, completeSubgraphOperation, claimSubgraphOperation, cancelSubgraphOperation };
@@ -42,6 +42,18 @@ async function requestSubgraphOperationCancel(db, subgraphId) {
42
42
  async function requestSubgraphOperationsCancelForDelete(db, subgraphId) {
43
43
  return await db.updateTable("subgraph_operations").set({ cancel_requested: true, updated_at: new Date }).where("subgraph_id", "=", subgraphId).where("status", "in", ACTIVE_STATUSES).returningAll().execute();
44
44
  }
45
+ async function waitForSubgraphOperationsClear(db, subgraphId, opts) {
46
+ const timeoutMs = opts?.timeoutMs ?? 30000;
47
+ const pollMs = opts?.pollMs ?? 500;
48
+ const deadline = Date.now() + timeoutMs;
49
+ while (Date.now() < deadline) {
50
+ const active = await db.selectFrom("subgraph_operations").select("id").where("subgraph_id", "=", subgraphId).where("status", "in", ACTIVE_STATUSES).limit(1).executeTakeFirst();
51
+ if (!active)
52
+ return true;
53
+ await new Promise((r) => setTimeout(r, pollMs));
54
+ }
55
+ return false;
56
+ }
45
57
  async function claimSubgraphOperation(db, lockedBy) {
46
58
  const result = await sql`
47
59
  UPDATE subgraph_operations
@@ -111,6 +123,7 @@ async function failSubgraphOperation(db, operationId, lockedBy, error, processed
111
123
  }).where("id", "=", operationId).where("locked_by", "=", lockedBy).execute();
112
124
  }
113
125
  export {
126
+ waitForSubgraphOperationsClear,
114
127
  requestSubgraphOperationsCancelForDelete,
115
128
  requestSubgraphOperationCancel,
116
129
  isActiveSubgraphOperationConflict,
@@ -124,5 +137,5 @@ export {
124
137
  cancelSubgraphOperation
125
138
  };
126
139
 
127
- //# debugId=E1C632440C2A33FE64756E2164756E21
140
+ //# debugId=276821D8C595B2AC64756E2164756E21
128
141
  //# sourceMappingURL=subgraph-operations.js.map
@@ -2,9 +2,9 @@
2
2
  "version": 3,
3
3
  "sources": ["../src/db/queries/subgraph-operations.ts"],
4
4
  "sourcesContent": [
5
- "import { type Kysely, sql } from \"kysely\";\nimport type {\n\tDatabase,\n\tSubgraphOperation,\n\tSubgraphOperationKind,\n\tSubgraphOperationStatus,\n} from \"../types.ts\";\n\nconst ACTIVE_STATUSES: SubgraphOperationStatus[] = [\"queued\", \"running\"];\n\nexport function isActiveSubgraphOperationConflict(err: unknown): boolean {\n\tif (!(err instanceof Error)) return false;\n\tconst candidate = err as Error & {\n\t\tcode?: string;\n\t\tconstraint?: string;\n\t\tconstraint_name?: string;\n\t};\n\treturn (\n\t\tcandidate.code === \"23505\" &&\n\t\t(candidate.constraint === \"subgraph_operations_active_unique\" ||\n\t\t\tcandidate.constraint_name === \"subgraph_operations_active_unique\")\n\t);\n}\n\nexport async function createSubgraphOperation(\n\tdb: Kysely<Database>,\n\tdata: {\n\t\tsubgraphId: string;\n\t\tsubgraphName: string;\n\t\taccountId?: string | null;\n\t\tkind: SubgraphOperationKind;\n\t\tfromBlock?: number;\n\t\ttoBlock?: number;\n\t},\n): Promise<SubgraphOperation> {\n\treturn await db\n\t\t.insertInto(\"subgraph_operations\")\n\t\t.values({\n\t\t\tsubgraph_id: data.subgraphId,\n\t\t\tsubgraph_name: data.subgraphName,\n\t\t\taccount_id: data.accountId ?? null,\n\t\t\tkind: data.kind,\n\t\t\tfrom_block: data.fromBlock ?? null,\n\t\t\tto_block: data.toBlock ?? null,\n\t\t})\n\t\t.returningAll()\n\t\t.executeTakeFirstOrThrow();\n}\n\nexport async function findActiveSubgraphOperation(\n\tdb: Kysely<Database>,\n\tsubgraphId: string,\n): Promise<SubgraphOperation | null> {\n\treturn (\n\t\t(await db\n\t\t\t.selectFrom(\"subgraph_operations\")\n\t\t\t.selectAll()\n\t\t\t.where(\"subgraph_id\", \"=\", subgraphId)\n\t\t\t.where(\"status\", \"in\", ACTIVE_STATUSES)\n\t\t\t.orderBy(\"created_at\", \"asc\")\n\t\t\t.executeTakeFirst()) ?? null\n\t);\n}\n\nexport async function requestSubgraphOperationCancel(\n\tdb: Kysely<Database>,\n\tsubgraphId: string,\n): Promise<SubgraphOperation | null> {\n\treturn (\n\t\t(await db\n\t\t\t.updateTable(\"subgraph_operations\")\n\t\t\t.set({ cancel_requested: true, updated_at: new Date() })\n\t\t\t.where(\"subgraph_id\", \"=\", subgraphId)\n\t\t\t.where(\"status\", \"in\", ACTIVE_STATUSES)\n\t\t\t.returningAll()\n\t\t\t.executeTakeFirst()) ?? null\n\t);\n}\n\nexport async function requestSubgraphOperationsCancelForDelete(\n\tdb: Kysely<Database>,\n\tsubgraphId: string,\n): Promise<SubgraphOperation[]> {\n\treturn await db\n\t\t.updateTable(\"subgraph_operations\")\n\t\t.set({ cancel_requested: true, updated_at: new Date() })\n\t\t.where(\"subgraph_id\", \"=\", subgraphId)\n\t\t.where(\"status\", \"in\", ACTIVE_STATUSES)\n\t\t.returningAll()\n\t\t.execute();\n}\n\nexport async function claimSubgraphOperation(\n\tdb: Kysely<Database>,\n\tlockedBy: string,\n): Promise<SubgraphOperation | null> {\n\tconst result = await sql<SubgraphOperation>`\n\t\tUPDATE subgraph_operations\n\t\tSET\n\t\t\tstatus = 'running',\n\t\t\tlocked_by = ${lockedBy},\n\t\t\tlocked_until = now() + interval '60 seconds',\n\t\t\tstarted_at = COALESCE(started_at, now()),\n\t\t\tupdated_at = now()\n\t\tWHERE id = (\n\t\t\tSELECT id\n\t\t\tFROM subgraph_operations\n\t\t\tWHERE\n\t\t\t\tstatus = 'queued'\n\t\t\t\tOR (\n\t\t\t\t\tstatus = 'running'\n\t\t\t\t\tAND (locked_until IS NULL OR locked_until < now())\n\t\t\t\t)\n\t\t\tORDER BY\n\t\t\t\tCASE WHEN status = 'queued' THEN 0 ELSE 1 END,\n\t\t\t\tcreated_at\n\t\t\tFOR UPDATE SKIP LOCKED\n\t\t\tLIMIT 1\n\t\t)\n\t\tRETURNING *\n\t`.execute(db);\n\treturn result.rows[0] ?? null;\n}\n\nexport async function heartbeatSubgraphOperation(\n\tdb: Kysely<Database>,\n\toperationId: string,\n\tlockedBy: string,\n): Promise<void> {\n\tawait db\n\t\t.updateTable(\"subgraph_operations\")\n\t\t.set({\n\t\t\tlocked_until: sql<Date>`now() + interval '60 seconds'`,\n\t\t\tupdated_at: new Date(),\n\t\t})\n\t\t.where(\"id\", \"=\", operationId)\n\t\t.where(\"status\", \"=\", \"running\")\n\t\t.where(\"locked_by\", \"=\", lockedBy)\n\t\t.execute();\n}\n\nexport async function getSubgraphOperation(\n\tdb: Kysely<Database>,\n\toperationId: string,\n): Promise<SubgraphOperation | null> {\n\treturn (\n\t\t(await db\n\t\t\t.selectFrom(\"subgraph_operations\")\n\t\t\t.selectAll()\n\t\t\t.where(\"id\", \"=\", operationId)\n\t\t\t.executeTakeFirst()) ?? null\n\t);\n}\n\nexport async function completeSubgraphOperation(\n\tdb: Kysely<Database>,\n\toperationId: string,\n\tlockedBy: string,\n\tprocessedBlocks: number,\n): Promise<void> {\n\tawait db\n\t\t.updateTable(\"subgraph_operations\")\n\t\t.set({\n\t\t\tstatus: \"completed\",\n\t\t\tfinished_at: new Date(),\n\t\t\tprocessed_blocks: processedBlocks,\n\t\t\tlocked_by: null,\n\t\t\tlocked_until: null,\n\t\t\tupdated_at: new Date(),\n\t\t})\n\t\t.where(\"id\", \"=\", operationId)\n\t\t.where(\"locked_by\", \"=\", lockedBy)\n\t\t.execute();\n}\n\nexport async function cancelSubgraphOperation(\n\tdb: Kysely<Database>,\n\toperationId: string,\n\tlockedBy: string,\n\tprocessedBlocks: number,\n): Promise<void> {\n\tawait db\n\t\t.updateTable(\"subgraph_operations\")\n\t\t.set({\n\t\t\tstatus: \"cancelled\",\n\t\t\tfinished_at: new Date(),\n\t\t\tprocessed_blocks: processedBlocks,\n\t\t\tlocked_by: null,\n\t\t\tlocked_until: null,\n\t\t\tupdated_at: new Date(),\n\t\t})\n\t\t.where(\"id\", \"=\", operationId)\n\t\t.where(\"locked_by\", \"=\", lockedBy)\n\t\t.execute();\n}\n\nexport async function failSubgraphOperation(\n\tdb: Kysely<Database>,\n\toperationId: string,\n\tlockedBy: string,\n\terror: string,\n\tprocessedBlocks?: number,\n): Promise<void> {\n\tawait db\n\t\t.updateTable(\"subgraph_operations\")\n\t\t.set({\n\t\t\tstatus: \"failed\",\n\t\t\tfinished_at: new Date(),\n\t\t\tprocessed_blocks: processedBlocks ?? null,\n\t\t\terror,\n\t\t\tlocked_by: null,\n\t\t\tlocked_until: null,\n\t\t\tupdated_at: new Date(),\n\t\t})\n\t\t.where(\"id\", \"=\", operationId)\n\t\t.where(\"locked_by\", \"=\", lockedBy)\n\t\t.execute();\n}\n"
5
+ "import { type Kysely, sql } from \"kysely\";\nimport type {\n\tDatabase,\n\tSubgraphOperation,\n\tSubgraphOperationKind,\n\tSubgraphOperationStatus,\n} from \"../types.ts\";\n\nconst ACTIVE_STATUSES: SubgraphOperationStatus[] = [\"queued\", \"running\"];\n\nexport function isActiveSubgraphOperationConflict(err: unknown): boolean {\n\tif (!(err instanceof Error)) return false;\n\tconst candidate = err as Error & {\n\t\tcode?: string;\n\t\tconstraint?: string;\n\t\tconstraint_name?: string;\n\t};\n\treturn (\n\t\tcandidate.code === \"23505\" &&\n\t\t(candidate.constraint === \"subgraph_operations_active_unique\" ||\n\t\t\tcandidate.constraint_name === \"subgraph_operations_active_unique\")\n\t);\n}\n\nexport async function createSubgraphOperation(\n\tdb: Kysely<Database>,\n\tdata: {\n\t\tsubgraphId: string;\n\t\tsubgraphName: string;\n\t\taccountId?: string | null;\n\t\tkind: SubgraphOperationKind;\n\t\tfromBlock?: number;\n\t\ttoBlock?: number;\n\t},\n): Promise<SubgraphOperation> {\n\treturn await db\n\t\t.insertInto(\"subgraph_operations\")\n\t\t.values({\n\t\t\tsubgraph_id: data.subgraphId,\n\t\t\tsubgraph_name: data.subgraphName,\n\t\t\taccount_id: data.accountId ?? null,\n\t\t\tkind: data.kind,\n\t\t\tfrom_block: data.fromBlock ?? null,\n\t\t\tto_block: data.toBlock ?? null,\n\t\t})\n\t\t.returningAll()\n\t\t.executeTakeFirstOrThrow();\n}\n\nexport async function findActiveSubgraphOperation(\n\tdb: Kysely<Database>,\n\tsubgraphId: string,\n): Promise<SubgraphOperation | null> {\n\treturn (\n\t\t(await db\n\t\t\t.selectFrom(\"subgraph_operations\")\n\t\t\t.selectAll()\n\t\t\t.where(\"subgraph_id\", \"=\", subgraphId)\n\t\t\t.where(\"status\", \"in\", ACTIVE_STATUSES)\n\t\t\t.orderBy(\"created_at\", \"asc\")\n\t\t\t.executeTakeFirst()) ?? null\n\t);\n}\n\nexport async function requestSubgraphOperationCancel(\n\tdb: Kysely<Database>,\n\tsubgraphId: string,\n): Promise<SubgraphOperation | null> {\n\treturn (\n\t\t(await db\n\t\t\t.updateTable(\"subgraph_operations\")\n\t\t\t.set({ cancel_requested: true, updated_at: new Date() })\n\t\t\t.where(\"subgraph_id\", \"=\", subgraphId)\n\t\t\t.where(\"status\", \"in\", ACTIVE_STATUSES)\n\t\t\t.returningAll()\n\t\t\t.executeTakeFirst()) ?? null\n\t);\n}\n\nexport async function requestSubgraphOperationsCancelForDelete(\n\tdb: Kysely<Database>,\n\tsubgraphId: string,\n): Promise<SubgraphOperation[]> {\n\treturn await db\n\t\t.updateTable(\"subgraph_operations\")\n\t\t.set({ cancel_requested: true, updated_at: new Date() })\n\t\t.where(\"subgraph_id\", \"=\", subgraphId)\n\t\t.where(\"status\", \"in\", ACTIVE_STATUSES)\n\t\t.returningAll()\n\t\t.execute();\n}\n\n/**\n * Poll until no active subgraph operations remain for the subgraph or until\n * `timeoutMs` elapses. Returns true if all active operations cleared, false\n * if we timed out. Callers should use this before `DROP SCHEMA` so the active\n * processor has a chance to observe `cancel_requested` and release its row /\n * advisory locks. Without this, the DROP blocks behind the live transaction\n * and the API socket times out before the lock releases.\n */\nexport async function waitForSubgraphOperationsClear(\n\tdb: Kysely<Database>,\n\tsubgraphId: string,\n\topts?: { timeoutMs?: number; pollMs?: number },\n): Promise<boolean> {\n\tconst timeoutMs = opts?.timeoutMs ?? 30_000;\n\tconst pollMs = opts?.pollMs ?? 500;\n\tconst deadline = Date.now() + timeoutMs;\n\twhile (Date.now() < deadline) {\n\t\tconst active = await db\n\t\t\t.selectFrom(\"subgraph_operations\")\n\t\t\t.select(\"id\")\n\t\t\t.where(\"subgraph_id\", \"=\", subgraphId)\n\t\t\t.where(\"status\", \"in\", ACTIVE_STATUSES)\n\t\t\t.limit(1)\n\t\t\t.executeTakeFirst();\n\t\tif (!active) return true;\n\t\tawait new Promise((r) => setTimeout(r, pollMs));\n\t}\n\treturn false;\n}\n\nexport async function claimSubgraphOperation(\n\tdb: Kysely<Database>,\n\tlockedBy: string,\n): Promise<SubgraphOperation | null> {\n\tconst result = await sql<SubgraphOperation>`\n\t\tUPDATE subgraph_operations\n\t\tSET\n\t\t\tstatus = 'running',\n\t\t\tlocked_by = ${lockedBy},\n\t\t\tlocked_until = now() + interval '60 seconds',\n\t\t\tstarted_at = COALESCE(started_at, now()),\n\t\t\tupdated_at = now()\n\t\tWHERE id = (\n\t\t\tSELECT id\n\t\t\tFROM subgraph_operations\n\t\t\tWHERE\n\t\t\t\tstatus = 'queued'\n\t\t\t\tOR (\n\t\t\t\t\tstatus = 'running'\n\t\t\t\t\tAND (locked_until IS NULL OR locked_until < now())\n\t\t\t\t)\n\t\t\tORDER BY\n\t\t\t\tCASE WHEN status = 'queued' THEN 0 ELSE 1 END,\n\t\t\t\tcreated_at\n\t\t\tFOR UPDATE SKIP LOCKED\n\t\t\tLIMIT 1\n\t\t)\n\t\tRETURNING *\n\t`.execute(db);\n\treturn result.rows[0] ?? null;\n}\n\nexport async function heartbeatSubgraphOperation(\n\tdb: Kysely<Database>,\n\toperationId: string,\n\tlockedBy: string,\n): Promise<void> {\n\tawait db\n\t\t.updateTable(\"subgraph_operations\")\n\t\t.set({\n\t\t\tlocked_until: sql<Date>`now() + interval '60 seconds'`,\n\t\t\tupdated_at: new Date(),\n\t\t})\n\t\t.where(\"id\", \"=\", operationId)\n\t\t.where(\"status\", \"=\", \"running\")\n\t\t.where(\"locked_by\", \"=\", lockedBy)\n\t\t.execute();\n}\n\nexport async function getSubgraphOperation(\n\tdb: Kysely<Database>,\n\toperationId: string,\n): Promise<SubgraphOperation | null> {\n\treturn (\n\t\t(await db\n\t\t\t.selectFrom(\"subgraph_operations\")\n\t\t\t.selectAll()\n\t\t\t.where(\"id\", \"=\", operationId)\n\t\t\t.executeTakeFirst()) ?? null\n\t);\n}\n\nexport async function completeSubgraphOperation(\n\tdb: Kysely<Database>,\n\toperationId: string,\n\tlockedBy: string,\n\tprocessedBlocks: number,\n): Promise<void> {\n\tawait db\n\t\t.updateTable(\"subgraph_operations\")\n\t\t.set({\n\t\t\tstatus: \"completed\",\n\t\t\tfinished_at: new Date(),\n\t\t\tprocessed_blocks: processedBlocks,\n\t\t\tlocked_by: null,\n\t\t\tlocked_until: null,\n\t\t\tupdated_at: new Date(),\n\t\t})\n\t\t.where(\"id\", \"=\", operationId)\n\t\t.where(\"locked_by\", \"=\", lockedBy)\n\t\t.execute();\n}\n\nexport async function cancelSubgraphOperation(\n\tdb: Kysely<Database>,\n\toperationId: string,\n\tlockedBy: string,\n\tprocessedBlocks: number,\n): Promise<void> {\n\tawait db\n\t\t.updateTable(\"subgraph_operations\")\n\t\t.set({\n\t\t\tstatus: \"cancelled\",\n\t\t\tfinished_at: new Date(),\n\t\t\tprocessed_blocks: processedBlocks,\n\t\t\tlocked_by: null,\n\t\t\tlocked_until: null,\n\t\t\tupdated_at: new Date(),\n\t\t})\n\t\t.where(\"id\", \"=\", operationId)\n\t\t.where(\"locked_by\", \"=\", lockedBy)\n\t\t.execute();\n}\n\nexport async function failSubgraphOperation(\n\tdb: Kysely<Database>,\n\toperationId: string,\n\tlockedBy: string,\n\terror: string,\n\tprocessedBlocks?: number,\n): Promise<void> {\n\tawait db\n\t\t.updateTable(\"subgraph_operations\")\n\t\t.set({\n\t\t\tstatus: \"failed\",\n\t\t\tfinished_at: new Date(),\n\t\t\tprocessed_blocks: processedBlocks ?? null,\n\t\t\terror,\n\t\t\tlocked_by: null,\n\t\t\tlocked_until: null,\n\t\t\tupdated_at: new Date(),\n\t\t})\n\t\t.where(\"id\", \"=\", operationId)\n\t\t.where(\"locked_by\", \"=\", lockedBy)\n\t\t.execute();\n}\n"
6
6
  ],
7
- "mappings": "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",
8
- "debugId": "E1C632440C2A33FE64756E2164756E21",
7
+ "mappings": "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",
8
+ "debugId": "276821D8C595B2AC64756E2164756E21",
9
9
  "names": []
10
10
  }
@@ -19,12 +19,12 @@ var BYTES_PER_GB = 1024 ** 3;
19
19
  function alloc(totalMb, totalCpus) {
20
20
  return {
21
21
  postgres: {
22
- memoryMb: Math.floor(totalMb * 0.5),
23
- cpus: round2(totalCpus * 0.5)
22
+ memoryMb: Math.floor(totalMb * 0.25),
23
+ cpus: round2(totalCpus * 0.25)
24
24
  },
25
25
  processor: {
26
- memoryMb: Math.floor(totalMb * 0.3),
27
- cpus: round2(totalCpus * 0.3)
26
+ memoryMb: Math.floor(totalMb * 0.55),
27
+ cpus: round2(totalCpus * 0.55)
28
28
  },
29
29
  api: {
30
30
  memoryMb: Math.floor(totalMb * 0.2),
@@ -160,5 +160,5 @@ export {
160
160
  BYTES_PER_GB
161
161
  };
162
162
 
163
- //# debugId=D52F48DDE4C1BD2764756E2164756E21
163
+ //# debugId=E8AD34879BFF76E564756E2164756E21
164
164
  //# 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: number = 1024 ** 3;\n\nexport type AccountPlanId = \"none\" | PlanId;\nexport type PlanId = \"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\t/** Annual subscription price in cents. null = no self-serve annual price. */\n\tannualPriceCents: 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 monthly recurring tier price. null for enterprise. */\n\tstripeLookupKey: string | null;\n\t/** Stripe `lookup_key` for annual recurring tier price. null for enterprise. */\n\tstripeAnnualLookupKey: 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 — PG 60% / proc 25% / api 15%\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\tlaunch: {\n\t\tid: \"launch\",\n\t\tdisplayName: \"Launch\",\n\t\tmonthlyPriceCents: 9_900, // $99\n\t\tannualPriceCents: 99_000, // 2 months free\n\t\ttotalCpus: 2,\n\t\ttotalMemoryMb: 6_144,\n\t\tstorageLimitMb: 102_400, // 100 GB\n\t\tcontainers: alloc(6_144, 2),\n\t\ttagline: \"Real product\",\n\t\tfeatures: [\n\t\t\t\"2 vCPU · 6 GB RAM\",\n\t\t\t\"100 GB storage · always-on\",\n\t\t\t\"3-5 contracts\",\n\t\t\t\"Production 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\tstripeAnnualLookupKey: \"secondlayer_launch_yearly\",\n\t},\n\tscale: {\n\t\tid: \"scale\",\n\t\tdisplayName: \"Scale\",\n\t\tmonthlyPriceCents: 29_900, // $299\n\t\tannualPriceCents: 299_000, // 2 months free\n\t\ttotalCpus: 8,\n\t\ttotalMemoryMb: 24_576,\n\t\tstorageLimitMb: 512_000, // 500 GB\n\t\tcontainers: alloc(24_576, 8),\n\t\ttagline: \"Full indexing\",\n\t\tfeatures: [\n\t\t\t\"8 vCPU · 24 GB RAM\",\n\t\t\t\"500 GB storage · always-on\",\n\t\t\t\"Heavy history + replay\",\n\t\t\t\"24h SLA · priority support\",\n\t\t],\n\t\tstripeLookupKey: \"secondlayer_scale_monthly\",\n\t\tstripeAnnualLookupKey: \"secondlayer_scale_yearly\",\n\t},\n\tenterprise: {\n\t\tid: \"enterprise\",\n\t\tdisplayName: \"Enterprise\",\n\t\tmonthlyPriceCents: null,\n\t\tannualPriceCents: null,\n\t\ttotalCpus: 16,\n\t\ttotalMemoryMb: 65_536,\n\t\tstorageLimitMb: -1,\n\t\tcontainers: alloc(65_536, 16),\n\t\ttagline: \"Whatever needed\",\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\tstripeAnnualLookupKey: null,\n\t},\n};\n\nexport const PLAN_IDS: readonly PlanId[] = [\"launch\", \"scale\", \"enterprise\"];\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 0;\n\tif (planDef.storageLimitMb < 0) return Number.POSITIVE_INFINITY;\n\treturn planDef.storageLimitMb * 1024 * 1024;\n}\n\n/** Paid tiers bill $2/GB over allowance. Accounts with no plan do not accrue overage. */\nexport function hasStorageOverage(plan: string): boolean {\n\treturn plan !== \"none\" && 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 AccountPlanId = \"none\" | PlanId;\nexport type PlanId = \"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\t/** Annual subscription price in cents. null = no self-serve annual price. */\n\tannualPriceCents: 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 monthly recurring tier price. null for enterprise. */\n\tstripeLookupKey: string | null;\n\t/** Stripe `lookup_key` for annual recurring tier price. null for enterprise. */\n\tstripeAnnualLookupKey: string | null;\n}\n\n// ── Allocation helpers ──────────────────────────────────────────────\n//\n// Allocation within a plan (3 containers per tenant):\n// Default split (paid tiers) — PG 25% / proc 55% / api 20%\n// Sub-1GB total — PG 60% / proc 25% / api 15%\n//\n// Why proc-heavy: the subgraph processor is CPU-bound during backfill\n// (event decode + handler exec + DB writes), while PG idles at <1% CPU\n// observed during steady-state indexing on Launch tier. The 2026-05-13\n// rebalance shifts CPU from idle PG to active proc to recover backfill\n// throughput regressed by the move from shared infra to per-tenant\n// containers — Launch went from 5 blocks/min → ~100 blocks/min just by\n// raising the proc's `--cpus` allotment.\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.25),\n\t\t\tcpus: round2(totalCpus * 0.25),\n\t\t},\n\t\tprocessor: {\n\t\t\tmemoryMb: Math.floor(totalMb * 0.55),\n\t\t\tcpus: round2(totalCpus * 0.55),\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\tlaunch: {\n\t\tid: \"launch\",\n\t\tdisplayName: \"Launch\",\n\t\tmonthlyPriceCents: 9_900, // $99\n\t\tannualPriceCents: 99_000, // 2 months free\n\t\ttotalCpus: 2,\n\t\ttotalMemoryMb: 6_144,\n\t\tstorageLimitMb: 102_400, // 100 GB\n\t\tcontainers: alloc(6_144, 2),\n\t\ttagline: \"Real product\",\n\t\tfeatures: [\n\t\t\t\"2 vCPU · 6 GB RAM\",\n\t\t\t\"100 GB storage · always-on\",\n\t\t\t\"3-5 contracts\",\n\t\t\t\"Production 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\tstripeAnnualLookupKey: \"secondlayer_launch_yearly\",\n\t},\n\tscale: {\n\t\tid: \"scale\",\n\t\tdisplayName: \"Scale\",\n\t\tmonthlyPriceCents: 29_900, // $299\n\t\tannualPriceCents: 299_000, // 2 months free\n\t\ttotalCpus: 8,\n\t\ttotalMemoryMb: 24_576,\n\t\tstorageLimitMb: 512_000, // 500 GB\n\t\tcontainers: alloc(24_576, 8),\n\t\ttagline: \"Full indexing\",\n\t\tfeatures: [\n\t\t\t\"8 vCPU · 24 GB RAM\",\n\t\t\t\"500 GB storage · always-on\",\n\t\t\t\"Heavy history + replay\",\n\t\t\t\"24h SLA · priority support\",\n\t\t],\n\t\tstripeLookupKey: \"secondlayer_scale_monthly\",\n\t\tstripeAnnualLookupKey: \"secondlayer_scale_yearly\",\n\t},\n\tenterprise: {\n\t\tid: \"enterprise\",\n\t\tdisplayName: \"Enterprise\",\n\t\tmonthlyPriceCents: null,\n\t\tannualPriceCents: null,\n\t\ttotalCpus: 16,\n\t\ttotalMemoryMb: 65_536,\n\t\tstorageLimitMb: -1,\n\t\tcontainers: alloc(65_536, 16),\n\t\ttagline: \"Whatever needed\",\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\tstripeAnnualLookupKey: null,\n\t},\n};\n\nexport const PLAN_IDS: readonly PlanId[] = [\"launch\", \"scale\", \"enterprise\"];\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 0;\n\tif (planDef.storageLimitMb < 0) return Number.POSITIVE_INFINITY;\n\treturn planDef.storageLimitMb * 1024 * 1024;\n}\n\n/** Paid tiers bill $2/GB over allowance. Accounts with no plan do not accrue overage. */\nexport function hasStorageOverage(plan: string): boolean {\n\treturn plan !== \"none\" && 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": "D52F48DDE4C1BD2764756E2164756E21",
7
+ "mappings": "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",
8
+ "debugId": "E8AD34879BFF76E564756E2164756E21",
9
9
  "names": []
10
10
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@secondlayer/shared",
3
- "version": "6.3.1",
3
+ "version": "6.3.3",
4
4
  "type": "module",
5
5
  "main": "./dist/src/index.js",
6
6
  "types": "./dist/src/index.d.ts",