@openmdm/cli 0.2.0 → 0.3.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/dist/generate-466DLVPN.js +879 -0
- package/dist/generate-466DLVPN.js.map +1 -0
- package/dist/index.js +6 -2
- package/dist/index.js.map +1 -1
- package/dist/{migrate-HMECTTGA.js → migrate-XBKC6VPK.js} +6 -1
- package/dist/{migrate-HMECTTGA.js.map → migrate-XBKC6VPK.js.map} +1 -1
- package/package.json +2 -2
|
@@ -0,0 +1,879 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/commands/generate.ts
|
|
4
|
+
import chalk from "chalk";
|
|
5
|
+
import ora from "ora";
|
|
6
|
+
import { writeFileSync, existsSync } from "fs";
|
|
7
|
+
import { resolve, dirname } from "path";
|
|
8
|
+
import { mkdir } from "fs/promises";
|
|
9
|
+
import inquirer from "inquirer";
|
|
10
|
+
|
|
11
|
+
// src/generators/drizzle.ts
|
|
12
|
+
import {
|
|
13
|
+
mdmSchema
|
|
14
|
+
} from "@openmdm/core/schema";
|
|
15
|
+
function generateDrizzleSchema(options) {
|
|
16
|
+
const { provider, tablePrefix = "" } = options;
|
|
17
|
+
const imports = generateImports(provider);
|
|
18
|
+
const enums = generateEnums(provider, tablePrefix);
|
|
19
|
+
const tables = generateTables(provider, tablePrefix);
|
|
20
|
+
const relations = generateRelations(tablePrefix);
|
|
21
|
+
return `${imports}
|
|
22
|
+
|
|
23
|
+
${enums}
|
|
24
|
+
|
|
25
|
+
${tables}
|
|
26
|
+
|
|
27
|
+
${relations}
|
|
28
|
+
|
|
29
|
+
// Export all tables for easy schema setup
|
|
30
|
+
export const mdmSchema = {
|
|
31
|
+
${Object.keys(mdmSchema.tables).map((name) => ` ${toCamelCase(applyPrefix(name, tablePrefix))},`).join("\n")}
|
|
32
|
+
};
|
|
33
|
+
`;
|
|
34
|
+
}
|
|
35
|
+
function generateImports(provider) {
|
|
36
|
+
switch (provider) {
|
|
37
|
+
case "pg":
|
|
38
|
+
return `/**
|
|
39
|
+
* OpenMDM Drizzle Schema for PostgreSQL
|
|
40
|
+
*
|
|
41
|
+
* Generated by @openmdm/cli - Do not edit manually.
|
|
42
|
+
* Regenerate with: npx openmdm generate --adapter drizzle --provider pg
|
|
43
|
+
*/
|
|
44
|
+
|
|
45
|
+
import {
|
|
46
|
+
pgTable,
|
|
47
|
+
pgEnum,
|
|
48
|
+
text,
|
|
49
|
+
varchar,
|
|
50
|
+
boolean,
|
|
51
|
+
integer,
|
|
52
|
+
bigint,
|
|
53
|
+
timestamp,
|
|
54
|
+
json,
|
|
55
|
+
index,
|
|
56
|
+
uniqueIndex,
|
|
57
|
+
primaryKey,
|
|
58
|
+
} from 'drizzle-orm/pg-core';
|
|
59
|
+
import { relations } from 'drizzle-orm';`;
|
|
60
|
+
case "mysql":
|
|
61
|
+
return `/**
|
|
62
|
+
* OpenMDM Drizzle Schema for MySQL
|
|
63
|
+
*
|
|
64
|
+
* Generated by @openmdm/cli - Do not edit manually.
|
|
65
|
+
* Regenerate with: npx openmdm generate --adapter drizzle --provider mysql
|
|
66
|
+
*/
|
|
67
|
+
|
|
68
|
+
import {
|
|
69
|
+
mysqlTable,
|
|
70
|
+
mysqlEnum,
|
|
71
|
+
text,
|
|
72
|
+
varchar,
|
|
73
|
+
boolean,
|
|
74
|
+
int,
|
|
75
|
+
bigint,
|
|
76
|
+
timestamp,
|
|
77
|
+
json,
|
|
78
|
+
index,
|
|
79
|
+
uniqueIndex,
|
|
80
|
+
primaryKey,
|
|
81
|
+
} from 'drizzle-orm/mysql-core';
|
|
82
|
+
import { relations } from 'drizzle-orm';`;
|
|
83
|
+
case "sqlite":
|
|
84
|
+
return `/**
|
|
85
|
+
* OpenMDM Drizzle Schema for SQLite
|
|
86
|
+
*
|
|
87
|
+
* Generated by @openmdm/cli - Do not edit manually.
|
|
88
|
+
* Regenerate with: npx openmdm generate --adapter drizzle --provider sqlite
|
|
89
|
+
*/
|
|
90
|
+
|
|
91
|
+
import {
|
|
92
|
+
sqliteTable,
|
|
93
|
+
text,
|
|
94
|
+
integer,
|
|
95
|
+
blob,
|
|
96
|
+
index,
|
|
97
|
+
uniqueIndex,
|
|
98
|
+
primaryKey,
|
|
99
|
+
} from 'drizzle-orm/sqlite-core';
|
|
100
|
+
import { relations, sql } from 'drizzle-orm';`;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
function generateEnums(provider, tablePrefix) {
|
|
104
|
+
const enums = /* @__PURE__ */ new Map();
|
|
105
|
+
for (const [tableName, table] of Object.entries(mdmSchema.tables)) {
|
|
106
|
+
for (const [colName, col] of Object.entries(table.columns)) {
|
|
107
|
+
if (col.type === "enum" && col.enumValues) {
|
|
108
|
+
const enumName = `${tableName}_${colName}`;
|
|
109
|
+
enums.set(enumName, col.enumValues);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
if (enums.size === 0) return "";
|
|
114
|
+
const enumDefs = [];
|
|
115
|
+
for (const [name, values] of enums) {
|
|
116
|
+
const enumVarName = toCamelCase(applyPrefix(name, tablePrefix)) + "Enum";
|
|
117
|
+
const enumDbName = applyPrefix(name, tablePrefix);
|
|
118
|
+
switch (provider) {
|
|
119
|
+
case "pg":
|
|
120
|
+
enumDefs.push(
|
|
121
|
+
`export const ${enumVarName} = pgEnum('${enumDbName}', [${values.map((v) => `'${v}'`).join(", ")}]);`
|
|
122
|
+
);
|
|
123
|
+
break;
|
|
124
|
+
case "mysql":
|
|
125
|
+
enumDefs.push(
|
|
126
|
+
`export const ${enumVarName} = mysqlEnum('${enumDbName}', [${values.map((v) => `'${v}'`).join(", ")}]);`
|
|
127
|
+
);
|
|
128
|
+
break;
|
|
129
|
+
case "sqlite":
|
|
130
|
+
enumDefs.push(
|
|
131
|
+
`// SQLite enum values for ${enumVarName}: ${values.map((v) => `'${v}'`).join(", ")}`
|
|
132
|
+
);
|
|
133
|
+
break;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
return `// ============================================
|
|
137
|
+
// Enums
|
|
138
|
+
// ============================================
|
|
139
|
+
|
|
140
|
+
${enumDefs.join("\n\n")}`;
|
|
141
|
+
}
|
|
142
|
+
function generateTables(provider, tablePrefix) {
|
|
143
|
+
const tableDefs = [];
|
|
144
|
+
for (const [tableName, table] of Object.entries(mdmSchema.tables)) {
|
|
145
|
+
tableDefs.push(generateTable(tableName, table, provider, tablePrefix));
|
|
146
|
+
}
|
|
147
|
+
return `// ============================================
|
|
148
|
+
// Tables
|
|
149
|
+
// ============================================
|
|
150
|
+
|
|
151
|
+
${tableDefs.join("\n\n")}`;
|
|
152
|
+
}
|
|
153
|
+
function generateTable(tableName, table, provider, tablePrefix) {
|
|
154
|
+
const varName = toCamelCase(applyPrefix(tableName, tablePrefix));
|
|
155
|
+
const dbTableName = applyPrefix(tableName, tablePrefix);
|
|
156
|
+
const tableFunc = getTableFunc(provider);
|
|
157
|
+
const columns = Object.entries(table.columns).map(([colName, col]) => generateColumn(colName, col, tableName, provider, tablePrefix)).join(",\n ");
|
|
158
|
+
const indexes = table.indexes ? generateIndexes(table.indexes, varName, provider) : "";
|
|
159
|
+
return `export const ${varName} = ${tableFunc}(
|
|
160
|
+
'${dbTableName}',
|
|
161
|
+
{
|
|
162
|
+
${columns}
|
|
163
|
+
}${indexes ? `,
|
|
164
|
+
(table) => [
|
|
165
|
+
${indexes}
|
|
166
|
+
]` : ""}
|
|
167
|
+
);`;
|
|
168
|
+
}
|
|
169
|
+
function generateColumn(colName, col, tableName, provider, tablePrefix) {
|
|
170
|
+
const camelColName = toCamelCase(colName);
|
|
171
|
+
let colDef = "";
|
|
172
|
+
switch (provider) {
|
|
173
|
+
case "pg":
|
|
174
|
+
colDef = generatePgColumn(colName, col, tableName, tablePrefix);
|
|
175
|
+
break;
|
|
176
|
+
case "mysql":
|
|
177
|
+
colDef = generateMysqlColumn(colName, col, tableName, tablePrefix);
|
|
178
|
+
break;
|
|
179
|
+
case "sqlite":
|
|
180
|
+
colDef = generateSqliteColumn(colName, col, tableName, tablePrefix);
|
|
181
|
+
break;
|
|
182
|
+
}
|
|
183
|
+
return `${camelColName}: ${colDef}`;
|
|
184
|
+
}
|
|
185
|
+
function generatePgColumn(colName, col, tableName, tablePrefix) {
|
|
186
|
+
let def = "";
|
|
187
|
+
switch (col.type) {
|
|
188
|
+
case "string":
|
|
189
|
+
def = `varchar('${colName}', { length: 255 })`;
|
|
190
|
+
break;
|
|
191
|
+
case "text":
|
|
192
|
+
def = `text('${colName}')`;
|
|
193
|
+
break;
|
|
194
|
+
case "integer":
|
|
195
|
+
def = `integer('${colName}')`;
|
|
196
|
+
break;
|
|
197
|
+
case "bigint":
|
|
198
|
+
def = `bigint('${colName}', { mode: 'number' })`;
|
|
199
|
+
break;
|
|
200
|
+
case "boolean":
|
|
201
|
+
def = `boolean('${colName}')`;
|
|
202
|
+
break;
|
|
203
|
+
case "datetime":
|
|
204
|
+
def = `timestamp('${colName}', { withTimezone: true })`;
|
|
205
|
+
break;
|
|
206
|
+
case "json":
|
|
207
|
+
def = `json('${colName}')`;
|
|
208
|
+
break;
|
|
209
|
+
case "enum":
|
|
210
|
+
const enumVarName = toCamelCase(applyPrefix(`${tableName}_${colName}`, tablePrefix)) + "Enum";
|
|
211
|
+
def = `${enumVarName}('${colName}')`;
|
|
212
|
+
break;
|
|
213
|
+
}
|
|
214
|
+
if (col.primaryKey) {
|
|
215
|
+
def += ".primaryKey()";
|
|
216
|
+
}
|
|
217
|
+
if (col.unique) {
|
|
218
|
+
def += ".unique()";
|
|
219
|
+
}
|
|
220
|
+
if (!col.nullable && !col.primaryKey) {
|
|
221
|
+
def += ".notNull()";
|
|
222
|
+
}
|
|
223
|
+
if (col.default !== void 0) {
|
|
224
|
+
if (col.default === "now") {
|
|
225
|
+
def += ".defaultNow()";
|
|
226
|
+
} else if (typeof col.default === "boolean") {
|
|
227
|
+
def += `.default(${col.default})`;
|
|
228
|
+
} else if (typeof col.default === "number") {
|
|
229
|
+
def += `.default(${col.default})`;
|
|
230
|
+
} else if (typeof col.default === "string") {
|
|
231
|
+
def += `.default('${col.default}')`;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
if (col.references) {
|
|
235
|
+
const refTable = toCamelCase(applyPrefix(col.references.table, tablePrefix));
|
|
236
|
+
const onDelete = col.references.onDelete || "restrict";
|
|
237
|
+
def += `.references(() => ${refTable}.${toCamelCase(col.references.column)}, { onDelete: '${onDelete}' })`;
|
|
238
|
+
}
|
|
239
|
+
return def;
|
|
240
|
+
}
|
|
241
|
+
function generateMysqlColumn(colName, col, tableName, tablePrefix) {
|
|
242
|
+
let def = "";
|
|
243
|
+
switch (col.type) {
|
|
244
|
+
case "string":
|
|
245
|
+
def = `varchar('${colName}', { length: 255 })`;
|
|
246
|
+
break;
|
|
247
|
+
case "text":
|
|
248
|
+
def = `text('${colName}')`;
|
|
249
|
+
break;
|
|
250
|
+
case "integer":
|
|
251
|
+
def = `int('${colName}')`;
|
|
252
|
+
break;
|
|
253
|
+
case "bigint":
|
|
254
|
+
def = `bigint('${colName}', { mode: 'number' })`;
|
|
255
|
+
break;
|
|
256
|
+
case "boolean":
|
|
257
|
+
def = `boolean('${colName}')`;
|
|
258
|
+
break;
|
|
259
|
+
case "datetime":
|
|
260
|
+
def = `timestamp('${colName}')`;
|
|
261
|
+
break;
|
|
262
|
+
case "json":
|
|
263
|
+
def = `json('${colName}')`;
|
|
264
|
+
break;
|
|
265
|
+
case "enum":
|
|
266
|
+
const enumVarName = toCamelCase(applyPrefix(`${tableName}_${colName}`, tablePrefix)) + "Enum";
|
|
267
|
+
def = `${enumVarName}('${colName}')`;
|
|
268
|
+
break;
|
|
269
|
+
}
|
|
270
|
+
if (col.primaryKey) {
|
|
271
|
+
def += ".primaryKey()";
|
|
272
|
+
}
|
|
273
|
+
if (col.unique) {
|
|
274
|
+
def += ".unique()";
|
|
275
|
+
}
|
|
276
|
+
if (!col.nullable && !col.primaryKey) {
|
|
277
|
+
def += ".notNull()";
|
|
278
|
+
}
|
|
279
|
+
if (col.default !== void 0) {
|
|
280
|
+
if (col.default === "now") {
|
|
281
|
+
def += ".defaultNow()";
|
|
282
|
+
} else if (typeof col.default === "boolean") {
|
|
283
|
+
def += `.default(${col.default})`;
|
|
284
|
+
} else if (typeof col.default === "number") {
|
|
285
|
+
def += `.default(${col.default})`;
|
|
286
|
+
} else if (typeof col.default === "string") {
|
|
287
|
+
def += `.default('${col.default}')`;
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
if (col.references) {
|
|
291
|
+
const refTable = toCamelCase(applyPrefix(col.references.table, tablePrefix));
|
|
292
|
+
const onDelete = col.references.onDelete || "restrict";
|
|
293
|
+
def += `.references(() => ${refTable}.${toCamelCase(col.references.column)}, { onDelete: '${onDelete}' })`;
|
|
294
|
+
}
|
|
295
|
+
return def;
|
|
296
|
+
}
|
|
297
|
+
function generateSqliteColumn(colName, col, tableName, tablePrefix) {
|
|
298
|
+
let def = "";
|
|
299
|
+
switch (col.type) {
|
|
300
|
+
case "string":
|
|
301
|
+
case "text":
|
|
302
|
+
case "enum":
|
|
303
|
+
def = `text('${colName}')`;
|
|
304
|
+
break;
|
|
305
|
+
case "integer":
|
|
306
|
+
case "bigint":
|
|
307
|
+
def = `integer('${colName}')`;
|
|
308
|
+
break;
|
|
309
|
+
case "boolean":
|
|
310
|
+
def = `integer('${colName}', { mode: 'boolean' })`;
|
|
311
|
+
break;
|
|
312
|
+
case "datetime":
|
|
313
|
+
def = `integer('${colName}', { mode: 'timestamp_ms' })`;
|
|
314
|
+
break;
|
|
315
|
+
case "json":
|
|
316
|
+
def = `text('${colName}', { mode: 'json' })`;
|
|
317
|
+
break;
|
|
318
|
+
}
|
|
319
|
+
if (col.primaryKey) {
|
|
320
|
+
def += ".primaryKey()";
|
|
321
|
+
}
|
|
322
|
+
if (col.unique) {
|
|
323
|
+
def += ".unique()";
|
|
324
|
+
}
|
|
325
|
+
if (!col.nullable && !col.primaryKey) {
|
|
326
|
+
def += ".notNull()";
|
|
327
|
+
}
|
|
328
|
+
if (col.default !== void 0) {
|
|
329
|
+
if (col.default === "now") {
|
|
330
|
+
def += `.default(sql\`(cast(unixepoch('subsecond') * 1000 as integer))\`)`;
|
|
331
|
+
} else if (typeof col.default === "boolean") {
|
|
332
|
+
def += `.default(${col.default})`;
|
|
333
|
+
} else if (typeof col.default === "number") {
|
|
334
|
+
def += `.default(${col.default})`;
|
|
335
|
+
} else if (typeof col.default === "string") {
|
|
336
|
+
def += `.default('${col.default}')`;
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
if (col.references) {
|
|
340
|
+
const refTable = toCamelCase(applyPrefix(col.references.table, tablePrefix));
|
|
341
|
+
const onDelete = col.references.onDelete || "restrict";
|
|
342
|
+
def += `.references(() => ${refTable}.${toCamelCase(col.references.column)}, { onDelete: '${onDelete}' })`;
|
|
343
|
+
}
|
|
344
|
+
return def;
|
|
345
|
+
}
|
|
346
|
+
function generateIndexes(indexes, tableVarName, provider) {
|
|
347
|
+
return indexes.map((idx) => {
|
|
348
|
+
const cols = idx.columns.map((c) => `table.${toCamelCase(c)}`).join(", ");
|
|
349
|
+
const idxName = idx.name || `${tableVarName}_${idx.columns.join("_")}_idx`;
|
|
350
|
+
if (idx.unique) {
|
|
351
|
+
return ` uniqueIndex('${idxName}').on(${cols})`;
|
|
352
|
+
}
|
|
353
|
+
return ` index('${idxName}').on(${cols})`;
|
|
354
|
+
}).join(",\n");
|
|
355
|
+
}
|
|
356
|
+
function generateRelations(tablePrefix) {
|
|
357
|
+
const relations = [];
|
|
358
|
+
for (const [tableName, table] of Object.entries(mdmSchema.tables)) {
|
|
359
|
+
const varName = toCamelCase(applyPrefix(tableName, tablePrefix));
|
|
360
|
+
const fks = [];
|
|
361
|
+
const reverseRefs = [];
|
|
362
|
+
for (const [colName, col] of Object.entries(table.columns)) {
|
|
363
|
+
if (col.references) {
|
|
364
|
+
fks.push({
|
|
365
|
+
colName,
|
|
366
|
+
refTable: col.references.table,
|
|
367
|
+
refCol: col.references.column
|
|
368
|
+
});
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
for (const [otherTableName, otherTable] of Object.entries(mdmSchema.tables)) {
|
|
372
|
+
if (otherTableName === tableName) continue;
|
|
373
|
+
for (const [otherColName, otherCol] of Object.entries(otherTable.columns)) {
|
|
374
|
+
if (otherCol.references?.table === tableName) {
|
|
375
|
+
reverseRefs.push({
|
|
376
|
+
fromTable: otherTableName,
|
|
377
|
+
fromCol: otherColName
|
|
378
|
+
});
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
if (fks.length === 0 && reverseRefs.length === 0) continue;
|
|
383
|
+
const relationParts = [];
|
|
384
|
+
for (const fk of fks) {
|
|
385
|
+
const refTableVar = toCamelCase(applyPrefix(fk.refTable, tablePrefix));
|
|
386
|
+
const colVar = toCamelCase(fk.colName);
|
|
387
|
+
const refColVar = toCamelCase(fk.refCol);
|
|
388
|
+
const relationName = fk.colName.replace(/_id$/, "");
|
|
389
|
+
relationParts.push(
|
|
390
|
+
` ${toCamelCase(relationName)}: one(${refTableVar}, {
|
|
391
|
+
fields: [${varName}.${colVar}],
|
|
392
|
+
references: [${refTableVar}.${refColVar}],
|
|
393
|
+
})`
|
|
394
|
+
);
|
|
395
|
+
}
|
|
396
|
+
for (const ref of reverseRefs) {
|
|
397
|
+
const fromTableVar = toCamelCase(applyPrefix(ref.fromTable, tablePrefix));
|
|
398
|
+
const pluralName = ref.fromTable.replace(/^mdm_/, "");
|
|
399
|
+
relationParts.push(` ${toCamelCase(pluralName)}: many(${fromTableVar})`);
|
|
400
|
+
}
|
|
401
|
+
if (relationParts.length > 0) {
|
|
402
|
+
relations.push(
|
|
403
|
+
`export const ${varName}Relations = relations(${varName}, ({ one, many }) => ({
|
|
404
|
+
${relationParts.join(",\n")}
|
|
405
|
+
}));`
|
|
406
|
+
);
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
if (relations.length === 0) return "";
|
|
410
|
+
return `// ============================================
|
|
411
|
+
// Relations
|
|
412
|
+
// ============================================
|
|
413
|
+
|
|
414
|
+
${relations.join("\n\n")}`;
|
|
415
|
+
}
|
|
416
|
+
function getTableFunc(provider) {
|
|
417
|
+
switch (provider) {
|
|
418
|
+
case "pg":
|
|
419
|
+
return "pgTable";
|
|
420
|
+
case "mysql":
|
|
421
|
+
return "mysqlTable";
|
|
422
|
+
case "sqlite":
|
|
423
|
+
return "sqliteTable";
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
function toCamelCase(str) {
|
|
427
|
+
return str.replace(/_([a-z])/g, (_, letter) => letter.toUpperCase());
|
|
428
|
+
}
|
|
429
|
+
function applyPrefix(name, prefix) {
|
|
430
|
+
if (!prefix) return name;
|
|
431
|
+
return `${prefix}_${name}`;
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
// src/generators/sql.ts
|
|
435
|
+
import {
|
|
436
|
+
mdmSchema as mdmSchema2
|
|
437
|
+
} from "@openmdm/core/schema";
|
|
438
|
+
function generateSqlSchema(options) {
|
|
439
|
+
const { provider, tablePrefix = "" } = options;
|
|
440
|
+
switch (provider) {
|
|
441
|
+
case "pg":
|
|
442
|
+
return generatePostgresSchema(tablePrefix);
|
|
443
|
+
case "mysql":
|
|
444
|
+
return generateMysqlSchema(tablePrefix);
|
|
445
|
+
case "sqlite":
|
|
446
|
+
return generateSqliteSchema(tablePrefix);
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
function generatePostgresSchema(tablePrefix) {
|
|
450
|
+
const lines = [
|
|
451
|
+
"-- OpenMDM PostgreSQL Schema",
|
|
452
|
+
"-- Generated by @openmdm/cli - Do not edit manually.",
|
|
453
|
+
"-- Regenerate with: npx openmdm generate --adapter sql --provider pg",
|
|
454
|
+
""
|
|
455
|
+
];
|
|
456
|
+
const enums = collectEnums();
|
|
457
|
+
for (const [name, values] of enums) {
|
|
458
|
+
const enumName = applyPrefix2(name, tablePrefix);
|
|
459
|
+
lines.push(`DO $$ BEGIN`);
|
|
460
|
+
lines.push(` CREATE TYPE "${enumName}" AS ENUM (${values.map((v) => `'${v}'`).join(", ")});`);
|
|
461
|
+
lines.push(`EXCEPTION`);
|
|
462
|
+
lines.push(` WHEN duplicate_object THEN null;`);
|
|
463
|
+
lines.push(`END $$;`);
|
|
464
|
+
lines.push("");
|
|
465
|
+
}
|
|
466
|
+
for (const [tableName, table] of Object.entries(mdmSchema2.tables)) {
|
|
467
|
+
lines.push(generatePostgresTable(tableName, table, tablePrefix));
|
|
468
|
+
lines.push("");
|
|
469
|
+
if (table.indexes) {
|
|
470
|
+
for (const idx of table.indexes) {
|
|
471
|
+
lines.push(generatePostgresIndex(tableName, idx, tablePrefix));
|
|
472
|
+
}
|
|
473
|
+
lines.push("");
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
return lines.join("\n");
|
|
477
|
+
}
|
|
478
|
+
function generatePostgresTable(tableName, table, tablePrefix) {
|
|
479
|
+
const dbTableName = applyPrefix2(tableName, tablePrefix);
|
|
480
|
+
const columns = [];
|
|
481
|
+
const constraints = [];
|
|
482
|
+
for (const [colName, col] of Object.entries(table.columns)) {
|
|
483
|
+
columns.push(generatePostgresColumn(colName, col, tableName, tablePrefix));
|
|
484
|
+
if (col.references) {
|
|
485
|
+
const refTable = applyPrefix2(col.references.table, tablePrefix);
|
|
486
|
+
const onDelete = col.references.onDelete?.toUpperCase() || "RESTRICT";
|
|
487
|
+
constraints.push(
|
|
488
|
+
` CONSTRAINT "fk_${dbTableName}_${colName}" FOREIGN KEY ("${colName}") REFERENCES "${refTable}"("${col.references.column}") ON DELETE ${onDelete.replace("_", " ")}`
|
|
489
|
+
);
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
const pkCols = Object.entries(table.columns).filter(([_, col]) => col.primaryKey).map(([name]) => `"${name}"`);
|
|
493
|
+
if (pkCols.length === 0) {
|
|
494
|
+
const uniqueIdx = table.indexes?.find((idx) => idx.unique && idx.columns.length > 1);
|
|
495
|
+
if (uniqueIdx) {
|
|
496
|
+
constraints.push(
|
|
497
|
+
` PRIMARY KEY (${uniqueIdx.columns.map((c) => `"${c}"`).join(", ")})`
|
|
498
|
+
);
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
const allParts = [...columns, ...constraints];
|
|
502
|
+
return `CREATE TABLE IF NOT EXISTS "${dbTableName}" (
|
|
503
|
+
${allParts.join(",\n")}
|
|
504
|
+
);`;
|
|
505
|
+
}
|
|
506
|
+
function generatePostgresColumn(colName, col, tableName, tablePrefix) {
|
|
507
|
+
let type = "";
|
|
508
|
+
switch (col.type) {
|
|
509
|
+
case "string":
|
|
510
|
+
type = "varchar(255)";
|
|
511
|
+
break;
|
|
512
|
+
case "text":
|
|
513
|
+
type = "text";
|
|
514
|
+
break;
|
|
515
|
+
case "integer":
|
|
516
|
+
type = "integer";
|
|
517
|
+
break;
|
|
518
|
+
case "bigint":
|
|
519
|
+
type = "bigint";
|
|
520
|
+
break;
|
|
521
|
+
case "boolean":
|
|
522
|
+
type = "boolean";
|
|
523
|
+
break;
|
|
524
|
+
case "datetime":
|
|
525
|
+
type = "timestamp with time zone";
|
|
526
|
+
break;
|
|
527
|
+
case "json":
|
|
528
|
+
type = "json";
|
|
529
|
+
break;
|
|
530
|
+
case "enum":
|
|
531
|
+
type = `"${applyPrefix2(`${tableName}_${colName}`, tablePrefix)}"`;
|
|
532
|
+
break;
|
|
533
|
+
}
|
|
534
|
+
let def = ` "${colName}" ${type}`;
|
|
535
|
+
if (col.primaryKey) {
|
|
536
|
+
def += " PRIMARY KEY";
|
|
537
|
+
}
|
|
538
|
+
if (col.unique) {
|
|
539
|
+
def += " UNIQUE";
|
|
540
|
+
}
|
|
541
|
+
if (!col.nullable && !col.primaryKey) {
|
|
542
|
+
def += " NOT NULL";
|
|
543
|
+
}
|
|
544
|
+
if (col.default !== void 0) {
|
|
545
|
+
if (col.default === "now") {
|
|
546
|
+
def += " DEFAULT now()";
|
|
547
|
+
} else if (typeof col.default === "boolean") {
|
|
548
|
+
def += ` DEFAULT ${col.default}`;
|
|
549
|
+
} else if (typeof col.default === "number") {
|
|
550
|
+
def += ` DEFAULT ${col.default}`;
|
|
551
|
+
} else if (typeof col.default === "string") {
|
|
552
|
+
def += ` DEFAULT '${col.default}'`;
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
return def;
|
|
556
|
+
}
|
|
557
|
+
function generatePostgresIndex(tableName, idx, tablePrefix) {
|
|
558
|
+
const dbTableName = applyPrefix2(tableName, tablePrefix);
|
|
559
|
+
const idxName = idx.name || `idx_${dbTableName}_${idx.columns.join("_")}`;
|
|
560
|
+
const cols = idx.columns.map((c) => `"${c}"`).join(", ");
|
|
561
|
+
const unique = idx.unique ? "UNIQUE " : "";
|
|
562
|
+
return `CREATE ${unique}INDEX IF NOT EXISTS "${idxName}" ON "${dbTableName}" (${cols});`;
|
|
563
|
+
}
|
|
564
|
+
function generateMysqlSchema(tablePrefix) {
|
|
565
|
+
const lines = [
|
|
566
|
+
"-- OpenMDM MySQL Schema",
|
|
567
|
+
"-- Generated by @openmdm/cli - Do not edit manually.",
|
|
568
|
+
"-- Regenerate with: npx openmdm generate --adapter sql --provider mysql",
|
|
569
|
+
""
|
|
570
|
+
];
|
|
571
|
+
for (const [tableName, table] of Object.entries(mdmSchema2.tables)) {
|
|
572
|
+
lines.push(generateMysqlTable(tableName, table, tablePrefix));
|
|
573
|
+
lines.push("");
|
|
574
|
+
}
|
|
575
|
+
return lines.join("\n");
|
|
576
|
+
}
|
|
577
|
+
function generateMysqlTable(tableName, table, tablePrefix) {
|
|
578
|
+
const dbTableName = applyPrefix2(tableName, tablePrefix);
|
|
579
|
+
const columns = [];
|
|
580
|
+
const constraints = [];
|
|
581
|
+
const indexes = [];
|
|
582
|
+
for (const [colName, col] of Object.entries(table.columns)) {
|
|
583
|
+
columns.push(generateMysqlColumn2(colName, col, tableName));
|
|
584
|
+
if (col.references) {
|
|
585
|
+
const refTable = applyPrefix2(col.references.table, tablePrefix);
|
|
586
|
+
const onDelete = col.references.onDelete?.toUpperCase() || "RESTRICT";
|
|
587
|
+
constraints.push(
|
|
588
|
+
` CONSTRAINT \`fk_${dbTableName}_${colName}\` FOREIGN KEY (\`${colName}\`) REFERENCES \`${refTable}\`(\`${col.references.column}\`) ON DELETE ${onDelete.replace("_", " ")}`
|
|
589
|
+
);
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
if (table.indexes) {
|
|
593
|
+
for (const idx of table.indexes) {
|
|
594
|
+
const idxName = idx.name || `idx_${dbTableName}_${idx.columns.join("_")}`;
|
|
595
|
+
const cols = idx.columns.map((c) => `\`${c}\``).join(", ");
|
|
596
|
+
const unique = idx.unique ? "UNIQUE " : "";
|
|
597
|
+
indexes.push(` ${unique}INDEX \`${idxName}\` (${cols})`);
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
const allParts = [...columns, ...constraints, ...indexes];
|
|
601
|
+
return `CREATE TABLE IF NOT EXISTS \`${dbTableName}\` (
|
|
602
|
+
${allParts.join(",\n")}
|
|
603
|
+
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;`;
|
|
604
|
+
}
|
|
605
|
+
function generateMysqlColumn2(colName, col, tableName) {
|
|
606
|
+
let type = "";
|
|
607
|
+
switch (col.type) {
|
|
608
|
+
case "string":
|
|
609
|
+
type = "VARCHAR(255)";
|
|
610
|
+
break;
|
|
611
|
+
case "text":
|
|
612
|
+
type = "TEXT";
|
|
613
|
+
break;
|
|
614
|
+
case "integer":
|
|
615
|
+
type = "INT";
|
|
616
|
+
break;
|
|
617
|
+
case "bigint":
|
|
618
|
+
type = "BIGINT";
|
|
619
|
+
break;
|
|
620
|
+
case "boolean":
|
|
621
|
+
type = "BOOLEAN";
|
|
622
|
+
break;
|
|
623
|
+
case "datetime":
|
|
624
|
+
type = "TIMESTAMP";
|
|
625
|
+
break;
|
|
626
|
+
case "json":
|
|
627
|
+
type = "JSON";
|
|
628
|
+
break;
|
|
629
|
+
case "enum":
|
|
630
|
+
type = `ENUM(${col.enumValues?.map((v) => `'${v}'`).join(", ")})`;
|
|
631
|
+
break;
|
|
632
|
+
}
|
|
633
|
+
let def = ` \`${colName}\` ${type}`;
|
|
634
|
+
if (col.primaryKey) {
|
|
635
|
+
def += " PRIMARY KEY";
|
|
636
|
+
}
|
|
637
|
+
if (col.unique) {
|
|
638
|
+
def += " UNIQUE";
|
|
639
|
+
}
|
|
640
|
+
if (!col.nullable && !col.primaryKey) {
|
|
641
|
+
def += " NOT NULL";
|
|
642
|
+
}
|
|
643
|
+
if (col.default !== void 0) {
|
|
644
|
+
if (col.default === "now") {
|
|
645
|
+
def += " DEFAULT CURRENT_TIMESTAMP";
|
|
646
|
+
} else if (typeof col.default === "boolean") {
|
|
647
|
+
def += ` DEFAULT ${col.default}`;
|
|
648
|
+
} else if (typeof col.default === "number") {
|
|
649
|
+
def += ` DEFAULT ${col.default}`;
|
|
650
|
+
} else if (typeof col.default === "string") {
|
|
651
|
+
def += ` DEFAULT '${col.default}'`;
|
|
652
|
+
}
|
|
653
|
+
}
|
|
654
|
+
return def;
|
|
655
|
+
}
|
|
656
|
+
function generateSqliteSchema(tablePrefix) {
|
|
657
|
+
const lines = [
|
|
658
|
+
"-- OpenMDM SQLite Schema",
|
|
659
|
+
"-- Generated by @openmdm/cli - Do not edit manually.",
|
|
660
|
+
"-- Regenerate with: npx openmdm generate --adapter sql --provider sqlite",
|
|
661
|
+
""
|
|
662
|
+
];
|
|
663
|
+
for (const [tableName, table] of Object.entries(mdmSchema2.tables)) {
|
|
664
|
+
lines.push(generateSqliteTable(tableName, table, tablePrefix));
|
|
665
|
+
lines.push("");
|
|
666
|
+
if (table.indexes) {
|
|
667
|
+
for (const idx of table.indexes) {
|
|
668
|
+
lines.push(generateSqliteIndex(tableName, idx, tablePrefix));
|
|
669
|
+
}
|
|
670
|
+
lines.push("");
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
return lines.join("\n");
|
|
674
|
+
}
|
|
675
|
+
function generateSqliteTable(tableName, table, tablePrefix) {
|
|
676
|
+
const dbTableName = applyPrefix2(tableName, tablePrefix);
|
|
677
|
+
const columns = [];
|
|
678
|
+
const constraints = [];
|
|
679
|
+
for (const [colName, col] of Object.entries(table.columns)) {
|
|
680
|
+
columns.push(generateSqliteColumn2(colName, col));
|
|
681
|
+
if (col.references) {
|
|
682
|
+
const refTable = applyPrefix2(col.references.table, tablePrefix);
|
|
683
|
+
const onDelete = col.references.onDelete?.toUpperCase() || "RESTRICT";
|
|
684
|
+
constraints.push(
|
|
685
|
+
` FOREIGN KEY ("${colName}") REFERENCES "${refTable}"("${col.references.column}") ON DELETE ${onDelete.replace("_", " ")}`
|
|
686
|
+
);
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
const uniqueIdx = table.indexes?.find((idx) => idx.unique && idx.columns.length > 1);
|
|
690
|
+
if (uniqueIdx) {
|
|
691
|
+
const hasSinglePk = Object.values(table.columns).some((c) => c.primaryKey);
|
|
692
|
+
if (!hasSinglePk) {
|
|
693
|
+
constraints.push(
|
|
694
|
+
` PRIMARY KEY (${uniqueIdx.columns.map((c) => `"${c}"`).join(", ")})`
|
|
695
|
+
);
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
const allParts = [...columns, ...constraints];
|
|
699
|
+
return `CREATE TABLE IF NOT EXISTS "${dbTableName}" (
|
|
700
|
+
${allParts.join(",\n")}
|
|
701
|
+
);`;
|
|
702
|
+
}
|
|
703
|
+
function generateSqliteColumn2(colName, col) {
|
|
704
|
+
let type = "";
|
|
705
|
+
switch (col.type) {
|
|
706
|
+
case "string":
|
|
707
|
+
case "text":
|
|
708
|
+
case "enum":
|
|
709
|
+
type = "TEXT";
|
|
710
|
+
break;
|
|
711
|
+
case "integer":
|
|
712
|
+
case "bigint":
|
|
713
|
+
case "boolean":
|
|
714
|
+
type = "INTEGER";
|
|
715
|
+
break;
|
|
716
|
+
case "datetime":
|
|
717
|
+
type = "INTEGER";
|
|
718
|
+
break;
|
|
719
|
+
case "json":
|
|
720
|
+
type = "TEXT";
|
|
721
|
+
break;
|
|
722
|
+
}
|
|
723
|
+
let def = ` "${colName}" ${type}`;
|
|
724
|
+
if (col.primaryKey) {
|
|
725
|
+
def += " PRIMARY KEY";
|
|
726
|
+
}
|
|
727
|
+
if (col.unique) {
|
|
728
|
+
def += " UNIQUE";
|
|
729
|
+
}
|
|
730
|
+
if (!col.nullable && !col.primaryKey) {
|
|
731
|
+
def += " NOT NULL";
|
|
732
|
+
}
|
|
733
|
+
if (col.default !== void 0) {
|
|
734
|
+
if (col.default === "now") {
|
|
735
|
+
def += ` DEFAULT (cast(unixepoch('subsecond') * 1000 as integer))`;
|
|
736
|
+
} else if (typeof col.default === "boolean") {
|
|
737
|
+
def += ` DEFAULT ${col.default ? 1 : 0}`;
|
|
738
|
+
} else if (typeof col.default === "number") {
|
|
739
|
+
def += ` DEFAULT ${col.default}`;
|
|
740
|
+
} else if (typeof col.default === "string") {
|
|
741
|
+
def += ` DEFAULT '${col.default}'`;
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
return def;
|
|
745
|
+
}
|
|
746
|
+
function generateSqliteIndex(tableName, idx, tablePrefix) {
|
|
747
|
+
const dbTableName = applyPrefix2(tableName, tablePrefix);
|
|
748
|
+
const idxName = idx.name || `idx_${dbTableName}_${idx.columns.join("_")}`;
|
|
749
|
+
const cols = idx.columns.map((c) => `"${c}"`).join(", ");
|
|
750
|
+
const unique = idx.unique ? "UNIQUE " : "";
|
|
751
|
+
return `CREATE ${unique}INDEX IF NOT EXISTS "${idxName}" ON "${dbTableName}" (${cols});`;
|
|
752
|
+
}
|
|
753
|
+
function collectEnums() {
|
|
754
|
+
const enums = /* @__PURE__ */ new Map();
|
|
755
|
+
for (const [tableName, table] of Object.entries(mdmSchema2.tables)) {
|
|
756
|
+
for (const [colName, col] of Object.entries(table.columns)) {
|
|
757
|
+
if (col.type === "enum" && col.enumValues) {
|
|
758
|
+
const enumName = `${tableName}_${colName}`;
|
|
759
|
+
enums.set(enumName, col.enumValues);
|
|
760
|
+
}
|
|
761
|
+
}
|
|
762
|
+
}
|
|
763
|
+
return enums;
|
|
764
|
+
}
|
|
765
|
+
function applyPrefix2(name, prefix) {
|
|
766
|
+
if (!prefix) return name;
|
|
767
|
+
return `${prefix}_${name}`;
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
// src/commands/generate.ts
|
|
771
|
+
var DEFAULT_OUTPUTS = {
|
|
772
|
+
drizzle: {
|
|
773
|
+
pg: "src/db/schema.ts",
|
|
774
|
+
mysql: "src/db/schema.ts",
|
|
775
|
+
sqlite: "src/db/schema.ts"
|
|
776
|
+
},
|
|
777
|
+
sql: {
|
|
778
|
+
pg: "schema.sql",
|
|
779
|
+
mysql: "schema.sql",
|
|
780
|
+
sqlite: "schema.sql"
|
|
781
|
+
}
|
|
782
|
+
};
|
|
783
|
+
async function runGenerate(options) {
|
|
784
|
+
console.log(chalk.blue("\n\u{1F4E6} OpenMDM Schema Generator\n"));
|
|
785
|
+
let { adapter, provider, output } = options;
|
|
786
|
+
if (!adapter) {
|
|
787
|
+
const answer = await inquirer.prompt([
|
|
788
|
+
{
|
|
789
|
+
type: "list",
|
|
790
|
+
name: "adapter",
|
|
791
|
+
message: "Which adapter do you want to generate schema for?",
|
|
792
|
+
choices: [
|
|
793
|
+
{ name: "Drizzle ORM (TypeScript schema)", value: "drizzle" },
|
|
794
|
+
{ name: "Raw SQL (SQL file)", value: "sql" }
|
|
795
|
+
]
|
|
796
|
+
}
|
|
797
|
+
]);
|
|
798
|
+
adapter = answer.adapter;
|
|
799
|
+
}
|
|
800
|
+
if (!provider) {
|
|
801
|
+
const answer = await inquirer.prompt([
|
|
802
|
+
{
|
|
803
|
+
type: "list",
|
|
804
|
+
name: "provider",
|
|
805
|
+
message: "Which database provider?",
|
|
806
|
+
choices: [
|
|
807
|
+
{ name: "PostgreSQL", value: "pg" },
|
|
808
|
+
{ name: "MySQL", value: "mysql" },
|
|
809
|
+
{ name: "SQLite", value: "sqlite" }
|
|
810
|
+
]
|
|
811
|
+
}
|
|
812
|
+
]);
|
|
813
|
+
provider = answer.provider;
|
|
814
|
+
}
|
|
815
|
+
const defaultOutput = DEFAULT_OUTPUTS[adapter][provider];
|
|
816
|
+
if (!output) {
|
|
817
|
+
if (options.yes) {
|
|
818
|
+
output = defaultOutput;
|
|
819
|
+
} else {
|
|
820
|
+
const answer = await inquirer.prompt([
|
|
821
|
+
{
|
|
822
|
+
type: "input",
|
|
823
|
+
name: "output",
|
|
824
|
+
message: "Output file path:",
|
|
825
|
+
default: defaultOutput
|
|
826
|
+
}
|
|
827
|
+
]);
|
|
828
|
+
output = answer.output;
|
|
829
|
+
}
|
|
830
|
+
}
|
|
831
|
+
const outputPath = resolve(process.cwd(), output);
|
|
832
|
+
if (existsSync(outputPath) && !options.yes) {
|
|
833
|
+
const { confirm } = await inquirer.prompt([
|
|
834
|
+
{
|
|
835
|
+
type: "confirm",
|
|
836
|
+
name: "confirm",
|
|
837
|
+
message: `File ${output} already exists. Overwrite?`,
|
|
838
|
+
default: false
|
|
839
|
+
}
|
|
840
|
+
]);
|
|
841
|
+
if (!confirm) {
|
|
842
|
+
console.log(chalk.yellow("Aborted."));
|
|
843
|
+
return;
|
|
844
|
+
}
|
|
845
|
+
}
|
|
846
|
+
const spinner = ora("Generating schema...").start();
|
|
847
|
+
try {
|
|
848
|
+
let content;
|
|
849
|
+
if (adapter === "drizzle") {
|
|
850
|
+
content = generateDrizzleSchema({ provider });
|
|
851
|
+
} else {
|
|
852
|
+
content = generateSqlSchema({ provider });
|
|
853
|
+
}
|
|
854
|
+
const dir = dirname(outputPath);
|
|
855
|
+
await mkdir(dir, { recursive: true });
|
|
856
|
+
writeFileSync(outputPath, content, "utf-8");
|
|
857
|
+
spinner.succeed(`Schema generated at ${chalk.cyan(output)}`);
|
|
858
|
+
console.log(chalk.gray("\nNext steps:"));
|
|
859
|
+
if (adapter === "drizzle") {
|
|
860
|
+
console.log(chalk.gray(` 1. Review the generated schema at ${output}`));
|
|
861
|
+
console.log(chalk.gray(" 2. Run drizzle-kit to generate migrations:"));
|
|
862
|
+
console.log(chalk.cyan(" npx drizzle-kit generate"));
|
|
863
|
+
console.log(chalk.gray(" 3. Apply migrations to your database:"));
|
|
864
|
+
console.log(chalk.cyan(" npx drizzle-kit migrate"));
|
|
865
|
+
} else {
|
|
866
|
+
console.log(chalk.gray(` 1. Review the generated SQL at ${output}`));
|
|
867
|
+
console.log(chalk.gray(" 2. Execute the SQL against your database"));
|
|
868
|
+
}
|
|
869
|
+
console.log("");
|
|
870
|
+
} catch (error) {
|
|
871
|
+
spinner.fail("Failed to generate schema");
|
|
872
|
+
console.error(chalk.red(error));
|
|
873
|
+
process.exit(1);
|
|
874
|
+
}
|
|
875
|
+
}
|
|
876
|
+
export {
|
|
877
|
+
runGenerate
|
|
878
|
+
};
|
|
879
|
+
//# sourceMappingURL=generate-466DLVPN.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/commands/generate.ts","../src/generators/drizzle.ts","../src/generators/sql.ts"],"sourcesContent":["/**\n * Generate Command\n *\n * Generates database schema files for different ORMs.\n * Similar to better-auth's approach.\n */\n\nimport chalk from 'chalk';\nimport ora from 'ora';\nimport { writeFileSync, existsSync } from 'node:fs';\nimport { resolve, dirname } from 'node:path';\nimport { mkdir } from 'node:fs/promises';\nimport inquirer from 'inquirer';\nimport {\n generateDrizzleSchema,\n generateSqlSchema,\n type AdapterType,\n type DatabaseProvider,\n} from '../generators/index.js';\n\ninterface GenerateOptions {\n adapter?: AdapterType;\n provider?: DatabaseProvider;\n output?: string;\n yes?: boolean;\n}\n\nconst DEFAULT_OUTPUTS: Record<AdapterType, Record<DatabaseProvider, string>> = {\n drizzle: {\n pg: 'src/db/schema.ts',\n mysql: 'src/db/schema.ts',\n sqlite: 'src/db/schema.ts',\n },\n sql: {\n pg: 'schema.sql',\n mysql: 'schema.sql',\n sqlite: 'schema.sql',\n },\n};\n\nexport async function runGenerate(options: GenerateOptions): Promise<void> {\n console.log(chalk.blue('\\n📦 OpenMDM Schema Generator\\n'));\n\n let { adapter, provider, output } = options;\n\n // Interactive prompts if not provided\n if (!adapter) {\n const answer = await inquirer.prompt([\n {\n type: 'list',\n name: 'adapter',\n message: 'Which adapter do you want to generate schema for?',\n choices: [\n { name: 'Drizzle ORM (TypeScript schema)', value: 'drizzle' },\n { name: 'Raw SQL (SQL file)', value: 'sql' },\n ],\n },\n ]);\n adapter = answer.adapter as AdapterType;\n }\n\n if (!provider) {\n const answer = await inquirer.prompt([\n {\n type: 'list',\n name: 'provider',\n message: 'Which database provider?',\n choices: [\n { name: 'PostgreSQL', value: 'pg' },\n { name: 'MySQL', value: 'mysql' },\n { name: 'SQLite', value: 'sqlite' },\n ],\n },\n ]);\n provider = answer.provider as DatabaseProvider;\n }\n\n // Determine output path\n const defaultOutput = DEFAULT_OUTPUTS[adapter][provider];\n if (!output) {\n if (options.yes) {\n output = defaultOutput;\n } else {\n const answer = await inquirer.prompt([\n {\n type: 'input',\n name: 'output',\n message: 'Output file path:',\n default: defaultOutput,\n },\n ]);\n output = answer.output;\n }\n }\n\n const outputPath = resolve(process.cwd(), output);\n\n // Confirm before overwriting\n if (existsSync(outputPath) && !options.yes) {\n const { confirm } = await inquirer.prompt([\n {\n type: 'confirm',\n name: 'confirm',\n message: `File ${output} already exists. Overwrite?`,\n default: false,\n },\n ]);\n if (!confirm) {\n console.log(chalk.yellow('Aborted.'));\n return;\n }\n }\n\n const spinner = ora('Generating schema...').start();\n\n try {\n let content: string;\n\n if (adapter === 'drizzle') {\n content = generateDrizzleSchema({ provider });\n } else {\n content = generateSqlSchema({ provider });\n }\n\n // Ensure directory exists\n const dir = dirname(outputPath);\n await mkdir(dir, { recursive: true });\n\n // Write file\n writeFileSync(outputPath, content, 'utf-8');\n\n spinner.succeed(`Schema generated at ${chalk.cyan(output)}`);\n\n // Show next steps\n console.log(chalk.gray('\\nNext steps:'));\n\n if (adapter === 'drizzle') {\n console.log(chalk.gray(` 1. Review the generated schema at ${output}`));\n console.log(chalk.gray(' 2. Run drizzle-kit to generate migrations:'));\n console.log(chalk.cyan(' npx drizzle-kit generate'));\n console.log(chalk.gray(' 3. Apply migrations to your database:'));\n console.log(chalk.cyan(' npx drizzle-kit migrate'));\n } else {\n console.log(chalk.gray(` 1. Review the generated SQL at ${output}`));\n console.log(chalk.gray(' 2. Execute the SQL against your database'));\n }\n\n console.log('');\n } catch (error) {\n spinner.fail('Failed to generate schema');\n console.error(chalk.red(error));\n process.exit(1);\n }\n}\n","/**\n * Drizzle Schema Generator\n *\n * Generates Drizzle ORM schema files from OpenMDM core schema.\n */\n\nimport {\n mdmSchema,\n type ColumnDefinition,\n type TableDefinition,\n type IndexDefinition,\n} from '@openmdm/core/schema';\nimport type { DatabaseProvider } from './index.js';\n\ninterface DrizzleGeneratorOptions {\n provider: DatabaseProvider;\n tablePrefix?: string;\n}\n\n/**\n * Generate Drizzle schema TypeScript code\n */\nexport function generateDrizzleSchema(options: DrizzleGeneratorOptions): string {\n const { provider, tablePrefix = '' } = options;\n\n const imports = generateImports(provider);\n const enums = generateEnums(provider, tablePrefix);\n const tables = generateTables(provider, tablePrefix);\n const relations = generateRelations(tablePrefix);\n\n return `${imports}\n\n${enums}\n\n${tables}\n\n${relations}\n\n// Export all tables for easy schema setup\nexport const mdmSchema = {\n${Object.keys(mdmSchema.tables)\n .map((name) => ` ${toCamelCase(applyPrefix(name, tablePrefix))},`)\n .join('\\n')}\n};\n`;\n}\n\nfunction generateImports(provider: DatabaseProvider): string {\n switch (provider) {\n case 'pg':\n return `/**\n * OpenMDM Drizzle Schema for PostgreSQL\n *\n * Generated by @openmdm/cli - Do not edit manually.\n * Regenerate with: npx openmdm generate --adapter drizzle --provider pg\n */\n\nimport {\n pgTable,\n pgEnum,\n text,\n varchar,\n boolean,\n integer,\n bigint,\n timestamp,\n json,\n index,\n uniqueIndex,\n primaryKey,\n} from 'drizzle-orm/pg-core';\nimport { relations } from 'drizzle-orm';`;\n\n case 'mysql':\n return `/**\n * OpenMDM Drizzle Schema for MySQL\n *\n * Generated by @openmdm/cli - Do not edit manually.\n * Regenerate with: npx openmdm generate --adapter drizzle --provider mysql\n */\n\nimport {\n mysqlTable,\n mysqlEnum,\n text,\n varchar,\n boolean,\n int,\n bigint,\n timestamp,\n json,\n index,\n uniqueIndex,\n primaryKey,\n} from 'drizzle-orm/mysql-core';\nimport { relations } from 'drizzle-orm';`;\n\n case 'sqlite':\n return `/**\n * OpenMDM Drizzle Schema for SQLite\n *\n * Generated by @openmdm/cli - Do not edit manually.\n * Regenerate with: npx openmdm generate --adapter drizzle --provider sqlite\n */\n\nimport {\n sqliteTable,\n text,\n integer,\n blob,\n index,\n uniqueIndex,\n primaryKey,\n} from 'drizzle-orm/sqlite-core';\nimport { relations, sql } from 'drizzle-orm';`;\n }\n}\n\nfunction generateEnums(provider: DatabaseProvider, tablePrefix: string): string {\n // Collect all enums from schema\n const enums: Map<string, string[]> = new Map();\n\n for (const [tableName, table] of Object.entries(mdmSchema.tables)) {\n for (const [colName, col] of Object.entries(table.columns)) {\n if (col.type === 'enum' && col.enumValues) {\n const enumName = `${tableName}_${colName}`;\n enums.set(enumName, col.enumValues);\n }\n }\n }\n\n if (enums.size === 0) return '';\n\n const enumDefs: string[] = [];\n\n for (const [name, values] of enums) {\n const enumVarName = toCamelCase(applyPrefix(name, tablePrefix)) + 'Enum';\n const enumDbName = applyPrefix(name, tablePrefix);\n\n switch (provider) {\n case 'pg':\n enumDefs.push(\n `export const ${enumVarName} = pgEnum('${enumDbName}', [${values.map((v) => `'${v}'`).join(', ')}]);`\n );\n break;\n case 'mysql':\n enumDefs.push(\n `export const ${enumVarName} = mysqlEnum('${enumDbName}', [${values.map((v) => `'${v}'`).join(', ')}]);`\n );\n break;\n case 'sqlite':\n // SQLite doesn't have native enums, we use text with CHECK constraint\n enumDefs.push(\n `// SQLite enum values for ${enumVarName}: ${values.map((v) => `'${v}'`).join(', ')}`\n );\n break;\n }\n }\n\n return `// ============================================\n// Enums\n// ============================================\n\n${enumDefs.join('\\n\\n')}`;\n}\n\nfunction generateTables(provider: DatabaseProvider, tablePrefix: string): string {\n const tableDefs: string[] = [];\n\n for (const [tableName, table] of Object.entries(mdmSchema.tables)) {\n tableDefs.push(generateTable(tableName, table, provider, tablePrefix));\n }\n\n return `// ============================================\n// Tables\n// ============================================\n\n${tableDefs.join('\\n\\n')}`;\n}\n\nfunction generateTable(\n tableName: string,\n table: TableDefinition,\n provider: DatabaseProvider,\n tablePrefix: string\n): string {\n const varName = toCamelCase(applyPrefix(tableName, tablePrefix));\n const dbTableName = applyPrefix(tableName, tablePrefix);\n const tableFunc = getTableFunc(provider);\n\n const columns = Object.entries(table.columns)\n .map(([colName, col]) => generateColumn(colName, col, tableName, provider, tablePrefix))\n .join(',\\n ');\n\n const indexes = table.indexes\n ? generateIndexes(table.indexes, varName, provider)\n : '';\n\n return `export const ${varName} = ${tableFunc}(\n '${dbTableName}',\n {\n ${columns}\n }${indexes ? `,\\n (table) => [\\n${indexes}\\n ]` : ''}\n);`;\n}\n\nfunction generateColumn(\n colName: string,\n col: ColumnDefinition,\n tableName: string,\n provider: DatabaseProvider,\n tablePrefix: string\n): string {\n const camelColName = toCamelCase(colName);\n let colDef = '';\n\n switch (provider) {\n case 'pg':\n colDef = generatePgColumn(colName, col, tableName, tablePrefix);\n break;\n case 'mysql':\n colDef = generateMysqlColumn(colName, col, tableName, tablePrefix);\n break;\n case 'sqlite':\n colDef = generateSqliteColumn(colName, col, tableName, tablePrefix);\n break;\n }\n\n return `${camelColName}: ${colDef}`;\n}\n\nfunction generatePgColumn(\n colName: string,\n col: ColumnDefinition,\n tableName: string,\n tablePrefix: string\n): string {\n let def = '';\n\n switch (col.type) {\n case 'string':\n def = `varchar('${colName}', { length: 255 })`;\n break;\n case 'text':\n def = `text('${colName}')`;\n break;\n case 'integer':\n def = `integer('${colName}')`;\n break;\n case 'bigint':\n def = `bigint('${colName}', { mode: 'number' })`;\n break;\n case 'boolean':\n def = `boolean('${colName}')`;\n break;\n case 'datetime':\n def = `timestamp('${colName}', { withTimezone: true })`;\n break;\n case 'json':\n def = `json('${colName}')`;\n break;\n case 'enum':\n const enumVarName = toCamelCase(applyPrefix(`${tableName}_${colName}`, tablePrefix)) + 'Enum';\n def = `${enumVarName}('${colName}')`;\n break;\n }\n\n // Add constraints\n if (col.primaryKey) {\n def += '.primaryKey()';\n }\n if (col.unique) {\n def += '.unique()';\n }\n if (!col.nullable && !col.primaryKey) {\n def += '.notNull()';\n }\n if (col.default !== undefined) {\n if (col.default === 'now') {\n def += '.defaultNow()';\n } else if (typeof col.default === 'boolean') {\n def += `.default(${col.default})`;\n } else if (typeof col.default === 'number') {\n def += `.default(${col.default})`;\n } else if (typeof col.default === 'string') {\n def += `.default('${col.default}')`;\n }\n }\n\n // Add foreign key reference\n if (col.references) {\n const refTable = toCamelCase(applyPrefix(col.references.table, tablePrefix));\n const onDelete = col.references.onDelete || 'restrict';\n def += `.references(() => ${refTable}.${toCamelCase(col.references.column)}, { onDelete: '${onDelete}' })`;\n }\n\n return def;\n}\n\nfunction generateMysqlColumn(\n colName: string,\n col: ColumnDefinition,\n tableName: string,\n tablePrefix: string\n): string {\n let def = '';\n\n switch (col.type) {\n case 'string':\n def = `varchar('${colName}', { length: 255 })`;\n break;\n case 'text':\n def = `text('${colName}')`;\n break;\n case 'integer':\n def = `int('${colName}')`;\n break;\n case 'bigint':\n def = `bigint('${colName}', { mode: 'number' })`;\n break;\n case 'boolean':\n def = `boolean('${colName}')`;\n break;\n case 'datetime':\n def = `timestamp('${colName}')`;\n break;\n case 'json':\n def = `json('${colName}')`;\n break;\n case 'enum':\n const enumVarName = toCamelCase(applyPrefix(`${tableName}_${colName}`, tablePrefix)) + 'Enum';\n def = `${enumVarName}('${colName}')`;\n break;\n }\n\n // Add constraints\n if (col.primaryKey) {\n def += '.primaryKey()';\n }\n if (col.unique) {\n def += '.unique()';\n }\n if (!col.nullable && !col.primaryKey) {\n def += '.notNull()';\n }\n if (col.default !== undefined) {\n if (col.default === 'now') {\n def += '.defaultNow()';\n } else if (typeof col.default === 'boolean') {\n def += `.default(${col.default})`;\n } else if (typeof col.default === 'number') {\n def += `.default(${col.default})`;\n } else if (typeof col.default === 'string') {\n def += `.default('${col.default}')`;\n }\n }\n\n // Add foreign key reference\n if (col.references) {\n const refTable = toCamelCase(applyPrefix(col.references.table, tablePrefix));\n const onDelete = col.references.onDelete || 'restrict';\n def += `.references(() => ${refTable}.${toCamelCase(col.references.column)}, { onDelete: '${onDelete}' })`;\n }\n\n return def;\n}\n\nfunction generateSqliteColumn(\n colName: string,\n col: ColumnDefinition,\n tableName: string,\n tablePrefix: string\n): string {\n let def = '';\n\n switch (col.type) {\n case 'string':\n case 'text':\n case 'enum':\n def = `text('${colName}')`;\n break;\n case 'integer':\n case 'bigint':\n def = `integer('${colName}')`;\n break;\n case 'boolean':\n def = `integer('${colName}', { mode: 'boolean' })`;\n break;\n case 'datetime':\n def = `integer('${colName}', { mode: 'timestamp_ms' })`;\n break;\n case 'json':\n def = `text('${colName}', { mode: 'json' })`;\n break;\n }\n\n // Add constraints\n if (col.primaryKey) {\n def += '.primaryKey()';\n }\n if (col.unique) {\n def += '.unique()';\n }\n if (!col.nullable && !col.primaryKey) {\n def += '.notNull()';\n }\n if (col.default !== undefined) {\n if (col.default === 'now') {\n def += `.default(sql\\`(cast(unixepoch('subsecond') * 1000 as integer))\\`)`;\n } else if (typeof col.default === 'boolean') {\n def += `.default(${col.default})`;\n } else if (typeof col.default === 'number') {\n def += `.default(${col.default})`;\n } else if (typeof col.default === 'string') {\n def += `.default('${col.default}')`;\n }\n }\n\n // Add foreign key reference\n if (col.references) {\n const refTable = toCamelCase(applyPrefix(col.references.table, tablePrefix));\n const onDelete = col.references.onDelete || 'restrict';\n def += `.references(() => ${refTable}.${toCamelCase(col.references.column)}, { onDelete: '${onDelete}' })`;\n }\n\n return def;\n}\n\nfunction generateIndexes(\n indexes: IndexDefinition[],\n tableVarName: string,\n provider: DatabaseProvider\n): string {\n return indexes\n .map((idx) => {\n const cols = idx.columns.map((c) => `table.${toCamelCase(c)}`).join(', ');\n const idxName = idx.name || `${tableVarName}_${idx.columns.join('_')}_idx`;\n\n if (idx.unique) {\n return ` uniqueIndex('${idxName}').on(${cols})`;\n }\n return ` index('${idxName}').on(${cols})`;\n })\n .join(',\\n');\n}\n\nfunction generateRelations(tablePrefix: string): string {\n // Generate Drizzle relations based on foreign keys\n const relations: string[] = [];\n\n for (const [tableName, table] of Object.entries(mdmSchema.tables)) {\n const varName = toCamelCase(applyPrefix(tableName, tablePrefix));\n const fks: { colName: string; refTable: string; refCol: string }[] = [];\n const reverseRefs: { fromTable: string; fromCol: string }[] = [];\n\n // Find foreign keys in this table\n for (const [colName, col] of Object.entries(table.columns)) {\n if (col.references) {\n fks.push({\n colName,\n refTable: col.references.table,\n refCol: col.references.column,\n });\n }\n }\n\n // Find tables that reference this table\n for (const [otherTableName, otherTable] of Object.entries(mdmSchema.tables)) {\n if (otherTableName === tableName) continue;\n for (const [otherColName, otherCol] of Object.entries(otherTable.columns)) {\n if (otherCol.references?.table === tableName) {\n reverseRefs.push({\n fromTable: otherTableName,\n fromCol: otherColName,\n });\n }\n }\n }\n\n if (fks.length === 0 && reverseRefs.length === 0) continue;\n\n const relationParts: string[] = [];\n\n // one() relations for foreign keys\n for (const fk of fks) {\n const refTableVar = toCamelCase(applyPrefix(fk.refTable, tablePrefix));\n const colVar = toCamelCase(fk.colName);\n const refColVar = toCamelCase(fk.refCol);\n const relationName = fk.colName.replace(/_id$/, '');\n\n relationParts.push(\n ` ${toCamelCase(relationName)}: one(${refTableVar}, {\n fields: [${varName}.${colVar}],\n references: [${refTableVar}.${refColVar}],\n })`\n );\n }\n\n // many() relations for reverse references\n for (const ref of reverseRefs) {\n const fromTableVar = toCamelCase(applyPrefix(ref.fromTable, tablePrefix));\n const pluralName = ref.fromTable.replace(/^mdm_/, '');\n\n relationParts.push(` ${toCamelCase(pluralName)}: many(${fromTableVar})`);\n }\n\n if (relationParts.length > 0) {\n relations.push(\n `export const ${varName}Relations = relations(${varName}, ({ one, many }) => ({\n${relationParts.join(',\\n')}\n}));`\n );\n }\n }\n\n if (relations.length === 0) return '';\n\n return `// ============================================\n// Relations\n// ============================================\n\n${relations.join('\\n\\n')}`;\n}\n\nfunction getTableFunc(provider: DatabaseProvider): string {\n switch (provider) {\n case 'pg':\n return 'pgTable';\n case 'mysql':\n return 'mysqlTable';\n case 'sqlite':\n return 'sqliteTable';\n }\n}\n\nfunction toCamelCase(str: string): string {\n return str.replace(/_([a-z])/g, (_, letter) => letter.toUpperCase());\n}\n\nfunction applyPrefix(name: string, prefix: string): string {\n if (!prefix) return name;\n return `${prefix}_${name}`;\n}\n","/**\n * SQL Schema Generator\n *\n * Generates raw SQL schema files from OpenMDM core schema.\n */\n\nimport {\n mdmSchema,\n type ColumnDefinition,\n type TableDefinition,\n type IndexDefinition,\n} from '@openmdm/core/schema';\nimport type { DatabaseProvider } from './index.js';\n\ninterface SqlGeneratorOptions {\n provider: DatabaseProvider;\n tablePrefix?: string;\n}\n\n/**\n * Generate raw SQL schema\n */\nexport function generateSqlSchema(options: SqlGeneratorOptions): string {\n const { provider, tablePrefix = '' } = options;\n\n switch (provider) {\n case 'pg':\n return generatePostgresSchema(tablePrefix);\n case 'mysql':\n return generateMysqlSchema(tablePrefix);\n case 'sqlite':\n return generateSqliteSchema(tablePrefix);\n }\n}\n\nfunction generatePostgresSchema(tablePrefix: string): string {\n const lines: string[] = [\n '-- OpenMDM PostgreSQL Schema',\n '-- Generated by @openmdm/cli - Do not edit manually.',\n '-- Regenerate with: npx openmdm generate --adapter sql --provider pg',\n '',\n ];\n\n // Generate enums\n const enums = collectEnums();\n for (const [name, values] of enums) {\n const enumName = applyPrefix(name, tablePrefix);\n lines.push(`DO $$ BEGIN`);\n lines.push(` CREATE TYPE \"${enumName}\" AS ENUM (${values.map((v) => `'${v}'`).join(', ')});`);\n lines.push(`EXCEPTION`);\n lines.push(` WHEN duplicate_object THEN null;`);\n lines.push(`END $$;`);\n lines.push('');\n }\n\n // Generate tables\n for (const [tableName, table] of Object.entries(mdmSchema.tables)) {\n lines.push(generatePostgresTable(tableName, table, tablePrefix));\n lines.push('');\n\n // Generate indexes\n if (table.indexes) {\n for (const idx of table.indexes) {\n lines.push(generatePostgresIndex(tableName, idx, tablePrefix));\n }\n lines.push('');\n }\n }\n\n return lines.join('\\n');\n}\n\nfunction generatePostgresTable(\n tableName: string,\n table: TableDefinition,\n tablePrefix: string\n): string {\n const dbTableName = applyPrefix(tableName, tablePrefix);\n const columns: string[] = [];\n const constraints: string[] = [];\n\n for (const [colName, col] of Object.entries(table.columns)) {\n columns.push(generatePostgresColumn(colName, col, tableName, tablePrefix));\n\n // Collect FK constraints\n if (col.references) {\n const refTable = applyPrefix(col.references.table, tablePrefix);\n const onDelete = col.references.onDelete?.toUpperCase() || 'RESTRICT';\n constraints.push(\n ` CONSTRAINT \"fk_${dbTableName}_${colName}\" FOREIGN KEY (\"${colName}\") REFERENCES \"${refTable}\"(\"${col.references.column}\") ON DELETE ${onDelete.replace('_', ' ')}`\n );\n }\n }\n\n // Find composite primary key (for junction tables)\n const pkCols = Object.entries(table.columns)\n .filter(([_, col]) => col.primaryKey)\n .map(([name]) => `\"${name}\"`);\n\n if (pkCols.length === 0) {\n // Check for unique index that could be a composite PK\n const uniqueIdx = table.indexes?.find((idx) => idx.unique && idx.columns.length > 1);\n if (uniqueIdx) {\n constraints.push(\n ` PRIMARY KEY (${uniqueIdx.columns.map((c) => `\"${c}\"`).join(', ')})`\n );\n }\n }\n\n const allParts = [...columns, ...constraints];\n\n return `CREATE TABLE IF NOT EXISTS \"${dbTableName}\" (\n${allParts.join(',\\n')}\n);`;\n}\n\nfunction generatePostgresColumn(\n colName: string,\n col: ColumnDefinition,\n tableName: string,\n tablePrefix: string\n): string {\n let type = '';\n\n switch (col.type) {\n case 'string':\n type = 'varchar(255)';\n break;\n case 'text':\n type = 'text';\n break;\n case 'integer':\n type = 'integer';\n break;\n case 'bigint':\n type = 'bigint';\n break;\n case 'boolean':\n type = 'boolean';\n break;\n case 'datetime':\n type = 'timestamp with time zone';\n break;\n case 'json':\n type = 'json';\n break;\n case 'enum':\n type = `\"${applyPrefix(`${tableName}_${colName}`, tablePrefix)}\"`;\n break;\n }\n\n let def = ` \"${colName}\" ${type}`;\n\n if (col.primaryKey) {\n def += ' PRIMARY KEY';\n }\n if (col.unique) {\n def += ' UNIQUE';\n }\n if (!col.nullable && !col.primaryKey) {\n def += ' NOT NULL';\n }\n if (col.default !== undefined) {\n if (col.default === 'now') {\n def += ' DEFAULT now()';\n } else if (typeof col.default === 'boolean') {\n def += ` DEFAULT ${col.default}`;\n } else if (typeof col.default === 'number') {\n def += ` DEFAULT ${col.default}`;\n } else if (typeof col.default === 'string') {\n def += ` DEFAULT '${col.default}'`;\n }\n }\n\n return def;\n}\n\nfunction generatePostgresIndex(\n tableName: string,\n idx: IndexDefinition,\n tablePrefix: string\n): string {\n const dbTableName = applyPrefix(tableName, tablePrefix);\n const idxName = idx.name || `idx_${dbTableName}_${idx.columns.join('_')}`;\n const cols = idx.columns.map((c) => `\"${c}\"`).join(', ');\n const unique = idx.unique ? 'UNIQUE ' : '';\n\n return `CREATE ${unique}INDEX IF NOT EXISTS \"${idxName}\" ON \"${dbTableName}\" (${cols});`;\n}\n\nfunction generateMysqlSchema(tablePrefix: string): string {\n const lines: string[] = [\n '-- OpenMDM MySQL Schema',\n '-- Generated by @openmdm/cli - Do not edit manually.',\n '-- Regenerate with: npx openmdm generate --adapter sql --provider mysql',\n '',\n ];\n\n // Generate tables\n for (const [tableName, table] of Object.entries(mdmSchema.tables)) {\n lines.push(generateMysqlTable(tableName, table, tablePrefix));\n lines.push('');\n }\n\n return lines.join('\\n');\n}\n\nfunction generateMysqlTable(\n tableName: string,\n table: TableDefinition,\n tablePrefix: string\n): string {\n const dbTableName = applyPrefix(tableName, tablePrefix);\n const columns: string[] = [];\n const constraints: string[] = [];\n const indexes: string[] = [];\n\n for (const [colName, col] of Object.entries(table.columns)) {\n columns.push(generateMysqlColumn(colName, col, tableName));\n\n // Collect FK constraints\n if (col.references) {\n const refTable = applyPrefix(col.references.table, tablePrefix);\n const onDelete = col.references.onDelete?.toUpperCase() || 'RESTRICT';\n constraints.push(\n ` CONSTRAINT \\`fk_${dbTableName}_${colName}\\` FOREIGN KEY (\\`${colName}\\`) REFERENCES \\`${refTable}\\`(\\`${col.references.column}\\`) ON DELETE ${onDelete.replace('_', ' ')}`\n );\n }\n }\n\n // Generate indexes\n if (table.indexes) {\n for (const idx of table.indexes) {\n const idxName = idx.name || `idx_${dbTableName}_${idx.columns.join('_')}`;\n const cols = idx.columns.map((c) => `\\`${c}\\``).join(', ');\n const unique = idx.unique ? 'UNIQUE ' : '';\n indexes.push(` ${unique}INDEX \\`${idxName}\\` (${cols})`);\n }\n }\n\n const allParts = [...columns, ...constraints, ...indexes];\n\n return `CREATE TABLE IF NOT EXISTS \\`${dbTableName}\\` (\n${allParts.join(',\\n')}\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;`;\n}\n\nfunction generateMysqlColumn(\n colName: string,\n col: ColumnDefinition,\n tableName: string\n): string {\n let type = '';\n\n switch (col.type) {\n case 'string':\n type = 'VARCHAR(255)';\n break;\n case 'text':\n type = 'TEXT';\n break;\n case 'integer':\n type = 'INT';\n break;\n case 'bigint':\n type = 'BIGINT';\n break;\n case 'boolean':\n type = 'BOOLEAN';\n break;\n case 'datetime':\n type = 'TIMESTAMP';\n break;\n case 'json':\n type = 'JSON';\n break;\n case 'enum':\n type = `ENUM(${col.enumValues?.map((v) => `'${v}'`).join(', ')})`;\n break;\n }\n\n let def = ` \\`${colName}\\` ${type}`;\n\n if (col.primaryKey) {\n def += ' PRIMARY KEY';\n }\n if (col.unique) {\n def += ' UNIQUE';\n }\n if (!col.nullable && !col.primaryKey) {\n def += ' NOT NULL';\n }\n if (col.default !== undefined) {\n if (col.default === 'now') {\n def += ' DEFAULT CURRENT_TIMESTAMP';\n } else if (typeof col.default === 'boolean') {\n def += ` DEFAULT ${col.default}`;\n } else if (typeof col.default === 'number') {\n def += ` DEFAULT ${col.default}`;\n } else if (typeof col.default === 'string') {\n def += ` DEFAULT '${col.default}'`;\n }\n }\n\n return def;\n}\n\nfunction generateSqliteSchema(tablePrefix: string): string {\n const lines: string[] = [\n '-- OpenMDM SQLite Schema',\n '-- Generated by @openmdm/cli - Do not edit manually.',\n '-- Regenerate with: npx openmdm generate --adapter sql --provider sqlite',\n '',\n ];\n\n // Generate tables\n for (const [tableName, table] of Object.entries(mdmSchema.tables)) {\n lines.push(generateSqliteTable(tableName, table, tablePrefix));\n lines.push('');\n\n // Generate indexes\n if (table.indexes) {\n for (const idx of table.indexes) {\n lines.push(generateSqliteIndex(tableName, idx, tablePrefix));\n }\n lines.push('');\n }\n }\n\n return lines.join('\\n');\n}\n\nfunction generateSqliteTable(\n tableName: string,\n table: TableDefinition,\n tablePrefix: string\n): string {\n const dbTableName = applyPrefix(tableName, tablePrefix);\n const columns: string[] = [];\n const constraints: string[] = [];\n\n for (const [colName, col] of Object.entries(table.columns)) {\n columns.push(generateSqliteColumn(colName, col));\n\n // Collect FK constraints\n if (col.references) {\n const refTable = applyPrefix(col.references.table, tablePrefix);\n const onDelete = col.references.onDelete?.toUpperCase() || 'RESTRICT';\n constraints.push(\n ` FOREIGN KEY (\"${colName}\") REFERENCES \"${refTable}\"(\"${col.references.column}\") ON DELETE ${onDelete.replace('_', ' ')}`\n );\n }\n }\n\n // Find composite primary key\n const uniqueIdx = table.indexes?.find((idx) => idx.unique && idx.columns.length > 1);\n if (uniqueIdx) {\n const hasSinglePk = Object.values(table.columns).some((c) => c.primaryKey);\n if (!hasSinglePk) {\n constraints.push(\n ` PRIMARY KEY (${uniqueIdx.columns.map((c) => `\"${c}\"`).join(', ')})`\n );\n }\n }\n\n const allParts = [...columns, ...constraints];\n\n return `CREATE TABLE IF NOT EXISTS \"${dbTableName}\" (\n${allParts.join(',\\n')}\n);`;\n}\n\nfunction generateSqliteColumn(colName: string, col: ColumnDefinition): string {\n let type = '';\n\n switch (col.type) {\n case 'string':\n case 'text':\n case 'enum':\n type = 'TEXT';\n break;\n case 'integer':\n case 'bigint':\n case 'boolean':\n type = 'INTEGER';\n break;\n case 'datetime':\n type = 'INTEGER'; // Store as Unix timestamp\n break;\n case 'json':\n type = 'TEXT'; // JSON stored as text\n break;\n }\n\n let def = ` \"${colName}\" ${type}`;\n\n if (col.primaryKey) {\n def += ' PRIMARY KEY';\n }\n if (col.unique) {\n def += ' UNIQUE';\n }\n if (!col.nullable && !col.primaryKey) {\n def += ' NOT NULL';\n }\n if (col.default !== undefined) {\n if (col.default === 'now') {\n def += ` DEFAULT (cast(unixepoch('subsecond') * 1000 as integer))`;\n } else if (typeof col.default === 'boolean') {\n def += ` DEFAULT ${col.default ? 1 : 0}`;\n } else if (typeof col.default === 'number') {\n def += ` DEFAULT ${col.default}`;\n } else if (typeof col.default === 'string') {\n def += ` DEFAULT '${col.default}'`;\n }\n }\n\n return def;\n}\n\nfunction generateSqliteIndex(\n tableName: string,\n idx: IndexDefinition,\n tablePrefix: string\n): string {\n const dbTableName = applyPrefix(tableName, tablePrefix);\n const idxName = idx.name || `idx_${dbTableName}_${idx.columns.join('_')}`;\n const cols = idx.columns.map((c) => `\"${c}\"`).join(', ');\n const unique = idx.unique ? 'UNIQUE ' : '';\n\n return `CREATE ${unique}INDEX IF NOT EXISTS \"${idxName}\" ON \"${dbTableName}\" (${cols});`;\n}\n\nfunction collectEnums(): Map<string, string[]> {\n const enums: Map<string, string[]> = new Map();\n\n for (const [tableName, table] of Object.entries(mdmSchema.tables)) {\n for (const [colName, col] of Object.entries(table.columns)) {\n if (col.type === 'enum' && col.enumValues) {\n const enumName = `${tableName}_${colName}`;\n enums.set(enumName, col.enumValues);\n }\n }\n }\n\n return enums;\n}\n\nfunction applyPrefix(name: string, prefix: string): string {\n if (!prefix) return name;\n return `${prefix}_${name}`;\n}\n"],"mappings":"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","names":["mdmSchema","applyPrefix","generateMysqlColumn","generateSqliteColumn"]}
|
package/dist/index.js
CHANGED
|
@@ -11,8 +11,12 @@ program.command("init").description("Initialize a new OpenMDM configuration").op
|
|
|
11
11
|
const { initProject } = await import("./init-625O2CRF.js");
|
|
12
12
|
await initProject(options);
|
|
13
13
|
});
|
|
14
|
-
program.command("
|
|
15
|
-
const {
|
|
14
|
+
program.command("generate").description("Generate database schema for your ORM").option("-a, --adapter <type>", "Adapter type (drizzle, sql)").option("-p, --provider <type>", "Database provider (pg, mysql, sqlite)").option("-o, --output <path>", "Output file path").option("-y, --yes", "Skip confirmation prompts").action(async (options) => {
|
|
15
|
+
const { runGenerate } = await import("./generate-466DLVPN.js");
|
|
16
|
+
await runGenerate(options);
|
|
17
|
+
});
|
|
18
|
+
program.command("migrate").description("Run database migrations (deprecated: use generate + drizzle-kit)").option("--dry-run", "Show migration SQL without executing").option("--rollback", "Rollback last migration").action(async (options) => {
|
|
19
|
+
const { runMigrations } = await import("./migrate-XBKC6VPK.js");
|
|
16
20
|
await runMigrations(options);
|
|
17
21
|
});
|
|
18
22
|
var deviceCmd = program.command("device").description("Device management commands");
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["/**\n * OpenMDM CLI\n *\n * Command-line tools for managing OpenMDM deployments.\n *\n * Commands:\n * - init: Initialize a new OpenMDM project\n * - migrate: Run database migrations\n * - device: Manage devices (list, show, wipe, etc.)\n * - policy: Manage policies\n * - enroll: Generate enrollment QR codes/tokens\n * - push: Test push notifications\n */\n\nimport { Command } from 'commander';\nimport chalk from 'chalk';\nimport { config as dotenvConfig } from 'dotenv';\n\n// Load environment variables\ndotenvConfig();\n\nconst program = new Command();\n\nprogram\n .name('openmdm')\n .description('OpenMDM CLI - Mobile Device Management tools')\n .version('0.1.0');\n\n// Init command\nprogram\n .command('init')\n .description('Initialize a new OpenMDM configuration')\n .option('-d, --database <type>', 'Database type (postgres, mysql, sqlite)', 'postgres')\n .option('-p, --push <type>', 'Push provider (fcm, mqtt, polling)', 'fcm')\n .action(async (options) => {\n const { initProject } = await import('./commands/init.js');\n await initProject(options);\n });\n\n// Migrate command\nprogram\n .command('migrate')\n .description('Run database migrations')\n .option('--dry-run', 'Show migration SQL without executing')\n .option('--rollback', 'Rollback last migration')\n .action(async (options) => {\n const { runMigrations } = await import('./commands/migrate.js');\n await runMigrations(options);\n });\n\n// Device commands\nconst deviceCmd = program\n .command('device')\n .description('Device management commands');\n\ndeviceCmd\n .command('list')\n .description('List all enrolled devices')\n .option('-s, --status <status>', 'Filter by status (enrolled, pending, blocked)')\n .option('-l, --limit <number>', 'Limit results', '50')\n .option('-j, --json', 'Output as JSON')\n .action(async (options) => {\n const { listDevices } = await import('./commands/device.js');\n await listDevices(options);\n });\n\ndeviceCmd\n .command('show <deviceId>')\n .description('Show device details')\n .option('-j, --json', 'Output as JSON')\n .action(async (deviceId, options) => {\n const { showDevice } = await import('./commands/device.js');\n await showDevice(deviceId, options);\n });\n\ndeviceCmd\n .command('sync <deviceId>')\n .description('Send sync command to device')\n .action(async (deviceId) => {\n const { syncDevice } = await import('./commands/device.js');\n await syncDevice(deviceId);\n });\n\ndeviceCmd\n .command('lock <deviceId>')\n .description('Lock a device')\n .option('-m, --message <message>', 'Lock screen message')\n .action(async (deviceId, options) => {\n const { lockDevice } = await import('./commands/device.js');\n await lockDevice(deviceId, options);\n });\n\ndeviceCmd\n .command('wipe <deviceId>')\n .description('Wipe/factory reset a device')\n .option('-f, --force', 'Skip confirmation')\n .option('--preserve-data', 'Preserve SD card data')\n .action(async (deviceId, options) => {\n const { wipeDevice } = await import('./commands/device.js');\n await wipeDevice(deviceId, options);\n });\n\ndeviceCmd\n .command('remove <deviceId>')\n .description('Remove a device from MDM')\n .option('-f, --force', 'Skip confirmation')\n .action(async (deviceId, options) => {\n const { removeDevice } = await import('./commands/device.js');\n await removeDevice(deviceId, options);\n });\n\n// Policy commands\nconst policyCmd = program\n .command('policy')\n .description('Policy management commands');\n\npolicyCmd\n .command('list')\n .description('List all policies')\n .option('-j, --json', 'Output as JSON')\n .action(async (options) => {\n const { listPolicies } = await import('./commands/policy.js');\n await listPolicies(options);\n });\n\npolicyCmd\n .command('show <policyId>')\n .description('Show policy details')\n .option('-j, --json', 'Output as JSON')\n .action(async (policyId, options) => {\n const { showPolicy } = await import('./commands/policy.js');\n await showPolicy(policyId, options);\n });\n\npolicyCmd\n .command('create')\n .description('Create a new policy interactively')\n .option('-f, --file <path>', 'Create from JSON file')\n .action(async (options) => {\n const { createPolicy } = await import('./commands/policy.js');\n await createPolicy(options);\n });\n\npolicyCmd\n .command('apply <policyId> <deviceId>')\n .description('Apply policy to a device')\n .action(async (policyId, deviceId) => {\n const { applyPolicy } = await import('./commands/policy.js');\n await applyPolicy(policyId, deviceId);\n });\n\n// Enrollment commands\nconst enrollCmd = program\n .command('enroll')\n .description('Device enrollment commands');\n\nenrollCmd\n .command('qr')\n .description('Generate enrollment QR code')\n .option('-p, --policy <policyId>', 'Pre-assign policy')\n .option('-g, --group <groupId>', 'Pre-assign group')\n .option('-o, --output <path>', 'Save QR code to file')\n .option('--ascii', 'Output ASCII QR code to terminal')\n .action(async (options) => {\n const { generateQR } = await import('./commands/enroll.js');\n await generateQR(options);\n });\n\nenrollCmd\n .command('token')\n .description('Generate enrollment token')\n .option('-p, --policy <policyId>', 'Pre-assign policy')\n .option('-g, --group <groupId>', 'Pre-assign group')\n .option('-e, --expires <hours>', 'Token expiration in hours', '24')\n .action(async (options) => {\n const { generateToken } = await import('./commands/enroll.js');\n await generateToken(options);\n });\n\n// Push test command\nprogram\n .command('push-test <deviceId>')\n .description('Send test push notification to device')\n .option('-t, --title <title>', 'Notification title', 'Test Notification')\n .option('-m, --message <message>', 'Notification message', 'This is a test from OpenMDM')\n .action(async (deviceId, options) => {\n const { testPush } = await import('./commands/push.js');\n await testPush(deviceId, options);\n });\n\n// Stats command\nprogram\n .command('stats')\n .description('Show MDM statistics')\n .option('-j, --json', 'Output as JSON')\n .action(async (options) => {\n const { showStats } = await import('./commands/stats.js');\n await showStats(options);\n });\n\n// Parse and execute\nprogram.parse();\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["/**\n * OpenMDM CLI\n *\n * Command-line tools for managing OpenMDM deployments.\n *\n * Commands:\n * - init: Initialize a new OpenMDM project\n * - migrate: Run database migrations\n * - device: Manage devices (list, show, wipe, etc.)\n * - policy: Manage policies\n * - enroll: Generate enrollment QR codes/tokens\n * - push: Test push notifications\n */\n\nimport { Command } from 'commander';\nimport chalk from 'chalk';\nimport { config as dotenvConfig } from 'dotenv';\n\n// Load environment variables\ndotenvConfig();\n\nconst program = new Command();\n\nprogram\n .name('openmdm')\n .description('OpenMDM CLI - Mobile Device Management tools')\n .version('0.1.0');\n\n// Init command\nprogram\n .command('init')\n .description('Initialize a new OpenMDM configuration')\n .option('-d, --database <type>', 'Database type (postgres, mysql, sqlite)', 'postgres')\n .option('-p, --push <type>', 'Push provider (fcm, mqtt, polling)', 'fcm')\n .action(async (options) => {\n const { initProject } = await import('./commands/init.js');\n await initProject(options);\n });\n\n// Generate command (like better-auth)\nprogram\n .command('generate')\n .description('Generate database schema for your ORM')\n .option('-a, --adapter <type>', 'Adapter type (drizzle, sql)')\n .option('-p, --provider <type>', 'Database provider (pg, mysql, sqlite)')\n .option('-o, --output <path>', 'Output file path')\n .option('-y, --yes', 'Skip confirmation prompts')\n .action(async (options) => {\n const { runGenerate } = await import('./commands/generate.js');\n await runGenerate(options);\n });\n\n// Migrate command\nprogram\n .command('migrate')\n .description('Run database migrations (deprecated: use generate + drizzle-kit)')\n .option('--dry-run', 'Show migration SQL without executing')\n .option('--rollback', 'Rollback last migration')\n .action(async (options) => {\n const { runMigrations } = await import('./commands/migrate.js');\n await runMigrations(options);\n });\n\n// Device commands\nconst deviceCmd = program\n .command('device')\n .description('Device management commands');\n\ndeviceCmd\n .command('list')\n .description('List all enrolled devices')\n .option('-s, --status <status>', 'Filter by status (enrolled, pending, blocked)')\n .option('-l, --limit <number>', 'Limit results', '50')\n .option('-j, --json', 'Output as JSON')\n .action(async (options) => {\n const { listDevices } = await import('./commands/device.js');\n await listDevices(options);\n });\n\ndeviceCmd\n .command('show <deviceId>')\n .description('Show device details')\n .option('-j, --json', 'Output as JSON')\n .action(async (deviceId, options) => {\n const { showDevice } = await import('./commands/device.js');\n await showDevice(deviceId, options);\n });\n\ndeviceCmd\n .command('sync <deviceId>')\n .description('Send sync command to device')\n .action(async (deviceId) => {\n const { syncDevice } = await import('./commands/device.js');\n await syncDevice(deviceId);\n });\n\ndeviceCmd\n .command('lock <deviceId>')\n .description('Lock a device')\n .option('-m, --message <message>', 'Lock screen message')\n .action(async (deviceId, options) => {\n const { lockDevice } = await import('./commands/device.js');\n await lockDevice(deviceId, options);\n });\n\ndeviceCmd\n .command('wipe <deviceId>')\n .description('Wipe/factory reset a device')\n .option('-f, --force', 'Skip confirmation')\n .option('--preserve-data', 'Preserve SD card data')\n .action(async (deviceId, options) => {\n const { wipeDevice } = await import('./commands/device.js');\n await wipeDevice(deviceId, options);\n });\n\ndeviceCmd\n .command('remove <deviceId>')\n .description('Remove a device from MDM')\n .option('-f, --force', 'Skip confirmation')\n .action(async (deviceId, options) => {\n const { removeDevice } = await import('./commands/device.js');\n await removeDevice(deviceId, options);\n });\n\n// Policy commands\nconst policyCmd = program\n .command('policy')\n .description('Policy management commands');\n\npolicyCmd\n .command('list')\n .description('List all policies')\n .option('-j, --json', 'Output as JSON')\n .action(async (options) => {\n const { listPolicies } = await import('./commands/policy.js');\n await listPolicies(options);\n });\n\npolicyCmd\n .command('show <policyId>')\n .description('Show policy details')\n .option('-j, --json', 'Output as JSON')\n .action(async (policyId, options) => {\n const { showPolicy } = await import('./commands/policy.js');\n await showPolicy(policyId, options);\n });\n\npolicyCmd\n .command('create')\n .description('Create a new policy interactively')\n .option('-f, --file <path>', 'Create from JSON file')\n .action(async (options) => {\n const { createPolicy } = await import('./commands/policy.js');\n await createPolicy(options);\n });\n\npolicyCmd\n .command('apply <policyId> <deviceId>')\n .description('Apply policy to a device')\n .action(async (policyId, deviceId) => {\n const { applyPolicy } = await import('./commands/policy.js');\n await applyPolicy(policyId, deviceId);\n });\n\n// Enrollment commands\nconst enrollCmd = program\n .command('enroll')\n .description('Device enrollment commands');\n\nenrollCmd\n .command('qr')\n .description('Generate enrollment QR code')\n .option('-p, --policy <policyId>', 'Pre-assign policy')\n .option('-g, --group <groupId>', 'Pre-assign group')\n .option('-o, --output <path>', 'Save QR code to file')\n .option('--ascii', 'Output ASCII QR code to terminal')\n .action(async (options) => {\n const { generateQR } = await import('./commands/enroll.js');\n await generateQR(options);\n });\n\nenrollCmd\n .command('token')\n .description('Generate enrollment token')\n .option('-p, --policy <policyId>', 'Pre-assign policy')\n .option('-g, --group <groupId>', 'Pre-assign group')\n .option('-e, --expires <hours>', 'Token expiration in hours', '24')\n .action(async (options) => {\n const { generateToken } = await import('./commands/enroll.js');\n await generateToken(options);\n });\n\n// Push test command\nprogram\n .command('push-test <deviceId>')\n .description('Send test push notification to device')\n .option('-t, --title <title>', 'Notification title', 'Test Notification')\n .option('-m, --message <message>', 'Notification message', 'This is a test from OpenMDM')\n .action(async (deviceId, options) => {\n const { testPush } = await import('./commands/push.js');\n await testPush(deviceId, options);\n });\n\n// Stats command\nprogram\n .command('stats')\n .description('Show MDM statistics')\n .option('-j, --json', 'Output as JSON')\n .action(async (options) => {\n const { showStats } = await import('./commands/stats.js');\n await showStats(options);\n });\n\n// Parse and execute\nprogram.parse();\n"],"mappings":"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","names":[]}
|
|
@@ -195,6 +195,11 @@ DROP TYPE IF EXISTS mdm_device_status;
|
|
|
195
195
|
`;
|
|
196
196
|
async function runMigrations(options) {
|
|
197
197
|
console.log(chalk.blue("\\n\u{1F4E6} OpenMDM Database Migration\\n"));
|
|
198
|
+
console.log(chalk.yellow("\u26A0\uFE0F Note: This command is deprecated."));
|
|
199
|
+
console.log(chalk.yellow(" Use the new generate command instead:"));
|
|
200
|
+
console.log(chalk.cyan(" npx openmdm generate --adapter drizzle --provider pg"));
|
|
201
|
+
console.log(chalk.cyan(" npx drizzle-kit generate"));
|
|
202
|
+
console.log(chalk.cyan(" npx drizzle-kit migrate\\n"));
|
|
198
203
|
const databaseUrl = process.env.DATABASE_URL;
|
|
199
204
|
if (!databaseUrl && !options.dryRun) {
|
|
200
205
|
console.error(chalk.red("Error: DATABASE_URL environment variable is required"));
|
|
@@ -242,4 +247,4 @@ async function runMigrations(options) {
|
|
|
242
247
|
export {
|
|
243
248
|
runMigrations
|
|
244
249
|
};
|
|
245
|
-
//# sourceMappingURL=migrate-
|
|
250
|
+
//# sourceMappingURL=migrate-XBKC6VPK.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/commands/migrate.ts"],"sourcesContent":["import chalk from 'chalk';\nimport ora from 'ora';\n\ninterface MigrateOptions {\n dryRun?: boolean;\n rollback?: boolean;\n}\n\nconst MIGRATION_SQL = `\n-- OpenMDM Database Schema\n-- PostgreSQL Migration\n\n-- Device Status Enum\nDO $$ BEGIN\n CREATE TYPE mdm_device_status AS ENUM ('pending', 'enrolled', 'unenrolled', 'blocked');\nEXCEPTION\n WHEN duplicate_object THEN null;\nEND $$;\n\n-- Command Status Enum\nDO $$ BEGIN\n CREATE TYPE mdm_command_status AS ENUM ('pending', 'sent', 'acknowledged', 'completed', 'failed', 'cancelled');\nEXCEPTION\n WHEN duplicate_object THEN null;\nEND $$;\n\n-- Push Provider Enum\nDO $$ BEGIN\n CREATE TYPE mdm_push_provider AS ENUM ('fcm', 'mqtt', 'websocket');\nEXCEPTION\n WHEN duplicate_object THEN null;\nEND $$;\n\n-- Devices Table\nCREATE TABLE IF NOT EXISTS mdm_devices (\n id VARCHAR(255) PRIMARY KEY,\n external_id VARCHAR(255),\n enrollment_id VARCHAR(255) NOT NULL UNIQUE,\n status mdm_device_status NOT NULL DEFAULT 'pending',\n\n model VARCHAR(255),\n manufacturer VARCHAR(255),\n os_version VARCHAR(50),\n serial_number VARCHAR(255),\n imei VARCHAR(50),\n mac_address VARCHAR(50),\n android_id VARCHAR(255),\n\n policy_id VARCHAR(255),\n last_heartbeat TIMESTAMP,\n last_sync TIMESTAMP,\n\n battery_level INTEGER,\n storage_used BIGINT,\n storage_total BIGINT,\n location JSONB,\n installed_apps JSONB,\n\n tags JSONB,\n metadata JSONB,\n\n created_at TIMESTAMP NOT NULL DEFAULT NOW(),\n updated_at TIMESTAMP NOT NULL DEFAULT NOW()\n);\n\n-- Policies Table\nCREATE TABLE IF NOT EXISTS mdm_policies (\n id VARCHAR(255) PRIMARY KEY,\n name VARCHAR(255) NOT NULL,\n description TEXT,\n is_default BOOLEAN NOT NULL DEFAULT false,\n settings JSONB NOT NULL DEFAULT '{}',\n created_at TIMESTAMP NOT NULL DEFAULT NOW(),\n updated_at TIMESTAMP NOT NULL DEFAULT NOW()\n);\n\n-- Applications Table\nCREATE TABLE IF NOT EXISTS mdm_applications (\n id VARCHAR(255) PRIMARY KEY,\n name VARCHAR(255) NOT NULL,\n package_name VARCHAR(255) NOT NULL,\n version VARCHAR(50) NOT NULL,\n version_code INTEGER NOT NULL,\n url TEXT NOT NULL,\n hash VARCHAR(255),\n size BIGINT,\n min_sdk_version INTEGER,\n\n show_icon BOOLEAN NOT NULL DEFAULT true,\n run_after_install BOOLEAN NOT NULL DEFAULT false,\n run_at_boot BOOLEAN NOT NULL DEFAULT false,\n is_system BOOLEAN NOT NULL DEFAULT false,\n is_active BOOLEAN NOT NULL DEFAULT true,\n\n metadata JSONB,\n created_at TIMESTAMP NOT NULL DEFAULT NOW(),\n updated_at TIMESTAMP NOT NULL DEFAULT NOW()\n);\n\n-- Commands Table\nCREATE TABLE IF NOT EXISTS mdm_commands (\n id VARCHAR(255) PRIMARY KEY,\n device_id VARCHAR(255) NOT NULL REFERENCES mdm_devices(id) ON DELETE CASCADE,\n type VARCHAR(50) NOT NULL,\n payload JSONB,\n status mdm_command_status NOT NULL DEFAULT 'pending',\n result JSONB,\n error TEXT,\n\n created_at TIMESTAMP NOT NULL DEFAULT NOW(),\n sent_at TIMESTAMP,\n acknowledged_at TIMESTAMP,\n completed_at TIMESTAMP\n);\n\n-- Events Table\nCREATE TABLE IF NOT EXISTS mdm_events (\n id VARCHAR(255) PRIMARY KEY,\n device_id VARCHAR(255) NOT NULL REFERENCES mdm_devices(id) ON DELETE CASCADE,\n type VARCHAR(100) NOT NULL,\n payload JSONB NOT NULL DEFAULT '{}',\n created_at TIMESTAMP NOT NULL DEFAULT NOW()\n);\n\n-- Groups Table\nCREATE TABLE IF NOT EXISTS mdm_groups (\n id VARCHAR(255) PRIMARY KEY,\n name VARCHAR(255) NOT NULL,\n description TEXT,\n policy_id VARCHAR(255) REFERENCES mdm_policies(id) ON DELETE SET NULL,\n parent_id VARCHAR(255) REFERENCES mdm_groups(id) ON DELETE SET NULL,\n metadata JSONB,\n created_at TIMESTAMP NOT NULL DEFAULT NOW(),\n updated_at TIMESTAMP NOT NULL DEFAULT NOW()\n);\n\n-- Device Groups Junction Table\nCREATE TABLE IF NOT EXISTS mdm_device_groups (\n device_id VARCHAR(255) NOT NULL REFERENCES mdm_devices(id) ON DELETE CASCADE,\n group_id VARCHAR(255) NOT NULL REFERENCES mdm_groups(id) ON DELETE CASCADE,\n PRIMARY KEY (device_id, group_id)\n);\n\n-- Push Tokens Table\nCREATE TABLE IF NOT EXISTS mdm_push_tokens (\n id VARCHAR(255) PRIMARY KEY,\n device_id VARCHAR(255) NOT NULL REFERENCES mdm_devices(id) ON DELETE CASCADE,\n provider mdm_push_provider NOT NULL,\n token TEXT NOT NULL,\n is_active BOOLEAN NOT NULL DEFAULT true,\n created_at TIMESTAMP NOT NULL DEFAULT NOW(),\n updated_at TIMESTAMP NOT NULL DEFAULT NOW(),\n UNIQUE(device_id, provider)\n);\n\n-- App Deployments Table\nCREATE TABLE IF NOT EXISTS mdm_app_deployments (\n id VARCHAR(255) PRIMARY KEY,\n application_id VARCHAR(255) NOT NULL REFERENCES mdm_applications(id) ON DELETE CASCADE,\n target_type VARCHAR(20) NOT NULL, -- 'device', 'policy', 'group'\n target_id VARCHAR(255) NOT NULL,\n created_at TIMESTAMP NOT NULL DEFAULT NOW()\n);\n\n-- Indexes\nCREATE INDEX IF NOT EXISTS idx_devices_status ON mdm_devices(status);\nCREATE INDEX IF NOT EXISTS idx_devices_policy ON mdm_devices(policy_id);\nCREATE INDEX IF NOT EXISTS idx_devices_enrollment ON mdm_devices(enrollment_id);\nCREATE INDEX IF NOT EXISTS idx_devices_last_heartbeat ON mdm_devices(last_heartbeat);\n\nCREATE INDEX IF NOT EXISTS idx_commands_device ON mdm_commands(device_id);\nCREATE INDEX IF NOT EXISTS idx_commands_status ON mdm_commands(status);\nCREATE INDEX IF NOT EXISTS idx_commands_device_status ON mdm_commands(device_id, status);\n\nCREATE INDEX IF NOT EXISTS idx_events_device ON mdm_events(device_id);\nCREATE INDEX IF NOT EXISTS idx_events_type ON mdm_events(type);\nCREATE INDEX IF NOT EXISTS idx_events_created ON mdm_events(created_at);\n\nCREATE INDEX IF NOT EXISTS idx_applications_package ON mdm_applications(package_name);\nCREATE INDEX IF NOT EXISTS idx_push_tokens_device ON mdm_push_tokens(device_id);\n`;\n\nconst ROLLBACK_SQL = `\n-- OpenMDM Database Rollback\n\nDROP TABLE IF EXISTS mdm_app_deployments;\nDROP TABLE IF EXISTS mdm_push_tokens;\nDROP TABLE IF EXISTS mdm_device_groups;\nDROP TABLE IF EXISTS mdm_events;\nDROP TABLE IF EXISTS mdm_commands;\nDROP TABLE IF EXISTS mdm_applications;\nDROP TABLE IF EXISTS mdm_groups;\nDROP TABLE IF EXISTS mdm_devices;\nDROP TABLE IF EXISTS mdm_policies;\n\nDROP TYPE IF EXISTS mdm_push_provider;\nDROP TYPE IF EXISTS mdm_command_status;\nDROP TYPE IF EXISTS mdm_device_status;\n`;\n\nexport async function runMigrations(options: MigrateOptions): Promise<void> {\n console.log(chalk.blue('\\\\n📦 OpenMDM Database Migration\\\\n'));\n\n const databaseUrl = process.env.DATABASE_URL;\n\n if (!databaseUrl && !options.dryRun) {\n console.error(chalk.red('Error: DATABASE_URL environment variable is required'));\n console.log(chalk.gray('Set it in your .env file or export it before running migrations.'));\n process.exit(1);\n }\n\n if (options.rollback) {\n console.log(chalk.yellow('⚠️ Rolling back migrations...\\\\n'));\n\n if (options.dryRun) {\n console.log(chalk.gray('SQL to execute:'));\n console.log(ROLLBACK_SQL);\n return;\n }\n\n const spinner = ora('Rolling back...').start();\n\n try {\n // In a real implementation, we'd use a database client here\n // For now, just output the SQL\n console.log(chalk.gray('\\\\nRollback SQL:'));\n console.log(ROLLBACK_SQL);\n spinner.succeed('Rollback SQL generated');\n\n console.log(chalk.yellow('\\\\n⚠️ Execute the above SQL manually or use a database client.'));\n } catch (error) {\n spinner.fail('Rollback failed');\n console.error(chalk.red(error));\n }\n\n return;\n }\n\n if (options.dryRun) {\n console.log(chalk.gray('SQL to execute:'));\n console.log(MIGRATION_SQL);\n return;\n }\n\n const spinner = ora('Running migrations...').start();\n\n try {\n // In a real implementation, we'd connect to the database and execute\n // For now, output the SQL\n console.log(chalk.gray('\\\\nMigration SQL:'));\n console.log(MIGRATION_SQL);\n spinner.succeed('Migration SQL generated');\n\n console.log(chalk.green('\\\\n✅ Migration complete!'));\n console.log(chalk.gray('\\\\nExecute the above SQL against your database,'));\n console.log(chalk.gray('or integrate with Drizzle Kit for automated migrations:'));\n console.log(chalk.gray(' npx drizzle-kit push:pg'));\n } catch (error) {\n spinner.fail('Migration failed');\n console.error(chalk.red(error));\n }\n}\n"],"mappings":";;;AAAA,OAAO,WAAW;AAClB,OAAO,SAAS;AAOhB,IAAM,gBAAgB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA8KtB,IAAM,eAAe;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAkBrB,eAAsB,cAAc,SAAwC;AAC1E,UAAQ,IAAI,MAAM,KAAK,4CAAqC,CAAC;AAE7D,QAAM,cAAc,QAAQ,IAAI;AAEhC,MAAI,CAAC,eAAe,CAAC,QAAQ,QAAQ;AACnC,YAAQ,MAAM,MAAM,IAAI,sDAAsD,CAAC;AAC/E,YAAQ,IAAI,MAAM,KAAK,kEAAkE,CAAC;AAC1F,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,MAAI,QAAQ,UAAU;AACpB,YAAQ,IAAI,MAAM,OAAO,6CAAmC,CAAC;AAE7D,QAAI,QAAQ,QAAQ;AAClB,cAAQ,IAAI,MAAM,KAAK,iBAAiB,CAAC;AACzC,cAAQ,IAAI,YAAY;AACxB;AAAA,IACF;AAEA,UAAMA,WAAU,IAAI,iBAAiB,EAAE,MAAM;AAE7C,QAAI;AAGF,cAAQ,IAAI,MAAM,KAAK,kBAAkB,CAAC;AAC1C,cAAQ,IAAI,YAAY;AACxB,MAAAA,SAAQ,QAAQ,wBAAwB;AAExC,cAAQ,IAAI,MAAM,OAAO,2EAAiE,CAAC;AAAA,IAC7F,SAAS,OAAO;AACd,MAAAA,SAAQ,KAAK,iBAAiB;AAC9B,cAAQ,MAAM,MAAM,IAAI,KAAK,CAAC;AAAA,IAChC;AAEA;AAAA,EACF;AAEA,MAAI,QAAQ,QAAQ;AAClB,YAAQ,IAAI,MAAM,KAAK,iBAAiB,CAAC;AACzC,YAAQ,IAAI,aAAa;AACzB;AAAA,EACF;AAEA,QAAM,UAAU,IAAI,uBAAuB,EAAE,MAAM;AAEnD,MAAI;AAGF,YAAQ,IAAI,MAAM,KAAK,mBAAmB,CAAC;AAC3C,YAAQ,IAAI,aAAa;AACzB,YAAQ,QAAQ,yBAAyB;AAEzC,YAAQ,IAAI,MAAM,MAAM,+BAA0B,CAAC;AACnD,YAAQ,IAAI,MAAM,KAAK,iDAAiD,CAAC;AACzE,YAAQ,IAAI,MAAM,KAAK,yDAAyD,CAAC;AACjF,YAAQ,IAAI,MAAM,KAAK,2BAA2B,CAAC;AAAA,EACrD,SAAS,OAAO;AACd,YAAQ,KAAK,kBAAkB;AAC/B,YAAQ,MAAM,MAAM,IAAI,KAAK,CAAC;AAAA,EAChC;AACF;","names":["spinner"]}
|
|
1
|
+
{"version":3,"sources":["../src/commands/migrate.ts"],"sourcesContent":["import chalk from 'chalk';\nimport ora from 'ora';\n\ninterface MigrateOptions {\n dryRun?: boolean;\n rollback?: boolean;\n}\n\nconst MIGRATION_SQL = `\n-- OpenMDM Database Schema\n-- PostgreSQL Migration\n\n-- Device Status Enum\nDO $$ BEGIN\n CREATE TYPE mdm_device_status AS ENUM ('pending', 'enrolled', 'unenrolled', 'blocked');\nEXCEPTION\n WHEN duplicate_object THEN null;\nEND $$;\n\n-- Command Status Enum\nDO $$ BEGIN\n CREATE TYPE mdm_command_status AS ENUM ('pending', 'sent', 'acknowledged', 'completed', 'failed', 'cancelled');\nEXCEPTION\n WHEN duplicate_object THEN null;\nEND $$;\n\n-- Push Provider Enum\nDO $$ BEGIN\n CREATE TYPE mdm_push_provider AS ENUM ('fcm', 'mqtt', 'websocket');\nEXCEPTION\n WHEN duplicate_object THEN null;\nEND $$;\n\n-- Devices Table\nCREATE TABLE IF NOT EXISTS mdm_devices (\n id VARCHAR(255) PRIMARY KEY,\n external_id VARCHAR(255),\n enrollment_id VARCHAR(255) NOT NULL UNIQUE,\n status mdm_device_status NOT NULL DEFAULT 'pending',\n\n model VARCHAR(255),\n manufacturer VARCHAR(255),\n os_version VARCHAR(50),\n serial_number VARCHAR(255),\n imei VARCHAR(50),\n mac_address VARCHAR(50),\n android_id VARCHAR(255),\n\n policy_id VARCHAR(255),\n last_heartbeat TIMESTAMP,\n last_sync TIMESTAMP,\n\n battery_level INTEGER,\n storage_used BIGINT,\n storage_total BIGINT,\n location JSONB,\n installed_apps JSONB,\n\n tags JSONB,\n metadata JSONB,\n\n created_at TIMESTAMP NOT NULL DEFAULT NOW(),\n updated_at TIMESTAMP NOT NULL DEFAULT NOW()\n);\n\n-- Policies Table\nCREATE TABLE IF NOT EXISTS mdm_policies (\n id VARCHAR(255) PRIMARY KEY,\n name VARCHAR(255) NOT NULL,\n description TEXT,\n is_default BOOLEAN NOT NULL DEFAULT false,\n settings JSONB NOT NULL DEFAULT '{}',\n created_at TIMESTAMP NOT NULL DEFAULT NOW(),\n updated_at TIMESTAMP NOT NULL DEFAULT NOW()\n);\n\n-- Applications Table\nCREATE TABLE IF NOT EXISTS mdm_applications (\n id VARCHAR(255) PRIMARY KEY,\n name VARCHAR(255) NOT NULL,\n package_name VARCHAR(255) NOT NULL,\n version VARCHAR(50) NOT NULL,\n version_code INTEGER NOT NULL,\n url TEXT NOT NULL,\n hash VARCHAR(255),\n size BIGINT,\n min_sdk_version INTEGER,\n\n show_icon BOOLEAN NOT NULL DEFAULT true,\n run_after_install BOOLEAN NOT NULL DEFAULT false,\n run_at_boot BOOLEAN NOT NULL DEFAULT false,\n is_system BOOLEAN NOT NULL DEFAULT false,\n is_active BOOLEAN NOT NULL DEFAULT true,\n\n metadata JSONB,\n created_at TIMESTAMP NOT NULL DEFAULT NOW(),\n updated_at TIMESTAMP NOT NULL DEFAULT NOW()\n);\n\n-- Commands Table\nCREATE TABLE IF NOT EXISTS mdm_commands (\n id VARCHAR(255) PRIMARY KEY,\n device_id VARCHAR(255) NOT NULL REFERENCES mdm_devices(id) ON DELETE CASCADE,\n type VARCHAR(50) NOT NULL,\n payload JSONB,\n status mdm_command_status NOT NULL DEFAULT 'pending',\n result JSONB,\n error TEXT,\n\n created_at TIMESTAMP NOT NULL DEFAULT NOW(),\n sent_at TIMESTAMP,\n acknowledged_at TIMESTAMP,\n completed_at TIMESTAMP\n);\n\n-- Events Table\nCREATE TABLE IF NOT EXISTS mdm_events (\n id VARCHAR(255) PRIMARY KEY,\n device_id VARCHAR(255) NOT NULL REFERENCES mdm_devices(id) ON DELETE CASCADE,\n type VARCHAR(100) NOT NULL,\n payload JSONB NOT NULL DEFAULT '{}',\n created_at TIMESTAMP NOT NULL DEFAULT NOW()\n);\n\n-- Groups Table\nCREATE TABLE IF NOT EXISTS mdm_groups (\n id VARCHAR(255) PRIMARY KEY,\n name VARCHAR(255) NOT NULL,\n description TEXT,\n policy_id VARCHAR(255) REFERENCES mdm_policies(id) ON DELETE SET NULL,\n parent_id VARCHAR(255) REFERENCES mdm_groups(id) ON DELETE SET NULL,\n metadata JSONB,\n created_at TIMESTAMP NOT NULL DEFAULT NOW(),\n updated_at TIMESTAMP NOT NULL DEFAULT NOW()\n);\n\n-- Device Groups Junction Table\nCREATE TABLE IF NOT EXISTS mdm_device_groups (\n device_id VARCHAR(255) NOT NULL REFERENCES mdm_devices(id) ON DELETE CASCADE,\n group_id VARCHAR(255) NOT NULL REFERENCES mdm_groups(id) ON DELETE CASCADE,\n PRIMARY KEY (device_id, group_id)\n);\n\n-- Push Tokens Table\nCREATE TABLE IF NOT EXISTS mdm_push_tokens (\n id VARCHAR(255) PRIMARY KEY,\n device_id VARCHAR(255) NOT NULL REFERENCES mdm_devices(id) ON DELETE CASCADE,\n provider mdm_push_provider NOT NULL,\n token TEXT NOT NULL,\n is_active BOOLEAN NOT NULL DEFAULT true,\n created_at TIMESTAMP NOT NULL DEFAULT NOW(),\n updated_at TIMESTAMP NOT NULL DEFAULT NOW(),\n UNIQUE(device_id, provider)\n);\n\n-- App Deployments Table\nCREATE TABLE IF NOT EXISTS mdm_app_deployments (\n id VARCHAR(255) PRIMARY KEY,\n application_id VARCHAR(255) NOT NULL REFERENCES mdm_applications(id) ON DELETE CASCADE,\n target_type VARCHAR(20) NOT NULL, -- 'device', 'policy', 'group'\n target_id VARCHAR(255) NOT NULL,\n created_at TIMESTAMP NOT NULL DEFAULT NOW()\n);\n\n-- Indexes\nCREATE INDEX IF NOT EXISTS idx_devices_status ON mdm_devices(status);\nCREATE INDEX IF NOT EXISTS idx_devices_policy ON mdm_devices(policy_id);\nCREATE INDEX IF NOT EXISTS idx_devices_enrollment ON mdm_devices(enrollment_id);\nCREATE INDEX IF NOT EXISTS idx_devices_last_heartbeat ON mdm_devices(last_heartbeat);\n\nCREATE INDEX IF NOT EXISTS idx_commands_device ON mdm_commands(device_id);\nCREATE INDEX IF NOT EXISTS idx_commands_status ON mdm_commands(status);\nCREATE INDEX IF NOT EXISTS idx_commands_device_status ON mdm_commands(device_id, status);\n\nCREATE INDEX IF NOT EXISTS idx_events_device ON mdm_events(device_id);\nCREATE INDEX IF NOT EXISTS idx_events_type ON mdm_events(type);\nCREATE INDEX IF NOT EXISTS idx_events_created ON mdm_events(created_at);\n\nCREATE INDEX IF NOT EXISTS idx_applications_package ON mdm_applications(package_name);\nCREATE INDEX IF NOT EXISTS idx_push_tokens_device ON mdm_push_tokens(device_id);\n`;\n\nconst ROLLBACK_SQL = `\n-- OpenMDM Database Rollback\n\nDROP TABLE IF EXISTS mdm_app_deployments;\nDROP TABLE IF EXISTS mdm_push_tokens;\nDROP TABLE IF EXISTS mdm_device_groups;\nDROP TABLE IF EXISTS mdm_events;\nDROP TABLE IF EXISTS mdm_commands;\nDROP TABLE IF EXISTS mdm_applications;\nDROP TABLE IF EXISTS mdm_groups;\nDROP TABLE IF EXISTS mdm_devices;\nDROP TABLE IF EXISTS mdm_policies;\n\nDROP TYPE IF EXISTS mdm_push_provider;\nDROP TYPE IF EXISTS mdm_command_status;\nDROP TYPE IF EXISTS mdm_device_status;\n`;\n\nexport async function runMigrations(options: MigrateOptions): Promise<void> {\n console.log(chalk.blue('\\\\n📦 OpenMDM Database Migration\\\\n'));\n\n console.log(chalk.yellow('⚠️ Note: This command is deprecated.'));\n console.log(chalk.yellow(' Use the new generate command instead:'));\n console.log(chalk.cyan(' npx openmdm generate --adapter drizzle --provider pg'));\n console.log(chalk.cyan(' npx drizzle-kit generate'));\n console.log(chalk.cyan(' npx drizzle-kit migrate\\\\n'));\n\n const databaseUrl = process.env.DATABASE_URL;\n\n if (!databaseUrl && !options.dryRun) {\n console.error(chalk.red('Error: DATABASE_URL environment variable is required'));\n console.log(chalk.gray('Set it in your .env file or export it before running migrations.'));\n process.exit(1);\n }\n\n if (options.rollback) {\n console.log(chalk.yellow('⚠️ Rolling back migrations...\\\\n'));\n\n if (options.dryRun) {\n console.log(chalk.gray('SQL to execute:'));\n console.log(ROLLBACK_SQL);\n return;\n }\n\n const spinner = ora('Rolling back...').start();\n\n try {\n // In a real implementation, we'd use a database client here\n // For now, just output the SQL\n console.log(chalk.gray('\\\\nRollback SQL:'));\n console.log(ROLLBACK_SQL);\n spinner.succeed('Rollback SQL generated');\n\n console.log(chalk.yellow('\\\\n⚠️ Execute the above SQL manually or use a database client.'));\n } catch (error) {\n spinner.fail('Rollback failed');\n console.error(chalk.red(error));\n }\n\n return;\n }\n\n if (options.dryRun) {\n console.log(chalk.gray('SQL to execute:'));\n console.log(MIGRATION_SQL);\n return;\n }\n\n const spinner = ora('Running migrations...').start();\n\n try {\n // In a real implementation, we'd connect to the database and execute\n // For now, output the SQL\n console.log(chalk.gray('\\\\nMigration SQL:'));\n console.log(MIGRATION_SQL);\n spinner.succeed('Migration SQL generated');\n\n console.log(chalk.green('\\\\n✅ Migration complete!'));\n console.log(chalk.gray('\\\\nExecute the above SQL against your database,'));\n console.log(chalk.gray('or integrate with Drizzle Kit for automated migrations:'));\n console.log(chalk.gray(' npx drizzle-kit push:pg'));\n } catch (error) {\n spinner.fail('Migration failed');\n console.error(chalk.red(error));\n }\n}\n"],"mappings":"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","names":["spinner"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openmdm/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "CLI tools for OpenMDM - database migrations, admin commands, and device management",
|
|
5
5
|
"author": "OpenMDM Contributors",
|
|
6
6
|
"type": "module",
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
"ora": "^8.0.0",
|
|
20
20
|
"dotenv": "^16.0.0",
|
|
21
21
|
"qrcode": "^1.5.3",
|
|
22
|
-
"@openmdm/core": "0.
|
|
22
|
+
"@openmdm/core": "0.3.0"
|
|
23
23
|
},
|
|
24
24
|
"devDependencies": {
|
|
25
25
|
"@types/inquirer": "^9.0.0",
|