peta-orm 0.5.0 → 0.6.0
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 +12 -18
- package/dist/{collection-PFmrQHyM.mjs → collection-D9YZn2mL.mjs} +4 -9
- package/dist/{crud-BCWvg5MI.mjs → crud-Di2nvpjB.mjs} +6 -23
- package/dist/{errors-sfFJolfu.mjs → errors-i-gCZnlW.mjs} +14 -1
- package/dist/{factory-BBvIMQuc.mjs → factory-_JPR5fVl.mjs} +5 -4
- package/dist/helpers-CcxHFhtz.mjs +40 -0
- package/dist/{hooks-BD0xy7uw.mjs → hooks-D508wLQg.mjs} +2 -16
- package/dist/index.d.mts +104 -86
- package/dist/index.mjs +646 -402
- package/dist/model-helpers-BBpD3qdv.mjs +14 -0
- package/package.json +1 -6
- package/dist/save-D5UKXvqC.mjs +0 -331
package/dist/index.mjs
CHANGED
|
@@ -1,22 +1,28 @@
|
|
|
1
1
|
import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.mjs";
|
|
2
|
-
import { n as createCollection } from "./collection-
|
|
3
|
-
import { a as
|
|
4
|
-
import { a as registerSoftDeletesFor, n as getSoftDeleteConfig, o as
|
|
5
|
-
import {
|
|
6
|
-
import { a as
|
|
7
|
-
import { a as
|
|
2
|
+
import { n as createCollection } from "./collection-D9YZn2mL.mjs";
|
|
3
|
+
import { a as ModelNotFoundError, c as RelationNotFoundError, i as DatabaseError, l as ValidationError, n as isUniqueConstraintError, o as ModelNotRegisteredError, r as normalizeError, s as RelationNotAllowedError } from "./errors-i-gCZnlW.mjs";
|
|
4
|
+
import { a as registerTimestampsFor, i as registerSoftDeletesFor, n as getSoftDeleteConfig, o as createHookManager, r as hasSoftDelete, t as getHooksFor } from "./hooks-D508wLQg.mjs";
|
|
5
|
+
import { n as getPrimaryKeyColumn, t as getDb } from "./model-helpers-BBpD3qdv.mjs";
|
|
6
|
+
import { a as castValue, i as applyCastsToData, o as prepareForDb, r as initRuntime, t as createInstance } from "./factory-_JPR5fVl.mjs";
|
|
7
|
+
import { a as getRawRelations, d as setExists, f as syncOriginal, i as getExists, o as getState } from "./state-LtlHp6XV.mjs";
|
|
8
|
+
import { a as resolveThunk, i as resolveTargetId, n as getPivotInfo, r as groupByArray, t as findRelated } from "./helpers-CcxHFhtz.mjs";
|
|
8
9
|
import { type } from "arktype";
|
|
9
10
|
import { Kysely, sql } from "kysely";
|
|
10
|
-
import { pathToFileURL } from "url";
|
|
11
|
+
import { pathToFileURL } from "node:url";
|
|
11
12
|
import { ulid as ulid$1 } from "ulid";
|
|
12
13
|
//#region src/columns/arktype.ts
|
|
13
14
|
function createArkTypeSchemaConfig() {
|
|
14
15
|
function compile(dataType, args, constraints) {
|
|
15
16
|
return type(buildDef(dataType, args, constraints));
|
|
16
17
|
}
|
|
18
|
+
function formatProblems(result) {
|
|
19
|
+
const raw = result.flatProblemsByPath;
|
|
20
|
+
if (!raw) return "Validation failed";
|
|
21
|
+
return Object.entries(raw).map(([path, msgs]) => `${path}: ${msgs.join(", ")}`).join("; ");
|
|
22
|
+
}
|
|
17
23
|
function parse(schema, value) {
|
|
18
24
|
const result = schema(value);
|
|
19
|
-
if (result instanceof type.errors) throw new ValidationError(
|
|
25
|
+
if (result instanceof type.errors) throw new ValidationError(formatProblems(result));
|
|
20
26
|
return result;
|
|
21
27
|
}
|
|
22
28
|
function assert(schema, value) {
|
|
@@ -24,7 +30,7 @@ function createArkTypeSchemaConfig() {
|
|
|
24
30
|
try {
|
|
25
31
|
return t.assert(value);
|
|
26
32
|
} catch (e) {
|
|
27
|
-
if (isArkError(e)) throw new ValidationError(
|
|
33
|
+
if (isArkError(e)) throw new ValidationError(formatProblems(e.arkErrors));
|
|
28
34
|
throw e;
|
|
29
35
|
}
|
|
30
36
|
}
|
|
@@ -98,12 +104,6 @@ function subTypeModifier(dataType, hasEmail, hasUrl) {
|
|
|
98
104
|
function isArkError(e) {
|
|
99
105
|
return typeof e === "object" && e !== null && "arkErrors" in e;
|
|
100
106
|
}
|
|
101
|
-
function extractProblems(result) {
|
|
102
|
-
const problems = /* @__PURE__ */ new Map();
|
|
103
|
-
const raw = result.flatProblemsByPath;
|
|
104
|
-
if (raw) for (const [path, msgs] of Object.entries(raw)) problems.set(path, msgs);
|
|
105
|
-
return problems;
|
|
106
|
-
}
|
|
107
107
|
//#endregion
|
|
108
108
|
//#region src/columns/column.ts
|
|
109
109
|
function createColumn(schema, dataType, args = [], constraints = []) {
|
|
@@ -140,8 +140,7 @@ function createColumn(schema, dataType, args = [], constraints = []) {
|
|
|
140
140
|
get defaultValue() {
|
|
141
141
|
const c = constraints.find((c) => c.type === "default");
|
|
142
142
|
if (!c) return void 0;
|
|
143
|
-
|
|
144
|
-
return typeof val === "function" ? val : val;
|
|
143
|
+
return c.args[0];
|
|
145
144
|
},
|
|
146
145
|
hasConstraint(type) {
|
|
147
146
|
return constraints.some((c) => c.type === type);
|
|
@@ -275,6 +274,25 @@ function createDb(factory) {
|
|
|
275
274
|
};
|
|
276
275
|
}
|
|
277
276
|
//#endregion
|
|
277
|
+
//#region src/integrations/elysia.ts
|
|
278
|
+
/**
|
|
279
|
+
* Elysia.js plugin that attaches the ORM instance to the app context.
|
|
280
|
+
*/
|
|
281
|
+
function petaPlugin(options) {
|
|
282
|
+
return (app) => app.decorate("peta", options.peta);
|
|
283
|
+
}
|
|
284
|
+
//#endregion
|
|
285
|
+
//#region src/integrations/hono.ts
|
|
286
|
+
/**
|
|
287
|
+
* Hono middleware that sets the ORM instance on the context.
|
|
288
|
+
*/
|
|
289
|
+
function petaMiddleware(options) {
|
|
290
|
+
return async (c, next) => {
|
|
291
|
+
c.set("peta", options.peta);
|
|
292
|
+
await next();
|
|
293
|
+
};
|
|
294
|
+
}
|
|
295
|
+
//#endregion
|
|
278
296
|
//#region src/hooks/static.ts
|
|
279
297
|
var static_exports = /* @__PURE__ */ __exportAll({
|
|
280
298
|
addStaticHook: () => addStaticHook,
|
|
@@ -306,11 +324,353 @@ function hasStaticHooks(def, event) {
|
|
|
306
324
|
return (staticHooks.get(def)?.get(event)?.length ?? 0) > 0;
|
|
307
325
|
}
|
|
308
326
|
//#endregion
|
|
327
|
+
//#region src/model/save.ts
|
|
328
|
+
var save_exports = /* @__PURE__ */ __exportAll({
|
|
329
|
+
getConfig: () => getConfig,
|
|
330
|
+
insertManyModel: () => insertManyModel,
|
|
331
|
+
insertModel: () => insertModel,
|
|
332
|
+
reloadModel: () => reloadModel,
|
|
333
|
+
saveModel: () => saveModel,
|
|
334
|
+
setConfig: () => setConfig,
|
|
335
|
+
updateModel: () => updateModel
|
|
336
|
+
});
|
|
337
|
+
async function saveModel(def, model) {
|
|
338
|
+
const hm = getHooksFor(def);
|
|
339
|
+
const exists = getExists(model);
|
|
340
|
+
const pk = getPrimaryKeyColumn(def);
|
|
341
|
+
const db = getDb(def);
|
|
342
|
+
const config = getConfig(def);
|
|
343
|
+
if (exists) {
|
|
344
|
+
const dirty = getState(model).attributes;
|
|
345
|
+
const original = getState(model).original;
|
|
346
|
+
const changed = {};
|
|
347
|
+
for (const key of Object.keys(dirty)) if (dirty[key] !== original[key]) changed[key] = config?.casts?.[key] ? prepareForDb(dirty[key], config.casts[key]) : dirty[key];
|
|
348
|
+
if (Object.keys(changed).length === 0) return model;
|
|
349
|
+
await hm.trigger("beforeUpdate", model);
|
|
350
|
+
await hm.trigger("beforeSave", model);
|
|
351
|
+
const pkValue = model.get(pk);
|
|
352
|
+
try {
|
|
353
|
+
await db.updateTable(def.table).set(changed).where(pk, "=", pkValue).execute();
|
|
354
|
+
} catch (e) {
|
|
355
|
+
throw normalizeError(e, def.table);
|
|
356
|
+
}
|
|
357
|
+
syncOriginal(model);
|
|
358
|
+
await hm.trigger("afterUpdate", model);
|
|
359
|
+
await hm.trigger("afterSave", model);
|
|
360
|
+
} else {
|
|
361
|
+
await hm.trigger("beforeCreate", model);
|
|
362
|
+
await hm.trigger("beforeSave", model);
|
|
363
|
+
const data = {};
|
|
364
|
+
const attrs = getState(model).attributes;
|
|
365
|
+
for (const [key, value] of Object.entries(attrs)) if (key !== pk || value !== void 0) data[key] = config?.casts?.[key] ? prepareForDb(value, config.casts[key]) : value;
|
|
366
|
+
try {
|
|
367
|
+
const result = await db.insertInto(def.table).values(data).returningAll().executeTakeFirst();
|
|
368
|
+
if (result) {
|
|
369
|
+
const applied = config?.casts ? applyCastsToData(config, result) : result;
|
|
370
|
+
for (const [key, value] of Object.entries(applied)) getState(model).attributes[key] = value;
|
|
371
|
+
}
|
|
372
|
+
} catch (e) {
|
|
373
|
+
throw normalizeError(e, def.table);
|
|
374
|
+
}
|
|
375
|
+
setExists(model, true);
|
|
376
|
+
syncOriginal(model);
|
|
377
|
+
await hm.trigger("afterCreate", model);
|
|
378
|
+
await hm.trigger("afterSave", model);
|
|
379
|
+
}
|
|
380
|
+
return model;
|
|
381
|
+
}
|
|
382
|
+
async function insertModel(def, data) {
|
|
383
|
+
const config = getConfig(def) ?? { columns: def.columns };
|
|
384
|
+
if (!Object.keys(data).some((key) => key in def.relations)) {
|
|
385
|
+
const model = createInstance(def, config, data, false);
|
|
386
|
+
await saveModel(def, model);
|
|
387
|
+
return model;
|
|
388
|
+
}
|
|
389
|
+
const { extractRelationData, processCreateRelations } = await import("./crud-Di2nvpjB.mjs");
|
|
390
|
+
const { columnData, relationOps } = extractRelationData(def, data);
|
|
391
|
+
for (const [relName, op] of Object.entries(relationOps)) {
|
|
392
|
+
const relation = def.relations[relName];
|
|
393
|
+
if (relation?.type === "belongsTo") {
|
|
394
|
+
const bop = op;
|
|
395
|
+
const relatedDef = relation.relatedModelClass;
|
|
396
|
+
if (bop.create) {
|
|
397
|
+
const related = await relatedDef.insert(bop.create);
|
|
398
|
+
columnData[relation.foreignKey] = related.get(relation.localKey);
|
|
399
|
+
} else if (bop.connect) {
|
|
400
|
+
const cond = bop.connect;
|
|
401
|
+
const condKey = Object.keys(cond)[0];
|
|
402
|
+
const found = await relatedDef.query().where(condKey, "=", cond[condKey]).executeTakeFirst();
|
|
403
|
+
if (found) columnData[relation.foreignKey] = found.get(relation.localKey);
|
|
404
|
+
} else if (bop.connectOrCreate) {
|
|
405
|
+
const { where, create } = bop.connectOrCreate;
|
|
406
|
+
const whereKey = Object.keys(where)[0];
|
|
407
|
+
const found = await relatedDef.query().where(whereKey, "=", where[whereKey]).executeTakeFirst();
|
|
408
|
+
if (found) columnData[relation.foreignKey] = found.get(relation.localKey);
|
|
409
|
+
else {
|
|
410
|
+
const created = await relatedDef.insert(create);
|
|
411
|
+
columnData[relation.foreignKey] = created.get(relation.localKey);
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
const model = createInstance(def, config, columnData, false);
|
|
417
|
+
await saveModel(def, model);
|
|
418
|
+
const postOps = {};
|
|
419
|
+
for (const [relName, op] of Object.entries(relationOps)) {
|
|
420
|
+
const relation = def.relations[relName];
|
|
421
|
+
if (relation && relation.type !== "belongsTo") postOps[relName] = op;
|
|
422
|
+
}
|
|
423
|
+
if (Object.keys(postOps).length > 0) await processCreateRelations(def, model, postOps);
|
|
424
|
+
return model;
|
|
425
|
+
}
|
|
426
|
+
async function insertManyModel(def, dataArray) {
|
|
427
|
+
const db = getDb(def);
|
|
428
|
+
const pk = getPrimaryKeyColumn(def);
|
|
429
|
+
const config = getConfig(def);
|
|
430
|
+
const hm = getHooksFor(def);
|
|
431
|
+
const instances = [];
|
|
432
|
+
for (const data of dataArray) {
|
|
433
|
+
const instance = createInstance(def, config ?? { columns: def.columns }, {}, false);
|
|
434
|
+
const fillData = {};
|
|
435
|
+
for (const [key, value] of Object.entries(data)) if (key !== pk) fillData[key] = value;
|
|
436
|
+
instance.fill(fillData);
|
|
437
|
+
await hm.trigger("beforeCreate", instance);
|
|
438
|
+
instances.push(instance);
|
|
439
|
+
}
|
|
440
|
+
const prepared = instances.map((inst) => {
|
|
441
|
+
const attrs = inst.attributes ?? {};
|
|
442
|
+
const row = {};
|
|
443
|
+
for (const [key, value] of Object.entries(attrs)) row[key] = config?.casts?.[key] ? prepareForDb(value, config.casts[key]) : value;
|
|
444
|
+
return row;
|
|
445
|
+
});
|
|
446
|
+
let results;
|
|
447
|
+
try {
|
|
448
|
+
results = await db.insertInto(def.table).values(prepared).returningAll().execute();
|
|
449
|
+
} catch (e) {
|
|
450
|
+
throw normalizeError(e, def.table);
|
|
451
|
+
}
|
|
452
|
+
const models = results.map((row) => {
|
|
453
|
+
const applied = config?.casts ? applyCastsToData(config, row) : row;
|
|
454
|
+
return createInstance(def, config ?? { columns: def.columns }, applied, true);
|
|
455
|
+
});
|
|
456
|
+
for (const model of models) await hm.trigger("afterCreate", model);
|
|
457
|
+
return models;
|
|
458
|
+
}
|
|
459
|
+
async function updateModel(def, id, data) {
|
|
460
|
+
const model = await def.findOrFail(id);
|
|
461
|
+
if (!Object.keys(data).some((key) => key in def.relations)) {
|
|
462
|
+
model.fill(data);
|
|
463
|
+
await saveModel(def, model);
|
|
464
|
+
return model;
|
|
465
|
+
}
|
|
466
|
+
const { extractRelationData } = await import("./crud-Di2nvpjB.mjs");
|
|
467
|
+
const { columnData, relationOps } = extractRelationData(def, data);
|
|
468
|
+
model.fill(columnData);
|
|
469
|
+
await saveModel(def, model);
|
|
470
|
+
const pkValue = model.get("id");
|
|
471
|
+
if (pkValue == null) return model;
|
|
472
|
+
for (const [relName, op] of Object.entries(relationOps)) {
|
|
473
|
+
const relation = def.relations[relName];
|
|
474
|
+
if (!relation) continue;
|
|
475
|
+
const relatedDef = relation.relatedModelClass;
|
|
476
|
+
const db = relatedDef._orm?.kysely;
|
|
477
|
+
if (!db) continue;
|
|
478
|
+
if (relation.type === "belongsTo") {
|
|
479
|
+
const bop = op;
|
|
480
|
+
if (bop.update) {
|
|
481
|
+
const fkValue = model.get(relation.foreignKey);
|
|
482
|
+
if (fkValue != null) {
|
|
483
|
+
const related = await relatedDef.find(fkValue);
|
|
484
|
+
if (related) {
|
|
485
|
+
related.fill(bop.update);
|
|
486
|
+
const { saveModel: saveRel } = await Promise.resolve().then(() => save_exports);
|
|
487
|
+
await saveRel(relatedDef, related);
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
} else if (bop.upsert) {
|
|
491
|
+
const { update, create } = bop.upsert;
|
|
492
|
+
const fkValue = model.get(relation.foreignKey);
|
|
493
|
+
if (fkValue != null) {
|
|
494
|
+
const related = await relatedDef.find(fkValue);
|
|
495
|
+
if (related) {
|
|
496
|
+
related.fill(update);
|
|
497
|
+
const { saveModel: saveRel } = await Promise.resolve().then(() => save_exports);
|
|
498
|
+
await saveRel(relatedDef, related);
|
|
499
|
+
}
|
|
500
|
+
} else {
|
|
501
|
+
const created = await relatedDef.insert(create);
|
|
502
|
+
await db.updateTable(def.table).set({ [relation.foreignKey]: created.get(relation.localKey) }).where("id", "=", pkValue).execute();
|
|
503
|
+
model.set(relation.foreignKey, created.get(relation.localKey));
|
|
504
|
+
}
|
|
505
|
+
} else if (bop.disconnect) {
|
|
506
|
+
await db.updateTable(def.table).set({ [relation.foreignKey]: null }).where("id", "=", pkValue).execute();
|
|
507
|
+
model.set(relation.foreignKey, null);
|
|
508
|
+
} else if (bop.delete) {
|
|
509
|
+
const fkValue = model.get(relation.foreignKey);
|
|
510
|
+
if (fkValue != null) {
|
|
511
|
+
const related = await relatedDef.find(fkValue);
|
|
512
|
+
if (related) {
|
|
513
|
+
const { deleteModel: delRel } = await Promise.resolve().then(() => delete_exports);
|
|
514
|
+
await delRel(relatedDef, related);
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
} else if (relation.type === "hasMany" || relation.type === "hasOne") {
|
|
519
|
+
const hop = op;
|
|
520
|
+
if (hop.create) for (const childData of hop.create) await relatedDef.insert({
|
|
521
|
+
...childData,
|
|
522
|
+
[relation.foreignKey]: pkValue
|
|
523
|
+
});
|
|
524
|
+
if (hop.update) {
|
|
525
|
+
const queries = Array.isArray(hop.update?.where) ? hop.update.where : [hop.update?.where];
|
|
526
|
+
for (const where of queries) {
|
|
527
|
+
const whereKey = Object.keys(where)[0];
|
|
528
|
+
await relatedDef.query().where(whereKey, "=", where[whereKey]).all().updateMany(hop.update.data);
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
if (hop.delete) {
|
|
532
|
+
const queries = Array.isArray(hop.delete) ? hop.delete : [hop.delete];
|
|
533
|
+
for (const where of queries) {
|
|
534
|
+
const whereKey = Object.keys(where)[0];
|
|
535
|
+
await relatedDef.query().where(whereKey, "=", where[whereKey]).all().deleteMany();
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
} else if (relation.type === "manyToMany") {
|
|
539
|
+
const mop = op;
|
|
540
|
+
if (mop.create) {
|
|
541
|
+
const throughTable = relation.throughTable;
|
|
542
|
+
const fpk = relation.foreignPivotKey;
|
|
543
|
+
const rpk = relation.relatedPivotKey;
|
|
544
|
+
for (const childData of mop.create) {
|
|
545
|
+
const child = await relatedDef.insert(childData);
|
|
546
|
+
try {
|
|
547
|
+
await db.insertInto(throughTable).values({
|
|
548
|
+
[fpk]: pkValue,
|
|
549
|
+
[rpk]: child.get(relation.localKey ?? "id")
|
|
550
|
+
}).execute();
|
|
551
|
+
} catch (e) {
|
|
552
|
+
if (!isUniqueConstraintError(e)) throw normalizeError(e, throughTable);
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
if (mop.connect) {
|
|
557
|
+
const throughTable = relation.throughTable;
|
|
558
|
+
const fpk = relation.foreignPivotKey;
|
|
559
|
+
const rpk = relation.relatedPivotKey;
|
|
560
|
+
const connectLookup = /* @__PURE__ */ new Map();
|
|
561
|
+
const connectObjects = mop.connect.filter((t) => typeof t !== "number" && typeof t !== "string");
|
|
562
|
+
if (connectObjects.length > 0) {
|
|
563
|
+
const byKey = /* @__PURE__ */ new Map();
|
|
564
|
+
for (const t of connectObjects) {
|
|
565
|
+
const k = Object.keys(t)[0];
|
|
566
|
+
if (!byKey.has(k)) byKey.set(k, []);
|
|
567
|
+
byKey.get(k).push(t[k]);
|
|
568
|
+
}
|
|
569
|
+
for (const [k, vals] of byKey) {
|
|
570
|
+
const records = await relatedDef.query().whereIn(k, vals).execute();
|
|
571
|
+
for (const r of records) connectLookup.set(r.get(k), r.get("id"));
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
for (const target of mop.connect) {
|
|
575
|
+
let targetId = target;
|
|
576
|
+
if (typeof target !== "number" && typeof target !== "string") {
|
|
577
|
+
const t = target;
|
|
578
|
+
targetId = connectLookup.get(t[Object.keys(t)[0]]);
|
|
579
|
+
}
|
|
580
|
+
if (targetId != null) try {
|
|
581
|
+
await db.insertInto(throughTable).values({
|
|
582
|
+
[fpk]: pkValue,
|
|
583
|
+
[rpk]: targetId
|
|
584
|
+
}).execute();
|
|
585
|
+
} catch (e) {
|
|
586
|
+
if (!isUniqueConstraintError(e)) throw normalizeError(e, throughTable);
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
if (mop.disconnect) {
|
|
591
|
+
const throughTable = relation.throughTable;
|
|
592
|
+
const fpk = relation.foreignPivotKey;
|
|
593
|
+
const rpk = relation.relatedPivotKey;
|
|
594
|
+
const queries = Array.isArray(mop.disconnect) ? mop.disconnect : [mop.disconnect];
|
|
595
|
+
for (const where of queries) if (typeof where === "object" && Object.keys(where).length > 0) {
|
|
596
|
+
const key = Object.keys(where)[0];
|
|
597
|
+
const val = Object.values(where)[0];
|
|
598
|
+
if (key === "id") await db.deleteFrom(throughTable).where(fpk, "=", pkValue).where(rpk, "=", val).execute();
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
if (mop.set) {
|
|
602
|
+
const throughTable = relation.throughTable;
|
|
603
|
+
const fpk = relation.foreignPivotKey;
|
|
604
|
+
const rpk = relation.relatedPivotKey;
|
|
605
|
+
const current = await db.selectFrom(throughTable).select(rpk).where(fpk, "=", pkValue).execute();
|
|
606
|
+
const currentIds = new Set(current.map((r) => r[rpk]));
|
|
607
|
+
const desiredIds = /* @__PURE__ */ new Set();
|
|
608
|
+
const setLookup = /* @__PURE__ */ new Map();
|
|
609
|
+
const setObjects = mop.set.filter((t) => typeof t !== "number" && typeof t !== "string");
|
|
610
|
+
if (setObjects.length > 0) {
|
|
611
|
+
const byKey = /* @__PURE__ */ new Map();
|
|
612
|
+
for (const t of setObjects) {
|
|
613
|
+
const k = Object.keys(t)[0];
|
|
614
|
+
if (!byKey.has(k)) byKey.set(k, []);
|
|
615
|
+
byKey.get(k).push(t[k]);
|
|
616
|
+
}
|
|
617
|
+
for (const [k, vals] of byKey) {
|
|
618
|
+
const records = await relatedDef.query().whereIn(k, vals).execute();
|
|
619
|
+
for (const r of records) setLookup.set(r.get(k), r.get("id"));
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
for (const target of mop.set) {
|
|
623
|
+
let targetId = target;
|
|
624
|
+
if (typeof target !== "number" && typeof target !== "string") {
|
|
625
|
+
const t = target;
|
|
626
|
+
targetId = setLookup.get(t[Object.keys(t)[0]]);
|
|
627
|
+
}
|
|
628
|
+
if (targetId != null) {
|
|
629
|
+
desiredIds.add(targetId);
|
|
630
|
+
if (!currentIds.has(targetId)) try {
|
|
631
|
+
await db.insertInto(throughTable).values({
|
|
632
|
+
[fpk]: pkValue,
|
|
633
|
+
[rpk]: targetId
|
|
634
|
+
}).execute();
|
|
635
|
+
} catch (e) {
|
|
636
|
+
if (!isUniqueConstraintError(e)) throw normalizeError(e, throughTable);
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
for (const id of currentIds) if (!desiredIds.has(id)) await db.deleteFrom(throughTable).where(fpk, "=", pkValue).where(rpk, "=", id).execute();
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
return model;
|
|
645
|
+
}
|
|
646
|
+
async function reloadModel(def, model) {
|
|
647
|
+
const pk = getPrimaryKeyColumn(def);
|
|
648
|
+
const pkValue = model.get(pk);
|
|
649
|
+
if (pkValue == null) throw new DatabaseError("Cannot reload model without primary key", "MISSING_ID");
|
|
650
|
+
const db = getDb(def);
|
|
651
|
+
try {
|
|
652
|
+
const row = await db.selectFrom(def.table).selectAll().where(pk, "=", pkValue).executeTakeFirst();
|
|
653
|
+
if (row) {
|
|
654
|
+
const config = getConfig(def);
|
|
655
|
+
const applied = config?.casts ? applyCastsToData(config, row) : row;
|
|
656
|
+
const state = getState(model);
|
|
657
|
+
state.attributes = { ...applied };
|
|
658
|
+
state.original = { ...applied };
|
|
659
|
+
}
|
|
660
|
+
} catch (e) {
|
|
661
|
+
throw normalizeError(e, def.table);
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
const configMap = /* @__PURE__ */ new WeakMap();
|
|
665
|
+
function setConfig(def, config) {
|
|
666
|
+
configMap.set(def, config);
|
|
667
|
+
}
|
|
668
|
+
function getConfig(def) {
|
|
669
|
+
return configMap.get(def);
|
|
670
|
+
}
|
|
671
|
+
//#endregion
|
|
309
672
|
//#region src/relations/eager.ts
|
|
310
673
|
var eager_exports = /* @__PURE__ */ __exportAll({ EagerLoader: () => EagerLoader });
|
|
311
|
-
function isMorphRelation(relation) {
|
|
312
|
-
return relation._morphMap !== void 0;
|
|
313
|
-
}
|
|
314
674
|
var EagerLoader = class {
|
|
315
675
|
async loadRelated(models, eagerLoad, def) {
|
|
316
676
|
const { name, constraints } = eagerLoad;
|
|
@@ -324,7 +684,7 @@ var EagerLoader = class {
|
|
|
324
684
|
const nestedName = name.slice(dotIdx + 1);
|
|
325
685
|
const relation = def.relations[baseName];
|
|
326
686
|
if (!relation) throw new Error(`Relation "${baseName}" not found on ${def.name}`);
|
|
327
|
-
if (
|
|
687
|
+
if (relation._morphMap !== void 0) throw new Error(`Cannot eagerly load nested relation "${nestedName}" through polymorphic relation "${baseName}" on ${def.name}. Nested eager loading through polymorphic belongsTo is not supported.`);
|
|
328
688
|
await relation.loadEager(models, baseName, null);
|
|
329
689
|
const relatedModels = [];
|
|
330
690
|
for (const model of models) {
|
|
@@ -346,64 +706,31 @@ var EagerLoader = class {
|
|
|
346
706
|
}
|
|
347
707
|
};
|
|
348
708
|
//#endregion
|
|
349
|
-
//#region src/relations/graph/
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
}
|
|
370
|
-
const THUNK_CACHE$3 = /* @__PURE__ */ new WeakMap();
|
|
371
|
-
function resolveThunk$3(thunk) {
|
|
372
|
-
let cls = THUNK_CACHE$3.get(thunk);
|
|
373
|
-
if (!cls) {
|
|
374
|
-
cls = thunk();
|
|
375
|
-
THUNK_CACHE$3.set(thunk, cls);
|
|
709
|
+
//#region src/relations/graph/delete.ts
|
|
710
|
+
async function deleteGraph(def, model, options = {}) {
|
|
711
|
+
const db = getDb(def);
|
|
712
|
+
const pk = getPrimaryKeyColumn(def);
|
|
713
|
+
const pkValue = model.get(pk);
|
|
714
|
+
if (pkValue == null) throw new Error("Cannot deleteGraph: model has no primary key");
|
|
715
|
+
for (const [relationName, relation] of Object.entries(def.relations)) {
|
|
716
|
+
if (options.allowedRelations && !options.allowedRelations.includes(relationName)) continue;
|
|
717
|
+
const relatedDef = relation.relatedModelClass;
|
|
718
|
+
if (relation.type === "hasMany") {
|
|
719
|
+
const children = await relatedDef.query().where(relation.foreignKey, "=", pkValue).execute();
|
|
720
|
+
for (const child of children) await child.$delete();
|
|
721
|
+
} else if (relation.type === "manyToMany") {
|
|
722
|
+
const throughTable = relation.throughTable;
|
|
723
|
+
const fpk = relation.foreignPivotKey;
|
|
724
|
+
await db.deleteFrom(throughTable).where(fpk, "=", pkValue).execute();
|
|
725
|
+
} else if (relation.type === "hasOne") {
|
|
726
|
+
const child = await relatedDef.query().where(relation.foreignKey, "=", pkValue).first();
|
|
727
|
+
if (child) await child.$delete();
|
|
728
|
+
}
|
|
376
729
|
}
|
|
377
|
-
|
|
730
|
+
await model.$delete();
|
|
378
731
|
}
|
|
379
732
|
//#endregion
|
|
380
733
|
//#region src/relations/graph/parser.ts
|
|
381
|
-
function getPrimaryKeyColumn$2(def) {
|
|
382
|
-
const cols = def.columns;
|
|
383
|
-
for (const [name, col] of Object.entries(cols)) if (col.isPrimaryKey) return name;
|
|
384
|
-
return "id";
|
|
385
|
-
}
|
|
386
|
-
function getDb$1(def) {
|
|
387
|
-
if (!def._orm) throw new Error("Model not registered");
|
|
388
|
-
return def._orm.kysely;
|
|
389
|
-
}
|
|
390
|
-
function getPivotInfo(relation) {
|
|
391
|
-
if (relation.type !== "manyToMany" || !relation.throughTable) throw new Error("Not a many-to-many relation");
|
|
392
|
-
return {
|
|
393
|
-
throughTable: relation.throughTable,
|
|
394
|
-
foreignPivotKey: relation.foreignPivotKey ?? "",
|
|
395
|
-
relatedPivotKey: relation.relatedPivotKey ?? ""
|
|
396
|
-
};
|
|
397
|
-
}
|
|
398
|
-
async function findRelated(def, conditions) {
|
|
399
|
-
const key = Object.keys(conditions)[0];
|
|
400
|
-
return def.query().where(key, "=", conditions[key]).executeTakeFirst();
|
|
401
|
-
}
|
|
402
|
-
async function resolveTargetId(def, target) {
|
|
403
|
-
if (typeof target === "number" || typeof target === "string") return target;
|
|
404
|
-
const found = await findRelated(def, target);
|
|
405
|
-
if (found) return found.get("id");
|
|
406
|
-
}
|
|
407
734
|
/**
|
|
408
735
|
* Splits a graph node's keys into column data and relation operations.
|
|
409
736
|
* Handles both the old-style { create: ..., connect: ... } wrappers and
|
|
@@ -567,7 +894,7 @@ async function processNode(node, def, parentFK, options, context, path) {
|
|
|
567
894
|
}
|
|
568
895
|
const instance = await def.insert(columnData);
|
|
569
896
|
if (nodeId && typeof nodeId === "string") context.processedRefs.set(nodeId, instance);
|
|
570
|
-
const pkCol = getPrimaryKeyColumn
|
|
897
|
+
const pkCol = getPrimaryKeyColumn(def);
|
|
571
898
|
const pkValue = instance.get(pkCol);
|
|
572
899
|
if (pkValue == null) throw new DatabaseError("Cannot process relations without primary key", "MISSING_ID");
|
|
573
900
|
for (const [relName, op] of Object.entries(relationOps)) {
|
|
@@ -586,7 +913,7 @@ async function processNode(node, def, parentFK, options, context, path) {
|
|
|
586
913
|
return instance;
|
|
587
914
|
}
|
|
588
915
|
async function processBelongsTo(relation, op, options, context, path, parentColumnData) {
|
|
589
|
-
if (
|
|
916
|
+
if (relation._morphMap !== void 0) return processMorphTo(relation, op, options, context, path, parentColumnData);
|
|
590
917
|
const relatedDef = relation.relatedModelClass;
|
|
591
918
|
if (op["#dbRef"] != null) {
|
|
592
919
|
const id = op["#dbRef"];
|
|
@@ -614,8 +941,8 @@ async function processBelongsTo(relation, op, options, context, path, parentColu
|
|
|
614
941
|
*/
|
|
615
942
|
async function processMorphTo(relation, op, options, context, path, parentColumnData) {
|
|
616
943
|
const morphMap = relation._morphMap;
|
|
617
|
-
const morphType =
|
|
618
|
-
const morphId =
|
|
944
|
+
const morphType = relation._morphType;
|
|
945
|
+
const morphId = relation._morphId;
|
|
619
946
|
if (!morphMap || Object.keys(morphMap).length === 0) throw new Error("Cannot process MorphTo relation: no morphMap provided. Define a morphMap with model thunks when calling defineMorphTo().");
|
|
620
947
|
let typeValue = op.type;
|
|
621
948
|
if (!typeValue) {
|
|
@@ -625,7 +952,7 @@ async function processMorphTo(relation, op, options, context, path, parentColumn
|
|
|
625
952
|
if (!typeValue) throw new Error(`Cannot resolve MorphTo: no type specified. Provide a "type" key in the relation data (e.g., { type: "${Object.keys(morphMap)[0]}" }). Available types: ${Object.keys(morphMap).join(", ")}`);
|
|
626
953
|
const thunk = morphMap[typeValue];
|
|
627
954
|
if (!thunk) throw new Error(`No model registered for morph type "${typeValue}" in MorphTo. Available types: ${Object.keys(morphMap).join(", ")}`);
|
|
628
|
-
const relatedDef = resolveThunk
|
|
955
|
+
const relatedDef = resolveThunk(thunk);
|
|
629
956
|
let instance = null;
|
|
630
957
|
if (op["#dbRef"] != null) {
|
|
631
958
|
const id = op["#dbRef"];
|
|
@@ -667,9 +994,9 @@ async function processHasMany(_instance, relation, op, pkValue, options, context
|
|
|
667
994
|
continue;
|
|
668
995
|
}
|
|
669
996
|
const parentData = { [fk]: pkValue };
|
|
670
|
-
if (
|
|
671
|
-
const typeCol =
|
|
672
|
-
const typeVal =
|
|
997
|
+
if (relation._morphType !== void 0 && relation._morphMap === void 0) {
|
|
998
|
+
const typeCol = relation._morphType;
|
|
999
|
+
const typeVal = relation._morphTypeValue;
|
|
673
1000
|
if (typeVal !== void 0) parentData[typeCol] = typeVal;
|
|
674
1001
|
}
|
|
675
1002
|
await processNode(item, relatedDef, parentData, options, context, path);
|
|
@@ -689,7 +1016,7 @@ async function processHasMany(_instance, relation, op, pkValue, options, context
|
|
|
689
1016
|
async function processManyToMany(_instance, relation, op, pkValue, options, context, path) {
|
|
690
1017
|
const relatedDef = relation.relatedModelClass;
|
|
691
1018
|
const { throughTable, foreignPivotKey, relatedPivotKey } = getPivotInfo(relation);
|
|
692
|
-
const db = getDb
|
|
1019
|
+
const db = getDb(relatedDef);
|
|
693
1020
|
const items = Array.isArray(op) ? op : Array.isArray(op?.create) ? op.create : [];
|
|
694
1021
|
for (const item of items) {
|
|
695
1022
|
if (item["#dbRef"] != null) {
|
|
@@ -699,7 +1026,9 @@ async function processManyToMany(_instance, relation, op, pkValue, options, cont
|
|
|
699
1026
|
[foreignPivotKey]: pkValue,
|
|
700
1027
|
[relatedPivotKey]: id
|
|
701
1028
|
}).execute();
|
|
702
|
-
} catch {
|
|
1029
|
+
} catch (e) {
|
|
1030
|
+
if (!isUniqueConstraintError(e)) throw normalizeError(e, throughTable);
|
|
1031
|
+
}
|
|
703
1032
|
continue;
|
|
704
1033
|
}
|
|
705
1034
|
const relatedId = (await processNode(item, relatedDef, null, options, context, path)).get(relation.localKey ?? "id");
|
|
@@ -708,7 +1037,9 @@ async function processManyToMany(_instance, relation, op, pkValue, options, cont
|
|
|
708
1037
|
[foreignPivotKey]: pkValue,
|
|
709
1038
|
[relatedPivotKey]: relatedId
|
|
710
1039
|
}).execute();
|
|
711
|
-
} catch {
|
|
1040
|
+
} catch (e) {
|
|
1041
|
+
if (!isUniqueConstraintError(e)) throw normalizeError(e, throughTable);
|
|
1042
|
+
}
|
|
712
1043
|
}
|
|
713
1044
|
const connectItems = !Array.isArray(op) ? op?.connect ?? [] : [];
|
|
714
1045
|
for (const target of connectItems) {
|
|
@@ -718,7 +1049,9 @@ async function processManyToMany(_instance, relation, op, pkValue, options, cont
|
|
|
718
1049
|
[foreignPivotKey]: pkValue,
|
|
719
1050
|
[relatedPivotKey]: targetId
|
|
720
1051
|
}).execute();
|
|
721
|
-
} catch {
|
|
1052
|
+
} catch (e) {
|
|
1053
|
+
if (!isUniqueConstraintError(e)) throw normalizeError(e, throughTable);
|
|
1054
|
+
}
|
|
722
1055
|
}
|
|
723
1056
|
}
|
|
724
1057
|
//#endregion
|
|
@@ -762,7 +1095,7 @@ async function upsertNode(node, def, parentFK, options, context, _path) {
|
|
|
762
1095
|
if (relatedInstance) columnData[relation.foreignKey] = relatedInstance.get(relation.localKey);
|
|
763
1096
|
}
|
|
764
1097
|
}
|
|
765
|
-
const pkCol = getPrimaryKeyColumn
|
|
1098
|
+
const pkCol = getPrimaryKeyColumn(def);
|
|
766
1099
|
const idValue = columnData[pkCol] ?? node[pkCol];
|
|
767
1100
|
let instance;
|
|
768
1101
|
if (idValue != null) if (isRelPathAllowed(pkCol, options.noUpdate)) {
|
|
@@ -798,13 +1131,13 @@ async function upsertHasMany(_instance, relation, op, pkValue, options, context,
|
|
|
798
1131
|
const existingChildren = await relatedDef.query().where(fk, "=", pkValue).execute();
|
|
799
1132
|
const existingMap = /* @__PURE__ */ new Map();
|
|
800
1133
|
for (const child of existingChildren) {
|
|
801
|
-
const pkCol = getPrimaryKeyColumn
|
|
1134
|
+
const pkCol = getPrimaryKeyColumn(relatedDef);
|
|
802
1135
|
existingMap.set(child.get(pkCol), child);
|
|
803
1136
|
}
|
|
804
1137
|
const items = Array.isArray(op) ? op : Array.isArray(op?.create) ? op.create : [];
|
|
805
1138
|
const incomingIds = /* @__PURE__ */ new Set();
|
|
806
1139
|
for (const item of items) {
|
|
807
|
-
const itemId = item[getPrimaryKeyColumn
|
|
1140
|
+
const itemId = item[getPrimaryKeyColumn(relatedDef)] ?? item.id;
|
|
808
1141
|
if (itemId != null) {
|
|
809
1142
|
incomingIds.add(itemId);
|
|
810
1143
|
if (!isRelPathAllowed(relNameFromPath(path), options.noUpdate)) {
|
|
@@ -820,7 +1153,7 @@ async function upsertHasMany(_instance, relation, op, pkValue, options, context,
|
|
|
820
1153
|
if (!rel) continue;
|
|
821
1154
|
const nestedPath = `${path}.${relName}`;
|
|
822
1155
|
assertRelationAllowed(relatedDef, nestedPath, context.allowedGraphSet);
|
|
823
|
-
const childPk = existing.get(getPrimaryKeyColumn
|
|
1156
|
+
const childPk = existing.get(getPrimaryKeyColumn(relatedDef));
|
|
824
1157
|
if (rel.type === "hasMany" || rel.type === "hasOne") await upsertHasMany(existing, rel, relOp, childPk, options, context, nestedPath);
|
|
825
1158
|
else if (rel.type === "manyToMany") await upsertManyToMany(existing, rel, relOp, childPk, options, context, nestedPath);
|
|
826
1159
|
}
|
|
@@ -829,9 +1162,9 @@ async function upsertHasMany(_instance, relation, op, pkValue, options, context,
|
|
|
829
1162
|
}
|
|
830
1163
|
}
|
|
831
1164
|
const parentData = { [fk]: pkValue };
|
|
832
|
-
if (
|
|
833
|
-
const typeCol =
|
|
834
|
-
const typeVal =
|
|
1165
|
+
if (relation._morphType !== void 0 && relation._morphMap === void 0) {
|
|
1166
|
+
const typeCol = relation._morphType;
|
|
1167
|
+
const typeVal = relation._morphTypeValue;
|
|
835
1168
|
if (typeVal !== void 0) parentData[typeCol] = typeVal;
|
|
836
1169
|
}
|
|
837
1170
|
await processNode(item, relatedDef, parentData, options, context, path);
|
|
@@ -851,7 +1184,7 @@ async function upsertHasMany(_instance, relation, op, pkValue, options, context,
|
|
|
851
1184
|
async function upsertManyToMany(_instance, relation, op, pkValue, options, context, path) {
|
|
852
1185
|
const relatedDef = relation.relatedModelClass;
|
|
853
1186
|
const { throughTable, foreignPivotKey, relatedPivotKey } = getPivotInfo(relation);
|
|
854
|
-
const db = getDb
|
|
1187
|
+
const db = getDb(relatedDef);
|
|
855
1188
|
let existingPivotIds = /* @__PURE__ */ new Set();
|
|
856
1189
|
try {
|
|
857
1190
|
const pivots = await db.selectFrom(throughTable).select(relatedPivotKey).where(foreignPivotKey, "=", pkValue).execute();
|
|
@@ -859,6 +1192,14 @@ async function upsertManyToMany(_instance, relation, op, pkValue, options, conte
|
|
|
859
1192
|
} catch {}
|
|
860
1193
|
const incomingIds = /* @__PURE__ */ new Set();
|
|
861
1194
|
const items = Array.isArray(op) ? op : Array.isArray(op?.create) ? op.create : [];
|
|
1195
|
+
const needsUpdate = !isRelPathAllowed(relNameFromPath(path), options.noUpdate);
|
|
1196
|
+
const pkCol = getPrimaryKeyColumn(relatedDef);
|
|
1197
|
+
const itemIds = items.filter((i) => i["#dbRef"] == null).map((i) => i[pkCol] ?? i.id).filter((id) => id != null);
|
|
1198
|
+
const batchMap = /* @__PURE__ */ new Map();
|
|
1199
|
+
if (needsUpdate && itemIds.length > 0) {
|
|
1200
|
+
const records = await relatedDef.query().whereIn(pkCol, itemIds).execute();
|
|
1201
|
+
for (const r of records) batchMap.set(r.get(pkCol), r);
|
|
1202
|
+
}
|
|
862
1203
|
for (const item of items) {
|
|
863
1204
|
if (item["#dbRef"] != null) {
|
|
864
1205
|
const id = item["#dbRef"];
|
|
@@ -868,14 +1209,16 @@ async function upsertManyToMany(_instance, relation, op, pkValue, options, conte
|
|
|
868
1209
|
[foreignPivotKey]: pkValue,
|
|
869
1210
|
[relatedPivotKey]: id
|
|
870
1211
|
}).execute();
|
|
871
|
-
} catch {
|
|
1212
|
+
} catch (e) {
|
|
1213
|
+
if (!isUniqueConstraintError(e)) throw normalizeError(e, throughTable);
|
|
1214
|
+
}
|
|
872
1215
|
continue;
|
|
873
1216
|
}
|
|
874
|
-
const itemId = item[
|
|
1217
|
+
const itemId = item[pkCol] ?? item.id;
|
|
875
1218
|
if (itemId != null) {
|
|
876
1219
|
incomingIds.add(itemId);
|
|
877
|
-
if (
|
|
878
|
-
const existing =
|
|
1220
|
+
if (needsUpdate) {
|
|
1221
|
+
const existing = batchMap.get(itemId);
|
|
879
1222
|
if (existing) {
|
|
880
1223
|
existing.fill(item);
|
|
881
1224
|
await existing.$save();
|
|
@@ -886,7 +1229,9 @@ async function upsertManyToMany(_instance, relation, op, pkValue, options, conte
|
|
|
886
1229
|
[foreignPivotKey]: pkValue,
|
|
887
1230
|
[relatedPivotKey]: itemId
|
|
888
1231
|
}).execute();
|
|
889
|
-
} catch {
|
|
1232
|
+
} catch (e) {
|
|
1233
|
+
if (!isUniqueConstraintError(e)) throw normalizeError(e, throughTable);
|
|
1234
|
+
}
|
|
890
1235
|
} else {
|
|
891
1236
|
const relatedId = (await processNode(item, relatedDef, null, options, context, path)).get(relation.localKey ?? "id");
|
|
892
1237
|
if (relatedId != null) {
|
|
@@ -896,13 +1241,33 @@ async function upsertManyToMany(_instance, relation, op, pkValue, options, conte
|
|
|
896
1241
|
[foreignPivotKey]: pkValue,
|
|
897
1242
|
[relatedPivotKey]: relatedId
|
|
898
1243
|
}).execute();
|
|
899
|
-
} catch {
|
|
1244
|
+
} catch (e) {
|
|
1245
|
+
if (!isUniqueConstraintError(e)) throw normalizeError(e, throughTable);
|
|
1246
|
+
}
|
|
900
1247
|
}
|
|
901
1248
|
}
|
|
902
1249
|
}
|
|
903
1250
|
const connectItems = !Array.isArray(op) ? op?.connect ?? [] : [];
|
|
1251
|
+
const connectLookup = /* @__PURE__ */ new Map();
|
|
1252
|
+
const connectObjects = connectItems.filter((t) => typeof t !== "number" && typeof t !== "string");
|
|
1253
|
+
if (connectObjects.length > 0) {
|
|
1254
|
+
const byKey = /* @__PURE__ */ new Map();
|
|
1255
|
+
for (const t of connectObjects) {
|
|
1256
|
+
const k = Object.keys(t)[0];
|
|
1257
|
+
if (!byKey.has(k)) byKey.set(k, []);
|
|
1258
|
+
byKey.get(k).push(t[k]);
|
|
1259
|
+
}
|
|
1260
|
+
for (const [k, vals] of byKey) {
|
|
1261
|
+
const records = await relatedDef.query().whereIn(k, vals).execute();
|
|
1262
|
+
for (const r of records) connectLookup.set(r.get(k), r.get("id"));
|
|
1263
|
+
}
|
|
1264
|
+
}
|
|
904
1265
|
for (const target of connectItems) {
|
|
905
|
-
|
|
1266
|
+
let targetId = target;
|
|
1267
|
+
if (typeof target !== "number" && typeof target !== "string") {
|
|
1268
|
+
const t = target;
|
|
1269
|
+
targetId = connectLookup.get(t[Object.keys(t)[0]]);
|
|
1270
|
+
}
|
|
906
1271
|
if (targetId != null) {
|
|
907
1272
|
incomingIds.add(targetId);
|
|
908
1273
|
if (!existingPivotIds.has(targetId)) try {
|
|
@@ -910,7 +1275,9 @@ async function upsertManyToMany(_instance, relation, op, pkValue, options, conte
|
|
|
910
1275
|
[foreignPivotKey]: pkValue,
|
|
911
1276
|
[relatedPivotKey]: targetId
|
|
912
1277
|
}).execute();
|
|
913
|
-
} catch {
|
|
1278
|
+
} catch (e) {
|
|
1279
|
+
if (!isUniqueConstraintError(e)) throw normalizeError(e, throughTable);
|
|
1280
|
+
}
|
|
914
1281
|
}
|
|
915
1282
|
}
|
|
916
1283
|
const relName = relNameFromPath(path);
|
|
@@ -920,12 +1287,16 @@ async function upsertManyToMany(_instance, relation, op, pkValue, options, conte
|
|
|
920
1287
|
for (const pivotId of existingPivotIds) if (!incomingIds.has(pivotId)) {
|
|
921
1288
|
if (shouldUnrelate) try {
|
|
922
1289
|
await db.deleteFrom(throughTable).where(foreignPivotKey, "=", pkValue).where(relatedPivotKey, "=", pivotId).execute();
|
|
923
|
-
} catch {
|
|
1290
|
+
} catch (e) {
|
|
1291
|
+
if (!isUniqueConstraintError(e)) throw normalizeError(e, throughTable);
|
|
1292
|
+
}
|
|
924
1293
|
else if (shouldDelete) {
|
|
925
1294
|
await (await relatedDef.find(pivotId))?.$delete();
|
|
926
1295
|
try {
|
|
927
1296
|
await db.deleteFrom(throughTable).where(foreignPivotKey, "=", pkValue).where(relatedPivotKey, "=", pivotId).execute();
|
|
928
|
-
} catch {
|
|
1297
|
+
} catch (e) {
|
|
1298
|
+
if (!isUniqueConstraintError(e)) throw normalizeError(e, throughTable);
|
|
1299
|
+
}
|
|
929
1300
|
}
|
|
930
1301
|
}
|
|
931
1302
|
}
|
|
@@ -933,14 +1304,12 @@ async function upsertManyToMany(_instance, relation, op, pkValue, options, conte
|
|
|
933
1304
|
//#endregion
|
|
934
1305
|
//#region src/relations/graph/index.ts
|
|
935
1306
|
var graph_exports = /* @__PURE__ */ __exportAll({
|
|
1307
|
+
deleteGraph: () => deleteGraph,
|
|
936
1308
|
insertGraph: () => insertGraph,
|
|
937
1309
|
upsertGraph: () => upsertGraph
|
|
938
1310
|
});
|
|
939
1311
|
//#endregion
|
|
940
1312
|
//#region src/query/builder.ts
|
|
941
|
-
function rawSql(str) {
|
|
942
|
-
return sql([str]);
|
|
943
|
-
}
|
|
944
1313
|
const SAFE_COLUMN = /^[a-zA-Z_*][a-zA-Z0-9_.*]*$/;
|
|
945
1314
|
function createQueryBuilder(def, peta) {
|
|
946
1315
|
const db = peta?.kysely ?? def._orm?.kysely;
|
|
@@ -955,7 +1324,6 @@ function createQueryBuilder(def, peta) {
|
|
|
955
1324
|
let hasEffectiveWhere = false;
|
|
956
1325
|
let selectedColumns = null;
|
|
957
1326
|
const aggregateColumns = [];
|
|
958
|
-
const aggregateAliases = [];
|
|
959
1327
|
const whereOps = [];
|
|
960
1328
|
let allowedGraphSet = null;
|
|
961
1329
|
function validateColumn(ref) {
|
|
@@ -999,6 +1367,16 @@ function createQueryBuilder(def, peta) {
|
|
|
999
1367
|
}
|
|
1000
1368
|
return models;
|
|
1001
1369
|
}
|
|
1370
|
+
const _withAggregate = (relationName, aggregateSql, alias, isExists = false) => {
|
|
1371
|
+
const rel = def.relations[relationName];
|
|
1372
|
+
if (!rel) throw new RelationNotFoundError(def.name, relationName);
|
|
1373
|
+
const relatedTable = rel.relatedModelClass.table;
|
|
1374
|
+
const fk = rel.foreignKey;
|
|
1375
|
+
const lk = rel.localKey;
|
|
1376
|
+
const sql = isExists ? `(SELECT EXISTS(SELECT 1 FROM ${relatedTable} WHERE ${relatedTable}.${fk} = ${def.table}.${lk})) as ${alias}` : `(SELECT ${aggregateSql} FROM ${relatedTable} WHERE ${relatedTable}.${fk} = ${def.table}.${lk}) as ${alias}`;
|
|
1377
|
+
aggregateColumns.push(sql);
|
|
1378
|
+
return self;
|
|
1379
|
+
};
|
|
1002
1380
|
const self = {
|
|
1003
1381
|
then(onfulfilled, onrejected) {
|
|
1004
1382
|
return runExecute().then(onfulfilled, onrejected);
|
|
@@ -1006,7 +1384,7 @@ function createQueryBuilder(def, peta) {
|
|
|
1006
1384
|
execute: runExecute,
|
|
1007
1385
|
async collect() {
|
|
1008
1386
|
const items = await runExecute();
|
|
1009
|
-
const { createCollection } = await import("./collection-
|
|
1387
|
+
const { createCollection } = await import("./collection-D9YZn2mL.mjs").then((n) => n.t);
|
|
1010
1388
|
return createCollection(items);
|
|
1011
1389
|
},
|
|
1012
1390
|
async executeTakeFirst() {
|
|
@@ -1062,70 +1440,10 @@ function createQueryBuilder(def, peta) {
|
|
|
1062
1440
|
return Number(result?.max ?? 0);
|
|
1063
1441
|
},
|
|
1064
1442
|
withCount(relation) {
|
|
1065
|
-
|
|
1066
|
-
const rel = def.relations[relation];
|
|
1067
|
-
if (!rel) throw new RelationNotFoundError(def.name, relation);
|
|
1068
|
-
const relatedTable = rel.relatedModelClass.table;
|
|
1069
|
-
const fk = rel.foreignKey;
|
|
1070
|
-
const lk = rel.localKey;
|
|
1071
|
-
aggregateColumns.push(`(SELECT COUNT(*) FROM ${relatedTable} WHERE ${relatedTable}.${fk} = ${def.table}.${lk}) as ${alias}`);
|
|
1072
|
-
aggregateAliases.push(alias);
|
|
1073
|
-
return self;
|
|
1443
|
+
return _withAggregate(relation, "COUNT(*)", `${relation}_count`);
|
|
1074
1444
|
},
|
|
1075
1445
|
withSum(relation, column) {
|
|
1076
|
-
|
|
1077
|
-
const rel = def.relations[relation];
|
|
1078
|
-
if (!rel) throw new RelationNotFoundError(def.name, relation);
|
|
1079
|
-
const relatedTable = rel.relatedModelClass.table;
|
|
1080
|
-
const fk = rel.foreignKey;
|
|
1081
|
-
const lk = rel.localKey;
|
|
1082
|
-
aggregateColumns.push(`(SELECT COALESCE(SUM(${relatedTable}.${validateColumn(column)}), 0) FROM ${relatedTable} WHERE ${relatedTable}.${fk} = ${def.table}.${lk}) as ${alias}`);
|
|
1083
|
-
aggregateAliases.push(alias);
|
|
1084
|
-
return self;
|
|
1085
|
-
},
|
|
1086
|
-
withAvg(relation, column) {
|
|
1087
|
-
const alias = `${relation}_avg_${column}`;
|
|
1088
|
-
const rel = def.relations[relation];
|
|
1089
|
-
if (!rel) throw new RelationNotFoundError(def.name, relation);
|
|
1090
|
-
const relatedTable = rel.relatedModelClass.table;
|
|
1091
|
-
const fk = rel.foreignKey;
|
|
1092
|
-
const lk = rel.localKey;
|
|
1093
|
-
aggregateColumns.push(`(SELECT AVG(${relatedTable}.${validateColumn(column)}) FROM ${relatedTable} WHERE ${relatedTable}.${fk} = ${def.table}.${lk}) as ${alias}`);
|
|
1094
|
-
aggregateAliases.push(alias);
|
|
1095
|
-
return self;
|
|
1096
|
-
},
|
|
1097
|
-
withMin(relation, column) {
|
|
1098
|
-
const alias = `${relation}_min_${column}`;
|
|
1099
|
-
const rel = def.relations[relation];
|
|
1100
|
-
if (!rel) throw new RelationNotFoundError(def.name, relation);
|
|
1101
|
-
const relatedTable = rel.relatedModelClass.table;
|
|
1102
|
-
const fk = rel.foreignKey;
|
|
1103
|
-
const lk = rel.localKey;
|
|
1104
|
-
aggregateColumns.push(`(SELECT MIN(${relatedTable}.${validateColumn(column)}) FROM ${relatedTable} WHERE ${relatedTable}.${fk} = ${def.table}.${lk}) as ${alias}`);
|
|
1105
|
-
aggregateAliases.push(alias);
|
|
1106
|
-
return self;
|
|
1107
|
-
},
|
|
1108
|
-
withMax(relation, column) {
|
|
1109
|
-
const alias = `${relation}_max_${column}`;
|
|
1110
|
-
const rel = def.relations[relation];
|
|
1111
|
-
if (!rel) throw new RelationNotFoundError(def.name, relation);
|
|
1112
|
-
const relatedTable = rel.relatedModelClass.table;
|
|
1113
|
-
const fk = rel.foreignKey;
|
|
1114
|
-
const lk = rel.localKey;
|
|
1115
|
-
aggregateColumns.push(`(SELECT MAX(${relatedTable}.${validateColumn(column)}) FROM ${relatedTable} WHERE ${relatedTable}.${fk} = ${def.table}.${lk}) as ${alias}`);
|
|
1116
|
-
aggregateAliases.push(alias);
|
|
1117
|
-
return self;
|
|
1118
|
-
},
|
|
1119
|
-
withExists(relation) {
|
|
1120
|
-
const alias = `${relation}_exists`;
|
|
1121
|
-
const rel = def.relations[relation];
|
|
1122
|
-
if (!rel) throw new RelationNotFoundError(def.name, relation);
|
|
1123
|
-
const relatedTable = rel.relatedModelClass.table;
|
|
1124
|
-
const fk = rel.foreignKey;
|
|
1125
|
-
const lk = rel.localKey;
|
|
1126
|
-
aggregateColumns.push(`(SELECT EXISTS(SELECT 1 FROM ${relatedTable} WHERE ${relatedTable}.${fk} = ${def.table}.${lk})) as ${alias}`);
|
|
1127
|
-
aggregateAliases.push(alias);
|
|
1128
|
-
return self;
|
|
1446
|
+
return _withAggregate(relation, `COALESCE(SUM(${validateColumn(column)}), 0)`, `${relation}_sum_${column}`);
|
|
1129
1447
|
},
|
|
1130
1448
|
async chunk(size, callback) {
|
|
1131
1449
|
let offset = 0;
|
|
@@ -1186,6 +1504,19 @@ function createQueryBuilder(def, peta) {
|
|
|
1186
1504
|
allowGraph: allowedGraphSet ? [...allowedGraphSet] : options?.allowGraph
|
|
1187
1505
|
});
|
|
1188
1506
|
},
|
|
1507
|
+
async upsert(data) {
|
|
1508
|
+
const pk = getPrimaryKeyColumn(def);
|
|
1509
|
+
const pkValue = data[pk];
|
|
1510
|
+
if (pkValue != null) {
|
|
1511
|
+
const existing = await db.selectFrom(def.table).selectAll().where(pk, "=", pkValue).executeTakeFirst();
|
|
1512
|
+
if (existing) {
|
|
1513
|
+
const instance = def.hydrate(existing);
|
|
1514
|
+
instance.fill(data);
|
|
1515
|
+
return instance.$save();
|
|
1516
|
+
}
|
|
1517
|
+
}
|
|
1518
|
+
return insertModel(def, data);
|
|
1519
|
+
},
|
|
1189
1520
|
async updateMany(data) {
|
|
1190
1521
|
applyScopes();
|
|
1191
1522
|
assertWhereForMutation();
|
|
@@ -1214,9 +1545,9 @@ function createQueryBuilder(def, peta) {
|
|
|
1214
1545
|
for (const op of whereOps) updateQb = op(updateQb);
|
|
1215
1546
|
try {
|
|
1216
1547
|
const result = await updateQb.execute();
|
|
1217
|
-
return Number(result
|
|
1548
|
+
return Number(result[0]?.numUpdatedRows ?? 0);
|
|
1218
1549
|
} catch (e) {
|
|
1219
|
-
const { normalizeError } = await import("./errors-
|
|
1550
|
+
const { normalizeError } = await import("./errors-i-gCZnlW.mjs").then((n) => n.t);
|
|
1220
1551
|
throw normalizeError(e, def.table);
|
|
1221
1552
|
}
|
|
1222
1553
|
},
|
|
@@ -1257,9 +1588,9 @@ function createQueryBuilder(def, peta) {
|
|
|
1257
1588
|
let numDeleted = 0;
|
|
1258
1589
|
try {
|
|
1259
1590
|
const result = await deleteQb.execute();
|
|
1260
|
-
numDeleted = Number(result
|
|
1591
|
+
numDeleted = Number(result[0]?.numDeletedRows ?? 0);
|
|
1261
1592
|
} catch (e) {
|
|
1262
|
-
const { normalizeError } = await import("./errors-
|
|
1593
|
+
const { normalizeError } = await import("./errors-i-gCZnlW.mjs").then((n) => n.t);
|
|
1263
1594
|
throw normalizeError(e, def.table);
|
|
1264
1595
|
}
|
|
1265
1596
|
if (hasAfter && deletedRows.length > 0) {
|
|
@@ -1292,34 +1623,38 @@ function createQueryBuilder(def, peta) {
|
|
|
1292
1623
|
hasEffectiveWhere = values.length > 0;
|
|
1293
1624
|
return self;
|
|
1294
1625
|
},
|
|
1295
|
-
whereInPivot(column, values) {
|
|
1296
|
-
qb = qb.where(validateColumn(column), "in", values);
|
|
1297
|
-
whereOps.push((q) => q.where(validateColumn(column), "in", values));
|
|
1298
|
-
hasEffectiveWhere = values.length > 0;
|
|
1299
|
-
return self;
|
|
1300
|
-
},
|
|
1301
1626
|
has(relationName) {
|
|
1302
1627
|
const rel = def.relations[relationName];
|
|
1303
1628
|
if (!rel) throw new RelationNotFoundError(def.name, relationName);
|
|
1304
1629
|
const relatedTable = rel.relatedModelClass.table;
|
|
1305
1630
|
const fk = rel.foreignKey;
|
|
1306
1631
|
const lk = rel.localKey;
|
|
1307
|
-
const existsExpr =
|
|
1632
|
+
const existsExpr = sql([`EXISTS (SELECT 1 FROM ${relatedTable} WHERE ${relatedTable}.${fk} = ${def.table}.${lk})`]);
|
|
1308
1633
|
qb = qb.where(existsExpr);
|
|
1309
1634
|
hasEffectiveWhere = true;
|
|
1310
1635
|
return self;
|
|
1311
1636
|
},
|
|
1312
|
-
whereHas(relationName,
|
|
1313
|
-
|
|
1637
|
+
whereHas(relationName, callback) {
|
|
1638
|
+
const rel = def.relations[relationName];
|
|
1639
|
+
if (!rel) throw new RelationNotFoundError(def.name, relationName);
|
|
1640
|
+
const relatedTable = rel.relatedModelClass.table;
|
|
1641
|
+
const fk = rel.foreignKey;
|
|
1642
|
+
const lk = rel.localKey;
|
|
1643
|
+
let subQuery = db.selectFrom(relatedTable).select(sql`1`.as("dummy")).whereRef(`${relatedTable}.${fk}`, "=", `${def.table}.${lk}`);
|
|
1644
|
+
if (callback) subQuery = callback(subQuery) ?? subQuery;
|
|
1645
|
+
qb = qb.where(sql`EXISTS ${subQuery}`);
|
|
1646
|
+
hasEffectiveWhere = true;
|
|
1647
|
+
return self;
|
|
1314
1648
|
},
|
|
1315
|
-
whereDoesntHave(relationName,
|
|
1649
|
+
whereDoesntHave(relationName, callback) {
|
|
1316
1650
|
const rel = def.relations[relationName];
|
|
1317
1651
|
if (!rel) throw new RelationNotFoundError(def.name, relationName);
|
|
1318
1652
|
const relatedTable = rel.relatedModelClass.table;
|
|
1319
1653
|
const fk = rel.foreignKey;
|
|
1320
1654
|
const lk = rel.localKey;
|
|
1321
|
-
|
|
1322
|
-
|
|
1655
|
+
let subQuery = db.selectFrom(relatedTable).select(sql`1`.as("dummy")).whereRef(`${relatedTable}.${fk}`, "=", `${def.table}.${lk}`);
|
|
1656
|
+
if (callback) subQuery = callback(subQuery) ?? subQuery;
|
|
1657
|
+
qb = qb.where(sql`NOT EXISTS ${subQuery}`);
|
|
1323
1658
|
hasEffectiveWhere = true;
|
|
1324
1659
|
return self;
|
|
1325
1660
|
},
|
|
@@ -1415,6 +1750,44 @@ function createQueryBuilder(def, peta) {
|
|
|
1415
1750
|
return self;
|
|
1416
1751
|
}
|
|
1417
1752
|
//#endregion
|
|
1753
|
+
//#region src/model/computed.ts
|
|
1754
|
+
var computed_exports = /* @__PURE__ */ __exportAll({
|
|
1755
|
+
applyComputedColumnsAsync: () => applyComputedColumnsAsync,
|
|
1756
|
+
getComputedConfig: () => getComputedConfig,
|
|
1757
|
+
setComputedConfig: () => setComputedConfig
|
|
1758
|
+
});
|
|
1759
|
+
const computedConfigs = /* @__PURE__ */ new WeakMap();
|
|
1760
|
+
function setComputedConfig(def, config) {
|
|
1761
|
+
computedConfigs.set(def, config);
|
|
1762
|
+
}
|
|
1763
|
+
function getComputedConfig(def) {
|
|
1764
|
+
return computedConfigs.get(def);
|
|
1765
|
+
}
|
|
1766
|
+
/**
|
|
1767
|
+
* Apply computed columns to a set of loaded records.
|
|
1768
|
+
* Handles SQL, runtime, and batch computed columns.
|
|
1769
|
+
*/
|
|
1770
|
+
/** Resolve a ComputedConfig entry (lazy function or plain object). */
|
|
1771
|
+
function resolveComputedColumn(entry) {
|
|
1772
|
+
return typeof entry === "function" ? entry() : entry;
|
|
1773
|
+
}
|
|
1774
|
+
/**
|
|
1775
|
+
* Apply computed columns and return a promise (for async batch computes).
|
|
1776
|
+
*/
|
|
1777
|
+
async function applyComputedColumnsAsync(records, computedConfig, selectedColumns) {
|
|
1778
|
+
if (records.length === 0) return;
|
|
1779
|
+
const relevant = Object.entries(computedConfig).filter(([name]) => !selectedColumns || selectedColumns.includes(name));
|
|
1780
|
+
const batchDefs = relevant.filter(([, c]) => resolveComputedColumn(c).type === "batch");
|
|
1781
|
+
for (const [name, col] of batchDefs) if (col.batchCompute) {
|
|
1782
|
+
const values = await col.batchCompute(records);
|
|
1783
|
+
for (let i = 0; i < records.length && i < values.length; i++) records[i].set(name, values[i]);
|
|
1784
|
+
}
|
|
1785
|
+
for (const record of records) for (const [name, c] of relevant) {
|
|
1786
|
+
const col = resolveComputedColumn(c);
|
|
1787
|
+
if (col.type === "runtime" && col.compute) record.set(name, col.compute(record));
|
|
1788
|
+
}
|
|
1789
|
+
}
|
|
1790
|
+
//#endregion
|
|
1418
1791
|
//#region src/model/scopes.ts
|
|
1419
1792
|
const globalScopes = /* @__PURE__ */ new WeakMap();
|
|
1420
1793
|
function addScope(def, name, callback) {
|
|
@@ -1454,16 +1827,41 @@ function defineModel(table, config) {
|
|
|
1454
1827
|
return this.query().first();
|
|
1455
1828
|
},
|
|
1456
1829
|
async create(data) {
|
|
1457
|
-
return (await
|
|
1830
|
+
return (await Promise.resolve().then(() => save_exports)).insertModel(def, data);
|
|
1458
1831
|
},
|
|
1459
1832
|
async insert(data) {
|
|
1460
|
-
return (await
|
|
1833
|
+
return (await Promise.resolve().then(() => save_exports)).insertModel(def, data);
|
|
1834
|
+
},
|
|
1835
|
+
upsert(data) {
|
|
1836
|
+
return this.query().all().upsert(data);
|
|
1461
1837
|
},
|
|
1462
1838
|
async insertMany(dataArray) {
|
|
1463
|
-
return (await
|
|
1839
|
+
return (await Promise.resolve().then(() => save_exports)).insertManyModel(def, dataArray);
|
|
1840
|
+
},
|
|
1841
|
+
async updateMany(data, where) {
|
|
1842
|
+
let qb = this.query().all();
|
|
1843
|
+
if (where.length > 0) {
|
|
1844
|
+
const keys = Object.keys(where[0]);
|
|
1845
|
+
for (const key of keys) {
|
|
1846
|
+
const values = where.map((c) => c[key]);
|
|
1847
|
+
qb = qb.whereIn(key, values);
|
|
1848
|
+
}
|
|
1849
|
+
}
|
|
1850
|
+
return qb.updateMany(data);
|
|
1851
|
+
},
|
|
1852
|
+
async deleteMany(where) {
|
|
1853
|
+
let qb = this.query().all();
|
|
1854
|
+
if (where.length > 0) {
|
|
1855
|
+
const keys = Object.keys(where[0]);
|
|
1856
|
+
for (const key of keys) {
|
|
1857
|
+
const values = where.map((c) => c[key]);
|
|
1858
|
+
qb = qb.whereIn(key, values);
|
|
1859
|
+
}
|
|
1860
|
+
}
|
|
1861
|
+
return qb.deleteMany();
|
|
1464
1862
|
},
|
|
1465
1863
|
async update(id, data) {
|
|
1466
|
-
return (await
|
|
1864
|
+
return (await Promise.resolve().then(() => save_exports)).updateModel(def, id, data);
|
|
1467
1865
|
},
|
|
1468
1866
|
async insertGraph(data, options) {
|
|
1469
1867
|
return (await Promise.resolve().then(() => graph_exports)).insertGraph(def, data, options);
|
|
@@ -1471,6 +1869,12 @@ function defineModel(table, config) {
|
|
|
1471
1869
|
async upsertGraph(data, options) {
|
|
1472
1870
|
return (await Promise.resolve().then(() => graph_exports)).upsertGraph(def, data, options);
|
|
1473
1871
|
},
|
|
1872
|
+
async deleteGraph(idOrInstance, options) {
|
|
1873
|
+
const model = typeof idOrInstance === "object" ? idOrInstance : await def.find(idOrInstance);
|
|
1874
|
+
if (!model) return;
|
|
1875
|
+
const { deleteGraph: deleteGraphFn } = await Promise.resolve().then(() => graph_exports);
|
|
1876
|
+
return deleteGraphFn(def, model, options);
|
|
1877
|
+
},
|
|
1474
1878
|
async delete(id) {
|
|
1475
1879
|
const model = await this.findOrFail(id);
|
|
1476
1880
|
await (await Promise.resolve().then(() => delete_exports)).deleteModel(def, model);
|
|
@@ -1511,30 +1915,13 @@ function defineModel(table, config) {
|
|
|
1511
1915
|
beforeUpdate(callback) {
|
|
1512
1916
|
return addStaticHook(def, "beforeUpdate", callback);
|
|
1513
1917
|
},
|
|
1514
|
-
afterUpdate(callback) {
|
|
1515
|
-
return addStaticHook(def, "afterUpdate", callback);
|
|
1516
|
-
},
|
|
1517
|
-
beforeCreate(callback) {
|
|
1518
|
-
return addStaticHook(def, "beforeCreate", callback);
|
|
1519
|
-
},
|
|
1520
|
-
afterCreate(callback) {
|
|
1521
|
-
return addStaticHook(def, "afterCreate", callback);
|
|
1522
|
-
},
|
|
1523
|
-
beforeFind(callback) {
|
|
1524
|
-
return addStaticHook(def, "beforeFind", callback);
|
|
1525
|
-
},
|
|
1526
|
-
afterFind(callback) {
|
|
1527
|
-
return addStaticHook(def, "afterFind", callback);
|
|
1528
|
-
},
|
|
1529
1918
|
_init(orm) {
|
|
1530
1919
|
def._orm = orm;
|
|
1531
|
-
setConfig
|
|
1532
|
-
Promise.resolve().then(() => serialize_exports).then((mod) => mod.setConfig?.(def, config));
|
|
1920
|
+
setConfig(def, config);
|
|
1533
1921
|
}
|
|
1534
1922
|
};
|
|
1535
|
-
setConfig
|
|
1536
|
-
|
|
1537
|
-
if (config.computed) Promise.resolve().then(() => computed_exports).then((mod) => mod.setComputedConfig?.(def, config.computed));
|
|
1923
|
+
setConfig(def, config);
|
|
1924
|
+
if (config.computed) setComputedConfig(def, config.computed);
|
|
1538
1925
|
def.registerTimestamps = (createdAtCol, updatedAtCol) => {
|
|
1539
1926
|
registerTimestampsFor(def, createdAtCol, updatedAtCol);
|
|
1540
1927
|
};
|
|
@@ -1587,44 +1974,6 @@ var Attribute = class Attribute {
|
|
|
1587
1974
|
}
|
|
1588
1975
|
};
|
|
1589
1976
|
//#endregion
|
|
1590
|
-
//#region src/model/computed.ts
|
|
1591
|
-
var computed_exports = /* @__PURE__ */ __exportAll({
|
|
1592
|
-
applyComputedColumnsAsync: () => applyComputedColumnsAsync,
|
|
1593
|
-
getComputedConfig: () => getComputedConfig,
|
|
1594
|
-
setComputedConfig: () => setComputedConfig
|
|
1595
|
-
});
|
|
1596
|
-
const computedConfigs = /* @__PURE__ */ new WeakMap();
|
|
1597
|
-
function setComputedConfig(def, config) {
|
|
1598
|
-
computedConfigs.set(def, config);
|
|
1599
|
-
}
|
|
1600
|
-
function getComputedConfig(def) {
|
|
1601
|
-
return computedConfigs.get(def);
|
|
1602
|
-
}
|
|
1603
|
-
/**
|
|
1604
|
-
* Apply computed columns to a set of loaded records.
|
|
1605
|
-
* Handles SQL, runtime, and batch computed columns.
|
|
1606
|
-
*/
|
|
1607
|
-
/** Resolve a ComputedConfig entry (lazy function or plain object). */
|
|
1608
|
-
function resolveComputedColumn(entry) {
|
|
1609
|
-
return typeof entry === "function" ? entry() : entry;
|
|
1610
|
-
}
|
|
1611
|
-
/**
|
|
1612
|
-
* Apply computed columns and return a promise (for async batch computes).
|
|
1613
|
-
*/
|
|
1614
|
-
async function applyComputedColumnsAsync(records, computedConfig, selectedColumns) {
|
|
1615
|
-
if (records.length === 0) return;
|
|
1616
|
-
const relevant = Object.entries(computedConfig).filter(([name]) => !selectedColumns || selectedColumns.includes(name));
|
|
1617
|
-
const batchDefs = relevant.filter(([, c]) => resolveComputedColumn(c).type === "batch");
|
|
1618
|
-
for (const [name, col] of batchDefs) if (col.batchCompute) {
|
|
1619
|
-
const values = await col.batchCompute(records);
|
|
1620
|
-
for (let i = 0; i < records.length && i < values.length; i++) records[i].set(name, values[i]);
|
|
1621
|
-
}
|
|
1622
|
-
for (const record of records) for (const [name, c] of relevant) {
|
|
1623
|
-
const col = resolveComputedColumn(c);
|
|
1624
|
-
if (col.type === "runtime" && col.compute) record.set(name, col.compute(record));
|
|
1625
|
-
}
|
|
1626
|
-
}
|
|
1627
|
-
//#endregion
|
|
1628
1977
|
//#region src/model/delete.ts
|
|
1629
1978
|
var delete_exports = /* @__PURE__ */ __exportAll({
|
|
1630
1979
|
deleteModel: () => deleteModel,
|
|
@@ -1632,20 +1981,8 @@ var delete_exports = /* @__PURE__ */ __exportAll({
|
|
|
1632
1981
|
restoreModel: () => restoreModel,
|
|
1633
1982
|
trashedModel: () => trashedModel
|
|
1634
1983
|
});
|
|
1635
|
-
function getTable(def) {
|
|
1636
|
-
return def.table;
|
|
1637
|
-
}
|
|
1638
|
-
function getDb(def) {
|
|
1639
|
-
if (!def._orm) throw new ModelNotRegisteredError(def.name);
|
|
1640
|
-
return def._orm.kysely;
|
|
1641
|
-
}
|
|
1642
|
-
function getPrimaryKeyColumn$1(def) {
|
|
1643
|
-
const cols = def.columns;
|
|
1644
|
-
for (const [name, col] of Object.entries(cols)) if (col.isPrimaryKey) return name;
|
|
1645
|
-
return "id";
|
|
1646
|
-
}
|
|
1647
1984
|
async function deleteModel(def, model) {
|
|
1648
|
-
const pk = getPrimaryKeyColumn
|
|
1985
|
+
const pk = getPrimaryKeyColumn(def);
|
|
1649
1986
|
const pkValue = model.get(pk);
|
|
1650
1987
|
if (pkValue == null) throw new DatabaseError("Cannot delete model without primary key", "MISSING_ID");
|
|
1651
1988
|
const hm = getHooksFor(def);
|
|
@@ -1654,9 +1991,9 @@ async function deleteModel(def, model) {
|
|
|
1654
1991
|
const config = getSoftDeleteConfig(def);
|
|
1655
1992
|
const db = getDb(def);
|
|
1656
1993
|
try {
|
|
1657
|
-
await db.updateTable(
|
|
1994
|
+
await db.updateTable(def.table).set({ [config.column]: (/* @__PURE__ */ new Date()).toISOString() }).where(pk, "=", pkValue).execute();
|
|
1658
1995
|
} catch (e) {
|
|
1659
|
-
throw normalizeError(e,
|
|
1996
|
+
throw normalizeError(e, def.table);
|
|
1660
1997
|
}
|
|
1661
1998
|
model.set(config.column, (/* @__PURE__ */ new Date()).toISOString());
|
|
1662
1999
|
await hm.trigger("afterDelete", model);
|
|
@@ -1664,31 +2001,31 @@ async function deleteModel(def, model) {
|
|
|
1664
2001
|
await hm.trigger("beforeDelete", model);
|
|
1665
2002
|
const db = getDb(def);
|
|
1666
2003
|
try {
|
|
1667
|
-
await db.deleteFrom(
|
|
2004
|
+
await db.deleteFrom(def.table).where(pk, "=", pkValue).execute();
|
|
1668
2005
|
} catch (e) {
|
|
1669
|
-
throw normalizeError(e,
|
|
2006
|
+
throw normalizeError(e, def.table);
|
|
1670
2007
|
}
|
|
1671
2008
|
setExists(model, false);
|
|
1672
2009
|
await hm.trigger("afterDelete", model);
|
|
1673
2010
|
}
|
|
1674
2011
|
}
|
|
1675
2012
|
async function forceDeleteModel(def, model) {
|
|
1676
|
-
const pk = getPrimaryKeyColumn
|
|
2013
|
+
const pk = getPrimaryKeyColumn(def);
|
|
1677
2014
|
const pkValue = model.get(pk);
|
|
1678
2015
|
if (pkValue == null) throw new DatabaseError("Cannot delete model without primary key", "MISSING_ID");
|
|
1679
2016
|
const hm = getHooksFor(def);
|
|
1680
2017
|
await hm.trigger("beforeForceDelete", model);
|
|
1681
2018
|
const db = getDb(def);
|
|
1682
2019
|
try {
|
|
1683
|
-
await db.deleteFrom(
|
|
2020
|
+
await db.deleteFrom(def.table).where(pk, "=", pkValue).execute();
|
|
1684
2021
|
} catch (e) {
|
|
1685
|
-
throw normalizeError(e,
|
|
2022
|
+
throw normalizeError(e, def.table);
|
|
1686
2023
|
}
|
|
1687
2024
|
setExists(model, false);
|
|
1688
2025
|
await hm.trigger("afterForceDelete", model);
|
|
1689
2026
|
}
|
|
1690
2027
|
async function restoreModel(def, model) {
|
|
1691
|
-
const pk = getPrimaryKeyColumn
|
|
2028
|
+
const pk = getPrimaryKeyColumn(def);
|
|
1692
2029
|
const pkValue = model.get(pk);
|
|
1693
2030
|
if (pkValue == null) return;
|
|
1694
2031
|
if (!hasSoftDelete(def)) return;
|
|
@@ -1697,9 +2034,9 @@ async function restoreModel(def, model) {
|
|
|
1697
2034
|
const config = getSoftDeleteConfig(def);
|
|
1698
2035
|
const db = getDb(def);
|
|
1699
2036
|
try {
|
|
1700
|
-
await db.updateTable(
|
|
2037
|
+
await db.updateTable(def.table).set({ [config.column]: null }).where(pk, "=", pkValue).execute();
|
|
1701
2038
|
} catch (e) {
|
|
1702
|
-
throw normalizeError(e,
|
|
2039
|
+
throw normalizeError(e, def.table);
|
|
1703
2040
|
}
|
|
1704
2041
|
model.set(config.column, null);
|
|
1705
2042
|
setExists(model, true);
|
|
@@ -1712,14 +2049,6 @@ function trashedModel(def, model) {
|
|
|
1712
2049
|
}
|
|
1713
2050
|
//#endregion
|
|
1714
2051
|
//#region src/model/relation.ts
|
|
1715
|
-
var relation_exports = /* @__PURE__ */ __exportAll({
|
|
1716
|
-
getModelDef: () => getModelDef,
|
|
1717
|
-
loadModelRelations: () => loadModelRelations
|
|
1718
|
-
});
|
|
1719
|
-
const modelDefs = /* @__PURE__ */ new WeakMap();
|
|
1720
|
-
function getModelDef(instance) {
|
|
1721
|
-
return modelDefs.get(instance);
|
|
1722
|
-
}
|
|
1723
2052
|
async function loadModelRelations(model, def, ...relations) {
|
|
1724
2053
|
const { EagerLoader } = await Promise.resolve().then(() => eager_exports);
|
|
1725
2054
|
const loader = new EagerLoader();
|
|
@@ -1727,18 +2056,13 @@ async function loadModelRelations(model, def, ...relations) {
|
|
|
1727
2056
|
}
|
|
1728
2057
|
//#endregion
|
|
1729
2058
|
//#region src/model/serialize.ts
|
|
1730
|
-
var serialize_exports = /* @__PURE__ */ __exportAll({
|
|
1731
|
-
getConfig: () => getConfig,
|
|
1732
|
-
modelToJSON: () => modelToJSON,
|
|
1733
|
-
setConfig: () => setConfig
|
|
1734
|
-
});
|
|
1735
2059
|
const VISITED = /* @__PURE__ */ new WeakSet();
|
|
1736
2060
|
/** Type guard: checks if a value looks like a SerializableModel. */
|
|
1737
2061
|
function isSerializableModel(value) {
|
|
1738
2062
|
return value !== null && typeof value === "object" && typeof value.$toJSON === "function";
|
|
1739
2063
|
}
|
|
1740
2064
|
function modelToJSON(def, model) {
|
|
1741
|
-
const config = getConfig
|
|
2065
|
+
const config = getConfig(def);
|
|
1742
2066
|
const state = getState(model);
|
|
1743
2067
|
const result = {};
|
|
1744
2068
|
const attributes = state.attributes;
|
|
@@ -1785,13 +2109,6 @@ function modelToJSON(def, model) {
|
|
|
1785
2109
|
}
|
|
1786
2110
|
return result;
|
|
1787
2111
|
}
|
|
1788
|
-
const configMap = /* @__PURE__ */ new WeakMap();
|
|
1789
|
-
function setConfig(def, config) {
|
|
1790
|
-
configMap.set(def, config);
|
|
1791
|
-
}
|
|
1792
|
-
function getConfig(def) {
|
|
1793
|
-
return configMap.get(def);
|
|
1794
|
-
}
|
|
1795
2112
|
//#endregion
|
|
1796
2113
|
//#region src/relations/related-query.ts
|
|
1797
2114
|
/**
|
|
@@ -1976,7 +2293,14 @@ function createORM(config) {
|
|
|
1976
2293
|
await kysely.destroy();
|
|
1977
2294
|
},
|
|
1978
2295
|
async transaction(fn) {
|
|
1979
|
-
return kysely.transaction().execute((trx) =>
|
|
2296
|
+
return kysely.transaction().execute(async (trx) => {
|
|
2297
|
+
this._trx = trx;
|
|
2298
|
+
try {
|
|
2299
|
+
return await fn(this);
|
|
2300
|
+
} finally {
|
|
2301
|
+
delete this._trx;
|
|
2302
|
+
}
|
|
2303
|
+
});
|
|
1980
2304
|
},
|
|
1981
2305
|
get models() {
|
|
1982
2306
|
return modelMap;
|
|
@@ -1985,10 +2309,17 @@ function createORM(config) {
|
|
|
1985
2309
|
return modelMap.get(name);
|
|
1986
2310
|
},
|
|
1987
2311
|
async discover(pattern) {
|
|
1988
|
-
const entries =
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
2312
|
+
const entries = [];
|
|
2313
|
+
const glob = new Bun.Glob("**/*.ts");
|
|
2314
|
+
const starIdx = pattern.lastIndexOf("*");
|
|
2315
|
+
const baseDir = starIdx >= 0 ? pattern.slice(0, pattern.lastIndexOf("/", starIdx)) : pattern;
|
|
2316
|
+
try {
|
|
2317
|
+
for await (const match of glob.scan({
|
|
2318
|
+
cwd: baseDir,
|
|
2319
|
+
absolute: true,
|
|
2320
|
+
onlyFiles: true
|
|
2321
|
+
})) entries.push(match);
|
|
2322
|
+
} catch {}
|
|
1992
2323
|
if (entries.length === 0) throw new Error(`discover: no files matched pattern "${pattern}"`);
|
|
1993
2324
|
const models = [];
|
|
1994
2325
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -2048,12 +2379,6 @@ function createPaginator(items, total, perPage, currentPage) {
|
|
|
2048
2379
|
get onLastPage() {
|
|
2049
2380
|
return currentPage >= lastPage;
|
|
2050
2381
|
},
|
|
2051
|
-
get count() {
|
|
2052
|
-
return items.length;
|
|
2053
|
-
},
|
|
2054
|
-
map(fn) {
|
|
2055
|
-
return items.map(fn);
|
|
2056
|
-
},
|
|
2057
2382
|
toJSON() {
|
|
2058
2383
|
return {
|
|
2059
2384
|
data: collection.toJSON(),
|
|
@@ -2073,11 +2398,6 @@ function createPaginator(items, total, perPage, currentPage) {
|
|
|
2073
2398
|
}
|
|
2074
2399
|
//#endregion
|
|
2075
2400
|
//#region src/plugins/soft-deletes.ts
|
|
2076
|
-
let _hooksMod = null;
|
|
2077
|
-
async function getHooksMod() {
|
|
2078
|
-
if (!_hooksMod) _hooksMod = await import("./hooks-BD0xy7uw.mjs").then((n) => n.i);
|
|
2079
|
-
return _hooksMod;
|
|
2080
|
-
}
|
|
2081
2401
|
/**
|
|
2082
2402
|
* Plugin that enables soft-delete behavior on a model.
|
|
2083
2403
|
* Sets `deletedAt` on delete, automatically filters out deleted records.
|
|
@@ -2091,24 +2411,20 @@ async function getHooksMod() {
|
|
|
2091
2411
|
function softDeletes(opts) {
|
|
2092
2412
|
const column = opts?.column ?? "deletedAt";
|
|
2093
2413
|
return (def) => {
|
|
2094
|
-
|
|
2414
|
+
registerSoftDeletesFor(def, column);
|
|
2095
2415
|
def.on("beforeDelete", async (model) => {
|
|
2096
2416
|
const pk = getPrimaryKeyColumn(def);
|
|
2097
2417
|
const pkValue = model.get(pk);
|
|
2098
2418
|
if (pkValue == null) return;
|
|
2099
|
-
const db = def
|
|
2100
|
-
if (!db) return;
|
|
2419
|
+
const db = getDb(def);
|
|
2101
2420
|
try {
|
|
2102
2421
|
await db.updateTable(def.table).set({ [column]: (/* @__PURE__ */ new Date()).toISOString() }).where(pk, "=", pkValue).execute();
|
|
2103
|
-
} catch {
|
|
2422
|
+
} catch (e) {
|
|
2423
|
+
if (!isUniqueConstraintError(e)) throw normalizeError(e, def.table);
|
|
2424
|
+
}
|
|
2104
2425
|
});
|
|
2105
2426
|
};
|
|
2106
2427
|
}
|
|
2107
|
-
function getPrimaryKeyColumn(def) {
|
|
2108
|
-
const cols = def.columns;
|
|
2109
|
-
for (const [name, col] of Object.entries(cols)) if (col.isPrimaryKey) return name;
|
|
2110
|
-
return "id";
|
|
2111
|
-
}
|
|
2112
2428
|
//#endregion
|
|
2113
2429
|
//#region src/plugins/timestamps.ts
|
|
2114
2430
|
/**
|
|
@@ -2156,34 +2472,14 @@ function ulid() {
|
|
|
2156
2472
|
}
|
|
2157
2473
|
//#endregion
|
|
2158
2474
|
//#region src/relations/has-many.ts
|
|
2159
|
-
const THUNK_CACHE$2 = /* @__PURE__ */ new WeakMap();
|
|
2160
|
-
function resolveThunk$2(thunk) {
|
|
2161
|
-
let cls = THUNK_CACHE$2.get(thunk);
|
|
2162
|
-
if (!cls) {
|
|
2163
|
-
cls = thunk();
|
|
2164
|
-
THUNK_CACHE$2.set(thunk, cls);
|
|
2165
|
-
}
|
|
2166
|
-
return cls;
|
|
2167
|
-
}
|
|
2168
2475
|
function guessForeignKey(modelDef) {
|
|
2169
2476
|
const table = modelDef.table;
|
|
2170
2477
|
return `${table.endsWith("s") ? table.slice(0, -1) : table}Id`;
|
|
2171
2478
|
}
|
|
2172
|
-
function groupByArray$1(items, key) {
|
|
2173
|
-
const result = {};
|
|
2174
|
-
for (const item of items) {
|
|
2175
|
-
const v = item.get(key);
|
|
2176
|
-
if (v == null) continue;
|
|
2177
|
-
const k = String(v);
|
|
2178
|
-
if (!result[k]) result[k] = [];
|
|
2179
|
-
result[k].push(item);
|
|
2180
|
-
}
|
|
2181
|
-
return result;
|
|
2182
|
-
}
|
|
2183
2479
|
function hasMany(relatedThunk, options = {}) {
|
|
2184
2480
|
let _related;
|
|
2185
2481
|
function getRelated() {
|
|
2186
|
-
if (!_related) _related = resolveThunk
|
|
2482
|
+
if (!_related) _related = resolveThunk(relatedThunk) ?? void 0;
|
|
2187
2483
|
if (!_related) throw new Error(`Cannot resolve hasMany relation — related model not found`);
|
|
2188
2484
|
return _related;
|
|
2189
2485
|
}
|
|
@@ -2214,7 +2510,7 @@ function hasMany(relatedThunk, options = {}) {
|
|
|
2214
2510
|
else qb.where(foreignKey, "=", -1);
|
|
2215
2511
|
},
|
|
2216
2512
|
match(models, results, relationName) {
|
|
2217
|
-
const grouped = groupByArray
|
|
2513
|
+
const grouped = groupByArray(results, foreignKey);
|
|
2218
2514
|
for (const model of models) {
|
|
2219
2515
|
const key = String(model.get(localKey));
|
|
2220
2516
|
model.$setRelation(relationName, grouped[key] ?? []);
|
|
@@ -2242,7 +2538,7 @@ function hasOne(relatedThunk, options = {}) {
|
|
|
2242
2538
|
Object.defineProperty(result, key, desc);
|
|
2243
2539
|
}
|
|
2244
2540
|
result.match = function match(models, results, relationName) {
|
|
2245
|
-
const grouped = groupByArray
|
|
2541
|
+
const grouped = groupByArray(results, base.foreignKey);
|
|
2246
2542
|
for (const model of models) {
|
|
2247
2543
|
const key = String(model.get(base.localKey));
|
|
2248
2544
|
model.$setRelation(relationName, (grouped[key] ?? [])[0] ?? null);
|
|
@@ -2254,7 +2550,7 @@ function hasOne(relatedThunk, options = {}) {
|
|
|
2254
2550
|
return result;
|
|
2255
2551
|
}
|
|
2256
2552
|
function belongsTo(relatedThunk, options = {}) {
|
|
2257
|
-
const related = resolveThunk
|
|
2553
|
+
const related = resolveThunk(relatedThunk);
|
|
2258
2554
|
const foreignKey = options.foreignKey ?? guessForeignKey(related);
|
|
2259
2555
|
const localKey = options.localKey ?? "id";
|
|
2260
2556
|
return {
|
|
@@ -2301,24 +2597,15 @@ function belongsTo(relatedThunk, options = {}) {
|
|
|
2301
2597
|
}
|
|
2302
2598
|
//#endregion
|
|
2303
2599
|
//#region src/relations/many-to-many.ts
|
|
2304
|
-
const THUNK_CACHE$1 = /* @__PURE__ */ new WeakMap();
|
|
2305
2600
|
/** Stores pivot data for many-to-many relation results (instead of attaching to the model). */
|
|
2306
2601
|
const pivotData = /* @__PURE__ */ new WeakMap();
|
|
2307
|
-
function resolveThunk$1(thunk) {
|
|
2308
|
-
let cls = THUNK_CACHE$1.get(thunk);
|
|
2309
|
-
if (!cls) {
|
|
2310
|
-
cls = thunk();
|
|
2311
|
-
THUNK_CACHE$1.set(thunk, cls);
|
|
2312
|
-
}
|
|
2313
|
-
return cls;
|
|
2314
|
-
}
|
|
2315
2602
|
function snakeCase(str) {
|
|
2316
2603
|
return str.replace(/[A-Z]/g, (c) => `_${c.toLowerCase()}`).replace(/^_/, "");
|
|
2317
2604
|
}
|
|
2318
2605
|
function manyToMany(relatedThunk, options) {
|
|
2319
2606
|
let _related;
|
|
2320
2607
|
function getRelated() {
|
|
2321
|
-
if (!_related) _related = resolveThunk
|
|
2608
|
+
if (!_related) _related = resolveThunk(relatedThunk) ?? void 0;
|
|
2322
2609
|
if (!_related) throw new Error(`Cannot resolve manyToMany relation — related model not found`);
|
|
2323
2610
|
return _related;
|
|
2324
2611
|
}
|
|
@@ -2404,11 +2691,11 @@ function manyToMany(relatedThunk, options) {
|
|
|
2404
2691
|
};
|
|
2405
2692
|
}
|
|
2406
2693
|
function hasManyThrough(relatedThunk, throughThunk, options = {}) {
|
|
2407
|
-
const related = resolveThunk
|
|
2408
|
-
const through = resolveThunk
|
|
2694
|
+
const related = resolveThunk(relatedThunk);
|
|
2695
|
+
const through = resolveThunk(throughThunk);
|
|
2409
2696
|
const foreignKey = options.foreignKey ?? `${snakeCase(through.table)}Id`;
|
|
2410
2697
|
const localKey = options.localKey ?? "id";
|
|
2411
|
-
const throughForeignKey = options.throughForeignKey ?? `${snakeCase(resolveThunk
|
|
2698
|
+
const throughForeignKey = options.throughForeignKey ?? `${snakeCase(resolveThunk(relatedThunk).table)}Id`;
|
|
2412
2699
|
const throughLocalKey = options.throughLocalKey ?? "id";
|
|
2413
2700
|
return {
|
|
2414
2701
|
type: "hasManyThrough",
|
|
@@ -2467,26 +2754,6 @@ function hasManyThrough(relatedThunk, throughThunk, options = {}) {
|
|
|
2467
2754
|
}
|
|
2468
2755
|
//#endregion
|
|
2469
2756
|
//#region src/relations/morph.ts
|
|
2470
|
-
function groupByArray(items, key) {
|
|
2471
|
-
const result = {};
|
|
2472
|
-
for (const item of items) {
|
|
2473
|
-
const v = item.get(key);
|
|
2474
|
-
if (v == null) continue;
|
|
2475
|
-
const k = String(v);
|
|
2476
|
-
if (!result[k]) result[k] = [];
|
|
2477
|
-
result[k].push(item);
|
|
2478
|
-
}
|
|
2479
|
-
return result;
|
|
2480
|
-
}
|
|
2481
|
-
const THUNK_CACHE = /* @__PURE__ */ new WeakMap();
|
|
2482
|
-
function resolveThunk(thunk) {
|
|
2483
|
-
let cls = THUNK_CACHE.get(thunk);
|
|
2484
|
-
if (!cls) {
|
|
2485
|
-
cls = thunk();
|
|
2486
|
-
THUNK_CACHE.set(thunk, cls);
|
|
2487
|
-
}
|
|
2488
|
-
return cls;
|
|
2489
|
-
}
|
|
2490
2757
|
/**
|
|
2491
2758
|
* Resolve the related model for a MorphTo relation given a parent instance.
|
|
2492
2759
|
* Looks up the parent's `{name}Type` column value in the relation's morphMap
|
|
@@ -2554,12 +2821,12 @@ function defineMorphTo(options) {
|
|
|
2554
2821
|
*/
|
|
2555
2822
|
query(parent) {
|
|
2556
2823
|
const typeValue = parent.get(morphType);
|
|
2557
|
-
if (!typeValue) throw new Error(`Cannot resolve morphTo "${options.name}": "${morphType}" is null on ${
|
|
2824
|
+
if (!typeValue) throw new Error(`Cannot resolve morphTo "${options.name}": "${morphType}" is null on ${parent.constructor?.name ?? "model"}`);
|
|
2558
2825
|
const thunk = morphMap[typeValue];
|
|
2559
2826
|
if (!thunk) throw new Error(`No model registered for morph type "${typeValue}" in morphTo "${options.name}". Available types: ${Object.keys(morphMap).join(", ") || "(none)"}`);
|
|
2560
2827
|
const relatedDef = resolveThunk(thunk);
|
|
2561
2828
|
const id = parent.get(morphId);
|
|
2562
|
-
if (id == null) throw new Error(`Cannot resolve morphTo "${options.name}": "${morphId}" is null on ${
|
|
2829
|
+
if (id == null) throw new Error(`Cannot resolve morphTo "${options.name}": "${morphId}" is null on ${parent.constructor?.name ?? "model"}`);
|
|
2563
2830
|
return relatedDef.query().where("id", "=", id);
|
|
2564
2831
|
},
|
|
2565
2832
|
addEagerConstraints(_query, _models) {},
|
|
@@ -2657,29 +2924,6 @@ function defineMorphMany(options) {
|
|
|
2657
2924
|
}
|
|
2658
2925
|
};
|
|
2659
2926
|
}
|
|
2660
|
-
/**
|
|
2661
|
-
* Define a polymorphic hasOne relationship.
|
|
2662
|
-
*/
|
|
2663
|
-
function defineMorphOne(options) {
|
|
2664
|
-
const base = defineMorphMany(options);
|
|
2665
|
-
return {
|
|
2666
|
-
...base,
|
|
2667
|
-
type: "hasOne",
|
|
2668
|
-
async getResults(parent) {
|
|
2669
|
-
return (await base.getResults(parent))[0] ?? null;
|
|
2670
|
-
},
|
|
2671
|
-
match(models, results, relationName) {
|
|
2672
|
-
const grouped = groupByArray(results, base.foreignKey);
|
|
2673
|
-
for (const model of models) {
|
|
2674
|
-
const related = grouped[String(model.get("id"))] ?? [];
|
|
2675
|
-
model.$setRelation(relationName, related[0] ?? null);
|
|
2676
|
-
}
|
|
2677
|
-
}
|
|
2678
|
-
};
|
|
2679
|
-
}
|
|
2680
|
-
function defName(instance) {
|
|
2681
|
-
return instance.constructor?.name ?? "model";
|
|
2682
|
-
}
|
|
2683
2927
|
//#endregion
|
|
2684
2928
|
//#region src/repo/index.ts
|
|
2685
2929
|
/**
|
|
@@ -2742,4 +2986,4 @@ function createRepo(model, methods) {
|
|
|
2742
2986
|
} });
|
|
2743
2987
|
}
|
|
2744
2988
|
//#endregion
|
|
2745
|
-
export { Attribute, DatabaseError, ModelNotFoundError, ModelNotRegisteredError, RelationNotAllowedError, RelationNotFoundError, ValidationError, belongsTo, createArkTypeSchemaConfig, createCollection, createColumn, createColumnTypes, createDb, createHookManager, createORM, createORM as createPeta, createPaginator, createQueryBuilder, createRepo, defineModel, defineMorphMany,
|
|
2989
|
+
export { Attribute, DatabaseError, ModelNotFoundError, ModelNotRegisteredError, RelationNotAllowedError, RelationNotFoundError, ValidationError, belongsTo, createArkTypeSchemaConfig, createCollection, createColumn, createColumnTypes, createDb, createHookManager, createORM, createORM as createPeta, createPaginator, createQueryBuilder, createRepo, defineModel, defineMorphMany, defineMorphTo, deleteGraph, hasMany, hasManyThrough, hasOne, manyToMany, eager_exports as n, normalizeError, petaMiddleware, petaPlugin, resolveMorphRelation, softDeletes, t, timestamps, ulid };
|