@farming-labs/orm 0.0.1
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 +721 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +254 -0
- package/dist/index.d.ts +254 -0
- package/dist/index.js +677 -0
- package/dist/index.js.map +1 -0
- package/package.json +29 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,677 @@
|
|
|
1
|
+
// src/client.ts
|
|
2
|
+
function createModelClient(schema, driver, model2) {
|
|
3
|
+
return {
|
|
4
|
+
findMany(args) {
|
|
5
|
+
return driver.findMany(schema, model2, args ?? {});
|
|
6
|
+
},
|
|
7
|
+
findFirst(args) {
|
|
8
|
+
return driver.findFirst(schema, model2, args ?? {});
|
|
9
|
+
},
|
|
10
|
+
create(args) {
|
|
11
|
+
return driver.create(schema, model2, args);
|
|
12
|
+
},
|
|
13
|
+
update(args) {
|
|
14
|
+
return driver.update(schema, model2, args);
|
|
15
|
+
},
|
|
16
|
+
delete(args) {
|
|
17
|
+
return driver.delete(schema, model2, args);
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
function createOrm(options) {
|
|
22
|
+
const { schema, driver } = options;
|
|
23
|
+
const models = {};
|
|
24
|
+
for (const model2 of Object.keys(schema.models)) {
|
|
25
|
+
models[model2] = createModelClient(schema, driver, model2);
|
|
26
|
+
}
|
|
27
|
+
const orm = models;
|
|
28
|
+
orm.transaction = (run) => driver.transaction(schema, async (txDriver) => {
|
|
29
|
+
const tx = createOrm({
|
|
30
|
+
schema,
|
|
31
|
+
driver: txDriver
|
|
32
|
+
});
|
|
33
|
+
return run(tx);
|
|
34
|
+
});
|
|
35
|
+
return orm;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// src/fields.ts
|
|
39
|
+
var cloneField = (config) => new FieldBuilder(config);
|
|
40
|
+
var FieldBuilder = class {
|
|
41
|
+
constructor(config) {
|
|
42
|
+
this.config = config;
|
|
43
|
+
}
|
|
44
|
+
_tag = "field";
|
|
45
|
+
unique() {
|
|
46
|
+
return cloneField({
|
|
47
|
+
...this.config,
|
|
48
|
+
unique: true
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
nullable() {
|
|
52
|
+
return cloneField({
|
|
53
|
+
...this.config,
|
|
54
|
+
nullable: true
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
default(value) {
|
|
58
|
+
return cloneField({
|
|
59
|
+
...this.config,
|
|
60
|
+
defaultValue: value
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
defaultNow() {
|
|
64
|
+
return cloneField({
|
|
65
|
+
...this.config,
|
|
66
|
+
generated: "now"
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
references(reference) {
|
|
70
|
+
return cloneField({
|
|
71
|
+
...this.config,
|
|
72
|
+
references: reference
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
map(name) {
|
|
76
|
+
return cloneField({
|
|
77
|
+
...this.config,
|
|
78
|
+
mappedName: name
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
describe(description) {
|
|
82
|
+
return cloneField({
|
|
83
|
+
...this.config,
|
|
84
|
+
description
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
function id() {
|
|
89
|
+
return new FieldBuilder({
|
|
90
|
+
kind: "id",
|
|
91
|
+
nullable: false,
|
|
92
|
+
unique: true,
|
|
93
|
+
generated: "id"
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
function string() {
|
|
97
|
+
return new FieldBuilder({
|
|
98
|
+
kind: "string",
|
|
99
|
+
nullable: false,
|
|
100
|
+
unique: false
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
function boolean() {
|
|
104
|
+
return new FieldBuilder({
|
|
105
|
+
kind: "boolean",
|
|
106
|
+
nullable: false,
|
|
107
|
+
unique: false
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
function datetime() {
|
|
111
|
+
return new FieldBuilder({
|
|
112
|
+
kind: "datetime",
|
|
113
|
+
nullable: false,
|
|
114
|
+
unique: false
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// src/manifest.ts
|
|
119
|
+
function createManifest(schema) {
|
|
120
|
+
const models = Object.fromEntries(
|
|
121
|
+
Object.entries(schema.models).map(
|
|
122
|
+
([name, definition]) => {
|
|
123
|
+
const fields = Object.fromEntries(
|
|
124
|
+
Object.entries(definition.fields).map(([fieldName, field]) => [
|
|
125
|
+
fieldName,
|
|
126
|
+
{
|
|
127
|
+
name: fieldName,
|
|
128
|
+
column: field.config.mappedName ?? fieldName,
|
|
129
|
+
kind: field.config.kind,
|
|
130
|
+
nullable: field.config.nullable,
|
|
131
|
+
unique: field.config.unique,
|
|
132
|
+
generated: field.config.generated,
|
|
133
|
+
defaultValue: field.config.defaultValue,
|
|
134
|
+
references: field.config.references,
|
|
135
|
+
description: field.config.description
|
|
136
|
+
}
|
|
137
|
+
])
|
|
138
|
+
);
|
|
139
|
+
return [
|
|
140
|
+
name,
|
|
141
|
+
{
|
|
142
|
+
name,
|
|
143
|
+
table: definition.table,
|
|
144
|
+
description: definition.description,
|
|
145
|
+
fields,
|
|
146
|
+
relations: definition.relations
|
|
147
|
+
}
|
|
148
|
+
];
|
|
149
|
+
}
|
|
150
|
+
)
|
|
151
|
+
);
|
|
152
|
+
return { models };
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// src/generators.ts
|
|
156
|
+
var capitalize = (value) => value.charAt(0).toUpperCase() + value.slice(1);
|
|
157
|
+
var pluralize = (value) => value.endsWith("s") ? value : `${value}s`;
|
|
158
|
+
function prismaType(field) {
|
|
159
|
+
switch (field.kind) {
|
|
160
|
+
case "id":
|
|
161
|
+
case "string":
|
|
162
|
+
return "String";
|
|
163
|
+
case "boolean":
|
|
164
|
+
return "Boolean";
|
|
165
|
+
case "datetime":
|
|
166
|
+
return "DateTime";
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
function drizzleImports(dialect, manifest) {
|
|
170
|
+
const models = Object.values(manifest.models);
|
|
171
|
+
const needsBoolean = models.some(
|
|
172
|
+
(model2) => Object.values(model2.fields).some((field) => field.kind === "boolean")
|
|
173
|
+
);
|
|
174
|
+
const needsDate = models.some(
|
|
175
|
+
(model2) => Object.values(model2.fields).some((field) => field.kind === "datetime")
|
|
176
|
+
);
|
|
177
|
+
if (dialect === "pg") {
|
|
178
|
+
return [
|
|
179
|
+
"pgTable",
|
|
180
|
+
"text",
|
|
181
|
+
needsBoolean ? "boolean" : null,
|
|
182
|
+
needsDate ? "timestamp" : null
|
|
183
|
+
].filter(Boolean);
|
|
184
|
+
}
|
|
185
|
+
if (dialect === "mysql") {
|
|
186
|
+
return [
|
|
187
|
+
"mysqlTable",
|
|
188
|
+
"varchar",
|
|
189
|
+
"text",
|
|
190
|
+
needsBoolean ? "boolean" : null,
|
|
191
|
+
needsDate ? "datetime" : null
|
|
192
|
+
].filter(Boolean);
|
|
193
|
+
}
|
|
194
|
+
return ["sqliteTable", "text", "integer"];
|
|
195
|
+
}
|
|
196
|
+
function drizzleColumn(field, dialect) {
|
|
197
|
+
if (field.kind === "id") {
|
|
198
|
+
if (dialect === "mysql") {
|
|
199
|
+
return `varchar("${field.column}", { length: 191 }).primaryKey()`;
|
|
200
|
+
}
|
|
201
|
+
return `text("${field.column}").primaryKey()`;
|
|
202
|
+
}
|
|
203
|
+
if (field.kind === "string") {
|
|
204
|
+
if (dialect === "mysql") {
|
|
205
|
+
const base = field.unique || field.references ? `varchar("${field.column}", { length: 191 })` : `text("${field.column}")`;
|
|
206
|
+
return `${base}${field.nullable ? "" : ".notNull()"}${field.unique ? ".unique()" : ""}${field.defaultValue !== void 0 ? `.default(${JSON.stringify(field.defaultValue)})` : ""}`;
|
|
207
|
+
}
|
|
208
|
+
return `text("${field.column}")${field.nullable ? "" : ".notNull()"}${field.unique ? ".unique()" : ""}${field.defaultValue !== void 0 ? `.default(${JSON.stringify(field.defaultValue)})` : ""}`;
|
|
209
|
+
}
|
|
210
|
+
if (field.kind === "boolean") {
|
|
211
|
+
if (dialect === "sqlite") {
|
|
212
|
+
return `integer("${field.column}", { mode: "boolean" })${field.nullable ? "" : ".notNull()"}${field.defaultValue !== void 0 ? `.default(${String(field.defaultValue)})` : ""}`;
|
|
213
|
+
}
|
|
214
|
+
return `boolean("${field.column}")${field.nullable ? "" : ".notNull()"}${field.defaultValue !== void 0 ? `.default(${String(field.defaultValue)})` : ""}`;
|
|
215
|
+
}
|
|
216
|
+
if (dialect === "mysql") {
|
|
217
|
+
return `datetime("${field.column}", { mode: "date" })${field.nullable ? "" : ".notNull()"}`;
|
|
218
|
+
}
|
|
219
|
+
if (dialect === "sqlite") {
|
|
220
|
+
return `integer("${field.column}", { mode: "timestamp" })${field.nullable ? "" : ".notNull()"}`;
|
|
221
|
+
}
|
|
222
|
+
return `timestamp("${field.column}")${field.nullable ? "" : ".notNull()"}`;
|
|
223
|
+
}
|
|
224
|
+
function sqlType(field, dialect) {
|
|
225
|
+
if (field.kind === "id") {
|
|
226
|
+
return dialect === "mysql" ? "varchar(191)" : "text";
|
|
227
|
+
}
|
|
228
|
+
if (field.kind === "string") {
|
|
229
|
+
return dialect === "mysql" && field.unique ? "varchar(191)" : "text";
|
|
230
|
+
}
|
|
231
|
+
if (field.kind === "boolean") {
|
|
232
|
+
return dialect === "sqlite" ? "integer" : "boolean";
|
|
233
|
+
}
|
|
234
|
+
if (dialect === "mysql") {
|
|
235
|
+
return "datetime";
|
|
236
|
+
}
|
|
237
|
+
if (dialect === "sqlite") {
|
|
238
|
+
return "text";
|
|
239
|
+
}
|
|
240
|
+
return "timestamp";
|
|
241
|
+
}
|
|
242
|
+
function renderPrismaSchema(schema, options = {}) {
|
|
243
|
+
const manifest = createManifest(schema);
|
|
244
|
+
const provider = options.provider ?? "postgresql";
|
|
245
|
+
const generatorName = options.generatorName ?? "client";
|
|
246
|
+
const datasourceName = options.datasourceName ?? "db";
|
|
247
|
+
const reverseRelations = /* @__PURE__ */ new Map();
|
|
248
|
+
for (const model2 of Object.values(manifest.models)) {
|
|
249
|
+
for (const field of Object.values(model2.fields)) {
|
|
250
|
+
if (!field.references) continue;
|
|
251
|
+
const [targetModel] = field.references.split(".");
|
|
252
|
+
reverseRelations.set(targetModel, [
|
|
253
|
+
...reverseRelations.get(targetModel) ?? [],
|
|
254
|
+
{
|
|
255
|
+
sourceModel: model2.name,
|
|
256
|
+
many: !field.unique
|
|
257
|
+
}
|
|
258
|
+
]);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
const blocks = Object.values(manifest.models).map((model2) => {
|
|
262
|
+
const lines = [];
|
|
263
|
+
const modelName = capitalize(model2.name);
|
|
264
|
+
for (const field of Object.values(model2.fields)) {
|
|
265
|
+
const fieldType = prismaType(field);
|
|
266
|
+
const modifiers = [];
|
|
267
|
+
if (field.kind === "id") modifiers.push("@id");
|
|
268
|
+
if (field.generated === "id") modifiers.push("@default(cuid())");
|
|
269
|
+
if (field.generated === "now") modifiers.push("@default(now())");
|
|
270
|
+
if (field.defaultValue !== void 0 && field.generated === void 0) {
|
|
271
|
+
modifiers.push(
|
|
272
|
+
typeof field.defaultValue === "string" ? `@default("${field.defaultValue}")` : `@default(${String(field.defaultValue)})`
|
|
273
|
+
);
|
|
274
|
+
}
|
|
275
|
+
if (field.unique && field.kind !== "id") modifiers.push("@unique");
|
|
276
|
+
if (field.column !== field.name) modifiers.push(`@map("${field.column}")`);
|
|
277
|
+
lines.push(
|
|
278
|
+
` ${field.name} ${fieldType}${field.nullable ? "?" : ""}${modifiers.length ? ` ${modifiers.join(" ")}` : ""}`
|
|
279
|
+
);
|
|
280
|
+
if (field.references) {
|
|
281
|
+
const [targetModel, targetField] = field.references.split(".");
|
|
282
|
+
lines.push(
|
|
283
|
+
` ${targetModel} ${capitalize(targetModel)} @relation(fields: [${field.name}], references: [${targetField}])`
|
|
284
|
+
);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
for (const relation of reverseRelations.get(model2.name) ?? []) {
|
|
288
|
+
lines.push(
|
|
289
|
+
relation.many ? ` ${pluralize(relation.sourceModel)} ${capitalize(relation.sourceModel)}[]` : ` ${relation.sourceModel} ${capitalize(relation.sourceModel)}?`
|
|
290
|
+
);
|
|
291
|
+
}
|
|
292
|
+
const mapLine = model2.table !== modelName ? `
|
|
293
|
+
@@map("${model2.table}")` : "";
|
|
294
|
+
return `model ${modelName} {
|
|
295
|
+
${lines.join("\n")}${mapLine}
|
|
296
|
+
}`;
|
|
297
|
+
});
|
|
298
|
+
return `generator ${generatorName} {
|
|
299
|
+
provider = "prisma-client-js"
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
datasource ${datasourceName} {
|
|
303
|
+
provider = "${provider}"
|
|
304
|
+
url = ${provider === "sqlite" ? '"file:./dev.db"' : 'env("DATABASE_URL")'}
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
${blocks.join("\n\n")}
|
|
308
|
+
`;
|
|
309
|
+
}
|
|
310
|
+
function renderDrizzleSchema(schema, options) {
|
|
311
|
+
const manifest = createManifest(schema);
|
|
312
|
+
const imports = drizzleImports(options.dialect, manifest).join(", ");
|
|
313
|
+
const tableFactory = options.dialect === "pg" ? "pgTable" : options.dialect === "mysql" ? "mysqlTable" : "sqliteTable";
|
|
314
|
+
const modelBlocks = Object.values(manifest.models).map((model2) => {
|
|
315
|
+
const lines = Object.values(model2.fields).map((field) => {
|
|
316
|
+
let value = drizzleColumn(field, options.dialect);
|
|
317
|
+
if (field.references) {
|
|
318
|
+
const [targetModel, targetField] = field.references.split(".");
|
|
319
|
+
value += `.references(() => ${targetModel}.${targetField})`;
|
|
320
|
+
}
|
|
321
|
+
return ` ${field.name}: ${value}`;
|
|
322
|
+
});
|
|
323
|
+
return `export const ${model2.name} = ${tableFactory}("${model2.table}", {
|
|
324
|
+
${lines.join(",\n")}
|
|
325
|
+
});`;
|
|
326
|
+
});
|
|
327
|
+
return `import { ${imports} } from "drizzle-orm/${options.dialect === "pg" ? "pg-core" : options.dialect === "mysql" ? "mysql-core" : "sqlite-core"}";
|
|
328
|
+
|
|
329
|
+
${modelBlocks.join("\n\n")}
|
|
330
|
+
`;
|
|
331
|
+
}
|
|
332
|
+
function renderSafeSql(schema, options) {
|
|
333
|
+
const manifest = createManifest(schema);
|
|
334
|
+
const statements = Object.values(manifest.models).map((model2) => {
|
|
335
|
+
const columns = Object.values(model2.fields).map((field) => {
|
|
336
|
+
const parts = [`${field.column} ${sqlType(field, options.dialect)}`];
|
|
337
|
+
if (field.kind === "id") parts.push("primary key");
|
|
338
|
+
if (!field.nullable) parts.push("not null");
|
|
339
|
+
if (field.unique && field.kind !== "id") parts.push("unique");
|
|
340
|
+
if (field.defaultValue !== void 0) {
|
|
341
|
+
parts.push(
|
|
342
|
+
`default ${typeof field.defaultValue === "string" ? `'${field.defaultValue}'` : String(field.defaultValue)}`
|
|
343
|
+
);
|
|
344
|
+
}
|
|
345
|
+
if (field.references) {
|
|
346
|
+
const [targetModel, targetField] = field.references.split(".");
|
|
347
|
+
const targetTable = manifest.models[targetModel]?.table ?? targetModel;
|
|
348
|
+
parts.push(`references ${targetTable}(${targetField})`);
|
|
349
|
+
}
|
|
350
|
+
return ` ${parts.join(" ")}`;
|
|
351
|
+
});
|
|
352
|
+
return `create table if not exists ${model2.table} (
|
|
353
|
+
${columns.join(",\n")}
|
|
354
|
+
);`;
|
|
355
|
+
});
|
|
356
|
+
return `${statements.join("\n\n")}
|
|
357
|
+
`;
|
|
358
|
+
}
|
|
359
|
+
function replaceGeneratedBlock(input) {
|
|
360
|
+
const start = `// @farming-labs/orm:start:${input.label}`;
|
|
361
|
+
const end = `// @farming-labs/orm:end:${input.label}`;
|
|
362
|
+
const block = `${start}
|
|
363
|
+
${input.content.trim()}
|
|
364
|
+
${end}`;
|
|
365
|
+
if (input.current.includes(start) && input.current.includes(end)) {
|
|
366
|
+
const pattern = new RegExp(`${start}[\\s\\S]*?${end}`, "m");
|
|
367
|
+
return input.current.replace(pattern, block);
|
|
368
|
+
}
|
|
369
|
+
return `${input.current.trim()}
|
|
370
|
+
|
|
371
|
+
${block}
|
|
372
|
+
`;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
// src/memory.ts
|
|
376
|
+
import { randomUUID } from "crypto";
|
|
377
|
+
var isDate = (value) => value instanceof Date;
|
|
378
|
+
function evaluateFilter(value, filter) {
|
|
379
|
+
if (filter === void 0 || filter === null || typeof filter !== "object" || isDate(filter) || Array.isArray(filter)) {
|
|
380
|
+
return value === filter;
|
|
381
|
+
}
|
|
382
|
+
const record = filter;
|
|
383
|
+
if ("eq" in record && value !== record.eq) return false;
|
|
384
|
+
if ("not" in record && value === record.not) return false;
|
|
385
|
+
if ("in" in record) {
|
|
386
|
+
const values = Array.isArray(record.in) ? record.in : [];
|
|
387
|
+
if (!values.includes(value)) return false;
|
|
388
|
+
}
|
|
389
|
+
if ("contains" in record) {
|
|
390
|
+
if (typeof value !== "string" || typeof record.contains !== "string") return false;
|
|
391
|
+
if (!value.includes(record.contains)) return false;
|
|
392
|
+
}
|
|
393
|
+
if ("gt" in record && value !== void 0) {
|
|
394
|
+
if (!(value instanceof Date || typeof value === "number" || typeof value === "string")) {
|
|
395
|
+
return false;
|
|
396
|
+
}
|
|
397
|
+
if (!(value > record.gt)) return false;
|
|
398
|
+
}
|
|
399
|
+
if ("gte" in record && value !== void 0) {
|
|
400
|
+
if (!(value instanceof Date || typeof value === "number" || typeof value === "string")) {
|
|
401
|
+
return false;
|
|
402
|
+
}
|
|
403
|
+
if (!(value >= record.gte)) return false;
|
|
404
|
+
}
|
|
405
|
+
if ("lt" in record && value !== void 0) {
|
|
406
|
+
if (!(value instanceof Date || typeof value === "number" || typeof value === "string")) {
|
|
407
|
+
return false;
|
|
408
|
+
}
|
|
409
|
+
if (!(value < record.lt)) return false;
|
|
410
|
+
}
|
|
411
|
+
if ("lte" in record && value !== void 0) {
|
|
412
|
+
if (!(value instanceof Date || typeof value === "number" || typeof value === "string")) {
|
|
413
|
+
return false;
|
|
414
|
+
}
|
|
415
|
+
if (!(value <= record.lte)) return false;
|
|
416
|
+
}
|
|
417
|
+
return true;
|
|
418
|
+
}
|
|
419
|
+
function matchesWhere(record, where) {
|
|
420
|
+
if (!where) return true;
|
|
421
|
+
if (where.AND && !where.AND.every((clause) => matchesWhere(record, clause))) {
|
|
422
|
+
return false;
|
|
423
|
+
}
|
|
424
|
+
if (where.OR && !where.OR.some((clause) => matchesWhere(record, clause))) {
|
|
425
|
+
return false;
|
|
426
|
+
}
|
|
427
|
+
if (where.NOT && matchesWhere(record, where.NOT)) {
|
|
428
|
+
return false;
|
|
429
|
+
}
|
|
430
|
+
for (const [key, filter] of Object.entries(where)) {
|
|
431
|
+
if (key === "AND" || key === "OR" || key === "NOT") continue;
|
|
432
|
+
if (!evaluateFilter(record[key], filter)) return false;
|
|
433
|
+
}
|
|
434
|
+
return true;
|
|
435
|
+
}
|
|
436
|
+
function applyDefault(value, field) {
|
|
437
|
+
if (value !== void 0) return value;
|
|
438
|
+
if (field.generated === "id") return randomUUID();
|
|
439
|
+
if (field.generated === "now") return /* @__PURE__ */ new Date();
|
|
440
|
+
if (typeof field.defaultValue === "function") {
|
|
441
|
+
return field.defaultValue();
|
|
442
|
+
}
|
|
443
|
+
return field.defaultValue;
|
|
444
|
+
}
|
|
445
|
+
function sortRows(rows, orderBy) {
|
|
446
|
+
if (!orderBy) return rows;
|
|
447
|
+
const entries = Object.entries(orderBy);
|
|
448
|
+
if (!entries.length) return rows;
|
|
449
|
+
return [...rows].sort((left, right) => {
|
|
450
|
+
for (const [field, direction] of entries) {
|
|
451
|
+
const a = left[field];
|
|
452
|
+
const b = right[field];
|
|
453
|
+
if (a === b) continue;
|
|
454
|
+
if (a === void 0) return direction === "asc" ? -1 : 1;
|
|
455
|
+
if (b === void 0) return direction === "asc" ? 1 : -1;
|
|
456
|
+
if (a == null) return direction === "asc" ? -1 : 1;
|
|
457
|
+
if (b == null) return direction === "asc" ? 1 : -1;
|
|
458
|
+
if (a < b) return direction === "asc" ? -1 : 1;
|
|
459
|
+
if (a > b) return direction === "asc" ? 1 : -1;
|
|
460
|
+
}
|
|
461
|
+
return 0;
|
|
462
|
+
});
|
|
463
|
+
}
|
|
464
|
+
function pageRows(rows, skip, take) {
|
|
465
|
+
const start = skip ?? 0;
|
|
466
|
+
const end = take === void 0 ? void 0 : start + take;
|
|
467
|
+
return rows.slice(start, end);
|
|
468
|
+
}
|
|
469
|
+
function createMemoryDriver(seed) {
|
|
470
|
+
const state = structuredClone(seed ?? {});
|
|
471
|
+
function getRows(model2) {
|
|
472
|
+
const rows = state[model2] ?? [];
|
|
473
|
+
state[model2] = rows;
|
|
474
|
+
return rows;
|
|
475
|
+
}
|
|
476
|
+
function applyQuery(model2, args) {
|
|
477
|
+
const rows = getRows(model2);
|
|
478
|
+
const filtered = rows.filter((row) => matchesWhere(row, args.where));
|
|
479
|
+
const sorted = sortRows(
|
|
480
|
+
filtered,
|
|
481
|
+
args.orderBy
|
|
482
|
+
);
|
|
483
|
+
return pageRows(sorted, args.skip, args.take);
|
|
484
|
+
}
|
|
485
|
+
async function projectRow(schema, model2, row, select) {
|
|
486
|
+
const modelDefinition = schema.models[model2];
|
|
487
|
+
const output = {};
|
|
488
|
+
if (!select) {
|
|
489
|
+
for (const fieldName of Object.keys(modelDefinition.fields)) {
|
|
490
|
+
output[fieldName] = row[fieldName];
|
|
491
|
+
}
|
|
492
|
+
return output;
|
|
493
|
+
}
|
|
494
|
+
for (const [key, value] of Object.entries(select)) {
|
|
495
|
+
if (value !== true && value === void 0) continue;
|
|
496
|
+
if (key in modelDefinition.fields && value === true) {
|
|
497
|
+
output[key] = row[key];
|
|
498
|
+
continue;
|
|
499
|
+
}
|
|
500
|
+
if (key in modelDefinition.relations) {
|
|
501
|
+
output[key] = await resolveRelation(
|
|
502
|
+
schema,
|
|
503
|
+
model2,
|
|
504
|
+
key,
|
|
505
|
+
row,
|
|
506
|
+
value
|
|
507
|
+
);
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
return output;
|
|
511
|
+
}
|
|
512
|
+
async function resolveRelation(schema, model2, relationName, row, value) {
|
|
513
|
+
const relation = schema.models[model2].relations[relationName];
|
|
514
|
+
const relationArgs = value === true ? {} : value;
|
|
515
|
+
if (relation.kind === "belongsTo") {
|
|
516
|
+
const targetRows2 = getRows(relation.target);
|
|
517
|
+
const foreignValue = row[relation.foreignKey];
|
|
518
|
+
const target = targetRows2.find((item) => item.id === foreignValue);
|
|
519
|
+
return target ? projectRow(schema, relation.target, target, relationArgs.select) : null;
|
|
520
|
+
}
|
|
521
|
+
if (relation.kind === "hasOne") {
|
|
522
|
+
const targetRows2 = getRows(relation.target);
|
|
523
|
+
const target = targetRows2.find((item) => item[relation.foreignKey] === row.id);
|
|
524
|
+
return target ? projectRow(schema, relation.target, target, relationArgs.select) : null;
|
|
525
|
+
}
|
|
526
|
+
if (relation.kind === "hasMany") {
|
|
527
|
+
const targetRows2 = getRows(relation.target).filter(
|
|
528
|
+
(item) => item[relation.foreignKey] === row.id
|
|
529
|
+
);
|
|
530
|
+
const sorted2 = sortRows(
|
|
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));
|
|
536
|
+
return Promise.all(
|
|
537
|
+
filtered2.map(
|
|
538
|
+
(item) => projectRow(schema, relation.target, item, relationArgs.select)
|
|
539
|
+
)
|
|
540
|
+
);
|
|
541
|
+
}
|
|
542
|
+
const throughRows = getRows(relation.through).filter(
|
|
543
|
+
(item) => item[relation.from] === row.id
|
|
544
|
+
);
|
|
545
|
+
const targetIds = throughRows.map((item) => item[relation.to]);
|
|
546
|
+
const targetRows = getRows(relation.target).filter(
|
|
547
|
+
(item) => targetIds.includes(item.id)
|
|
548
|
+
);
|
|
549
|
+
const sorted = sortRows(
|
|
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));
|
|
555
|
+
return Promise.all(
|
|
556
|
+
filtered.map(
|
|
557
|
+
(item) => projectRow(schema, relation.target, item, relationArgs.select)
|
|
558
|
+
)
|
|
559
|
+
);
|
|
560
|
+
}
|
|
561
|
+
const driver = {
|
|
562
|
+
async findMany(schema, model2, args) {
|
|
563
|
+
const rows = applyQuery(model2, args);
|
|
564
|
+
return Promise.all(rows.map((row) => projectRow(schema, model2, row, args.select)));
|
|
565
|
+
},
|
|
566
|
+
async findFirst(schema, model2, args) {
|
|
567
|
+
const row = applyQuery(model2, args)[0];
|
|
568
|
+
if (!row) return null;
|
|
569
|
+
return projectRow(schema, model2, row, args.select);
|
|
570
|
+
},
|
|
571
|
+
async create(schema, model2, args) {
|
|
572
|
+
const modelDefinition = schema.models[model2];
|
|
573
|
+
const nextRow = {};
|
|
574
|
+
for (const [fieldName, field] of Object.entries(modelDefinition.fields)) {
|
|
575
|
+
nextRow[fieldName] = applyDefault(args.data[fieldName], field.config);
|
|
576
|
+
}
|
|
577
|
+
getRows(model2).push(nextRow);
|
|
578
|
+
return projectRow(schema, model2, nextRow, args.select);
|
|
579
|
+
},
|
|
580
|
+
async update(schema, model2, args) {
|
|
581
|
+
const rows = getRows(model2);
|
|
582
|
+
const row = rows.find((item) => matchesWhere(item, args.where));
|
|
583
|
+
if (!row) return null;
|
|
584
|
+
Object.assign(row, args.data);
|
|
585
|
+
return projectRow(schema, model2, row, args.select);
|
|
586
|
+
},
|
|
587
|
+
async delete(_schema, model2, args) {
|
|
588
|
+
const rows = getRows(model2);
|
|
589
|
+
const before = rows.length;
|
|
590
|
+
state[model2] = rows.filter((item) => !matchesWhere(item, args.where));
|
|
591
|
+
return before - (state[model2]?.length ?? 0);
|
|
592
|
+
},
|
|
593
|
+
async transaction(_schema, run) {
|
|
594
|
+
const snapshot = structuredClone(state);
|
|
595
|
+
try {
|
|
596
|
+
return await run(driver);
|
|
597
|
+
} catch (error) {
|
|
598
|
+
Object.keys(state).forEach((key) => {
|
|
599
|
+
delete state[key];
|
|
600
|
+
});
|
|
601
|
+
Object.assign(state, snapshot);
|
|
602
|
+
throw error;
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
};
|
|
606
|
+
return driver;
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
// src/relations.ts
|
|
610
|
+
function belongsTo(target, config) {
|
|
611
|
+
return {
|
|
612
|
+
kind: "belongsTo",
|
|
613
|
+
target,
|
|
614
|
+
foreignKey: config.foreignKey
|
|
615
|
+
};
|
|
616
|
+
}
|
|
617
|
+
function hasOne(target, config) {
|
|
618
|
+
return {
|
|
619
|
+
kind: "hasOne",
|
|
620
|
+
target,
|
|
621
|
+
foreignKey: config.foreignKey
|
|
622
|
+
};
|
|
623
|
+
}
|
|
624
|
+
function hasMany(target, config) {
|
|
625
|
+
return {
|
|
626
|
+
kind: "hasMany",
|
|
627
|
+
target,
|
|
628
|
+
foreignKey: config.foreignKey
|
|
629
|
+
};
|
|
630
|
+
}
|
|
631
|
+
function manyToMany(target, config) {
|
|
632
|
+
return {
|
|
633
|
+
kind: "manyToMany",
|
|
634
|
+
target,
|
|
635
|
+
through: config.through,
|
|
636
|
+
from: config.from,
|
|
637
|
+
to: config.to
|
|
638
|
+
};
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
// src/schema.ts
|
|
642
|
+
function model(config) {
|
|
643
|
+
return {
|
|
644
|
+
_tag: "model",
|
|
645
|
+
table: config.table,
|
|
646
|
+
fields: config.fields,
|
|
647
|
+
relations: config.relations ?? {},
|
|
648
|
+
description: config.description
|
|
649
|
+
};
|
|
650
|
+
}
|
|
651
|
+
function defineSchema(models) {
|
|
652
|
+
return {
|
|
653
|
+
_tag: "schema",
|
|
654
|
+
models
|
|
655
|
+
};
|
|
656
|
+
}
|
|
657
|
+
export {
|
|
658
|
+
FieldBuilder,
|
|
659
|
+
belongsTo,
|
|
660
|
+
boolean,
|
|
661
|
+
createManifest,
|
|
662
|
+
createMemoryDriver,
|
|
663
|
+
createOrm,
|
|
664
|
+
datetime,
|
|
665
|
+
defineSchema,
|
|
666
|
+
hasMany,
|
|
667
|
+
hasOne,
|
|
668
|
+
id,
|
|
669
|
+
manyToMany,
|
|
670
|
+
model,
|
|
671
|
+
renderDrizzleSchema,
|
|
672
|
+
renderPrismaSchema,
|
|
673
|
+
renderSafeSql,
|
|
674
|
+
replaceGeneratedBlock,
|
|
675
|
+
string
|
|
676
|
+
};
|
|
677
|
+
//# sourceMappingURL=index.js.map
|