@3lineas/d1-orm 1.0.10 → 1.0.12
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/README.md +24 -4
- package/dist/cli/index.cjs +619 -97
- package/dist/cli/index.js +619 -97
- package/dist/index.cjs +171 -1
- package/dist/index.d.cts +75 -2
- package/dist/index.d.ts +75 -2
- package/dist/index.js +170 -1
- package/package.json +1 -1
package/dist/cli/index.cjs
CHANGED
|
@@ -154,7 +154,7 @@ var p3 = __toESM(require("@clack/prompts"), 1);
|
|
|
154
154
|
var fs2 = __toESM(require("fs"), 1);
|
|
155
155
|
var path2 = __toESM(require("path"), 1);
|
|
156
156
|
var p2 = __toESM(require("@clack/prompts"), 1);
|
|
157
|
-
async function makeMigration(name) {
|
|
157
|
+
async function makeMigration(name, fields) {
|
|
158
158
|
const isStandalone = !name;
|
|
159
159
|
if (isStandalone) {
|
|
160
160
|
p2.intro("Creating a new migration...");
|
|
@@ -179,17 +179,38 @@ async function makeMigration(name) {
|
|
|
179
179
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[-:]/g, "").split(".")[0].replace("T", "_");
|
|
180
180
|
const filename = `${timestamp}_${migrationName}.mts`;
|
|
181
181
|
const targetPath = path2.join(process.cwd(), migrationsDir, filename);
|
|
182
|
+
const tableName = migrationName.replace("create_", "").replace("_table", "").replace("add_", "").replace("_to_", "").replace("_table", "");
|
|
183
|
+
const isAlter = migrationName.startsWith("add_");
|
|
184
|
+
let fieldsSql = "";
|
|
185
|
+
if (fields && fields.length > 0) {
|
|
186
|
+
fieldsSql = fields.map((f) => {
|
|
187
|
+
let line = "";
|
|
188
|
+
if (f.type === "enum") {
|
|
189
|
+
line = ` table.enum('${f.name}', ${JSON.stringify(f.values)})`;
|
|
190
|
+
} else {
|
|
191
|
+
line = ` table.${f.type}('${f.name}')`;
|
|
192
|
+
}
|
|
193
|
+
if (f.unique) line += ".unique()";
|
|
194
|
+
if (f.nullable) line += ".nullable()";
|
|
195
|
+
if (f.default !== void 0 && f.default !== "") {
|
|
196
|
+
const defVal = typeof f.default === "string" ? `'${f.default}'` : f.default;
|
|
197
|
+
line += `.default(${defVal})`;
|
|
198
|
+
}
|
|
199
|
+
return line + ";";
|
|
200
|
+
}).join("\n");
|
|
201
|
+
}
|
|
202
|
+
const method = isAlter ? "table" : "create";
|
|
203
|
+
const downSql = isAlter ? `// return Schema.table('${tableName}', (table: Blueprint) => { /* drop columns not supported in simple SQLite ALTER */ });` : `return Schema.dropIfExists('${tableName}');`;
|
|
182
204
|
const template = `import { Blueprint, Schema } from '@3lineas/d1-orm';
|
|
183
205
|
|
|
184
206
|
export const up = async () => {
|
|
185
|
-
return Schema
|
|
186
|
-
table.id()
|
|
187
|
-
table.timestamps();
|
|
188
|
-
});
|
|
207
|
+
return Schema.${method}('${tableName}', (table: Blueprint) => {
|
|
208
|
+
${!isAlter ? " table.id();\n" : ""}${fieldsSql}
|
|
209
|
+
${!isAlter ? " table.timestamps();\n" : ""} });
|
|
189
210
|
};
|
|
190
211
|
|
|
191
212
|
export const down = async () => {
|
|
192
|
-
|
|
213
|
+
${downSql}
|
|
193
214
|
};
|
|
194
215
|
`;
|
|
195
216
|
if (!fs2.existsSync(path2.join(process.cwd(), migrationsDir))) {
|
|
@@ -229,46 +250,245 @@ async function makeModel(name) {
|
|
|
229
250
|
const modelPath = await findModelsPath() || "src/database/models";
|
|
230
251
|
const filename = `${modelName}.mts`;
|
|
231
252
|
const targetPath = path3.join(process.cwd(), modelPath, filename);
|
|
253
|
+
if (fs3.existsSync(targetPath)) {
|
|
254
|
+
p3.log.error(`Model ${filename} already exists at ${modelPath}.`);
|
|
255
|
+
p3.outro("Process aborted.");
|
|
256
|
+
return;
|
|
257
|
+
}
|
|
258
|
+
const fields = [];
|
|
259
|
+
const relations = [];
|
|
260
|
+
let addMore = true;
|
|
261
|
+
p3.log.step("Let's add some attributes to your model!");
|
|
262
|
+
while (addMore) {
|
|
263
|
+
const fieldName = await p3.text({
|
|
264
|
+
message: "New attribute or relation name (leave empty to stop):",
|
|
265
|
+
placeholder: "e.g. title or posts"
|
|
266
|
+
});
|
|
267
|
+
if (p3.isCancel(fieldName)) break;
|
|
268
|
+
if (!fieldName) break;
|
|
269
|
+
const fieldType = await p3.select({
|
|
270
|
+
message: `Type for ${fieldName}:`,
|
|
271
|
+
options: [
|
|
272
|
+
{ value: "string", label: "String" },
|
|
273
|
+
{ value: "integer", label: "Integer" },
|
|
274
|
+
{ value: "boolean", label: "Boolean" },
|
|
275
|
+
{ value: "text", label: "Text" },
|
|
276
|
+
{ value: "float", label: "Float" },
|
|
277
|
+
{ value: "json", label: "JSON" },
|
|
278
|
+
{ value: "enum", label: "Enum" },
|
|
279
|
+
{ value: "date", label: "Date" },
|
|
280
|
+
{ value: "blob", label: "Blob" },
|
|
281
|
+
{ value: "belongsToMany", label: "ManyToMany (Pivot Table)" },
|
|
282
|
+
{ value: "relation", label: "Relation (1:1, 1:N)" }
|
|
283
|
+
]
|
|
284
|
+
});
|
|
285
|
+
if (p3.isCancel(fieldType)) break;
|
|
286
|
+
if (fieldType === "relation" || fieldType === "belongsToMany") {
|
|
287
|
+
let relType = fieldType;
|
|
288
|
+
if (fieldType === "relation") {
|
|
289
|
+
relType = await p3.select({
|
|
290
|
+
message: `Relation type for ${fieldName}:`,
|
|
291
|
+
options: [
|
|
292
|
+
{ value: "hasOne", label: "HasOne" },
|
|
293
|
+
{ value: "hasMany", label: "HasMany" },
|
|
294
|
+
{ value: "belongsTo", label: "BelongsTo" }
|
|
295
|
+
]
|
|
296
|
+
});
|
|
297
|
+
}
|
|
298
|
+
if (p3.isCancel(relType)) break;
|
|
299
|
+
const modelsPath = await findModelsPath() || "src/database/models";
|
|
300
|
+
const availableModels = fs3.existsSync(
|
|
301
|
+
path3.join(process.cwd(), modelsPath)
|
|
302
|
+
) ? fs3.readdirSync(path3.join(process.cwd(), modelPath)).map((f) => f.replace(".mts", "").replace(".ts", "")) : [];
|
|
303
|
+
const targetModel = await p3.text({
|
|
304
|
+
message: "Target model for this relation:",
|
|
305
|
+
placeholder: "e.g. Post",
|
|
306
|
+
validate: (v) => !v ? "Target model is required" : void 0
|
|
307
|
+
});
|
|
308
|
+
if (p3.isCancel(targetModel)) break;
|
|
309
|
+
let foreignKey = "";
|
|
310
|
+
let pivotTable = "";
|
|
311
|
+
if (relType === "belongsToMany") {
|
|
312
|
+
p3.log.info("Automatically creating a pivot table migration.");
|
|
313
|
+
pivotTable = await p3.text({
|
|
314
|
+
message: "Pivot table name:",
|
|
315
|
+
initialValue: [modelName.toLowerCase(), targetModel.toLowerCase()].sort().join("_")
|
|
316
|
+
});
|
|
317
|
+
if (p3.isCancel(pivotTable)) break;
|
|
318
|
+
} else if (relType === "belongsTo") {
|
|
319
|
+
foreignKey = await p3.text({
|
|
320
|
+
message: "Foreign key column name:",
|
|
321
|
+
placeholder: `e.g. ${targetModel.toLowerCase()}_id`,
|
|
322
|
+
initialValue: `${targetModel.toLowerCase()}_id`
|
|
323
|
+
});
|
|
324
|
+
if (p3.isCancel(foreignKey)) break;
|
|
325
|
+
fields.push({
|
|
326
|
+
name: foreignKey,
|
|
327
|
+
type: "foreign",
|
|
328
|
+
nullable: false,
|
|
329
|
+
unique: false
|
|
330
|
+
});
|
|
331
|
+
}
|
|
332
|
+
relations.push({
|
|
333
|
+
method: fieldName,
|
|
334
|
+
type: relType,
|
|
335
|
+
model: targetModel,
|
|
336
|
+
foreignKey,
|
|
337
|
+
pivotTable
|
|
338
|
+
});
|
|
339
|
+
const addInverse = await p3.confirm({
|
|
340
|
+
message: `Add the inverse relation in ${targetModel}.mts?`,
|
|
341
|
+
initialValue: true
|
|
342
|
+
});
|
|
343
|
+
if (addInverse && !p3.isCancel(addInverse)) {
|
|
344
|
+
let inverseType = "belongsTo";
|
|
345
|
+
if (relType === "belongsTo") inverseType = "hasMany";
|
|
346
|
+
if (relType === "hasOne") inverseType = "belongsTo";
|
|
347
|
+
if (relType === "belongsToMany") inverseType = "belongsToMany";
|
|
348
|
+
const inverseMethod = await p3.text({
|
|
349
|
+
message: "Inverse method name:",
|
|
350
|
+
initialValue: relType === "belongsTo" ? modelName.toLowerCase() + "s" : modelName.toLowerCase()
|
|
351
|
+
});
|
|
352
|
+
if (!p3.isCancel(inverseMethod)) {
|
|
353
|
+
await updateTargetModel(
|
|
354
|
+
targetModel,
|
|
355
|
+
inverseMethod,
|
|
356
|
+
inverseType,
|
|
357
|
+
modelName,
|
|
358
|
+
pivotTable
|
|
359
|
+
);
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
} else {
|
|
363
|
+
let enumValues = [];
|
|
364
|
+
if (fieldType === "enum") {
|
|
365
|
+
const valuesStr = await p3.text({
|
|
366
|
+
message: "Allowed values (comma separated):",
|
|
367
|
+
placeholder: "active, inactive, pending"
|
|
368
|
+
});
|
|
369
|
+
if (p3.isCancel(valuesStr)) break;
|
|
370
|
+
enumValues = valuesStr.split(",").map((v) => v.trim());
|
|
371
|
+
}
|
|
372
|
+
const isNullable = await p3.confirm({
|
|
373
|
+
message: "Is it nullable?",
|
|
374
|
+
initialValue: false
|
|
375
|
+
});
|
|
376
|
+
if (p3.isCancel(isNullable)) break;
|
|
377
|
+
const isUnique = await p3.confirm({
|
|
378
|
+
message: "Is it unique?",
|
|
379
|
+
initialValue: false
|
|
380
|
+
});
|
|
381
|
+
if (p3.isCancel(isUnique)) break;
|
|
382
|
+
fields.push({
|
|
383
|
+
name: fieldName,
|
|
384
|
+
type: fieldType,
|
|
385
|
+
nullable: isNullable,
|
|
386
|
+
unique: isUnique,
|
|
387
|
+
values: enumValues
|
|
388
|
+
});
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
const fieldProperties = fields.filter(
|
|
392
|
+
(f) => f.type !== "foreign" || !relations.some((r) => r.foreignKey === f.name)
|
|
393
|
+
).map((f) => {
|
|
394
|
+
let tsType = "string";
|
|
395
|
+
if (f.type === "integer" || f.type === "float") tsType = "number";
|
|
396
|
+
else if (f.type === "boolean") tsType = "boolean";
|
|
397
|
+
else if (f.type === "json") tsType = "any";
|
|
398
|
+
else if (f.type === "blob") tsType = "Uint8Array";
|
|
399
|
+
return ` declare ${f.name}${f.nullable ? "?" : ""}: ${tsType};`;
|
|
400
|
+
}).join("\n");
|
|
401
|
+
const relationMethods = relations.map((r) => {
|
|
402
|
+
if (r.type === "belongsToMany") {
|
|
403
|
+
return ` ${r.method}() {
|
|
404
|
+
return this.belongsToMany(${r.model}, '${r.pivotTable}');
|
|
405
|
+
}`;
|
|
406
|
+
}
|
|
407
|
+
return ` ${r.method}() {
|
|
408
|
+
return this.${r.type}(${r.model});
|
|
409
|
+
}`;
|
|
410
|
+
}).join("\n\n");
|
|
232
411
|
const template = `import { Model } from '@3lineas/d1-orm';
|
|
412
|
+
${relations.map((r) => `import { ${r.model} } from './${r.model}.mts';`).join("\n")}
|
|
233
413
|
|
|
234
414
|
export class ${modelName} extends Model {
|
|
235
415
|
// protected static table = '${modelName.toLowerCase()}s';
|
|
236
416
|
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
417
|
+
declare id: number;
|
|
418
|
+
${fieldProperties}
|
|
419
|
+
declare created_at: string;
|
|
420
|
+
declare updated_at: string;
|
|
421
|
+
|
|
422
|
+
${relationMethods}
|
|
240
423
|
}
|
|
241
424
|
`;
|
|
242
|
-
if (fs3.existsSync(targetPath)) {
|
|
243
|
-
p3.log.error(`Model ${filename} already exists at ${modelPath}.`);
|
|
244
|
-
p3.outro("Process aborted.");
|
|
245
|
-
return;
|
|
246
|
-
}
|
|
247
425
|
if (!fs3.existsSync(path3.join(process.cwd(), modelPath))) {
|
|
248
426
|
fs3.mkdirSync(path3.join(process.cwd(), modelPath), { recursive: true });
|
|
249
427
|
}
|
|
250
428
|
fs3.writeFileSync(targetPath, template);
|
|
251
429
|
p3.log.success(`Created model: ${modelPath}/${filename}`);
|
|
252
|
-
const
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
430
|
+
const migrationName = `create_${modelName.toLowerCase()}s_table`;
|
|
431
|
+
await makeMigration(migrationName, fields);
|
|
432
|
+
for (const rel of relations) {
|
|
433
|
+
if (rel.type === "belongsToMany") {
|
|
434
|
+
const pivotMigrationName = `create_${rel.pivotTable}_table`;
|
|
435
|
+
const pivotFields = [
|
|
436
|
+
{
|
|
437
|
+
name: `${modelName.toLowerCase()}_id`,
|
|
438
|
+
type: "integer",
|
|
439
|
+
nullable: false
|
|
440
|
+
},
|
|
441
|
+
{
|
|
442
|
+
name: `${rel.model.toLowerCase()}_id`,
|
|
443
|
+
type: "integer",
|
|
444
|
+
nullable: false
|
|
445
|
+
}
|
|
446
|
+
];
|
|
447
|
+
await makeMigration(pivotMigrationName, pivotFields);
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
const wantSeeder = await p3.confirm({
|
|
451
|
+
message: "Would you like to create a seeder for this model?",
|
|
452
|
+
initialValue: true
|
|
259
453
|
});
|
|
260
|
-
if (
|
|
261
|
-
|
|
454
|
+
if (wantSeeder) {
|
|
455
|
+
await makeSeeder(modelName, modelPath, fields);
|
|
456
|
+
}
|
|
457
|
+
p3.outro("Model generation complete!");
|
|
458
|
+
}
|
|
459
|
+
async function updateTargetModel(targetModel, methodName, relType, sourceModel, pivotTable = "") {
|
|
460
|
+
const modelPath = await findModelsPath() || "src/database/models";
|
|
461
|
+
const filename = `${targetModel}.mts`;
|
|
462
|
+
const targetPath = path3.join(process.cwd(), modelPath, filename);
|
|
463
|
+
if (!fs3.existsSync(targetPath)) {
|
|
464
|
+
p3.log.warn(
|
|
465
|
+
`Model ${targetModel} not found at ${modelPath}. Skipping inverse relation.`
|
|
466
|
+
);
|
|
262
467
|
return;
|
|
263
468
|
}
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
469
|
+
let content = fs3.readFileSync(targetPath, "utf-8");
|
|
470
|
+
if (!content.includes(`import { ${sourceModel} }`)) {
|
|
471
|
+
content = `import { ${sourceModel} } from './${sourceModel}.mts';
|
|
472
|
+
` + content;
|
|
267
473
|
}
|
|
268
|
-
|
|
269
|
-
|
|
474
|
+
let methodString = "";
|
|
475
|
+
if (relType === "belongsToMany") {
|
|
476
|
+
methodString = ` ${methodName}() {
|
|
477
|
+
return this.belongsToMany(${sourceModel}, '${pivotTable}');
|
|
478
|
+
}`;
|
|
479
|
+
} else {
|
|
480
|
+
methodString = ` ${methodName}() {
|
|
481
|
+
return this.${relType}(${sourceModel});
|
|
482
|
+
}`;
|
|
483
|
+
}
|
|
484
|
+
const lastBraceIndex = content.lastIndexOf("}");
|
|
485
|
+
if (lastBraceIndex !== -1) {
|
|
486
|
+
content = content.substring(0, lastBraceIndex) + "\n" + methodString + "\n" + content.substring(lastBraceIndex);
|
|
487
|
+
fs3.writeFileSync(targetPath, content);
|
|
488
|
+
p3.log.success(
|
|
489
|
+
`Updated ${targetModel}.mts with inverse relation: ${methodName}()`
|
|
490
|
+
);
|
|
270
491
|
}
|
|
271
|
-
p3.outro("Model generation complete!");
|
|
272
492
|
}
|
|
273
493
|
async function findModelsPath() {
|
|
274
494
|
const commonPaths = [
|
|
@@ -278,12 +498,12 @@ async function findModelsPath() {
|
|
|
278
498
|
"models",
|
|
279
499
|
"app/Models"
|
|
280
500
|
];
|
|
281
|
-
for (const
|
|
282
|
-
if (fs3.existsSync(path3.join(process.cwd(),
|
|
501
|
+
for (const p8 of commonPaths) {
|
|
502
|
+
if (fs3.existsSync(path3.join(process.cwd(), p8))) return p8;
|
|
283
503
|
}
|
|
284
504
|
return null;
|
|
285
505
|
}
|
|
286
|
-
async function makeSeeder(modelName, modelPath) {
|
|
506
|
+
async function makeSeeder(modelName, modelPath, fields = []) {
|
|
287
507
|
const srcPath = path3.join(process.cwd(), "src");
|
|
288
508
|
const useSrc = fs3.existsSync(srcPath) && fs3.lstatSync(srcPath).isDirectory();
|
|
289
509
|
const seederDir = useSrc ? path3.join(process.cwd(), "src/database/seeders") : path3.join(process.cwd(), "database/seeders");
|
|
@@ -293,10 +513,25 @@ async function makeSeeder(modelName, modelPath) {
|
|
|
293
513
|
fs3.mkdirSync(seederDir, { recursive: true });
|
|
294
514
|
}
|
|
295
515
|
const relativeModelPath = path3.relative(seederDir, path3.join(process.cwd(), modelPath, modelName)).replace(/\\/g, "/");
|
|
516
|
+
const dummyData = fields.map((f) => {
|
|
517
|
+
let val = "''";
|
|
518
|
+
if (f.type === "integer" || f.type === "foreign") val = "1";
|
|
519
|
+
if (f.type === "float") val = "1.5";
|
|
520
|
+
if (f.type === "boolean") val = "true";
|
|
521
|
+
if (f.type === "string" || f.type === "text") val = `'Sample ${f.name}'`;
|
|
522
|
+
if (f.type === "json") val = "'{}'";
|
|
523
|
+
if (f.type === "enum")
|
|
524
|
+
val = f.values && f.values.length > 0 ? `'${f.values[0]}'` : "''";
|
|
525
|
+
if (f.type === "date" || f.type === "datetime")
|
|
526
|
+
val = `'${(/* @__PURE__ */ new Date()).toISOString()}'`;
|
|
527
|
+
return ` ${f.name}: ${val},`;
|
|
528
|
+
}).join("\n");
|
|
296
529
|
const template = `import { ${modelName} } from '${relativeModelPath}.mts';
|
|
297
530
|
|
|
298
531
|
export const seed = async () => {
|
|
299
|
-
|
|
532
|
+
await ${modelName}.create({
|
|
533
|
+
${dummyData}
|
|
534
|
+
});
|
|
300
535
|
};
|
|
301
536
|
`;
|
|
302
537
|
if (fs3.existsSync(targetPath)) {
|
|
@@ -304,18 +539,281 @@ export const seed = async () => {
|
|
|
304
539
|
return;
|
|
305
540
|
}
|
|
306
541
|
fs3.writeFileSync(targetPath, template);
|
|
307
|
-
p3.log.success(`Created seeder:
|
|
542
|
+
p3.log.success(`Created seeder: ${seederDir}/${seederName}`);
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
// src/cli/commands/model-add.ts
|
|
546
|
+
var fs4 = __toESM(require("fs"), 1);
|
|
547
|
+
var path4 = __toESM(require("path"), 1);
|
|
548
|
+
var p4 = __toESM(require("@clack/prompts"), 1);
|
|
549
|
+
async function modelAdd() {
|
|
550
|
+
p4.intro("Adding attributes to an existing model...");
|
|
551
|
+
const modelPath = await findModelsPath2() || "src/database/models";
|
|
552
|
+
const fullModelPath = path4.join(process.cwd(), modelPath);
|
|
553
|
+
if (!fs4.existsSync(fullModelPath)) {
|
|
554
|
+
p4.log.error(`Models directory not found at ${modelPath}.`);
|
|
555
|
+
return;
|
|
556
|
+
}
|
|
557
|
+
const modelFiles = fs4.readdirSync(fullModelPath).filter((f) => f.endsWith(".mts") || f.endsWith(".ts"));
|
|
558
|
+
const options = modelFiles.map((f) => ({
|
|
559
|
+
value: f,
|
|
560
|
+
label: f
|
|
561
|
+
}));
|
|
562
|
+
options.unshift({ value: "new", label: "[ Create new model ]" });
|
|
563
|
+
const selectedModel = await p4.select({
|
|
564
|
+
message: "Select a model to add attributes to:",
|
|
565
|
+
options
|
|
566
|
+
});
|
|
567
|
+
if (p4.isCancel(selectedModel)) {
|
|
568
|
+
p4.cancel("Operation cancelled.");
|
|
569
|
+
return;
|
|
570
|
+
}
|
|
571
|
+
if (selectedModel === "new") {
|
|
572
|
+
await makeModel();
|
|
573
|
+
return;
|
|
574
|
+
}
|
|
575
|
+
const modelName = selectedModel.replace(".mts", "").replace(".ts", "");
|
|
576
|
+
const targetPath = path4.join(fullModelPath, selectedModel);
|
|
577
|
+
const fields = [];
|
|
578
|
+
const relations = [];
|
|
579
|
+
let addMore = true;
|
|
580
|
+
p4.log.step(`Adding attributes to ${modelName}...`);
|
|
581
|
+
while (addMore) {
|
|
582
|
+
const fieldName = await p4.text({
|
|
583
|
+
message: "New attribute or relation name (leave empty to stop):",
|
|
584
|
+
placeholder: "e.g. description or comments"
|
|
585
|
+
});
|
|
586
|
+
if (p4.isCancel(fieldName)) break;
|
|
587
|
+
if (!fieldName) break;
|
|
588
|
+
const fieldType = await p4.select({
|
|
589
|
+
message: `Type for ${fieldName}:`,
|
|
590
|
+
options: [
|
|
591
|
+
{ value: "string", label: "String" },
|
|
592
|
+
{ value: "integer", label: "Integer" },
|
|
593
|
+
{ value: "boolean", label: "Boolean" },
|
|
594
|
+
{ value: "text", label: "Text" },
|
|
595
|
+
{ value: "float", label: "Float" },
|
|
596
|
+
{ value: "json", label: "JSON" },
|
|
597
|
+
{ value: "enum", label: "Enum" },
|
|
598
|
+
{ value: "date", label: "Date" },
|
|
599
|
+
{ value: "blob", label: "Blob" },
|
|
600
|
+
{ value: "belongsToMany", label: "ManyToMany (Pivot Table)" },
|
|
601
|
+
{ value: "relation", label: "Relation (1:1, 1:N)" }
|
|
602
|
+
]
|
|
603
|
+
});
|
|
604
|
+
if (p4.isCancel(fieldType)) break;
|
|
605
|
+
if (fieldType === "relation" || fieldType === "belongsToMany") {
|
|
606
|
+
let relType = fieldType;
|
|
607
|
+
if (fieldType === "relation") {
|
|
608
|
+
relType = await p4.select({
|
|
609
|
+
message: `Relation type for ${fieldName}:`,
|
|
610
|
+
options: [
|
|
611
|
+
{ value: "hasOne", label: "HasOne" },
|
|
612
|
+
{ value: "hasMany", label: "HasMany" },
|
|
613
|
+
{ value: "belongsTo", label: "BelongsTo" }
|
|
614
|
+
]
|
|
615
|
+
});
|
|
616
|
+
}
|
|
617
|
+
if (p4.isCancel(relType)) break;
|
|
618
|
+
const targetModel = await p4.text({
|
|
619
|
+
message: "Target model for this relation:",
|
|
620
|
+
placeholder: "e.g. Comment",
|
|
621
|
+
validate: (v) => !v ? "Target model is required" : void 0
|
|
622
|
+
});
|
|
623
|
+
if (p4.isCancel(targetModel)) break;
|
|
624
|
+
let foreignKey = "";
|
|
625
|
+
let pivotTable = "";
|
|
626
|
+
if (relType === "belongsToMany") {
|
|
627
|
+
p4.log.info("Automatically creating a pivot table migration.");
|
|
628
|
+
pivotTable = await p4.text({
|
|
629
|
+
message: "Pivot table name:",
|
|
630
|
+
initialValue: [modelName.toLowerCase(), targetModel.toLowerCase()].sort().join("_")
|
|
631
|
+
});
|
|
632
|
+
if (p4.isCancel(pivotTable)) break;
|
|
633
|
+
} else if (relType === "belongsTo") {
|
|
634
|
+
foreignKey = await p4.text({
|
|
635
|
+
message: "Foreign key column name:",
|
|
636
|
+
placeholder: `e.g. ${targetModel.toLowerCase()}_id`,
|
|
637
|
+
initialValue: `${targetModel.toLowerCase()}_id`
|
|
638
|
+
});
|
|
639
|
+
if (p4.isCancel(foreignKey)) break;
|
|
640
|
+
fields.push({
|
|
641
|
+
name: foreignKey,
|
|
642
|
+
type: "foreign",
|
|
643
|
+
nullable: false,
|
|
644
|
+
unique: false
|
|
645
|
+
});
|
|
646
|
+
}
|
|
647
|
+
relations.push({
|
|
648
|
+
method: fieldName,
|
|
649
|
+
type: relType,
|
|
650
|
+
model: targetModel,
|
|
651
|
+
foreignKey,
|
|
652
|
+
pivotTable
|
|
653
|
+
});
|
|
654
|
+
const addInverse = await p4.confirm({
|
|
655
|
+
message: `Add the inverse relation in ${targetModel}.mts?`,
|
|
656
|
+
initialValue: true
|
|
657
|
+
});
|
|
658
|
+
if (addInverse && !p4.isCancel(addInverse)) {
|
|
659
|
+
let inverseType = "belongsTo";
|
|
660
|
+
if (relType === "belongsTo") inverseType = "hasMany";
|
|
661
|
+
if (relType === "hasOne") inverseType = "belongsTo";
|
|
662
|
+
if (relType === "belongsToMany") inverseType = "belongsToMany";
|
|
663
|
+
const inverseMethod = await p4.text({
|
|
664
|
+
message: "Inverse method name:",
|
|
665
|
+
initialValue: relType === "belongsTo" ? modelName.toLowerCase() + "s" : modelName.toLowerCase()
|
|
666
|
+
});
|
|
667
|
+
if (!p4.isCancel(inverseMethod)) {
|
|
668
|
+
await updateTargetModel2(
|
|
669
|
+
targetModel,
|
|
670
|
+
inverseMethod,
|
|
671
|
+
inverseType,
|
|
672
|
+
modelName,
|
|
673
|
+
pivotTable
|
|
674
|
+
);
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
} else {
|
|
678
|
+
let enumValues = [];
|
|
679
|
+
if (fieldType === "enum") {
|
|
680
|
+
const valuesStr = await p4.text({
|
|
681
|
+
message: "Allowed values (comma separated):",
|
|
682
|
+
placeholder: "active, inactive, pending"
|
|
683
|
+
});
|
|
684
|
+
if (p4.isCancel(valuesStr)) break;
|
|
685
|
+
enumValues = valuesStr.split(",").map((v) => v.trim());
|
|
686
|
+
}
|
|
687
|
+
const isNullable = await p4.confirm({
|
|
688
|
+
message: "Is it nullable?",
|
|
689
|
+
initialValue: false
|
|
690
|
+
});
|
|
691
|
+
if (p4.isCancel(isNullable)) break;
|
|
692
|
+
const isUnique = await p4.confirm({
|
|
693
|
+
message: "Is it unique?",
|
|
694
|
+
initialValue: false
|
|
695
|
+
});
|
|
696
|
+
if (p4.isCancel(isUnique)) break;
|
|
697
|
+
fields.push({
|
|
698
|
+
name: fieldName,
|
|
699
|
+
type: fieldType,
|
|
700
|
+
nullable: isNullable,
|
|
701
|
+
unique: isUnique,
|
|
702
|
+
values: enumValues
|
|
703
|
+
});
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
if (fields.length === 0 && relations.length === 0) {
|
|
707
|
+
p4.outro("No changes made.");
|
|
708
|
+
return;
|
|
709
|
+
}
|
|
710
|
+
let modelContent = fs4.readFileSync(targetPath, "utf-8");
|
|
711
|
+
const newImports = relations.filter((r) => !modelContent.includes(`import { ${r.model} }`)).map((r) => `import { ${r.model} } from './${r.model}.mts';`).join("\n");
|
|
712
|
+
if (newImports) {
|
|
713
|
+
modelContent = newImports + "\n" + modelContent;
|
|
714
|
+
}
|
|
715
|
+
const fieldProperties = fields.filter(
|
|
716
|
+
(f) => f.type !== "foreign" || !relations.some((r) => r.foreignKey === f.name)
|
|
717
|
+
).map((f) => {
|
|
718
|
+
let tsType = "string";
|
|
719
|
+
if (f.type === "integer" || f.type === "float") tsType = "number";
|
|
720
|
+
else if (f.type === "boolean") tsType = "boolean";
|
|
721
|
+
else if (f.type === "json") tsType = "any";
|
|
722
|
+
else if (f.type === "blob") tsType = "Uint8Array";
|
|
723
|
+
return ` declare ${f.name}${f.nullable ? "?" : ""}: ${tsType};`;
|
|
724
|
+
}).join("\n");
|
|
725
|
+
const relationMethods = relations.map((r) => {
|
|
726
|
+
return ` ${r.method}() {
|
|
727
|
+
return this.${r.type}(${r.model});
|
|
728
|
+
}`;
|
|
729
|
+
}).join("\n\n");
|
|
730
|
+
const lastBraceIndex = modelContent.lastIndexOf("}");
|
|
731
|
+
if (lastBraceIndex !== -1) {
|
|
732
|
+
modelContent = modelContent.substring(0, lastBraceIndex) + fieldProperties + "\n" + relationMethods + "\n" + modelContent.substring(lastBraceIndex);
|
|
733
|
+
fs4.writeFileSync(targetPath, modelContent);
|
|
734
|
+
p4.log.success(`Updated model: ${modelPath}/${selectedModel}`);
|
|
735
|
+
}
|
|
736
|
+
if (fields.length > 0) {
|
|
737
|
+
const migrationName = `add_fields_to_${modelName.toLowerCase()}s_table`;
|
|
738
|
+
await makeMigration(migrationName, fields);
|
|
739
|
+
}
|
|
740
|
+
for (const rel of relations) {
|
|
741
|
+
if (rel.type === "belongsToMany") {
|
|
742
|
+
const pivotMigrationName = `create_${rel.pivotTable}_table`;
|
|
743
|
+
const pivotFields = [
|
|
744
|
+
{
|
|
745
|
+
name: `${modelName.toLowerCase()}_id`,
|
|
746
|
+
type: "integer",
|
|
747
|
+
nullable: false
|
|
748
|
+
},
|
|
749
|
+
{
|
|
750
|
+
name: `${rel.model.toLowerCase()}_id`,
|
|
751
|
+
type: "integer",
|
|
752
|
+
nullable: false
|
|
753
|
+
}
|
|
754
|
+
];
|
|
755
|
+
await makeMigration(pivotMigrationName, pivotFields);
|
|
756
|
+
}
|
|
757
|
+
}
|
|
758
|
+
p4.outro("Model update complete!");
|
|
759
|
+
}
|
|
760
|
+
async function updateTargetModel2(targetModel, methodName, relType, sourceModel, pivotTable = "") {
|
|
761
|
+
const modelPath = await findModelsPath2() || "src/database/models";
|
|
762
|
+
const filename = `${targetModel}.mts`;
|
|
763
|
+
const targetPath = path4.join(process.cwd(), modelPath, filename);
|
|
764
|
+
if (!fs4.existsSync(targetPath)) {
|
|
765
|
+
p4.log.warn(
|
|
766
|
+
`Model ${targetModel} not found at ${modelPath}. Skipping inverse relation.`
|
|
767
|
+
);
|
|
768
|
+
return;
|
|
769
|
+
}
|
|
770
|
+
let content = fs4.readFileSync(targetPath, "utf-8");
|
|
771
|
+
if (!content.includes(`import { ${sourceModel} }`)) {
|
|
772
|
+
content = `import { ${sourceModel} } from './${sourceModel}.mts';
|
|
773
|
+
` + content;
|
|
774
|
+
}
|
|
775
|
+
let methodString = "";
|
|
776
|
+
if (relType === "belongsToMany") {
|
|
777
|
+
methodString = ` ${methodName}() {
|
|
778
|
+
return this.belongsToMany(${sourceModel}, '${pivotTable}');
|
|
779
|
+
}`;
|
|
780
|
+
} else {
|
|
781
|
+
methodString = ` ${methodName}() {
|
|
782
|
+
return this.${relType}(${sourceModel});
|
|
783
|
+
}`;
|
|
784
|
+
}
|
|
785
|
+
const lastBraceIndex = content.lastIndexOf("}");
|
|
786
|
+
if (lastBraceIndex !== -1) {
|
|
787
|
+
content = content.substring(0, lastBraceIndex) + "\n" + methodString + "\n" + content.substring(lastBraceIndex);
|
|
788
|
+
fs4.writeFileSync(targetPath, content);
|
|
789
|
+
p4.log.success(
|
|
790
|
+
`Updated ${targetModel}.mts with inverse relation: ${methodName}()`
|
|
791
|
+
);
|
|
792
|
+
}
|
|
793
|
+
}
|
|
794
|
+
async function findModelsPath2() {
|
|
795
|
+
const commonPaths = [
|
|
796
|
+
"src/database/models",
|
|
797
|
+
"database/models",
|
|
798
|
+
"src/models",
|
|
799
|
+
"models",
|
|
800
|
+
"app/Models"
|
|
801
|
+
];
|
|
802
|
+
for (const p8 of commonPaths) {
|
|
803
|
+
if (fs4.existsSync(path4.join(process.cwd(), p8))) return p8;
|
|
804
|
+
}
|
|
805
|
+
return null;
|
|
308
806
|
}
|
|
309
807
|
|
|
310
808
|
// src/cli/commands/migrate.ts
|
|
311
|
-
var
|
|
312
|
-
var
|
|
809
|
+
var fs7 = __toESM(require("fs"), 1);
|
|
810
|
+
var path7 = __toESM(require("path"), 1);
|
|
313
811
|
var import_child_process2 = require("child_process");
|
|
314
|
-
var
|
|
812
|
+
var p6 = __toESM(require("@clack/prompts"), 1);
|
|
315
813
|
|
|
316
814
|
// src/cli/utils/config.ts
|
|
317
|
-
var
|
|
318
|
-
var
|
|
815
|
+
var fs5 = __toESM(require("fs"), 1);
|
|
816
|
+
var path5 = __toESM(require("path"), 1);
|
|
319
817
|
var Config = class {
|
|
320
818
|
/**
|
|
321
819
|
* Detect the D1 database binding from Wrangler configuration or d1-orm config.
|
|
@@ -323,24 +821,24 @@ var Config = class {
|
|
|
323
821
|
* @returns The binding name (e.g., "DB").
|
|
324
822
|
*/
|
|
325
823
|
static getD1Binding() {
|
|
326
|
-
const srcConfig =
|
|
327
|
-
const srcConfigTs =
|
|
328
|
-
const rootConfig =
|
|
329
|
-
const rootConfigTs =
|
|
330
|
-
const configPath =
|
|
824
|
+
const srcConfig = path5.join(process.cwd(), "src/database/config.mts");
|
|
825
|
+
const srcConfigTs = path5.join(process.cwd(), "src/database/config.ts");
|
|
826
|
+
const rootConfig = path5.join(process.cwd(), "database/config.mts");
|
|
827
|
+
const rootConfigTs = path5.join(process.cwd(), "database/config.ts");
|
|
828
|
+
const configPath = fs5.existsSync(srcConfig) ? srcConfig : fs5.existsSync(srcConfigTs) ? srcConfigTs : fs5.existsSync(rootConfig) ? rootConfig : fs5.existsSync(rootConfigTs) ? rootConfigTs : null;
|
|
331
829
|
if (configPath) {
|
|
332
|
-
const content =
|
|
830
|
+
const content = fs5.readFileSync(configPath, "utf-8");
|
|
333
831
|
const match = content.match(/binding\s*:\s*["'](.+?)["']/);
|
|
334
832
|
if (match) return match[1];
|
|
335
833
|
}
|
|
336
834
|
const wranglerPaths = ["wrangler.jsonc", "wrangler.json", "wrangler.toml"];
|
|
337
835
|
for (const configName of wranglerPaths) {
|
|
338
|
-
const fullPath =
|
|
339
|
-
if (
|
|
340
|
-
const content =
|
|
836
|
+
const fullPath = path5.join(process.cwd(), configName);
|
|
837
|
+
if (fs5.existsSync(fullPath)) {
|
|
838
|
+
const content = fs5.readFileSync(fullPath, "utf-8");
|
|
341
839
|
if (configName.endsWith(".json") || configName.endsWith(".jsonc")) {
|
|
342
840
|
try {
|
|
343
|
-
const jsonStr = content.replace(
|
|
841
|
+
const jsonStr = content.replace(/\/\/.*$/gm, "").replace(/\/\*[\s\S]*?\*\//g, "");
|
|
344
842
|
const config = JSON.parse(jsonStr);
|
|
345
843
|
const d1Databases = config.d1_databases;
|
|
346
844
|
if (Array.isArray(d1Databases) && d1Databases.length > 0) {
|
|
@@ -357,14 +855,28 @@ var Config = class {
|
|
|
357
855
|
}
|
|
358
856
|
return "DB";
|
|
359
857
|
}
|
|
858
|
+
/**
|
|
859
|
+
* Get a filtered environment object to avoid system warnings.
|
|
860
|
+
* Filters out npm_config_* variables and sets NODE_NO_WARNINGS.
|
|
861
|
+
*/
|
|
862
|
+
static getCleanEnv() {
|
|
863
|
+
const cleanEnv = { ...process.env };
|
|
864
|
+
Object.keys(cleanEnv).forEach((key) => {
|
|
865
|
+
if (key.startsWith("npm_config_")) {
|
|
866
|
+
delete cleanEnv[key];
|
|
867
|
+
}
|
|
868
|
+
});
|
|
869
|
+
cleanEnv.NODE_NO_WARNINGS = "1";
|
|
870
|
+
return cleanEnv;
|
|
871
|
+
}
|
|
360
872
|
/**
|
|
361
873
|
* Detect if the project is ESM.
|
|
362
874
|
*/
|
|
363
875
|
static isESM() {
|
|
364
|
-
const pkgPath =
|
|
365
|
-
if (
|
|
876
|
+
const pkgPath = path5.join(process.cwd(), "package.json");
|
|
877
|
+
if (fs5.existsSync(pkgPath)) {
|
|
366
878
|
try {
|
|
367
|
-
const pkg = JSON.parse(
|
|
879
|
+
const pkg = JSON.parse(fs5.readFileSync(pkgPath, "utf-8"));
|
|
368
880
|
return pkg.type === "module";
|
|
369
881
|
} catch (e) {
|
|
370
882
|
return false;
|
|
@@ -375,8 +887,8 @@ var Config = class {
|
|
|
375
887
|
};
|
|
376
888
|
|
|
377
889
|
// src/cli/commands/seed.ts
|
|
378
|
-
var
|
|
379
|
-
var
|
|
890
|
+
var fs6 = __toESM(require("fs"), 1);
|
|
891
|
+
var path6 = __toESM(require("path"), 1);
|
|
380
892
|
|
|
381
893
|
// src/core/connection.ts
|
|
382
894
|
var Connection = class {
|
|
@@ -610,38 +1122,38 @@ var CLIPREparedStatement = class {
|
|
|
610
1122
|
};
|
|
611
1123
|
|
|
612
1124
|
// src/cli/commands/seed.ts
|
|
613
|
-
var
|
|
1125
|
+
var p5 = __toESM(require("@clack/prompts"), 1);
|
|
614
1126
|
async function seed(args2) {
|
|
615
|
-
|
|
616
|
-
const srcSeedersDir =
|
|
617
|
-
const rootSeedersDir =
|
|
618
|
-
const seedersDir =
|
|
619
|
-
if (!
|
|
620
|
-
|
|
1127
|
+
p5.intro("Seeding database...");
|
|
1128
|
+
const srcSeedersDir = path6.join(process.cwd(), "src/database/seeders");
|
|
1129
|
+
const rootSeedersDir = path6.join(process.cwd(), "database/seeders");
|
|
1130
|
+
const seedersDir = fs6.existsSync(srcSeedersDir) ? srcSeedersDir : rootSeedersDir;
|
|
1131
|
+
if (!fs6.existsSync(seedersDir)) {
|
|
1132
|
+
p5.log.warn(
|
|
621
1133
|
`No seeders directory found (checked ${srcSeedersDir} and ${rootSeedersDir}).`
|
|
622
1134
|
);
|
|
623
|
-
|
|
1135
|
+
p5.outro("Nothing to seed.");
|
|
624
1136
|
return;
|
|
625
1137
|
}
|
|
626
1138
|
const isRemote = args2.includes("--remote");
|
|
627
1139
|
const dbName = Config.getD1Binding();
|
|
628
|
-
const s =
|
|
1140
|
+
const s = p5.spinner();
|
|
629
1141
|
s.start(`Initializing ORM (Binding: ${dbName})...`);
|
|
630
1142
|
try {
|
|
631
1143
|
const connection = new CLIConnection(dbName, isRemote);
|
|
632
1144
|
Database.setup(connection);
|
|
633
1145
|
s.stop(`ORM initialized successfully with binding "${dbName}".`);
|
|
634
|
-
const files =
|
|
1146
|
+
const files = fs6.readdirSync(seedersDir).filter(
|
|
635
1147
|
(f) => f.endsWith(".ts") || f.endsWith(".js") || f.endsWith(".mts") || f.endsWith(".mjs")
|
|
636
1148
|
).sort();
|
|
637
1149
|
if (files.length === 0) {
|
|
638
|
-
|
|
639
|
-
|
|
1150
|
+
p5.log.info("No seeder files found.");
|
|
1151
|
+
p5.outro("Seeding complete.");
|
|
640
1152
|
return;
|
|
641
1153
|
}
|
|
642
1154
|
for (const file of files) {
|
|
643
1155
|
s.start(`Running seeder: ${file}`);
|
|
644
|
-
const filePath =
|
|
1156
|
+
const filePath = path6.join(seedersDir, file);
|
|
645
1157
|
try {
|
|
646
1158
|
const seeder = await import(filePath);
|
|
647
1159
|
if (seeder.seed) {
|
|
@@ -652,59 +1164,62 @@ async function seed(args2) {
|
|
|
652
1164
|
}
|
|
653
1165
|
} catch (error) {
|
|
654
1166
|
s.stop(`Failed: ${file}`, 1);
|
|
655
|
-
|
|
1167
|
+
p5.log.error(`Error running seeder ${file}: ${error}`);
|
|
656
1168
|
}
|
|
657
1169
|
}
|
|
658
|
-
|
|
1170
|
+
p5.outro("Seeding completed successfully.");
|
|
659
1171
|
} catch (error) {
|
|
660
1172
|
s.stop("Initialization failed.", 1);
|
|
661
|
-
|
|
1173
|
+
p5.log.error(`Seeding error: ${error}`);
|
|
662
1174
|
}
|
|
663
1175
|
}
|
|
664
1176
|
|
|
665
1177
|
// src/cli/commands/migrate.ts
|
|
666
1178
|
async function migrate(args2) {
|
|
667
|
-
|
|
668
|
-
const srcMigrationsDir =
|
|
669
|
-
const rootMigrationsDir =
|
|
670
|
-
const migrationsDir =
|
|
671
|
-
if (!
|
|
672
|
-
|
|
1179
|
+
p6.intro("Running migrations...");
|
|
1180
|
+
const srcMigrationsDir = path7.join(process.cwd(), "src/database/migrations");
|
|
1181
|
+
const rootMigrationsDir = path7.join(process.cwd(), "database/migrations");
|
|
1182
|
+
const migrationsDir = fs7.existsSync(srcMigrationsDir) ? srcMigrationsDir : rootMigrationsDir;
|
|
1183
|
+
if (!fs7.existsSync(migrationsDir)) {
|
|
1184
|
+
p6.log.warn(
|
|
673
1185
|
`No migrations directory found (checked ${srcMigrationsDir} and ${rootMigrationsDir}).`
|
|
674
1186
|
);
|
|
675
|
-
|
|
1187
|
+
p6.outro("Nothing to migrate.");
|
|
676
1188
|
return;
|
|
677
1189
|
}
|
|
678
|
-
const files =
|
|
1190
|
+
const files = fs7.readdirSync(migrationsDir).filter(
|
|
679
1191
|
(f) => f.endsWith(".ts") || f.endsWith(".js") || f.endsWith(".mts") || f.endsWith(".mjs")
|
|
680
1192
|
).sort();
|
|
681
1193
|
if (files.length === 0) {
|
|
682
|
-
|
|
683
|
-
|
|
1194
|
+
p6.log.info("No migration files found.");
|
|
1195
|
+
p6.outro("Migrations complete.");
|
|
684
1196
|
return;
|
|
685
1197
|
}
|
|
686
|
-
const s =
|
|
1198
|
+
const s = p6.spinner();
|
|
687
1199
|
const dbName = Config.getD1Binding();
|
|
688
1200
|
for (const file of files) {
|
|
689
1201
|
s.start(`Processing migration: ${file} (Binding: ${dbName})`);
|
|
690
|
-
const filePath =
|
|
1202
|
+
const filePath = path7.join(migrationsDir, file);
|
|
691
1203
|
try {
|
|
692
1204
|
const migration = await import(filePath);
|
|
693
1205
|
if (migration.up) {
|
|
694
1206
|
const sql = await migration.up();
|
|
695
1207
|
if (sql) {
|
|
1208
|
+
const sqlStatements = Array.isArray(sql) ? sql : [sql];
|
|
696
1209
|
const isRemote = args2.includes("--remote");
|
|
697
1210
|
const command2 = isRemote ? "--remote" : "--local";
|
|
698
1211
|
try {
|
|
699
|
-
const
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
1212
|
+
for (const statement of sqlStatements) {
|
|
1213
|
+
const execCmd = `npx wrangler d1 execute ${dbName} --command "${statement.replace(/"/g, '\\"')}" ${command2}`;
|
|
1214
|
+
(0, import_child_process2.execSync)(execCmd, {
|
|
1215
|
+
stdio: "pipe",
|
|
1216
|
+
env: Config.getCleanEnv()
|
|
1217
|
+
});
|
|
1218
|
+
}
|
|
704
1219
|
s.stop(`Migrated: ${file}`);
|
|
705
1220
|
} catch (e) {
|
|
706
1221
|
s.stop(`Failed: ${file}`, 1);
|
|
707
|
-
|
|
1222
|
+
p6.log.error(`Failed to execute migration: ${file}`);
|
|
708
1223
|
throw e;
|
|
709
1224
|
}
|
|
710
1225
|
} else {
|
|
@@ -715,10 +1230,10 @@ async function migrate(args2) {
|
|
|
715
1230
|
}
|
|
716
1231
|
} catch (error) {
|
|
717
1232
|
s.stop(`Error: ${file}`, 1);
|
|
718
|
-
|
|
1233
|
+
p6.log.error(`Error processing migration ${file}: ${error}`);
|
|
719
1234
|
}
|
|
720
1235
|
}
|
|
721
|
-
|
|
1236
|
+
p6.outro("All migrations executed.");
|
|
722
1237
|
if (args2.includes("--seed")) {
|
|
723
1238
|
await seed(args2);
|
|
724
1239
|
}
|
|
@@ -726,19 +1241,20 @@ async function migrate(args2) {
|
|
|
726
1241
|
|
|
727
1242
|
// src/cli/commands/migrate-fresh.ts
|
|
728
1243
|
var import_child_process3 = require("child_process");
|
|
729
|
-
var
|
|
1244
|
+
var p7 = __toESM(require("@clack/prompts"), 1);
|
|
730
1245
|
async function migrateFresh(args2) {
|
|
731
|
-
|
|
1246
|
+
p7.intro("Resetting database...");
|
|
732
1247
|
const isRemote = args2.includes("--remote");
|
|
733
1248
|
const dbName = Config.getD1Binding();
|
|
734
1249
|
const flag = isRemote ? "--remote" : "--local";
|
|
735
|
-
const s =
|
|
1250
|
+
const s = p7.spinner();
|
|
736
1251
|
s.start("Scanning for tables to drop...");
|
|
737
1252
|
try {
|
|
738
1253
|
const listTablesCmd = `npx wrangler d1 execute ${dbName} --command "SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%' AND name NOT LIKE '_cf_%'" ${flag} --json`;
|
|
739
1254
|
const output = (0, import_child_process3.execSync)(listTablesCmd, {
|
|
740
1255
|
encoding: "utf-8",
|
|
741
|
-
stdio: ["ignore", "pipe", "
|
|
1256
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
1257
|
+
env: Config.getCleanEnv()
|
|
742
1258
|
});
|
|
743
1259
|
const jsonStart = output.indexOf("[");
|
|
744
1260
|
if (jsonStart !== -1) {
|
|
@@ -748,7 +1264,10 @@ async function migrateFresh(args2) {
|
|
|
748
1264
|
s.message(`Dropping ${tables.length} tables...`);
|
|
749
1265
|
const dropCommands = tables.map((t) => `DROP TABLE IF EXISTS ${t.name};`).join(" ");
|
|
750
1266
|
const dropCmd = `npx wrangler d1 execute ${dbName} --command "${dropCommands}" ${flag}`;
|
|
751
|
-
(0, import_child_process3.execSync)(dropCmd, {
|
|
1267
|
+
(0, import_child_process3.execSync)(dropCmd, {
|
|
1268
|
+
stdio: "pipe",
|
|
1269
|
+
env: Config.getCleanEnv()
|
|
1270
|
+
});
|
|
752
1271
|
s.stop("All tables dropped successfully.");
|
|
753
1272
|
} else {
|
|
754
1273
|
s.stop("No tables found to drop.");
|
|
@@ -759,7 +1278,7 @@ async function migrateFresh(args2) {
|
|
|
759
1278
|
await migrate(args2);
|
|
760
1279
|
} catch (error) {
|
|
761
1280
|
s.stop("Process failed.", 1);
|
|
762
|
-
|
|
1281
|
+
p7.log.error(`Error during migrate:fresh: ${error}`);
|
|
763
1282
|
}
|
|
764
1283
|
}
|
|
765
1284
|
|
|
@@ -777,6 +1296,9 @@ switch (command) {
|
|
|
777
1296
|
case "make:migration":
|
|
778
1297
|
makeMigration(param);
|
|
779
1298
|
break;
|
|
1299
|
+
case "model:add":
|
|
1300
|
+
modelAdd();
|
|
1301
|
+
break;
|
|
780
1302
|
case "migrate":
|
|
781
1303
|
migrate(args);
|
|
782
1304
|
break;
|
|
@@ -788,7 +1310,7 @@ switch (command) {
|
|
|
788
1310
|
break;
|
|
789
1311
|
default:
|
|
790
1312
|
console.log(
|
|
791
|
-
"Available commands: init, make:model, make:migration, migrate, migrate:fresh, db:seed"
|
|
1313
|
+
"Available commands: init, make:model, make:migration, model:add, migrate, migrate:fresh, db:seed"
|
|
792
1314
|
);
|
|
793
1315
|
break;
|
|
794
1316
|
}
|