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/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-PFmrQHyM.mjs";
3
- import { a as ModelNotRegisteredError, c as ValidationError, i as ModelNotFoundError, n as normalizeError, o as RelationNotAllowedError, r as DatabaseError, s as RelationNotFoundError } from "./errors-sfFJolfu.mjs";
4
- import { a as registerSoftDeletesFor, n as getSoftDeleteConfig, o as registerTimestampsFor, r as hasSoftDelete, s as createHookManager, t as getHooksFor } from "./hooks-BD0xy7uw.mjs";
5
- import { a as castValue, r as initRuntime, t as createInstance } from "./factory-BBvIMQuc.mjs";
6
- import { a as getRawRelations, d as setExists, o as getState } from "./state-LtlHp6XV.mjs";
7
- import { a as setConfig$1, n as reloadModel, r as saveModel, t as getConfig$1 } from "./save-D5UKXvqC.mjs";
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([...extractProblems(result).entries()].map(([path, msgs]) => `${path}: ${msgs.join(", ")}`).join("; "));
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([...extractProblems(e.arkErrors).entries()].map(([path, msgs]) => `${path}: ${msgs.join(", ")}`).join("; "));
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
- const val = c.args[0];
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 (isMorphRelation(relation)) 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.`);
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/morph.ts
350
- /** Whether this relation is a MorphTo (polymorphic belongsTo) */
351
- function isMorphToRelation(relation) {
352
- return relation._morphMap !== void 0;
353
- }
354
- /** Whether this relation is a MorphMany or MorphOne (polymorphic hasMany/hasOne) */
355
- function isMorphManyRelation(relation) {
356
- return relation._morphType !== void 0 && !isMorphToRelation(relation);
357
- }
358
- /** Get the morph type column name (e.g. "commentableType") from a morph relation */
359
- function getMorphType(relation) {
360
- return relation._morphType;
361
- }
362
- /** Get the morph type value (e.g. "morph_posts") from a MorphMany/MorphOne relation */
363
- function getMorphTypeValue(relation) {
364
- return relation._morphTypeValue;
365
- }
366
- /** Get the morph id column name (e.g. "commentableId") from a morph relation */
367
- function getMorphId(relation) {
368
- return relation._morphId;
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
- return cls;
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$2(def);
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 (isMorphToRelation(relation)) return processMorphTo(relation, op, options, context, path, parentColumnData);
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 = getMorphType(relation);
618
- const morphId = getMorphId(relation);
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$3(thunk);
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 (isMorphManyRelation(relation)) {
671
- const typeCol = getMorphType(relation);
672
- const typeVal = getMorphTypeValue(relation);
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$1(relatedDef);
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$2(def);
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$2(relatedDef);
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$2(relatedDef)] ?? item.id;
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$2(relatedDef));
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 (isMorphManyRelation(relation)) {
833
- const typeCol = getMorphType(relation);
834
- const typeVal = getMorphTypeValue(relation);
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$1(relatedDef);
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[getPrimaryKeyColumn$2(relatedDef)] ?? item.id;
1217
+ const itemId = item[pkCol] ?? item.id;
875
1218
  if (itemId != null) {
876
1219
  incomingIds.add(itemId);
877
- if (!isRelPathAllowed(relNameFromPath(path), options.noUpdate)) {
878
- const existing = await relatedDef.find(itemId);
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
- const targetId = await resolveTargetId(relatedDef, target);
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-PFmrQHyM.mjs").then((n) => n.t);
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
- const alias = `${relation}_count`;
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
- const alias = `${relation}_sum_${column}`;
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.numUpdatedRows ?? 0);
1548
+ return Number(result[0]?.numUpdatedRows ?? 0);
1218
1549
  } catch (e) {
1219
- const { normalizeError } = await import("./errors-sfFJolfu.mjs").then((n) => n.t);
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.numDeletedRows ?? 0);
1591
+ numDeleted = Number(result[0]?.numDeletedRows ?? 0);
1261
1592
  } catch (e) {
1262
- const { normalizeError } = await import("./errors-sfFJolfu.mjs").then((n) => n.t);
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 = rawSql(`EXISTS (SELECT 1 FROM ${relatedTable} WHERE ${relatedTable}.${fk} = ${def.table}.${lk})`);
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, _callback) {
1313
- return self.has(relationName);
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, _callback) {
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
- const notExistsExpr = rawSql(`NOT EXISTS (SELECT 1 FROM ${relatedTable} WHERE ${relatedTable}.${fk} = ${def.table}.${lk})`);
1322
- qb = qb.where(notExistsExpr);
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 import("./save-D5UKXvqC.mjs").then((n) => n.i)).insertModel(def, data);
1830
+ return (await Promise.resolve().then(() => save_exports)).insertModel(def, data);
1458
1831
  },
1459
1832
  async insert(data) {
1460
- return (await import("./save-D5UKXvqC.mjs").then((n) => n.i)).insertModel(def, data);
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 import("./save-D5UKXvqC.mjs").then((n) => n.i)).insertManyModel(def, dataArray);
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 import("./save-D5UKXvqC.mjs").then((n) => n.i)).updateModel(def, id, data);
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$1(def, config);
1532
- Promise.resolve().then(() => serialize_exports).then((mod) => mod.setConfig?.(def, config));
1920
+ setConfig(def, config);
1533
1921
  }
1534
1922
  };
1535
- setConfig$1(def, config);
1536
- Promise.resolve().then(() => serialize_exports).then((mod) => mod.setConfig?.(def, config));
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$1(def);
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(getTable(def)).set({ [config.column]: (/* @__PURE__ */ new Date()).toISOString() }).where(pk, "=", pkValue).execute();
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, getTable(def));
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(getTable(def)).where(pk, "=", pkValue).execute();
2004
+ await db.deleteFrom(def.table).where(pk, "=", pkValue).execute();
1668
2005
  } catch (e) {
1669
- throw normalizeError(e, getTable(def));
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$1(def);
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(getTable(def)).where(pk, "=", pkValue).execute();
2020
+ await db.deleteFrom(def.table).where(pk, "=", pkValue).execute();
1684
2021
  } catch (e) {
1685
- throw normalizeError(e, getTable(def));
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$1(def);
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(getTable(def)).set({ [config.column]: null }).where(pk, "=", pkValue).execute();
2037
+ await db.updateTable(def.table).set({ [config.column]: null }).where(pk, "=", pkValue).execute();
1701
2038
  } catch (e) {
1702
- throw normalizeError(e, getTable(def));
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$1(def) ?? getConfig(def);
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) => fn(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 = await (await import("fast-glob")).glob(pattern, {
1989
- absolute: true,
1990
- onlyFiles: true
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
- getHooksMod().then((mod) => mod.registerSoftDeletesFor(def, column));
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._orm?.kysely;
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$2(relatedThunk) ?? void 0;
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$1(results, foreignKey);
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$1(results, base.foreignKey);
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$2(relatedThunk);
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$1(relatedThunk) ?? void 0;
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$1(relatedThunk);
2408
- const through = resolveThunk$1(throughThunk);
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$1(relatedThunk).table)}Id`;
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 ${defName(parent)}`);
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 ${defName(parent)}`);
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, defineMorphOne, defineMorphTo, hasMany, hasManyThrough, hasOne, eager_exports as i, manyToMany, relation_exports as n, normalizeError, delete_exports as r, resolveMorphRelation, softDeletes, t, timestamps, ulid };
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 };