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.js CHANGED
@@ -36,7 +36,7 @@ var init_schema_plan_executor = __esm({
36
36
  });
37
37
 
38
38
  // src/schema/table.ts
39
- var defineTable = (name, columns, relations = {}, hooks, options = {}) => {
39
+ var defineTable = (name, columns, relations = {}, options = {}) => {
40
40
  const colsWithNames = Object.entries(columns).reduce((acc, [key, def]) => {
41
41
  const colDef = { ...def, name: key, table: name };
42
42
  acc[key] = colDef;
@@ -47,7 +47,6 @@ var defineTable = (name, columns, relations = {}, hooks, options = {}) => {
47
47
  schema: options.schema,
48
48
  columns: colsWithNames,
49
49
  relations,
50
- hooks,
51
50
  primaryKey: options.primaryKey,
52
51
  indexes: options.indexes,
53
52
  checks: options.checks,
@@ -7963,14 +7962,11 @@ var addTransformerMetadata = (target, propertyKey, transformer) => {
7963
7962
  const meta = ensureEntityMetadata(target);
7964
7963
  meta.transformers[propertyKey] = transformer;
7965
7964
  };
7966
- var setEntityTableName = (target, tableName, hooks, type) => {
7965
+ var setEntityTableName = (target, tableName, type) => {
7967
7966
  const meta = ensureEntityMetadata(target);
7968
7967
  if (tableName && tableName.length > 0) {
7969
7968
  meta.tableName = tableName;
7970
7969
  }
7971
- if (hooks) {
7972
- meta.hooks = hooks;
7973
- }
7974
7970
  if (type) {
7975
7971
  meta.type = type;
7976
7972
  }
@@ -7987,7 +7983,7 @@ var buildTableDef = (meta) => {
7987
7983
  table: meta.tableName
7988
7984
  };
7989
7985
  }
7990
- const table = defineTable(meta.tableName, columns, {}, meta.hooks);
7986
+ const table = defineTable(meta.tableName, columns);
7991
7987
  meta.table = table;
7992
7988
  return table;
7993
7989
  };
@@ -11409,7 +11405,6 @@ var PgInformationSchemaColumns = defineTable(
11409
11405
  ordinal_position: col.int()
11410
11406
  },
11411
11407
  {},
11412
- void 0,
11413
11408
  { schema: "information_schema" }
11414
11409
  );
11415
11410
  var PgClass = defineTable(
@@ -11421,7 +11416,6 @@ var PgClass = defineTable(
11421
11416
  relkind: col.varchar(1)
11422
11417
  },
11423
11418
  {},
11424
- void 0,
11425
11419
  { schema: "pg_catalog" }
11426
11420
  );
11427
11421
  var PgNamespace = defineTable(
@@ -11431,7 +11425,6 @@ var PgNamespace = defineTable(
11431
11425
  nspname: col.varchar(255)
11432
11426
  },
11433
11427
  {},
11434
- void 0,
11435
11428
  { schema: "pg_catalog" }
11436
11429
  );
11437
11430
  var PgIndex = defineTable(
@@ -11444,7 +11437,6 @@ var PgIndex = defineTable(
11444
11437
  indpred: col.varchar(1024)
11445
11438
  },
11446
11439
  {},
11447
- void 0,
11448
11440
  { schema: "pg_catalog" }
11449
11441
  );
11450
11442
  var PgAttribute = defineTable(
@@ -11455,7 +11447,6 @@ var PgAttribute = defineTable(
11455
11447
  attnum: col.int()
11456
11448
  },
11457
11449
  {},
11458
- void 0,
11459
11450
  { schema: "pg_catalog" }
11460
11451
  );
11461
11452
  var PgTableConstraints = defineTable(
@@ -11470,7 +11461,6 @@ var PgTableConstraints = defineTable(
11470
11461
  constraint_type: col.varchar(255)
11471
11462
  },
11472
11463
  {},
11473
- void 0,
11474
11464
  { schema: "information_schema" }
11475
11465
  );
11476
11466
  var PgKeyColumnUsage = defineTable(
@@ -11486,7 +11476,6 @@ var PgKeyColumnUsage = defineTable(
11486
11476
  ordinal_position: col.int()
11487
11477
  },
11488
11478
  {},
11489
- void 0,
11490
11479
  { schema: "information_schema" }
11491
11480
  );
11492
11481
  var PgConstraintColumnUsage = defineTable(
@@ -11501,7 +11490,6 @@ var PgConstraintColumnUsage = defineTable(
11501
11490
  column_name: col.varchar(255)
11502
11491
  },
11503
11492
  {},
11504
- void 0,
11505
11493
  { schema: "information_schema" }
11506
11494
  );
11507
11495
  var PgReferentialConstraints = defineTable(
@@ -11518,7 +11506,6 @@ var PgReferentialConstraints = defineTable(
11518
11506
  delete_rule: col.varchar(64)
11519
11507
  },
11520
11508
  {},
11521
- void 0,
11522
11509
  { schema: "information_schema" }
11523
11510
  );
11524
11511
 
@@ -11900,7 +11887,6 @@ var InformationSchemaTables = defineTable(
11900
11887
  table_comment: col.varchar(1024)
11901
11888
  },
11902
11889
  {},
11903
- void 0,
11904
11890
  { schema: INFORMATION_SCHEMA }
11905
11891
  );
11906
11892
  var InformationSchemaColumns = defineTable(
@@ -11918,7 +11904,6 @@ var InformationSchemaColumns = defineTable(
11918
11904
  ordinal_position: col.int()
11919
11905
  },
11920
11906
  {},
11921
- void 0,
11922
11907
  { schema: INFORMATION_SCHEMA }
11923
11908
  );
11924
11909
  var InformationSchemaKeyColumnUsage = defineTable(
@@ -11935,7 +11920,6 @@ var InformationSchemaKeyColumnUsage = defineTable(
11935
11920
  referenced_column_name: col.varchar(255)
11936
11921
  },
11937
11922
  {},
11938
- void 0,
11939
11923
  { schema: INFORMATION_SCHEMA }
11940
11924
  );
11941
11925
  var InformationSchemaReferentialConstraints = defineTable(
@@ -11947,7 +11931,6 @@ var InformationSchemaReferentialConstraints = defineTable(
11947
11931
  update_rule: col.varchar(255)
11948
11932
  },
11949
11933
  {},
11950
- void 0,
11951
11934
  { schema: INFORMATION_SCHEMA }
11952
11935
  );
11953
11936
  var InformationSchemaStatistics = defineTable(
@@ -11961,7 +11944,6 @@ var InformationSchemaStatistics = defineTable(
11961
11944
  seq_in_index: col.int()
11962
11945
  },
11963
11946
  {},
11964
- void 0,
11965
11947
  { schema: INFORMATION_SCHEMA }
11966
11948
  );
11967
11949
 
@@ -12582,7 +12564,6 @@ var SysColumns = defineTable(
12582
12564
  user_type_id: col.int()
12583
12565
  },
12584
12566
  {},
12585
- void 0,
12586
12567
  { schema: "sys" }
12587
12568
  );
12588
12569
  var SysTables = defineTable(
@@ -12594,7 +12575,6 @@ var SysTables = defineTable(
12594
12575
  is_ms_shipped: col.boolean()
12595
12576
  },
12596
12577
  {},
12597
- void 0,
12598
12578
  { schema: "sys" }
12599
12579
  );
12600
12580
  var SysSchemas = defineTable(
@@ -12604,7 +12584,6 @@ var SysSchemas = defineTable(
12604
12584
  name: col.varchar(255)
12605
12585
  },
12606
12586
  {},
12607
- void 0,
12608
12587
  { schema: "sys" }
12609
12588
  );
12610
12589
  var SysTypes = defineTable(
@@ -12614,7 +12593,6 @@ var SysTypes = defineTable(
12614
12593
  name: col.varchar(255)
12615
12594
  },
12616
12595
  {},
12617
- void 0,
12618
12596
  { schema: "sys" }
12619
12597
  );
12620
12598
  var SysIndexes = defineTable(
@@ -12630,7 +12608,6 @@ var SysIndexes = defineTable(
12630
12608
  is_hypothetical: col.boolean()
12631
12609
  },
12632
12610
  {},
12633
- void 0,
12634
12611
  { schema: "sys" }
12635
12612
  );
12636
12613
  var SysIndexColumns = defineTable(
@@ -12642,7 +12619,6 @@ var SysIndexColumns = defineTable(
12642
12619
  key_ordinal: col.int()
12643
12620
  },
12644
12621
  {},
12645
- void 0,
12646
12622
  { schema: "sys" }
12647
12623
  );
12648
12624
  var SysForeignKeys = defineTable(
@@ -12654,7 +12630,6 @@ var SysForeignKeys = defineTable(
12654
12630
  update_referential_action_desc: col.varchar(64)
12655
12631
  },
12656
12632
  {},
12657
- void 0,
12658
12633
  { schema: "sys" }
12659
12634
  );
12660
12635
  var SysForeignKeyColumns = defineTable(
@@ -12668,7 +12643,6 @@ var SysForeignKeyColumns = defineTable(
12668
12643
  constraint_column_id: col.int()
12669
12644
  },
12670
12645
  {},
12671
- void 0,
12672
12646
  { schema: "sys" }
12673
12647
  );
12674
12648
 
@@ -14093,12 +14067,14 @@ var UnitOfWork = class {
14093
14067
  * @param executor - The database executor
14094
14068
  * @param identityMap - The identity map
14095
14069
  * @param hookContext - Function to get the hook context
14070
+ * @param resolveTableHooks - Session/runtime lifecycle hook resolver
14096
14071
  */
14097
- constructor(dialect, executor, identityMap, hookContext) {
14072
+ constructor(dialect, executor, identityMap, hookContext, resolveTableHooks = () => void 0) {
14098
14073
  this.dialect = dialect;
14099
14074
  this.executor = executor;
14100
14075
  this.identityMap = identityMap;
14101
14076
  this.hookContext = hookContext;
14077
+ this.resolveTableHooks = resolveTableHooks;
14102
14078
  }
14103
14079
  trackedEntities = /* @__PURE__ */ new Map();
14104
14080
  /**
@@ -14250,7 +14226,8 @@ var UnitOfWork = class {
14250
14226
  * @param tracked - The tracked entity to insert
14251
14227
  */
14252
14228
  async flushInsert(tracked) {
14253
- await this.runHook(tracked.table.hooks?.beforeInsert, tracked);
14229
+ const hooks = this.resolveTableHooks(tracked.table);
14230
+ await this.runHook(hooks?.beforeInsert, tracked);
14254
14231
  const payload = this.extractColumns(tracked.table, tracked.entity);
14255
14232
  let builder = new InsertQueryBuilder(tracked.table).values(payload);
14256
14233
  if (this.dialect.supportsDmlReturningClause()) {
@@ -14264,7 +14241,7 @@ var UnitOfWork = class {
14264
14241
  tracked.original = this.createSnapshot(tracked.table, tracked.entity);
14265
14242
  tracked.pk = this.getPrimaryKeyValue(tracked);
14266
14243
  this.registerIdentity(tracked);
14267
- await this.runHook(tracked.table.hooks?.afterInsert, tracked);
14244
+ await this.runHook(hooks?.afterInsert, tracked);
14268
14245
  }
14269
14246
  /**
14270
14247
  * Flushes an update operation for a modified entity.
@@ -14277,7 +14254,8 @@ var UnitOfWork = class {
14277
14254
  tracked.status = "managed" /* Managed */;
14278
14255
  return;
14279
14256
  }
14280
- await this.runHook(tracked.table.hooks?.beforeUpdate, tracked);
14257
+ const hooks = this.resolveTableHooks(tracked.table);
14258
+ await this.runHook(hooks?.beforeUpdate, tracked);
14281
14259
  const pkColumn = tracked.table.columns[findPrimaryKey(tracked.table)];
14282
14260
  if (!pkColumn) return;
14283
14261
  let builder = new UpdateQueryBuilder(tracked.table).set(changes).where(eq(pkColumn, tracked.pk));
@@ -14290,7 +14268,7 @@ var UnitOfWork = class {
14290
14268
  tracked.status = "managed" /* Managed */;
14291
14269
  tracked.original = this.createSnapshot(tracked.table, tracked.entity);
14292
14270
  this.registerIdentity(tracked);
14293
- await this.runHook(tracked.table.hooks?.afterUpdate, tracked);
14271
+ await this.runHook(hooks?.afterUpdate, tracked);
14294
14272
  }
14295
14273
  /**
14296
14274
  * Flushes a delete operation for a removed entity.
@@ -14298,7 +14276,8 @@ var UnitOfWork = class {
14298
14276
  */
14299
14277
  async flushDelete(tracked) {
14300
14278
  if (tracked.pk == null) return;
14301
- await this.runHook(tracked.table.hooks?.beforeDelete, tracked);
14279
+ const hooks = this.resolveTableHooks(tracked.table);
14280
+ await this.runHook(hooks?.beforeDelete, tracked);
14302
14281
  const pkColumn = tracked.table.columns[findPrimaryKey(tracked.table)];
14303
14282
  if (!pkColumn) return;
14304
14283
  const builder = new DeleteQueryBuilder(tracked.table).where(eq(pkColumn, tracked.pk));
@@ -14307,10 +14286,10 @@ var UnitOfWork = class {
14307
14286
  tracked.status = "detached" /* Detached */;
14308
14287
  this.trackedEntities.delete(tracked.entity);
14309
14288
  this.identityMap.remove(tracked);
14310
- await this.runHook(tracked.table.hooks?.afterDelete, tracked);
14289
+ await this.runHook(hooks?.afterDelete, tracked);
14311
14290
  }
14312
14291
  /**
14313
- * Runs a table hook if defined.
14292
+ * Runs a lifecycle hook if defined.
14314
14293
  * @param hook - The hook function
14315
14294
  * @param tracked - The tracked entity
14316
14295
  */
@@ -14629,7 +14608,10 @@ var RelationChangeProcessor = class {
14629
14608
  const rootKey = relation.localKey || findPrimaryKey(entry.rootTable);
14630
14609
  const rootId = entry.root[rootKey];
14631
14610
  if (rootId === void 0 || rootId === null) return;
14632
- const targetId = this.resolvePrimaryKeyValue(entry.change.entity, relation.target);
14611
+ const targetId = this.resolveBelongsToManyTargetValue(
14612
+ entry.change.entity,
14613
+ relation
14614
+ );
14633
14615
  if (targetId === null) return;
14634
14616
  const pivotPayload = this.buildPivotPayload(relation, entry.change.pivot);
14635
14617
  if (entry.change.kind === "update") {
@@ -14701,7 +14683,7 @@ var RelationChangeProcessor = class {
14701
14683
  * Inserts a pivot row for belongs-to-many relations.
14702
14684
  * @param relation - The belongs-to-many relation
14703
14685
  * @param rootId - The root entity's primary key value
14704
- * @param targetId - The target entity's primary key value
14686
+ * @param targetId - The target entity's relation key value
14705
14687
  */
14706
14688
  async insertPivotRow(relation, rootId, targetId, pivotPayload) {
14707
14689
  const payload = {
@@ -14717,7 +14699,7 @@ var RelationChangeProcessor = class {
14717
14699
  * Updates a pivot row for belongs-to-many relations.
14718
14700
  * @param relation - The belongs-to-many relation
14719
14701
  * @param rootId - The root entity's primary key value
14720
- * @param targetId - The target entity's primary key value
14702
+ * @param targetId - The target entity's relation key value
14721
14703
  * @param pivotPayload - The pivot columns to update
14722
14704
  */
14723
14705
  async updatePivotRow(relation, rootId, targetId, pivotPayload) {
@@ -14732,7 +14714,7 @@ var RelationChangeProcessor = class {
14732
14714
  * Deletes a pivot row for belongs-to-many relations.
14733
14715
  * @param relation - The belongs-to-many relation
14734
14716
  * @param rootId - The root entity's primary key value
14735
- * @param targetId - The target entity's primary key value
14717
+ * @param targetId - The target entity's relation key value
14736
14718
  */
14737
14719
  async deletePivotRow(relation, rootId, targetId) {
14738
14720
  const rootCol = relation.pivotTable.columns[relation.pivotForeignKeyToRoot];
@@ -14745,14 +14727,13 @@ var RelationChangeProcessor = class {
14745
14727
  await this.executor.executeSql(compiled.sql, compiled.params);
14746
14728
  }
14747
14729
  /**
14748
- * Resolves the primary key value from an entity.
14749
- * @param entity - The entity
14750
- * @param table - The table definition
14751
- * @returns The primary key value or null
14730
+ * Resolves the target-side key value used by a belongs-to-many relation.
14731
+ * The declared targetKey is part of the relation contract and takes
14732
+ * precedence over the target table primary key.
14752
14733
  */
14753
- resolvePrimaryKeyValue(entity, table) {
14734
+ resolveBelongsToManyTargetValue(entity, relation) {
14754
14735
  if (!entity) return null;
14755
- const key = findPrimaryKey(table);
14736
+ const key = relation.targetKey || findPrimaryKey(relation.target);
14756
14737
  const value = entity[key];
14757
14738
  if (value === void 0 || value === null) return null;
14758
14739
  return value ?? null;
@@ -15213,6 +15194,7 @@ var OrmSession = class {
15213
15194
  /** The tenant ID for multi-tenancy support */
15214
15195
  tenantId;
15215
15196
  interceptors;
15197
+ tableHooks = /* @__PURE__ */ new WeakMap();
15216
15198
  saveGraphDefaults;
15217
15199
  transactionDepth = 0;
15218
15200
  savepointCounter = 0;
@@ -15226,7 +15208,13 @@ var OrmSession = class {
15226
15208
  this.executor = createQueryLoggingExecutor(opts.executor, opts.queryLogger);
15227
15209
  this.interceptors = [...opts.interceptors ?? []];
15228
15210
  this.identityMap = new IdentityMap();
15229
- this.unitOfWork = new UnitOfWork(this.orm.dialect, this.executor, this.identityMap, () => this);
15211
+ this.unitOfWork = new UnitOfWork(
15212
+ this.orm.dialect,
15213
+ this.executor,
15214
+ this.identityMap,
15215
+ () => this,
15216
+ (table) => this.tableHooks.get(table)
15217
+ );
15230
15218
  this.relationChanges = new RelationChangeProcessor(this.unitOfWork, this.orm.dialect, this.executor);
15231
15219
  this.domainEvents = new DomainEventBus(opts.domainEventHandlers);
15232
15220
  this.cacheManager = opts.cacheManager;
@@ -15334,6 +15322,18 @@ var OrmSession = class {
15334
15322
  getEntitiesForTable(table) {
15335
15323
  return this.unitOfWork.getEntitiesForTable(table);
15336
15324
  }
15325
+ /**
15326
+ * Registers INSERT/UPDATE/DELETE lifecycle hooks for this Session only.
15327
+ * The target can be a TableDef or a decorated entity constructor.
15328
+ * Registering again for the same table replaces the previous hook set.
15329
+ */
15330
+ registerTableHooks(target, hooks) {
15331
+ const table = typeof target === "function" ? getTableDefFromEntity(target) : target;
15332
+ if (!table) {
15333
+ throw new Error("Entity metadata has not been bootstrapped");
15334
+ }
15335
+ this.tableHooks.set(table, hooks);
15336
+ }
15337
15337
  /**
15338
15338
  * Registers an interceptor for flush lifecycle hooks.
15339
15339
  * @param interceptor - The interceptor to register
@@ -15344,7 +15344,7 @@ var OrmSession = class {
15344
15344
  /**
15345
15345
  * Registers a domain event handler.
15346
15346
  * @param type - The event type
15347
- * @param handler - The event handler
15347
+ * @param handler - The domain event handler
15348
15348
  */
15349
15349
  registerDomainEventHandler(type, handler) {
15350
15350
  this.domainEvents.on(type, handler);
@@ -15518,7 +15518,9 @@ var OrmSession = class {
15518
15518
  this.markRemoved(entity);
15519
15519
  }
15520
15520
  /**
15521
- * Flushes pending changes to the database without session hooks, relation processing, or domain events.
15521
+ * Flushes pending changes to the database without session interceptors,
15522
+ * relation processing, or domain events. Table lifecycle hooks still run
15523
+ * because they are part of the Unit of Work.
15522
15524
  */
15523
15525
  async flush() {
15524
15526
  await this.unitOfWork.flush();
@@ -16413,7 +16415,7 @@ function Entity(options = {}) {
16413
16415
  return function(value, context) {
16414
16416
  const ctor = value;
16415
16417
  const tableName = options.tableName ?? deriveTableNameFromConstructor(ctor);
16416
- setEntityTableName(ctor, tableName, options.hooks, options.type);
16418
+ setEntityTableName(ctor, tableName, options.type);
16417
16419
  const bag = context ? readMetadataBag(context) : readMetadataBagFromConstructor(ctor);
16418
16420
  if (bag) {
16419
16421
  const meta = ensureEntityMetadata(ctor);
@@ -20633,7 +20635,7 @@ var TreeManager = class _TreeManager {
20633
20635
  * Moves a node to be the last child of a new parent.
20634
20636
  */
20635
20637
  async moveTo(node, newParentId) {
20636
- NestedSetStrategy.subtreeWidth(node.lft, node.rght);
20638
+ const width = NestedSetStrategy.subtreeWidth(node.lft, node.rght);
20637
20639
  let newPos;
20638
20640
  if (newParentId === null) {
20639
20641
  const maxRght = await this.getMaxRght();
@@ -20648,7 +20650,8 @@ var TreeManager = class _TreeManager {
20648
20650
  newParent.depth ?? await this.getLevel(newParent)
20649
20651
  );
20650
20652
  }
20651
- await this.moveSubtree(node, newPos.lft, newParentId, newPos.depth);
20653
+ const targetLft = newPos.lft > node.rght ? newPos.lft - width : newPos.lft;
20654
+ await this.moveSubtree(node, targetLft, newParentId, newPos.depth);
20652
20655
  }
20653
20656
  /**
20654
20657
  * Inserts a new node as a child of a parent.
@@ -20687,7 +20690,10 @@ var TreeManager = class _TreeManager {
20687
20690
  if (insertData[this.pkName] !== void 0) {
20688
20691
  return insertData[this.pkName];
20689
20692
  }
20690
- const findQuery = selectFrom(this.table).where(eq(this.table.columns[this.config.leftKey], insertPos.lft));
20693
+ const scopeExpressions = this.getScopeExpressions();
20694
+ const lftCondition = eq(this.table.columns[this.config.leftKey], insertPos.lft);
20695
+ const findCondition = scopeExpressions.length > 0 ? and(lftCondition, ...scopeExpressions) : lftCondition;
20696
+ const findQuery = selectFrom(this.table).where(findCondition);
20691
20697
  const { sql: findSql, params: findParams } = findQuery.compile(this.dialect);
20692
20698
  const results = await this.executor.executeSql(findSql, findParams);
20693
20699
  const rows = queryResultsToRows(results);
@@ -20697,23 +20703,37 @@ var TreeManager = class _TreeManager {
20697
20703
  return void 0;
20698
20704
  }
20699
20705
  /**
20700
- * Removes a node and re-parents its children to the node's parent.
20706
+ * Removes a node from its current tree position, promotes its direct children
20707
+ * to the removed node's parent, and retains the removed row as a standalone root.
20701
20708
  */
20702
20709
  async removeFromTree(node) {
20703
20710
  const nodeId = node.data[this.pkName];
20711
+ const originalMaxRght = await this.getMaxRght();
20704
20712
  await this.executeUpdate(
20705
20713
  eq(this.table.columns[this.config.parentKey], nodeId),
20706
20714
  { [this.config.parentKey]: node.parentId }
20707
20715
  );
20708
- const gap = NestedSetStrategy.calculateDeleteGap(node.lft, node.rght);
20716
+ if (node.rght - node.lft > 1) {
20717
+ 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`;
20718
+ if (this.config.depthKey) {
20719
+ sql += `, ${this.quoteCol(this.config.depthKey)} = ${this.quoteCol(this.config.depthKey)} - 1`;
20720
+ }
20721
+ sql += ` WHERE ${this.quoteCol(this.config.leftKey)} > ? AND ${this.quoteCol(this.config.rightKey)} < ?`;
20722
+ await this.executeRawUpdate(sql, [node.lft, node.rght]);
20723
+ }
20709
20724
  await this.shiftForDelete(node.rght, 2);
20710
- NestedSetStrategy.calculateShiftForDelete(node.lft + 1, gap.width - 2);
20711
- if (gap.width > 2) {
20712
- await this.executeRawUpdate(
20713
- `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)} < ?`,
20714
- [node.lft, node.rght]
20715
- );
20725
+ const detachedData = {
20726
+ [this.config.parentKey]: null,
20727
+ [this.config.leftKey]: originalMaxRght - 1,
20728
+ [this.config.rightKey]: originalMaxRght
20729
+ };
20730
+ if (this.config.depthKey) {
20731
+ detachedData[this.config.depthKey] = 0;
20716
20732
  }
20733
+ await this.executeUpdate(
20734
+ eq(this.table.columns[this.pkName], nodeId),
20735
+ detachedData
20736
+ );
20717
20737
  }
20718
20738
  /**
20719
20739
  * Deletes a node and all its descendants.
@@ -20838,22 +20858,32 @@ var TreeManager = class _TreeManager {
20838
20858
  }
20839
20859
  return "id";
20840
20860
  }
20861
+ getScopeEntries() {
20862
+ return Object.entries(buildScopeConditions(this.config.scope, this.scopeValues));
20863
+ }
20864
+ getScopeExpressions() {
20865
+ return this.getScopeEntries().map(
20866
+ ([key, value]) => eq(this.table.columns[key], value)
20867
+ );
20868
+ }
20841
20869
  async getMaxRght() {
20842
20870
  const query = selectFrom(this.table).selectRaw(`MAX(${this.config.rightKey}) as max_rght`);
20843
- const { sql, params } = query.compile(this.dialect);
20871
+ const scopeExpressions = this.getScopeExpressions();
20872
+ const finalQuery = scopeExpressions.length > 0 ? query.where(and(...scopeExpressions)) : query;
20873
+ const { sql, params } = finalQuery.compile(this.dialect);
20844
20874
  const queryResults = await this.executor.executeSql(sql, params);
20845
20875
  const rows = queryResultsToRows(queryResults);
20846
20876
  const maxRght = rows[0]?.max_rght;
20847
20877
  return typeof maxRght === "number" ? maxRght : 0;
20848
20878
  }
20849
- async shiftForInsert(insertPoint) {
20879
+ async shiftForInsert(insertPoint, width = 2) {
20850
20880
  await this.executeRawUpdate(
20851
- `UPDATE ${this.quoteTable()} SET ${this.quoteCol(this.config.rightKey)} = ${this.quoteCol(this.config.rightKey)} + 2 WHERE ${this.quoteCol(this.config.rightKey)} >= ?`,
20852
- [insertPoint]
20881
+ `UPDATE ${this.quoteTable()} SET ${this.quoteCol(this.config.rightKey)} = ${this.quoteCol(this.config.rightKey)} + ? WHERE ${this.quoteCol(this.config.rightKey)} >= ?`,
20882
+ [width, insertPoint]
20853
20883
  );
20854
20884
  await this.executeRawUpdate(
20855
- `UPDATE ${this.quoteTable()} SET ${this.quoteCol(this.config.leftKey)} = ${this.quoteCol(this.config.leftKey)} + 2 WHERE ${this.quoteCol(this.config.leftKey)} > ?`,
20856
- [insertPoint]
20885
+ `UPDATE ${this.quoteTable()} SET ${this.quoteCol(this.config.leftKey)} = ${this.quoteCol(this.config.leftKey)} + ? WHERE ${this.quoteCol(this.config.leftKey)} > ?`,
20886
+ [width, insertPoint]
20857
20887
  );
20858
20888
  }
20859
20889
  async shiftForDelete(deletedRght, width) {
@@ -20889,28 +20919,27 @@ var TreeManager = class _TreeManager {
20889
20919
  }
20890
20920
  async moveSubtree(node, newLft, newParentId, newDepth) {
20891
20921
  const width = NestedSetStrategy.subtreeWidth(node.lft, node.rght);
20892
- const delta = newLft - node.lft;
20893
- const depthDelta = this.config.depthKey ? newDepth - (node.depth ?? 0) : 0;
20922
+ const oldDepth = this.config.depthKey ? node.depth ?? await this.getLevel(node) : 0;
20923
+ const depthDelta = this.config.depthKey ? newDepth - oldDepth : 0;
20894
20924
  const nodeId = node.data[this.pkName];
20895
- const tempOffset = 1e7;
20925
+ const isolateDelta = -1e7 - node.rght;
20926
+ const isolatedLft = node.lft + isolateDelta;
20927
+ const isolatedRght = node.rght + isolateDelta;
20896
20928
  await this.executeRawUpdate(
20897
- `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)} <= ?`,
20898
- [tempOffset, node.lft, node.rght]
20899
- );
20900
- await this.executeRawUpdate(
20901
- `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)} <= ?`,
20902
- [tempOffset, node.lft + tempOffset, node.rght]
20929
+ `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)} <= ?`,
20930
+ [isolateDelta, isolateDelta, node.lft, node.rght]
20903
20931
  );
20904
20932
  await this.shiftForDelete(node.rght, width);
20905
- await this.shiftForInsert(newLft);
20906
- 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)} - ? + ?`;
20907
- const updateParams = [tempOffset, delta, tempOffset, delta];
20933
+ await this.shiftForInsert(newLft, width);
20934
+ const restoreDelta = newLft - node.lft - isolateDelta;
20935
+ 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)} + ?`;
20936
+ const updateParams = [restoreDelta, restoreDelta];
20908
20937
  if (this.config.depthKey && depthDelta !== 0) {
20909
20938
  updateSql += `, ${this.quoteCol(this.config.depthKey)} = ${this.quoteCol(this.config.depthKey)} + ?`;
20910
20939
  updateParams.push(depthDelta);
20911
20940
  }
20912
- updateSql += ` WHERE ${this.quoteCol(this.config.leftKey)} >= ?`;
20913
- updateParams.push(node.lft + tempOffset);
20941
+ updateSql += ` WHERE ${this.quoteCol(this.config.leftKey)} >= ? AND ${this.quoteCol(this.config.rightKey)} <= ?`;
20942
+ updateParams.push(isolatedLft, isolatedRght);
20914
20943
  await this.executeRawUpdate(updateSql, updateParams);
20915
20944
  await this.executeUpdate(
20916
20945
  eq(this.table.columns[this.pkName], nodeId),
@@ -20935,12 +20964,23 @@ var TreeManager = class _TreeManager {
20935
20964
  }));
20936
20965
  }
20937
20966
  async executeUpdate(condition, data) {
20938
- const query = update(this.table).set(data).where(condition);
20967
+ const scopeExpressions = this.getScopeExpressions();
20968
+ const finalCondition = scopeExpressions.length > 0 ? and(condition, ...scopeExpressions) : condition;
20969
+ const query = update(this.table).set(data).where(finalCondition);
20939
20970
  const { sql, params } = query.compile(this.dialect);
20940
20971
  await this.executor.executeSql(sql, params);
20941
20972
  }
20942
20973
  async executeRawUpdate(sql, params) {
20943
- await this.executor.executeSql(sql, params);
20974
+ let scopedSql = sql;
20975
+ const scopedParams = [...params];
20976
+ let hasWhere = /\bWHERE\b/i.test(scopedSql);
20977
+ for (const [key, value] of this.getScopeEntries()) {
20978
+ scopedSql += hasWhere ? " AND " : " WHERE ";
20979
+ scopedSql += `${this.quoteCol(key)} = ?`;
20980
+ scopedParams.push(value);
20981
+ hasWhere = true;
20982
+ }
20983
+ await this.executor.executeSql(scopedSql, scopedParams);
20944
20984
  }
20945
20985
  quoteTable() {
20946
20986
  const quote = this.getQuoteChar();