arkormx 2.6.0 → 2.7.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.
@@ -1,5 +1,5 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
- const require_relationship = require('../relationship-D9u7BlWh.cjs');
2
+ const require_relationship = require('../relationship-B8FaJYIx.cjs');
3
3
 
4
4
  exports.BelongsToManyRelation = require_relationship.BelongsToManyRelation;
5
5
  exports.BelongsToRelation = require_relationship.BelongsToRelation;
@@ -1,2 +1,2 @@
1
- import { Co as BelongsToRelation, Do as RelationTableLoader, Eo as Relation, So as HasManyRelation, To as BelongsToManyRelation, _o as MorphOneRelation, bo as HasOneRelation, go as MorphToManyRelation, ho as MorphToRelation, mo as SetBasedEagerLoader, vo as MorphManyRelation, wo as SingleResultRelation, xo as HasManyThroughRelation, yo as HasOneThroughRelation } from "../index-CpZK_wI9.cjs";
1
+ import { Ao as MorphManyRelation, Do as MorphToRelation, Eo as SetBasedEagerLoader, Fo as BelongsToRelation, Io as SingleResultRelation, Lo as BelongsToManyRelation, Mo as HasOneRelation, No as HasManyThroughRelation, Oo as MorphToManyRelation, Po as HasManyRelation, Ro as Relation, jo as HasOneThroughRelation, ko as MorphOneRelation, zo as RelationTableLoader } from "../index-Dnn0lsDy.cjs";
2
2
  export { BelongsToManyRelation, BelongsToRelation, HasManyRelation, HasManyThroughRelation, HasOneRelation, HasOneThroughRelation, MorphManyRelation, MorphOneRelation, MorphToManyRelation, MorphToRelation, Relation, RelationTableLoader, SetBasedEagerLoader, SingleResultRelation };
@@ -1,2 +1,2 @@
1
- import { Co as BelongsToRelation, Do as RelationTableLoader, Eo as Relation, So as HasManyRelation, To as BelongsToManyRelation, _o as MorphOneRelation, bo as HasOneRelation, go as MorphToManyRelation, ho as MorphToRelation, mo as SetBasedEagerLoader, vo as MorphManyRelation, wo as SingleResultRelation, xo as HasManyThroughRelation, yo as HasOneThroughRelation } from "../index-DjiQjqkW.mjs";
1
+ import { Ao as MorphManyRelation, Do as MorphToRelation, Eo as SetBasedEagerLoader, Fo as BelongsToRelation, Io as SingleResultRelation, Lo as BelongsToManyRelation, Mo as HasOneRelation, No as HasManyThroughRelation, Oo as MorphToManyRelation, Po as HasManyRelation, Ro as Relation, jo as HasOneThroughRelation, ko as MorphOneRelation, zo as RelationTableLoader } from "../index-C77nMASp.mjs";
2
2
  export { BelongsToManyRelation, BelongsToRelation, HasManyRelation, HasManyThroughRelation, HasOneRelation, HasOneThroughRelation, MorphManyRelation, MorphOneRelation, MorphToManyRelation, MorphToRelation, Relation, RelationTableLoader, SetBasedEagerLoader, SingleResultRelation };
@@ -1,3 +1,3 @@
1
- import { Nn as SetBasedEagerLoader, Pn as RelationTableLoader, a as HasOneThroughRelation, c as HasManyRelation, d as BelongsToManyRelation, f as Relation, i as MorphManyRelation, l as BelongsToRelation, n as MorphToManyRelation, o as HasOneRelation, r as MorphOneRelation, s as HasManyThroughRelation, t as MorphToRelation, u as SingleResultRelation } from "../relationship-C5hos4aH.mjs";
1
+ import { Nn as SetBasedEagerLoader, Pn as RelationTableLoader, a as HasOneThroughRelation, c as HasManyRelation, d as BelongsToManyRelation, f as Relation, i as MorphManyRelation, l as BelongsToRelation, n as MorphToManyRelation, o as HasOneRelation, r as MorphOneRelation, s as HasManyThroughRelation, t as MorphToRelation, u as SingleResultRelation } from "../relationship-BuwKUTOb.mjs";
2
2
 
3
3
  export { BelongsToManyRelation, BelongsToRelation, HasManyRelation, HasManyThroughRelation, HasOneRelation, HasOneThroughRelation, MorphManyRelation, MorphOneRelation, MorphToManyRelation, MorphToRelation, Relation, RelationTableLoader, SetBasedEagerLoader, SingleResultRelation };
@@ -205,6 +205,12 @@ var SetBasedEagerLoader = class SetBasedEagerLoader {
205
205
  case "morphTo":
206
206
  await this.loadMorphTo(name, metadata, constraint);
207
207
  return;
208
+ case "morphOne":
209
+ await this.loadMorphOne(name, resolver, metadata, constraint);
210
+ return;
211
+ case "morphMany":
212
+ await this.loadMorphMany(name, metadata, constraint);
213
+ return;
208
214
  default: await this.loadIndividually(name, resolver, constraint);
209
215
  }
210
216
  }
@@ -631,6 +637,86 @@ var SetBasedEagerLoader = class SetBasedEagerLoader {
631
637
  });
632
638
  }
633
639
  /**
640
+ * Loads a polymorphic one-to-one ("morph one") relationship for the set of
641
+ * models. See {@link loadMorphChildren} for the batching strategy.
642
+ *
643
+ * @param name
644
+ * @param resolver
645
+ * @param metadata
646
+ * @param constraint
647
+ */
648
+ async loadMorphOne(name, resolver, metadata, constraint) {
649
+ const relatedByKey = await this.loadMorphChildren(metadata, constraint);
650
+ this.models.forEach((model) => {
651
+ const bucket = relatedByKey.get(this.morphParentKey(model, metadata.localKey));
652
+ model.setLoadedRelation(name, bucket?.[0] ?? this.resolveSingleDefault(resolver, model));
653
+ });
654
+ }
655
+ /**
656
+ * Loads a polymorphic one-to-many ("morph many") relationship for the set of
657
+ * models. See {@link loadMorphChildren} for the batching strategy.
658
+ *
659
+ * @param name
660
+ * @param metadata
661
+ * @param constraint
662
+ */
663
+ async loadMorphMany(name, metadata, constraint) {
664
+ const relatedByKey = await this.loadMorphChildren(metadata, constraint);
665
+ this.models.forEach((model) => {
666
+ const bucket = relatedByKey.get(this.morphParentKey(model, metadata.localKey)) ?? [];
667
+ model.setLoadedRelation(name, new ArkormCollection(bucket));
668
+ });
669
+ }
670
+ /**
671
+ * Batch-loads the children for a morphOne/morphMany relationship. Parents are
672
+ * grouped by their own morph type (constructor name), so each distinct type
673
+ * runs a single query constrained by `idColumn IN (...) AND typeColumn = type`
674
+ * instead of one query per parent row. This also supports a heterogeneous
675
+ * parent set (e.g. the result of a nested load after a morphTo).
676
+ *
677
+ * @param metadata
678
+ * @param constraint
679
+ * @returns A map of `"{type}:{localKey}" -> related[]`.
680
+ */
681
+ async loadMorphChildren(metadata, constraint) {
682
+ const keysByType = /* @__PURE__ */ new Map();
683
+ const seenByType = /* @__PURE__ */ new Map();
684
+ this.models.forEach((model) => {
685
+ const morphType = this.morphTypeOf(model);
686
+ const localValue = model.getAttribute(metadata.localKey);
687
+ if (morphType.length === 0 || localValue == null) return;
688
+ const keys = keysByType.get(morphType) ?? [];
689
+ const seen = seenByType.get(morphType) ?? /* @__PURE__ */ new Set();
690
+ const lookupKey = this.toLookupKey(localValue);
691
+ if (!seen.has(lookupKey)) {
692
+ seen.add(lookupKey);
693
+ keys.push(localValue);
694
+ }
695
+ keysByType.set(morphType, keys);
696
+ seenByType.set(morphType, seen);
697
+ });
698
+ const relatedByKey = /* @__PURE__ */ new Map();
699
+ await Promise.all(Array.from(keysByType.entries()).map(async ([morphType, keys]) => {
700
+ let query = metadata.relatedModel.query().whereIn(metadata.morphIdColumn, keys).where({ [metadata.morphTypeColumn]: morphType });
701
+ query = this.applyConstraint(query, constraint);
702
+ (await query.get()).all().forEach((related) => {
703
+ const value = this.readModelAttribute(related, metadata.morphIdColumn);
704
+ if (value == null) return;
705
+ const key = `${morphType}:${this.toLookupKey(value)}`;
706
+ const bucket = relatedByKey.get(key) ?? [];
707
+ bucket.push(related);
708
+ relatedByKey.set(key, bucket);
709
+ });
710
+ }));
711
+ return relatedByKey;
712
+ }
713
+ morphParentKey(model, localKey) {
714
+ return `${this.morphTypeOf(model)}:${this.toLookupKey(model.getAttribute(localKey))}`;
715
+ }
716
+ morphTypeOf(model) {
717
+ return model.constructor?.name ?? "";
718
+ }
719
+ /**
634
720
  * Fallback method to load relationships individually for each model when the
635
721
  * relationship type is not supported for set-based loading.
636
722
  *
@@ -1043,6 +1129,10 @@ var PrimaryKeyGenerationPlanner = class {
1043
1129
  //#endregion
1044
1130
  //#region src/database/TableBuilder.ts
1045
1131
  const PRISMA_ENUM_MEMBER_REGEX$1 = /^[A-Za-z][A-Za-z0-9_]*$/;
1132
+ const defaultTimestampNames = {
1133
+ createdAt: "createdAt",
1134
+ updatedAt: "updatedAt"
1135
+ };
1046
1136
  const normalizeEnumMember = (columnName, value) => {
1047
1137
  const normalized = value.trim();
1048
1138
  if (!normalized) throw new Error(`Enum column [${columnName}] must define only non-empty values.`);
@@ -1354,21 +1444,59 @@ var TableBuilder = class {
1354
1444
  }
1355
1445
  /**
1356
1446
  * Defines both createdAt and updatedAt timestamp columns in the table.
1357
- *
1358
- * @returns
1447
+ *
1448
+ * The attribute casing (the names exposed on the model) is controlled by the
1449
+ * first argument, while the optional second argument controls the casing used
1450
+ * for the persisted database column names via `.map()`.
1451
+ *
1452
+ * @example
1453
+ * table.timestamps() // createdAt / updatedAt
1454
+ * table.timestamps('snake') // created_at / updated_at
1455
+ * table.timestamps('camel', 'snake') // createdAt -> created_at (mapped)
1456
+ * table.timestamps({ createdAt: 'createdOn' })
1457
+ * table.timestamps('camel', { createdAt: 'created_on' })
1458
+ *
1459
+ * @param casing The casing (or explicit names) used for the attribute names.
1460
+ * @param mapCasing The casing (or explicit names) used for the mapped database columns.
1461
+ * @returns
1359
1462
  */
1360
- timestamps() {
1361
- this.timestamp("createdAt", {
1463
+ timestamps(casing = "camel", mapCasing) {
1464
+ const names = this.resolveTimestampNames(casing, defaultTimestampNames);
1465
+ const maps = mapCasing === void 0 ? void 0 : this.resolveTimestampNames(mapCasing, names);
1466
+ this.timestamp(names.createdAt, {
1362
1467
  nullable: false,
1363
- default: "now()"
1468
+ default: "now()",
1469
+ ...maps && maps.createdAt !== names.createdAt ? { map: maps.createdAt } : {}
1364
1470
  });
1365
- this.timestamp("updatedAt", {
1471
+ this.timestamp(names.updatedAt, {
1366
1472
  nullable: false,
1367
- updatedAt: true
1473
+ updatedAt: true,
1474
+ ...maps && maps.updatedAt !== names.updatedAt ? { map: maps.updatedAt } : {}
1368
1475
  });
1369
1476
  return this;
1370
1477
  }
1371
1478
  /**
1479
+ * Resolves a timestamp naming option into concrete createdAt / updatedAt names.
1480
+ *
1481
+ * @param naming The casing keyword or explicit name overrides.
1482
+ * @param fallback The names used when an explicit override is omitted.
1483
+ * @returns
1484
+ */
1485
+ resolveTimestampNames(naming, fallback) {
1486
+ if (naming === "snake") return {
1487
+ createdAt: "created_at",
1488
+ updatedAt: "updated_at"
1489
+ };
1490
+ if (naming === "camel") return {
1491
+ createdAt: "createdAt",
1492
+ updatedAt: "updatedAt"
1493
+ };
1494
+ return {
1495
+ createdAt: naming.createdAt ?? fallback.createdAt,
1496
+ updatedAt: naming.updatedAt ?? fallback.updatedAt
1497
+ };
1498
+ }
1499
+ /**
1372
1500
  * Defines a soft delete timestamp column in the table.
1373
1501
  *
1374
1502
  * @param column The name of the soft delete column.
@@ -177,6 +177,12 @@ var SetBasedEagerLoader = class SetBasedEagerLoader {
177
177
  case "morphTo":
178
178
  await this.loadMorphTo(name, metadata, constraint);
179
179
  return;
180
+ case "morphOne":
181
+ await this.loadMorphOne(name, resolver, metadata, constraint);
182
+ return;
183
+ case "morphMany":
184
+ await this.loadMorphMany(name, metadata, constraint);
185
+ return;
180
186
  default: await this.loadIndividually(name, resolver, constraint);
181
187
  }
182
188
  }
@@ -603,6 +609,86 @@ var SetBasedEagerLoader = class SetBasedEagerLoader {
603
609
  });
604
610
  }
605
611
  /**
612
+ * Loads a polymorphic one-to-one ("morph one") relationship for the set of
613
+ * models. See {@link loadMorphChildren} for the batching strategy.
614
+ *
615
+ * @param name
616
+ * @param resolver
617
+ * @param metadata
618
+ * @param constraint
619
+ */
620
+ async loadMorphOne(name, resolver, metadata, constraint) {
621
+ const relatedByKey = await this.loadMorphChildren(metadata, constraint);
622
+ this.models.forEach((model) => {
623
+ const bucket = relatedByKey.get(this.morphParentKey(model, metadata.localKey));
624
+ model.setLoadedRelation(name, bucket?.[0] ?? this.resolveSingleDefault(resolver, model));
625
+ });
626
+ }
627
+ /**
628
+ * Loads a polymorphic one-to-many ("morph many") relationship for the set of
629
+ * models. See {@link loadMorphChildren} for the batching strategy.
630
+ *
631
+ * @param name
632
+ * @param metadata
633
+ * @param constraint
634
+ */
635
+ async loadMorphMany(name, metadata, constraint) {
636
+ const relatedByKey = await this.loadMorphChildren(metadata, constraint);
637
+ this.models.forEach((model) => {
638
+ const bucket = relatedByKey.get(this.morphParentKey(model, metadata.localKey)) ?? [];
639
+ model.setLoadedRelation(name, new ArkormCollection(bucket));
640
+ });
641
+ }
642
+ /**
643
+ * Batch-loads the children for a morphOne/morphMany relationship. Parents are
644
+ * grouped by their own morph type (constructor name), so each distinct type
645
+ * runs a single query constrained by `idColumn IN (...) AND typeColumn = type`
646
+ * instead of one query per parent row. This also supports a heterogeneous
647
+ * parent set (e.g. the result of a nested load after a morphTo).
648
+ *
649
+ * @param metadata
650
+ * @param constraint
651
+ * @returns A map of `"{type}:{localKey}" -> related[]`.
652
+ */
653
+ async loadMorphChildren(metadata, constraint) {
654
+ const keysByType = /* @__PURE__ */ new Map();
655
+ const seenByType = /* @__PURE__ */ new Map();
656
+ this.models.forEach((model) => {
657
+ const morphType = this.morphTypeOf(model);
658
+ const localValue = model.getAttribute(metadata.localKey);
659
+ if (morphType.length === 0 || localValue == null) return;
660
+ const keys = keysByType.get(morphType) ?? [];
661
+ const seen = seenByType.get(morphType) ?? /* @__PURE__ */ new Set();
662
+ const lookupKey = this.toLookupKey(localValue);
663
+ if (!seen.has(lookupKey)) {
664
+ seen.add(lookupKey);
665
+ keys.push(localValue);
666
+ }
667
+ keysByType.set(morphType, keys);
668
+ seenByType.set(morphType, seen);
669
+ });
670
+ const relatedByKey = /* @__PURE__ */ new Map();
671
+ await Promise.all(Array.from(keysByType.entries()).map(async ([morphType, keys]) => {
672
+ let query = metadata.relatedModel.query().whereIn(metadata.morphIdColumn, keys).where({ [metadata.morphTypeColumn]: morphType });
673
+ query = this.applyConstraint(query, constraint);
674
+ (await query.get()).all().forEach((related) => {
675
+ const value = this.readModelAttribute(related, metadata.morphIdColumn);
676
+ if (value == null) return;
677
+ const key = `${morphType}:${this.toLookupKey(value)}`;
678
+ const bucket = relatedByKey.get(key) ?? [];
679
+ bucket.push(related);
680
+ relatedByKey.set(key, bucket);
681
+ });
682
+ }));
683
+ return relatedByKey;
684
+ }
685
+ morphParentKey(model, localKey) {
686
+ return `${this.morphTypeOf(model)}:${this.toLookupKey(model.getAttribute(localKey))}`;
687
+ }
688
+ morphTypeOf(model) {
689
+ return model.constructor?.name ?? "";
690
+ }
691
+ /**
606
692
  * Fallback method to load relationships individually for each model when the
607
693
  * relationship type is not supported for set-based loading.
608
694
  *
@@ -1015,6 +1101,10 @@ var PrimaryKeyGenerationPlanner = class {
1015
1101
  //#endregion
1016
1102
  //#region src/database/TableBuilder.ts
1017
1103
  const PRISMA_ENUM_MEMBER_REGEX$1 = /^[A-Za-z][A-Za-z0-9_]*$/;
1104
+ const defaultTimestampNames = {
1105
+ createdAt: "createdAt",
1106
+ updatedAt: "updatedAt"
1107
+ };
1018
1108
  const normalizeEnumMember = (columnName, value) => {
1019
1109
  const normalized = value.trim();
1020
1110
  if (!normalized) throw new Error(`Enum column [${columnName}] must define only non-empty values.`);
@@ -1326,21 +1416,59 @@ var TableBuilder = class {
1326
1416
  }
1327
1417
  /**
1328
1418
  * Defines both createdAt and updatedAt timestamp columns in the table.
1329
- *
1330
- * @returns
1419
+ *
1420
+ * The attribute casing (the names exposed on the model) is controlled by the
1421
+ * first argument, while the optional second argument controls the casing used
1422
+ * for the persisted database column names via `.map()`.
1423
+ *
1424
+ * @example
1425
+ * table.timestamps() // createdAt / updatedAt
1426
+ * table.timestamps('snake') // created_at / updated_at
1427
+ * table.timestamps('camel', 'snake') // createdAt -> created_at (mapped)
1428
+ * table.timestamps({ createdAt: 'createdOn' })
1429
+ * table.timestamps('camel', { createdAt: 'created_on' })
1430
+ *
1431
+ * @param casing The casing (or explicit names) used for the attribute names.
1432
+ * @param mapCasing The casing (or explicit names) used for the mapped database columns.
1433
+ * @returns
1331
1434
  */
1332
- timestamps() {
1333
- this.timestamp("createdAt", {
1435
+ timestamps(casing = "camel", mapCasing) {
1436
+ const names = this.resolveTimestampNames(casing, defaultTimestampNames);
1437
+ const maps = mapCasing === void 0 ? void 0 : this.resolveTimestampNames(mapCasing, names);
1438
+ this.timestamp(names.createdAt, {
1334
1439
  nullable: false,
1335
- default: "now()"
1440
+ default: "now()",
1441
+ ...maps && maps.createdAt !== names.createdAt ? { map: maps.createdAt } : {}
1336
1442
  });
1337
- this.timestamp("updatedAt", {
1443
+ this.timestamp(names.updatedAt, {
1338
1444
  nullable: false,
1339
- updatedAt: true
1445
+ updatedAt: true,
1446
+ ...maps && maps.updatedAt !== names.updatedAt ? { map: maps.updatedAt } : {}
1340
1447
  });
1341
1448
  return this;
1342
1449
  }
1343
1450
  /**
1451
+ * Resolves a timestamp naming option into concrete createdAt / updatedAt names.
1452
+ *
1453
+ * @param naming The casing keyword or explicit name overrides.
1454
+ * @param fallback The names used when an explicit override is omitted.
1455
+ * @returns
1456
+ */
1457
+ resolveTimestampNames(naming, fallback) {
1458
+ if (naming === "snake") return {
1459
+ createdAt: "created_at",
1460
+ updatedAt: "updated_at"
1461
+ };
1462
+ if (naming === "camel") return {
1463
+ createdAt: "createdAt",
1464
+ updatedAt: "updatedAt"
1465
+ };
1466
+ return {
1467
+ createdAt: naming.createdAt ?? fallback.createdAt,
1468
+ updatedAt: naming.updatedAt ?? fallback.updatedAt
1469
+ };
1470
+ }
1471
+ /**
1344
1472
  * Defines a soft delete timestamp column in the table.
1345
1473
  *
1346
1474
  * @param column The name of the soft delete column.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "arkormx",
3
- "version": "2.6.0",
3
+ "version": "2.7.0",
4
4
  "description": "Modern TypeScript-first ORM for Node.js.",
5
5
  "keywords": [
6
6
  "orm",