@farming-labs/orm 0.0.1 → 0.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +203 -46
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +43 -1
- package/dist/index.d.ts +43 -1
- package/dist/index.js +203 -46
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -47,17 +47,38 @@ function createModelClient(schema, driver, model2) {
|
|
|
47
47
|
findMany(args) {
|
|
48
48
|
return driver.findMany(schema, model2, args ?? {});
|
|
49
49
|
},
|
|
50
|
+
findOne(args) {
|
|
51
|
+
return driver.findFirst(schema, model2, args ?? {});
|
|
52
|
+
},
|
|
50
53
|
findFirst(args) {
|
|
51
54
|
return driver.findFirst(schema, model2, args ?? {});
|
|
52
55
|
},
|
|
56
|
+
findUnique(args) {
|
|
57
|
+
return driver.findUnique(schema, model2, args);
|
|
58
|
+
},
|
|
59
|
+
count(args) {
|
|
60
|
+
return driver.count(schema, model2, args);
|
|
61
|
+
},
|
|
53
62
|
create(args) {
|
|
54
63
|
return driver.create(schema, model2, args);
|
|
55
64
|
},
|
|
65
|
+
createMany(args) {
|
|
66
|
+
return driver.createMany(schema, model2, args);
|
|
67
|
+
},
|
|
56
68
|
update(args) {
|
|
57
69
|
return driver.update(schema, model2, args);
|
|
58
70
|
},
|
|
71
|
+
updateMany(args) {
|
|
72
|
+
return driver.updateMany(schema, model2, args);
|
|
73
|
+
},
|
|
74
|
+
upsert(args) {
|
|
75
|
+
return driver.upsert(schema, model2, args);
|
|
76
|
+
},
|
|
59
77
|
delete(args) {
|
|
60
78
|
return driver.delete(schema, model2, args);
|
|
79
|
+
},
|
|
80
|
+
deleteMany(args) {
|
|
81
|
+
return driver.deleteMany(schema, model2, args);
|
|
61
82
|
}
|
|
62
83
|
};
|
|
63
84
|
}
|
|
@@ -75,6 +96,13 @@ function createOrm(options) {
|
|
|
75
96
|
});
|
|
76
97
|
return run(tx);
|
|
77
98
|
});
|
|
99
|
+
orm.batch = async (tasks) => orm.transaction(async (tx) => {
|
|
100
|
+
const results = [];
|
|
101
|
+
for (const task of tasks) {
|
|
102
|
+
results.push(await task(tx));
|
|
103
|
+
}
|
|
104
|
+
return results;
|
|
105
|
+
});
|
|
78
106
|
return orm;
|
|
79
107
|
}
|
|
80
108
|
|
|
@@ -198,6 +226,29 @@ function createManifest(schema) {
|
|
|
198
226
|
// src/generators.ts
|
|
199
227
|
var capitalize = (value) => value.charAt(0).toUpperCase() + value.slice(1);
|
|
200
228
|
var pluralize = (value) => value.endsWith("s") ? value : `${value}s`;
|
|
229
|
+
function resolveReferenceTarget(manifest, model2, foreignKey, fallbackTarget) {
|
|
230
|
+
const reference = model2.fields[foreignKey]?.references;
|
|
231
|
+
if (!reference) {
|
|
232
|
+
return {
|
|
233
|
+
targetModel: fallbackTarget,
|
|
234
|
+
targetField: "id"
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
const [targetModel, targetField = "id"] = reference.split(".");
|
|
238
|
+
return {
|
|
239
|
+
targetModel,
|
|
240
|
+
targetField
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
function hasExplicitInverseRelation(manifest, modelName, sourceModel, foreignKey) {
|
|
244
|
+
const model2 = manifest.models[modelName];
|
|
245
|
+
if (!model2) return false;
|
|
246
|
+
return Object.values(model2.relations).some((relation) => {
|
|
247
|
+
if (relation.target !== sourceModel) return false;
|
|
248
|
+
if (relation.kind === "belongsTo" || relation.kind === "manyToMany") return false;
|
|
249
|
+
return relation.foreignKey === foreignKey;
|
|
250
|
+
});
|
|
251
|
+
}
|
|
201
252
|
function prismaType(field) {
|
|
202
253
|
switch (field.kind) {
|
|
203
254
|
case "id":
|
|
@@ -269,7 +320,7 @@ function sqlType(field, dialect) {
|
|
|
269
320
|
return dialect === "mysql" ? "varchar(191)" : "text";
|
|
270
321
|
}
|
|
271
322
|
if (field.kind === "string") {
|
|
272
|
-
return dialect === "mysql" && field.unique ? "varchar(191)" : "text";
|
|
323
|
+
return dialect === "mysql" && (field.unique || field.references) ? "varchar(191)" : "text";
|
|
273
324
|
}
|
|
274
325
|
if (field.kind === "boolean") {
|
|
275
326
|
return dialect === "sqlite" ? "integer" : "boolean";
|
|
@@ -282,6 +333,12 @@ function sqlType(field, dialect) {
|
|
|
282
333
|
}
|
|
283
334
|
return "timestamp";
|
|
284
335
|
}
|
|
336
|
+
function sqlIdentifier(dialect, value) {
|
|
337
|
+
if (dialect === "mysql") {
|
|
338
|
+
return `\`${value}\``;
|
|
339
|
+
}
|
|
340
|
+
return `"${value}"`;
|
|
341
|
+
}
|
|
285
342
|
function renderPrismaSchema(schema, options = {}) {
|
|
286
343
|
const manifest = createManifest(schema);
|
|
287
344
|
const provider = options.provider ?? "postgresql";
|
|
@@ -296,6 +353,7 @@ function renderPrismaSchema(schema, options = {}) {
|
|
|
296
353
|
...reverseRelations.get(targetModel) ?? [],
|
|
297
354
|
{
|
|
298
355
|
sourceModel: model2.name,
|
|
356
|
+
foreignKey: field.name,
|
|
299
357
|
many: !field.unique
|
|
300
358
|
}
|
|
301
359
|
]);
|
|
@@ -304,6 +362,8 @@ function renderPrismaSchema(schema, options = {}) {
|
|
|
304
362
|
const blocks = Object.values(manifest.models).map((model2) => {
|
|
305
363
|
const lines = [];
|
|
306
364
|
const modelName = capitalize(model2.name);
|
|
365
|
+
const relationFieldNames = /* @__PURE__ */ new Set();
|
|
366
|
+
const handledForeignKeys = /* @__PURE__ */ new Set();
|
|
307
367
|
for (const field of Object.values(model2.fields)) {
|
|
308
368
|
const fieldType = prismaType(field);
|
|
309
369
|
const modifiers = [];
|
|
@@ -320,16 +380,45 @@ function renderPrismaSchema(schema, options = {}) {
|
|
|
320
380
|
lines.push(
|
|
321
381
|
` ${field.name} ${fieldType}${field.nullable ? "?" : ""}${modifiers.length ? ` ${modifiers.join(" ")}` : ""}`
|
|
322
382
|
);
|
|
323
|
-
|
|
324
|
-
|
|
383
|
+
}
|
|
384
|
+
for (const [relationName, relation] of Object.entries(model2.relations)) {
|
|
385
|
+
if (relation.kind === "manyToMany") continue;
|
|
386
|
+
relationFieldNames.add(relationName);
|
|
387
|
+
if (relation.kind === "belongsTo") {
|
|
388
|
+
const { targetField } = resolveReferenceTarget(
|
|
389
|
+
manifest,
|
|
390
|
+
model2,
|
|
391
|
+
relation.foreignKey,
|
|
392
|
+
relation.target
|
|
393
|
+
);
|
|
394
|
+
handledForeignKeys.add(relation.foreignKey);
|
|
325
395
|
lines.push(
|
|
326
|
-
` ${
|
|
396
|
+
` ${relationName} ${capitalize(relation.target)} @relation(fields: [${relation.foreignKey}], references: [${targetField}])`
|
|
327
397
|
);
|
|
398
|
+
continue;
|
|
399
|
+
}
|
|
400
|
+
if (relation.kind === "hasOne") {
|
|
401
|
+
lines.push(` ${relationName} ${capitalize(relation.target)}?`);
|
|
402
|
+
continue;
|
|
328
403
|
}
|
|
404
|
+
lines.push(` ${relationName} ${capitalize(relation.target)}[]`);
|
|
405
|
+
}
|
|
406
|
+
for (const field of Object.values(model2.fields)) {
|
|
407
|
+
if (!field.references || handledForeignKeys.has(field.name)) continue;
|
|
408
|
+
const [targetModel, targetField] = field.references.split(".");
|
|
409
|
+
if (relationFieldNames.has(targetModel)) continue;
|
|
410
|
+
lines.push(
|
|
411
|
+
` ${targetModel} ${capitalize(targetModel)} @relation(fields: [${field.name}], references: [${targetField}])`
|
|
412
|
+
);
|
|
329
413
|
}
|
|
330
414
|
for (const relation of reverseRelations.get(model2.name) ?? []) {
|
|
415
|
+
if (hasExplicitInverseRelation(manifest, model2.name, relation.sourceModel, relation.foreignKey)) {
|
|
416
|
+
continue;
|
|
417
|
+
}
|
|
418
|
+
const relationName = relation.many ? pluralize(relation.sourceModel) : relation.sourceModel;
|
|
419
|
+
if (relationFieldNames.has(relationName)) continue;
|
|
331
420
|
lines.push(
|
|
332
|
-
relation.many ? ` ${
|
|
421
|
+
relation.many ? ` ${relationName} ${capitalize(relation.sourceModel)}[]` : ` ${relationName} ${capitalize(relation.sourceModel)}?`
|
|
333
422
|
);
|
|
334
423
|
}
|
|
335
424
|
const mapLine = model2.table !== modelName ? `
|
|
@@ -352,7 +441,7 @@ ${blocks.join("\n\n")}
|
|
|
352
441
|
}
|
|
353
442
|
function renderDrizzleSchema(schema, options) {
|
|
354
443
|
const manifest = createManifest(schema);
|
|
355
|
-
const
|
|
444
|
+
const coreImports = drizzleImports(options.dialect, manifest).join(", ");
|
|
356
445
|
const tableFactory = options.dialect === "pg" ? "pgTable" : options.dialect === "mysql" ? "mysqlTable" : "sqliteTable";
|
|
357
446
|
const modelBlocks = Object.values(manifest.models).map((model2) => {
|
|
358
447
|
const lines = Object.values(model2.fields).map((field) => {
|
|
@@ -367,16 +456,48 @@ function renderDrizzleSchema(schema, options) {
|
|
|
367
456
|
${lines.join(",\n")}
|
|
368
457
|
});`;
|
|
369
458
|
});
|
|
370
|
-
|
|
459
|
+
const relationBlocks = Object.values(manifest.models).map((model2) => {
|
|
460
|
+
const lines = Object.entries(model2.relations).flatMap(([relationName, relation]) => {
|
|
461
|
+
if (relation.kind === "manyToMany") {
|
|
462
|
+
return [];
|
|
463
|
+
}
|
|
464
|
+
if (relation.kind === "belongsTo") {
|
|
465
|
+
const { targetField } = resolveReferenceTarget(
|
|
466
|
+
manifest,
|
|
467
|
+
model2,
|
|
468
|
+
relation.foreignKey,
|
|
469
|
+
relation.target
|
|
470
|
+
);
|
|
471
|
+
return [
|
|
472
|
+
` ${relationName}: one(${relation.target}, { fields: [${model2.name}.${relation.foreignKey}], references: [${relation.target}.${targetField}] })`
|
|
473
|
+
];
|
|
474
|
+
}
|
|
475
|
+
if (relation.kind === "hasOne") {
|
|
476
|
+
return [` ${relationName}: one(${relation.target})`];
|
|
477
|
+
}
|
|
478
|
+
return [` ${relationName}: many(${relation.target})`];
|
|
479
|
+
}).filter(Boolean);
|
|
480
|
+
if (!lines.length) return null;
|
|
481
|
+
return `export const ${model2.name}Relations = relations(${model2.name}, ({ one, many }) => ({
|
|
482
|
+
${lines.join(",\n")}
|
|
483
|
+
}));`;
|
|
484
|
+
}).filter(Boolean);
|
|
485
|
+
const imports = [
|
|
486
|
+
`import { ${coreImports} } from "drizzle-orm/${options.dialect === "pg" ? "pg-core" : options.dialect === "mysql" ? "mysql-core" : "sqlite-core"}";`,
|
|
487
|
+
relationBlocks.length ? `import { relations } from "drizzle-orm";` : null
|
|
488
|
+
].filter(Boolean).join("\n");
|
|
489
|
+
return `${imports}
|
|
371
490
|
|
|
372
|
-
${modelBlocks.join("\n\n")}
|
|
491
|
+
${[...modelBlocks, ...relationBlocks].join("\n\n")}
|
|
373
492
|
`;
|
|
374
493
|
}
|
|
375
494
|
function renderSafeSql(schema, options) {
|
|
376
495
|
const manifest = createManifest(schema);
|
|
377
496
|
const statements = Object.values(manifest.models).map((model2) => {
|
|
378
497
|
const columns = Object.values(model2.fields).map((field) => {
|
|
379
|
-
const parts = [
|
|
498
|
+
const parts = [
|
|
499
|
+
`${sqlIdentifier(options.dialect, field.column)} ${sqlType(field, options.dialect)}`
|
|
500
|
+
];
|
|
380
501
|
if (field.kind === "id") parts.push("primary key");
|
|
381
502
|
if (!field.nullable) parts.push("not null");
|
|
382
503
|
if (field.unique && field.kind !== "id") parts.push("unique");
|
|
@@ -388,11 +509,17 @@ function renderSafeSql(schema, options) {
|
|
|
388
509
|
if (field.references) {
|
|
389
510
|
const [targetModel, targetField] = field.references.split(".");
|
|
390
511
|
const targetTable = manifest.models[targetModel]?.table ?? targetModel;
|
|
391
|
-
|
|
512
|
+
const targetColumn = manifest.models[targetModel]?.fields[targetField]?.column ?? targetField;
|
|
513
|
+
parts.push(
|
|
514
|
+
`references ${sqlIdentifier(options.dialect, targetTable)}(${sqlIdentifier(
|
|
515
|
+
options.dialect,
|
|
516
|
+
targetColumn
|
|
517
|
+
)})`
|
|
518
|
+
);
|
|
392
519
|
}
|
|
393
520
|
return ` ${parts.join(" ")}`;
|
|
394
521
|
});
|
|
395
|
-
return `create table if not exists ${model2.table} (
|
|
522
|
+
return `create table if not exists ${sqlIdentifier(options.dialect, model2.table)} (
|
|
396
523
|
${columns.join(",\n")}
|
|
397
524
|
);`;
|
|
398
525
|
});
|
|
@@ -509,6 +636,11 @@ function pageRows(rows, skip, take) {
|
|
|
509
636
|
const end = take === void 0 ? void 0 : start + take;
|
|
510
637
|
return rows.slice(start, end);
|
|
511
638
|
}
|
|
639
|
+
function applyQuery(rows, args = {}) {
|
|
640
|
+
const filtered = rows.filter((row) => matchesWhere(row, args.where));
|
|
641
|
+
const sorted = sortRows(filtered, args.orderBy);
|
|
642
|
+
return pageRows(sorted, args.skip, args.take);
|
|
643
|
+
}
|
|
512
644
|
function createMemoryDriver(seed) {
|
|
513
645
|
const state = structuredClone(seed ?? {});
|
|
514
646
|
function getRows(model2) {
|
|
@@ -516,14 +648,13 @@ function createMemoryDriver(seed) {
|
|
|
516
648
|
state[model2] = rows;
|
|
517
649
|
return rows;
|
|
518
650
|
}
|
|
519
|
-
function
|
|
520
|
-
const
|
|
521
|
-
const
|
|
522
|
-
const
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
return pageRows(sorted, args.skip, args.take);
|
|
651
|
+
function buildRow(schema, model2, data) {
|
|
652
|
+
const modelDefinition = schema.models[model2];
|
|
653
|
+
const nextRow = {};
|
|
654
|
+
for (const [fieldName, field] of Object.entries(modelDefinition.fields)) {
|
|
655
|
+
nextRow[fieldName] = applyDefault(data[fieldName], field.config);
|
|
656
|
+
}
|
|
657
|
+
return nextRow;
|
|
527
658
|
}
|
|
528
659
|
async function projectRow(schema, model2, row, select) {
|
|
529
660
|
const modelDefinition = schema.models[model2];
|
|
@@ -556,28 +687,27 @@ function createMemoryDriver(seed) {
|
|
|
556
687
|
const relation = schema.models[model2].relations[relationName];
|
|
557
688
|
const relationArgs = value === true ? {} : value;
|
|
558
689
|
if (relation.kind === "belongsTo") {
|
|
559
|
-
const targetRows2 = getRows(relation.target);
|
|
560
690
|
const foreignValue = row[relation.foreignKey];
|
|
561
|
-
const
|
|
691
|
+
const targetRows2 = getRows(relation.target).filter(
|
|
692
|
+
(item) => item.id === foreignValue
|
|
693
|
+
);
|
|
694
|
+
const target = applyQuery(targetRows2, relationArgs)[0];
|
|
562
695
|
return target ? projectRow(schema, relation.target, target, relationArgs.select) : null;
|
|
563
696
|
}
|
|
564
697
|
if (relation.kind === "hasOne") {
|
|
565
|
-
const targetRows2 = getRows(relation.target)
|
|
566
|
-
|
|
698
|
+
const targetRows2 = getRows(relation.target).filter(
|
|
699
|
+
(item) => item[relation.foreignKey] === row.id
|
|
700
|
+
);
|
|
701
|
+
const target = applyQuery(targetRows2, relationArgs)[0];
|
|
567
702
|
return target ? projectRow(schema, relation.target, target, relationArgs.select) : null;
|
|
568
703
|
}
|
|
569
704
|
if (relation.kind === "hasMany") {
|
|
570
705
|
const targetRows2 = getRows(relation.target).filter(
|
|
571
706
|
(item) => item[relation.foreignKey] === row.id
|
|
572
707
|
);
|
|
573
|
-
const
|
|
574
|
-
targetRows2,
|
|
575
|
-
relationArgs.orderBy
|
|
576
|
-
);
|
|
577
|
-
const paged2 = pageRows(sorted2, relationArgs.skip, relationArgs.take);
|
|
578
|
-
const filtered2 = paged2.filter((item) => matchesWhere(item, relationArgs.where));
|
|
708
|
+
const matchedRows2 = applyQuery(targetRows2, relationArgs);
|
|
579
709
|
return Promise.all(
|
|
580
|
-
|
|
710
|
+
matchedRows2.map(
|
|
581
711
|
(item) => projectRow(schema, relation.target, item, relationArgs.select)
|
|
582
712
|
)
|
|
583
713
|
);
|
|
@@ -589,45 +719,72 @@ function createMemoryDriver(seed) {
|
|
|
589
719
|
const targetRows = getRows(relation.target).filter(
|
|
590
720
|
(item) => targetIds.includes(item.id)
|
|
591
721
|
);
|
|
592
|
-
const
|
|
593
|
-
targetRows,
|
|
594
|
-
relationArgs.orderBy
|
|
595
|
-
);
|
|
596
|
-
const paged = pageRows(sorted, relationArgs.skip, relationArgs.take);
|
|
597
|
-
const filtered = paged.filter((item) => matchesWhere(item, relationArgs.where));
|
|
722
|
+
const matchedRows = applyQuery(targetRows, relationArgs);
|
|
598
723
|
return Promise.all(
|
|
599
|
-
|
|
724
|
+
matchedRows.map(
|
|
600
725
|
(item) => projectRow(schema, relation.target, item, relationArgs.select)
|
|
601
726
|
)
|
|
602
727
|
);
|
|
603
728
|
}
|
|
604
729
|
const driver = {
|
|
605
730
|
async findMany(schema, model2, args) {
|
|
606
|
-
const rows = applyQuery(model2, args);
|
|
731
|
+
const rows = applyQuery(getRows(model2), args);
|
|
607
732
|
return Promise.all(rows.map((row) => projectRow(schema, model2, row, args.select)));
|
|
608
733
|
},
|
|
609
734
|
async findFirst(schema, model2, args) {
|
|
610
|
-
const row = applyQuery(model2, args)[0];
|
|
735
|
+
const row = applyQuery(getRows(model2), args)[0];
|
|
736
|
+
if (!row) return null;
|
|
737
|
+
return projectRow(schema, model2, row, args.select);
|
|
738
|
+
},
|
|
739
|
+
async findUnique(schema, model2, args) {
|
|
740
|
+
const row = applyQuery(getRows(model2), args)[0];
|
|
611
741
|
if (!row) return null;
|
|
612
742
|
return projectRow(schema, model2, row, args.select);
|
|
613
743
|
},
|
|
744
|
+
async count(_schema, model2, args) {
|
|
745
|
+
return applyQuery(getRows(model2), args).length;
|
|
746
|
+
},
|
|
614
747
|
async create(schema, model2, args) {
|
|
615
|
-
const
|
|
616
|
-
const nextRow = {};
|
|
617
|
-
for (const [fieldName, field] of Object.entries(modelDefinition.fields)) {
|
|
618
|
-
nextRow[fieldName] = applyDefault(args.data[fieldName], field.config);
|
|
619
|
-
}
|
|
748
|
+
const nextRow = buildRow(schema, model2, args.data);
|
|
620
749
|
getRows(model2).push(nextRow);
|
|
621
750
|
return projectRow(schema, model2, nextRow, args.select);
|
|
622
751
|
},
|
|
752
|
+
async createMany(schema, model2, args) {
|
|
753
|
+
const rows = args.data.map((entry) => buildRow(schema, model2, entry));
|
|
754
|
+
getRows(model2).push(...rows);
|
|
755
|
+
return Promise.all(rows.map((row) => projectRow(schema, model2, row, args.select)));
|
|
756
|
+
},
|
|
623
757
|
async update(schema, model2, args) {
|
|
624
|
-
const
|
|
625
|
-
const row = rows.find((item) => matchesWhere(item, args.where));
|
|
758
|
+
const row = getRows(model2).find((item) => matchesWhere(item, args.where));
|
|
626
759
|
if (!row) return null;
|
|
627
760
|
Object.assign(row, args.data);
|
|
628
761
|
return projectRow(schema, model2, row, args.select);
|
|
629
762
|
},
|
|
763
|
+
async updateMany(_schema, model2, args) {
|
|
764
|
+
const rows = getRows(model2).filter((item) => matchesWhere(item, args.where));
|
|
765
|
+
for (const row of rows) {
|
|
766
|
+
Object.assign(row, args.data);
|
|
767
|
+
}
|
|
768
|
+
return rows.length;
|
|
769
|
+
},
|
|
770
|
+
async upsert(schema, model2, args) {
|
|
771
|
+
const row = getRows(model2).find((item) => matchesWhere(item, args.where));
|
|
772
|
+
if (row) {
|
|
773
|
+
Object.assign(row, args.update);
|
|
774
|
+
return projectRow(schema, model2, row, args.select);
|
|
775
|
+
}
|
|
776
|
+
const created = buildRow(schema, model2, args.create);
|
|
777
|
+
getRows(model2).push(created);
|
|
778
|
+
return projectRow(schema, model2, created, args.select);
|
|
779
|
+
},
|
|
630
780
|
async delete(_schema, model2, args) {
|
|
781
|
+
const rows = getRows(model2);
|
|
782
|
+
const index = rows.findIndex((item) => matchesWhere(item, args.where));
|
|
783
|
+
if (index === -1) return 0;
|
|
784
|
+
rows.splice(index, 1);
|
|
785
|
+
return 1;
|
|
786
|
+
},
|
|
787
|
+
async deleteMany(_schema, model2, args) {
|
|
631
788
|
const rows = getRows(model2);
|
|
632
789
|
const before = rows.length;
|
|
633
790
|
state[model2] = rows.filter((item) => !matchesWhere(item, args.where));
|