@secondlayer/shared 2.1.0 → 3.0.0-beta.1

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 (65) hide show
  1. package/README.md +2 -2
  2. package/dist/src/crypto/secrets.js +47 -3
  3. package/dist/src/crypto/secrets.js.map +5 -4
  4. package/dist/src/db/index.d.ts +112 -137
  5. package/dist/src/db/index.js.map +2 -2
  6. package/dist/src/db/jsonb.d.ts +5 -1
  7. package/dist/src/db/jsonb.js.map +2 -2
  8. package/dist/src/db/queries/account-spend-caps.d.ts +444 -0
  9. package/dist/src/db/queries/account-spend-caps.js +60 -0
  10. package/dist/src/db/queries/account-spend-caps.js.map +10 -0
  11. package/dist/src/db/queries/account-usage.d.ts +468 -0
  12. package/dist/src/db/queries/account-usage.js +222 -0
  13. package/dist/src/db/queries/account-usage.js.map +11 -0
  14. package/dist/src/db/queries/accounts.d.ts +100 -109
  15. package/dist/src/db/queries/accounts.js +15 -1
  16. package/dist/src/db/queries/accounts.js.map +3 -3
  17. package/dist/src/db/queries/integrity.d.ts +85 -107
  18. package/dist/src/db/queries/projects.d.ts +87 -109
  19. package/dist/src/db/queries/provisioning-audit.d.ts +85 -107
  20. package/dist/src/db/queries/subgraph-gaps.d.ts +85 -107
  21. package/dist/src/db/queries/subgraphs.d.ts +86 -109
  22. package/dist/src/db/queries/subgraphs.js +2 -3
  23. package/dist/src/db/queries/subgraphs.js.map +4 -4
  24. package/dist/src/db/queries/{workflows.d.ts → tenant-compute-addons.d.ts} +108 -142
  25. package/dist/src/db/queries/tenant-compute-addons.js +47 -0
  26. package/dist/src/db/queries/tenant-compute-addons.js.map +10 -0
  27. package/dist/src/db/queries/tenants.d.ts +98 -110
  28. package/dist/src/db/queries/tenants.js +55 -8
  29. package/dist/src/db/queries/tenants.js.map +6 -5
  30. package/dist/src/db/queries/usage.d.ts +86 -132
  31. package/dist/src/db/queries/usage.js +5 -64
  32. package/dist/src/db/queries/usage.js.map +4 -5
  33. package/dist/src/db/schema.d.ts +107 -136
  34. package/dist/src/errors.d.ts +8 -7
  35. package/dist/src/errors.js +11 -12
  36. package/dist/src/errors.js.map +3 -3
  37. package/dist/src/index.d.ts +119 -143
  38. package/dist/src/index.js +11 -12
  39. package/dist/src/index.js.map +4 -4
  40. package/dist/src/node/local-client.d.ts +85 -107
  41. package/dist/src/pricing.d.ts +20 -1
  42. package/dist/src/pricing.js +58 -1
  43. package/dist/src/pricing.js.map +3 -3
  44. package/migrations/0045_drop_marketplace_columns.ts +47 -0
  45. package/migrations/0046_tenant_activity_signal.ts +47 -0
  46. package/migrations/0047_usage_daily_tenant_id.ts +73 -0
  47. package/migrations/0048_tenant_compute_addons.ts +49 -0
  48. package/migrations/0049_accounts_stripe_customer_id.ts +30 -0
  49. package/migrations/0050_account_spend_caps.ts +45 -0
  50. package/migrations/0051_workflow_ai_usage_daily.ts +40 -0
  51. package/migrations/0052_sentries.ts +61 -0
  52. package/migrations/0053_workflow_runtime.ts +88 -0
  53. package/migrations/0054_accounts_plan_hobby.ts +32 -0
  54. package/migrations/0055_ai_usage_account_scope.ts +108 -0
  55. package/migrations/0056_drop_workflow_sentry_residuals.ts +23 -0
  56. package/migrations/0057_subscriptions.ts +137 -0
  57. package/package.json +26 -14
  58. package/dist/src/db/queries/workflows.js +0 -260
  59. package/dist/src/db/queries/workflows.js.map +0 -12
  60. package/dist/src/lib/plans.d.ts +0 -9
  61. package/dist/src/lib/plans.js +0 -37
  62. package/dist/src/lib/plans.js.map +0 -10
  63. package/dist/src/schemas/workflows.d.ts +0 -70
  64. package/dist/src/schemas/workflows.js +0 -43
  65. package/dist/src/schemas/workflows.js.map +0 -10
@@ -0,0 +1,11 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/pricing.ts", "../src/db/queries/account-usage.ts"],
4
+ "sourcesContent": [
5
+ "/**\n * Token → USD pricing constants for internal observability.\n *\n * **Not customer billing.** Tier pricing covers compute; these constants\n * let the dashboard display \"~$X spent in AI today\" and let us track\n * gross-margin internally. Update manually when providers change prices\n * (roughly twice per year).\n *\n * Source: public provider pricing pages as of 2026-04-17.\n */\n\nexport interface ModelPricing {\n\t/** USD per 1M input tokens */\n\tinputPerMTokens: number;\n\t/** USD per 1M output tokens */\n\toutputPerMTokens: number;\n}\n\nexport const MODEL_PRICING: Record<string, Record<string, ModelPricing>> = {\n\tanthropic: {\n\t\t\"claude-haiku-4-5\": { inputPerMTokens: 1, outputPerMTokens: 5 },\n\t\t\"claude-haiku-4-5-20251001\": { inputPerMTokens: 1, outputPerMTokens: 5 },\n\t\t\"claude-sonnet-4-6\": { inputPerMTokens: 3, outputPerMTokens: 15 },\n\t\t\"claude-opus-4-7\": { inputPerMTokens: 15, outputPerMTokens: 75 },\n\t},\n\topenai: {\n\t\t\"gpt-4.1\": { inputPerMTokens: 2.5, outputPerMTokens: 10 },\n\t\t\"gpt-4o\": { inputPerMTokens: 2.5, outputPerMTokens: 10 },\n\t\t\"gpt-4o-mini\": { inputPerMTokens: 0.15, outputPerMTokens: 0.6 },\n\t},\n\tgoogle: {\n\t\t\"gemini-2.5-pro\": { inputPerMTokens: 1.25, outputPerMTokens: 10 },\n\t\t\"gemini-2.5-flash\": { inputPerMTokens: 0.3, outputPerMTokens: 2.5 },\n\t},\n};\n\nexport interface TokenUsage {\n\tinputTokens: number;\n\toutputTokens: number;\n}\n\n/**\n * Compute approximate USD cost for an AI step. Returns `null` if the\n * (provider, model) pair isn't in the pricing table — the caller should\n * display \"-\" rather than \"$0\".\n */\nexport function computeUsdCost(\n\tprovider: string,\n\tmodelId: string,\n\tusage: TokenUsage,\n): number | null {\n\tconst p = MODEL_PRICING[provider]?.[modelId];\n\tif (!p) return null;\n\treturn (\n\t\t(usage.inputTokens * p.inputPerMTokens) / 1_000_000 +\n\t\t(usage.outputTokens * p.outputPerMTokens) / 1_000_000\n\t);\n}\n\n// ── Per-tier AI eval caps (workflow-runner) ───────────────────────────\n//\n// Daily `step.ai` budget by tier. Hit the cap → runner throws\n// AI_CAP_REACHED and the step fails cleanly so condition-only paths\n// continue. Overage (past cap) bills to the `ai_evals` Stripe meter on\n// paid tiers.\n//\n// Caps set conservatively so Pro Micro stays margin-positive at\n// realistic AI utilization. Raise based on data, not aspiration.\n\nexport interface AiCap {\n\t/** Max step.ai / generateText / generateObject calls per UTC day. */\n\tevalsPerDay: number;\n\t/** Stripe meter events emitted for overage past this cap. */\n\toverageMeterEventName: \"ai_evals\";\n}\n\nconst AI_CAP_UNLIMITED: AiCap = {\n\tevalsPerDay: Number.POSITIVE_INFINITY,\n\toverageMeterEventName: \"ai_evals\",\n};\n\nconst AI_CAPS_BY_PLAN: Record<string, AiCap> = {\n\thobby: { evalsPerDay: 50, overageMeterEventName: \"ai_evals\" },\n\tlaunch: { evalsPerDay: 500, overageMeterEventName: \"ai_evals\" },\n\tgrow: { evalsPerDay: 1000, overageMeterEventName: \"ai_evals\" },\n\tscale: { evalsPerDay: 2500, overageMeterEventName: \"ai_evals\" },\n\tenterprise: AI_CAP_UNLIMITED,\n};\n\n/** Resolve the daily AI eval cap for a plan. Unknown plans get Hobby's\n * cap so a stray DB value can't accidentally become unlimited. */\nexport function getAiCapForPlan(plan: string): AiCap {\n\treturn AI_CAPS_BY_PLAN[plan] ?? AI_CAPS_BY_PLAN.hobby;\n}\n\n// ── Per-tier compute + storage allowances (usage page) ──────────────\n//\n// \"Included\" amounts per billing period. Actual billing comes from\n// Stripe compute-hours + storage-overage meters; these constants power\n// the dashboard display and the approximation used by `/api/accounts/usage`.\n//\n// Values picked to match `project_supabase_pricing_model.md`:\n// Hobby — 50 h / 5 GB (Nano — free tier)\n// Launch — 500 h / 50 GB ($149/mo)\n// Grow — 1,000 h / 200 GB ($349/mo)\n// Scale — 2,500 h / 1 TB ($799/mo)\n// Enterprise — ∞ / ∞\n\nconst BYTES_PER_GB = 1024 ** 3;\n\n// Hobby has no compute-hour cap — auto-pause after 7d idle is the cap.\n// Paid tiers bill Stripe-metered hours past the included credit; the\n// hour equivalents below are approximate display values. Real billing\n// uses the `compute_hours` meter in Stripe, not these numbers.\nconst COMPUTE_ALLOWANCE_BY_PLAN: Record<string, number> = {\n\thobby: Number.POSITIVE_INFINITY,\n\tlaunch: 500,\n\tgrow: 1000,\n\tscale: 2500,\n\tenterprise: Number.POSITIVE_INFINITY,\n};\n\nconst STORAGE_ALLOWANCE_BYTES_BY_PLAN: Record<string, number> = {\n\thobby: 5 * BYTES_PER_GB,\n\tlaunch: 50 * BYTES_PER_GB,\n\tgrow: 200 * BYTES_PER_GB,\n\tscale: 1000 * BYTES_PER_GB,\n\tenterprise: Number.POSITIVE_INFINITY,\n};\n\n/** Included compute hours per billing period. Unknown → hobby. */\nexport function getComputeAllowanceHours(plan: string): number {\n\treturn COMPUTE_ALLOWANCE_BY_PLAN[plan] ?? COMPUTE_ALLOWANCE_BY_PLAN.hobby;\n}\n\n/** Included storage bytes. Unknown → hobby. */\nexport function getStorageAllowanceBytes(plan: string): number {\n\treturn (\n\t\tSTORAGE_ALLOWANCE_BYTES_BY_PLAN[plan] ??\n\t\tSTORAGE_ALLOWANCE_BYTES_BY_PLAN.hobby\n\t);\n}\n\n/** Whether this plan bills storage overage. Hobby has a hard cap\n * (no overage billing); paid tiers bill $2/GB over allowance. */\nexport function hasStorageOverage(plan: string): boolean {\n\treturn plan !== \"hobby\";\n}\n\n/** Base monthly price for a plan, in cents. Enterprise is custom → 0. */\nconst BASE_PRICE_CENTS_BY_PLAN: Record<string, number> = {\n\thobby: 0,\n\tlaunch: 14900,\n\tgrow: 34900,\n\tscale: 79900,\n\tenterprise: 0,\n};\n\nexport function getBasePriceCents(plan: string): number {\n\treturn BASE_PRICE_CENTS_BY_PLAN[plan] ?? 0;\n}\n\n/** Capitalized display name for a plan tier. */\nexport function getPlanDisplayName(plan: string): string {\n\treturn plan.charAt(0).toUpperCase() + plan.slice(1);\n}\n",
6
+ "import { type Kysely, sql } from \"kysely\";\nimport {\n\tgetAiCapForPlan,\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 AiUsage {\n\ttodayCount: number;\n\tperiodCount: number;\n\tdailyCap: 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\taiEvals: { todayCount: 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 getAiUsage(\n\t_db: Kysely<Database>,\n\t_accountId: string,\n\tplan: string,\n\t_periodStart: Date,\n\tnow: Date = new Date(),\n): Promise<AiUsage> {\n\tconst dailyCap = getAiCapForPlan(plan).evalsPerDay;\n\tconst sparkline: SparklinePoint[] = [];\n\tfor (const day of lastNDays(14, now)) sparkline.push({ day, value: 0 });\n\treturn { todayCount: 0, periodCount: 0, dailyCap, pct: 0, sparkline };\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 aiByTenant = new Map<string, number>();\n\n\tconst computeAllowance = getComputeAllowanceHours(plan);\n\tconst storageAllowance = getStorageAllowanceBytes(plan);\n\tconst aiDailyCap = getAiCapForPlan(plan).evalsPerDay;\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\t\tconst todayAi = aiByTenant.get(t.id) ?? 0;\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\taiEvals: { todayCount: todayAi, pct: pct(todayAi, aiDailyCap) },\n\t\t};\n\t});\n}\n"
7
+ ],
8
+ "mappings": "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",
9
+ "debugId": "D94D306CAF5E896064756E2164756E21",
10
+ "names": []
11
+ }
@@ -61,10 +61,6 @@ interface SubgraphsTable {
61
61
  handler_code: string | null;
62
62
  source_code: string | null;
63
63
  project_id: string | null;
64
- is_public: Generated<boolean>;
65
- tags: Generated<string[]>;
66
- description: string | null;
67
- forked_from_id: string | null;
68
64
  created_at: Generated<Date>;
69
65
  updated_at: Generated<Date>;
70
66
  }
@@ -99,6 +95,7 @@ interface AccountsTable {
99
95
  bio: string | null;
100
96
  avatar_url: string | null;
101
97
  slug: string | null;
98
+ stripe_customer_id: string | null;
102
99
  created_at: Generated<Date>;
103
100
  }
104
101
  interface SessionsTable {
@@ -124,6 +121,7 @@ interface MagicLinksTable {
124
121
  }
125
122
  interface UsageDailyTable {
126
123
  account_id: string;
124
+ tenant_id: string | null;
127
125
  date: string;
128
126
  api_requests: Generated<number>;
129
127
  deliveries: Generated<number>;
@@ -250,83 +248,6 @@ interface ChatMessagesTable {
250
248
  metadata: unknown | null;
251
249
  created_at: Generated<Date>;
252
250
  }
253
- interface WorkflowDefinitionsTable {
254
- id: Generated<string>;
255
- name: string;
256
- version: Generated<string>;
257
- status: Generated<string>;
258
- trigger_type: string;
259
- trigger_config: unknown;
260
- handler_path: string;
261
- source_code: string | null;
262
- retries_config: unknown | null;
263
- timeout_ms: number | null;
264
- api_key_id: string;
265
- project_id: string | null;
266
- created_at: Generated<Date>;
267
- updated_at: Generated<Date>;
268
- }
269
- interface WorkflowRunsTable {
270
- id: Generated<string>;
271
- definition_id: string;
272
- status: Generated<string>;
273
- trigger_type: string;
274
- trigger_data: unknown | null;
275
- dedup_key: string | null;
276
- error: string | null;
277
- started_at: Date | null;
278
- completed_at: Date | null;
279
- duration_ms: number | null;
280
- total_ai_tokens: Generated<number>;
281
- created_at: Generated<Date>;
282
- }
283
- interface WorkflowStepsTable {
284
- id: Generated<string>;
285
- run_id: string;
286
- step_index: number;
287
- step_id: string;
288
- step_type: string;
289
- status: Generated<string>;
290
- input: unknown | null;
291
- output: unknown | null;
292
- error: string | null;
293
- retry_count: Generated<number>;
294
- ai_tokens_used: Generated<number>;
295
- started_at: Date | null;
296
- completed_at: Date | null;
297
- duration_ms: number | null;
298
- memo_key: string | null;
299
- parent_step_id: string | null;
300
- created_at: Generated<Date>;
301
- }
302
- interface WorkflowQueueTable {
303
- id: Generated<string>;
304
- run_id: string;
305
- status: Generated<string>;
306
- attempts: Generated<number>;
307
- max_attempts: Generated<number>;
308
- scheduled_for: Generated<Date>;
309
- locked_at: Date | null;
310
- locked_by: string | null;
311
- error: string | null;
312
- created_at: Generated<Date>;
313
- completed_at: Date | null;
314
- }
315
- interface WorkflowSchedulesTable {
316
- id: Generated<string>;
317
- definition_id: string;
318
- cron_expr: string;
319
- timezone: Generated<string>;
320
- next_run_at: Date;
321
- last_run_at: Date | null;
322
- enabled: Generated<boolean>;
323
- created_at: Generated<Date>;
324
- }
325
- interface WorkflowCursorsTable {
326
- name: string;
327
- block_height: Generated<number>;
328
- updated_at: Generated<Date>;
329
- }
330
251
  interface Database {
331
252
  blocks: BlocksTable;
332
253
  transactions: TransactionsTable;
@@ -352,17 +273,14 @@ interface Database {
352
273
  team_invitations: TeamInvitationsTable;
353
274
  chat_sessions: ChatSessionsTable;
354
275
  chat_messages: ChatMessagesTable;
355
- workflow_definitions: WorkflowDefinitionsTable;
356
- workflow_runs: WorkflowRunsTable;
357
- workflow_steps: WorkflowStepsTable;
358
- workflow_queue: WorkflowQueueTable;
359
- workflow_schedules: WorkflowSchedulesTable;
360
- workflow_cursors: WorkflowCursorsTable;
361
- workflow_signer_secrets: WorkflowSignerSecretsTable;
362
- workflow_budgets: WorkflowBudgetsTable;
363
276
  tenants: TenantsTable;
364
277
  tenant_usage_monthly: TenantUsageMonthlyTable;
278
+ tenant_compute_addons: TenantComputeAddonsTable;
279
+ account_spend_caps: AccountSpendCapsTable;
365
280
  provisioning_audit_log: ProvisioningAuditLogTable;
281
+ subscriptions: SubscriptionsTable;
282
+ subscription_outbox: SubscriptionOutboxTable;
283
+ subscription_deliveries: SubscriptionDeliveriesTable;
366
284
  }
367
285
  type TenantStatus = "provisioning" | "active" | "suspended" | "error" | "deleted";
368
286
  interface TenantsTable {
@@ -384,9 +302,9 @@ interface TenantsTable {
384
302
  service_key_enc: Buffer;
385
303
  api_url_internal: string;
386
304
  api_url_public: string;
387
- trial_ends_at: Date;
388
305
  suspended_at: Date | null;
389
306
  last_health_check_at: Date | null;
307
+ last_active_at: Generated<Date>;
390
308
  service_gen: Generated<number>;
391
309
  anon_gen: Generated<number>;
392
310
  project_id: string | null;
@@ -404,6 +322,28 @@ interface TenantUsageMonthlyTable {
404
322
  first_at: Generated<Date>;
405
323
  last_at: Generated<Date>;
406
324
  }
325
+ interface TenantComputeAddonsTable {
326
+ id: Generated<string>;
327
+ tenant_id: string;
328
+ memory_mb_delta: Generated<number>;
329
+ cpu_delta: Generated<number | string>;
330
+ storage_mb_delta: Generated<number>;
331
+ effective_from: Generated<Date>;
332
+ effective_until: Date | null;
333
+ stripe_subscription_item_id: string | null;
334
+ created_at: Generated<Date>;
335
+ }
336
+ interface AccountSpendCapsTable {
337
+ account_id: string;
338
+ monthly_cap_cents: number | null;
339
+ compute_cap_cents: number | null;
340
+ storage_cap_cents: number | null;
341
+ ai_cap_cents: number | null;
342
+ alert_threshold_pct: Generated<number>;
343
+ alert_sent_at: Date | null;
344
+ frozen_at: Date | null;
345
+ updated_at: Generated<Date>;
346
+ }
407
347
  type ProvisioningAuditEvent = "provision.start" | "provision.success" | "provision.failure" | "suspend" | "resume" | "resize" | "keys.rotate" | "bastion.key.upload" | "bastion.key.revoke" | "teardown";
408
348
  type ProvisioningAuditStatus = "ok" | "error";
409
349
  interface ProvisioningAuditLogTable {
@@ -418,31 +358,69 @@ interface ProvisioningAuditLogTable {
418
358
  error: string | null;
419
359
  created_at: Generated<Date>;
420
360
  }
421
- interface WorkflowBudgetsTable {
361
+ type Account = Selectable<AccountsTable>;
362
+ type SubscriptionStatus = "active" | "paused" | "error";
363
+ type SubscriptionFormat = "standard-webhooks" | "inngest" | "trigger" | "cloudflare" | "cloudevents" | "raw";
364
+ type SubscriptionRuntime = "inngest" | "trigger" | "cloudflare" | "node";
365
+ interface SubscriptionsTable {
422
366
  id: Generated<string>;
423
- workflow_definition_id: string;
424
- /** Period key: "daily:YYYY-MM-DD" | "weekly:YYYY-Www" | "per-run:<uuid>". */
425
- period: string;
426
- ai_usd_used: Generated<string>;
427
- ai_tokens_used: Generated<string>;
428
- chain_microstx_used: Generated<string>;
429
- chain_tx_count: Generated<number>;
430
- run_count: Generated<number>;
431
- step_count: Generated<number>;
432
- reset_at: Date;
367
+ account_id: string;
368
+ project_id: string | null;
369
+ name: string;
370
+ status: ColumnType<SubscriptionStatus, SubscriptionStatus | undefined, SubscriptionStatus>;
371
+ subgraph_name: string;
372
+ table_name: string;
373
+ filter: Generated<unknown>;
374
+ format: ColumnType<SubscriptionFormat, SubscriptionFormat | undefined, SubscriptionFormat>;
375
+ runtime: SubscriptionRuntime | null;
376
+ url: string;
377
+ signing_secret_enc: Buffer;
378
+ auth_config: Generated<unknown>;
379
+ max_retries: Generated<number>;
380
+ timeout_ms: Generated<number>;
381
+ concurrency: Generated<number>;
382
+ circuit_failures: Generated<number>;
383
+ circuit_opened_at: Date | null;
384
+ last_delivery_at: Date | null;
385
+ last_success_at: Date | null;
386
+ last_error: string | null;
433
387
  created_at: Generated<Date>;
434
388
  updated_at: Generated<Date>;
435
389
  }
436
- interface WorkflowSignerSecretsTable {
390
+ type OutboxStatus = "pending" | "delivered" | "dead";
391
+ interface SubscriptionOutboxTable {
437
392
  id: Generated<string>;
438
- account_id: string;
439
- name: string;
440
- /** AES-GCM ciphertext bytes produced by the runner's KMS on write. */
441
- encrypted_value: Buffer;
393
+ subscription_id: string;
394
+ subgraph_name: string;
395
+ table_name: string;
396
+ block_height: number | bigint;
397
+ tx_id: string | null;
398
+ row_pk: unknown;
399
+ event_type: string;
400
+ payload: unknown;
401
+ dedup_key: string;
402
+ attempt: Generated<number>;
403
+ next_attempt_at: Generated<Date>;
404
+ status: ColumnType<OutboxStatus, OutboxStatus | undefined, OutboxStatus>;
405
+ is_replay: Generated<boolean>;
406
+ delivered_at: Date | null;
407
+ failed_at: Date | null;
408
+ locked_by: string | null;
409
+ locked_until: Date | null;
442
410
  created_at: Generated<Date>;
443
- updated_at: Generated<Date>;
444
411
  }
445
- type Account = Selectable<AccountsTable>;
412
+ interface SubscriptionDeliveriesTable {
413
+ id: Generated<string>;
414
+ outbox_id: string;
415
+ subscription_id: string;
416
+ attempt: number;
417
+ status_code: number | null;
418
+ response_headers: unknown | null;
419
+ response_body: string | null;
420
+ error_message: string | null;
421
+ duration_ms: number | null;
422
+ dispatched_at: Generated<Date>;
423
+ }
446
424
  declare function upsertAccount(db: Kysely<Database>, email: string): Promise<Account>;
447
425
  declare function getAccountById(db: Kysely<Database>, id: string): Promise<Account | null>;
448
426
  declare function updateAccountProfile(db: Kysely<Database>, id: string, data: {
@@ -450,6 +428,19 @@ declare function updateAccountProfile(db: Kysely<Database>, id: string, data: {
450
428
  bio?: string
451
429
  slug?: string
452
430
  }): Promise<Account>;
431
+ /** Persist the Stripe customer id on first upgrade (lazy customer model). */
432
+ declare function setStripeCustomerId(db: Kysely<Database>, accountId: string, stripeCustomerId: string): Promise<void>;
433
+ /**
434
+ * Set the plan tier on an account. Called by the Stripe webhook on
435
+ * subscription lifecycle events + by the billing page's fast-resolve
436
+ * after a successful Checkout redirect. Returns true if a row was
437
+ * updated (account exists).
438
+ */
439
+ declare function setAccountPlan(db: Kysely<Database>, accountId: string, plan: string): Promise<boolean>;
440
+ /** Resolve an account by its Stripe customer id. Null if no match. */
441
+ declare function getAccountByStripeCustomerId(db: Kysely<Database>, stripeCustomerId: string): Promise<{
442
+ id: string
443
+ } | null>;
453
444
  declare function isSlugTaken(db: Kysely<Database>, slug: string, excludeAccountId: string): Promise<boolean>;
454
445
  declare function isEmailAllowed(db: Kysely<Database>, email: string): Promise<boolean>;
455
446
  declare function createMagicLink(db: Kysely<Database>, email: string, token: string, code: string, expiresInMs?: number): Promise<void>;
@@ -472,4 +463,4 @@ declare function approveWaitlistEntry(db: Kysely<Database>, email: string): Prom
472
463
  code: string
473
464
  status: "approved" | "already_approved" | "not_found"
474
465
  }>;
475
- export { verifyMagicLinkByCode, verifyMagicLink, upsertAccount, updateAccountProfile, listWaitlist, isSlugTaken, isEmailAllowed, getWaitlistById, getAccountById, createMagicLink, approveWaitlistEntry, WaitlistEntry };
466
+ export { verifyMagicLinkByCode, verifyMagicLink, upsertAccount, updateAccountProfile, setStripeCustomerId, setAccountPlan, listWaitlist, isSlugTaken, isEmailAllowed, getWaitlistById, getAccountByStripeCustomerId, getAccountById, createMagicLink, approveWaitlistEntry, WaitlistEntry };
@@ -32,6 +32,17 @@ async function updateAccountProfile(db, id, data) {
32
32
  set.slug = data.slug;
33
33
  return db.updateTable("accounts").set(set).where("id", "=", id).returningAll().executeTakeFirstOrThrow();
34
34
  }
35
+ async function setStripeCustomerId(db, accountId, stripeCustomerId) {
36
+ await db.updateTable("accounts").set({ stripe_customer_id: stripeCustomerId }).where("id", "=", accountId).execute();
37
+ }
38
+ async function setAccountPlan(db, accountId, plan) {
39
+ const result = await db.updateTable("accounts").set({ plan }).where("id", "=", accountId).executeTakeFirst();
40
+ return (result.numUpdatedRows ?? 0n) > 0n;
41
+ }
42
+ async function getAccountByStripeCustomerId(db, stripeCustomerId) {
43
+ const row = await db.selectFrom("accounts").select("id").where("stripe_customer_id", "=", stripeCustomerId).executeTakeFirst();
44
+ return row ?? null;
45
+ }
35
46
  async function isSlugTaken(db, slug, excludeAccountId) {
36
47
  const row = await db.selectFrom("accounts").select("id").where("slug", "=", slug).where("id", "!=", excludeAccountId).executeTakeFirst();
37
48
  return !!row;
@@ -94,14 +105,17 @@ export {
94
105
  verifyMagicLink,
95
106
  upsertAccount,
96
107
  updateAccountProfile,
108
+ setStripeCustomerId,
109
+ setAccountPlan,
97
110
  listWaitlist,
98
111
  isSlugTaken,
99
112
  isEmailAllowed,
100
113
  getWaitlistById,
114
+ getAccountByStripeCustomerId,
101
115
  getAccountById,
102
116
  createMagicLink,
103
117
  approveWaitlistEntry
104
118
  };
105
119
 
106
- //# debugId=3FB34D04C85084DA64756E2164756E21
120
+ //# debugId=E1E89B16EA4EC82664756E2164756E21
107
121
  //# sourceMappingURL=accounts.js.map
@@ -2,9 +2,9 @@
2
2
  "version": 3,
3
3
  "sources": ["../src/db/queries/accounts.ts"],
4
4
  "sourcesContent": [
5
- "import { type Kysely, sql } from \"kysely\";\nimport type { Selectable } from \"kysely\";\nimport type { Account, Database, WaitlistTable } from \"../types.ts\";\n\nexport async function upsertAccount(\n\tdb: Kysely<Database>,\n\temail: string,\n): Promise<Account> {\n\treturn await db\n\t\t.insertInto(\"accounts\")\n\t\t.values({ email })\n\t\t.onConflict(\n\t\t\t(oc) => oc.column(\"email\").doUpdateSet({ email }), // no-op update to return existing\n\t\t)\n\t\t.returningAll()\n\t\t.executeTakeFirstOrThrow();\n}\n\nexport async function getAccountById(\n\tdb: Kysely<Database>,\n\tid: string,\n): Promise<Account | null> {\n\treturn (\n\t\t(await db\n\t\t\t.selectFrom(\"accounts\")\n\t\t\t.selectAll()\n\t\t\t.where(\"id\", \"=\", id)\n\t\t\t.executeTakeFirst()) ?? null\n\t);\n}\n\nexport async function updateAccountProfile(\n\tdb: Kysely<Database>,\n\tid: string,\n\tdata: {\n\t\tdisplay_name?: string;\n\t\tbio?: string;\n\t\tslug?: string;\n\t},\n): Promise<Account> {\n\tconst set: Record<string, unknown> = {};\n\tif (data.display_name !== undefined) set.display_name = data.display_name;\n\tif (data.bio !== undefined) set.bio = data.bio;\n\tif (data.slug !== undefined) set.slug = data.slug;\n\n\treturn db\n\t\t.updateTable(\"accounts\")\n\t\t.set(set)\n\t\t.where(\"id\", \"=\", id)\n\t\t.returningAll()\n\t\t.executeTakeFirstOrThrow();\n}\n\nexport async function isSlugTaken(\n\tdb: Kysely<Database>,\n\tslug: string,\n\texcludeAccountId: string,\n): Promise<boolean> {\n\tconst row = await db\n\t\t.selectFrom(\"accounts\")\n\t\t.select(\"id\")\n\t\t.where(\"slug\", \"=\", slug)\n\t\t.where(\"id\", \"!=\", excludeAccountId)\n\t\t.executeTakeFirst();\n\treturn !!row;\n}\n\nexport async function isEmailAllowed(\n\tdb: Kysely<Database>,\n\temail: string,\n): Promise<boolean> {\n\tconst result = await sql<{ found: number }>`\n SELECT 1 AS found FROM accounts WHERE email = ${email}\n UNION ALL\n SELECT 1 AS found FROM waitlist WHERE email = ${email} AND status = 'approved'\n LIMIT 1\n `.execute(db);\n\n\treturn result.rows.length > 0;\n}\n\nexport async function createMagicLink(\n\tdb: Kysely<Database>,\n\temail: string,\n\ttoken: string,\n\tcode: string,\n\texpiresInMs: number = 15 * 60 * 1000,\n): Promise<void> {\n\tawait db\n\t\t.insertInto(\"magic_links\")\n\t\t.values({\n\t\t\temail,\n\t\t\ttoken,\n\t\t\tcode,\n\t\t\texpires_at: new Date(Date.now() + expiresInMs),\n\t\t})\n\t\t.execute();\n}\n\n/**\n * Verify a magic link token. Returns the email if valid, null otherwise.\n * Marks the token as used atomically. Rejects after 3 failed attempts.\n */\nexport async function verifyMagicLink(\n\tdb: Kysely<Database>,\n\ttoken: string,\n): Promise<string | null> {\n\tconst result = await db\n\t\t.updateTable(\"magic_links\")\n\t\t.set({ used_at: new Date() })\n\t\t.where(\"token\", \"=\", token)\n\t\t.where(\"used_at\", \"is\", null)\n\t\t.where(\"expires_at\", \">\", new Date())\n\t\t.where(\"failed_attempts\", \"<\", 3)\n\t\t.returning(\"email\")\n\t\t.executeTakeFirst();\n\n\tif (result?.email) return result.email;\n\n\t// Increment failed attempts if token exists but didn't verify\n\tawait db\n\t\t.updateTable(\"magic_links\")\n\t\t.set({ failed_attempts: sql`failed_attempts + 1` })\n\t\t.where(\"token\", \"=\", token)\n\t\t.where(\"used_at\", \"is\", null)\n\t\t.where(\"expires_at\", \">\", new Date())\n\t\t.execute();\n\n\treturn null;\n}\n\n/**\n * Verify by 6-digit code + email. Same atomic pattern as verifyMagicLink.\n * Rejects after 3 failed attempts. Increments failed_attempts on all\n * active codes for this email on failure (prevents parallel brute-force).\n */\nexport async function verifyMagicLinkByCode(\n\tdb: Kysely<Database>,\n\temail: string,\n\tcode: string,\n): Promise<string | null> {\n\tconst result = await db\n\t\t.updateTable(\"magic_links\")\n\t\t.set({ used_at: new Date() })\n\t\t.where(\"email\", \"=\", email)\n\t\t.where(\"code\", \"=\", code)\n\t\t.where(\"used_at\", \"is\", null)\n\t\t.where(\"expires_at\", \">\", new Date())\n\t\t.where(\"failed_attempts\", \"<\", 3)\n\t\t.returning(\"email\")\n\t\t.executeTakeFirst();\n\n\tif (result?.email) return result.email;\n\n\t// Increment failed attempts on all active codes for this email\n\tawait db\n\t\t.updateTable(\"magic_links\")\n\t\t.set({ failed_attempts: sql`failed_attempts + 1` })\n\t\t.where(\"email\", \"=\", email)\n\t\t.where(\"used_at\", \"is\", null)\n\t\t.where(\"expires_at\", \">\", new Date())\n\t\t.execute();\n\n\treturn null;\n}\n\n// ── Waitlist ──\n\nexport type WaitlistEntry = Selectable<WaitlistTable>;\n\nexport async function listWaitlist(\n\tdb: Kysely<Database>,\n\tstatus?: string,\n): Promise<WaitlistEntry[]> {\n\tlet query = db\n\t\t.selectFrom(\"waitlist\")\n\t\t.selectAll()\n\t\t.orderBy(\"created_at\", \"desc\");\n\tif (status) {\n\t\tquery = query.where(\"status\", \"=\", status);\n\t}\n\treturn query.execute();\n}\n\nexport async function getWaitlistById(\n\tdb: Kysely<Database>,\n\tid: string,\n): Promise<WaitlistEntry | null> {\n\treturn (\n\t\t(await db\n\t\t\t.selectFrom(\"waitlist\")\n\t\t\t.selectAll()\n\t\t\t.where(\"id\", \"=\", id)\n\t\t\t.executeTakeFirst()) ?? null\n\t);\n}\n\nexport async function approveWaitlistEntry(\n\tdb: Kysely<Database>,\n\temail: string,\n): Promise<{\n\ttoken: string;\n\tcode: string;\n\tstatus: \"approved\" | \"already_approved\" | \"not_found\";\n}> {\n\tconst row = await db\n\t\t.selectFrom(\"waitlist\")\n\t\t.select(\"status\")\n\t\t.where(\"email\", \"=\", email)\n\t\t.executeTakeFirst();\n\n\tif (!row) return { token: \"\", code: \"\", status: \"not_found\" };\n\tif (row.status !== \"pending\")\n\t\treturn { token: \"\", code: \"\", status: \"already_approved\" };\n\n\tawait db\n\t\t.updateTable(\"waitlist\")\n\t\t.set({ status: \"approved\" })\n\t\t.where(\"email\", \"=\", email)\n\t\t.execute();\n\n\tconst token = Math.floor(100000 + Math.random() * 900000).toString();\n\tconst code = String(Math.floor(Math.random() * 1_000_000)).padStart(6, \"0\");\n\tawait createMagicLink(db, email, token, code, 7 * 24 * 60 * 60 * 1000);\n\n\treturn { token, code, status: \"approved\" };\n}\n"
5
+ "import { type Kysely, sql } from \"kysely\";\nimport type { Selectable } from \"kysely\";\nimport type { Account, Database, WaitlistTable } from \"../types.ts\";\n\nexport async function upsertAccount(\n\tdb: Kysely<Database>,\n\temail: string,\n): Promise<Account> {\n\treturn await db\n\t\t.insertInto(\"accounts\")\n\t\t.values({ email })\n\t\t.onConflict(\n\t\t\t(oc) => oc.column(\"email\").doUpdateSet({ email }), // no-op update to return existing\n\t\t)\n\t\t.returningAll()\n\t\t.executeTakeFirstOrThrow();\n}\n\nexport async function getAccountById(\n\tdb: Kysely<Database>,\n\tid: string,\n): Promise<Account | null> {\n\treturn (\n\t\t(await db\n\t\t\t.selectFrom(\"accounts\")\n\t\t\t.selectAll()\n\t\t\t.where(\"id\", \"=\", id)\n\t\t\t.executeTakeFirst()) ?? null\n\t);\n}\n\nexport async function updateAccountProfile(\n\tdb: Kysely<Database>,\n\tid: string,\n\tdata: {\n\t\tdisplay_name?: string;\n\t\tbio?: string;\n\t\tslug?: string;\n\t},\n): Promise<Account> {\n\tconst set: Record<string, unknown> = {};\n\tif (data.display_name !== undefined) set.display_name = data.display_name;\n\tif (data.bio !== undefined) set.bio = data.bio;\n\tif (data.slug !== undefined) set.slug = data.slug;\n\n\treturn db\n\t\t.updateTable(\"accounts\")\n\t\t.set(set)\n\t\t.where(\"id\", \"=\", id)\n\t\t.returningAll()\n\t\t.executeTakeFirstOrThrow();\n}\n\n/** Persist the Stripe customer id on first upgrade (lazy customer model). */\nexport async function setStripeCustomerId(\n\tdb: Kysely<Database>,\n\taccountId: string,\n\tstripeCustomerId: string,\n): Promise<void> {\n\tawait db\n\t\t.updateTable(\"accounts\")\n\t\t.set({ stripe_customer_id: stripeCustomerId })\n\t\t.where(\"id\", \"=\", accountId)\n\t\t.execute();\n}\n\n/**\n * Set the plan tier on an account. Called by the Stripe webhook on\n * subscription lifecycle events + by the billing page's fast-resolve\n * after a successful Checkout redirect. Returns true if a row was\n * updated (account exists).\n */\nexport async function setAccountPlan(\n\tdb: Kysely<Database>,\n\taccountId: string,\n\tplan: string,\n): Promise<boolean> {\n\tconst result = await db\n\t\t.updateTable(\"accounts\")\n\t\t.set({ plan })\n\t\t.where(\"id\", \"=\", accountId)\n\t\t.executeTakeFirst();\n\treturn (result.numUpdatedRows ?? 0n) > 0n;\n}\n\n/** Resolve an account by its Stripe customer id. Null if no match. */\nexport async function getAccountByStripeCustomerId(\n\tdb: Kysely<Database>,\n\tstripeCustomerId: string,\n): Promise<{ id: string } | null> {\n\tconst row = await db\n\t\t.selectFrom(\"accounts\")\n\t\t.select(\"id\")\n\t\t.where(\"stripe_customer_id\", \"=\", stripeCustomerId)\n\t\t.executeTakeFirst();\n\treturn row ?? null;\n}\n\nexport async function isSlugTaken(\n\tdb: Kysely<Database>,\n\tslug: string,\n\texcludeAccountId: string,\n): Promise<boolean> {\n\tconst row = await db\n\t\t.selectFrom(\"accounts\")\n\t\t.select(\"id\")\n\t\t.where(\"slug\", \"=\", slug)\n\t\t.where(\"id\", \"!=\", excludeAccountId)\n\t\t.executeTakeFirst();\n\treturn !!row;\n}\n\nexport async function isEmailAllowed(\n\tdb: Kysely<Database>,\n\temail: string,\n): Promise<boolean> {\n\tconst result = await sql<{ found: number }>`\n SELECT 1 AS found FROM accounts WHERE email = ${email}\n UNION ALL\n SELECT 1 AS found FROM waitlist WHERE email = ${email} AND status = 'approved'\n LIMIT 1\n `.execute(db);\n\n\treturn result.rows.length > 0;\n}\n\nexport async function createMagicLink(\n\tdb: Kysely<Database>,\n\temail: string,\n\ttoken: string,\n\tcode: string,\n\texpiresInMs: number = 15 * 60 * 1000,\n): Promise<void> {\n\tawait db\n\t\t.insertInto(\"magic_links\")\n\t\t.values({\n\t\t\temail,\n\t\t\ttoken,\n\t\t\tcode,\n\t\t\texpires_at: new Date(Date.now() + expiresInMs),\n\t\t})\n\t\t.execute();\n}\n\n/**\n * Verify a magic link token. Returns the email if valid, null otherwise.\n * Marks the token as used atomically. Rejects after 3 failed attempts.\n */\nexport async function verifyMagicLink(\n\tdb: Kysely<Database>,\n\ttoken: string,\n): Promise<string | null> {\n\tconst result = await db\n\t\t.updateTable(\"magic_links\")\n\t\t.set({ used_at: new Date() })\n\t\t.where(\"token\", \"=\", token)\n\t\t.where(\"used_at\", \"is\", null)\n\t\t.where(\"expires_at\", \">\", new Date())\n\t\t.where(\"failed_attempts\", \"<\", 3)\n\t\t.returning(\"email\")\n\t\t.executeTakeFirst();\n\n\tif (result?.email) return result.email;\n\n\t// Increment failed attempts if token exists but didn't verify\n\tawait db\n\t\t.updateTable(\"magic_links\")\n\t\t.set({ failed_attempts: sql`failed_attempts + 1` })\n\t\t.where(\"token\", \"=\", token)\n\t\t.where(\"used_at\", \"is\", null)\n\t\t.where(\"expires_at\", \">\", new Date())\n\t\t.execute();\n\n\treturn null;\n}\n\n/**\n * Verify by 6-digit code + email. Same atomic pattern as verifyMagicLink.\n * Rejects after 3 failed attempts. Increments failed_attempts on all\n * active codes for this email on failure (prevents parallel brute-force).\n */\nexport async function verifyMagicLinkByCode(\n\tdb: Kysely<Database>,\n\temail: string,\n\tcode: string,\n): Promise<string | null> {\n\tconst result = await db\n\t\t.updateTable(\"magic_links\")\n\t\t.set({ used_at: new Date() })\n\t\t.where(\"email\", \"=\", email)\n\t\t.where(\"code\", \"=\", code)\n\t\t.where(\"used_at\", \"is\", null)\n\t\t.where(\"expires_at\", \">\", new Date())\n\t\t.where(\"failed_attempts\", \"<\", 3)\n\t\t.returning(\"email\")\n\t\t.executeTakeFirst();\n\n\tif (result?.email) return result.email;\n\n\t// Increment failed attempts on all active codes for this email\n\tawait db\n\t\t.updateTable(\"magic_links\")\n\t\t.set({ failed_attempts: sql`failed_attempts + 1` })\n\t\t.where(\"email\", \"=\", email)\n\t\t.where(\"used_at\", \"is\", null)\n\t\t.where(\"expires_at\", \">\", new Date())\n\t\t.execute();\n\n\treturn null;\n}\n\n// ── Waitlist ──\n\nexport type WaitlistEntry = Selectable<WaitlistTable>;\n\nexport async function listWaitlist(\n\tdb: Kysely<Database>,\n\tstatus?: string,\n): Promise<WaitlistEntry[]> {\n\tlet query = db\n\t\t.selectFrom(\"waitlist\")\n\t\t.selectAll()\n\t\t.orderBy(\"created_at\", \"desc\");\n\tif (status) {\n\t\tquery = query.where(\"status\", \"=\", status);\n\t}\n\treturn query.execute();\n}\n\nexport async function getWaitlistById(\n\tdb: Kysely<Database>,\n\tid: string,\n): Promise<WaitlistEntry | null> {\n\treturn (\n\t\t(await db\n\t\t\t.selectFrom(\"waitlist\")\n\t\t\t.selectAll()\n\t\t\t.where(\"id\", \"=\", id)\n\t\t\t.executeTakeFirst()) ?? null\n\t);\n}\n\nexport async function approveWaitlistEntry(\n\tdb: Kysely<Database>,\n\temail: string,\n): Promise<{\n\ttoken: string;\n\tcode: string;\n\tstatus: \"approved\" | \"already_approved\" | \"not_found\";\n}> {\n\tconst row = await db\n\t\t.selectFrom(\"waitlist\")\n\t\t.select(\"status\")\n\t\t.where(\"email\", \"=\", email)\n\t\t.executeTakeFirst();\n\n\tif (!row) return { token: \"\", code: \"\", status: \"not_found\" };\n\tif (row.status !== \"pending\")\n\t\treturn { token: \"\", code: \"\", status: \"already_approved\" };\n\n\tawait db\n\t\t.updateTable(\"waitlist\")\n\t\t.set({ status: \"approved\" })\n\t\t.where(\"email\", \"=\", email)\n\t\t.execute();\n\n\tconst token = Math.floor(100000 + Math.random() * 900000).toString();\n\tconst code = String(Math.floor(Math.random() * 1_000_000)).padStart(6, \"0\");\n\tawait createMagicLink(db, email, token, code, 7 * 24 * 60 * 60 * 1000);\n\n\treturn { token, code, status: \"approved\" };\n}\n"
6
6
  ],
7
- "mappings": ";;;;;;;;;;;;;;;;;AAAA;AAIA,eAAsB,aAAa,CAClC,IACA,OACmB;AAAA,EACnB,OAAO,MAAM,GACX,WAAW,UAAU,EACrB,OAAO,EAAE,MAAM,CAAC,EAChB,WACA,CAAC,OAAO,GAAG,OAAO,OAAO,EAAE,YAAY,EAAE,MAAM,CAAC,CACjD,EACC,aAAa,EACb,wBAAwB;AAAA;AAG3B,eAAsB,cAAc,CACnC,IACA,IAC0B;AAAA,EAC1B,OACE,MAAM,GACL,WAAW,UAAU,EACrB,UAAU,EACV,MAAM,MAAM,KAAK,EAAE,EACnB,iBAAiB,KAAM;AAAA;AAI3B,eAAsB,oBAAoB,CACzC,IACA,IACA,MAKmB;AAAA,EACnB,MAAM,MAA+B,CAAC;AAAA,EACtC,IAAI,KAAK,iBAAiB;AAAA,IAAW,IAAI,eAAe,KAAK;AAAA,EAC7D,IAAI,KAAK,QAAQ;AAAA,IAAW,IAAI,MAAM,KAAK;AAAA,EAC3C,IAAI,KAAK,SAAS;AAAA,IAAW,IAAI,OAAO,KAAK;AAAA,EAE7C,OAAO,GACL,YAAY,UAAU,EACtB,IAAI,GAAG,EACP,MAAM,MAAM,KAAK,EAAE,EACnB,aAAa,EACb,wBAAwB;AAAA;AAG3B,eAAsB,WAAW,CAChC,IACA,MACA,kBACmB;AAAA,EACnB,MAAM,MAAM,MAAM,GAChB,WAAW,UAAU,EACrB,OAAO,IAAI,EACX,MAAM,QAAQ,KAAK,IAAI,EACvB,MAAM,MAAM,MAAM,gBAAgB,EAClC,iBAAiB;AAAA,EACnB,OAAO,CAAC,CAAC;AAAA;AAGV,eAAsB,cAAc,CACnC,IACA,OACmB;AAAA,EACnB,MAAM,SAAS,MAAM;AAAA,oDAC8B;AAAA;AAAA,oDAEA;AAAA;AAAA,IAEhD,QAAQ,EAAE;AAAA,EAEb,OAAO,OAAO,KAAK,SAAS;AAAA;AAG7B,eAAsB,eAAe,CACpC,IACA,OACA,OACA,MACA,cAAsB,KAAK,KAAK,MAChB;AAAA,EAChB,MAAM,GACJ,WAAW,aAAa,EACxB,OAAO;AAAA,IACP;AAAA,IACA;AAAA,IACA;AAAA,IACA,YAAY,IAAI,KAAK,KAAK,IAAI,IAAI,WAAW;AAAA,EAC9C,CAAC,EACA,QAAQ;AAAA;AAOX,eAAsB,eAAe,CACpC,IACA,OACyB;AAAA,EACzB,MAAM,SAAS,MAAM,GACnB,YAAY,aAAa,EACzB,IAAI,EAAE,SAAS,IAAI,KAAO,CAAC,EAC3B,MAAM,SAAS,KAAK,KAAK,EACzB,MAAM,WAAW,MAAM,IAAI,EAC3B,MAAM,cAAc,KAAK,IAAI,IAAM,EACnC,MAAM,mBAAmB,KAAK,CAAC,EAC/B,UAAU,OAAO,EACjB,iBAAiB;AAAA,EAEnB,IAAI,QAAQ;AAAA,IAAO,OAAO,OAAO;AAAA,EAGjC,MAAM,GACJ,YAAY,aAAa,EACzB,IAAI,EAAE,iBAAiB,yBAAyB,CAAC,EACjD,MAAM,SAAS,KAAK,KAAK,EACzB,MAAM,WAAW,MAAM,IAAI,EAC3B,MAAM,cAAc,KAAK,IAAI,IAAM,EACnC,QAAQ;AAAA,EAEV,OAAO;AAAA;AAQR,eAAsB,qBAAqB,CAC1C,IACA,OACA,MACyB;AAAA,EACzB,MAAM,SAAS,MAAM,GACnB,YAAY,aAAa,EACzB,IAAI,EAAE,SAAS,IAAI,KAAO,CAAC,EAC3B,MAAM,SAAS,KAAK,KAAK,EACzB,MAAM,QAAQ,KAAK,IAAI,EACvB,MAAM,WAAW,MAAM,IAAI,EAC3B,MAAM,cAAc,KAAK,IAAI,IAAM,EACnC,MAAM,mBAAmB,KAAK,CAAC,EAC/B,UAAU,OAAO,EACjB,iBAAiB;AAAA,EAEnB,IAAI,QAAQ;AAAA,IAAO,OAAO,OAAO;AAAA,EAGjC,MAAM,GACJ,YAAY,aAAa,EACzB,IAAI,EAAE,iBAAiB,yBAAyB,CAAC,EACjD,MAAM,SAAS,KAAK,KAAK,EACzB,MAAM,WAAW,MAAM,IAAI,EAC3B,MAAM,cAAc,KAAK,IAAI,IAAM,EACnC,QAAQ;AAAA,EAEV,OAAO;AAAA;AAOR,eAAsB,YAAY,CACjC,IACA,QAC2B;AAAA,EAC3B,IAAI,QAAQ,GACV,WAAW,UAAU,EACrB,UAAU,EACV,QAAQ,cAAc,MAAM;AAAA,EAC9B,IAAI,QAAQ;AAAA,IACX,QAAQ,MAAM,MAAM,UAAU,KAAK,MAAM;AAAA,EAC1C;AAAA,EACA,OAAO,MAAM,QAAQ;AAAA;AAGtB,eAAsB,eAAe,CACpC,IACA,IACgC;AAAA,EAChC,OACE,MAAM,GACL,WAAW,UAAU,EACrB,UAAU,EACV,MAAM,MAAM,KAAK,EAAE,EACnB,iBAAiB,KAAM;AAAA;AAI3B,eAAsB,oBAAoB,CACzC,IACA,OAKE;AAAA,EACF,MAAM,MAAM,MAAM,GAChB,WAAW,UAAU,EACrB,OAAO,QAAQ,EACf,MAAM,SAAS,KAAK,KAAK,EACzB,iBAAiB;AAAA,EAEnB,IAAI,CAAC;AAAA,IAAK,OAAO,EAAE,OAAO,IAAI,MAAM,IAAI,QAAQ,YAAY;AAAA,EAC5D,IAAI,IAAI,WAAW;AAAA,IAClB,OAAO,EAAE,OAAO,IAAI,MAAM,IAAI,QAAQ,mBAAmB;AAAA,EAE1D,MAAM,GACJ,YAAY,UAAU,EACtB,IAAI,EAAE,QAAQ,WAAW,CAAC,EAC1B,MAAM,SAAS,KAAK,KAAK,EACzB,QAAQ;AAAA,EAEV,MAAM,QAAQ,KAAK,MAAM,MAAS,KAAK,OAAO,IAAI,MAAM,EAAE,SAAS;AAAA,EACnE,MAAM,OAAO,OAAO,KAAK,MAAM,KAAK,OAAO,IAAI,GAAS,CAAC,EAAE,SAAS,GAAG,GAAG;AAAA,EAC1E,MAAM,gBAAgB,IAAI,OAAO,OAAO,MAAM,IAAI,KAAK,KAAK,KAAK,IAAI;AAAA,EAErE,OAAO,EAAE,OAAO,MAAM,QAAQ,WAAW;AAAA;",
8
- "debugId": "3FB34D04C85084DA64756E2164756E21",
7
+ "mappings": "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",
8
+ "debugId": "E1E89B16EA4EC82664756E2164756E21",
9
9
  "names": []
10
10
  }