1import * as Effect from "effect/Effect";
2import * as FileSystem from "effect/FileSystem";
3import * as Path from "effect/Path";
4import { DrizzleV0LayoutError } from "./Format.ts";
5import { DRIZZLE_DIR_PATTERN } from "./Utils.ts";
6
7/**
8 * The on-disk layout of a migrations directory. `directory` covers both
9 * drizzle-kit v1 and Prisma layouts (`<ts>_<name>/migration.sql` — records
10 * key by directory name); `flat` is plain `.sql` files (records key by file
11 * path).
12 */
13export type MigrationLayout = "directory" | "flat";
14
15/**
16 * Fingerprint a migrations directory's layout to pick the record reader.
17 *
18 * A drizzle **v0** layout (`meta/_journal.json`) fails with a typed error:
19 * the fix (`drizzle-kit up`) is upstream of Alchemy.
20 */
21export const detectLayout = (dir: string) =>
22 Effect.gen(function* () {
23 const fs = yield* FileSystem.FileSystem;
24 const path = yield* Path.Path;
25 const exists = (p: string) =>
26 fs.exists(p).pipe(Effect.catch(() => Effect.succeed(false)));
27
28 if (yield* exists(path.join(dir, "meta", "_journal.json"))) {
29 return yield* new DrizzleV0LayoutError({
30 dir,
31 message:
32 `${dir} uses drizzle-kit's pre-v1 migration layout (meta/_journal.json). ` +
33 `Upgrade drizzle-kit and run "drizzle-kit up" to convert it, then redeploy.`,
34 });
35 }
36
37 const entries = yield* fs
38 .readDirectory(dir)
39 .pipe(Effect.catch(() => Effect.succeed([] as string[])));
40 for (const entry of entries) {
41 if (!DRIZZLE_DIR_PATTERN.test(entry)) continue;
42 if (yield* exists(path.join(dir, entry, "migration.sql"))) {
43 return "directory" as const;
44 }
45 }
46 return "flat" as const;
47 });