@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.js
CHANGED
|
@@ -4,17 +4,38 @@ function createModelClient(schema, driver, model2) {
|
|
|
4
4
|
findMany(args) {
|
|
5
5
|
return driver.findMany(schema, model2, args ?? {});
|
|
6
6
|
},
|
|
7
|
+
findOne(args) {
|
|
8
|
+
return driver.findFirst(schema, model2, args ?? {});
|
|
9
|
+
},
|
|
7
10
|
findFirst(args) {
|
|
8
11
|
return driver.findFirst(schema, model2, args ?? {});
|
|
9
12
|
},
|
|
13
|
+
findUnique(args) {
|
|
14
|
+
return driver.findUnique(schema, model2, args);
|
|
15
|
+
},
|
|
16
|
+
count(args) {
|
|
17
|
+
return driver.count(schema, model2, args);
|
|
18
|
+
},
|
|
10
19
|
create(args) {
|
|
11
20
|
return driver.create(schema, model2, args);
|
|
12
21
|
},
|
|
22
|
+
createMany(args) {
|
|
23
|
+
return driver.createMany(schema, model2, args);
|
|
24
|
+
},
|
|
13
25
|
update(args) {
|
|
14
26
|
return driver.update(schema, model2, args);
|
|
15
27
|
},
|
|
28
|
+
updateMany(args) {
|
|
29
|
+
return driver.updateMany(schema, model2, args);
|
|
30
|
+
},
|
|
31
|
+
upsert(args) {
|
|
32
|
+
return driver.upsert(schema, model2, args);
|
|
33
|
+
},
|
|
16
34
|
delete(args) {
|
|
17
35
|
return driver.delete(schema, model2, args);
|
|
36
|
+
},
|
|
37
|
+
deleteMany(args) {
|
|
38
|
+
return driver.deleteMany(schema, model2, args);
|
|
18
39
|
}
|
|
19
40
|
};
|
|
20
41
|
}
|
|
@@ -32,6 +53,13 @@ function createOrm(options) {
|
|
|
32
53
|
});
|
|
33
54
|
return run(tx);
|
|
34
55
|
});
|
|
56
|
+
orm.batch = async (tasks) => orm.transaction(async (tx) => {
|
|
57
|
+
const results = [];
|
|
58
|
+
for (const task of tasks) {
|
|
59
|
+
results.push(await task(tx));
|
|
60
|
+
}
|
|
61
|
+
return results;
|
|
62
|
+
});
|
|
35
63
|
return orm;
|
|
36
64
|
}
|
|
37
65
|
|
|
@@ -155,6 +183,29 @@ function createManifest(schema) {
|
|
|
155
183
|
// src/generators.ts
|
|
156
184
|
var capitalize = (value) => value.charAt(0).toUpperCase() + value.slice(1);
|
|
157
185
|
var pluralize = (value) => value.endsWith("s") ? value : `${value}s`;
|
|
186
|
+
function resolveReferenceTarget(manifest, model2, foreignKey, fallbackTarget) {
|
|
187
|
+
const reference = model2.fields[foreignKey]?.references;
|
|
188
|
+
if (!reference) {
|
|
189
|
+
return {
|
|
190
|
+
targetModel: fallbackTarget,
|
|
191
|
+
targetField: "id"
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
const [targetModel, targetField = "id"] = reference.split(".");
|
|
195
|
+
return {
|
|
196
|
+
targetModel,
|
|
197
|
+
targetField
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
function hasExplicitInverseRelation(manifest, modelName, sourceModel, foreignKey) {
|
|
201
|
+
const model2 = manifest.models[modelName];
|
|
202
|
+
if (!model2) return false;
|
|
203
|
+
return Object.values(model2.relations).some((relation) => {
|
|
204
|
+
if (relation.target !== sourceModel) return false;
|
|
205
|
+
if (relation.kind === "belongsTo" || relation.kind === "manyToMany") return false;
|
|
206
|
+
return relation.foreignKey === foreignKey;
|
|
207
|
+
});
|
|
208
|
+
}
|
|
158
209
|
function prismaType(field) {
|
|
159
210
|
switch (field.kind) {
|
|
160
211
|
case "id":
|
|
@@ -226,7 +277,7 @@ function sqlType(field, dialect) {
|
|
|
226
277
|
return dialect === "mysql" ? "varchar(191)" : "text";
|
|
227
278
|
}
|
|
228
279
|
if (field.kind === "string") {
|
|
229
|
-
return dialect === "mysql" && field.unique ? "varchar(191)" : "text";
|
|
280
|
+
return dialect === "mysql" && (field.unique || field.references) ? "varchar(191)" : "text";
|
|
230
281
|
}
|
|
231
282
|
if (field.kind === "boolean") {
|
|
232
283
|
return dialect === "sqlite" ? "integer" : "boolean";
|
|
@@ -239,6 +290,12 @@ function sqlType(field, dialect) {
|
|
|
239
290
|
}
|
|
240
291
|
return "timestamp";
|
|
241
292
|
}
|
|
293
|
+
function sqlIdentifier(dialect, value) {
|
|
294
|
+
if (dialect === "mysql") {
|
|
295
|
+
return `\`${value}\``;
|
|
296
|
+
}
|
|
297
|
+
return `"${value}"`;
|
|
298
|
+
}
|
|
242
299
|
function renderPrismaSchema(schema, options = {}) {
|
|
243
300
|
const manifest = createManifest(schema);
|
|
244
301
|
const provider = options.provider ?? "postgresql";
|
|
@@ -253,6 +310,7 @@ function renderPrismaSchema(schema, options = {}) {
|
|
|
253
310
|
...reverseRelations.get(targetModel) ?? [],
|
|
254
311
|
{
|
|
255
312
|
sourceModel: model2.name,
|
|
313
|
+
foreignKey: field.name,
|
|
256
314
|
many: !field.unique
|
|
257
315
|
}
|
|
258
316
|
]);
|
|
@@ -261,6 +319,8 @@ function renderPrismaSchema(schema, options = {}) {
|
|
|
261
319
|
const blocks = Object.values(manifest.models).map((model2) => {
|
|
262
320
|
const lines = [];
|
|
263
321
|
const modelName = capitalize(model2.name);
|
|
322
|
+
const relationFieldNames = /* @__PURE__ */ new Set();
|
|
323
|
+
const handledForeignKeys = /* @__PURE__ */ new Set();
|
|
264
324
|
for (const field of Object.values(model2.fields)) {
|
|
265
325
|
const fieldType = prismaType(field);
|
|
266
326
|
const modifiers = [];
|
|
@@ -277,16 +337,45 @@ function renderPrismaSchema(schema, options = {}) {
|
|
|
277
337
|
lines.push(
|
|
278
338
|
` ${field.name} ${fieldType}${field.nullable ? "?" : ""}${modifiers.length ? ` ${modifiers.join(" ")}` : ""}`
|
|
279
339
|
);
|
|
280
|
-
|
|
281
|
-
|
|
340
|
+
}
|
|
341
|
+
for (const [relationName, relation] of Object.entries(model2.relations)) {
|
|
342
|
+
if (relation.kind === "manyToMany") continue;
|
|
343
|
+
relationFieldNames.add(relationName);
|
|
344
|
+
if (relation.kind === "belongsTo") {
|
|
345
|
+
const { targetField } = resolveReferenceTarget(
|
|
346
|
+
manifest,
|
|
347
|
+
model2,
|
|
348
|
+
relation.foreignKey,
|
|
349
|
+
relation.target
|
|
350
|
+
);
|
|
351
|
+
handledForeignKeys.add(relation.foreignKey);
|
|
282
352
|
lines.push(
|
|
283
|
-
` ${
|
|
353
|
+
` ${relationName} ${capitalize(relation.target)} @relation(fields: [${relation.foreignKey}], references: [${targetField}])`
|
|
284
354
|
);
|
|
355
|
+
continue;
|
|
356
|
+
}
|
|
357
|
+
if (relation.kind === "hasOne") {
|
|
358
|
+
lines.push(` ${relationName} ${capitalize(relation.target)}?`);
|
|
359
|
+
continue;
|
|
285
360
|
}
|
|
361
|
+
lines.push(` ${relationName} ${capitalize(relation.target)}[]`);
|
|
362
|
+
}
|
|
363
|
+
for (const field of Object.values(model2.fields)) {
|
|
364
|
+
if (!field.references || handledForeignKeys.has(field.name)) continue;
|
|
365
|
+
const [targetModel, targetField] = field.references.split(".");
|
|
366
|
+
if (relationFieldNames.has(targetModel)) continue;
|
|
367
|
+
lines.push(
|
|
368
|
+
` ${targetModel} ${capitalize(targetModel)} @relation(fields: [${field.name}], references: [${targetField}])`
|
|
369
|
+
);
|
|
286
370
|
}
|
|
287
371
|
for (const relation of reverseRelations.get(model2.name) ?? []) {
|
|
372
|
+
if (hasExplicitInverseRelation(manifest, model2.name, relation.sourceModel, relation.foreignKey)) {
|
|
373
|
+
continue;
|
|
374
|
+
}
|
|
375
|
+
const relationName = relation.many ? pluralize(relation.sourceModel) : relation.sourceModel;
|
|
376
|
+
if (relationFieldNames.has(relationName)) continue;
|
|
288
377
|
lines.push(
|
|
289
|
-
relation.many ? ` ${
|
|
378
|
+
relation.many ? ` ${relationName} ${capitalize(relation.sourceModel)}[]` : ` ${relationName} ${capitalize(relation.sourceModel)}?`
|
|
290
379
|
);
|
|
291
380
|
}
|
|
292
381
|
const mapLine = model2.table !== modelName ? `
|
|
@@ -309,7 +398,7 @@ ${blocks.join("\n\n")}
|
|
|
309
398
|
}
|
|
310
399
|
function renderDrizzleSchema(schema, options) {
|
|
311
400
|
const manifest = createManifest(schema);
|
|
312
|
-
const
|
|
401
|
+
const coreImports = drizzleImports(options.dialect, manifest).join(", ");
|
|
313
402
|
const tableFactory = options.dialect === "pg" ? "pgTable" : options.dialect === "mysql" ? "mysqlTable" : "sqliteTable";
|
|
314
403
|
const modelBlocks = Object.values(manifest.models).map((model2) => {
|
|
315
404
|
const lines = Object.values(model2.fields).map((field) => {
|
|
@@ -324,16 +413,48 @@ function renderDrizzleSchema(schema, options) {
|
|
|
324
413
|
${lines.join(",\n")}
|
|
325
414
|
});`;
|
|
326
415
|
});
|
|
327
|
-
|
|
416
|
+
const relationBlocks = Object.values(manifest.models).map((model2) => {
|
|
417
|
+
const lines = Object.entries(model2.relations).flatMap(([relationName, relation]) => {
|
|
418
|
+
if (relation.kind === "manyToMany") {
|
|
419
|
+
return [];
|
|
420
|
+
}
|
|
421
|
+
if (relation.kind === "belongsTo") {
|
|
422
|
+
const { targetField } = resolveReferenceTarget(
|
|
423
|
+
manifest,
|
|
424
|
+
model2,
|
|
425
|
+
relation.foreignKey,
|
|
426
|
+
relation.target
|
|
427
|
+
);
|
|
428
|
+
return [
|
|
429
|
+
` ${relationName}: one(${relation.target}, { fields: [${model2.name}.${relation.foreignKey}], references: [${relation.target}.${targetField}] })`
|
|
430
|
+
];
|
|
431
|
+
}
|
|
432
|
+
if (relation.kind === "hasOne") {
|
|
433
|
+
return [` ${relationName}: one(${relation.target})`];
|
|
434
|
+
}
|
|
435
|
+
return [` ${relationName}: many(${relation.target})`];
|
|
436
|
+
}).filter(Boolean);
|
|
437
|
+
if (!lines.length) return null;
|
|
438
|
+
return `export const ${model2.name}Relations = relations(${model2.name}, ({ one, many }) => ({
|
|
439
|
+
${lines.join(",\n")}
|
|
440
|
+
}));`;
|
|
441
|
+
}).filter(Boolean);
|
|
442
|
+
const imports = [
|
|
443
|
+
`import { ${coreImports} } from "drizzle-orm/${options.dialect === "pg" ? "pg-core" : options.dialect === "mysql" ? "mysql-core" : "sqlite-core"}";`,
|
|
444
|
+
relationBlocks.length ? `import { relations } from "drizzle-orm";` : null
|
|
445
|
+
].filter(Boolean).join("\n");
|
|
446
|
+
return `${imports}
|
|
328
447
|
|
|
329
|
-
${modelBlocks.join("\n\n")}
|
|
448
|
+
${[...modelBlocks, ...relationBlocks].join("\n\n")}
|
|
330
449
|
`;
|
|
331
450
|
}
|
|
332
451
|
function renderSafeSql(schema, options) {
|
|
333
452
|
const manifest = createManifest(schema);
|
|
334
453
|
const statements = Object.values(manifest.models).map((model2) => {
|
|
335
454
|
const columns = Object.values(model2.fields).map((field) => {
|
|
336
|
-
const parts = [
|
|
455
|
+
const parts = [
|
|
456
|
+
`${sqlIdentifier(options.dialect, field.column)} ${sqlType(field, options.dialect)}`
|
|
457
|
+
];
|
|
337
458
|
if (field.kind === "id") parts.push("primary key");
|
|
338
459
|
if (!field.nullable) parts.push("not null");
|
|
339
460
|
if (field.unique && field.kind !== "id") parts.push("unique");
|
|
@@ -345,11 +466,17 @@ function renderSafeSql(schema, options) {
|
|
|
345
466
|
if (field.references) {
|
|
346
467
|
const [targetModel, targetField] = field.references.split(".");
|
|
347
468
|
const targetTable = manifest.models[targetModel]?.table ?? targetModel;
|
|
348
|
-
|
|
469
|
+
const targetColumn = manifest.models[targetModel]?.fields[targetField]?.column ?? targetField;
|
|
470
|
+
parts.push(
|
|
471
|
+
`references ${sqlIdentifier(options.dialect, targetTable)}(${sqlIdentifier(
|
|
472
|
+
options.dialect,
|
|
473
|
+
targetColumn
|
|
474
|
+
)})`
|
|
475
|
+
);
|
|
349
476
|
}
|
|
350
477
|
return ` ${parts.join(" ")}`;
|
|
351
478
|
});
|
|
352
|
-
return `create table if not exists ${model2.table} (
|
|
479
|
+
return `create table if not exists ${sqlIdentifier(options.dialect, model2.table)} (
|
|
353
480
|
${columns.join(",\n")}
|
|
354
481
|
);`;
|
|
355
482
|
});
|
|
@@ -466,6 +593,11 @@ function pageRows(rows, skip, take) {
|
|
|
466
593
|
const end = take === void 0 ? void 0 : start + take;
|
|
467
594
|
return rows.slice(start, end);
|
|
468
595
|
}
|
|
596
|
+
function applyQuery(rows, args = {}) {
|
|
597
|
+
const filtered = rows.filter((row) => matchesWhere(row, args.where));
|
|
598
|
+
const sorted = sortRows(filtered, args.orderBy);
|
|
599
|
+
return pageRows(sorted, args.skip, args.take);
|
|
600
|
+
}
|
|
469
601
|
function createMemoryDriver(seed) {
|
|
470
602
|
const state = structuredClone(seed ?? {});
|
|
471
603
|
function getRows(model2) {
|
|
@@ -473,14 +605,13 @@ function createMemoryDriver(seed) {
|
|
|
473
605
|
state[model2] = rows;
|
|
474
606
|
return rows;
|
|
475
607
|
}
|
|
476
|
-
function
|
|
477
|
-
const
|
|
478
|
-
const
|
|
479
|
-
const
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
return pageRows(sorted, args.skip, args.take);
|
|
608
|
+
function buildRow(schema, model2, data) {
|
|
609
|
+
const modelDefinition = schema.models[model2];
|
|
610
|
+
const nextRow = {};
|
|
611
|
+
for (const [fieldName, field] of Object.entries(modelDefinition.fields)) {
|
|
612
|
+
nextRow[fieldName] = applyDefault(data[fieldName], field.config);
|
|
613
|
+
}
|
|
614
|
+
return nextRow;
|
|
484
615
|
}
|
|
485
616
|
async function projectRow(schema, model2, row, select) {
|
|
486
617
|
const modelDefinition = schema.models[model2];
|
|
@@ -513,28 +644,27 @@ function createMemoryDriver(seed) {
|
|
|
513
644
|
const relation = schema.models[model2].relations[relationName];
|
|
514
645
|
const relationArgs = value === true ? {} : value;
|
|
515
646
|
if (relation.kind === "belongsTo") {
|
|
516
|
-
const targetRows2 = getRows(relation.target);
|
|
517
647
|
const foreignValue = row[relation.foreignKey];
|
|
518
|
-
const
|
|
648
|
+
const targetRows2 = getRows(relation.target).filter(
|
|
649
|
+
(item) => item.id === foreignValue
|
|
650
|
+
);
|
|
651
|
+
const target = applyQuery(targetRows2, relationArgs)[0];
|
|
519
652
|
return target ? projectRow(schema, relation.target, target, relationArgs.select) : null;
|
|
520
653
|
}
|
|
521
654
|
if (relation.kind === "hasOne") {
|
|
522
|
-
const targetRows2 = getRows(relation.target)
|
|
523
|
-
|
|
655
|
+
const targetRows2 = getRows(relation.target).filter(
|
|
656
|
+
(item) => item[relation.foreignKey] === row.id
|
|
657
|
+
);
|
|
658
|
+
const target = applyQuery(targetRows2, relationArgs)[0];
|
|
524
659
|
return target ? projectRow(schema, relation.target, target, relationArgs.select) : null;
|
|
525
660
|
}
|
|
526
661
|
if (relation.kind === "hasMany") {
|
|
527
662
|
const targetRows2 = getRows(relation.target).filter(
|
|
528
663
|
(item) => item[relation.foreignKey] === row.id
|
|
529
664
|
);
|
|
530
|
-
const
|
|
531
|
-
targetRows2,
|
|
532
|
-
relationArgs.orderBy
|
|
533
|
-
);
|
|
534
|
-
const paged2 = pageRows(sorted2, relationArgs.skip, relationArgs.take);
|
|
535
|
-
const filtered2 = paged2.filter((item) => matchesWhere(item, relationArgs.where));
|
|
665
|
+
const matchedRows2 = applyQuery(targetRows2, relationArgs);
|
|
536
666
|
return Promise.all(
|
|
537
|
-
|
|
667
|
+
matchedRows2.map(
|
|
538
668
|
(item) => projectRow(schema, relation.target, item, relationArgs.select)
|
|
539
669
|
)
|
|
540
670
|
);
|
|
@@ -546,45 +676,72 @@ function createMemoryDriver(seed) {
|
|
|
546
676
|
const targetRows = getRows(relation.target).filter(
|
|
547
677
|
(item) => targetIds.includes(item.id)
|
|
548
678
|
);
|
|
549
|
-
const
|
|
550
|
-
targetRows,
|
|
551
|
-
relationArgs.orderBy
|
|
552
|
-
);
|
|
553
|
-
const paged = pageRows(sorted, relationArgs.skip, relationArgs.take);
|
|
554
|
-
const filtered = paged.filter((item) => matchesWhere(item, relationArgs.where));
|
|
679
|
+
const matchedRows = applyQuery(targetRows, relationArgs);
|
|
555
680
|
return Promise.all(
|
|
556
|
-
|
|
681
|
+
matchedRows.map(
|
|
557
682
|
(item) => projectRow(schema, relation.target, item, relationArgs.select)
|
|
558
683
|
)
|
|
559
684
|
);
|
|
560
685
|
}
|
|
561
686
|
const driver = {
|
|
562
687
|
async findMany(schema, model2, args) {
|
|
563
|
-
const rows = applyQuery(model2, args);
|
|
688
|
+
const rows = applyQuery(getRows(model2), args);
|
|
564
689
|
return Promise.all(rows.map((row) => projectRow(schema, model2, row, args.select)));
|
|
565
690
|
},
|
|
566
691
|
async findFirst(schema, model2, args) {
|
|
567
|
-
const row = applyQuery(model2, args)[0];
|
|
692
|
+
const row = applyQuery(getRows(model2), args)[0];
|
|
693
|
+
if (!row) return null;
|
|
694
|
+
return projectRow(schema, model2, row, args.select);
|
|
695
|
+
},
|
|
696
|
+
async findUnique(schema, model2, args) {
|
|
697
|
+
const row = applyQuery(getRows(model2), args)[0];
|
|
568
698
|
if (!row) return null;
|
|
569
699
|
return projectRow(schema, model2, row, args.select);
|
|
570
700
|
},
|
|
701
|
+
async count(_schema, model2, args) {
|
|
702
|
+
return applyQuery(getRows(model2), args).length;
|
|
703
|
+
},
|
|
571
704
|
async create(schema, model2, args) {
|
|
572
|
-
const
|
|
573
|
-
const nextRow = {};
|
|
574
|
-
for (const [fieldName, field] of Object.entries(modelDefinition.fields)) {
|
|
575
|
-
nextRow[fieldName] = applyDefault(args.data[fieldName], field.config);
|
|
576
|
-
}
|
|
705
|
+
const nextRow = buildRow(schema, model2, args.data);
|
|
577
706
|
getRows(model2).push(nextRow);
|
|
578
707
|
return projectRow(schema, model2, nextRow, args.select);
|
|
579
708
|
},
|
|
709
|
+
async createMany(schema, model2, args) {
|
|
710
|
+
const rows = args.data.map((entry) => buildRow(schema, model2, entry));
|
|
711
|
+
getRows(model2).push(...rows);
|
|
712
|
+
return Promise.all(rows.map((row) => projectRow(schema, model2, row, args.select)));
|
|
713
|
+
},
|
|
580
714
|
async update(schema, model2, args) {
|
|
581
|
-
const
|
|
582
|
-
const row = rows.find((item) => matchesWhere(item, args.where));
|
|
715
|
+
const row = getRows(model2).find((item) => matchesWhere(item, args.where));
|
|
583
716
|
if (!row) return null;
|
|
584
717
|
Object.assign(row, args.data);
|
|
585
718
|
return projectRow(schema, model2, row, args.select);
|
|
586
719
|
},
|
|
720
|
+
async updateMany(_schema, model2, args) {
|
|
721
|
+
const rows = getRows(model2).filter((item) => matchesWhere(item, args.where));
|
|
722
|
+
for (const row of rows) {
|
|
723
|
+
Object.assign(row, args.data);
|
|
724
|
+
}
|
|
725
|
+
return rows.length;
|
|
726
|
+
},
|
|
727
|
+
async upsert(schema, model2, args) {
|
|
728
|
+
const row = getRows(model2).find((item) => matchesWhere(item, args.where));
|
|
729
|
+
if (row) {
|
|
730
|
+
Object.assign(row, args.update);
|
|
731
|
+
return projectRow(schema, model2, row, args.select);
|
|
732
|
+
}
|
|
733
|
+
const created = buildRow(schema, model2, args.create);
|
|
734
|
+
getRows(model2).push(created);
|
|
735
|
+
return projectRow(schema, model2, created, args.select);
|
|
736
|
+
},
|
|
587
737
|
async delete(_schema, model2, args) {
|
|
738
|
+
const rows = getRows(model2);
|
|
739
|
+
const index = rows.findIndex((item) => matchesWhere(item, args.where));
|
|
740
|
+
if (index === -1) return 0;
|
|
741
|
+
rows.splice(index, 1);
|
|
742
|
+
return 1;
|
|
743
|
+
},
|
|
744
|
+
async deleteMany(_schema, model2, args) {
|
|
588
745
|
const rows = getRows(model2);
|
|
589
746
|
const before = rows.length;
|
|
590
747
|
state[model2] = rows.filter((item) => !matchesWhere(item, args.where));
|