metal-orm 1.1.20 → 1.1.22

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -477,7 +477,7 @@ __export(index_exports, {
477
477
  module.exports = __toCommonJS(index_exports);
478
478
 
479
479
  // src/schema/table.ts
480
- var defineTable = (name, columns, relations = {}, hooks, options = {}) => {
480
+ var defineTable = (name, columns, relations = {}, options = {}) => {
481
481
  const colsWithNames = Object.entries(columns).reduce((acc, [key, def]) => {
482
482
  const colDef = { ...def, name: key, table: name };
483
483
  acc[key] = colDef;
@@ -488,7 +488,6 @@ var defineTable = (name, columns, relations = {}, hooks, options = {}) => {
488
488
  schema: options.schema,
489
489
  columns: colsWithNames,
490
490
  relations,
491
- hooks,
492
491
  primaryKey: options.primaryKey,
493
492
  indexes: options.indexes,
494
493
  checks: options.checks,
@@ -8404,14 +8403,11 @@ var addTransformerMetadata = (target, propertyKey, transformer) => {
8404
8403
  const meta = ensureEntityMetadata(target);
8405
8404
  meta.transformers[propertyKey] = transformer;
8406
8405
  };
8407
- var setEntityTableName = (target, tableName, hooks, type) => {
8406
+ var setEntityTableName = (target, tableName, type) => {
8408
8407
  const meta = ensureEntityMetadata(target);
8409
8408
  if (tableName && tableName.length > 0) {
8410
8409
  meta.tableName = tableName;
8411
8410
  }
8412
- if (hooks) {
8413
- meta.hooks = hooks;
8414
- }
8415
8411
  if (type) {
8416
8412
  meta.type = type;
8417
8413
  }
@@ -8428,7 +8424,7 @@ var buildTableDef = (meta) => {
8428
8424
  table: meta.tableName
8429
8425
  };
8430
8426
  }
8431
- const table = defineTable(meta.tableName, columns, {}, meta.hooks);
8427
+ const table = defineTable(meta.tableName, columns);
8432
8428
  meta.table = table;
8433
8429
  return table;
8434
8430
  };
@@ -11850,7 +11846,6 @@ var PgInformationSchemaColumns = defineTable(
11850
11846
  ordinal_position: col.int()
11851
11847
  },
11852
11848
  {},
11853
- void 0,
11854
11849
  { schema: "information_schema" }
11855
11850
  );
11856
11851
  var PgClass = defineTable(
@@ -11862,7 +11857,6 @@ var PgClass = defineTable(
11862
11857
  relkind: col.varchar(1)
11863
11858
  },
11864
11859
  {},
11865
- void 0,
11866
11860
  { schema: "pg_catalog" }
11867
11861
  );
11868
11862
  var PgNamespace = defineTable(
@@ -11872,7 +11866,6 @@ var PgNamespace = defineTable(
11872
11866
  nspname: col.varchar(255)
11873
11867
  },
11874
11868
  {},
11875
- void 0,
11876
11869
  { schema: "pg_catalog" }
11877
11870
  );
11878
11871
  var PgIndex = defineTable(
@@ -11885,7 +11878,6 @@ var PgIndex = defineTable(
11885
11878
  indpred: col.varchar(1024)
11886
11879
  },
11887
11880
  {},
11888
- void 0,
11889
11881
  { schema: "pg_catalog" }
11890
11882
  );
11891
11883
  var PgAttribute = defineTable(
@@ -11896,7 +11888,6 @@ var PgAttribute = defineTable(
11896
11888
  attnum: col.int()
11897
11889
  },
11898
11890
  {},
11899
- void 0,
11900
11891
  { schema: "pg_catalog" }
11901
11892
  );
11902
11893
  var PgTableConstraints = defineTable(
@@ -11911,7 +11902,6 @@ var PgTableConstraints = defineTable(
11911
11902
  constraint_type: col.varchar(255)
11912
11903
  },
11913
11904
  {},
11914
- void 0,
11915
11905
  { schema: "information_schema" }
11916
11906
  );
11917
11907
  var PgKeyColumnUsage = defineTable(
@@ -11927,7 +11917,6 @@ var PgKeyColumnUsage = defineTable(
11927
11917
  ordinal_position: col.int()
11928
11918
  },
11929
11919
  {},
11930
- void 0,
11931
11920
  { schema: "information_schema" }
11932
11921
  );
11933
11922
  var PgConstraintColumnUsage = defineTable(
@@ -11942,7 +11931,6 @@ var PgConstraintColumnUsage = defineTable(
11942
11931
  column_name: col.varchar(255)
11943
11932
  },
11944
11933
  {},
11945
- void 0,
11946
11934
  { schema: "information_schema" }
11947
11935
  );
11948
11936
  var PgReferentialConstraints = defineTable(
@@ -11959,7 +11947,6 @@ var PgReferentialConstraints = defineTable(
11959
11947
  delete_rule: col.varchar(64)
11960
11948
  },
11961
11949
  {},
11962
- void 0,
11963
11950
  { schema: "information_schema" }
11964
11951
  );
11965
11952
 
@@ -12341,7 +12328,6 @@ var InformationSchemaTables = defineTable(
12341
12328
  table_comment: col.varchar(1024)
12342
12329
  },
12343
12330
  {},
12344
- void 0,
12345
12331
  { schema: INFORMATION_SCHEMA }
12346
12332
  );
12347
12333
  var InformationSchemaColumns = defineTable(
@@ -12359,7 +12345,6 @@ var InformationSchemaColumns = defineTable(
12359
12345
  ordinal_position: col.int()
12360
12346
  },
12361
12347
  {},
12362
- void 0,
12363
12348
  { schema: INFORMATION_SCHEMA }
12364
12349
  );
12365
12350
  var InformationSchemaKeyColumnUsage = defineTable(
@@ -12376,7 +12361,6 @@ var InformationSchemaKeyColumnUsage = defineTable(
12376
12361
  referenced_column_name: col.varchar(255)
12377
12362
  },
12378
12363
  {},
12379
- void 0,
12380
12364
  { schema: INFORMATION_SCHEMA }
12381
12365
  );
12382
12366
  var InformationSchemaReferentialConstraints = defineTable(
@@ -12388,7 +12372,6 @@ var InformationSchemaReferentialConstraints = defineTable(
12388
12372
  update_rule: col.varchar(255)
12389
12373
  },
12390
12374
  {},
12391
- void 0,
12392
12375
  { schema: INFORMATION_SCHEMA }
12393
12376
  );
12394
12377
  var InformationSchemaStatistics = defineTable(
@@ -12402,7 +12385,6 @@ var InformationSchemaStatistics = defineTable(
12402
12385
  seq_in_index: col.int()
12403
12386
  },
12404
12387
  {},
12405
- void 0,
12406
12388
  { schema: INFORMATION_SCHEMA }
12407
12389
  );
12408
12390
 
@@ -13023,7 +13005,6 @@ var SysColumns = defineTable(
13023
13005
  user_type_id: col.int()
13024
13006
  },
13025
13007
  {},
13026
- void 0,
13027
13008
  { schema: "sys" }
13028
13009
  );
13029
13010
  var SysTables = defineTable(
@@ -13035,7 +13016,6 @@ var SysTables = defineTable(
13035
13016
  is_ms_shipped: col.boolean()
13036
13017
  },
13037
13018
  {},
13038
- void 0,
13039
13019
  { schema: "sys" }
13040
13020
  );
13041
13021
  var SysSchemas = defineTable(
@@ -13045,7 +13025,6 @@ var SysSchemas = defineTable(
13045
13025
  name: col.varchar(255)
13046
13026
  },
13047
13027
  {},
13048
- void 0,
13049
13028
  { schema: "sys" }
13050
13029
  );
13051
13030
  var SysTypes = defineTable(
@@ -13055,7 +13034,6 @@ var SysTypes = defineTable(
13055
13034
  name: col.varchar(255)
13056
13035
  },
13057
13036
  {},
13058
- void 0,
13059
13037
  { schema: "sys" }
13060
13038
  );
13061
13039
  var SysIndexes = defineTable(
@@ -13071,7 +13049,6 @@ var SysIndexes = defineTable(
13071
13049
  is_hypothetical: col.boolean()
13072
13050
  },
13073
13051
  {},
13074
- void 0,
13075
13052
  { schema: "sys" }
13076
13053
  );
13077
13054
  var SysIndexColumns = defineTable(
@@ -13083,7 +13060,6 @@ var SysIndexColumns = defineTable(
13083
13060
  key_ordinal: col.int()
13084
13061
  },
13085
13062
  {},
13086
- void 0,
13087
13063
  { schema: "sys" }
13088
13064
  );
13089
13065
  var SysForeignKeys = defineTable(
@@ -13095,7 +13071,6 @@ var SysForeignKeys = defineTable(
13095
13071
  update_referential_action_desc: col.varchar(64)
13096
13072
  },
13097
13073
  {},
13098
- void 0,
13099
13074
  { schema: "sys" }
13100
13075
  );
13101
13076
  var SysForeignKeyColumns = defineTable(
@@ -13109,7 +13084,6 @@ var SysForeignKeyColumns = defineTable(
13109
13084
  constraint_column_id: col.int()
13110
13085
  },
13111
13086
  {},
13112
- void 0,
13113
13087
  { schema: "sys" }
13114
13088
  );
13115
13089
 
@@ -14534,12 +14508,14 @@ var UnitOfWork = class {
14534
14508
  * @param executor - The database executor
14535
14509
  * @param identityMap - The identity map
14536
14510
  * @param hookContext - Function to get the hook context
14511
+ * @param resolveTableHooks - Session/runtime lifecycle hook resolver
14537
14512
  */
14538
- constructor(dialect, executor, identityMap, hookContext) {
14513
+ constructor(dialect, executor, identityMap, hookContext, resolveTableHooks = () => void 0) {
14539
14514
  this.dialect = dialect;
14540
14515
  this.executor = executor;
14541
14516
  this.identityMap = identityMap;
14542
14517
  this.hookContext = hookContext;
14518
+ this.resolveTableHooks = resolveTableHooks;
14543
14519
  }
14544
14520
  trackedEntities = /* @__PURE__ */ new Map();
14545
14521
  /**
@@ -14691,7 +14667,8 @@ var UnitOfWork = class {
14691
14667
  * @param tracked - The tracked entity to insert
14692
14668
  */
14693
14669
  async flushInsert(tracked) {
14694
- await this.runHook(tracked.table.hooks?.beforeInsert, tracked);
14670
+ const hooks = this.resolveTableHooks(tracked.table);
14671
+ await this.runHook(hooks?.beforeInsert, tracked);
14695
14672
  const payload = this.extractColumns(tracked.table, tracked.entity);
14696
14673
  let builder = new InsertQueryBuilder(tracked.table).values(payload);
14697
14674
  if (this.dialect.supportsDmlReturningClause()) {
@@ -14705,7 +14682,7 @@ var UnitOfWork = class {
14705
14682
  tracked.original = this.createSnapshot(tracked.table, tracked.entity);
14706
14683
  tracked.pk = this.getPrimaryKeyValue(tracked);
14707
14684
  this.registerIdentity(tracked);
14708
- await this.runHook(tracked.table.hooks?.afterInsert, tracked);
14685
+ await this.runHook(hooks?.afterInsert, tracked);
14709
14686
  }
14710
14687
  /**
14711
14688
  * Flushes an update operation for a modified entity.
@@ -14718,7 +14695,8 @@ var UnitOfWork = class {
14718
14695
  tracked.status = "managed" /* Managed */;
14719
14696
  return;
14720
14697
  }
14721
- await this.runHook(tracked.table.hooks?.beforeUpdate, tracked);
14698
+ const hooks = this.resolveTableHooks(tracked.table);
14699
+ await this.runHook(hooks?.beforeUpdate, tracked);
14722
14700
  const pkColumn = tracked.table.columns[findPrimaryKey(tracked.table)];
14723
14701
  if (!pkColumn) return;
14724
14702
  let builder = new UpdateQueryBuilder(tracked.table).set(changes).where(eq(pkColumn, tracked.pk));
@@ -14731,7 +14709,7 @@ var UnitOfWork = class {
14731
14709
  tracked.status = "managed" /* Managed */;
14732
14710
  tracked.original = this.createSnapshot(tracked.table, tracked.entity);
14733
14711
  this.registerIdentity(tracked);
14734
- await this.runHook(tracked.table.hooks?.afterUpdate, tracked);
14712
+ await this.runHook(hooks?.afterUpdate, tracked);
14735
14713
  }
14736
14714
  /**
14737
14715
  * Flushes a delete operation for a removed entity.
@@ -14739,7 +14717,8 @@ var UnitOfWork = class {
14739
14717
  */
14740
14718
  async flushDelete(tracked) {
14741
14719
  if (tracked.pk == null) return;
14742
- await this.runHook(tracked.table.hooks?.beforeDelete, tracked);
14720
+ const hooks = this.resolveTableHooks(tracked.table);
14721
+ await this.runHook(hooks?.beforeDelete, tracked);
14743
14722
  const pkColumn = tracked.table.columns[findPrimaryKey(tracked.table)];
14744
14723
  if (!pkColumn) return;
14745
14724
  const builder = new DeleteQueryBuilder(tracked.table).where(eq(pkColumn, tracked.pk));
@@ -14748,10 +14727,10 @@ var UnitOfWork = class {
14748
14727
  tracked.status = "detached" /* Detached */;
14749
14728
  this.trackedEntities.delete(tracked.entity);
14750
14729
  this.identityMap.remove(tracked);
14751
- await this.runHook(tracked.table.hooks?.afterDelete, tracked);
14730
+ await this.runHook(hooks?.afterDelete, tracked);
14752
14731
  }
14753
14732
  /**
14754
- * Runs a table hook if defined.
14733
+ * Runs a lifecycle hook if defined.
14755
14734
  * @param hook - The hook function
14756
14735
  * @param tracked - The tracked entity
14757
14736
  */
@@ -15070,7 +15049,10 @@ var RelationChangeProcessor = class {
15070
15049
  const rootKey = relation.localKey || findPrimaryKey(entry.rootTable);
15071
15050
  const rootId = entry.root[rootKey];
15072
15051
  if (rootId === void 0 || rootId === null) return;
15073
- const targetId = this.resolvePrimaryKeyValue(entry.change.entity, relation.target);
15052
+ const targetId = this.resolveBelongsToManyTargetValue(
15053
+ entry.change.entity,
15054
+ relation
15055
+ );
15074
15056
  if (targetId === null) return;
15075
15057
  const pivotPayload = this.buildPivotPayload(relation, entry.change.pivot);
15076
15058
  if (entry.change.kind === "update") {
@@ -15142,7 +15124,7 @@ var RelationChangeProcessor = class {
15142
15124
  * Inserts a pivot row for belongs-to-many relations.
15143
15125
  * @param relation - The belongs-to-many relation
15144
15126
  * @param rootId - The root entity's primary key value
15145
- * @param targetId - The target entity's primary key value
15127
+ * @param targetId - The target entity's relation key value
15146
15128
  */
15147
15129
  async insertPivotRow(relation, rootId, targetId, pivotPayload) {
15148
15130
  const payload = {
@@ -15158,7 +15140,7 @@ var RelationChangeProcessor = class {
15158
15140
  * Updates a pivot row for belongs-to-many relations.
15159
15141
  * @param relation - The belongs-to-many relation
15160
15142
  * @param rootId - The root entity's primary key value
15161
- * @param targetId - The target entity's primary key value
15143
+ * @param targetId - The target entity's relation key value
15162
15144
  * @param pivotPayload - The pivot columns to update
15163
15145
  */
15164
15146
  async updatePivotRow(relation, rootId, targetId, pivotPayload) {
@@ -15173,7 +15155,7 @@ var RelationChangeProcessor = class {
15173
15155
  * Deletes a pivot row for belongs-to-many relations.
15174
15156
  * @param relation - The belongs-to-many relation
15175
15157
  * @param rootId - The root entity's primary key value
15176
- * @param targetId - The target entity's primary key value
15158
+ * @param targetId - The target entity's relation key value
15177
15159
  */
15178
15160
  async deletePivotRow(relation, rootId, targetId) {
15179
15161
  const rootCol = relation.pivotTable.columns[relation.pivotForeignKeyToRoot];
@@ -15186,14 +15168,13 @@ var RelationChangeProcessor = class {
15186
15168
  await this.executor.executeSql(compiled.sql, compiled.params);
15187
15169
  }
15188
15170
  /**
15189
- * Resolves the primary key value from an entity.
15190
- * @param entity - The entity
15191
- * @param table - The table definition
15192
- * @returns The primary key value or null
15171
+ * Resolves the target-side key value used by a belongs-to-many relation.
15172
+ * The declared targetKey is part of the relation contract and takes
15173
+ * precedence over the target table primary key.
15193
15174
  */
15194
- resolvePrimaryKeyValue(entity, table) {
15175
+ resolveBelongsToManyTargetValue(entity, relation) {
15195
15176
  if (!entity) return null;
15196
- const key = findPrimaryKey(table);
15177
+ const key = relation.targetKey || findPrimaryKey(relation.target);
15197
15178
  const value = entity[key];
15198
15179
  if (value === void 0 || value === null) return null;
15199
15180
  return value ?? null;
@@ -15654,6 +15635,7 @@ var OrmSession = class {
15654
15635
  /** The tenant ID for multi-tenancy support */
15655
15636
  tenantId;
15656
15637
  interceptors;
15638
+ tableHooks = /* @__PURE__ */ new WeakMap();
15657
15639
  saveGraphDefaults;
15658
15640
  transactionDepth = 0;
15659
15641
  savepointCounter = 0;
@@ -15667,7 +15649,13 @@ var OrmSession = class {
15667
15649
  this.executor = createQueryLoggingExecutor(opts.executor, opts.queryLogger);
15668
15650
  this.interceptors = [...opts.interceptors ?? []];
15669
15651
  this.identityMap = new IdentityMap();
15670
- this.unitOfWork = new UnitOfWork(this.orm.dialect, this.executor, this.identityMap, () => this);
15652
+ this.unitOfWork = new UnitOfWork(
15653
+ this.orm.dialect,
15654
+ this.executor,
15655
+ this.identityMap,
15656
+ () => this,
15657
+ (table) => this.tableHooks.get(table)
15658
+ );
15671
15659
  this.relationChanges = new RelationChangeProcessor(this.unitOfWork, this.orm.dialect, this.executor);
15672
15660
  this.domainEvents = new DomainEventBus(opts.domainEventHandlers);
15673
15661
  this.cacheManager = opts.cacheManager;
@@ -15775,6 +15763,18 @@ var OrmSession = class {
15775
15763
  getEntitiesForTable(table) {
15776
15764
  return this.unitOfWork.getEntitiesForTable(table);
15777
15765
  }
15766
+ /**
15767
+ * Registers INSERT/UPDATE/DELETE lifecycle hooks for this Session only.
15768
+ * The target can be a TableDef or a decorated entity constructor.
15769
+ * Registering again for the same table replaces the previous hook set.
15770
+ */
15771
+ registerTableHooks(target, hooks) {
15772
+ const table = typeof target === "function" ? getTableDefFromEntity(target) : target;
15773
+ if (!table) {
15774
+ throw new Error("Entity metadata has not been bootstrapped");
15775
+ }
15776
+ this.tableHooks.set(table, hooks);
15777
+ }
15778
15778
  /**
15779
15779
  * Registers an interceptor for flush lifecycle hooks.
15780
15780
  * @param interceptor - The interceptor to register
@@ -15785,7 +15785,7 @@ var OrmSession = class {
15785
15785
  /**
15786
15786
  * Registers a domain event handler.
15787
15787
  * @param type - The event type
15788
- * @param handler - The event handler
15788
+ * @param handler - The domain event handler
15789
15789
  */
15790
15790
  registerDomainEventHandler(type, handler) {
15791
15791
  this.domainEvents.on(type, handler);
@@ -15959,7 +15959,9 @@ var OrmSession = class {
15959
15959
  this.markRemoved(entity);
15960
15960
  }
15961
15961
  /**
15962
- * Flushes pending changes to the database without session hooks, relation processing, or domain events.
15962
+ * Flushes pending changes to the database without session interceptors,
15963
+ * relation processing, or domain events. Table lifecycle hooks still run
15964
+ * because they are part of the Unit of Work.
15963
15965
  */
15964
15966
  async flush() {
15965
15967
  await this.unitOfWork.flush();
@@ -16854,7 +16856,7 @@ function Entity(options = {}) {
16854
16856
  return function(value, context) {
16855
16857
  const ctor = value;
16856
16858
  const tableName = options.tableName ?? deriveTableNameFromConstructor(ctor);
16857
- setEntityTableName(ctor, tableName, options.hooks, options.type);
16859
+ setEntityTableName(ctor, tableName, options.type);
16858
16860
  const bag = context ? readMetadataBag(context) : readMetadataBagFromConstructor(ctor);
16859
16861
  if (bag) {
16860
16862
  const meta = ensureEntityMetadata(ctor);
@@ -21074,7 +21076,7 @@ var TreeManager = class _TreeManager {
21074
21076
  * Moves a node to be the last child of a new parent.
21075
21077
  */
21076
21078
  async moveTo(node, newParentId) {
21077
- NestedSetStrategy.subtreeWidth(node.lft, node.rght);
21079
+ const width = NestedSetStrategy.subtreeWidth(node.lft, node.rght);
21078
21080
  let newPos;
21079
21081
  if (newParentId === null) {
21080
21082
  const maxRght = await this.getMaxRght();
@@ -21089,7 +21091,8 @@ var TreeManager = class _TreeManager {
21089
21091
  newParent.depth ?? await this.getLevel(newParent)
21090
21092
  );
21091
21093
  }
21092
- await this.moveSubtree(node, newPos.lft, newParentId, newPos.depth);
21094
+ const targetLft = newPos.lft > node.rght ? newPos.lft - width : newPos.lft;
21095
+ await this.moveSubtree(node, targetLft, newParentId, newPos.depth);
21093
21096
  }
21094
21097
  /**
21095
21098
  * Inserts a new node as a child of a parent.
@@ -21128,7 +21131,10 @@ var TreeManager = class _TreeManager {
21128
21131
  if (insertData[this.pkName] !== void 0) {
21129
21132
  return insertData[this.pkName];
21130
21133
  }
21131
- const findQuery = selectFrom(this.table).where(eq(this.table.columns[this.config.leftKey], insertPos.lft));
21134
+ const scopeExpressions = this.getScopeExpressions();
21135
+ const lftCondition = eq(this.table.columns[this.config.leftKey], insertPos.lft);
21136
+ const findCondition = scopeExpressions.length > 0 ? and(lftCondition, ...scopeExpressions) : lftCondition;
21137
+ const findQuery = selectFrom(this.table).where(findCondition);
21132
21138
  const { sql: findSql, params: findParams } = findQuery.compile(this.dialect);
21133
21139
  const results = await this.executor.executeSql(findSql, findParams);
21134
21140
  const rows = queryResultsToRows(results);
@@ -21138,23 +21144,37 @@ var TreeManager = class _TreeManager {
21138
21144
  return void 0;
21139
21145
  }
21140
21146
  /**
21141
- * Removes a node and re-parents its children to the node's parent.
21147
+ * Removes a node from its current tree position, promotes its direct children
21148
+ * to the removed node's parent, and retains the removed row as a standalone root.
21142
21149
  */
21143
21150
  async removeFromTree(node) {
21144
21151
  const nodeId = node.data[this.pkName];
21152
+ const originalMaxRght = await this.getMaxRght();
21145
21153
  await this.executeUpdate(
21146
21154
  eq(this.table.columns[this.config.parentKey], nodeId),
21147
21155
  { [this.config.parentKey]: node.parentId }
21148
21156
  );
21149
- const gap = NestedSetStrategy.calculateDeleteGap(node.lft, node.rght);
21157
+ if (node.rght - node.lft > 1) {
21158
+ let sql = `UPDATE ${this.quoteTable()} SET ${this.quoteCol(this.config.leftKey)} = ${this.quoteCol(this.config.leftKey)} - 1, ${this.quoteCol(this.config.rightKey)} = ${this.quoteCol(this.config.rightKey)} - 1`;
21159
+ if (this.config.depthKey) {
21160
+ sql += `, ${this.quoteCol(this.config.depthKey)} = ${this.quoteCol(this.config.depthKey)} - 1`;
21161
+ }
21162
+ sql += ` WHERE ${this.quoteCol(this.config.leftKey)} > ? AND ${this.quoteCol(this.config.rightKey)} < ?`;
21163
+ await this.executeRawUpdate(sql, [node.lft, node.rght]);
21164
+ }
21150
21165
  await this.shiftForDelete(node.rght, 2);
21151
- NestedSetStrategy.calculateShiftForDelete(node.lft + 1, gap.width - 2);
21152
- if (gap.width > 2) {
21153
- await this.executeRawUpdate(
21154
- `UPDATE ${this.quoteTable()} SET ${this.quoteCol(this.config.leftKey)} = ${this.quoteCol(this.config.leftKey)} - 1, ${this.quoteCol(this.config.rightKey)} = ${this.quoteCol(this.config.rightKey)} - 1 WHERE ${this.quoteCol(this.config.leftKey)} > ? AND ${this.quoteCol(this.config.rightKey)} < ?`,
21155
- [node.lft, node.rght]
21156
- );
21166
+ const detachedData = {
21167
+ [this.config.parentKey]: null,
21168
+ [this.config.leftKey]: originalMaxRght - 1,
21169
+ [this.config.rightKey]: originalMaxRght
21170
+ };
21171
+ if (this.config.depthKey) {
21172
+ detachedData[this.config.depthKey] = 0;
21157
21173
  }
21174
+ await this.executeUpdate(
21175
+ eq(this.table.columns[this.pkName], nodeId),
21176
+ detachedData
21177
+ );
21158
21178
  }
21159
21179
  /**
21160
21180
  * Deletes a node and all its descendants.
@@ -21279,22 +21299,32 @@ var TreeManager = class _TreeManager {
21279
21299
  }
21280
21300
  return "id";
21281
21301
  }
21302
+ getScopeEntries() {
21303
+ return Object.entries(buildScopeConditions(this.config.scope, this.scopeValues));
21304
+ }
21305
+ getScopeExpressions() {
21306
+ return this.getScopeEntries().map(
21307
+ ([key, value]) => eq(this.table.columns[key], value)
21308
+ );
21309
+ }
21282
21310
  async getMaxRght() {
21283
21311
  const query = selectFrom(this.table).selectRaw(`MAX(${this.config.rightKey}) as max_rght`);
21284
- const { sql, params } = query.compile(this.dialect);
21312
+ const scopeExpressions = this.getScopeExpressions();
21313
+ const finalQuery = scopeExpressions.length > 0 ? query.where(and(...scopeExpressions)) : query;
21314
+ const { sql, params } = finalQuery.compile(this.dialect);
21285
21315
  const queryResults = await this.executor.executeSql(sql, params);
21286
21316
  const rows = queryResultsToRows(queryResults);
21287
21317
  const maxRght = rows[0]?.max_rght;
21288
21318
  return typeof maxRght === "number" ? maxRght : 0;
21289
21319
  }
21290
- async shiftForInsert(insertPoint) {
21320
+ async shiftForInsert(insertPoint, width = 2) {
21291
21321
  await this.executeRawUpdate(
21292
- `UPDATE ${this.quoteTable()} SET ${this.quoteCol(this.config.rightKey)} = ${this.quoteCol(this.config.rightKey)} + 2 WHERE ${this.quoteCol(this.config.rightKey)} >= ?`,
21293
- [insertPoint]
21322
+ `UPDATE ${this.quoteTable()} SET ${this.quoteCol(this.config.rightKey)} = ${this.quoteCol(this.config.rightKey)} + ? WHERE ${this.quoteCol(this.config.rightKey)} >= ?`,
21323
+ [width, insertPoint]
21294
21324
  );
21295
21325
  await this.executeRawUpdate(
21296
- `UPDATE ${this.quoteTable()} SET ${this.quoteCol(this.config.leftKey)} = ${this.quoteCol(this.config.leftKey)} + 2 WHERE ${this.quoteCol(this.config.leftKey)} > ?`,
21297
- [insertPoint]
21326
+ `UPDATE ${this.quoteTable()} SET ${this.quoteCol(this.config.leftKey)} = ${this.quoteCol(this.config.leftKey)} + ? WHERE ${this.quoteCol(this.config.leftKey)} > ?`,
21327
+ [width, insertPoint]
21298
21328
  );
21299
21329
  }
21300
21330
  async shiftForDelete(deletedRght, width) {
@@ -21330,28 +21360,27 @@ var TreeManager = class _TreeManager {
21330
21360
  }
21331
21361
  async moveSubtree(node, newLft, newParentId, newDepth) {
21332
21362
  const width = NestedSetStrategy.subtreeWidth(node.lft, node.rght);
21333
- const delta = newLft - node.lft;
21334
- const depthDelta = this.config.depthKey ? newDepth - (node.depth ?? 0) : 0;
21363
+ const oldDepth = this.config.depthKey ? node.depth ?? await this.getLevel(node) : 0;
21364
+ const depthDelta = this.config.depthKey ? newDepth - oldDepth : 0;
21335
21365
  const nodeId = node.data[this.pkName];
21336
- const tempOffset = 1e7;
21366
+ const isolateDelta = -1e7 - node.rght;
21367
+ const isolatedLft = node.lft + isolateDelta;
21368
+ const isolatedRght = node.rght + isolateDelta;
21337
21369
  await this.executeRawUpdate(
21338
- `UPDATE ${this.quoteTable()} SET ${this.quoteCol(this.config.leftKey)} = ${this.quoteCol(this.config.leftKey)} + ? WHERE ${this.quoteCol(this.config.leftKey)} >= ? AND ${this.quoteCol(this.config.rightKey)} <= ?`,
21339
- [tempOffset, node.lft, node.rght]
21340
- );
21341
- await this.executeRawUpdate(
21342
- `UPDATE ${this.quoteTable()} SET ${this.quoteCol(this.config.rightKey)} = ${this.quoteCol(this.config.rightKey)} + ? WHERE ${this.quoteCol(this.config.leftKey)} >= ? AND ${this.quoteCol(this.config.rightKey)} <= ?`,
21343
- [tempOffset, node.lft + tempOffset, node.rght]
21370
+ `UPDATE ${this.quoteTable()} SET ${this.quoteCol(this.config.leftKey)} = ${this.quoteCol(this.config.leftKey)} + ?, ${this.quoteCol(this.config.rightKey)} = ${this.quoteCol(this.config.rightKey)} + ? WHERE ${this.quoteCol(this.config.leftKey)} >= ? AND ${this.quoteCol(this.config.rightKey)} <= ?`,
21371
+ [isolateDelta, isolateDelta, node.lft, node.rght]
21344
21372
  );
21345
21373
  await this.shiftForDelete(node.rght, width);
21346
- await this.shiftForInsert(newLft);
21347
- let updateSql = `UPDATE ${this.quoteTable()} SET ${this.quoteCol(this.config.leftKey)} = ${this.quoteCol(this.config.leftKey)} - ? + ?, ${this.quoteCol(this.config.rightKey)} = ${this.quoteCol(this.config.rightKey)} - ? + ?`;
21348
- const updateParams = [tempOffset, delta, tempOffset, delta];
21374
+ await this.shiftForInsert(newLft, width);
21375
+ const restoreDelta = newLft - node.lft - isolateDelta;
21376
+ let updateSql = `UPDATE ${this.quoteTable()} SET ${this.quoteCol(this.config.leftKey)} = ${this.quoteCol(this.config.leftKey)} + ?, ${this.quoteCol(this.config.rightKey)} = ${this.quoteCol(this.config.rightKey)} + ?`;
21377
+ const updateParams = [restoreDelta, restoreDelta];
21349
21378
  if (this.config.depthKey && depthDelta !== 0) {
21350
21379
  updateSql += `, ${this.quoteCol(this.config.depthKey)} = ${this.quoteCol(this.config.depthKey)} + ?`;
21351
21380
  updateParams.push(depthDelta);
21352
21381
  }
21353
- updateSql += ` WHERE ${this.quoteCol(this.config.leftKey)} >= ?`;
21354
- updateParams.push(node.lft + tempOffset);
21382
+ updateSql += ` WHERE ${this.quoteCol(this.config.leftKey)} >= ? AND ${this.quoteCol(this.config.rightKey)} <= ?`;
21383
+ updateParams.push(isolatedLft, isolatedRght);
21355
21384
  await this.executeRawUpdate(updateSql, updateParams);
21356
21385
  await this.executeUpdate(
21357
21386
  eq(this.table.columns[this.pkName], nodeId),
@@ -21376,12 +21405,23 @@ var TreeManager = class _TreeManager {
21376
21405
  }));
21377
21406
  }
21378
21407
  async executeUpdate(condition, data) {
21379
- const query = update(this.table).set(data).where(condition);
21408
+ const scopeExpressions = this.getScopeExpressions();
21409
+ const finalCondition = scopeExpressions.length > 0 ? and(condition, ...scopeExpressions) : condition;
21410
+ const query = update(this.table).set(data).where(finalCondition);
21380
21411
  const { sql, params } = query.compile(this.dialect);
21381
21412
  await this.executor.executeSql(sql, params);
21382
21413
  }
21383
21414
  async executeRawUpdate(sql, params) {
21384
- await this.executor.executeSql(sql, params);
21415
+ let scopedSql = sql;
21416
+ const scopedParams = [...params];
21417
+ let hasWhere = /\bWHERE\b/i.test(scopedSql);
21418
+ for (const [key, value] of this.getScopeEntries()) {
21419
+ scopedSql += hasWhere ? " AND " : " WHERE ";
21420
+ scopedSql += `${this.quoteCol(key)} = ?`;
21421
+ scopedParams.push(value);
21422
+ hasWhere = true;
21423
+ }
21424
+ await this.executor.executeSql(scopedSql, scopedParams);
21385
21425
  }
21386
21426
  quoteTable() {
21387
21427
  const quote = this.getQuoteChar();