{"version":3,"file":"db-studio.cjs","names":["getMigrationStorage","pluginMigrationSlug"],"sources":["../../src/cli/db-studio.ts"],"sourcesContent":["import { spawn } from \"node:child_process\";\nimport { existsSync, mkdirSync, writeFileSync } from \"node:fs\";\nimport { join, resolve } from \"node:path\";\nimport * as p from \"@clack/prompts\";\nimport { getMigrationStorage, pluginMigrationSlug } from \"../db\";\nimport type { RuntimeConfig } from \"../types\";\n\nexport interface PluginDbInfo {\n  key: string;\n  source: \"local\" | \"remote\";\n  section: \"app.api\" | \"app.auth\" | \"plugins\";\n  databaseSecret: string;\n  databaseUrl: string;\n  workspaceDir?: string;\n  projectDir: string;\n}\n\nexport function resolvePluginDbInfo(\n  pluginKey: string,\n  runtimeConfig: RuntimeConfig,\n  projectDir: string,\n): PluginDbInfo {\n  let source: \"local\" | \"remote\" | undefined;\n  let section: PluginDbInfo[\"section\"];\n  let secrets: string[] | undefined;\n  let localPath: string | undefined;\n  let key: string;\n\n  if (pluginKey === \"api\" && runtimeConfig.api) {\n    source = runtimeConfig.api.source;\n    section = \"app.api\";\n    secrets = runtimeConfig.api.secrets;\n    localPath = runtimeConfig.api.localPath;\n    key = \"api\";\n  } else if (pluginKey === \"auth\" && runtimeConfig.auth) {\n    source = runtimeConfig.auth.source;\n    section = \"app.auth\";\n    secrets = runtimeConfig.auth.secrets;\n    localPath = runtimeConfig.auth.localPath;\n    key = \"auth\";\n  } else if (runtimeConfig.plugins?.[pluginKey]) {\n    const plugin = runtimeConfig.plugins[pluginKey];\n    source = plugin.source;\n    section = \"plugins\";\n    secrets = plugin.secrets;\n    localPath = plugin.localPath;\n    key = pluginKey;\n  } else {\n    throw new Error(\n      `Plugin \"${pluginKey}\" not found in app.api, app.auth, or plugins. ` +\n        `Available: ${[\n          \"api\",\n          ...(runtimeConfig.auth ? [\"auth\"] : []),\n          ...Object.keys(runtimeConfig.plugins ?? {}),\n        ].join(\", \")}`,\n    );\n  }\n\n  const dbSecret = secrets?.find((s) => s.endsWith(\"_DATABASE_URL\"));\n  if (!dbSecret) {\n    throw new Error(\n      `Plugin \"${pluginKey}\" has no database secret (no secret ending in _DATABASE_URL). ` +\n        `Secrets: ${(secrets ?? []).join(\", \") || \"none\"}`,\n    );\n  }\n\n  const dbUrl = process.env[dbSecret];\n  if (!dbUrl) {\n    throw new Error(\n      `.env missing ${dbSecret} for plugin \"${pluginKey}\". ` +\n        `Add it to your .env file (see .env.example).`,\n    );\n  }\n\n  return {\n    key,\n    source: source ?? \"remote\",\n    section,\n    databaseSecret: dbSecret,\n    databaseUrl: dbUrl,\n    workspaceDir: localPath,\n    projectDir,\n  };\n}\n\nexport async function runStudioLocal(info: PluginDbInfo): Promise<void> {\n  const workspaceRoot = info.workspaceDir\n    ? resolve(info.projectDir, info.workspaceDir)\n    : resolve(info.projectDir, info.key);\n  const configPath = join(workspaceRoot, \"drizzle.config.ts\");\n\n  if (!existsSync(configPath)) {\n    throw new Error(\n      `No drizzle.config.ts found in ${workspaceRoot}. ` +\n        `Run 'drizzle-kit init' first in the plugin workspace.`,\n    );\n  }\n\n  p.log.info(`Starting Drizzle Studio for ${info.key} (local)...`);\n\n  await spawnAsync(\"npx\", [\"drizzle-kit\", \"studio\", \"--config\", configPath], {\n    cwd: workspaceRoot,\n  });\n}\n\nexport async function runStudioRemote(info: PluginDbInfo): Promise<void> {\n  const dbDir = resolve(info.projectDir, \".bos\", \"db\", info.key);\n\n  mkdirSync(dbDir, { recursive: true });\n\n  const storage = getMigrationStorage(pluginMigrationSlug(info.key));\n  const journalSchema = storage.schema;\n  const journalTable = storage.table;\n\n  const configPath = join(dbDir, \"drizzle.config.ts\");\n  const configContent = `import { defineConfig } from \"drizzle-kit\";\n\nexport default defineConfig({\n  schema: \"./drizzle/schema.ts\",\n  out: \"./drizzle\",\n  dialect: \"postgresql\",\n  dbCredentials: {\n    url: \"${info.databaseUrl}\",\n  },\n  migrations: {\n    schema: \"${journalSchema}\",\n    table: \"${journalTable}\",\n  },\n  verbose: true,\n  strict: true,\n});\n`;\n\n  writeFileSync(configPath, configContent);\n\n  p.log.info(`Introspecting database schema for ${info.key}...`);\n  try {\n    await spawnAsync(\"npx\", [\"drizzle-kit\", \"pull\", \"--config\", configPath], {\n      cwd: dbDir,\n    });\n  } catch {\n    throw new Error(\n      `Failed to introspect database for \"${info.key}\". ` +\n        `Check that ${info.databaseSecret} is correct and the database is reachable.`,\n    );\n  }\n\n  p.log.info(`Starting Drizzle Studio for ${info.key}...`);\n  await spawnAsync(\"npx\", [\"drizzle-kit\", \"studio\", \"--config\", configPath], {\n    cwd: dbDir,\n  });\n}\n\nfunction spawnAsync(cmd: string, args: string[], options: { cwd: string }): Promise<void> {\n  return new Promise((resolvePromise, reject) => {\n    const child = spawn(cmd, args, {\n      cwd: options.cwd,\n      stdio: \"inherit\",\n      shell: true,\n    });\n\n    child.on(\"error\", (err) => {\n      if ((err as NodeJS.ErrnoException).code === \"ENOENT\") {\n        reject(\n          new Error(\n            `\"${cmd}\" not found. Ensure it is installed (e.g. 'npm install -g drizzle-kit').`,\n          ),\n        );\n      } else {\n        reject(new Error(`Failed to start ${cmd}: ${err.message}`));\n      }\n    });\n\n    child.on(\"exit\", (code) => {\n      if (code === 0 || code === null) {\n        resolvePromise();\n      } else {\n        reject(new Error(`\"${cmd}\" exited with code ${code}`));\n      }\n    });\n  });\n}\n"],"mappings":"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"}