@secondlayer/shared 1.1.0 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. package/dist/src/db/index.d.ts +89 -6
  2. package/dist/src/db/index.js +53 -29
  3. package/dist/src/db/index.js.map +4 -4
  4. package/dist/src/db/jsonb.js.map +2 -2
  5. package/dist/src/db/queries/accounts.d.ts +58 -2
  6. package/dist/src/db/queries/integrity.d.ts +58 -2
  7. package/dist/src/db/queries/projects.d.ts +58 -2
  8. package/dist/src/db/queries/projects.js.map +2 -2
  9. package/dist/src/db/queries/{marketplace.d.ts → provisioning-audit.d.ts} +79 -56
  10. package/dist/src/db/queries/provisioning-audit.js +40 -0
  11. package/dist/src/db/queries/provisioning-audit.js.map +10 -0
  12. package/dist/src/db/queries/subgraph-gaps.d.ts +58 -2
  13. package/dist/src/db/queries/subgraphs.d.ts +62 -5
  14. package/dist/src/db/queries/subgraphs.js +3 -9
  15. package/dist/src/db/queries/subgraphs.js.map +4 -4
  16. package/dist/src/db/queries/tenants.d.ts +527 -0
  17. package/dist/src/db/queries/tenants.js +220 -0
  18. package/dist/src/db/queries/tenants.js.map +11 -0
  19. package/dist/src/db/queries/usage.d.ts +58 -2
  20. package/dist/src/db/queries/usage.js +3 -3
  21. package/dist/src/db/queries/usage.js.map +3 -3
  22. package/dist/src/db/queries/workflows.d.ts +58 -2
  23. package/dist/src/db/queries/workflows.js +31 -3
  24. package/dist/src/db/queries/workflows.js.map +4 -4
  25. package/dist/src/db/schema.d.ts +67 -3
  26. package/dist/src/env.d.ts +10 -0
  27. package/dist/src/env.js +3 -1
  28. package/dist/src/env.js.map +3 -3
  29. package/dist/src/errors.d.ts +17 -3
  30. package/dist/src/errors.js +34 -3
  31. package/dist/src/errors.js.map +3 -3
  32. package/dist/src/index.d.ts +142 -84
  33. package/dist/src/index.js +146 -99
  34. package/dist/src/index.js.map +8 -8
  35. package/dist/src/logger.js +3 -1
  36. package/dist/src/logger.js.map +3 -3
  37. package/dist/src/mode.d.ts +29 -0
  38. package/dist/src/mode.js +43 -0
  39. package/dist/src/mode.js.map +10 -0
  40. package/dist/src/node/archive-client.js +3 -1
  41. package/dist/src/node/archive-client.js.map +3 -3
  42. package/dist/src/node/hiro-client.js +3 -1
  43. package/dist/src/node/hiro-client.js.map +3 -3
  44. package/dist/src/node/local-client.d.ts +58 -2
  45. package/dist/src/queue/listener.d.ts +11 -2
  46. package/dist/src/queue/listener.js +11 -12
  47. package/dist/src/queue/listener.js.map +3 -3
  48. package/dist/src/schemas/accounts.d.ts +14 -0
  49. package/dist/src/schemas/{marketplace.js → accounts.js} +4 -14
  50. package/dist/src/schemas/accounts.js.map +10 -0
  51. package/dist/src/schemas/index.d.ts +28 -77
  52. package/dist/src/schemas/index.js +59 -69
  53. package/dist/src/schemas/index.js.map +4 -4
  54. package/dist/src/types.d.ts +10 -0
  55. package/migrations/0037_nullable_api_key.ts +35 -0
  56. package/migrations/0038_drop_workflow_tables.ts +46 -0
  57. package/migrations/0039_tenants.ts +66 -0
  58. package/migrations/0040_tenant_key_generations.ts +29 -0
  59. package/migrations/0041_subgraphs_drop_api_key_id.ts +49 -0
  60. package/migrations/0042_tenant_project_id.ts +25 -0
  61. package/migrations/0043_tenant_usage_monthly.ts +36 -0
  62. package/migrations/0044_provisioning_audit_log.ts +40 -0
  63. package/package.json +15 -7
  64. package/dist/src/db/queries/marketplace.js +0 -142
  65. package/dist/src/db/queries/marketplace.js.map +0 -10
  66. package/dist/src/schemas/marketplace.d.ts +0 -63
  67. package/dist/src/schemas/marketplace.js.map +0 -10
@@ -13,7 +13,7 @@ declare function jsonb(value: unknown): RawBuilder<unknown>;
13
13
  declare function parseJsonb<T = unknown>(value: unknown): T;
14
14
  import { Kysely } from "kysely";
15
15
  import postgres from "postgres";
16
- import { Generated, Insertable, Selectable, Updateable } from "kysely";
16
+ import { ColumnType, Generated, Insertable, Selectable, Updateable } from "kysely";
17
17
  interface BlocksTable {
18
18
  height: number;
19
19
  hash: string;
@@ -70,7 +70,6 @@ interface SubgraphsTable {
70
70
  last_error_at: Date | null;
71
71
  total_processed: Generated<number>;
72
72
  total_errors: Generated<number>;
73
- api_key_id: string | null;
74
73
  account_id: string;
75
74
  handler_code: string | null;
76
75
  source_code: string | null;
@@ -374,7 +373,72 @@ interface Database {
374
373
  workflow_cursors: WorkflowCursorsTable;
375
374
  workflow_signer_secrets: WorkflowSignerSecretsTable;
376
375
  workflow_budgets: WorkflowBudgetsTable;
376
+ tenants: TenantsTable;
377
+ tenant_usage_monthly: TenantUsageMonthlyTable;
378
+ provisioning_audit_log: ProvisioningAuditLogTable;
377
379
  }
380
+ type TenantStatus = "provisioning" | "active" | "suspended" | "error" | "deleted";
381
+ interface TenantsTable {
382
+ id: Generated<string>;
383
+ account_id: string;
384
+ slug: string;
385
+ status: ColumnType<TenantStatus, TenantStatus | undefined, TenantStatus>;
386
+ plan: string;
387
+ cpus: ColumnType<number, number | string, number | string>;
388
+ memory_mb: number;
389
+ storage_limit_mb: number;
390
+ storage_used_mb: number | null;
391
+ pg_container_id: string | null;
392
+ api_container_id: string | null;
393
+ processor_container_id: string | null;
394
+ target_database_url_enc: Buffer;
395
+ tenant_jwt_secret_enc: Buffer;
396
+ anon_key_enc: Buffer;
397
+ service_key_enc: Buffer;
398
+ api_url_internal: string;
399
+ api_url_public: string;
400
+ trial_ends_at: Date;
401
+ suspended_at: Date | null;
402
+ last_health_check_at: Date | null;
403
+ service_gen: Generated<number>;
404
+ anon_gen: Generated<number>;
405
+ project_id: string | null;
406
+ created_at: Generated<Date>;
407
+ updated_at: Generated<Date>;
408
+ }
409
+ type Tenant = Selectable<TenantsTable>;
410
+ type InsertTenant = Insertable<TenantsTable>;
411
+ type UpdateTenant = Updateable<TenantsTable>;
412
+ interface TenantUsageMonthlyTable {
413
+ id: Generated<string>;
414
+ tenant_id: string;
415
+ period_month: Date;
416
+ storage_peak_mb: Generated<number>;
417
+ storage_avg_mb: Generated<number>;
418
+ storage_last_mb: Generated<number>;
419
+ measurements: Generated<number>;
420
+ first_at: Generated<Date>;
421
+ last_at: Generated<Date>;
422
+ }
423
+ type TenantUsageMonthly = Selectable<TenantUsageMonthlyTable>;
424
+ type InsertTenantUsageMonthly = Insertable<TenantUsageMonthlyTable>;
425
+ type UpdateTenantUsageMonthly = Updateable<TenantUsageMonthlyTable>;
426
+ type ProvisioningAuditEvent = "provision.start" | "provision.success" | "provision.failure" | "suspend" | "resume" | "resize" | "keys.rotate" | "bastion.key.upload" | "bastion.key.revoke" | "teardown";
427
+ type ProvisioningAuditStatus = "ok" | "error";
428
+ interface ProvisioningAuditLogTable {
429
+ id: Generated<string>;
430
+ tenant_id: string | null;
431
+ tenant_slug: string | null;
432
+ account_id: string | null;
433
+ actor: string;
434
+ event: ProvisioningAuditEvent;
435
+ status: ProvisioningAuditStatus;
436
+ detail: unknown | null;
437
+ error: string | null;
438
+ created_at: Generated<Date>;
439
+ }
440
+ type ProvisioningAuditLog = Selectable<ProvisioningAuditLogTable>;
441
+ type InsertProvisioningAuditLog = Insertable<ProvisioningAuditLogTable>;
378
442
  interface WorkflowBudgetsTable {
379
443
  id: Generated<string>;
380
444
  workflow_definition_id: string;
@@ -469,9 +533,28 @@ type UpdateChatSession = Updateable<ChatSessionsTable>;
469
533
  type ChatMessage = Selectable<ChatMessagesTable>;
470
534
  type InsertChatMessage = Insertable<ChatMessagesTable>;
471
535
  import { sql } from "kysely";
536
+ /**
537
+ * Kysely instance for the SOURCE DB (block/tx/event reads from the shared
538
+ * indexer). Resolution: `SOURCE_DATABASE_URL || DATABASE_URL`.
539
+ */
540
+ declare function getSourceDb(): Kysely<Database>;
541
+ /**
542
+ * Kysely instance for the TARGET DB (subgraph schemas, subgraphs table,
543
+ * account-scoped data — tenant-side writes). Resolution:
544
+ * `TARGET_DATABASE_URL || DATABASE_URL`.
545
+ */
546
+ declare function getTargetDb(): Kysely<Database>;
547
+ /**
548
+ * Backward-compat alias for `getTargetDb()`. Accepts an optional
549
+ * `connectionString` override used by seed/test helpers — when supplied,
550
+ * bypasses env resolution and uses the provided URL directly (still cached).
551
+ */
472
552
  declare function getDb(connectionString?: string): Kysely<Database>;
473
- /** Raw postgres.js client for dynamic schema DDL (CREATE SCHEMA, DROP, etc.) */
474
- declare function getRawClient(): ReturnType<typeof postgres>;
475
- /** Close the DB connection pool. Call in CLI commands to allow process exit. */
553
+ /**
554
+ * Raw postgres.js client for dynamic schema DDL (CREATE SCHEMA, DROP, etc.).
555
+ * Defaults to the target role (tenant schemas live in the target DB).
556
+ */
557
+ declare function getRawClient(role?: "source" | "target"): ReturnType<typeof postgres>;
558
+ /** Close all DB connection pools. Call in CLI commands to allow process exit. */
476
559
  declare function closeDb(): Promise<void>;
477
- export { sql, parseJsonb, jsonb, getRawClient, getDb, closeDb, WorkflowStepsTable, WorkflowStep, WorkflowSignerSecretsTable, WorkflowSignerSecret, WorkflowSchedulesTable, WorkflowSchedule, WorkflowRunsTable, WorkflowRun, WorkflowQueueTable, WorkflowQueueItem, WorkflowDefinitionsTable, WorkflowDefinition, WorkflowCursorsTable, WorkflowCursor, WorkflowBudgetsTable, WorkflowBudget, WaitlistTable, UsageSnapshotsTable, UsageSnapshot, UsageDailyTable, UsageDaily, UpdateWorkflowStep, UpdateWorkflowSignerSecret, UpdateWorkflowSchedule, UpdateWorkflowRun, UpdateWorkflowDefinition, UpdateWorkflowBudget, UpdateTransaction, UpdateSubgraph, UpdateProject, UpdateIndexProgress, UpdateEvent, UpdateChatSession, UpdateBlock, UpdateApiKey, TransactionsTable, Transaction, TeamMembersTable, TeamMember, TeamInvitationsTable, TeamInvitation, SubgraphsTable, SubgraphUsageDailyTable, SubgraphUsageDaily, SubgraphTableSnapshotsTable, SubgraphProcessingStatsTable, SubgraphHealthSnapshotsTable, SubgraphHealthSnapshot, SubgraphGapsTable, SubgraphGap, Subgraph, SessionsTable, Session, ProjectsTable, Project, MagicLinksTable, MagicLink, InsertWorkflowStep, InsertWorkflowSignerSecret, InsertWorkflowSchedule, InsertWorkflowRun, InsertWorkflowQueueItem, InsertWorkflowDefinition, InsertWorkflowBudget, InsertTransaction, InsertTeamMember, InsertTeamInvitation, InsertSubgraphUsageDaily, InsertSubgraphHealthSnapshot, InsertSubgraphGap, InsertSubgraph, InsertSession, InsertProject, InsertMagicLink, InsertIndexProgress, InsertEvent, InsertChatSession, InsertChatMessage, InsertBlock, InsertApiKey, InsertAccountInsight, InsertAccountAgentRun, InsertAccount, IndexProgressTable, IndexProgress, EventsTable, Event, Database, ChatSessionsTable, ChatSession, ChatMessagesTable, ChatMessage, BlocksTable, Block, ApiKeysTable, ApiKey, AccountsTable, AccountInsightsTable, AccountInsight, AccountAgentRunsTable, AccountAgentRun, Account };
560
+ export { sql, parseJsonb, jsonb, getTargetDb, getSourceDb, getRawClient, getDb, closeDb, WorkflowStepsTable, WorkflowStep, WorkflowSignerSecretsTable, WorkflowSignerSecret, WorkflowSchedulesTable, WorkflowSchedule, WorkflowRunsTable, WorkflowRun, WorkflowQueueTable, WorkflowQueueItem, WorkflowDefinitionsTable, WorkflowDefinition, WorkflowCursorsTable, WorkflowCursor, WorkflowBudgetsTable, WorkflowBudget, WaitlistTable, UsageSnapshotsTable, UsageSnapshot, UsageDailyTable, UsageDaily, UpdateWorkflowStep, UpdateWorkflowSignerSecret, UpdateWorkflowSchedule, UpdateWorkflowRun, UpdateWorkflowDefinition, UpdateWorkflowBudget, UpdateTransaction, UpdateTenantUsageMonthly, UpdateTenant, UpdateSubgraph, UpdateProject, UpdateIndexProgress, UpdateEvent, UpdateChatSession, UpdateBlock, UpdateApiKey, TransactionsTable, Transaction, TenantsTable, TenantUsageMonthlyTable, TenantUsageMonthly, TenantStatus, Tenant, TeamMembersTable, TeamMember, TeamInvitationsTable, TeamInvitation, SubgraphsTable, SubgraphUsageDailyTable, SubgraphUsageDaily, SubgraphTableSnapshotsTable, SubgraphProcessingStatsTable, SubgraphHealthSnapshotsTable, SubgraphHealthSnapshot, SubgraphGapsTable, SubgraphGap, Subgraph, SessionsTable, Session, ProvisioningAuditStatus, ProvisioningAuditLogTable, ProvisioningAuditLog, ProvisioningAuditEvent, ProjectsTable, Project, MagicLinksTable, MagicLink, InsertWorkflowStep, InsertWorkflowSignerSecret, InsertWorkflowSchedule, InsertWorkflowRun, InsertWorkflowQueueItem, InsertWorkflowDefinition, InsertWorkflowBudget, InsertTransaction, InsertTenantUsageMonthly, InsertTenant, InsertTeamMember, InsertTeamInvitation, InsertSubgraphUsageDaily, InsertSubgraphHealthSnapshot, InsertSubgraphGap, InsertSubgraph, InsertSession, InsertProvisioningAuditLog, InsertProject, InsertMagicLink, InsertIndexProgress, InsertEvent, InsertChatSession, InsertChatMessage, InsertBlock, InsertApiKey, InsertAccountInsight, InsertAccountAgentRun, InsertAccount, IndexProgressTable, IndexProgress, EventsTable, Event, Database, ChatSessionsTable, ChatSession, ChatMessagesTable, ChatMessage, BlocksTable, Block, ApiKeysTable, ApiKey, AccountsTable, AccountInsightsTable, AccountInsight, AccountAgentRunsTable, AccountAgentRun, Account };
@@ -36,48 +36,72 @@ import { Kysely } from "kysely";
36
36
  import { PostgresJSDialect } from "kysely-postgres-js";
37
37
  import postgres from "postgres";
38
38
  import { sql as sql2 } from "kysely";
39
- var db = null;
40
- var rawClient = null;
39
+ var DEFAULT_URL = "postgres://postgres:postgres@localhost:5432/secondlayer_dev";
40
+ var pools = new Map;
41
+ function resolveSourceUrl() {
42
+ return process.env.SOURCE_DATABASE_URL || process.env.DATABASE_URL || DEFAULT_URL;
43
+ }
44
+ function resolveTargetUrl() {
45
+ return process.env.TARGET_DATABASE_URL || process.env.DATABASE_URL || DEFAULT_URL;
46
+ }
47
+ function getOrCreatePool(url) {
48
+ const existing = pools.get(url);
49
+ if (existing)
50
+ return existing;
51
+ const host = (() => {
52
+ try {
53
+ return new URL(url).hostname;
54
+ } catch {
55
+ return "";
56
+ }
57
+ })();
58
+ const isLocal = host === "localhost" || host === "127.0.0.1" || !host.includes(".");
59
+ const poolMax = Number.parseInt(process.env.DATABASE_POOL_MAX ?? "20", 10);
60
+ const rawClient = postgres(url, {
61
+ max: poolMax,
62
+ ssl: isLocal ? undefined : {
63
+ rejectUnauthorized: process.env.NODE_TLS_REJECT_UNAUTHORIZED !== "0"
64
+ }
65
+ });
66
+ const db = new Kysely({
67
+ dialect: new PostgresJSDialect({ postgres: rawClient })
68
+ });
69
+ const entry = { db, rawClient };
70
+ pools.set(url, entry);
71
+ return entry;
72
+ }
73
+ function getSourceDb() {
74
+ return getOrCreatePool(resolveSourceUrl()).db;
75
+ }
76
+ function getTargetDb() {
77
+ return getOrCreatePool(resolveTargetUrl()).db;
78
+ }
41
79
  function getDb(connectionString) {
42
- if (!db) {
43
- const url = connectionString || process.env.DATABASE_URL || "postgres://postgres:postgres@localhost:5432/secondlayer_dev";
44
- const isLocal = url.includes("localhost") || url.includes("127.0.0.1") || url.includes("@postgres:");
45
- const poolMax = Number.parseInt(process.env.DATABASE_POOL_MAX ?? "20", 10);
46
- rawClient = postgres(url, {
47
- max: poolMax,
48
- ssl: isLocal ? undefined : {
49
- rejectUnauthorized: process.env.NODE_TLS_REJECT_UNAUTHORIZED !== "0"
50
- }
51
- });
52
- db = new Kysely({
53
- dialect: new PostgresJSDialect({ postgres: rawClient })
54
- });
55
- }
56
- return db;
80
+ if (connectionString)
81
+ return getOrCreatePool(connectionString).db;
82
+ return getTargetDb();
57
83
  }
58
- function getRawClient() {
59
- if (!rawClient)
60
- getDb();
61
- return rawClient;
84
+ function getRawClient(role = "target") {
85
+ const url = role === "source" ? resolveSourceUrl() : resolveTargetUrl();
86
+ return getOrCreatePool(url).rawClient;
62
87
  }
63
88
  async function closeDb() {
64
- if (db) {
65
- await db.destroy();
66
- db = null;
67
- }
68
- if (rawClient) {
69
- await rawClient.end();
70
- rawClient = null;
89
+ for (const entry of pools.values()) {
90
+ await entry.db.destroy();
91
+ await entry.rawClient.end();
71
92
  }
93
+ pools.clear();
72
94
  }
73
95
  export {
74
96
  sql2 as sql,
75
97
  parseJsonb,
76
98
  jsonb,
99
+ getTargetDb,
100
+ getSourceDb,
77
101
  getRawClient,
78
102
  getDb,
79
103
  closeDb
80
104
  };
81
105
 
82
- //# debugId=E1023FA2C43975A264756E2164756E21
106
+ //# debugId=C0DD7408E000897364756E2164756E21
83
107
  //# sourceMappingURL=index.js.map
@@ -2,10 +2,10 @@
2
2
  "version": 3,
3
3
  "sources": ["../src/db/jsonb.ts", "../src/db/index.ts"],
4
4
  "sourcesContent": [
5
- "import { type RawBuilder, sql } from \"kysely\";\n\n/**\n * Safely encode a JS value as a JSONB literal for Kysely inserts/updates.\n * Kysely + postgres.js double-encodes JSON when using parameterized queries\n * with ::jsonb casts. This uses sql.raw to inline a properly escaped literal.\n */\nexport function jsonb(value: unknown): RawBuilder<unknown> {\n\tconst escaped = JSON.stringify(value, (_k, v) => (typeof v === \"bigint\" ? v.toString() : v)).replace(/'/g, \"''\");\n\treturn sql`${sql.raw(`'${escaped}'::jsonb`)}`;\n}\n\n/**\n * Safely parse a JSONB value from the database.\n * Handles double-encoded strings where postgres.js returns a JSON string\n * instead of a parsed object.\n */\nexport function parseJsonb<T = unknown>(value: unknown): T {\n\tif (typeof value === \"string\") {\n\t\ttry {\n\t\t\treturn JSON.parse(value) as T;\n\t\t} catch {\n\t\t\treturn value as T;\n\t\t}\n\t}\n\treturn (value ?? {}) as T;\n}\n",
6
- "import { Kysely } from \"kysely\";\nimport { PostgresJSDialect } from \"kysely-postgres-js\";\nimport postgres from \"postgres\";\nimport type { Database } from \"./types.ts\";\n\nlet db: Kysely<Database> | null = null;\nlet rawClient: ReturnType<typeof postgres> | null = null;\n\nexport function getDb(connectionString?: string): Kysely<Database> {\n\tif (!db) {\n\t\tconst url =\n\t\t\tconnectionString ||\n\t\t\tprocess.env.DATABASE_URL ||\n\t\t\t\"postgres://postgres:postgres@localhost:5432/secondlayer_dev\";\n\n\t\t// Always use SSL for remote databases, just disable cert verification if needed\n\t\tconst isLocal =\n\t\t\turl.includes(\"localhost\") ||\n\t\t\turl.includes(\"127.0.0.1\") ||\n\t\t\turl.includes(\"@postgres:\");\n\t\tconst poolMax = Number.parseInt(process.env.DATABASE_POOL_MAX ?? \"20\", 10);\n\t\trawClient = postgres(url, {\n\t\t\tmax: poolMax,\n\t\t\tssl: isLocal\n\t\t\t\t? undefined\n\t\t\t\t: {\n\t\t\t\t\t\trejectUnauthorized:\n\t\t\t\t\t\t\tprocess.env.NODE_TLS_REJECT_UNAUTHORIZED !== \"0\",\n\t\t\t\t\t},\n\t\t});\n\t\tdb = new Kysely<Database>({\n\t\t\tdialect: new PostgresJSDialect({ postgres: rawClient }),\n\t\t});\n\t}\n\treturn db;\n}\n\n/** Raw postgres.js client for dynamic schema DDL (CREATE SCHEMA, DROP, etc.) */\nexport function getRawClient(): ReturnType<typeof postgres> {\n\tif (!rawClient) getDb();\n\treturn rawClient!;\n}\n\n/** Close the DB connection pool. Call in CLI commands to allow process exit. */\nexport async function closeDb(): Promise<void> {\n\tif (db) {\n\t\tawait db.destroy();\n\t\tdb = null;\n\t}\n\tif (rawClient) {\n\t\tawait rawClient.end();\n\t\trawClient = null;\n\t}\n}\n\nimport { sql } from \"kysely\";\nexport { sql };\nexport * from \"./types.ts\";\nexport { jsonb, parseJsonb } from \"./jsonb.ts\";\n"
5
+ "import { type RawBuilder, sql } from \"kysely\";\n\n/**\n * Safely encode a JS value as a JSONB literal for Kysely inserts/updates.\n * Kysely + postgres.js double-encodes JSON when using parameterized queries\n * with ::jsonb casts. This uses sql.raw to inline a properly escaped literal.\n */\nexport function jsonb(value: unknown): RawBuilder<unknown> {\n\tconst escaped = JSON.stringify(value, (_k, v) =>\n\t\ttypeof v === \"bigint\" ? v.toString() : v,\n\t).replace(/'/g, \"''\");\n\treturn sql`${sql.raw(`'${escaped}'::jsonb`)}`;\n}\n\n/**\n * Safely parse a JSONB value from the database.\n * Handles double-encoded strings where postgres.js returns a JSON string\n * instead of a parsed object.\n */\nexport function parseJsonb<T = unknown>(value: unknown): T {\n\tif (typeof value === \"string\") {\n\t\ttry {\n\t\t\treturn JSON.parse(value) as T;\n\t\t} catch {\n\t\t\treturn value as T;\n\t\t}\n\t}\n\treturn (value ?? {}) as T;\n}\n",
6
+ "import { Kysely } from \"kysely\";\nimport { PostgresJSDialect } from \"kysely-postgres-js\";\nimport postgres from \"postgres\";\nimport type { Database } from \"./types.ts\";\n\nconst DEFAULT_URL =\n\t\"postgres://postgres:postgres@localhost:5432/secondlayer_dev\";\n\ninterface PoolEntry {\n\tdb: Kysely<Database>;\n\trawClient: ReturnType<typeof postgres>;\n}\n\n/**\n * Cache of Kysely + raw postgres.js pools keyed by resolved URL.\n * Two getters resolving to the same URL share one entry (single pool) —\n * this is the single-DB backward-compat contract: when only `DATABASE_URL`\n * is set, `getSourceDb() === getTargetDb()` (zero regression vs. pre-dual-DB).\n */\nconst pools = new Map<string, PoolEntry>();\n\nfunction resolveSourceUrl(): string {\n\treturn (\n\t\tprocess.env.SOURCE_DATABASE_URL || process.env.DATABASE_URL || DEFAULT_URL\n\t);\n}\n\nfunction resolveTargetUrl(): string {\n\treturn (\n\t\tprocess.env.TARGET_DATABASE_URL || process.env.DATABASE_URL || DEFAULT_URL\n\t);\n}\n\nfunction getOrCreatePool(url: string): PoolEntry {\n\tconst existing = pools.get(url);\n\tif (existing) return existing;\n\n\t// \"Local\" = we skip TLS. Any Docker service alias (single-label hostname\n\t// with no dots) is on an internal network and won't serve TLS.\n\tconst host = (() => {\n\t\ttry {\n\t\t\treturn new URL(url).hostname;\n\t\t} catch {\n\t\t\treturn \"\";\n\t\t}\n\t})();\n\tconst isLocal =\n\t\thost === \"localhost\" || host === \"127.0.0.1\" || !host.includes(\".\");\n\tconst poolMax = Number.parseInt(process.env.DATABASE_POOL_MAX ?? \"20\", 10);\n\tconst rawClient = postgres(url, {\n\t\tmax: poolMax,\n\t\tssl: isLocal\n\t\t\t? undefined\n\t\t\t: {\n\t\t\t\t\trejectUnauthorized: process.env.NODE_TLS_REJECT_UNAUTHORIZED !== \"0\",\n\t\t\t\t},\n\t});\n\tconst db = new Kysely<Database>({\n\t\tdialect: new PostgresJSDialect({ postgres: rawClient }),\n\t});\n\tconst entry: PoolEntry = { db, rawClient };\n\tpools.set(url, entry);\n\treturn entry;\n}\n\n/**\n * Kysely instance for the SOURCE DB (block/tx/event reads from the shared\n * indexer). Resolution: `SOURCE_DATABASE_URL || DATABASE_URL`.\n */\nexport function getSourceDb(): Kysely<Database> {\n\treturn getOrCreatePool(resolveSourceUrl()).db;\n}\n\n/**\n * Kysely instance for the TARGET DB (subgraph schemas, subgraphs table,\n * account-scoped data — tenant-side writes). Resolution:\n * `TARGET_DATABASE_URL || DATABASE_URL`.\n */\nexport function getTargetDb(): Kysely<Database> {\n\treturn getOrCreatePool(resolveTargetUrl()).db;\n}\n\n/**\n * Backward-compat alias for `getTargetDb()`. Accepts an optional\n * `connectionString` override used by seed/test helpers — when supplied,\n * bypasses env resolution and uses the provided URL directly (still cached).\n */\nexport function getDb(connectionString?: string): Kysely<Database> {\n\tif (connectionString) return getOrCreatePool(connectionString).db;\n\treturn getTargetDb();\n}\n\n/**\n * Raw postgres.js client for dynamic schema DDL (CREATE SCHEMA, DROP, etc.).\n * Defaults to the target role (tenant schemas live in the target DB).\n */\nexport function getRawClient(\n\trole: \"source\" | \"target\" = \"target\",\n): ReturnType<typeof postgres> {\n\tconst url = role === \"source\" ? resolveSourceUrl() : resolveTargetUrl();\n\treturn getOrCreatePool(url).rawClient;\n}\n\n/** Close all DB connection pools. Call in CLI commands to allow process exit. */\nexport async function closeDb(): Promise<void> {\n\tfor (const entry of pools.values()) {\n\t\tawait entry.db.destroy();\n\t\tawait entry.rawClient.end();\n\t}\n\tpools.clear();\n}\n\nimport { sql } from \"kysely\";\nexport { sql };\nexport * from \"./types.ts\";\nexport { jsonb, parseJsonb } from \"./jsonb.ts\";\n"
7
7
  ],
8
- "mappings": ";;;;;;;;;;;;;;;;;AAAA;AAOO,SAAS,KAAK,CAAC,OAAqC;AAAA,EAC1D,MAAM,UAAU,KAAK,UAAU,OAAO,CAAC,IAAI,MAAO,OAAO,MAAM,WAAW,EAAE,SAAS,IAAI,CAAE,EAAE,QAAQ,MAAM,IAAI;AAAA,EAC/G,OAAO,MAAM,IAAI,IAAI,IAAI,iBAAiB;AAAA;AAQpC,SAAS,UAAuB,CAAC,OAAmB;AAAA,EAC1D,IAAI,OAAO,UAAU,UAAU;AAAA,IAC9B,IAAI;AAAA,MACH,OAAO,KAAK,MAAM,KAAK;AAAA,MACtB,MAAM;AAAA,MACP,OAAO;AAAA;AAAA,EAET;AAAA,EACA,OAAQ,SAAS,CAAC;AAAA;;;ACzBnB;AACA;AACA;AAqDA,gBAAS;AAlDT,IAAI,KAA8B;AAClC,IAAI,YAAgD;AAE7C,SAAS,KAAK,CAAC,kBAA6C;AAAA,EAClE,IAAI,CAAC,IAAI;AAAA,IACR,MAAM,MACL,oBACA,QAAQ,IAAI,gBACZ;AAAA,IAGD,MAAM,UACL,IAAI,SAAS,WAAW,KACxB,IAAI,SAAS,WAAW,KACxB,IAAI,SAAS,YAAY;AAAA,IAC1B,MAAM,UAAU,OAAO,SAAS,QAAQ,IAAI,qBAAqB,MAAM,EAAE;AAAA,IACzE,YAAY,SAAS,KAAK;AAAA,MACzB,KAAK;AAAA,MACL,KAAK,UACF,YACA;AAAA,QACA,oBACC,QAAQ,IAAI,iCAAiC;AAAA,MAC/C;AAAA,IACH,CAAC;AAAA,IACD,KAAK,IAAI,OAAiB;AAAA,MACzB,SAAS,IAAI,kBAAkB,EAAE,UAAU,UAAU,CAAC;AAAA,IACvD,CAAC;AAAA,EACF;AAAA,EACA,OAAO;AAAA;AAID,SAAS,YAAY,GAAgC;AAAA,EAC3D,IAAI,CAAC;AAAA,IAAW,MAAM;AAAA,EACtB,OAAO;AAAA;AAIR,eAAsB,OAAO,GAAkB;AAAA,EAC9C,IAAI,IAAI;AAAA,IACP,MAAM,GAAG,QAAQ;AAAA,IACjB,KAAK;AAAA,EACN;AAAA,EACA,IAAI,WAAW;AAAA,IACd,MAAM,UAAU,IAAI;AAAA,IACpB,YAAY;AAAA,EACb;AAAA;",
9
- "debugId": "E1023FA2C43975A264756E2164756E21",
8
+ "mappings": ";;;;;;;;;;;;;;;;;AAAA;AAOO,SAAS,KAAK,CAAC,OAAqC;AAAA,EAC1D,MAAM,UAAU,KAAK,UAAU,OAAO,CAAC,IAAI,MAC1C,OAAO,MAAM,WAAW,EAAE,SAAS,IAAI,CACxC,EAAE,QAAQ,MAAM,IAAI;AAAA,EACpB,OAAO,MAAM,IAAI,IAAI,IAAI,iBAAiB;AAAA;AAQpC,SAAS,UAAuB,CAAC,OAAmB;AAAA,EAC1D,IAAI,OAAO,UAAU,UAAU;AAAA,IAC9B,IAAI;AAAA,MACH,OAAO,KAAK,MAAM,KAAK;AAAA,MACtB,MAAM;AAAA,MACP,OAAO;AAAA;AAAA,EAET;AAAA,EACA,OAAQ,SAAS,CAAC;AAAA;;;AC3BnB;AACA;AACA;AA8GA,gBAAS;AA3GT,IAAM,cACL;AAaD,IAAM,QAAQ,IAAI;AAElB,SAAS,gBAAgB,GAAW;AAAA,EACnC,OACC,QAAQ,IAAI,uBAAuB,QAAQ,IAAI,gBAAgB;AAAA;AAIjE,SAAS,gBAAgB,GAAW;AAAA,EACnC,OACC,QAAQ,IAAI,uBAAuB,QAAQ,IAAI,gBAAgB;AAAA;AAIjE,SAAS,eAAe,CAAC,KAAwB;AAAA,EAChD,MAAM,WAAW,MAAM,IAAI,GAAG;AAAA,EAC9B,IAAI;AAAA,IAAU,OAAO;AAAA,EAIrB,MAAM,QAAQ,MAAM;AAAA,IACnB,IAAI;AAAA,MACH,OAAO,IAAI,IAAI,GAAG,EAAE;AAAA,MACnB,MAAM;AAAA,MACP,OAAO;AAAA;AAAA,KAEN;AAAA,EACH,MAAM,UACL,SAAS,eAAe,SAAS,eAAe,CAAC,KAAK,SAAS,GAAG;AAAA,EACnE,MAAM,UAAU,OAAO,SAAS,QAAQ,IAAI,qBAAqB,MAAM,EAAE;AAAA,EACzE,MAAM,YAAY,SAAS,KAAK;AAAA,IAC/B,KAAK;AAAA,IACL,KAAK,UACF,YACA;AAAA,MACA,oBAAoB,QAAQ,IAAI,iCAAiC;AAAA,IAClE;AAAA,EACH,CAAC;AAAA,EACD,MAAM,KAAK,IAAI,OAAiB;AAAA,IAC/B,SAAS,IAAI,kBAAkB,EAAE,UAAU,UAAU,CAAC;AAAA,EACvD,CAAC;AAAA,EACD,MAAM,QAAmB,EAAE,IAAI,UAAU;AAAA,EACzC,MAAM,IAAI,KAAK,KAAK;AAAA,EACpB,OAAO;AAAA;AAOD,SAAS,WAAW,GAAqB;AAAA,EAC/C,OAAO,gBAAgB,iBAAiB,CAAC,EAAE;AAAA;AAQrC,SAAS,WAAW,GAAqB;AAAA,EAC/C,OAAO,gBAAgB,iBAAiB,CAAC,EAAE;AAAA;AAQrC,SAAS,KAAK,CAAC,kBAA6C;AAAA,EAClE,IAAI;AAAA,IAAkB,OAAO,gBAAgB,gBAAgB,EAAE;AAAA,EAC/D,OAAO,YAAY;AAAA;AAOb,SAAS,YAAY,CAC3B,OAA4B,UACE;AAAA,EAC9B,MAAM,MAAM,SAAS,WAAW,iBAAiB,IAAI,iBAAiB;AAAA,EACtE,OAAO,gBAAgB,GAAG,EAAE;AAAA;AAI7B,eAAsB,OAAO,GAAkB;AAAA,EAC9C,WAAW,SAAS,MAAM,OAAO,GAAG;AAAA,IACnC,MAAM,MAAM,GAAG,QAAQ;AAAA,IACvB,MAAM,MAAM,UAAU,IAAI;AAAA,EAC3B;AAAA,EACA,MAAM,MAAM;AAAA;",
9
+ "debugId": "C0DD7408E000897364756E2164756E21",
10
10
  "names": []
11
11
  }
@@ -2,9 +2,9 @@
2
2
  "version": 3,
3
3
  "sources": ["../src/db/jsonb.ts"],
4
4
  "sourcesContent": [
5
- "import { type RawBuilder, sql } from \"kysely\";\n\n/**\n * Safely encode a JS value as a JSONB literal for Kysely inserts/updates.\n * Kysely + postgres.js double-encodes JSON when using parameterized queries\n * with ::jsonb casts. This uses sql.raw to inline a properly escaped literal.\n */\nexport function jsonb(value: unknown): RawBuilder<unknown> {\n\tconst escaped = JSON.stringify(value, (_k, v) => (typeof v === \"bigint\" ? v.toString() : v)).replace(/'/g, \"''\");\n\treturn sql`${sql.raw(`'${escaped}'::jsonb`)}`;\n}\n\n/**\n * Safely parse a JSONB value from the database.\n * Handles double-encoded strings where postgres.js returns a JSON string\n * instead of a parsed object.\n */\nexport function parseJsonb<T = unknown>(value: unknown): T {\n\tif (typeof value === \"string\") {\n\t\ttry {\n\t\t\treturn JSON.parse(value) as T;\n\t\t} catch {\n\t\t\treturn value as T;\n\t\t}\n\t}\n\treturn (value ?? {}) as T;\n}\n"
5
+ "import { type RawBuilder, sql } from \"kysely\";\n\n/**\n * Safely encode a JS value as a JSONB literal for Kysely inserts/updates.\n * Kysely + postgres.js double-encodes JSON when using parameterized queries\n * with ::jsonb casts. This uses sql.raw to inline a properly escaped literal.\n */\nexport function jsonb(value: unknown): RawBuilder<unknown> {\n\tconst escaped = JSON.stringify(value, (_k, v) =>\n\t\ttypeof v === \"bigint\" ? v.toString() : v,\n\t).replace(/'/g, \"''\");\n\treturn sql`${sql.raw(`'${escaped}'::jsonb`)}`;\n}\n\n/**\n * Safely parse a JSONB value from the database.\n * Handles double-encoded strings where postgres.js returns a JSON string\n * instead of a parsed object.\n */\nexport function parseJsonb<T = unknown>(value: unknown): T {\n\tif (typeof value === \"string\") {\n\t\ttry {\n\t\t\treturn JSON.parse(value) as T;\n\t\t} catch {\n\t\t\treturn value as T;\n\t\t}\n\t}\n\treturn (value ?? {}) as T;\n}\n"
6
6
  ],
7
- "mappings": ";;;;;;;;;;;;;;;;;AAAA;AAOO,SAAS,KAAK,CAAC,OAAqC;AAAA,EAC1D,MAAM,UAAU,KAAK,UAAU,OAAO,CAAC,IAAI,MAAO,OAAO,MAAM,WAAW,EAAE,SAAS,IAAI,CAAE,EAAE,QAAQ,MAAM,IAAI;AAAA,EAC/G,OAAO,MAAM,IAAI,IAAI,IAAI,iBAAiB;AAAA;AAQpC,SAAS,UAAuB,CAAC,OAAmB;AAAA,EAC1D,IAAI,OAAO,UAAU,UAAU;AAAA,IAC9B,IAAI;AAAA,MACH,OAAO,KAAK,MAAM,KAAK;AAAA,MACtB,MAAM;AAAA,MACP,OAAO;AAAA;AAAA,EAET;AAAA,EACA,OAAQ,SAAS,CAAC;AAAA;",
7
+ "mappings": ";;;;;;;;;;;;;;;;;AAAA;AAOO,SAAS,KAAK,CAAC,OAAqC;AAAA,EAC1D,MAAM,UAAU,KAAK,UAAU,OAAO,CAAC,IAAI,MAC1C,OAAO,MAAM,WAAW,EAAE,SAAS,IAAI,CACxC,EAAE,QAAQ,MAAM,IAAI;AAAA,EACpB,OAAO,MAAM,IAAI,IAAI,IAAI,iBAAiB;AAAA;AAQpC,SAAS,UAAuB,CAAC,OAAmB;AAAA,EAC1D,IAAI,OAAO,UAAU,UAAU;AAAA,IAC9B,IAAI;AAAA,MACH,OAAO,KAAK,MAAM,KAAK;AAAA,MACtB,MAAM;AAAA,MACP,OAAO;AAAA;AAAA,EAET;AAAA,EACA,OAAQ,SAAS,CAAC;AAAA;",
8
8
  "debugId": "DA6ABA81E2ABDC7864756E2164756E21",
9
9
  "names": []
10
10
  }
@@ -1,6 +1,6 @@
1
1
  import { Kysely } from "kysely";
2
2
  import { Selectable as Selectable2 } from "kysely";
3
- import { Generated, Selectable } from "kysely";
3
+ import { ColumnType, Generated, Selectable } from "kysely";
4
4
  interface BlocksTable {
5
5
  height: number;
6
6
  hash: string;
@@ -57,7 +57,6 @@ interface SubgraphsTable {
57
57
  last_error_at: Date | null;
58
58
  total_processed: Generated<number>;
59
59
  total_errors: Generated<number>;
60
- api_key_id: string | null;
61
60
  account_id: string;
62
61
  handler_code: string | null;
63
62
  source_code: string | null;
@@ -361,6 +360,63 @@ interface Database {
361
360
  workflow_cursors: WorkflowCursorsTable;
362
361
  workflow_signer_secrets: WorkflowSignerSecretsTable;
363
362
  workflow_budgets: WorkflowBudgetsTable;
363
+ tenants: TenantsTable;
364
+ tenant_usage_monthly: TenantUsageMonthlyTable;
365
+ provisioning_audit_log: ProvisioningAuditLogTable;
366
+ }
367
+ type TenantStatus = "provisioning" | "active" | "suspended" | "error" | "deleted";
368
+ interface TenantsTable {
369
+ id: Generated<string>;
370
+ account_id: string;
371
+ slug: string;
372
+ status: ColumnType<TenantStatus, TenantStatus | undefined, TenantStatus>;
373
+ plan: string;
374
+ cpus: ColumnType<number, number | string, number | string>;
375
+ memory_mb: number;
376
+ storage_limit_mb: number;
377
+ storage_used_mb: number | null;
378
+ pg_container_id: string | null;
379
+ api_container_id: string | null;
380
+ processor_container_id: string | null;
381
+ target_database_url_enc: Buffer;
382
+ tenant_jwt_secret_enc: Buffer;
383
+ anon_key_enc: Buffer;
384
+ service_key_enc: Buffer;
385
+ api_url_internal: string;
386
+ api_url_public: string;
387
+ trial_ends_at: Date;
388
+ suspended_at: Date | null;
389
+ last_health_check_at: Date | null;
390
+ service_gen: Generated<number>;
391
+ anon_gen: Generated<number>;
392
+ project_id: string | null;
393
+ created_at: Generated<Date>;
394
+ updated_at: Generated<Date>;
395
+ }
396
+ interface TenantUsageMonthlyTable {
397
+ id: Generated<string>;
398
+ tenant_id: string;
399
+ period_month: Date;
400
+ storage_peak_mb: Generated<number>;
401
+ storage_avg_mb: Generated<number>;
402
+ storage_last_mb: Generated<number>;
403
+ measurements: Generated<number>;
404
+ first_at: Generated<Date>;
405
+ last_at: Generated<Date>;
406
+ }
407
+ type ProvisioningAuditEvent = "provision.start" | "provision.success" | "provision.failure" | "suspend" | "resume" | "resize" | "keys.rotate" | "bastion.key.upload" | "bastion.key.revoke" | "teardown";
408
+ type ProvisioningAuditStatus = "ok" | "error";
409
+ interface ProvisioningAuditLogTable {
410
+ id: Generated<string>;
411
+ tenant_id: string | null;
412
+ tenant_slug: string | null;
413
+ account_id: string | null;
414
+ actor: string;
415
+ event: ProvisioningAuditEvent;
416
+ status: ProvisioningAuditStatus;
417
+ detail: unknown | null;
418
+ error: string | null;
419
+ created_at: Generated<Date>;
364
420
  }
365
421
  interface WorkflowBudgetsTable {
366
422
  id: Generated<string>;
@@ -1,5 +1,5 @@
1
1
  import { Kysely } from "kysely";
2
- import { Generated } from "kysely";
2
+ import { ColumnType, Generated } from "kysely";
3
3
  interface BlocksTable {
4
4
  height: number;
5
5
  hash: string;
@@ -56,7 +56,6 @@ interface SubgraphsTable {
56
56
  last_error_at: Date | null;
57
57
  total_processed: Generated<number>;
58
58
  total_errors: Generated<number>;
59
- api_key_id: string | null;
60
59
  account_id: string;
61
60
  handler_code: string | null;
62
61
  source_code: string | null;
@@ -360,6 +359,63 @@ interface Database {
360
359
  workflow_cursors: WorkflowCursorsTable;
361
360
  workflow_signer_secrets: WorkflowSignerSecretsTable;
362
361
  workflow_budgets: WorkflowBudgetsTable;
362
+ tenants: TenantsTable;
363
+ tenant_usage_monthly: TenantUsageMonthlyTable;
364
+ provisioning_audit_log: ProvisioningAuditLogTable;
365
+ }
366
+ type TenantStatus = "provisioning" | "active" | "suspended" | "error" | "deleted";
367
+ interface TenantsTable {
368
+ id: Generated<string>;
369
+ account_id: string;
370
+ slug: string;
371
+ status: ColumnType<TenantStatus, TenantStatus | undefined, TenantStatus>;
372
+ plan: string;
373
+ cpus: ColumnType<number, number | string, number | string>;
374
+ memory_mb: number;
375
+ storage_limit_mb: number;
376
+ storage_used_mb: number | null;
377
+ pg_container_id: string | null;
378
+ api_container_id: string | null;
379
+ processor_container_id: string | null;
380
+ target_database_url_enc: Buffer;
381
+ tenant_jwt_secret_enc: Buffer;
382
+ anon_key_enc: Buffer;
383
+ service_key_enc: Buffer;
384
+ api_url_internal: string;
385
+ api_url_public: string;
386
+ trial_ends_at: Date;
387
+ suspended_at: Date | null;
388
+ last_health_check_at: Date | null;
389
+ service_gen: Generated<number>;
390
+ anon_gen: Generated<number>;
391
+ project_id: string | null;
392
+ created_at: Generated<Date>;
393
+ updated_at: Generated<Date>;
394
+ }
395
+ interface TenantUsageMonthlyTable {
396
+ id: Generated<string>;
397
+ tenant_id: string;
398
+ period_month: Date;
399
+ storage_peak_mb: Generated<number>;
400
+ storage_avg_mb: Generated<number>;
401
+ storage_last_mb: Generated<number>;
402
+ measurements: Generated<number>;
403
+ first_at: Generated<Date>;
404
+ last_at: Generated<Date>;
405
+ }
406
+ type ProvisioningAuditEvent = "provision.start" | "provision.success" | "provision.failure" | "suspend" | "resume" | "resize" | "keys.rotate" | "bastion.key.upload" | "bastion.key.revoke" | "teardown";
407
+ type ProvisioningAuditStatus = "ok" | "error";
408
+ interface ProvisioningAuditLogTable {
409
+ id: Generated<string>;
410
+ tenant_id: string | null;
411
+ tenant_slug: string | null;
412
+ account_id: string | null;
413
+ actor: string;
414
+ event: ProvisioningAuditEvent;
415
+ status: ProvisioningAuditStatus;
416
+ detail: unknown | null;
417
+ error: string | null;
418
+ created_at: Generated<Date>;
363
419
  }
364
420
  interface WorkflowBudgetsTable {
365
421
  id: Generated<string>;
@@ -1,5 +1,5 @@
1
1
  import { Kysely } from "kysely";
2
- import { Generated, Selectable } from "kysely";
2
+ import { ColumnType, Generated, Selectable } from "kysely";
3
3
  interface BlocksTable {
4
4
  height: number;
5
5
  hash: string;
@@ -56,7 +56,6 @@ interface SubgraphsTable {
56
56
  last_error_at: Date | null;
57
57
  total_processed: Generated<number>;
58
58
  total_errors: Generated<number>;
59
- api_key_id: string | null;
60
59
  account_id: string;
61
60
  handler_code: string | null;
62
61
  source_code: string | null;
@@ -360,6 +359,63 @@ interface Database {
360
359
  workflow_cursors: WorkflowCursorsTable;
361
360
  workflow_signer_secrets: WorkflowSignerSecretsTable;
362
361
  workflow_budgets: WorkflowBudgetsTable;
362
+ tenants: TenantsTable;
363
+ tenant_usage_monthly: TenantUsageMonthlyTable;
364
+ provisioning_audit_log: ProvisioningAuditLogTable;
365
+ }
366
+ type TenantStatus = "provisioning" | "active" | "suspended" | "error" | "deleted";
367
+ interface TenantsTable {
368
+ id: Generated<string>;
369
+ account_id: string;
370
+ slug: string;
371
+ status: ColumnType<TenantStatus, TenantStatus | undefined, TenantStatus>;
372
+ plan: string;
373
+ cpus: ColumnType<number, number | string, number | string>;
374
+ memory_mb: number;
375
+ storage_limit_mb: number;
376
+ storage_used_mb: number | null;
377
+ pg_container_id: string | null;
378
+ api_container_id: string | null;
379
+ processor_container_id: string | null;
380
+ target_database_url_enc: Buffer;
381
+ tenant_jwt_secret_enc: Buffer;
382
+ anon_key_enc: Buffer;
383
+ service_key_enc: Buffer;
384
+ api_url_internal: string;
385
+ api_url_public: string;
386
+ trial_ends_at: Date;
387
+ suspended_at: Date | null;
388
+ last_health_check_at: Date | null;
389
+ service_gen: Generated<number>;
390
+ anon_gen: Generated<number>;
391
+ project_id: string | null;
392
+ created_at: Generated<Date>;
393
+ updated_at: Generated<Date>;
394
+ }
395
+ interface TenantUsageMonthlyTable {
396
+ id: Generated<string>;
397
+ tenant_id: string;
398
+ period_month: Date;
399
+ storage_peak_mb: Generated<number>;
400
+ storage_avg_mb: Generated<number>;
401
+ storage_last_mb: Generated<number>;
402
+ measurements: Generated<number>;
403
+ first_at: Generated<Date>;
404
+ last_at: Generated<Date>;
405
+ }
406
+ type ProvisioningAuditEvent = "provision.start" | "provision.success" | "provision.failure" | "suspend" | "resume" | "resize" | "keys.rotate" | "bastion.key.upload" | "bastion.key.revoke" | "teardown";
407
+ type ProvisioningAuditStatus = "ok" | "error";
408
+ interface ProvisioningAuditLogTable {
409
+ id: Generated<string>;
410
+ tenant_id: string | null;
411
+ tenant_slug: string | null;
412
+ account_id: string | null;
413
+ actor: string;
414
+ event: ProvisioningAuditEvent;
415
+ status: ProvisioningAuditStatus;
416
+ detail: unknown | null;
417
+ error: string | null;
418
+ created_at: Generated<Date>;
363
419
  }
364
420
  interface WorkflowBudgetsTable {
365
421
  id: Generated<string>;
@@ -2,9 +2,9 @@
2
2
  "version": 3,
3
3
  "sources": ["../src/db/queries/projects.ts"],
4
4
  "sourcesContent": [
5
- "import type { Kysely } from \"kysely\";\nimport type { Database, Project, TeamInvitation } from \"../types.ts\";\n\nexport async function getProjectsByAccount(\n\tdb: Kysely<Database>,\n\taccountId: string,\n): Promise<Project[]> {\n\treturn db\n\t\t.selectFrom(\"projects\")\n\t\t.selectAll()\n\t\t.where(\"account_id\", \"=\", accountId)\n\t\t.orderBy(\"created_at\", \"asc\")\n\t\t.execute();\n}\n\nexport async function getProjectBySlug(\n\tdb: Kysely<Database>,\n\taccountId: string,\n\tslug: string,\n): Promise<Project | undefined> {\n\treturn db\n\t\t.selectFrom(\"projects\")\n\t\t.selectAll()\n\t\t.where(\"account_id\", \"=\", accountId)\n\t\t.where(\"slug\", \"=\", slug)\n\t\t.executeTakeFirst();\n}\n\nexport async function getTeamMembers(\n\tdb: Kysely<Database>,\n\tprojectId: string,\n): Promise<Array<{\n\tid: string;\n\trole: string;\n\tcreated_at: Date;\n\taccount_id: string;\n\temail: string;\n\tdisplay_name: string | null;\n\tavatar_url: string | null;\n\taccount_slug: string | null;\n}>> {\n\treturn db\n\t\t.selectFrom(\"team_members\")\n\t\t.innerJoin(\"accounts\", \"accounts.id\", \"team_members.account_id\")\n\t\t.select([\n\t\t\t\"team_members.id\",\n\t\t\t\"team_members.role\",\n\t\t\t\"team_members.created_at\",\n\t\t\t\"accounts.id as account_id\",\n\t\t\t\"accounts.email\",\n\t\t\t\"accounts.display_name\",\n\t\t\t\"accounts.avatar_url\",\n\t\t\t\"accounts.slug as account_slug\",\n\t\t])\n\t\t.where(\"team_members.project_id\", \"=\", projectId)\n\t\t.orderBy(\"team_members.created_at\", \"asc\")\n\t\t.execute();\n}\n\nexport async function getTeamInvitations(\n\tdb: Kysely<Database>,\n\tprojectId: string,\n): Promise<TeamInvitation[]> {\n\treturn db\n\t\t.selectFrom(\"team_invitations\")\n\t\t.selectAll()\n\t\t.where(\"project_id\", \"=\", projectId)\n\t\t.where(\"accepted_at\", \"is\", null)\n\t\t.orderBy(\"created_at\", \"desc\")\n\t\t.execute();\n}\n"
5
+ "import type { Kysely } from \"kysely\";\nimport type { Database, Project, TeamInvitation } from \"../types.ts\";\n\nexport async function getProjectsByAccount(\n\tdb: Kysely<Database>,\n\taccountId: string,\n): Promise<Project[]> {\n\treturn db\n\t\t.selectFrom(\"projects\")\n\t\t.selectAll()\n\t\t.where(\"account_id\", \"=\", accountId)\n\t\t.orderBy(\"created_at\", \"asc\")\n\t\t.execute();\n}\n\nexport async function getProjectBySlug(\n\tdb: Kysely<Database>,\n\taccountId: string,\n\tslug: string,\n): Promise<Project | undefined> {\n\treturn db\n\t\t.selectFrom(\"projects\")\n\t\t.selectAll()\n\t\t.where(\"account_id\", \"=\", accountId)\n\t\t.where(\"slug\", \"=\", slug)\n\t\t.executeTakeFirst();\n}\n\nexport async function getTeamMembers(\n\tdb: Kysely<Database>,\n\tprojectId: string,\n): Promise<\n\tArray<{\n\t\tid: string;\n\t\trole: string;\n\t\tcreated_at: Date;\n\t\taccount_id: string;\n\t\temail: string;\n\t\tdisplay_name: string | null;\n\t\tavatar_url: string | null;\n\t\taccount_slug: string | null;\n\t}>\n> {\n\treturn db\n\t\t.selectFrom(\"team_members\")\n\t\t.innerJoin(\"accounts\", \"accounts.id\", \"team_members.account_id\")\n\t\t.select([\n\t\t\t\"team_members.id\",\n\t\t\t\"team_members.role\",\n\t\t\t\"team_members.created_at\",\n\t\t\t\"accounts.id as account_id\",\n\t\t\t\"accounts.email\",\n\t\t\t\"accounts.display_name\",\n\t\t\t\"accounts.avatar_url\",\n\t\t\t\"accounts.slug as account_slug\",\n\t\t])\n\t\t.where(\"team_members.project_id\", \"=\", projectId)\n\t\t.orderBy(\"team_members.created_at\", \"asc\")\n\t\t.execute();\n}\n\nexport async function getTeamInvitations(\n\tdb: Kysely<Database>,\n\tprojectId: string,\n): Promise<TeamInvitation[]> {\n\treturn db\n\t\t.selectFrom(\"team_invitations\")\n\t\t.selectAll()\n\t\t.where(\"project_id\", \"=\", projectId)\n\t\t.where(\"accepted_at\", \"is\", null)\n\t\t.orderBy(\"created_at\", \"desc\")\n\t\t.execute();\n}\n"
6
6
  ],
7
- "mappings": ";;;;;;;;;;;;;;;;;AAGA,eAAsB,oBAAoB,CACzC,IACA,WACqB;AAAA,EACrB,OAAO,GACL,WAAW,UAAU,EACrB,UAAU,EACV,MAAM,cAAc,KAAK,SAAS,EAClC,QAAQ,cAAc,KAAK,EAC3B,QAAQ;AAAA;AAGX,eAAsB,gBAAgB,CACrC,IACA,WACA,MAC+B;AAAA,EAC/B,OAAO,GACL,WAAW,UAAU,EACrB,UAAU,EACV,MAAM,cAAc,KAAK,SAAS,EAClC,MAAM,QAAQ,KAAK,IAAI,EACvB,iBAAiB;AAAA;AAGpB,eAAsB,cAAc,CACnC,IACA,WAUG;AAAA,EACH,OAAO,GACL,WAAW,cAAc,EACzB,UAAU,YAAY,eAAe,yBAAyB,EAC9D,OAAO;AAAA,IACP;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACD,CAAC,EACA,MAAM,2BAA2B,KAAK,SAAS,EAC/C,QAAQ,2BAA2B,KAAK,EACxC,QAAQ;AAAA;AAGX,eAAsB,kBAAkB,CACvC,IACA,WAC4B;AAAA,EAC5B,OAAO,GACL,WAAW,kBAAkB,EAC7B,UAAU,EACV,MAAM,cAAc,KAAK,SAAS,EAClC,MAAM,eAAe,MAAM,IAAI,EAC/B,QAAQ,cAAc,MAAM,EAC5B,QAAQ;AAAA;",
7
+ "mappings": ";;;;;;;;;;;;;;;;;AAGA,eAAsB,oBAAoB,CACzC,IACA,WACqB;AAAA,EACrB,OAAO,GACL,WAAW,UAAU,EACrB,UAAU,EACV,MAAM,cAAc,KAAK,SAAS,EAClC,QAAQ,cAAc,KAAK,EAC3B,QAAQ;AAAA;AAGX,eAAsB,gBAAgB,CACrC,IACA,WACA,MAC+B;AAAA,EAC/B,OAAO,GACL,WAAW,UAAU,EACrB,UAAU,EACV,MAAM,cAAc,KAAK,SAAS,EAClC,MAAM,QAAQ,KAAK,IAAI,EACvB,iBAAiB;AAAA;AAGpB,eAAsB,cAAc,CACnC,IACA,WAYC;AAAA,EACD,OAAO,GACL,WAAW,cAAc,EACzB,UAAU,YAAY,eAAe,yBAAyB,EAC9D,OAAO;AAAA,IACP;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACD,CAAC,EACA,MAAM,2BAA2B,KAAK,SAAS,EAC/C,QAAQ,2BAA2B,KAAK,EACxC,QAAQ;AAAA;AAGX,eAAsB,kBAAkB,CACvC,IACA,WAC4B;AAAA,EAC5B,OAAO,GACL,WAAW,kBAAkB,EAC7B,UAAU,EACV,MAAM,cAAc,KAAK,SAAS,EAClC,MAAM,eAAe,MAAM,IAAI,EAC/B,QAAQ,cAAc,MAAM,EAC5B,QAAQ;AAAA;",
8
8
  "debugId": "F62DBD8C7A91B89F64756E2164756E21",
9
9
  "names": []
10
10
  }