@better-media/cli 0.1.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.
- package/LICENSE +21 -0
- package/README.md +27 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +521 -0
- package/dist/index.js.map +1 -0
- package/package.json +46 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 abenezeratnafu
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
|
|
20
|
+
ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# @better-media/cli
|
|
2
|
+
|
|
3
|
+
Command Line Tool for the Better Media framework.
|
|
4
|
+
|
|
5
|
+
## Commands
|
|
6
|
+
|
|
7
|
+
- `media init`: Bootstrap a `media.config.ts` file in your project.
|
|
8
|
+
- `media generate`: Auto-generate database migrations, types, or configuration structures.
|
|
9
|
+
- `media status`: Check the status of your Better Media configuration and database connection.
|
|
10
|
+
|
|
11
|
+
## Installation
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
pnpm add -D @better-media/cli
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Usage
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
# Initialize a new configuration
|
|
21
|
+
npx media init
|
|
22
|
+
|
|
23
|
+
# Generate Drizzle or Prisma schemas based on your media config
|
|
24
|
+
npx media generate --target drizzle --out src/db/schema.ts
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Visit [better-media.dev/docs/cli](https://better-media.dev/docs/cli) for all flags and options.
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,521 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/index.ts
|
|
4
|
+
import { Command } from "commander";
|
|
5
|
+
import chalk3 from "chalk";
|
|
6
|
+
import path6 from "path";
|
|
7
|
+
import process5 from "process";
|
|
8
|
+
|
|
9
|
+
// src/project-config.ts
|
|
10
|
+
import fs from "fs/promises";
|
|
11
|
+
import path from "path";
|
|
12
|
+
import { pathToFileURL } from "url";
|
|
13
|
+
import { createRequire } from "module";
|
|
14
|
+
import { randomUUID } from "crypto";
|
|
15
|
+
import ts from "typescript";
|
|
16
|
+
async function fileExists(p) {
|
|
17
|
+
try {
|
|
18
|
+
await fs.access(p);
|
|
19
|
+
return true;
|
|
20
|
+
} catch {
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
async function detectAdapterHint(cwd) {
|
|
25
|
+
try {
|
|
26
|
+
const pkgPath = path.join(cwd, "package.json");
|
|
27
|
+
const raw = await fs.readFile(pkgPath, "utf8");
|
|
28
|
+
const pkg = JSON.parse(raw);
|
|
29
|
+
const deps = {
|
|
30
|
+
...pkg.dependencies ?? {},
|
|
31
|
+
...pkg.devDependencies ?? {},
|
|
32
|
+
...pkg.peerDependencies ?? {}
|
|
33
|
+
};
|
|
34
|
+
if ("kysely" in deps) return "kysely";
|
|
35
|
+
if ("prisma" in deps || "@prisma/client" in deps) return "prisma";
|
|
36
|
+
if ("drizzle-orm" in deps) return "drizzle";
|
|
37
|
+
return "unknown";
|
|
38
|
+
} catch {
|
|
39
|
+
return "unknown";
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
function normalizeModuleExport(mod) {
|
|
43
|
+
if (!mod) return mod;
|
|
44
|
+
const m = mod;
|
|
45
|
+
return m.default ?? mod;
|
|
46
|
+
}
|
|
47
|
+
async function importJsModule(absPath) {
|
|
48
|
+
const url = pathToFileURL(absPath).href;
|
|
49
|
+
const mod = await import(url);
|
|
50
|
+
return normalizeModuleExport(mod);
|
|
51
|
+
}
|
|
52
|
+
async function requireTsModule(absPath) {
|
|
53
|
+
const require2 = createRequire(import.meta.url);
|
|
54
|
+
try {
|
|
55
|
+
const tsNode = require2("ts-node");
|
|
56
|
+
tsNode.register({
|
|
57
|
+
transpileOnly: true,
|
|
58
|
+
compilerOptions: { module: "CommonJS", moduleResolution: "Node" }
|
|
59
|
+
});
|
|
60
|
+
} catch {
|
|
61
|
+
}
|
|
62
|
+
const mod = require2(absPath);
|
|
63
|
+
return normalizeModuleExport(mod);
|
|
64
|
+
}
|
|
65
|
+
async function importTsModule(absPath) {
|
|
66
|
+
const source = await fs.readFile(absPath, "utf8");
|
|
67
|
+
const transpiled = ts.transpileModule(source, {
|
|
68
|
+
compilerOptions: {
|
|
69
|
+
target: ts.ScriptTarget.ES2022,
|
|
70
|
+
module: ts.ModuleKind.ESNext,
|
|
71
|
+
moduleResolution: ts.ModuleResolutionKind.Bundler,
|
|
72
|
+
esModuleInterop: true,
|
|
73
|
+
resolveJsonModule: true
|
|
74
|
+
},
|
|
75
|
+
fileName: absPath
|
|
76
|
+
});
|
|
77
|
+
const tempPath = path.join(path.dirname(absPath), `.media-config-${randomUUID()}.mjs`);
|
|
78
|
+
await fs.writeFile(tempPath, transpiled.outputText, "utf8");
|
|
79
|
+
try {
|
|
80
|
+
const mod = await import(pathToFileURL(tempPath).href);
|
|
81
|
+
return normalizeModuleExport(mod);
|
|
82
|
+
} finally {
|
|
83
|
+
await fs.unlink(tempPath).catch(() => void 0);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
function assertValidConfig(value, configPath) {
|
|
87
|
+
if (!value || typeof value !== "object") {
|
|
88
|
+
throw new Error(`[media] Config "${configPath}" must export an object.`);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
var CONFIG_CANDIDATES = [
|
|
92
|
+
"media.config.ts",
|
|
93
|
+
"media.config.js",
|
|
94
|
+
"better-media.config.ts",
|
|
95
|
+
"better-media.config.js"
|
|
96
|
+
];
|
|
97
|
+
async function loadProjectConfig(options) {
|
|
98
|
+
const cwd = path.resolve(options?.cwd ?? process.cwd());
|
|
99
|
+
let configPath = options?.configPath ? path.resolve(cwd, options.configPath) : void 0;
|
|
100
|
+
if (configPath && !await fileExists(configPath)) {
|
|
101
|
+
throw new Error(`[media] Config file not found: ${path.relative(cwd, configPath)}`);
|
|
102
|
+
}
|
|
103
|
+
if (!configPath) {
|
|
104
|
+
for (const candidate of CONFIG_CANDIDATES) {
|
|
105
|
+
const abs = path.join(cwd, candidate);
|
|
106
|
+
if (await fileExists(abs)) {
|
|
107
|
+
configPath = abs;
|
|
108
|
+
break;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
if (!configPath) {
|
|
112
|
+
throw new Error(
|
|
113
|
+
`[media] No configuration file found. Add one of: ${CONFIG_CANDIDATES.map(
|
|
114
|
+
(c) => `"${c}"`
|
|
115
|
+
).join(", ")}, or pass --config <path>.`
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
if (!configPath) {
|
|
120
|
+
throw new Error(
|
|
121
|
+
`[media] No configuration file found. Add one of: ${CONFIG_CANDIDATES.map(
|
|
122
|
+
(c) => `"${c}"`
|
|
123
|
+
).join(", ")}, or pass --config <path>.`
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
const adapterHint = await detectAdapterHint(cwd);
|
|
127
|
+
const ext = path.extname(configPath);
|
|
128
|
+
let rawConfig;
|
|
129
|
+
try {
|
|
130
|
+
rawConfig = ext === ".ts" ? await requireTsModule(configPath) : await importJsModule(configPath);
|
|
131
|
+
} catch (err) {
|
|
132
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
133
|
+
if (ext === ".ts") {
|
|
134
|
+
try {
|
|
135
|
+
rawConfig = await importTsModule(configPath);
|
|
136
|
+
} catch {
|
|
137
|
+
throw new Error(
|
|
138
|
+
`[media] Failed to load "${path.basename(
|
|
139
|
+
configPath
|
|
140
|
+
)}". For TypeScript configs, install "ts-node" in your app or use a .js config. Original error: ${msg}`
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
} else {
|
|
144
|
+
throw err;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
assertValidConfig(rawConfig, configPath);
|
|
148
|
+
const cfg = rawConfig;
|
|
149
|
+
const dialect = typeof cfg.dialect === "string" ? cfg.dialect : void 0;
|
|
150
|
+
const schemaOutput = typeof cfg.schemaOutput === "string" ? cfg.schemaOutput : void 0;
|
|
151
|
+
const migrationsDir = typeof cfg.migrationsDir === "string" ? cfg.migrationsDir : void 0;
|
|
152
|
+
return {
|
|
153
|
+
configPath,
|
|
154
|
+
adapterHint,
|
|
155
|
+
config: {
|
|
156
|
+
database: cfg.database,
|
|
157
|
+
createDatabase: cfg.createDatabase,
|
|
158
|
+
dialect,
|
|
159
|
+
schemaOutput,
|
|
160
|
+
migrationsDir
|
|
161
|
+
}
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// src/commands/generate.ts
|
|
166
|
+
import path3 from "path";
|
|
167
|
+
import process2 from "process";
|
|
168
|
+
import { getAdapter } from "@better-media/core";
|
|
169
|
+
|
|
170
|
+
// src/generators/index.ts
|
|
171
|
+
import path2 from "path";
|
|
172
|
+
import fs2 from "fs-extra";
|
|
173
|
+
import chalk from "chalk";
|
|
174
|
+
import {
|
|
175
|
+
schema,
|
|
176
|
+
getMigrations,
|
|
177
|
+
MigrationPlanner,
|
|
178
|
+
compileMigrationOperationsSql,
|
|
179
|
+
applyOperationsToMetadata
|
|
180
|
+
} from "@better-media/core";
|
|
181
|
+
function resolveMigrationsDir(cwd, outPath, options) {
|
|
182
|
+
const opts = options;
|
|
183
|
+
if (opts?.migrationsDir && typeof opts.migrationsDir === "string") {
|
|
184
|
+
return path2.resolve(cwd, opts.migrationsDir);
|
|
185
|
+
}
|
|
186
|
+
return path2.dirname(outPath);
|
|
187
|
+
}
|
|
188
|
+
var TIMESTAMP_DIR = /^\d{10,20}$/;
|
|
189
|
+
async function findLatestTimestampRunDir(migrationsDir) {
|
|
190
|
+
if (!await fs2.pathExists(migrationsDir)) return void 0;
|
|
191
|
+
const entries = await fs2.readdir(migrationsDir, { withFileTypes: true });
|
|
192
|
+
const names = entries.filter((e) => e.isDirectory() && TIMESTAMP_DIR.test(e.name)).map((e) => e.name);
|
|
193
|
+
const latest = names[0];
|
|
194
|
+
return latest ? path2.join(migrationsDir, latest) : void 0;
|
|
195
|
+
}
|
|
196
|
+
async function resolveSnapshotReadPath(migrationsDir) {
|
|
197
|
+
const latestRun = await findLatestTimestampRunDir(migrationsDir);
|
|
198
|
+
const nested = latestRun ? path2.join(latestRun, "snapshot.json") : void 0;
|
|
199
|
+
if (nested && await fs2.pathExists(nested)) return nested;
|
|
200
|
+
const legacyRoot = path2.join(migrationsDir, "snapshot.json");
|
|
201
|
+
if (await fs2.pathExists(legacyRoot)) return legacyRoot;
|
|
202
|
+
return void 0;
|
|
203
|
+
}
|
|
204
|
+
var builtInGenerators = {
|
|
205
|
+
async kysely({ cwd, outPath, dialect, adapter, options }) {
|
|
206
|
+
const migrationsDir = resolveMigrationsDir(cwd, outPath, options);
|
|
207
|
+
await fs2.ensureDir(migrationsDir);
|
|
208
|
+
let currentTables = [];
|
|
209
|
+
let snapshotLoaded = false;
|
|
210
|
+
const snapshotReadPath = await resolveSnapshotReadPath(migrationsDir);
|
|
211
|
+
if (snapshotReadPath) {
|
|
212
|
+
try {
|
|
213
|
+
currentTables = await fs2.readJson(snapshotReadPath);
|
|
214
|
+
snapshotLoaded = true;
|
|
215
|
+
} catch {
|
|
216
|
+
console.warn(
|
|
217
|
+
chalk.yellow(
|
|
218
|
+
`[media] Failed to load snapshot at ${snapshotReadPath}, falling back to DB introspection.`
|
|
219
|
+
)
|
|
220
|
+
);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
if (!snapshotLoaded) {
|
|
224
|
+
if (typeof adapter.__getMetadata === "function") {
|
|
225
|
+
currentTables = await adapter.__getMetadata();
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
const planner = new MigrationPlanner(dialect);
|
|
229
|
+
const plannedOperations = planner.plan(schema, currentTables);
|
|
230
|
+
if (plannedOperations.length === 0) {
|
|
231
|
+
console.log(
|
|
232
|
+
chalk.blue(`[media] No changes detected. Database/Snapshot is already up to date.`)
|
|
233
|
+
);
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
236
|
+
const sql = compileMigrationOperationsSql({ operations: plannedOperations, dialect });
|
|
237
|
+
const nextMetadata = applyOperationsToMetadata(currentTables, plannedOperations, dialect);
|
|
238
|
+
const runTs = Date.now();
|
|
239
|
+
const runDir = path2.join(migrationsDir, String(runTs));
|
|
240
|
+
await fs2.ensureDir(runDir);
|
|
241
|
+
const snapshotWritePath = path2.join(runDir, "snapshot.json");
|
|
242
|
+
await fs2.writeJson(snapshotWritePath, nextMetadata, { spaces: 2 });
|
|
243
|
+
const migrationFile = path2.join(runDir, "migration.sql");
|
|
244
|
+
await fs2.writeFile(migrationFile, sql, "utf8");
|
|
245
|
+
console.log(chalk.green(`[media] Generated migration at ${path2.relative(cwd, migrationFile)}`));
|
|
246
|
+
},
|
|
247
|
+
async postgres(args) {
|
|
248
|
+
const kysely = builtInGenerators.kysely;
|
|
249
|
+
if (!kysely) throw new Error("Kysely generator not found");
|
|
250
|
+
await kysely(args);
|
|
251
|
+
},
|
|
252
|
+
async prisma() {
|
|
253
|
+
throw new Error(
|
|
254
|
+
'[media] Prisma schema generator is not implemented yet. Run "npx better-media generate" with Kysely, then apply via Prisma migrate/push.'
|
|
255
|
+
);
|
|
256
|
+
},
|
|
257
|
+
async drizzle() {
|
|
258
|
+
throw new Error(
|
|
259
|
+
'[media] Drizzle schema generator is not implemented yet. Run "npx better-media generate" with Kysely, then apply via Drizzle migrate/push.'
|
|
260
|
+
);
|
|
261
|
+
}
|
|
262
|
+
};
|
|
263
|
+
var builtInMigrators = {
|
|
264
|
+
async kysely({ mode, dialect, adapter }) {
|
|
265
|
+
const hasPlannedMigration = typeof adapter.__getMetadata === "function" && typeof adapter.__executeMigration === "function";
|
|
266
|
+
if (!hasPlannedMigration) {
|
|
267
|
+
throw new Error(
|
|
268
|
+
`[media] This adapter does not support direct CLI migrations. Use "media generate" and apply via your ORM migration tool.`
|
|
269
|
+
);
|
|
270
|
+
}
|
|
271
|
+
const planned = await getMigrations(adapter, { mode, dialect });
|
|
272
|
+
await planned.runMigrations();
|
|
273
|
+
},
|
|
274
|
+
async postgres(args) {
|
|
275
|
+
const kysely = builtInMigrators.kysely;
|
|
276
|
+
if (!kysely) throw new Error("Kysely migrator not found");
|
|
277
|
+
await kysely(args);
|
|
278
|
+
},
|
|
279
|
+
async prisma() {
|
|
280
|
+
throw new Error(
|
|
281
|
+
'[media] The migrate command only works with the built-in Kysely adapter. For Prisma, run "npx better-media generate" and apply with Prisma migrate/push.'
|
|
282
|
+
);
|
|
283
|
+
},
|
|
284
|
+
async drizzle() {
|
|
285
|
+
throw new Error(
|
|
286
|
+
'[media] The migrate command only works with the built-in Kysely adapter. For Drizzle, run "npx better-media generate" and apply with Drizzle migrate/push.'
|
|
287
|
+
);
|
|
288
|
+
}
|
|
289
|
+
};
|
|
290
|
+
async function generateSchema(args) {
|
|
291
|
+
const adapterEx = args.adapter;
|
|
292
|
+
const id = adapterEx.id;
|
|
293
|
+
if (id && id in builtInGenerators) {
|
|
294
|
+
await builtInGenerators[id](args);
|
|
295
|
+
return;
|
|
296
|
+
}
|
|
297
|
+
if (typeof adapterEx.createSchema === "function") {
|
|
298
|
+
const generated = await adapterEx.createSchema(args.options, args.outPath);
|
|
299
|
+
const targetPath = generated.fileName ?? generated.path ?? args.outPath;
|
|
300
|
+
const overwrite = generated.overwrite ?? true;
|
|
301
|
+
if (!overwrite && await fs2.pathExists(targetPath)) {
|
|
302
|
+
throw new Error(`[media] Refusing to overwrite existing file: ${targetPath}`);
|
|
303
|
+
}
|
|
304
|
+
await fs2.ensureDir(path2.dirname(targetPath));
|
|
305
|
+
await fs2.writeFile(targetPath, generated.code, "utf8");
|
|
306
|
+
console.log(chalk.green(`[media] Generated schema at ${path2.relative(args.cwd, targetPath)}`));
|
|
307
|
+
return;
|
|
308
|
+
}
|
|
309
|
+
throw new Error(
|
|
310
|
+
`[media] ${id ?? "adapter"} is not supported. If it is a custom adapter, implement createSchema(options, file).`
|
|
311
|
+
);
|
|
312
|
+
}
|
|
313
|
+
async function migrateWithAdapter(args) {
|
|
314
|
+
const adapterEx = args.adapter;
|
|
315
|
+
const id = adapterEx.id;
|
|
316
|
+
if (id && id in builtInMigrators) {
|
|
317
|
+
await builtInMigrators[id](args);
|
|
318
|
+
return;
|
|
319
|
+
}
|
|
320
|
+
if (typeof args.adapter.__getMetadata === "function" && typeof args.adapter.__executeMigration === "function") {
|
|
321
|
+
const kysely = builtInMigrators.kysely;
|
|
322
|
+
if (!kysely) throw new Error("Kysely migrator not found");
|
|
323
|
+
await kysely(args);
|
|
324
|
+
return;
|
|
325
|
+
}
|
|
326
|
+
throw new Error(`[media] Direct migrations are not supported for adapter "${id ?? "unknown"}".`);
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
// src/commands/generate.ts
|
|
330
|
+
function getDefaultSchemaOutput() {
|
|
331
|
+
return "better-media-migrations/migration.sql";
|
|
332
|
+
}
|
|
333
|
+
function registerGenerateCommand(program2) {
|
|
334
|
+
program2.command("generate").description("Generate Better Media database schema for the configured adapter").option("-C, --cwd <path>", "Working directory to resolve config from", process2.cwd()).option("--config <path>", "Path to media config file").option(
|
|
335
|
+
"--dialect <dialect>",
|
|
336
|
+
"SQL dialect (postgres|mysql|sqlite|mssql). Defaults from adapter/config.",
|
|
337
|
+
void 0
|
|
338
|
+
).action(async (opts) => {
|
|
339
|
+
const cwd = path3.resolve(opts.cwd);
|
|
340
|
+
const loaded = await loadProjectConfig({ cwd, configPath: opts.config });
|
|
341
|
+
const adapter = await getAdapter(loaded.config);
|
|
342
|
+
const dialectFromAdapter = typeof adapter.__getDialect === "function" ? adapter.__getDialect() : void 0;
|
|
343
|
+
const dialect = opts.dialect ?? loaded.config.dialect ?? dialectFromAdapter ?? "postgres";
|
|
344
|
+
const outPath = path3.resolve(cwd, loaded.config.schemaOutput ?? getDefaultSchemaOutput());
|
|
345
|
+
await generateSchema({
|
|
346
|
+
cwd,
|
|
347
|
+
adapter,
|
|
348
|
+
outPath,
|
|
349
|
+
dialect,
|
|
350
|
+
options: loaded.config
|
|
351
|
+
});
|
|
352
|
+
});
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
// src/commands/init.ts
|
|
356
|
+
import path4 from "path";
|
|
357
|
+
import process3 from "process";
|
|
358
|
+
import fs3 from "fs-extra";
|
|
359
|
+
function detectFramework(pkg) {
|
|
360
|
+
const deps = { ...pkg.dependencies ?? {}, ...pkg.devDependencies ?? {} };
|
|
361
|
+
if ("@nestjs/core" in deps || "@nestjs/common" in deps) return "nestjs";
|
|
362
|
+
if ("express" in deps) return "express";
|
|
363
|
+
return "unknown";
|
|
364
|
+
}
|
|
365
|
+
function inferExtension(cwd, framework, pkgType) {
|
|
366
|
+
if (framework === "nestjs") return "ts";
|
|
367
|
+
if (pkgType === "module") return "js";
|
|
368
|
+
return "ts";
|
|
369
|
+
}
|
|
370
|
+
function buildConfigContent(ext) {
|
|
371
|
+
const importPool = ext === "ts" ? 'import { Pool } from "pg";' : 'import { Pool } from "pg";';
|
|
372
|
+
return `${importPool}
|
|
373
|
+
|
|
374
|
+
export const mediaOptions = {
|
|
375
|
+
database: new Pool({
|
|
376
|
+
connectionString:
|
|
377
|
+
process.env.DATABASE_URL ?? "postgres://postgres:postgres@localhost:5432/better_media",
|
|
378
|
+
}),
|
|
379
|
+
dialect: "postgres",
|
|
380
|
+
migrationsDir: "better-media",
|
|
381
|
+
};
|
|
382
|
+
|
|
383
|
+
export default mediaOptions;
|
|
384
|
+
`;
|
|
385
|
+
}
|
|
386
|
+
function registerInitCommand(program2) {
|
|
387
|
+
program2.command("init").description("Generate media.config.ts/js based on detected framework").option("-C, --cwd <path>", "Working directory", process3.cwd()).option("-f, --force", "Overwrite existing config file", false).action(async (opts) => {
|
|
388
|
+
const cwd = path4.resolve(opts.cwd);
|
|
389
|
+
const pkgPath = path4.join(cwd, "package.json");
|
|
390
|
+
if (!await fs3.pathExists(pkgPath)) {
|
|
391
|
+
throw new Error(`[media] No package.json found in ${cwd}`);
|
|
392
|
+
}
|
|
393
|
+
const pkg = await fs3.readJson(pkgPath);
|
|
394
|
+
const framework = detectFramework(pkg);
|
|
395
|
+
const ext = inferExtension(cwd, framework, pkg.type);
|
|
396
|
+
const configFile = path4.join(cwd, `media.config.${ext}`);
|
|
397
|
+
if (await fs3.pathExists(configFile) && !opts.force) {
|
|
398
|
+
throw new Error(
|
|
399
|
+
`[media] ${path4.basename(configFile)} already exists. Re-run with --force to overwrite.`
|
|
400
|
+
);
|
|
401
|
+
}
|
|
402
|
+
const content = buildConfigContent(ext);
|
|
403
|
+
await fs3.writeFile(configFile, content, "utf8");
|
|
404
|
+
const deps = { ...pkg.dependencies ?? {}, ...pkg.devDependencies ?? {} };
|
|
405
|
+
if (!("pg" in deps)) {
|
|
406
|
+
console.warn('[media] Added config uses "pg". Install it with: pnpm add pg');
|
|
407
|
+
}
|
|
408
|
+
console.log(
|
|
409
|
+
`[media] Created ${path4.basename(configFile)} (${framework === "unknown" ? "generic" : framework} template)`
|
|
410
|
+
);
|
|
411
|
+
});
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
// src/commands/migrate.ts
|
|
415
|
+
import chalk2 from "chalk";
|
|
416
|
+
import path5 from "path";
|
|
417
|
+
import process4 from "process";
|
|
418
|
+
import { existsSync } from "fs";
|
|
419
|
+
import { createInterface } from "readline/promises";
|
|
420
|
+
import {
|
|
421
|
+
getAdapter as getAdapter2,
|
|
422
|
+
getMigrations as getMigrations2
|
|
423
|
+
} from "@better-media/core";
|
|
424
|
+
function registerMigrateCommand(program2) {
|
|
425
|
+
program2.command("migrate").description("Apply Better Media schema directly to the database (Kysely adapter only)").option("-C, --cwd <path>", "Working directory to resolve config from", process4.cwd()).option("--config <path>", "Path to media config file").option("--mode <mode>", "Migration mode (safe|diff|force)", "diff").option("-y, --yes", "Automatically accept and run migrations without prompting", false).option(
|
|
426
|
+
"--dialect <dialect>",
|
|
427
|
+
"SQL dialect override (postgres|mysql|sqlite|mssql). Defaults from adapter/config.",
|
|
428
|
+
void 0
|
|
429
|
+
).action(async (opts) => {
|
|
430
|
+
const parsed = opts;
|
|
431
|
+
const options = {
|
|
432
|
+
cwd: parsed.cwd ?? process4.cwd(),
|
|
433
|
+
config: parsed.config,
|
|
434
|
+
mode: parsed.mode ?? "diff",
|
|
435
|
+
dialect: parsed.dialect,
|
|
436
|
+
y: Boolean(parsed.y),
|
|
437
|
+
yes: Boolean(parsed.yes)
|
|
438
|
+
};
|
|
439
|
+
if (!["safe", "diff", "force"].includes(options.mode)) {
|
|
440
|
+
throw new Error(`[media] Invalid --mode "${String(options.mode)}". Use safe|diff|force.`);
|
|
441
|
+
}
|
|
442
|
+
if (options.dialect && !["postgres", "mysql", "sqlite", "mssql"].includes(options.dialect)) {
|
|
443
|
+
throw new Error(
|
|
444
|
+
`[media] Invalid --dialect "${String(options.dialect)}". Use postgres|mysql|sqlite|mssql.`
|
|
445
|
+
);
|
|
446
|
+
}
|
|
447
|
+
const cwd = path5.resolve(options.cwd);
|
|
448
|
+
if (!existsSync(cwd)) {
|
|
449
|
+
console.error(`[media] The directory "${cwd}" does not exist.`);
|
|
450
|
+
process4.exit(1);
|
|
451
|
+
}
|
|
452
|
+
const loaded = await loadProjectConfig({ cwd, configPath: options.config });
|
|
453
|
+
const adapter = await getAdapter2(loaded.config);
|
|
454
|
+
const dialectFromAdapter = typeof adapter.__getDialect === "function" ? adapter.__getDialect() : void 0;
|
|
455
|
+
const dialect = options.dialect ?? loaded.config.dialect ?? dialectFromAdapter;
|
|
456
|
+
const planned = await getMigrations2(adapter, { mode: options.mode, dialect });
|
|
457
|
+
if (!planned.operations.length) {
|
|
458
|
+
console.log("No migrations needed.");
|
|
459
|
+
process4.exit(0);
|
|
460
|
+
}
|
|
461
|
+
console.log("[media] The migration will affect the following:");
|
|
462
|
+
for (const table of [...planned.toBeCreated, ...planned.toBeAdded]) {
|
|
463
|
+
console.log(
|
|
464
|
+
"->",
|
|
465
|
+
chalk2.magenta(table.fields.join(", ")),
|
|
466
|
+
chalk2.white("fields on"),
|
|
467
|
+
chalk2.yellow(table.table),
|
|
468
|
+
chalk2.white("table.")
|
|
469
|
+
);
|
|
470
|
+
}
|
|
471
|
+
let shouldMigrate = options.yes ?? false;
|
|
472
|
+
if (!shouldMigrate) {
|
|
473
|
+
const rl = createInterface({ input: process4.stdin, output: process4.stdout });
|
|
474
|
+
const answer = await rl.question("Are you sure you want to run these migrations? (y/N) ");
|
|
475
|
+
rl.close();
|
|
476
|
+
shouldMigrate = /^y(es)?$/i.test(answer.trim());
|
|
477
|
+
}
|
|
478
|
+
if (!shouldMigrate) {
|
|
479
|
+
console.log("Migration cancelled.");
|
|
480
|
+
process4.exit(0);
|
|
481
|
+
}
|
|
482
|
+
try {
|
|
483
|
+
await migrateWithAdapter({ adapter, mode: options.mode, dialect });
|
|
484
|
+
} catch (error) {
|
|
485
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
486
|
+
if (loaded.adapterHint === "prisma" || loaded.adapterHint === "drizzle") {
|
|
487
|
+
console.error(message);
|
|
488
|
+
process4.exit(0);
|
|
489
|
+
}
|
|
490
|
+
throw error;
|
|
491
|
+
}
|
|
492
|
+
console.log("Migration completed successfully.");
|
|
493
|
+
});
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
// src/index.ts
|
|
497
|
+
var program = new Command();
|
|
498
|
+
program.name("media").description("CLI for Better Media framework").version("1.0.0");
|
|
499
|
+
registerInitCommand(program);
|
|
500
|
+
registerGenerateCommand(program);
|
|
501
|
+
registerMigrateCommand(program);
|
|
502
|
+
program.command("config").description("Validate and print resolved module exports (debug)").option("-C, --cwd <path>", "Working directory to resolve config from", process5.cwd()).option("--config <path>", "Path to media config file").action(async (opts) => {
|
|
503
|
+
const cwd = path6.resolve(opts.cwd);
|
|
504
|
+
const loaded = await loadProjectConfig({ cwd, configPath: opts.config });
|
|
505
|
+
console.log(
|
|
506
|
+
chalk3.gray("[media] Resolved config:"),
|
|
507
|
+
JSON.stringify(
|
|
508
|
+
{
|
|
509
|
+
configPath: loaded.configPath,
|
|
510
|
+
adapterHint: loaded.adapterHint,
|
|
511
|
+
dialect: loaded.config.dialect,
|
|
512
|
+
schemaOutput: loaded.config.schemaOutput,
|
|
513
|
+
migrationsDir: loaded.config.migrationsDir
|
|
514
|
+
},
|
|
515
|
+
null,
|
|
516
|
+
2
|
|
517
|
+
)
|
|
518
|
+
);
|
|
519
|
+
});
|
|
520
|
+
program.parse(process5.argv);
|
|
521
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/project-config.ts","../src/commands/generate.ts","../src/generators/index.ts","../src/commands/init.ts","../src/commands/migrate.ts"],"sourcesContent":["import { Command } from \"commander\";\nimport chalk from \"chalk\";\nimport path from \"node:path\";\nimport process from \"node:process\";\nimport { loadProjectConfig } from \"./project-config\";\nimport { registerGenerateCommand } from \"./commands/generate\";\nimport { registerInitCommand } from \"./commands/init\";\nimport { registerMigrateCommand } from \"./commands/migrate\";\n\nconst program = new Command();\n\nprogram.name(\"media\").description(\"CLI for Better Media framework\").version(\"1.0.0\");\n\nregisterInitCommand(program);\n\nregisterGenerateCommand(program);\nregisterMigrateCommand(program);\n\nprogram\n .command(\"config\")\n .description(\"Validate and print resolved module exports (debug)\")\n .option(\"-C, --cwd <path>\", \"Working directory to resolve config from\", process.cwd())\n .option(\"--config <path>\", \"Path to media config file\")\n .action(async (opts: { cwd: string; config?: string }) => {\n const cwd = path.resolve(opts.cwd);\n const loaded = await loadProjectConfig({ cwd, configPath: opts.config });\n console.log(\n chalk.gray(\"[media] Resolved config:\"),\n JSON.stringify(\n {\n configPath: loaded.configPath,\n adapterHint: loaded.adapterHint,\n dialect: loaded.config.dialect,\n schemaOutput: loaded.config.schemaOutput,\n migrationsDir: loaded.config.migrationsDir,\n },\n null,\n 2\n )\n );\n });\n\nprogram.parse(process.argv);\n","import fs from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { pathToFileURL } from \"node:url\";\nimport { createRequire } from \"node:module\";\nimport { randomUUID } from \"node:crypto\";\nimport type { GetAdapterOptions } from \"@better-media/core\";\nimport ts from \"typescript\";\n\nexport type AdapterHint = \"kysely\" | \"prisma\" | \"drizzle\" | \"unknown\";\n\nexport type ProjectConfig = GetAdapterOptions & {\n dialect?: string;\n schemaOutput?: string;\n migrationsDir?: string;\n};\n\nexport type LoadedProjectConfig = {\n configPath: string;\n adapterHint: AdapterHint;\n config: ProjectConfig;\n};\n\nasync function fileExists(p: string): Promise<boolean> {\n try {\n await fs.access(p);\n return true;\n } catch {\n return false;\n }\n}\n\nasync function detectAdapterHint(cwd: string): Promise<AdapterHint> {\n try {\n const pkgPath = path.join(cwd, \"package.json\");\n const raw = await fs.readFile(pkgPath, \"utf8\");\n const pkg = JSON.parse(raw) as {\n dependencies?: Record<string, string>;\n devDependencies?: Record<string, string>;\n peerDependencies?: Record<string, string>;\n };\n const deps = {\n ...(pkg.dependencies ?? {}),\n ...(pkg.devDependencies ?? {}),\n ...(pkg.peerDependencies ?? {}),\n };\n if (\"kysely\" in deps) return \"kysely\";\n if (\"prisma\" in deps || \"@prisma/client\" in deps) return \"prisma\";\n if (\"drizzle-orm\" in deps) return \"drizzle\";\n return \"unknown\";\n } catch {\n return \"unknown\";\n }\n}\n\nfunction normalizeModuleExport(mod: unknown): unknown {\n if (!mod) return mod;\n const m = mod as Record<string, unknown>;\n return (m.default ?? mod) as unknown;\n}\n\nasync function importJsModule(absPath: string): Promise<unknown> {\n const url = pathToFileURL(absPath).href;\n const mod = await import(url);\n return normalizeModuleExport(mod);\n}\n\nasync function requireTsModule(absPath: string): Promise<unknown> {\n const require = createRequire(import.meta.url);\n\n try {\n const tsNode = require(\"ts-node\") as {\n register: (opts?: {\n transpileOnly?: boolean;\n compilerOptions?: Record<string, unknown>;\n }) => void;\n };\n tsNode.register({\n transpileOnly: true,\n compilerOptions: { module: \"CommonJS\", moduleResolution: \"Node\" },\n });\n } catch {\n // ignore (we'll surface a clearer error below if require fails)\n }\n\n const mod = require(absPath) as unknown;\n return normalizeModuleExport(mod);\n}\n\nasync function importTsModule(absPath: string): Promise<unknown> {\n const source = await fs.readFile(absPath, \"utf8\");\n const transpiled = ts.transpileModule(source, {\n compilerOptions: {\n target: ts.ScriptTarget.ES2022,\n module: ts.ModuleKind.ESNext,\n moduleResolution: ts.ModuleResolutionKind.Bundler,\n esModuleInterop: true,\n resolveJsonModule: true,\n },\n fileName: absPath,\n });\n\n // Emit beside source so relative imports keep working.\n const tempPath = path.join(path.dirname(absPath), `.media-config-${randomUUID()}.mjs`);\n await fs.writeFile(tempPath, transpiled.outputText, \"utf8\");\n\n try {\n const mod = await import(pathToFileURL(tempPath).href);\n return normalizeModuleExport(mod);\n } finally {\n await fs.unlink(tempPath).catch(() => undefined);\n }\n}\n\nfunction assertValidConfig(\n value: unknown,\n configPath: string\n): asserts value is Record<string, unknown> {\n if (!value || typeof value !== \"object\") {\n throw new Error(`[media] Config \"${configPath}\" must export an object.`);\n }\n}\n\nconst CONFIG_CANDIDATES = [\n \"media.config.ts\",\n \"media.config.js\",\n \"better-media.config.ts\",\n \"better-media.config.js\",\n] as const;\n\nexport async function loadProjectConfig(options?: {\n cwd?: string;\n configPath?: string;\n}): Promise<LoadedProjectConfig> {\n const cwd = path.resolve(options?.cwd ?? process.cwd());\n\n let configPath = options?.configPath ? path.resolve(cwd, options.configPath) : undefined;\n\n if (configPath && !(await fileExists(configPath))) {\n throw new Error(`[media] Config file not found: ${path.relative(cwd, configPath)}`);\n }\n\n if (!configPath) {\n for (const candidate of CONFIG_CANDIDATES) {\n const abs = path.join(cwd, candidate);\n\n if (await fileExists(abs)) {\n configPath = abs;\n break;\n }\n }\n if (!configPath) {\n throw new Error(\n `[media] No configuration file found. Add one of: ${CONFIG_CANDIDATES.map(\n (c) => `\"${c}\"`\n ).join(\", \")}, or pass --config <path>.`\n );\n }\n }\n\n if (!configPath) {\n throw new Error(\n `[media] No configuration file found. Add one of: ${CONFIG_CANDIDATES.map(\n (c) => `\"${c}\"`\n ).join(\", \")}, or pass --config <path>.`\n );\n }\n\n const adapterHint = await detectAdapterHint(cwd);\n const ext = path.extname(configPath);\n\n let rawConfig: unknown;\n try {\n rawConfig =\n ext === \".ts\" ? await requireTsModule(configPath) : await importJsModule(configPath);\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n if (ext === \".ts\") {\n try {\n rawConfig = await importTsModule(configPath);\n } catch {\n throw new Error(\n `[media] Failed to load \"${path.basename(\n configPath\n )}\". For TypeScript configs, install \"ts-node\" in your app or use a .js config. Original error: ${msg}`\n );\n }\n } else {\n throw err;\n }\n }\n\n assertValidConfig(rawConfig, configPath);\n\n const cfg = rawConfig as Record<string, unknown>;\n const dialect = typeof cfg.dialect === \"string\" ? (cfg.dialect as string) : undefined;\n const schemaOutput =\n typeof cfg.schemaOutput === \"string\" ? (cfg.schemaOutput as string) : undefined;\n const migrationsDir =\n typeof cfg.migrationsDir === \"string\" ? (cfg.migrationsDir as string) : undefined;\n\n return {\n configPath,\n adapterHint,\n config: {\n database: cfg.database as ProjectConfig[\"database\"],\n createDatabase: cfg.createDatabase as ProjectConfig[\"createDatabase\"],\n dialect,\n schemaOutput,\n migrationsDir,\n },\n };\n}\n","import { Command } from \"commander\";\nimport path from \"node:path\";\nimport process from \"node:process\";\nimport { getAdapter, type SqlDialect } from \"@better-media/core\";\nimport { loadProjectConfig } from \"../project-config\";\nimport { generateSchema } from \"../generators\";\n\n/** Default path whose directory is the migrations root (filename unused for output). */\nfunction getDefaultSchemaOutput(): string {\n return \"better-media-migrations/migration.sql\";\n}\n\nexport function registerGenerateCommand(program: Command): void {\n program\n .command(\"generate\")\n .description(\"Generate Better Media database schema for the configured adapter\")\n .option(\"-C, --cwd <path>\", \"Working directory to resolve config from\", process.cwd())\n .option(\"--config <path>\", \"Path to media config file\")\n .option(\n \"--dialect <dialect>\",\n \"SQL dialect (postgres|mysql|sqlite|mssql). Defaults from adapter/config.\",\n undefined\n )\n .action(async (opts: { cwd: string; config?: string; dialect?: SqlDialect }) => {\n const cwd = path.resolve(opts.cwd);\n const loaded = await loadProjectConfig({ cwd, configPath: opts.config });\n const adapter = await getAdapter(loaded.config);\n\n const dialectFromAdapter =\n typeof (adapter as unknown as { __getDialect?: () => SqlDialect }).__getDialect ===\n \"function\"\n ? (adapter as unknown as { __getDialect: () => SqlDialect }).__getDialect()\n : undefined;\n const dialect =\n opts.dialect ??\n (loaded.config.dialect as SqlDialect | undefined) ??\n dialectFromAdapter ??\n \"postgres\";\n\n const outPath = path.resolve(cwd, loaded.config.schemaOutput ?? getDefaultSchemaOutput());\n await generateSchema({\n cwd,\n adapter,\n outPath,\n dialect,\n options: loaded.config,\n });\n });\n}\n","import path from \"node:path\";\nimport fs from \"fs-extra\";\nimport chalk from \"chalk\";\nimport {\n schema,\n getMigrations,\n MigrationPlanner,\n compileMigrationOperationsSql,\n type SqlDialect,\n type MigrationOptions,\n type TableMetadata,\n applyOperationsToMetadata,\n} from \"@better-media/core\";\nimport type { DatabaseAdapter } from \"@better-media/core\";\n\ntype CustomSchemaResult = {\n code: string;\n fileName?: string;\n path?: string;\n overwrite?: boolean;\n};\n\ntype AdapterWithExtras = DatabaseAdapter & {\n id?: string;\n createSchema?: (options: unknown, file?: string) => Promise<CustomSchemaResult>;\n};\n\ntype GenerateFn = (args: {\n cwd: string;\n adapter: DatabaseAdapter;\n outPath: string;\n dialect: SqlDialect;\n options?: unknown;\n}) => Promise<void>;\n\ntype MigrateFn = (args: {\n adapter: DatabaseAdapter;\n mode: MigrationOptions[\"mode\"];\n dialect?: SqlDialect;\n}) => Promise<void>;\n\nfunction resolveMigrationsDir(cwd: string, outPath: string, options: unknown): string {\n const opts = options as { migrationsDir?: string } | undefined;\n if (opts?.migrationsDir && typeof opts.migrationsDir === \"string\") {\n return path.resolve(cwd, opts.migrationsDir);\n }\n return path.dirname(outPath);\n}\n\n/** Subfolders named with numeric timestamps (ms) contain `migration.sql` + `snapshot.json` per run. */\nconst TIMESTAMP_DIR = /^\\d{10,20}$/;\n\nasync function findLatestTimestampRunDir(migrationsDir: string): Promise<string | undefined> {\n if (!(await fs.pathExists(migrationsDir))) return undefined;\n const entries = await fs.readdir(migrationsDir, { withFileTypes: true });\n const names = entries\n .filter((e) => e.isDirectory() && TIMESTAMP_DIR.test(e.name))\n .map((e) => e.name);\n const latest = names[0];\n return latest ? path.join(migrationsDir, latest) : undefined;\n}\n\nasync function resolveSnapshotReadPath(migrationsDir: string): Promise<string | undefined> {\n const latestRun = await findLatestTimestampRunDir(migrationsDir);\n const nested = latestRun ? path.join(latestRun, \"snapshot.json\") : undefined;\n if (nested && (await fs.pathExists(nested))) return nested;\n const legacyRoot = path.join(migrationsDir, \"snapshot.json\");\n if (await fs.pathExists(legacyRoot)) return legacyRoot;\n return undefined;\n}\n\nconst builtInGenerators: Record<string, GenerateFn> = {\n async kysely({ cwd, outPath, dialect, adapter, options }) {\n const migrationsDir = resolveMigrationsDir(cwd, outPath, options);\n await fs.ensureDir(migrationsDir);\n\n let currentTables: TableMetadata[] = [];\n let snapshotLoaded = false;\n\n const snapshotReadPath = await resolveSnapshotReadPath(migrationsDir);\n if (snapshotReadPath) {\n try {\n currentTables = await fs.readJson(snapshotReadPath);\n snapshotLoaded = true;\n } catch {\n console.warn(\n chalk.yellow(\n `[media] Failed to load snapshot at ${snapshotReadPath}, falling back to DB introspection.`\n )\n );\n }\n }\n\n if (!snapshotLoaded) {\n if (typeof (adapter as { __getMetadata?: unknown }).__getMetadata === \"function\") {\n currentTables = await (\n adapter as unknown as { __getMetadata: () => Promise<TableMetadata[]> }\n ).__getMetadata();\n }\n }\n\n const planner = new MigrationPlanner(dialect);\n const plannedOperations = planner.plan(schema, currentTables);\n\n if (plannedOperations.length === 0) {\n console.log(\n chalk.blue(`[media] No changes detected. Database/Snapshot is already up to date.`)\n );\n return;\n }\n\n const sql = compileMigrationOperationsSql({ operations: plannedOperations, dialect });\n\n const nextMetadata = applyOperationsToMetadata(currentTables, plannedOperations, dialect);\n const runTs = Date.now();\n const runDir = path.join(migrationsDir, String(runTs));\n await fs.ensureDir(runDir);\n\n const snapshotWritePath = path.join(runDir, \"snapshot.json\");\n await fs.writeJson(snapshotWritePath, nextMetadata, { spaces: 2 });\n\n const migrationFile = path.join(runDir, \"migration.sql\");\n await fs.writeFile(migrationFile, sql, \"utf8\");\n console.log(chalk.green(`[media] Generated migration at ${path.relative(cwd, migrationFile)}`));\n },\n async postgres(args) {\n const kysely = builtInGenerators.kysely;\n if (!kysely) throw new Error(\"Kysely generator not found\");\n await kysely(args);\n },\n async prisma() {\n throw new Error(\n '[media] Prisma schema generator is not implemented yet. Run \"npx better-media generate\" with Kysely, then apply via Prisma migrate/push.'\n );\n },\n async drizzle() {\n throw new Error(\n '[media] Drizzle schema generator is not implemented yet. Run \"npx better-media generate\" with Kysely, then apply via Drizzle migrate/push.'\n );\n },\n};\n\nconst builtInMigrators: Record<string, MigrateFn> = {\n async kysely({ mode, dialect, adapter }) {\n const hasPlannedMigration =\n typeof (adapter as { __getMetadata?: unknown }).__getMetadata === \"function\" &&\n typeof (adapter as { __executeMigration?: unknown }).__executeMigration === \"function\";\n\n if (!hasPlannedMigration) {\n throw new Error(\n `[media] This adapter does not support direct CLI migrations. ` +\n `Use \"media generate\" and apply via your ORM migration tool.`\n );\n }\n const planned = await getMigrations(adapter, { mode, dialect });\n await planned.runMigrations();\n },\n async postgres(args) {\n const kysely = builtInMigrators.kysely;\n if (!kysely) throw new Error(\"Kysely migrator not found\");\n await kysely(args);\n },\n async prisma() {\n throw new Error(\n '[media] The migrate command only works with the built-in Kysely adapter. For Prisma, run \"npx better-media generate\" and apply with Prisma migrate/push.'\n );\n },\n async drizzle() {\n throw new Error(\n '[media] The migrate command only works with the built-in Kysely adapter. For Drizzle, run \"npx better-media generate\" and apply with Drizzle migrate/push.'\n );\n },\n};\n\nexport async function generateSchema(args: {\n cwd: string;\n adapter: DatabaseAdapter;\n outPath: string;\n dialect: SqlDialect;\n options?: unknown;\n}): Promise<void> {\n const adapterEx = args.adapter as AdapterWithExtras;\n const id = adapterEx.id;\n\n if (id && id in builtInGenerators) {\n await builtInGenerators[id]!(args);\n return;\n }\n\n if (typeof adapterEx.createSchema === \"function\") {\n const generated = await adapterEx.createSchema(args.options, args.outPath);\n const targetPath = generated.fileName ?? generated.path ?? args.outPath;\n const overwrite = generated.overwrite ?? true;\n if (!overwrite && (await fs.pathExists(targetPath))) {\n throw new Error(`[media] Refusing to overwrite existing file: ${targetPath}`);\n }\n await fs.ensureDir(path.dirname(targetPath));\n await fs.writeFile(targetPath, generated.code, \"utf8\");\n console.log(chalk.green(`[media] Generated schema at ${path.relative(args.cwd, targetPath)}`));\n return;\n }\n\n throw new Error(\n `[media] ${id ?? \"adapter\"} is not supported. If it is a custom adapter, implement createSchema(options, file).`\n );\n}\n\nexport async function migrateWithAdapter(args: {\n adapter: DatabaseAdapter;\n mode: MigrationOptions[\"mode\"];\n dialect?: SqlDialect;\n}): Promise<void> {\n const adapterEx = args.adapter as AdapterWithExtras;\n const id = adapterEx.id;\n\n if (id && id in builtInMigrators) {\n await builtInMigrators[id]!(args);\n return;\n }\n\n if (\n typeof (args.adapter as { __getMetadata?: unknown }).__getMetadata === \"function\" &&\n typeof (args.adapter as { __executeMigration?: unknown }).__executeMigration === \"function\"\n ) {\n const kysely = builtInMigrators.kysely;\n if (!kysely) throw new Error(\"Kysely migrator not found\");\n await kysely(args);\n return;\n }\n\n throw new Error(`[media] Direct migrations are not supported for adapter \"${id ?? \"unknown\"}\".`);\n}\n","import { Command } from \"commander\";\nimport path from \"node:path\";\nimport process from \"node:process\";\nimport fs from \"fs-extra\";\n\ntype DetectedFramework = \"express\" | \"nestjs\" | \"unknown\";\n\nfunction detectFramework(pkg: {\n dependencies?: Record<string, string>;\n devDependencies?: Record<string, string>;\n}): DetectedFramework {\n const deps = { ...(pkg.dependencies ?? {}), ...(pkg.devDependencies ?? {}) };\n if (\"@nestjs/core\" in deps || \"@nestjs/common\" in deps) return \"nestjs\";\n if (\"express\" in deps) return \"express\";\n return \"unknown\";\n}\n\nfunction inferExtension(cwd: string, framework: DetectedFramework, pkgType?: string): \"ts\" | \"js\" {\n if (framework === \"nestjs\") return \"ts\";\n if (pkgType === \"module\") return \"js\";\n return \"ts\";\n}\n\nfunction buildConfigContent(ext: \"ts\" | \"js\"): string {\n const importPool = ext === \"ts\" ? 'import { Pool } from \"pg\";' : 'import { Pool } from \"pg\";';\n return `${importPool}\n\nexport const mediaOptions = {\n database: new Pool({\n connectionString:\n process.env.DATABASE_URL ?? \"postgres://postgres:postgres@localhost:5432/better_media\",\n }),\n dialect: \"postgres\",\n migrationsDir: \"better-media\",\n};\n\nexport default mediaOptions;\n`;\n}\n\nexport function registerInitCommand(program: Command): void {\n program\n .command(\"init\")\n .description(\"Generate media.config.ts/js based on detected framework\")\n .option(\"-C, --cwd <path>\", \"Working directory\", process.cwd())\n .option(\"-f, --force\", \"Overwrite existing config file\", false)\n .action(async (opts: { cwd: string; force?: boolean }) => {\n const cwd = path.resolve(opts.cwd);\n const pkgPath = path.join(cwd, \"package.json\");\n if (!(await fs.pathExists(pkgPath))) {\n throw new Error(`[media] No package.json found in ${cwd}`);\n }\n\n const pkg = (await fs.readJson(pkgPath)) as {\n type?: string;\n dependencies?: Record<string, string>;\n devDependencies?: Record<string, string>;\n };\n\n const framework = detectFramework(pkg);\n const ext = inferExtension(cwd, framework, pkg.type);\n const configFile = path.join(cwd, `media.config.${ext}`);\n\n if ((await fs.pathExists(configFile)) && !opts.force) {\n throw new Error(\n `[media] ${path.basename(configFile)} already exists. Re-run with --force to overwrite.`\n );\n }\n\n const content = buildConfigContent(ext);\n await fs.writeFile(configFile, content, \"utf8\");\n\n const deps = { ...(pkg.dependencies ?? {}), ...(pkg.devDependencies ?? {}) };\n if (!(\"pg\" in deps)) {\n console.warn('[media] Added config uses \"pg\". Install it with: pnpm add pg');\n }\n\n console.log(\n `[media] Created ${path.basename(configFile)} (${framework === \"unknown\" ? \"generic\" : framework} template)`\n );\n });\n}\n","import { Command } from \"commander\";\nimport chalk from \"chalk\";\nimport path from \"node:path\";\nimport process from \"node:process\";\nimport { existsSync } from \"node:fs\";\nimport { createInterface } from \"node:readline/promises\";\nimport {\n getAdapter,\n getMigrations,\n type MigrationOptions,\n type SqlDialect,\n} from \"@better-media/core\";\nimport { loadProjectConfig } from \"../project-config\";\nimport { migrateWithAdapter } from \"../generators\";\n\nexport function registerMigrateCommand(program: Command): void {\n program\n .command(\"migrate\")\n .description(\"Apply Better Media schema directly to the database (Kysely adapter only)\")\n .option(\"-C, --cwd <path>\", \"Working directory to resolve config from\", process.cwd())\n .option(\"--config <path>\", \"Path to media config file\")\n .option(\"--mode <mode>\", \"Migration mode (safe|diff|force)\", \"diff\")\n .option(\"-y, --yes\", \"Automatically accept and run migrations without prompting\", false)\n .option(\n \"--dialect <dialect>\",\n \"SQL dialect override (postgres|mysql|sqlite|mssql). Defaults from adapter/config.\",\n undefined\n )\n .action(async (opts: unknown) => {\n const parsed = opts as {\n cwd?: string;\n config?: string;\n mode?: MigrationOptions[\"mode\"];\n dialect?: SqlDialect;\n y?: boolean;\n yes?: boolean;\n };\n const options = {\n cwd: parsed.cwd ?? process.cwd(),\n config: parsed.config,\n mode: parsed.mode ?? \"diff\",\n dialect: parsed.dialect,\n y: Boolean(parsed.y),\n yes: Boolean(parsed.yes),\n };\n if (![\"safe\", \"diff\", \"force\"].includes(options.mode)) {\n throw new Error(`[media] Invalid --mode \"${String(options.mode)}\". Use safe|diff|force.`);\n }\n if (options.dialect && ![\"postgres\", \"mysql\", \"sqlite\", \"mssql\"].includes(options.dialect)) {\n throw new Error(\n `[media] Invalid --dialect \"${String(options.dialect)}\". Use postgres|mysql|sqlite|mssql.`\n );\n }\n\n const cwd = path.resolve(options.cwd);\n if (!existsSync(cwd)) {\n console.error(`[media] The directory \"${cwd}\" does not exist.`);\n process.exit(1);\n }\n\n const loaded = await loadProjectConfig({ cwd, configPath: options.config });\n const adapter = await getAdapter(loaded.config);\n\n const dialectFromAdapter =\n typeof (adapter as unknown as { __getDialect?: () => SqlDialect }).__getDialect ===\n \"function\"\n ? (adapter as unknown as { __getDialect: () => SqlDialect }).__getDialect()\n : undefined;\n const dialect =\n options.dialect ?? (loaded.config.dialect as SqlDialect | undefined) ?? dialectFromAdapter;\n\n const planned = await getMigrations(adapter, { mode: options.mode, dialect });\n if (!planned.operations.length) {\n console.log(\"No migrations needed.\");\n process.exit(0);\n }\n\n console.log(\"[media] The migration will affect the following:\");\n for (const table of [...planned.toBeCreated, ...planned.toBeAdded]) {\n console.log(\n \"->\",\n chalk.magenta(table.fields.join(\", \")),\n chalk.white(\"fields on\"),\n chalk.yellow(table.table),\n chalk.white(\"table.\")\n );\n }\n\n let shouldMigrate = options.yes ?? false;\n if (!shouldMigrate) {\n const rl = createInterface({ input: process.stdin, output: process.stdout });\n const answer = await rl.question(\"Are you sure you want to run these migrations? (y/N) \");\n rl.close();\n shouldMigrate = /^y(es)?$/i.test(answer.trim());\n }\n\n if (!shouldMigrate) {\n console.log(\"Migration cancelled.\");\n process.exit(0);\n }\n\n try {\n await migrateWithAdapter({ adapter, mode: options.mode, dialect });\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n if (loaded.adapterHint === \"prisma\" || loaded.adapterHint === \"drizzle\") {\n console.error(message);\n process.exit(0);\n }\n throw error;\n }\n console.log(\"Migration completed successfully.\");\n });\n}\n"],"mappings":"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","names":["chalk","path","process","require","path","process","path","fs","program","process","path","path","process","fs","program","chalk","path","process","getAdapter","getMigrations","program","process","path","getAdapter","getMigrations","chalk","process","path","chalk"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@better-media/cli",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"private": false,
|
|
5
|
+
"author": "Abenezer Atnafu",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/AbenezerAtnafu/better-media.git"
|
|
9
|
+
},
|
|
10
|
+
"homepage": "https://better-media.dev",
|
|
11
|
+
"bugs": {
|
|
12
|
+
"url": "https://github.com/AbenezerAtnafu/better-media/issues"
|
|
13
|
+
},
|
|
14
|
+
"publishConfig": {
|
|
15
|
+
"access": "public"
|
|
16
|
+
},
|
|
17
|
+
"description": "CLI for Better Media framework",
|
|
18
|
+
"main": "dist/index.js",
|
|
19
|
+
"bin": {
|
|
20
|
+
"better-media": "dist/index.js"
|
|
21
|
+
},
|
|
22
|
+
"type": "module",
|
|
23
|
+
"files": [
|
|
24
|
+
"dist",
|
|
25
|
+
"README.md"
|
|
26
|
+
],
|
|
27
|
+
"dependencies": {
|
|
28
|
+
"commander": "^12.1.0",
|
|
29
|
+
"chalk": "^5.3.0",
|
|
30
|
+
"fs-extra": "^11.2.0",
|
|
31
|
+
"typescript": "^5.9.3",
|
|
32
|
+
"ts-node": "^10.9.2",
|
|
33
|
+
"@better-media/core": "0.1.0"
|
|
34
|
+
},
|
|
35
|
+
"devDependencies": {
|
|
36
|
+
"@types/fs-extra": "^11.0.4",
|
|
37
|
+
"@types/node": "^22.10.0",
|
|
38
|
+
"tsup": "^8.5.1"
|
|
39
|
+
},
|
|
40
|
+
"scripts": {
|
|
41
|
+
"build": "tsup",
|
|
42
|
+
"dev": "tsup --watch",
|
|
43
|
+
"typecheck": "tsc --noEmit",
|
|
44
|
+
"lint": "eslint ."
|
|
45
|
+
}
|
|
46
|
+
}
|