@secondlayer/shared 1.1.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/dist/src/db/index.d.ts +57 -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 +31 -2
  6. package/dist/src/db/queries/integrity.d.ts +31 -2
  7. package/dist/src/db/queries/marketplace.d.ts +31 -2
  8. package/dist/src/db/queries/marketplace.js +6 -9
  9. package/dist/src/db/queries/marketplace.js.map +3 -3
  10. package/dist/src/db/queries/projects.d.ts +31 -2
  11. package/dist/src/db/queries/projects.js.map +2 -2
  12. package/dist/src/db/queries/subgraph-gaps.d.ts +31 -2
  13. package/dist/src/db/queries/subgraphs.d.ts +35 -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 +493 -0
  17. package/dist/src/db/queries/tenants.js +194 -0
  18. package/dist/src/db/queries/tenants.js.map +11 -0
  19. package/dist/src/db/queries/usage.d.ts +31 -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 +31 -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 +35 -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 +83 -8
  33. package/dist/src/index.js +88 -31
  34. package/dist/src/index.js.map +6 -6
  35. package/dist/src/logger.js +3 -1
  36. package/dist/src/logger.js.map +3 -3
  37. package/dist/src/mode.d.ts +30 -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 +31 -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/types.d.ts +10 -0
  49. package/migrations/0037_nullable_api_key.ts +35 -0
  50. package/migrations/0038_drop_workflow_tables.ts +46 -0
  51. package/migrations/0039_tenants.ts +66 -0
  52. package/migrations/0040_tenant_key_generations.ts +29 -0
  53. package/migrations/0041_subgraphs_drop_api_key_id.ts +49 -0
  54. package/migrations/0042_tenant_project_id.ts +25 -0
  55. package/package.json +9 -1
@@ -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,40 @@ interface Database {
374
373
  workflow_cursors: WorkflowCursorsTable;
375
374
  workflow_signer_secrets: WorkflowSignerSecretsTable;
376
375
  workflow_budgets: WorkflowBudgetsTable;
376
+ tenants: TenantsTable;
377
+ }
378
+ type TenantStatus = "provisioning" | "active" | "suspended" | "error" | "deleted";
379
+ interface TenantsTable {
380
+ id: Generated<string>;
381
+ account_id: string;
382
+ slug: string;
383
+ status: ColumnType<TenantStatus, TenantStatus | undefined, TenantStatus>;
384
+ plan: string;
385
+ cpus: ColumnType<number, number | string, number | string>;
386
+ memory_mb: number;
387
+ storage_limit_mb: number;
388
+ storage_used_mb: number | null;
389
+ pg_container_id: string | null;
390
+ api_container_id: string | null;
391
+ processor_container_id: string | null;
392
+ target_database_url_enc: Buffer;
393
+ tenant_jwt_secret_enc: Buffer;
394
+ anon_key_enc: Buffer;
395
+ service_key_enc: Buffer;
396
+ api_url_internal: string;
397
+ api_url_public: string;
398
+ trial_ends_at: Date;
399
+ suspended_at: Date | null;
400
+ last_health_check_at: Date | null;
401
+ service_gen: Generated<number>;
402
+ anon_gen: Generated<number>;
403
+ project_id: string | null;
404
+ created_at: Generated<Date>;
405
+ updated_at: Generated<Date>;
377
406
  }
407
+ type Tenant = Selectable<TenantsTable>;
408
+ type InsertTenant = Insertable<TenantsTable>;
409
+ type UpdateTenant = Updateable<TenantsTable>;
378
410
  interface WorkflowBudgetsTable {
379
411
  id: Generated<string>;
380
412
  workflow_definition_id: string;
@@ -469,9 +501,28 @@ type UpdateChatSession = Updateable<ChatSessionsTable>;
469
501
  type ChatMessage = Selectable<ChatMessagesTable>;
470
502
  type InsertChatMessage = Insertable<ChatMessagesTable>;
471
503
  import { sql } from "kysely";
504
+ /**
505
+ * Kysely instance for the SOURCE DB (block/tx/event reads from the shared
506
+ * indexer). Resolution: `SOURCE_DATABASE_URL || DATABASE_URL`.
507
+ */
508
+ declare function getSourceDb(): Kysely<Database>;
509
+ /**
510
+ * Kysely instance for the TARGET DB (subgraph schemas, subgraphs table,
511
+ * account-scoped data — tenant-side writes). Resolution:
512
+ * `TARGET_DATABASE_URL || DATABASE_URL`.
513
+ */
514
+ declare function getTargetDb(): Kysely<Database>;
515
+ /**
516
+ * Backward-compat alias for `getTargetDb()`. Accepts an optional
517
+ * `connectionString` override used by seed/test helpers — when supplied,
518
+ * bypasses env resolution and uses the provided URL directly (still cached).
519
+ */
472
520
  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. */
521
+ /**
522
+ * Raw postgres.js client for dynamic schema DDL (CREATE SCHEMA, DROP, etc.).
523
+ * Defaults to the target role (tenant schemas live in the target DB).
524
+ */
525
+ declare function getRawClient(role?: "source" | "target"): ReturnType<typeof postgres>;
526
+ /** Close all DB connection pools. Call in CLI commands to allow process exit. */
476
527
  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 };
528
+ 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, UpdateTenant, UpdateSubgraph, UpdateProject, UpdateIndexProgress, UpdateEvent, UpdateChatSession, UpdateBlock, UpdateApiKey, TransactionsTable, Transaction, TenantsTable, TenantStatus, Tenant, 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, InsertTenant, 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 };
@@ -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,36 @@ interface Database {
361
360
  workflow_cursors: WorkflowCursorsTable;
362
361
  workflow_signer_secrets: WorkflowSignerSecretsTable;
363
362
  workflow_budgets: WorkflowBudgetsTable;
363
+ tenants: TenantsTable;
364
+ }
365
+ type TenantStatus = "provisioning" | "active" | "suspended" | "error" | "deleted";
366
+ interface TenantsTable {
367
+ id: Generated<string>;
368
+ account_id: string;
369
+ slug: string;
370
+ status: ColumnType<TenantStatus, TenantStatus | undefined, TenantStatus>;
371
+ plan: string;
372
+ cpus: ColumnType<number, number | string, number | string>;
373
+ memory_mb: number;
374
+ storage_limit_mb: number;
375
+ storage_used_mb: number | null;
376
+ pg_container_id: string | null;
377
+ api_container_id: string | null;
378
+ processor_container_id: string | null;
379
+ target_database_url_enc: Buffer;
380
+ tenant_jwt_secret_enc: Buffer;
381
+ anon_key_enc: Buffer;
382
+ service_key_enc: Buffer;
383
+ api_url_internal: string;
384
+ api_url_public: string;
385
+ trial_ends_at: Date;
386
+ suspended_at: Date | null;
387
+ last_health_check_at: Date | null;
388
+ service_gen: Generated<number>;
389
+ anon_gen: Generated<number>;
390
+ project_id: string | null;
391
+ created_at: Generated<Date>;
392
+ updated_at: Generated<Date>;
364
393
  }
365
394
  interface WorkflowBudgetsTable {
366
395
  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,36 @@ interface Database {
360
359
  workflow_cursors: WorkflowCursorsTable;
361
360
  workflow_signer_secrets: WorkflowSignerSecretsTable;
362
361
  workflow_budgets: WorkflowBudgetsTable;
362
+ tenants: TenantsTable;
363
+ }
364
+ type TenantStatus = "provisioning" | "active" | "suspended" | "error" | "deleted";
365
+ interface TenantsTable {
366
+ id: Generated<string>;
367
+ account_id: string;
368
+ slug: string;
369
+ status: ColumnType<TenantStatus, TenantStatus | undefined, TenantStatus>;
370
+ plan: string;
371
+ cpus: ColumnType<number, number | string, number | string>;
372
+ memory_mb: number;
373
+ storage_limit_mb: number;
374
+ storage_used_mb: number | null;
375
+ pg_container_id: string | null;
376
+ api_container_id: string | null;
377
+ processor_container_id: string | null;
378
+ target_database_url_enc: Buffer;
379
+ tenant_jwt_secret_enc: Buffer;
380
+ anon_key_enc: Buffer;
381
+ service_key_enc: Buffer;
382
+ api_url_internal: string;
383
+ api_url_public: string;
384
+ trial_ends_at: Date;
385
+ suspended_at: Date | null;
386
+ last_health_check_at: Date | null;
387
+ service_gen: Generated<number>;
388
+ anon_gen: Generated<number>;
389
+ project_id: string | null;
390
+ created_at: Generated<Date>;
391
+ updated_at: Generated<Date>;
363
392
  }
364
393
  interface WorkflowBudgetsTable {
365
394
  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,36 @@ interface Database {
360
359
  workflow_cursors: WorkflowCursorsTable;
361
360
  workflow_signer_secrets: WorkflowSignerSecretsTable;
362
361
  workflow_budgets: WorkflowBudgetsTable;
362
+ tenants: TenantsTable;
363
+ }
364
+ type TenantStatus = "provisioning" | "active" | "suspended" | "error" | "deleted";
365
+ interface TenantsTable {
366
+ id: Generated<string>;
367
+ account_id: string;
368
+ slug: string;
369
+ status: ColumnType<TenantStatus, TenantStatus | undefined, TenantStatus>;
370
+ plan: string;
371
+ cpus: ColumnType<number, number | string, number | string>;
372
+ memory_mb: number;
373
+ storage_limit_mb: number;
374
+ storage_used_mb: number | null;
375
+ pg_container_id: string | null;
376
+ api_container_id: string | null;
377
+ processor_container_id: string | null;
378
+ target_database_url_enc: Buffer;
379
+ tenant_jwt_secret_enc: Buffer;
380
+ anon_key_enc: Buffer;
381
+ service_key_enc: Buffer;
382
+ api_url_internal: string;
383
+ api_url_public: string;
384
+ trial_ends_at: Date;
385
+ suspended_at: Date | null;
386
+ last_health_check_at: Date | null;
387
+ service_gen: Generated<number>;
388
+ anon_gen: Generated<number>;
389
+ project_id: string | null;
390
+ created_at: Generated<Date>;
391
+ updated_at: Generated<Date>;
363
392
  }
364
393
  interface WorkflowBudgetsTable {
365
394
  id: Generated<string>;
@@ -19,7 +19,7 @@ import { sql } from "kysely";
19
19
  async function listPublicSubgraphs(db, opts = {}) {
20
20
  const limit = Math.min(Math.max(1, opts.limit ?? 50), 100);
21
21
  const offset = Math.max(0, opts.offset ?? 0);
22
- let query = db.selectFrom("subgraphs").innerJoin("api_keys", "api_keys.id", "subgraphs.api_key_id").innerJoin("accounts", "accounts.id", "api_keys.account_id").select([
22
+ let query = db.selectFrom("subgraphs").innerJoin("accounts", "accounts.id", "subgraphs.account_id").select([
23
23
  "subgraphs.id",
24
24
  "subgraphs.name",
25
25
  "subgraphs.description",
@@ -60,10 +60,7 @@ async function listPublicSubgraphs(db, opts = {}) {
60
60
  }
61
61
  if (opts.search) {
62
62
  const term = `%${opts.search}%`;
63
- countQuery = countQuery.where((eb) => eb.or([
64
- eb("name", "ilike", term),
65
- eb("description", "ilike", term)
66
- ]));
63
+ countQuery = countQuery.where((eb) => eb.or([eb("name", "ilike", term), eb("description", "ilike", term)]));
67
64
  }
68
65
  const [rows, countRow] = await Promise.all([
69
66
  query.limit(limit).offset(offset).execute(),
@@ -75,13 +72,13 @@ async function listPublicSubgraphs(db, opts = {}) {
75
72
  };
76
73
  }
77
74
  async function getPublicSubgraph(db, name) {
78
- return db.selectFrom("subgraphs").innerJoin("api_keys", "api_keys.id", "subgraphs.api_key_id").innerJoin("accounts", "accounts.id", "api_keys.account_id").selectAll("subgraphs").select(["accounts.display_name", "accounts.slug"]).where("subgraphs.name", "=", name).where("subgraphs.is_public", "=", true).executeTakeFirst();
75
+ return db.selectFrom("subgraphs").innerJoin("accounts", "accounts.id", "subgraphs.account_id").selectAll("subgraphs").select(["accounts.display_name", "accounts.slug"]).where("subgraphs.name", "=", name).where("subgraphs.is_public", "=", true).executeTakeFirst();
79
76
  }
80
77
  async function getCreatorProfile(db, slug) {
81
78
  const account = await db.selectFrom("accounts").select(["id", "display_name", "bio", "avatar_url", "slug"]).where("slug", "=", slug).executeTakeFirst();
82
79
  if (!account)
83
80
  return null;
84
- const subgraphs = await db.selectFrom("subgraphs").innerJoin("api_keys", "api_keys.id", "subgraphs.api_key_id").select([
81
+ const subgraphs = await db.selectFrom("subgraphs").select([
85
82
  "subgraphs.id",
86
83
  "subgraphs.name",
87
84
  "subgraphs.description",
@@ -94,7 +91,7 @@ async function getCreatorProfile(db, slug) {
94
91
  "subgraphs.total_processed",
95
92
  "subgraphs.created_at",
96
93
  sql`(SELECT COALESCE(SUM(query_count), 0) FROM subgraph_usage_daily WHERE subgraph_id = subgraphs.id AND date >= CURRENT_DATE - 7)::int`.as("queries_7d")
97
- ]).where("api_keys.account_id", "=", account.id).where("subgraphs.is_public", "=", true).orderBy("subgraphs.created_at", "desc").execute();
94
+ ]).where("subgraphs.account_id", "=", account.id).where("subgraphs.is_public", "=", true).orderBy("subgraphs.created_at", "desc").execute();
98
95
  return { account, subgraphs };
99
96
  }
100
97
  async function publishSubgraph(db, subgraphId, opts) {
@@ -138,5 +135,5 @@ export {
138
135
  getCreatorProfile
139
136
  };
140
137
 
141
- //# debugId=3D863C761F66BE2664756E2164756E21
138
+ //# debugId=C477C503DAA658BE64756E2164756E21
142
139
  //# sourceMappingURL=marketplace.js.map
@@ -2,9 +2,9 @@
2
2
  "version": 3,
3
3
  "sources": ["../src/db/queries/marketplace.ts"],
4
4
  "sourcesContent": [
5
- "import { type Kysely, sql } from \"kysely\";\nimport type { Database, Subgraph } from \"../types.ts\";\n\n/**\n * List public subgraphs with creator info and usage stats.\n */\nexport async function listPublicSubgraphs(\n\tdb: Kysely<Database>,\n\topts: {\n\t\tlimit?: number;\n\t\toffset?: number;\n\t\ttags?: string[];\n\t\tsearch?: string;\n\t\tsort?: \"recent\" | \"popular\" | \"name\";\n\t} = {},\n): Promise<{ data: any[]; meta: { total: number; limit: number; offset: number } }> {\n\tconst limit = Math.min(Math.max(1, opts.limit ?? 50), 100);\n\tconst offset = Math.max(0, opts.offset ?? 0);\n\n\tlet query = db\n\t\t.selectFrom(\"subgraphs\")\n\t\t.innerJoin(\"api_keys\", \"api_keys.id\", \"subgraphs.api_key_id\")\n\t\t.innerJoin(\"accounts\", \"accounts.id\", \"api_keys.account_id\")\n\t\t.select([\n\t\t\t\"subgraphs.id\",\n\t\t\t\"subgraphs.name\",\n\t\t\t\"subgraphs.description\",\n\t\t\t\"subgraphs.tags\",\n\t\t\t\"subgraphs.status\",\n\t\t\t\"subgraphs.version\",\n\t\t\t\"subgraphs.definition\",\n\t\t\t\"subgraphs.last_processed_block\",\n\t\t\t\"subgraphs.start_block\",\n\t\t\t\"subgraphs.total_processed\",\n\t\t\t\"subgraphs.created_at\",\n\t\t\t\"subgraphs.forked_from_id\",\n\t\t\t\"accounts.display_name\",\n\t\t\t\"accounts.slug\",\n\t\t\tsql<number>`(SELECT COALESCE(SUM(query_count), 0) FROM subgraph_usage_daily WHERE subgraph_id = subgraphs.id AND date >= CURRENT_DATE - 7)::int`.as(\"queries_7d\"),\n\t\t\tsql<number>`(SELECT COUNT(*) FROM subgraphs s2 WHERE s2.forked_from_id = subgraphs.id)::int`.as(\"fork_count\"),\n\t\t])\n\t\t.where(\"subgraphs.is_public\", \"=\", true);\n\n\t// Filter by tags (AND — all must match)\n\tif (opts.tags && opts.tags.length > 0) {\n\t\tquery = query.where(\n\t\t\tsql<boolean>`subgraphs.tags @> ${sql.val(opts.tags)}::text[]`,\n\t\t);\n\t}\n\n\t// Search by name or description\n\tif (opts.search) {\n\t\tconst term = `%${opts.search}%`;\n\t\tquery = query.where((eb) =>\n\t\t\teb.or([\n\t\t\t\teb(\"subgraphs.name\", \"ilike\", term),\n\t\t\t\teb(\"subgraphs.description\", \"ilike\", term),\n\t\t\t]),\n\t\t);\n\t}\n\n\t// Sort\n\tif (opts.sort === \"popular\") {\n\t\tquery = query.orderBy(sql`queries_7d`, \"desc\");\n\t} else if (opts.sort === \"name\") {\n\t\tquery = query.orderBy(\"subgraphs.name\", \"asc\");\n\t} else {\n\t\t// Default: recent\n\t\tquery = query.orderBy(\"subgraphs.created_at\", \"desc\");\n\t}\n\n\t// Count\n\tlet countQuery = db\n\t\t.selectFrom(\"subgraphs\")\n\t\t.select(sql<number>`count(*)::int`.as(\"count\"))\n\t\t.where(\"is_public\", \"=\", true);\n\n\tif (opts.tags && opts.tags.length > 0) {\n\t\tcountQuery = countQuery.where(\n\t\t\tsql<boolean>`tags @> ${sql.val(opts.tags)}::text[]`,\n\t\t);\n\t}\n\tif (opts.search) {\n\t\tconst term = `%${opts.search}%`;\n\t\tcountQuery = countQuery.where((eb) =>\n\t\t\teb.or([\n\t\t\t\teb(\"name\", \"ilike\", term),\n\t\t\t\teb(\"description\", \"ilike\", term),\n\t\t\t]),\n\t\t);\n\t}\n\n\tconst [rows, countRow] = await Promise.all([\n\t\tquery.limit(limit).offset(offset).execute(),\n\t\tcountQuery.executeTakeFirst(),\n\t]);\n\n\treturn {\n\t\tdata: rows,\n\t\tmeta: { total: countRow?.count ?? 0, limit, offset },\n\t};\n}\n\n/**\n * Get a single public subgraph by name with creator info.\n */\nexport async function getPublicSubgraph(db: Kysely<Database>, name: string): Promise<any | undefined> {\n\treturn db\n\t\t.selectFrom(\"subgraphs\")\n\t\t.innerJoin(\"api_keys\", \"api_keys.id\", \"subgraphs.api_key_id\")\n\t\t.innerJoin(\"accounts\", \"accounts.id\", \"api_keys.account_id\")\n\t\t.selectAll(\"subgraphs\")\n\t\t.select([\"accounts.display_name\", \"accounts.slug\"])\n\t\t.where(\"subgraphs.name\", \"=\", name)\n\t\t.where(\"subgraphs.is_public\", \"=\", true)\n\t\t.executeTakeFirst();\n}\n\n/**\n * Get a creator profile by slug with their public subgraphs.\n */\nexport async function getCreatorProfile(db: Kysely<Database>, slug: string): Promise<{ account: any; subgraphs: any[] } | null> {\n\tconst account = await db\n\t\t.selectFrom(\"accounts\")\n\t\t.select([\"id\", \"display_name\", \"bio\", \"avatar_url\", \"slug\"])\n\t\t.where(\"slug\", \"=\", slug)\n\t\t.executeTakeFirst();\n\n\tif (!account) return null;\n\n\tconst subgraphs = await db\n\t\t.selectFrom(\"subgraphs\")\n\t\t.innerJoin(\"api_keys\", \"api_keys.id\", \"subgraphs.api_key_id\")\n\t\t.select([\n\t\t\t\"subgraphs.id\",\n\t\t\t\"subgraphs.name\",\n\t\t\t\"subgraphs.description\",\n\t\t\t\"subgraphs.tags\",\n\t\t\t\"subgraphs.status\",\n\t\t\t\"subgraphs.version\",\n\t\t\t\"subgraphs.definition\",\n\t\t\t\"subgraphs.last_processed_block\",\n\t\t\t\"subgraphs.start_block\",\n\t\t\t\"subgraphs.total_processed\",\n\t\t\t\"subgraphs.created_at\",\n\t\t\tsql<number>`(SELECT COALESCE(SUM(query_count), 0) FROM subgraph_usage_daily WHERE subgraph_id = subgraphs.id AND date >= CURRENT_DATE - 7)::int`.as(\"queries_7d\"),\n\t\t])\n\t\t.where(\"api_keys.account_id\", \"=\", account.id)\n\t\t.where(\"subgraphs.is_public\", \"=\", true)\n\t\t.orderBy(\"subgraphs.created_at\", \"desc\")\n\t\t.execute();\n\n\treturn { account, subgraphs };\n}\n\n/**\n * Publish a subgraph (set is_public = true).\n */\nexport async function publishSubgraph(\n\tdb: Kysely<Database>,\n\tsubgraphId: string,\n\topts?: { tags?: string[]; description?: string },\n): Promise<Subgraph> {\n\tconst set: Record<string, unknown> = {\n\t\tis_public: true,\n\t\tupdated_at: new Date(),\n\t};\n\tif (opts?.tags) set.tags = sql`${sql.val(opts.tags)}::text[]`;\n\tif (opts?.description !== undefined) set.description = opts.description;\n\n\treturn db\n\t\t.updateTable(\"subgraphs\")\n\t\t.set(set)\n\t\t.where(\"id\", \"=\", subgraphId)\n\t\t.returningAll()\n\t\t.executeTakeFirstOrThrow();\n}\n\n/**\n * Unpublish a subgraph (set is_public = false).\n */\nexport async function unpublishSubgraph(\n\tdb: Kysely<Database>,\n\tsubgraphId: string,\n): Promise<Subgraph> {\n\treturn db\n\t\t.updateTable(\"subgraphs\")\n\t\t.set({ is_public: false, updated_at: new Date() })\n\t\t.where(\"id\", \"=\", subgraphId)\n\t\t.returningAll()\n\t\t.executeTakeFirstOrThrow();\n}\n\n/**\n * Increment per-subgraph query count for today. Fire-and-forget safe.\n */\nexport async function incrementSubgraphQueryCount(\n\tdb: Kysely<Database>,\n\tsubgraphId: string,\n): Promise<void> {\n\tconst today = new Date().toISOString().slice(0, 10);\n\tawait sql`\n\t\tINSERT INTO subgraph_usage_daily (subgraph_id, date, query_count)\n\t\tVALUES (${subgraphId}, ${today}, 1)\n\t\tON CONFLICT (subgraph_id, date)\n\t\tDO UPDATE SET query_count = subgraph_usage_daily.query_count + 1\n\t`.execute(db);\n}\n\n/**\n * Get daily usage history for a subgraph.\n */\nexport async function getSubgraphUsageHistory(\n\tdb: Kysely<Database>,\n\tsubgraphId: string,\n\tdays: number,\n): Promise<Array<{ date: string; query_count: number }>> {\n\treturn db\n\t\t.selectFrom(\"subgraph_usage_daily\")\n\t\t.select([\"date\", \"query_count\"])\n\t\t.where(\"subgraph_id\", \"=\", subgraphId)\n\t\t.where(\"date\", \">=\", sql<string>`CURRENT_DATE - ${days}::int`)\n\t\t.orderBy(\"date\", \"asc\")\n\t\t.execute();\n}\n\n/**\n * Get total query count for a subgraph over last N days.\n */\nexport async function getSubgraphQueryTotal(\n\tdb: Kysely<Database>,\n\tsubgraphId: string,\n\tdays: number,\n): Promise<number> {\n\tconst row = await db\n\t\t.selectFrom(\"subgraph_usage_daily\")\n\t\t.select(sql<number>`COALESCE(SUM(query_count), 0)::int`.as(\"total\"))\n\t\t.where(\"subgraph_id\", \"=\", subgraphId)\n\t\t.where(\"date\", \">=\", sql<string>`CURRENT_DATE - ${days}::int`)\n\t\t.executeTakeFirst();\n\treturn row?.total ?? 0;\n}\n"
5
+ "import { type Kysely, sql } from \"kysely\";\nimport type { Database, Subgraph } from \"../types.ts\";\n\n/**\n * List public subgraphs with creator info and usage stats.\n */\nexport async function listPublicSubgraphs(\n\tdb: Kysely<Database>,\n\topts: {\n\t\tlimit?: number;\n\t\toffset?: number;\n\t\ttags?: string[];\n\t\tsearch?: string;\n\t\tsort?: \"recent\" | \"popular\" | \"name\";\n\t} = {},\n): Promise<{\n\tdata: any[];\n\tmeta: { total: number; limit: number; offset: number };\n}> {\n\tconst limit = Math.min(Math.max(1, opts.limit ?? 50), 100);\n\tconst offset = Math.max(0, opts.offset ?? 0);\n\n\tlet query = db\n\t\t.selectFrom(\"subgraphs\")\n\t\t.innerJoin(\"accounts\", \"accounts.id\", \"subgraphs.account_id\")\n\t\t.select([\n\t\t\t\"subgraphs.id\",\n\t\t\t\"subgraphs.name\",\n\t\t\t\"subgraphs.description\",\n\t\t\t\"subgraphs.tags\",\n\t\t\t\"subgraphs.status\",\n\t\t\t\"subgraphs.version\",\n\t\t\t\"subgraphs.definition\",\n\t\t\t\"subgraphs.last_processed_block\",\n\t\t\t\"subgraphs.start_block\",\n\t\t\t\"subgraphs.total_processed\",\n\t\t\t\"subgraphs.created_at\",\n\t\t\t\"subgraphs.forked_from_id\",\n\t\t\t\"accounts.display_name\",\n\t\t\t\"accounts.slug\",\n\t\t\tsql<number>`(SELECT COALESCE(SUM(query_count), 0) FROM subgraph_usage_daily WHERE subgraph_id = subgraphs.id AND date >= CURRENT_DATE - 7)::int`.as(\n\t\t\t\t\"queries_7d\",\n\t\t\t),\n\t\t\tsql<number>`(SELECT COUNT(*) FROM subgraphs s2 WHERE s2.forked_from_id = subgraphs.id)::int`.as(\n\t\t\t\t\"fork_count\",\n\t\t\t),\n\t\t])\n\t\t.where(\"subgraphs.is_public\", \"=\", true);\n\n\t// Filter by tags (AND — all must match)\n\tif (opts.tags && opts.tags.length > 0) {\n\t\tquery = query.where(\n\t\t\tsql<boolean>`subgraphs.tags @> ${sql.val(opts.tags)}::text[]`,\n\t\t);\n\t}\n\n\t// Search by name or description\n\tif (opts.search) {\n\t\tconst term = `%${opts.search}%`;\n\t\tquery = query.where((eb) =>\n\t\t\teb.or([\n\t\t\t\teb(\"subgraphs.name\", \"ilike\", term),\n\t\t\t\teb(\"subgraphs.description\", \"ilike\", term),\n\t\t\t]),\n\t\t);\n\t}\n\n\t// Sort\n\tif (opts.sort === \"popular\") {\n\t\tquery = query.orderBy(sql`queries_7d`, \"desc\");\n\t} else if (opts.sort === \"name\") {\n\t\tquery = query.orderBy(\"subgraphs.name\", \"asc\");\n\t} else {\n\t\t// Default: recent\n\t\tquery = query.orderBy(\"subgraphs.created_at\", \"desc\");\n\t}\n\n\t// Count\n\tlet countQuery = db\n\t\t.selectFrom(\"subgraphs\")\n\t\t.select(sql<number>`count(*)::int`.as(\"count\"))\n\t\t.where(\"is_public\", \"=\", true);\n\n\tif (opts.tags && opts.tags.length > 0) {\n\t\tcountQuery = countQuery.where(\n\t\t\tsql<boolean>`tags @> ${sql.val(opts.tags)}::text[]`,\n\t\t);\n\t}\n\tif (opts.search) {\n\t\tconst term = `%${opts.search}%`;\n\t\tcountQuery = countQuery.where((eb) =>\n\t\t\teb.or([eb(\"name\", \"ilike\", term), eb(\"description\", \"ilike\", term)]),\n\t\t);\n\t}\n\n\tconst [rows, countRow] = await Promise.all([\n\t\tquery.limit(limit).offset(offset).execute(),\n\t\tcountQuery.executeTakeFirst(),\n\t]);\n\n\treturn {\n\t\tdata: rows,\n\t\tmeta: { total: countRow?.count ?? 0, limit, offset },\n\t};\n}\n\n/**\n * Get a single public subgraph by name with creator info.\n */\nexport async function getPublicSubgraph(\n\tdb: Kysely<Database>,\n\tname: string,\n): Promise<any | undefined> {\n\treturn db\n\t\t.selectFrom(\"subgraphs\")\n\t\t.innerJoin(\"accounts\", \"accounts.id\", \"subgraphs.account_id\")\n\t\t.selectAll(\"subgraphs\")\n\t\t.select([\"accounts.display_name\", \"accounts.slug\"])\n\t\t.where(\"subgraphs.name\", \"=\", name)\n\t\t.where(\"subgraphs.is_public\", \"=\", true)\n\t\t.executeTakeFirst();\n}\n\n/**\n * Get a creator profile by slug with their public subgraphs.\n */\nexport async function getCreatorProfile(\n\tdb: Kysely<Database>,\n\tslug: string,\n): Promise<{ account: any; subgraphs: any[] } | null> {\n\tconst account = await db\n\t\t.selectFrom(\"accounts\")\n\t\t.select([\"id\", \"display_name\", \"bio\", \"avatar_url\", \"slug\"])\n\t\t.where(\"slug\", \"=\", slug)\n\t\t.executeTakeFirst();\n\n\tif (!account) return null;\n\n\tconst subgraphs = await db\n\t\t.selectFrom(\"subgraphs\")\n\t\t.select([\n\t\t\t\"subgraphs.id\",\n\t\t\t\"subgraphs.name\",\n\t\t\t\"subgraphs.description\",\n\t\t\t\"subgraphs.tags\",\n\t\t\t\"subgraphs.status\",\n\t\t\t\"subgraphs.version\",\n\t\t\t\"subgraphs.definition\",\n\t\t\t\"subgraphs.last_processed_block\",\n\t\t\t\"subgraphs.start_block\",\n\t\t\t\"subgraphs.total_processed\",\n\t\t\t\"subgraphs.created_at\",\n\t\t\tsql<number>`(SELECT COALESCE(SUM(query_count), 0) FROM subgraph_usage_daily WHERE subgraph_id = subgraphs.id AND date >= CURRENT_DATE - 7)::int`.as(\n\t\t\t\t\"queries_7d\",\n\t\t\t),\n\t\t])\n\t\t.where(\"subgraphs.account_id\", \"=\", account.id)\n\t\t.where(\"subgraphs.is_public\", \"=\", true)\n\t\t.orderBy(\"subgraphs.created_at\", \"desc\")\n\t\t.execute();\n\n\treturn { account, subgraphs };\n}\n\n/**\n * Publish a subgraph (set is_public = true).\n */\nexport async function publishSubgraph(\n\tdb: Kysely<Database>,\n\tsubgraphId: string,\n\topts?: { tags?: string[]; description?: string },\n): Promise<Subgraph> {\n\tconst set: Record<string, unknown> = {\n\t\tis_public: true,\n\t\tupdated_at: new Date(),\n\t};\n\tif (opts?.tags) set.tags = sql`${sql.val(opts.tags)}::text[]`;\n\tif (opts?.description !== undefined) set.description = opts.description;\n\n\treturn db\n\t\t.updateTable(\"subgraphs\")\n\t\t.set(set)\n\t\t.where(\"id\", \"=\", subgraphId)\n\t\t.returningAll()\n\t\t.executeTakeFirstOrThrow();\n}\n\n/**\n * Unpublish a subgraph (set is_public = false).\n */\nexport async function unpublishSubgraph(\n\tdb: Kysely<Database>,\n\tsubgraphId: string,\n): Promise<Subgraph> {\n\treturn db\n\t\t.updateTable(\"subgraphs\")\n\t\t.set({ is_public: false, updated_at: new Date() })\n\t\t.where(\"id\", \"=\", subgraphId)\n\t\t.returningAll()\n\t\t.executeTakeFirstOrThrow();\n}\n\n/**\n * Increment per-subgraph query count for today. Fire-and-forget safe.\n */\nexport async function incrementSubgraphQueryCount(\n\tdb: Kysely<Database>,\n\tsubgraphId: string,\n): Promise<void> {\n\tconst today = new Date().toISOString().slice(0, 10);\n\tawait sql`\n\t\tINSERT INTO subgraph_usage_daily (subgraph_id, date, query_count)\n\t\tVALUES (${subgraphId}, ${today}, 1)\n\t\tON CONFLICT (subgraph_id, date)\n\t\tDO UPDATE SET query_count = subgraph_usage_daily.query_count + 1\n\t`.execute(db);\n}\n\n/**\n * Get daily usage history for a subgraph.\n */\nexport async function getSubgraphUsageHistory(\n\tdb: Kysely<Database>,\n\tsubgraphId: string,\n\tdays: number,\n): Promise<Array<{ date: string; query_count: number }>> {\n\treturn db\n\t\t.selectFrom(\"subgraph_usage_daily\")\n\t\t.select([\"date\", \"query_count\"])\n\t\t.where(\"subgraph_id\", \"=\", subgraphId)\n\t\t.where(\"date\", \">=\", sql<string>`CURRENT_DATE - ${days}::int`)\n\t\t.orderBy(\"date\", \"asc\")\n\t\t.execute();\n}\n\n/**\n * Get total query count for a subgraph over last N days.\n */\nexport async function getSubgraphQueryTotal(\n\tdb: Kysely<Database>,\n\tsubgraphId: string,\n\tdays: number,\n): Promise<number> {\n\tconst row = await db\n\t\t.selectFrom(\"subgraph_usage_daily\")\n\t\t.select(sql<number>`COALESCE(SUM(query_count), 0)::int`.as(\"total\"))\n\t\t.where(\"subgraph_id\", \"=\", subgraphId)\n\t\t.where(\"date\", \">=\", sql<string>`CURRENT_DATE - ${days}::int`)\n\t\t.executeTakeFirst();\n\treturn row?.total ?? 0;\n}\n"
6
6
  ],
7
- "mappings": "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",
8
- "debugId": "3D863C761F66BE2664756E2164756E21",
7
+ "mappings": "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",
8
+ "debugId": "C477C503DAA658BE64756E2164756E21",
9
9
  "names": []
10
10
  }
@@ -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,36 @@ interface Database {
360
359
  workflow_cursors: WorkflowCursorsTable;
361
360
  workflow_signer_secrets: WorkflowSignerSecretsTable;
362
361
  workflow_budgets: WorkflowBudgetsTable;
362
+ tenants: TenantsTable;
363
+ }
364
+ type TenantStatus = "provisioning" | "active" | "suspended" | "error" | "deleted";
365
+ interface TenantsTable {
366
+ id: Generated<string>;
367
+ account_id: string;
368
+ slug: string;
369
+ status: ColumnType<TenantStatus, TenantStatus | undefined, TenantStatus>;
370
+ plan: string;
371
+ cpus: ColumnType<number, number | string, number | string>;
372
+ memory_mb: number;
373
+ storage_limit_mb: number;
374
+ storage_used_mb: number | null;
375
+ pg_container_id: string | null;
376
+ api_container_id: string | null;
377
+ processor_container_id: string | null;
378
+ target_database_url_enc: Buffer;
379
+ tenant_jwt_secret_enc: Buffer;
380
+ anon_key_enc: Buffer;
381
+ service_key_enc: Buffer;
382
+ api_url_internal: string;
383
+ api_url_public: string;
384
+ trial_ends_at: Date;
385
+ suspended_at: Date | null;
386
+ last_health_check_at: Date | null;
387
+ service_gen: Generated<number>;
388
+ anon_gen: Generated<number>;
389
+ project_id: string | null;
390
+ created_at: Generated<Date>;
391
+ updated_at: Generated<Date>;
363
392
  }
364
393
  interface WorkflowBudgetsTable {
365
394
  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
  }