@prisma-next/migration-tools 0.4.0-dev.6 → 0.4.0-dev.8

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 (34) hide show
  1. package/dist/attestation-DnebS4XZ.mjs +64 -0
  2. package/dist/attestation-DnebS4XZ.mjs.map +1 -0
  3. package/dist/{constants-DARNL_LD.mjs → constants-BRi0X7B_.mjs} +1 -1
  4. package/dist/{constants-DARNL_LD.mjs.map → constants-BRi0X7B_.mjs.map} +1 -1
  5. package/dist/exports/attestation.d.mts +1 -1
  6. package/dist/exports/attestation.mjs +3 -63
  7. package/dist/exports/constants.mjs +1 -1
  8. package/dist/exports/dag.d.mts +1 -1
  9. package/dist/exports/dag.mjs +1 -1
  10. package/dist/exports/io.d.mts +14 -2
  11. package/dist/exports/io.d.mts.map +1 -1
  12. package/dist/exports/io.mjs +2 -2
  13. package/dist/exports/migration-ts.d.mts +27 -18
  14. package/dist/exports/migration-ts.d.mts.map +1 -1
  15. package/dist/exports/migration-ts.mjs +39 -85
  16. package/dist/exports/migration-ts.mjs.map +1 -1
  17. package/dist/exports/migration.d.mts +35 -6
  18. package/dist/exports/migration.d.mts.map +1 -1
  19. package/dist/exports/migration.mjs +83 -29
  20. package/dist/exports/migration.mjs.map +1 -1
  21. package/dist/exports/types.d.mts +1 -1
  22. package/dist/{io-BO18-Evu.mjs → io-Cun81AIZ.mjs} +24 -3
  23. package/dist/io-Cun81AIZ.mjs.map +1 -0
  24. package/dist/{types-DXjq7Fum.d.mts → types-D2uX4ql7.d.mts} +1 -1
  25. package/dist/{types-DXjq7Fum.d.mts.map → types-D2uX4ql7.d.mts.map} +1 -1
  26. package/package.json +4 -4
  27. package/src/exports/io.ts +1 -0
  28. package/src/exports/migration-ts.ts +3 -6
  29. package/src/io.ts +26 -1
  30. package/src/migration-base.ts +123 -35
  31. package/src/migration-ts.ts +27 -144
  32. package/src/runtime-detection.ts +18 -0
  33. package/dist/exports/attestation.mjs.map +0 -1
  34. package/dist/io-BO18-Evu.mjs.map +0 -1
@@ -1,153 +1,33 @@
1
1
  /**
2
- * Utilities for scaffolding and evaluating migration.ts files.
2
+ * Utilities for reading/writing `migration.ts` files.
3
3
  *
4
- * - scaffoldMigrationTs: writes a migration.ts file with boilerplate
5
- * - evaluateMigrationTs: loads migration.ts via native Node import, returns descriptors
6
- *
7
- * Shared by migration plan (scaffold), migration new (scaffold), and
8
- * migration emit (evaluate).
4
+ * Rendering migration.ts source is now the target's responsibility — the CLI
5
+ * obtains source strings either from a class-flow planner's
6
+ * `plan.renderTypeScript()` or from a descriptor-flow target's
7
+ * `migrations.renderDescriptorTypeScript(descriptors, context)`. The helper
8
+ * here is limited to file I/O: writing the returned source with the right
9
+ * executable bit, probing for existence, and evaluating legacy descriptor-
10
+ * flow files.
9
11
  */
10
12
 
11
13
  import { stat, writeFile } from 'node:fs/promises';
12
- import type { OperationDescriptor } from '@prisma-next/framework-components/control';
13
- import { join, relative, resolve } from 'pathe';
14
+ import { join, resolve } from 'pathe';
14
15
 
15
16
  const MIGRATION_TS_FILE = 'migration.ts';
16
17
 
17
18
  /**
18
- * Options for scaffolding a migration.ts file.
19
+ * Writes a pre-rendered `migration.ts` source string to the given package
20
+ * directory. If the source begins with a shebang, the file is written with
21
+ * executable permissions (0o755) so it can be run directly via
22
+ * `./migration.ts` by the authoring class's `Migration.run(...)` guard.
19
23
  */
20
- export interface ScaffoldOptions {
21
- /** Operation descriptors to serialize as builder calls. */
22
- readonly descriptors?: readonly OperationDescriptor[];
23
- /** Absolute path to contract.json — used to derive contract.d.ts import for typed builders. */
24
- readonly contractJsonPath?: string;
25
- }
26
-
27
- function serializeQueryInput(input: unknown): string {
28
- if (typeof input === 'boolean') return String(input);
29
- if (typeof input === 'symbol') return 'TODO /* fill in using db.sql.from(...) */';
30
- if (input === null || input === undefined) return 'null';
31
- if (Array.isArray(input)) {
32
- if (input.length === 0) return '[]';
33
- if (input.every((item) => typeof item === 'symbol'))
34
- return '[TODO /* fill in using db.sql.from(...) */]';
35
- return `[${input.map(serializeQueryInput).join(', ')}]`;
36
- }
37
- return JSON.stringify(input);
38
- }
39
-
40
- function descriptorToBuilderCall(desc: OperationDescriptor): string {
41
- switch (desc.kind) {
42
- case 'createTable':
43
- return `createTable(${JSON.stringify(desc['table'])})`;
44
- case 'dropTable':
45
- return `dropTable(${JSON.stringify(desc['table'])})`;
46
- case 'addColumn': {
47
- const args = [JSON.stringify(desc['table']), JSON.stringify(desc['column'])];
48
- if (desc['overrides']) {
49
- args.push(JSON.stringify(desc['overrides']));
50
- }
51
- return `addColumn(${args.join(', ')})`;
52
- }
53
- case 'dropColumn':
54
- return `dropColumn(${JSON.stringify(desc['table'])}, ${JSON.stringify(desc['column'])})`;
55
- case 'alterColumnType': {
56
- const opts: Record<string, unknown> = {};
57
- if (desc['using']) opts['using'] = desc['using'];
58
- if (desc['toType']) opts['toType'] = desc['toType'];
59
- const hasOpts = Object.keys(opts).length > 0;
60
- return hasOpts
61
- ? `alterColumnType(${JSON.stringify(desc['table'])}, ${JSON.stringify(desc['column'])}, ${JSON.stringify(opts)})`
62
- : `alterColumnType(${JSON.stringify(desc['table'])}, ${JSON.stringify(desc['column'])})`;
63
- }
64
- case 'setNotNull':
65
- return `setNotNull(${JSON.stringify(desc['table'])}, ${JSON.stringify(desc['column'])})`;
66
- case 'dropNotNull':
67
- return `dropNotNull(${JSON.stringify(desc['table'])}, ${JSON.stringify(desc['column'])})`;
68
- case 'setDefault':
69
- return `setDefault(${JSON.stringify(desc['table'])}, ${JSON.stringify(desc['column'])})`;
70
- case 'dropDefault':
71
- return `dropDefault(${JSON.stringify(desc['table'])}, ${JSON.stringify(desc['column'])})`;
72
- case 'addPrimaryKey':
73
- return `addPrimaryKey(${JSON.stringify(desc['table'])})`;
74
- case 'addUnique':
75
- return `addUnique(${JSON.stringify(desc['table'])}, ${JSON.stringify(desc['columns'])})`;
76
- case 'addForeignKey':
77
- return `addForeignKey(${JSON.stringify(desc['table'])}, ${JSON.stringify(desc['columns'])})`;
78
- case 'dropConstraint':
79
- return `dropConstraint(${JSON.stringify(desc['table'])}, ${JSON.stringify(desc['constraintName'])})`;
80
- case 'createIndex':
81
- return `createIndex(${JSON.stringify(desc['table'])}, ${JSON.stringify(desc['columns'])})`;
82
- case 'dropIndex':
83
- return `dropIndex(${JSON.stringify(desc['table'])}, ${JSON.stringify(desc['indexName'])})`;
84
- case 'createEnumType':
85
- return desc['values']
86
- ? `createEnumType(${JSON.stringify(desc['typeName'])}, ${JSON.stringify(desc['values'])})`
87
- : `createEnumType(${JSON.stringify(desc['typeName'])})`;
88
- case 'addEnumValues':
89
- return `addEnumValues(${JSON.stringify(desc['typeName'])}, ${JSON.stringify(desc['values'])})`;
90
- case 'dropEnumType':
91
- return `dropEnumType(${JSON.stringify(desc['typeName'])})`;
92
- case 'renameType':
93
- return `renameType(${JSON.stringify(desc['fromName'])}, ${JSON.stringify(desc['toName'])})`;
94
- case 'createDependency':
95
- return `createDependency(${JSON.stringify(desc['dependencyId'])})`;
96
- case 'dataTransform':
97
- return `dataTransform(${JSON.stringify(desc['name'])}, {\n check: ${serializeQueryInput(desc['check'])},\n run: ${serializeQueryInput(desc['run'])},\n })`;
98
- default:
99
- throw new Error(`Unknown descriptor kind: ${desc.kind}`);
100
- }
101
- }
102
-
103
- /**
104
- * Scaffolds a migration.ts file in the given package directory.
105
- * Serializes operation descriptors as builder calls that the user can edit.
106
- * On emit, this file is re-evaluated to produce the final ops.
107
- */
108
- export async function scaffoldMigrationTs(
109
- packageDir: string,
110
- options: ScaffoldOptions = {},
111
- ): Promise<void> {
112
- const filePath = join(packageDir, MIGRATION_TS_FILE);
113
-
114
- const descriptors = options.descriptors ?? [];
115
- const hasDataTransform = descriptors.some((d) => d.kind === 'dataTransform');
116
-
117
- const lines: string[] = [];
118
-
119
- if (hasDataTransform && options.contractJsonPath) {
120
- const relativeContractDts = relative(packageDir, options.contractJsonPath).replace(
121
- /\.json$/,
122
- '.d',
123
- );
124
- lines.push(`import type { Contract } from "${relativeContractDts}"`);
125
- lines.push(`import { createBuilders } from "@prisma-next/target-postgres/migration-builders"`);
126
- lines.push('');
127
- const importList = [...new Set(descriptors.map((d) => d.kind))];
128
- importList.push('TODO');
129
- lines.push(`const { ${importList.join(', ')} } = createBuilders<Contract>()`);
130
- } else {
131
- const importList = [...new Set(descriptors.map((d) => d.kind))];
132
- if (importList.length === 0) {
133
- importList.push('createTable');
134
- }
135
- if (hasDataTransform) {
136
- importList.push('TODO');
137
- }
138
- lines.push(
139
- `import { ${importList.join(', ')} } from "@prisma-next/target-postgres/migration-builders"`,
140
- );
141
- }
142
-
143
- const calls = descriptors.map((d) => ` ${descriptorToBuilderCall(d)},`).join('\n');
144
- const body = calls.length > 0 ? `\n${calls}\n` : '';
145
-
146
- lines.push('');
147
- lines.push(`export default () => [${body}]`);
148
- lines.push('');
149
-
150
- await writeFile(filePath, lines.join('\n'));
24
+ export async function writeMigrationTs(packageDir: string, content: string): Promise<void> {
25
+ const isExecutable = content.startsWith('#!');
26
+ await writeFile(
27
+ join(packageDir, MIGRATION_TS_FILE),
28
+ content,
29
+ isExecutable ? { mode: 0o755 } : undefined,
30
+ );
151
31
  }
152
32
 
153
33
  /**
@@ -163,9 +43,13 @@ export async function hasMigrationTs(packageDir: string): Promise<boolean> {
163
43
  }
164
44
 
165
45
  /**
166
- * Evaluates a migration.ts file by loading it via native Node import.
167
- * Returns the result of calling the default export (expected to be a
168
- * function returning an array of operation descriptors).
46
+ * Evaluates a descriptor-flow migration.ts file by loading it via native
47
+ * Node import. Returns the result of calling the default export (expected
48
+ * to be a function returning an array of operation descriptors).
49
+ *
50
+ * Class-flow migration.ts files use a different shape — their default
51
+ * export is a class that extends `Migration` — and are evaluated by the
52
+ * target's `emit` capability, not this helper.
169
53
  *
170
54
  * Requires Node ≥24 for native TypeScript support.
171
55
  */
@@ -178,7 +62,6 @@ export async function evaluateMigrationTs(packageDir: string): Promise<readonly
178
62
  throw new Error(`migration.ts not found at "${filePath}"`);
179
63
  }
180
64
 
181
- // Use native Node TS import (Node ≥24, stable type stripping)
182
65
  const mod = (await import(filePath)) as { default?: unknown };
183
66
 
184
67
  if (typeof mod.default !== 'function') {
@@ -0,0 +1,18 @@
1
+ export type ScaffoldRuntime = 'node' | 'bun' | 'deno';
2
+
3
+ export function detectScaffoldRuntime(): ScaffoldRuntime {
4
+ if (typeof (globalThis as { Bun?: unknown }).Bun !== 'undefined') return 'bun';
5
+ if (typeof (globalThis as { Deno?: unknown }).Deno !== 'undefined') return 'deno';
6
+ return 'node';
7
+ }
8
+
9
+ export function shebangLineFor(runtime: ScaffoldRuntime): string {
10
+ switch (runtime) {
11
+ case 'bun':
12
+ return '#!/usr/bin/env -S bun';
13
+ case 'deno':
14
+ return '#!/usr/bin/env -S deno run -A';
15
+ case 'node':
16
+ return '#!/usr/bin/env -S node';
17
+ }
18
+ }
@@ -1 +0,0 @@
1
- {"version":3,"file":"attestation.mjs","names":["sorted: Record<string, unknown>"],"sources":["../../src/canonicalize-json.ts","../../src/attestation.ts"],"sourcesContent":["function sortKeys(value: unknown): unknown {\n if (value === null || typeof value !== 'object') {\n return value;\n }\n if (Array.isArray(value)) {\n return value.map(sortKeys);\n }\n const sorted: Record<string, unknown> = {};\n for (const key of Object.keys(value).sort()) {\n sorted[key] = sortKeys((value as Record<string, unknown>)[key]);\n }\n return sorted;\n}\n\nexport function canonicalizeJson(value: unknown): string {\n return JSON.stringify(sortKeys(value));\n}\n","import { createHash } from 'node:crypto';\nimport { canonicalizeJson } from './canonicalize-json';\nimport { readMigrationPackage, writeMigrationManifest } from './io';\nimport type { MigrationManifest, MigrationOps } from './types';\n\nexport interface VerifyResult {\n readonly ok: boolean;\n readonly reason?: 'draft' | 'mismatch';\n readonly storedMigrationId?: string;\n readonly computedMigrationId?: string;\n}\n\nfunction sha256Hex(input: string): string {\n return createHash('sha256').update(input).digest('hex');\n}\n\n/**\n * Content-addressed migration identity over (manifest envelope sans\n * contracts/hints, ops). See ADR 199 \"Storage-only migration identity\"\n * for the rationale: contracts are anchored separately by the\n * storage-hash bookends inside the envelope; planner hints are advisory\n * and must not affect identity.\n */\nexport function computeMigrationId(manifest: MigrationManifest, ops: MigrationOps): string {\n const {\n migrationId: _migrationId,\n signature: _signature,\n fromContract: _fromContract,\n toContract: _toContract,\n hints: _hints,\n ...strippedMeta\n } = manifest;\n\n const canonicalManifest = canonicalizeJson(strippedMeta);\n const canonicalOps = canonicalizeJson(ops);\n\n const partHashes = [canonicalManifest, canonicalOps].map(sha256Hex);\n const hash = sha256Hex(canonicalizeJson(partHashes));\n\n return `sha256:${hash}`;\n}\n\n/** Compute and persist `migrationId` to `manifest.json`. */\nexport async function attestMigration(dir: string): Promise<string> {\n const pkg = await readMigrationPackage(dir);\n const migrationId = computeMigrationId(pkg.manifest, pkg.ops);\n\n const updated = { ...pkg.manifest, migrationId };\n await writeMigrationManifest(dir, updated);\n\n return migrationId;\n}\n\nexport async function verifyMigration(dir: string): Promise<VerifyResult> {\n const pkg = await readMigrationPackage(dir);\n\n if (pkg.manifest.migrationId === null) {\n return { ok: false, reason: 'draft' };\n }\n\n const computed = computeMigrationId(pkg.manifest, pkg.ops);\n\n if (pkg.manifest.migrationId === computed) {\n return { ok: true, storedMigrationId: pkg.manifest.migrationId, computedMigrationId: computed };\n }\n\n return {\n ok: false,\n reason: 'mismatch',\n storedMigrationId: pkg.manifest.migrationId,\n computedMigrationId: computed,\n };\n}\n"],"mappings":";;;;AAAA,SAAS,SAAS,OAAyB;AACzC,KAAI,UAAU,QAAQ,OAAO,UAAU,SACrC,QAAO;AAET,KAAI,MAAM,QAAQ,MAAM,CACtB,QAAO,MAAM,IAAI,SAAS;CAE5B,MAAMA,SAAkC,EAAE;AAC1C,MAAK,MAAM,OAAO,OAAO,KAAK,MAAM,CAAC,MAAM,CACzC,QAAO,OAAO,SAAU,MAAkC,KAAK;AAEjE,QAAO;;AAGT,SAAgB,iBAAiB,OAAwB;AACvD,QAAO,KAAK,UAAU,SAAS,MAAM,CAAC;;;;;ACHxC,SAAS,UAAU,OAAuB;AACxC,QAAO,WAAW,SAAS,CAAC,OAAO,MAAM,CAAC,OAAO,MAAM;;;;;;;;;AAUzD,SAAgB,mBAAmB,UAA6B,KAA2B;CACzF,MAAM,EACJ,aAAa,cACb,WAAW,YACX,cAAc,eACd,YAAY,aACZ,OAAO,QACP,GAAG,iBACD;AAQJ,QAAO,UAFM,UAAU,iBADJ,CAHO,iBAAiB,aAAa,EACnC,iBAAiB,IAAI,CAEU,CAAC,IAAI,UAAU,CAChB,CAAC;;;AAMtD,eAAsB,gBAAgB,KAA8B;CAClE,MAAM,MAAM,MAAM,qBAAqB,IAAI;CAC3C,MAAM,cAAc,mBAAmB,IAAI,UAAU,IAAI,IAAI;AAG7D,OAAM,uBAAuB,KADb;EAAE,GAAG,IAAI;EAAU;EAAa,CACN;AAE1C,QAAO;;AAGT,eAAsB,gBAAgB,KAAoC;CACxE,MAAM,MAAM,MAAM,qBAAqB,IAAI;AAE3C,KAAI,IAAI,SAAS,gBAAgB,KAC/B,QAAO;EAAE,IAAI;EAAO,QAAQ;EAAS;CAGvC,MAAM,WAAW,mBAAmB,IAAI,UAAU,IAAI,IAAI;AAE1D,KAAI,IAAI,SAAS,gBAAgB,SAC/B,QAAO;EAAE,IAAI;EAAM,mBAAmB,IAAI,SAAS;EAAa,qBAAqB;EAAU;AAGjG,QAAO;EACL,IAAI;EACJ,QAAQ;EACR,mBAAmB,IAAI,SAAS;EAChC,qBAAqB;EACtB"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"io-BO18-Evu.mjs","names":["manifestRaw: string","opsRaw: string","manifest: MigrationManifest","ops: MigrationOps","entries: string[]","packages: BaseMigrationBundle[]"],"sources":["../src/io.ts"],"sourcesContent":["import { mkdir, readdir, readFile, stat, writeFile } from 'node:fs/promises';\nimport { type } from 'arktype';\nimport { basename, dirname, join } from 'pathe';\nimport {\n errorDirectoryExists,\n errorInvalidJson,\n errorInvalidManifest,\n errorInvalidSlug,\n errorMissingFile,\n} from './errors';\nimport type { BaseMigrationBundle, MigrationManifest, MigrationOps } from './types';\n\nconst MANIFEST_FILE = 'migration.json';\nconst OPS_FILE = 'ops.json';\nconst MAX_SLUG_LENGTH = 64;\n\nfunction hasErrnoCode(error: unknown, code: string): boolean {\n return error instanceof Error && (error as { code?: string }).code === code;\n}\n\nconst MigrationHintsSchema = type({\n used: 'string[]',\n applied: 'string[]',\n plannerVersion: 'string',\n planningStrategy: 'string',\n});\n\nconst MigrationManifestSchema = type({\n from: 'string',\n to: 'string',\n migrationId: 'string | null',\n kind: \"'regular' | 'baseline'\",\n fromContract: 'object | null',\n toContract: 'object',\n hints: MigrationHintsSchema,\n labels: 'string[]',\n 'authorship?': type({\n 'author?': 'string',\n 'email?': 'string',\n }),\n 'signature?': type({\n keyId: 'string',\n value: 'string',\n }).or('null'),\n createdAt: 'string',\n});\n\nconst MigrationOpSchema = type({\n id: 'string',\n label: 'string',\n operationClass: \"'additive' | 'widening' | 'destructive' | 'data'\",\n});\n\n// Intentionally shallow: operation-specific payload validation is owned by planner/runner layers.\nconst MigrationOpsSchema = MigrationOpSchema.array();\n\nexport async function writeMigrationPackage(\n dir: string,\n manifest: MigrationManifest,\n ops: MigrationOps,\n): Promise<void> {\n await mkdir(dirname(dir), { recursive: true });\n\n try {\n await mkdir(dir);\n } catch (error) {\n if (hasErrnoCode(error, 'EEXIST')) {\n throw errorDirectoryExists(dir);\n }\n throw error;\n }\n\n await writeFile(join(dir, MANIFEST_FILE), JSON.stringify(manifest, null, 2), { flag: 'wx' });\n await writeFile(join(dir, OPS_FILE), JSON.stringify(ops, null, 2), { flag: 'wx' });\n}\n\nexport async function writeMigrationManifest(\n dir: string,\n manifest: MigrationManifest,\n): Promise<void> {\n await writeFile(join(dir, MANIFEST_FILE), `${JSON.stringify(manifest, null, 2)}\\n`);\n}\n\nexport async function writeMigrationOps(dir: string, ops: MigrationOps): Promise<void> {\n await writeFile(join(dir, OPS_FILE), `${JSON.stringify(ops, null, 2)}\\n`);\n}\n\nexport async function readMigrationPackage(dir: string): Promise<BaseMigrationBundle> {\n const manifestPath = join(dir, MANIFEST_FILE);\n const opsPath = join(dir, OPS_FILE);\n\n let manifestRaw: string;\n try {\n manifestRaw = await readFile(manifestPath, 'utf-8');\n } catch (error) {\n if (hasErrnoCode(error, 'ENOENT')) {\n throw errorMissingFile(MANIFEST_FILE, dir);\n }\n throw error;\n }\n\n let opsRaw: string;\n try {\n opsRaw = await readFile(opsPath, 'utf-8');\n } catch (error) {\n if (hasErrnoCode(error, 'ENOENT')) {\n throw errorMissingFile(OPS_FILE, dir);\n }\n throw error;\n }\n\n let manifest: MigrationManifest;\n try {\n manifest = JSON.parse(manifestRaw);\n } catch (e) {\n throw errorInvalidJson(manifestPath, e instanceof Error ? e.message : String(e));\n }\n\n let ops: MigrationOps;\n try {\n ops = JSON.parse(opsRaw);\n } catch (e) {\n throw errorInvalidJson(opsPath, e instanceof Error ? e.message : String(e));\n }\n\n validateManifest(manifest, manifestPath);\n validateOps(ops, opsPath);\n\n return {\n dirName: basename(dir),\n dirPath: dir,\n manifest,\n ops,\n };\n}\n\nfunction validateManifest(\n manifest: unknown,\n filePath: string,\n): asserts manifest is MigrationManifest {\n const result = MigrationManifestSchema(manifest);\n if (result instanceof type.errors) {\n throw errorInvalidManifest(filePath, result.summary);\n }\n}\n\nfunction validateOps(ops: unknown, filePath: string): asserts ops is MigrationOps {\n const result = MigrationOpsSchema(ops);\n if (result instanceof type.errors) {\n throw errorInvalidManifest(filePath, result.summary);\n }\n}\n\nexport async function readMigrationsDir(\n migrationsRoot: string,\n): Promise<readonly BaseMigrationBundle[]> {\n let entries: string[];\n try {\n entries = await readdir(migrationsRoot);\n } catch (error) {\n if (hasErrnoCode(error, 'ENOENT')) {\n return [];\n }\n throw error;\n }\n\n const packages: BaseMigrationBundle[] = [];\n\n for (const entry of entries.sort()) {\n const entryPath = join(migrationsRoot, entry);\n const entryStat = await stat(entryPath);\n if (!entryStat.isDirectory()) continue;\n\n const manifestPath = join(entryPath, MANIFEST_FILE);\n try {\n await stat(manifestPath);\n } catch {\n continue; // skip non-migration directories\n }\n\n packages.push(await readMigrationPackage(entryPath));\n }\n\n return packages;\n}\n\nexport function formatMigrationDirName(timestamp: Date, slug: string): string {\n const sanitized = slug\n .toLowerCase()\n .replace(/[^a-z0-9]/g, '_')\n .replace(/_+/g, '_')\n .replace(/^_|_$/g, '');\n\n if (sanitized.length === 0) {\n throw errorInvalidSlug(slug);\n }\n\n const truncated = sanitized.slice(0, MAX_SLUG_LENGTH);\n\n const y = timestamp.getUTCFullYear();\n const mo = String(timestamp.getUTCMonth() + 1).padStart(2, '0');\n const d = String(timestamp.getUTCDate()).padStart(2, '0');\n const h = String(timestamp.getUTCHours()).padStart(2, '0');\n const mi = String(timestamp.getUTCMinutes()).padStart(2, '0');\n\n return `${y}${mo}${d}T${h}${mi}_${truncated}`;\n}\n"],"mappings":"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"}