{"version":3,"file":"drizzle-kit.mjs","names":[],"sources":["../../src/db/drizzle-kit.ts"],"sourcesContent":["import { spawn } from \"node:child_process\";\nimport { existsSync, mkdirSync, writeFileSync } from \"node:fs\";\nimport { join, resolve } from \"node:path\";\nimport { Context, Data, Effect, Layer } from \"effect\";\nimport { bindingEnv } from \"./binding\";\nimport type { DatabaseBinding } from \"./bindings\";\n\nexport class DrizzleKitError extends Data.TaggedError(\"DrizzleKitError\")<{\n  readonly command: string;\n  readonly message: string;\n  readonly exitCode: number | undefined;\n}> {}\n\nexport interface DrizzleKitService {\n  readonly studio: (binding: DatabaseBinding) => Effect.Effect<void, DrizzleKitError>;\n  readonly migrate: (binding: DatabaseBinding) => Effect.Effect<void, DrizzleKitError>;\n}\n\nexport class DrizzleKit extends Context.Service<DrizzleKit, DrizzleKitService>()(\n  \"everything-dev/DrizzleKit\",\n) {}\n\ninterface SpawnSpec {\n  readonly args: readonly string[];\n  readonly cwd: string;\n  readonly env: Record<string, string | undefined>;\n  readonly command: string;\n  readonly stdio: \"inherit\" | \"pipe\";\n}\n\n/**\n * The single choke point for invoking drizzle-kit. Every child receives the\n * composition root's materialized environment (`bindingEnv`) so the config it\n * evaluates never has to re-derive credentials from ambient state.\n */\nfunction spawnDrizzleKit(spec: SpawnSpec): Effect.Effect<void, DrizzleKitError> {\n  return Effect.callback((resume) => {\n    let stderr = \"\";\n    let settled = false;\n    const settle = (effect: Effect.Effect<void, DrizzleKitError>) => {\n      if (settled) return;\n      settled = true;\n      resume(effect);\n    };\n\n    const child = spawn(process.platform === \"win32\" ? \"npx.cmd\" : \"npx\", [...spec.args], {\n      cwd: spec.cwd,\n      env: spec.env,\n      stdio: spec.stdio,\n    });\n\n    if (spec.stdio === \"pipe\") {\n      child.stderr?.on(\"data\", (data: Buffer) => {\n        stderr += data.toString();\n      });\n    }\n\n    child.on(\"error\", (err) => {\n      const hint =\n        (err as NodeJS.ErrnoException).code === \"ENOENT\"\n          ? '\"npx\" not found. Ensure Node.js is installed.'\n          : err.message;\n      settle(\n        Effect.fail(\n          new DrizzleKitError({ command: spec.command, message: hint, exitCode: undefined }),\n        ),\n      );\n    });\n\n    child.on(\"exit\", (code) => {\n      if (code === 0 || code === null) {\n        settle(Effect.void);\n        return;\n      }\n      settle(\n        Effect.fail(\n          new DrizzleKitError({\n            command: spec.command,\n            message: `drizzle-kit ${spec.command} exited with code ${code}${\n              stderr.trim() ? `: ${stderr.trim()}` : \"\"\n            }`,\n            exitCode: code,\n          }),\n        ),\n      );\n    });\n\n    return Effect.sync(() => {\n      if (child.exitCode === null && !child.killed) child.kill();\n    });\n  });\n}\n\nfunction childEnv(binding: DatabaseBinding): Record<string, string | undefined> {\n  return {\n    ...process.env,\n    ...bindingEnv({ secretName: binding.identity.secretName, url: binding.url }),\n  };\n}\n\nfunction workspaceConfigPath(binding: DatabaseBinding): string | undefined {\n  const workspaceDir = binding.identity.workspaceDir;\n  if (!workspaceDir) return undefined;\n  const configPath = join(workspaceDir, \"drizzle.config.ts\");\n  return existsSync(configPath) ? configPath : undefined;\n}\n\nfunction writeGeneratedConfig(binding: DatabaseBinding, projectDir: string): string {\n  const dbDir = resolve(projectDir, \".bos\", \"db\", binding.key);\n  mkdirSync(dbDir, { recursive: true });\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: \"${binding.url}\",\n  },\n  migrations: {\n    schema: \"${binding.identity.journal.schema}\",\n    table: \"${binding.identity.journal.table}\",\n  },\n  verbose: true,\n  strict: true,\n});\n`;\n\n  writeFileSync(configPath, configContent);\n  return dbDir;\n}\n\nexport interface DrizzleKitOptions {\n  readonly projectDir: string;\n  readonly onLog?: (message: string) => void;\n}\n\nexport const makeDrizzleKitLive = (options: DrizzleKitOptions): Layer.Layer<DrizzleKit> =>\n  Layer.succeed(DrizzleKit, {\n    studio: (binding) =>\n      Effect.gen(function* () {\n        const localConfig = workspaceConfigPath(binding);\n\n        if (localConfig && binding.identity.workspaceDir) {\n          options.onLog?.(`Starting Drizzle Studio for ${binding.key} (local)...`);\n          yield* spawnDrizzleKit({\n            args: [\"drizzle-kit\", \"studio\", \"--config\", localConfig],\n            cwd: binding.identity.workspaceDir,\n            env: childEnv(binding),\n            command: \"studio\",\n            stdio: \"inherit\",\n          });\n          return;\n        }\n\n        if (binding.source === \"local\") {\n          return yield* new DrizzleKitError({\n            command: \"studio\",\n            message:\n              `No drizzle.config.ts found in ${binding.identity.workspaceDir ?? binding.key}. ` +\n              `Run 'drizzle-kit init' first in the plugin workspace.`,\n            exitCode: undefined,\n          });\n        }\n\n        options.onLog?.(`Introspecting database schema for ${binding.key}...`);\n        const dbDir = writeGeneratedConfig(binding, options.projectDir);\n        yield* spawnDrizzleKit({\n          args: [\"drizzle-kit\", \"pull\", \"--config\", join(dbDir, \"drizzle.config.ts\")],\n          cwd: dbDir,\n          env: childEnv(binding),\n          command: \"pull\",\n          stdio: \"inherit\",\n        }).pipe(\n          Effect.mapError(\n            (error) =>\n              new DrizzleKitError({\n                command: \"pull\",\n                exitCode: error.exitCode,\n                message:\n                  `Failed to introspect database for \"${binding.key}\". ` +\n                  `Check that ${binding.identity.secretName} is correct and the database is reachable. ` +\n                  error.message,\n              }),\n          ),\n        );\n\n        options.onLog?.(`Starting Drizzle Studio for ${binding.key}...`);\n        yield* spawnDrizzleKit({\n          args: [\"drizzle-kit\", \"studio\", \"--config\", join(dbDir, \"drizzle.config.ts\")],\n          cwd: dbDir,\n          env: childEnv(binding),\n          command: \"studio\",\n          stdio: \"inherit\",\n        });\n      }),\n\n    migrate: (binding) => {\n      const configPath = workspaceConfigPath(binding);\n      if (!configPath || !binding.identity.workspaceDir) {\n        return Effect.fail(\n          new DrizzleKitError({\n            command: \"migrate\",\n            message: `No drizzle.config.ts found in ${binding.identity.workspaceDir ?? binding.key}.`,\n            exitCode: undefined,\n          }),\n        );\n      }\n      return spawnDrizzleKit({\n        args: [\"drizzle-kit\", \"migrate\", \"--config\", configPath],\n        cwd: binding.identity.workspaceDir,\n        env: childEnv(binding),\n        command: \"migrate\",\n        stdio: \"pipe\",\n      });\n    },\n  });\n"],"mappings":"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"}