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