@famgia/omnify-cli 0.0.1
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/dist/index.cjs +733 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +30 -0
- package/dist/index.d.ts +30 -0
- package/dist/index.js +701 -0
- package/dist/index.js.map +1 -0
- package/package.json +38 -0
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,733 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
#!/usr/bin/env node
|
|
3
|
+
"use strict";
|
|
4
|
+
var __create = Object.create;
|
|
5
|
+
var __defProp = Object.defineProperty;
|
|
6
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
7
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
8
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
9
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
10
|
+
var __export = (target, all) => {
|
|
11
|
+
for (var name in all)
|
|
12
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
13
|
+
};
|
|
14
|
+
var __copyProps = (to, from, except, desc) => {
|
|
15
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
16
|
+
for (let key of __getOwnPropNames(from))
|
|
17
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
18
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
19
|
+
}
|
|
20
|
+
return to;
|
|
21
|
+
};
|
|
22
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
23
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
24
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
25
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
26
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
27
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
28
|
+
mod
|
|
29
|
+
));
|
|
30
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
31
|
+
|
|
32
|
+
// src/index.ts
|
|
33
|
+
var index_exports = {};
|
|
34
|
+
__export(index_exports, {
|
|
35
|
+
defineConfig: () => defineConfig,
|
|
36
|
+
loadConfig: () => loadConfig
|
|
37
|
+
});
|
|
38
|
+
module.exports = __toCommonJS(index_exports);
|
|
39
|
+
var import_commander = require("commander");
|
|
40
|
+
var import_omnify_core6 = require("@famgia/omnify-core");
|
|
41
|
+
|
|
42
|
+
// src/commands/init.ts
|
|
43
|
+
var import_node_fs = require("fs");
|
|
44
|
+
var import_node_path = require("path");
|
|
45
|
+
|
|
46
|
+
// src/output/logger.ts
|
|
47
|
+
var import_picocolors = __toESM(require("picocolors"), 1);
|
|
48
|
+
var import_omnify_core = require("@famgia/omnify-core");
|
|
49
|
+
var Logger = class {
|
|
50
|
+
_verbose;
|
|
51
|
+
_quiet;
|
|
52
|
+
_startTime;
|
|
53
|
+
constructor(options = {}) {
|
|
54
|
+
this._verbose = options.verbose ?? false;
|
|
55
|
+
this._quiet = options.quiet ?? false;
|
|
56
|
+
this._startTime = Date.now();
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Enable or disable verbose mode.
|
|
60
|
+
*/
|
|
61
|
+
setVerbose(verbose) {
|
|
62
|
+
this._verbose = verbose;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Enable or disable quiet mode.
|
|
66
|
+
*/
|
|
67
|
+
setQuiet(quiet) {
|
|
68
|
+
this._quiet = quiet;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Log an info message.
|
|
72
|
+
*/
|
|
73
|
+
info(message) {
|
|
74
|
+
if (!this._quiet) {
|
|
75
|
+
console.log(message);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Log a success message.
|
|
80
|
+
*/
|
|
81
|
+
success(message) {
|
|
82
|
+
if (!this._quiet) {
|
|
83
|
+
console.log(import_picocolors.default.green("\u2713") + " " + message);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Log a warning message.
|
|
88
|
+
*/
|
|
89
|
+
warn(message) {
|
|
90
|
+
if (!this._quiet) {
|
|
91
|
+
console.log(import_picocolors.default.yellow("\u26A0") + " " + import_picocolors.default.yellow(message));
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Log an error message.
|
|
96
|
+
*/
|
|
97
|
+
error(message) {
|
|
98
|
+
console.error(import_picocolors.default.red("\u2717") + " " + import_picocolors.default.red(message));
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Log a debug message (only in verbose mode).
|
|
102
|
+
*/
|
|
103
|
+
debug(message) {
|
|
104
|
+
if (this._verbose && !this._quiet) {
|
|
105
|
+
console.log(import_picocolors.default.dim(" " + message));
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Log a step message.
|
|
110
|
+
*/
|
|
111
|
+
step(message) {
|
|
112
|
+
if (!this._quiet) {
|
|
113
|
+
console.log(import_picocolors.default.cyan("\u2192") + " " + message);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Log a header.
|
|
118
|
+
*/
|
|
119
|
+
header(message) {
|
|
120
|
+
if (!this._quiet) {
|
|
121
|
+
console.log();
|
|
122
|
+
console.log(import_picocolors.default.bold(message));
|
|
123
|
+
console.log();
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Log a list item.
|
|
128
|
+
*/
|
|
129
|
+
list(items) {
|
|
130
|
+
if (!this._quiet) {
|
|
131
|
+
for (const item of items) {
|
|
132
|
+
console.log(" \u2022 " + item);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Log a timing message.
|
|
138
|
+
*/
|
|
139
|
+
timing(message) {
|
|
140
|
+
if (this._verbose && !this._quiet) {
|
|
141
|
+
const elapsed = Date.now() - this._startTime;
|
|
142
|
+
console.log(import_picocolors.default.dim(` [${elapsed}ms] ${message}`));
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Log an empty line.
|
|
147
|
+
*/
|
|
148
|
+
newline() {
|
|
149
|
+
if (!this._quiet) {
|
|
150
|
+
console.log();
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Format and log an OmnifyError.
|
|
155
|
+
*/
|
|
156
|
+
formatError(error) {
|
|
157
|
+
const formatted = (0, import_omnify_core.formatError)(error, { color: true });
|
|
158
|
+
console.error(formatted);
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Get exit code for an error.
|
|
162
|
+
*/
|
|
163
|
+
getExitCode(error) {
|
|
164
|
+
return (0, import_omnify_core.getExitCode)(error);
|
|
165
|
+
}
|
|
166
|
+
};
|
|
167
|
+
var logger = new Logger();
|
|
168
|
+
|
|
169
|
+
// src/commands/init.ts
|
|
170
|
+
var EXAMPLE_SCHEMA = `# Example User schema
|
|
171
|
+
# See https://omnify.dev/docs/schemas for documentation
|
|
172
|
+
|
|
173
|
+
name: User
|
|
174
|
+
displayName: User Account
|
|
175
|
+
kind: object
|
|
176
|
+
|
|
177
|
+
properties:
|
|
178
|
+
email:
|
|
179
|
+
type: Email
|
|
180
|
+
unique: true
|
|
181
|
+
displayName: Email Address
|
|
182
|
+
|
|
183
|
+
name:
|
|
184
|
+
type: String
|
|
185
|
+
displayName: Full Name
|
|
186
|
+
|
|
187
|
+
bio:
|
|
188
|
+
type: Text
|
|
189
|
+
nullable: true
|
|
190
|
+
displayName: Biography
|
|
191
|
+
|
|
192
|
+
options:
|
|
193
|
+
timestamps: true
|
|
194
|
+
softDelete: true
|
|
195
|
+
`;
|
|
196
|
+
var EXAMPLE_CONFIG = `import { defineConfig } from '@famgia/omnify-cli';
|
|
197
|
+
|
|
198
|
+
export default defineConfig({
|
|
199
|
+
// Schema files location
|
|
200
|
+
schemasDir: './schemas',
|
|
201
|
+
|
|
202
|
+
// Database configuration
|
|
203
|
+
database: {
|
|
204
|
+
driver: 'mysql',
|
|
205
|
+
// Development database URL for Atlas diff operations
|
|
206
|
+
// devUrl: 'mysql://root@localhost:3306/omnify_dev',
|
|
207
|
+
},
|
|
208
|
+
|
|
209
|
+
// Output configuration
|
|
210
|
+
output: {
|
|
211
|
+
laravel: {
|
|
212
|
+
migrationsPath: 'database/migrations',
|
|
213
|
+
},
|
|
214
|
+
typescript: {
|
|
215
|
+
path: 'types',
|
|
216
|
+
singleFile: true,
|
|
217
|
+
},
|
|
218
|
+
},
|
|
219
|
+
|
|
220
|
+
// Plugins for custom types
|
|
221
|
+
plugins: [
|
|
222
|
+
// '@famgia/omnify-japan',
|
|
223
|
+
],
|
|
224
|
+
});
|
|
225
|
+
`;
|
|
226
|
+
async function runInit(options) {
|
|
227
|
+
const cwd = process.cwd();
|
|
228
|
+
logger.header("Initializing Omnify Project");
|
|
229
|
+
const configPath = (0, import_node_path.resolve)(cwd, "omnify.config.ts");
|
|
230
|
+
if ((0, import_node_fs.existsSync)(configPath) && !options.force) {
|
|
231
|
+
logger.warn("omnify.config.ts already exists. Use --force to overwrite.");
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
234
|
+
const schemasDir = (0, import_node_path.resolve)(cwd, "schemas");
|
|
235
|
+
if (!(0, import_node_fs.existsSync)(schemasDir)) {
|
|
236
|
+
(0, import_node_fs.mkdirSync)(schemasDir, { recursive: true });
|
|
237
|
+
logger.step("Created schemas/ directory");
|
|
238
|
+
} else {
|
|
239
|
+
logger.debug("schemas/ directory already exists");
|
|
240
|
+
}
|
|
241
|
+
const examplePath = (0, import_node_path.resolve)(schemasDir, "User.yaml");
|
|
242
|
+
if (!(0, import_node_fs.existsSync)(examplePath) || options.force) {
|
|
243
|
+
(0, import_node_fs.writeFileSync)(examplePath, EXAMPLE_SCHEMA);
|
|
244
|
+
logger.step("Created example schema: schemas/User.yaml");
|
|
245
|
+
} else {
|
|
246
|
+
logger.debug("Example schema already exists");
|
|
247
|
+
}
|
|
248
|
+
(0, import_node_fs.writeFileSync)(configPath, EXAMPLE_CONFIG);
|
|
249
|
+
logger.step("Created omnify.config.ts");
|
|
250
|
+
logger.newline();
|
|
251
|
+
logger.success("Project initialized successfully!");
|
|
252
|
+
logger.newline();
|
|
253
|
+
logger.info("Next steps:");
|
|
254
|
+
logger.list([
|
|
255
|
+
"Edit omnify.config.ts to configure your database",
|
|
256
|
+
"Add your schema files to the schemas/ directory",
|
|
257
|
+
'Run "omnify validate" to check your schemas',
|
|
258
|
+
'Run "omnify diff" to preview changes',
|
|
259
|
+
'Run "omnify generate" to create migrations and types'
|
|
260
|
+
]);
|
|
261
|
+
}
|
|
262
|
+
function registerInitCommand(program2) {
|
|
263
|
+
program2.command("init").description("Initialize a new omnify project").option("-f, --force", "Overwrite existing files").action(async (options) => {
|
|
264
|
+
try {
|
|
265
|
+
await runInit(options);
|
|
266
|
+
} catch (error) {
|
|
267
|
+
if (error instanceof Error) {
|
|
268
|
+
logger.error(error.message);
|
|
269
|
+
}
|
|
270
|
+
process.exit(1);
|
|
271
|
+
}
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
// src/commands/validate.ts
|
|
276
|
+
var import_node_path3 = require("path");
|
|
277
|
+
var import_omnify_core3 = require("@famgia/omnify-core");
|
|
278
|
+
|
|
279
|
+
// src/config/loader.ts
|
|
280
|
+
var import_node_fs2 = require("fs");
|
|
281
|
+
var import_node_path2 = require("path");
|
|
282
|
+
var import_jiti = require("jiti");
|
|
283
|
+
var import_omnify_core2 = require("@famgia/omnify-core");
|
|
284
|
+
var CONFIG_FILES = [
|
|
285
|
+
"omnify.config.ts",
|
|
286
|
+
"omnify.config.js",
|
|
287
|
+
"omnify.config.mjs",
|
|
288
|
+
"omnify.config.cjs"
|
|
289
|
+
];
|
|
290
|
+
function findConfigFile(startDir) {
|
|
291
|
+
const cwd = (0, import_node_path2.resolve)(startDir);
|
|
292
|
+
for (const filename of CONFIG_FILES) {
|
|
293
|
+
const configPath = (0, import_node_path2.resolve)(cwd, filename);
|
|
294
|
+
if ((0, import_node_fs2.existsSync)(configPath)) {
|
|
295
|
+
return configPath;
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
return null;
|
|
299
|
+
}
|
|
300
|
+
async function loadConfigFile(configPath) {
|
|
301
|
+
const jiti = (0, import_jiti.createJiti)(configPath, {
|
|
302
|
+
interopDefault: true,
|
|
303
|
+
moduleCache: false
|
|
304
|
+
});
|
|
305
|
+
try {
|
|
306
|
+
const module2 = await jiti.import(configPath);
|
|
307
|
+
const config = module2;
|
|
308
|
+
if ("default" in config) {
|
|
309
|
+
return config.default;
|
|
310
|
+
}
|
|
311
|
+
return config;
|
|
312
|
+
} catch (error) {
|
|
313
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
314
|
+
throw (0, import_omnify_core2.configError)(
|
|
315
|
+
`Failed to load config file: ${message}. Check your omnify.config.ts for syntax errors.`,
|
|
316
|
+
"E002"
|
|
317
|
+
);
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
async function resolvePlugins(plugins, configPath) {
|
|
321
|
+
if (!plugins || plugins.length === 0) {
|
|
322
|
+
return [];
|
|
323
|
+
}
|
|
324
|
+
const resolved = [];
|
|
325
|
+
const configDir = configPath ? (0, import_node_path2.dirname)(configPath) : process.cwd();
|
|
326
|
+
for (const plugin of plugins) {
|
|
327
|
+
if (typeof plugin === "string") {
|
|
328
|
+
const jiti = (0, import_jiti.createJiti)(configDir, {
|
|
329
|
+
interopDefault: true,
|
|
330
|
+
moduleCache: false
|
|
331
|
+
});
|
|
332
|
+
try {
|
|
333
|
+
const module2 = await jiti.import(plugin);
|
|
334
|
+
const loadedPlugin = module2.default ?? module2;
|
|
335
|
+
resolved.push(loadedPlugin);
|
|
336
|
+
} catch (error) {
|
|
337
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
338
|
+
throw (0, import_omnify_core2.configError)(
|
|
339
|
+
`Failed to load plugin '${plugin}': ${message}. Ensure the plugin is installed: npm install ${plugin}`,
|
|
340
|
+
"E301"
|
|
341
|
+
);
|
|
342
|
+
}
|
|
343
|
+
} else {
|
|
344
|
+
resolved.push(plugin);
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
return resolved;
|
|
348
|
+
}
|
|
349
|
+
async function resolveConfig(userConfig, configPath) {
|
|
350
|
+
const plugins = await resolvePlugins(userConfig.plugins, configPath);
|
|
351
|
+
const databaseConfig = {
|
|
352
|
+
driver: userConfig.database.driver,
|
|
353
|
+
enableFieldComments: userConfig.database.enableFieldComments ?? false
|
|
354
|
+
};
|
|
355
|
+
const database = userConfig.database.devUrl !== void 0 ? { ...databaseConfig, devUrl: userConfig.database.devUrl } : databaseConfig;
|
|
356
|
+
const laravelConfig = {
|
|
357
|
+
migrationsPath: userConfig.output?.laravel?.migrationsPath ?? "database/migrations"
|
|
358
|
+
};
|
|
359
|
+
const laravel = buildLaravelConfig(laravelConfig, userConfig.output?.laravel);
|
|
360
|
+
const typescript = {
|
|
361
|
+
path: userConfig.output?.typescript?.path ?? "types",
|
|
362
|
+
singleFile: userConfig.output?.typescript?.singleFile ?? true,
|
|
363
|
+
generateEnums: userConfig.output?.typescript?.generateEnums ?? true,
|
|
364
|
+
generateRelationships: userConfig.output?.typescript?.generateRelationships ?? true
|
|
365
|
+
};
|
|
366
|
+
const result = {
|
|
367
|
+
schemasDir: userConfig.schemasDir ?? "./schemas",
|
|
368
|
+
database,
|
|
369
|
+
output: {
|
|
370
|
+
laravel,
|
|
371
|
+
typescript
|
|
372
|
+
},
|
|
373
|
+
plugins,
|
|
374
|
+
verbose: userConfig.verbose ?? false,
|
|
375
|
+
lockFilePath: userConfig.lockFilePath ?? ".omnify.lock"
|
|
376
|
+
};
|
|
377
|
+
return result;
|
|
378
|
+
}
|
|
379
|
+
function buildLaravelConfig(base, userLaravel) {
|
|
380
|
+
const config = { ...base };
|
|
381
|
+
if (userLaravel?.modelsPath !== void 0) {
|
|
382
|
+
config.modelsPath = userLaravel.modelsPath;
|
|
383
|
+
}
|
|
384
|
+
if (userLaravel?.modelsNamespace !== void 0) {
|
|
385
|
+
config.modelsNamespace = userLaravel.modelsNamespace;
|
|
386
|
+
}
|
|
387
|
+
if (userLaravel?.factoriesPath !== void 0) {
|
|
388
|
+
config.factoriesPath = userLaravel.factoriesPath;
|
|
389
|
+
}
|
|
390
|
+
if (userLaravel?.enumsPath !== void 0) {
|
|
391
|
+
config.enumsPath = userLaravel.enumsPath;
|
|
392
|
+
}
|
|
393
|
+
if (userLaravel?.enumsNamespace !== void 0) {
|
|
394
|
+
config.enumsNamespace = userLaravel.enumsNamespace;
|
|
395
|
+
}
|
|
396
|
+
return config;
|
|
397
|
+
}
|
|
398
|
+
function validateConfig(config, rootDir) {
|
|
399
|
+
const schemaPath = (0, import_node_path2.resolve)(rootDir, config.schemasDir);
|
|
400
|
+
if (!(0, import_node_fs2.existsSync)(schemaPath)) {
|
|
401
|
+
throw (0, import_omnify_core2.configError)(
|
|
402
|
+
`Schema directory not found: ${schemaPath}. Create the '${config.schemasDir}' directory or update schemasDir in config.`,
|
|
403
|
+
"E002"
|
|
404
|
+
);
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
function requireDevUrl(config) {
|
|
408
|
+
if (!config.database.devUrl) {
|
|
409
|
+
throw (0, import_omnify_core2.configError)(
|
|
410
|
+
`database.devUrl is required for diff and generate operations. Add devUrl to your database config, e.g., "mysql://root@localhost:3306/omnify_dev"`,
|
|
411
|
+
"E003"
|
|
412
|
+
);
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
async function loadConfig(startDir = process.cwd()) {
|
|
416
|
+
const cwd = (0, import_node_path2.resolve)(startDir);
|
|
417
|
+
const configPath = findConfigFile(cwd);
|
|
418
|
+
if (configPath) {
|
|
419
|
+
const userConfig = await loadConfigFile(configPath);
|
|
420
|
+
const config = await resolveConfig(userConfig, configPath);
|
|
421
|
+
return {
|
|
422
|
+
config,
|
|
423
|
+
configPath
|
|
424
|
+
};
|
|
425
|
+
}
|
|
426
|
+
throw (0, import_omnify_core2.configNotFoundError)((0, import_node_path2.resolve)(cwd, "omnify.config.ts"));
|
|
427
|
+
}
|
|
428
|
+
function defineConfig(config) {
|
|
429
|
+
return config;
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
// src/commands/validate.ts
|
|
433
|
+
async function runValidate(options) {
|
|
434
|
+
logger.setVerbose(options.verbose ?? false);
|
|
435
|
+
logger.header("Validating Schemas");
|
|
436
|
+
logger.debug("Loading configuration...");
|
|
437
|
+
logger.timing("Config load start");
|
|
438
|
+
const { config, configPath } = await loadConfig();
|
|
439
|
+
logger.timing("Config loaded");
|
|
440
|
+
const rootDir = configPath ? (0, import_node_path3.dirname)(configPath) : process.cwd();
|
|
441
|
+
validateConfig(config, rootDir);
|
|
442
|
+
const schemaPath = (0, import_node_path3.resolve)(rootDir, config.schemasDir);
|
|
443
|
+
logger.step(`Loading schemas from ${schemaPath}`);
|
|
444
|
+
logger.timing("Schema load start");
|
|
445
|
+
const schemas = await (0, import_omnify_core3.loadSchemas)(schemaPath);
|
|
446
|
+
const schemaCount = Object.keys(schemas).length;
|
|
447
|
+
logger.timing("Schemas loaded");
|
|
448
|
+
if (schemaCount === 0) {
|
|
449
|
+
logger.warn("No schema files found");
|
|
450
|
+
return;
|
|
451
|
+
}
|
|
452
|
+
logger.debug(`Found ${schemaCount} schema(s)`);
|
|
453
|
+
logger.step("Validating schemas...");
|
|
454
|
+
logger.timing("Validation start");
|
|
455
|
+
const result = (0, import_omnify_core3.validateSchemas)(schemas);
|
|
456
|
+
logger.timing("Validation complete");
|
|
457
|
+
if (result.valid) {
|
|
458
|
+
logger.success(`All ${schemaCount} schema(s) are valid`);
|
|
459
|
+
} else {
|
|
460
|
+
logger.error(`Found ${result.errors.length} validation error(s)`);
|
|
461
|
+
logger.newline();
|
|
462
|
+
for (const error of result.errors) {
|
|
463
|
+
const omnifyError = import_omnify_core3.OmnifyError.fromInfo(error);
|
|
464
|
+
logger.formatError(omnifyError);
|
|
465
|
+
logger.newline();
|
|
466
|
+
}
|
|
467
|
+
process.exit(2);
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
function registerValidateCommand(program2) {
|
|
471
|
+
program2.command("validate").description("Validate schema files").option("-v, --verbose", "Show detailed output").action(async (options) => {
|
|
472
|
+
try {
|
|
473
|
+
await runValidate(options);
|
|
474
|
+
} catch (error) {
|
|
475
|
+
if (error instanceof import_omnify_core3.OmnifyError) {
|
|
476
|
+
logger.formatError(error);
|
|
477
|
+
process.exit(logger.getExitCode(error));
|
|
478
|
+
} else if (error instanceof Error) {
|
|
479
|
+
logger.error(error.message);
|
|
480
|
+
process.exit(1);
|
|
481
|
+
}
|
|
482
|
+
process.exit(1);
|
|
483
|
+
}
|
|
484
|
+
});
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
// src/commands/diff.ts
|
|
488
|
+
var import_node_path4 = require("path");
|
|
489
|
+
var import_omnify_core4 = require("@famgia/omnify-core");
|
|
490
|
+
|
|
491
|
+
// src/operations/diff.ts
|
|
492
|
+
var import_omnify_atlas = require("@famgia/omnify-atlas");
|
|
493
|
+
async function runDiffOperation(options) {
|
|
494
|
+
const { schemas, devUrl, driver, workDir } = options;
|
|
495
|
+
const preview = await (0, import_omnify_atlas.generatePreview)(schemas, {
|
|
496
|
+
driver,
|
|
497
|
+
devUrl,
|
|
498
|
+
workDir
|
|
499
|
+
}, {
|
|
500
|
+
warnDestructive: true,
|
|
501
|
+
showSql: true
|
|
502
|
+
});
|
|
503
|
+
const formattedPreview = (0, import_omnify_atlas.formatPreview)(preview, "text");
|
|
504
|
+
return {
|
|
505
|
+
hasChanges: preview.hasChanges,
|
|
506
|
+
hasDestructiveChanges: preview.hasDestructiveChanges,
|
|
507
|
+
preview,
|
|
508
|
+
formattedPreview,
|
|
509
|
+
sql: preview.sql
|
|
510
|
+
};
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
// src/commands/diff.ts
|
|
514
|
+
var import_picocolors2 = __toESM(require("picocolors"), 1);
|
|
515
|
+
async function runDiff(options) {
|
|
516
|
+
logger.setVerbose(options.verbose ?? false);
|
|
517
|
+
logger.header("Checking for Schema Changes");
|
|
518
|
+
logger.debug("Loading configuration...");
|
|
519
|
+
const { config, configPath } = await loadConfig();
|
|
520
|
+
const rootDir = configPath ? (0, import_node_path4.dirname)(configPath) : process.cwd();
|
|
521
|
+
validateConfig(config, rootDir);
|
|
522
|
+
requireDevUrl(config);
|
|
523
|
+
const schemaPath = (0, import_node_path4.resolve)(rootDir, config.schemasDir);
|
|
524
|
+
logger.step(`Loading schemas from ${schemaPath}`);
|
|
525
|
+
const schemas = await (0, import_omnify_core4.loadSchemas)(schemaPath);
|
|
526
|
+
const schemaCount = Object.keys(schemas).length;
|
|
527
|
+
if (schemaCount === 0) {
|
|
528
|
+
logger.warn("No schema files found");
|
|
529
|
+
return;
|
|
530
|
+
}
|
|
531
|
+
logger.debug(`Found ${schemaCount} schema(s)`);
|
|
532
|
+
logger.step("Validating schemas...");
|
|
533
|
+
const validationResult = (0, import_omnify_core4.validateSchemas)(schemas);
|
|
534
|
+
if (!validationResult.valid) {
|
|
535
|
+
logger.error("Schema validation failed. Fix errors before running diff.");
|
|
536
|
+
for (const error of validationResult.errors) {
|
|
537
|
+
const omnifyError = import_omnify_core4.OmnifyError.fromInfo(error);
|
|
538
|
+
logger.formatError(omnifyError);
|
|
539
|
+
}
|
|
540
|
+
process.exit(2);
|
|
541
|
+
}
|
|
542
|
+
logger.step("Running Atlas diff...");
|
|
543
|
+
const lockPath = (0, import_node_path4.resolve)(rootDir, config.lockFilePath);
|
|
544
|
+
const diffResult = await runDiffOperation({
|
|
545
|
+
schemas,
|
|
546
|
+
devUrl: config.database.devUrl,
|
|
547
|
+
lockFilePath: lockPath,
|
|
548
|
+
driver: config.database.driver,
|
|
549
|
+
workDir: rootDir
|
|
550
|
+
});
|
|
551
|
+
if (!diffResult.hasChanges) {
|
|
552
|
+
logger.success("No changes detected");
|
|
553
|
+
return;
|
|
554
|
+
}
|
|
555
|
+
logger.newline();
|
|
556
|
+
console.log(import_picocolors2.default.bold("Changes detected:"));
|
|
557
|
+
console.log();
|
|
558
|
+
console.log(diffResult.formattedPreview);
|
|
559
|
+
if (diffResult.hasDestructiveChanges) {
|
|
560
|
+
logger.newline();
|
|
561
|
+
logger.warn("This preview contains destructive changes. Review carefully.");
|
|
562
|
+
}
|
|
563
|
+
if (options.check) {
|
|
564
|
+
logger.newline();
|
|
565
|
+
logger.info("Changes detected (--check mode)");
|
|
566
|
+
process.exit(1);
|
|
567
|
+
}
|
|
568
|
+
logger.newline();
|
|
569
|
+
logger.info('Run "omnify generate" to create migrations');
|
|
570
|
+
}
|
|
571
|
+
function registerDiffCommand(program2) {
|
|
572
|
+
program2.command("diff").description("Show pending schema changes").option("-v, --verbose", "Show detailed output").option("--check", "Exit with code 1 if changes exist (for CI)").action(async (options) => {
|
|
573
|
+
try {
|
|
574
|
+
await runDiff(options);
|
|
575
|
+
} catch (error) {
|
|
576
|
+
if (error instanceof import_omnify_core4.OmnifyError) {
|
|
577
|
+
logger.formatError(error);
|
|
578
|
+
process.exit(logger.getExitCode(error));
|
|
579
|
+
} else if (error instanceof Error) {
|
|
580
|
+
logger.error(error.message);
|
|
581
|
+
process.exit(1);
|
|
582
|
+
}
|
|
583
|
+
process.exit(1);
|
|
584
|
+
}
|
|
585
|
+
});
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
// src/commands/generate.ts
|
|
589
|
+
var import_node_fs3 = require("fs");
|
|
590
|
+
var import_node_path5 = require("path");
|
|
591
|
+
var import_omnify_core5 = require("@famgia/omnify-core");
|
|
592
|
+
var import_omnify_atlas2 = require("@famgia/omnify-atlas");
|
|
593
|
+
var import_omnify_laravel = require("@famgia/omnify-laravel");
|
|
594
|
+
async function runGenerate(options) {
|
|
595
|
+
logger.setVerbose(options.verbose ?? false);
|
|
596
|
+
logger.header("Generating Outputs");
|
|
597
|
+
logger.debug("Loading configuration...");
|
|
598
|
+
const { config, configPath } = await loadConfig();
|
|
599
|
+
const rootDir = configPath ? (0, import_node_path5.dirname)(configPath) : process.cwd();
|
|
600
|
+
validateConfig(config, rootDir);
|
|
601
|
+
requireDevUrl(config);
|
|
602
|
+
const schemaPath = (0, import_node_path5.resolve)(rootDir, config.schemasDir);
|
|
603
|
+
logger.step(`Loading schemas from ${schemaPath}`);
|
|
604
|
+
const schemas = await (0, import_omnify_core5.loadSchemas)(schemaPath);
|
|
605
|
+
const schemaCount = Object.keys(schemas).length;
|
|
606
|
+
if (schemaCount === 0) {
|
|
607
|
+
logger.warn("No schema files found");
|
|
608
|
+
return;
|
|
609
|
+
}
|
|
610
|
+
logger.debug(`Found ${schemaCount} schema(s)`);
|
|
611
|
+
logger.step("Validating schemas...");
|
|
612
|
+
const validationResult = (0, import_omnify_core5.validateSchemas)(schemas);
|
|
613
|
+
if (!validationResult.valid) {
|
|
614
|
+
logger.error("Schema validation failed. Fix errors before generating.");
|
|
615
|
+
for (const error of validationResult.errors) {
|
|
616
|
+
const omnifyError = import_omnify_core5.OmnifyError.fromInfo(error);
|
|
617
|
+
logger.formatError(omnifyError);
|
|
618
|
+
}
|
|
619
|
+
process.exit(2);
|
|
620
|
+
}
|
|
621
|
+
logger.step("Checking for changes...");
|
|
622
|
+
const lockPath = (0, import_node_path5.resolve)(rootDir, config.lockFilePath);
|
|
623
|
+
const diffResult = await runDiffOperation({
|
|
624
|
+
schemas,
|
|
625
|
+
devUrl: config.database.devUrl,
|
|
626
|
+
lockFilePath: lockPath,
|
|
627
|
+
driver: config.database.driver,
|
|
628
|
+
workDir: rootDir
|
|
629
|
+
});
|
|
630
|
+
if (!diffResult.hasChanges && !options.force) {
|
|
631
|
+
logger.success("No changes to generate");
|
|
632
|
+
return;
|
|
633
|
+
}
|
|
634
|
+
let migrationsGenerated = 0;
|
|
635
|
+
let typesGenerated = 0;
|
|
636
|
+
if (!options.typesOnly) {
|
|
637
|
+
logger.step("Generating Laravel migrations...");
|
|
638
|
+
const migrationsDir = (0, import_node_path5.resolve)(rootDir, config.output.laravel.migrationsPath);
|
|
639
|
+
if (!(0, import_node_fs3.existsSync)(migrationsDir)) {
|
|
640
|
+
(0, import_node_fs3.mkdirSync)(migrationsDir, { recursive: true });
|
|
641
|
+
logger.debug(`Created directory: ${migrationsDir}`);
|
|
642
|
+
}
|
|
643
|
+
const migrations = (0, import_omnify_laravel.generateMigrations)(schemas);
|
|
644
|
+
for (const migration of migrations) {
|
|
645
|
+
const filePath = (0, import_node_path5.resolve)(migrationsDir, migration.fileName);
|
|
646
|
+
(0, import_node_fs3.writeFileSync)(filePath, migration.content);
|
|
647
|
+
logger.debug(`Created: ${migration.fileName}`);
|
|
648
|
+
migrationsGenerated++;
|
|
649
|
+
}
|
|
650
|
+
logger.success(`Generated ${migrationsGenerated} migration(s)`);
|
|
651
|
+
}
|
|
652
|
+
if (!options.migrationsOnly) {
|
|
653
|
+
logger.step("Generating TypeScript types...");
|
|
654
|
+
const typesDir = (0, import_node_path5.resolve)(rootDir, config.output.typescript.path);
|
|
655
|
+
if (!(0, import_node_fs3.existsSync)(typesDir)) {
|
|
656
|
+
(0, import_node_fs3.mkdirSync)(typesDir, { recursive: true });
|
|
657
|
+
logger.debug(`Created directory: ${typesDir}`);
|
|
658
|
+
}
|
|
659
|
+
const typeFiles = (0, import_omnify_laravel.generateTypeScript)(schemas, {
|
|
660
|
+
singleFile: config.output.typescript.singleFile
|
|
661
|
+
});
|
|
662
|
+
for (const file of typeFiles) {
|
|
663
|
+
const filePath = (0, import_node_path5.resolve)(typesDir, file.fileName);
|
|
664
|
+
(0, import_node_fs3.writeFileSync)(filePath, file.content);
|
|
665
|
+
logger.debug(`Created: ${file.fileName}`);
|
|
666
|
+
typesGenerated++;
|
|
667
|
+
}
|
|
668
|
+
logger.success(`Generated ${typesGenerated} TypeScript file(s)`);
|
|
669
|
+
}
|
|
670
|
+
logger.step("Updating lock file...");
|
|
671
|
+
await (0, import_omnify_atlas2.updateLockFile)(lockPath, schemas);
|
|
672
|
+
logger.debug(`Updated: ${config.lockFilePath}`);
|
|
673
|
+
logger.newline();
|
|
674
|
+
logger.success("Generation complete!");
|
|
675
|
+
if (migrationsGenerated > 0) {
|
|
676
|
+
logger.info(` Migrations: ${config.output.laravel.migrationsPath}/`);
|
|
677
|
+
}
|
|
678
|
+
if (typesGenerated > 0) {
|
|
679
|
+
logger.info(` Types: ${config.output.typescript.path}/`);
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
function registerGenerateCommand(program2) {
|
|
683
|
+
program2.command("generate").description("Generate Laravel migrations and TypeScript types").option("-v, --verbose", "Show detailed output").option("--migrations-only", "Only generate migrations").option("--types-only", "Only generate TypeScript types").option("-f, --force", "Generate even if no changes detected").action(async (options) => {
|
|
684
|
+
try {
|
|
685
|
+
await runGenerate(options);
|
|
686
|
+
} catch (error) {
|
|
687
|
+
if (error instanceof import_omnify_core5.OmnifyError) {
|
|
688
|
+
logger.formatError(error);
|
|
689
|
+
process.exit(logger.getExitCode(error));
|
|
690
|
+
} else if (error instanceof Error) {
|
|
691
|
+
logger.error(error.message);
|
|
692
|
+
process.exit(1);
|
|
693
|
+
}
|
|
694
|
+
process.exit(1);
|
|
695
|
+
}
|
|
696
|
+
});
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
// src/index.ts
|
|
700
|
+
var VERSION = "0.0.1";
|
|
701
|
+
var program = new import_commander.Command();
|
|
702
|
+
program.name("omnify").description("Schema-first database migrations for Laravel and TypeScript").version(VERSION);
|
|
703
|
+
registerInitCommand(program);
|
|
704
|
+
registerValidateCommand(program);
|
|
705
|
+
registerDiffCommand(program);
|
|
706
|
+
registerGenerateCommand(program);
|
|
707
|
+
process.on("uncaughtException", (error) => {
|
|
708
|
+
if (error instanceof import_omnify_core6.OmnifyError) {
|
|
709
|
+
logger.formatError(error);
|
|
710
|
+
process.exit(logger.getExitCode(error));
|
|
711
|
+
} else {
|
|
712
|
+
logger.error(error.message);
|
|
713
|
+
process.exit(1);
|
|
714
|
+
}
|
|
715
|
+
});
|
|
716
|
+
process.on("unhandledRejection", (reason) => {
|
|
717
|
+
if (reason instanceof import_omnify_core6.OmnifyError) {
|
|
718
|
+
logger.formatError(reason);
|
|
719
|
+
process.exit(logger.getExitCode(reason));
|
|
720
|
+
} else if (reason instanceof Error) {
|
|
721
|
+
logger.error(reason.message);
|
|
722
|
+
} else {
|
|
723
|
+
logger.error(String(reason));
|
|
724
|
+
}
|
|
725
|
+
process.exit(1);
|
|
726
|
+
});
|
|
727
|
+
program.parse();
|
|
728
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
729
|
+
0 && (module.exports = {
|
|
730
|
+
defineConfig,
|
|
731
|
+
loadConfig
|
|
732
|
+
});
|
|
733
|
+
//# sourceMappingURL=index.cjs.map
|