metal-orm 1.1.6 → 1.1.8

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
@@ -395,6 +395,7 @@ __export(index_exports, {
395
395
  sub: () => sub,
396
396
  substr: () => substr,
397
397
  sum: () => sum,
398
+ syncTreeEntityMetadata: () => syncTreeEntityMetadata,
398
399
  synchronizeSchema: () => synchronizeSchema,
399
400
  tableRef: () => tableRef,
400
401
  tan: () => tan,
@@ -14969,6 +14970,265 @@ var jsonify = (value) => {
14969
14970
  return result;
14970
14971
  };
14971
14972
 
14973
+ // src/tree/tree-types.ts
14974
+ var DEFAULT_TREE_CONFIG = {
14975
+ parentKey: "parentId",
14976
+ leftKey: "lft",
14977
+ rightKey: "rght",
14978
+ cascadeCallbacks: false
14979
+ };
14980
+ function isTreeConfig(value) {
14981
+ if (typeof value !== "object" || value === null) return false;
14982
+ const obj = value;
14983
+ return typeof obj.parentKey === "string" && typeof obj.leftKey === "string" && typeof obj.rightKey === "string";
14984
+ }
14985
+ function resolveTreeConfig(partial) {
14986
+ return {
14987
+ parentKey: partial.parentKey ?? DEFAULT_TREE_CONFIG.parentKey,
14988
+ leftKey: partial.leftKey ?? DEFAULT_TREE_CONFIG.leftKey,
14989
+ rightKey: partial.rightKey ?? DEFAULT_TREE_CONFIG.rightKey,
14990
+ depthKey: partial.depthKey,
14991
+ scope: partial.scope,
14992
+ cascadeCallbacks: partial.cascadeCallbacks ?? DEFAULT_TREE_CONFIG.cascadeCallbacks,
14993
+ recoverOrder: partial.recoverOrder
14994
+ };
14995
+ }
14996
+ function validateTreeTable(table, config) {
14997
+ const missingColumns = [];
14998
+ const warnings = [];
14999
+ const requiredColumns = [config.parentKey, config.leftKey, config.rightKey];
15000
+ for (const col2 of requiredColumns) {
15001
+ if (!(col2 in table.columns)) {
15002
+ missingColumns.push(col2);
15003
+ }
15004
+ }
15005
+ if (config.depthKey && !(config.depthKey in table.columns)) {
15006
+ warnings.push(`Optional depth column '${config.depthKey}' not found in table '${table.name}'`);
15007
+ }
15008
+ if (config.scope) {
15009
+ for (const scopeCol of config.scope) {
15010
+ if (!(scopeCol in table.columns)) {
15011
+ missingColumns.push(scopeCol);
15012
+ }
15013
+ }
15014
+ }
15015
+ const leftCol = table.columns[config.leftKey];
15016
+ const rightCol = table.columns[config.rightKey];
15017
+ if (leftCol && leftCol.type !== "int" && leftCol.type !== "integer") {
15018
+ warnings.push(`Left column '${config.leftKey}' should be an integer type`);
15019
+ }
15020
+ if (rightCol && rightCol.type !== "int" && rightCol.type !== "integer") {
15021
+ warnings.push(`Right column '${config.rightKey}' should be an integer type`);
15022
+ }
15023
+ return {
15024
+ valid: missingColumns.length === 0,
15025
+ missingColumns,
15026
+ warnings
15027
+ };
15028
+ }
15029
+ function getTreeColumns(table, config) {
15030
+ const parentColumn = table.columns[config.parentKey];
15031
+ const leftColumn = table.columns[config.leftKey];
15032
+ const rightColumn = table.columns[config.rightKey];
15033
+ const depthColumn = config.depthKey ? table.columns[config.depthKey] : void 0;
15034
+ const scopeColumns = (config.scope ?? []).map((s) => table.columns[s]).filter(Boolean);
15035
+ if (!parentColumn || !leftColumn || !rightColumn) {
15036
+ throw new Error(
15037
+ `Table '${table.name}' is missing required tree columns. Required: ${config.parentKey}, ${config.leftKey}, ${config.rightKey}`
15038
+ );
15039
+ }
15040
+ return {
15041
+ parentColumn,
15042
+ leftColumn,
15043
+ rightColumn,
15044
+ depthColumn,
15045
+ scopeColumns
15046
+ };
15047
+ }
15048
+
15049
+ // src/tree/tree-decorator.ts
15050
+ var TREE_METADATA_KEY = /* @__PURE__ */ Symbol("metal-orm:tree");
15051
+ function Tree(options = {}) {
15052
+ return function(target, context) {
15053
+ const config = resolveTreeConfig(options);
15054
+ const metadataBag = readMetadataBag(context) ?? readMetadataBagFromConstructor(target);
15055
+ const metadata = {
15056
+ config,
15057
+ parentProperty: metadataBag?.tree?.parentProperty,
15058
+ childrenProperty: metadataBag?.tree?.childrenProperty
15059
+ };
15060
+ setTreeMetadataOnClass(target, metadata);
15061
+ registerTreeRelations(target, metadata);
15062
+ return target;
15063
+ };
15064
+ }
15065
+ function TreeParent() {
15066
+ return function(_value, context) {
15067
+ if (!context.name) {
15068
+ throw new Error("TreeParent decorator requires a property name");
15069
+ }
15070
+ if (context.private) {
15071
+ throw new Error("TreeParent decorator does not support private fields");
15072
+ }
15073
+ const propertyName = String(context.name);
15074
+ const bag = getOrCreateMetadataBag(context);
15075
+ bag.tree = { ...bag.tree, parentProperty: propertyName };
15076
+ };
15077
+ }
15078
+ function TreeChildren() {
15079
+ return function(_value, context) {
15080
+ if (!context.name) {
15081
+ throw new Error("TreeChildren decorator requires a property name");
15082
+ }
15083
+ if (context.private) {
15084
+ throw new Error("TreeChildren decorator does not support private fields");
15085
+ }
15086
+ const propertyName = String(context.name);
15087
+ const bag = getOrCreateMetadataBag(context);
15088
+ bag.tree = { ...bag.tree, childrenProperty: propertyName };
15089
+ };
15090
+ }
15091
+ function syncTreeEntityMetadata(ctor, treeMetadata) {
15092
+ const current = getTreeMetadata(ctor);
15093
+ if (!current) return;
15094
+ const metadataBag = readMetadataBagFromConstructor(ctor);
15095
+ const nextParentProperty = treeMetadata?.parentProperty ?? metadataBag?.tree?.parentProperty ?? current.parentProperty;
15096
+ const nextChildrenProperty = treeMetadata?.childrenProperty ?? metadataBag?.tree?.childrenProperty ?? current.childrenProperty;
15097
+ if (nextParentProperty !== current.parentProperty || nextChildrenProperty !== current.childrenProperty) {
15098
+ setTreeMetadata(ctor, {
15099
+ ...current,
15100
+ parentProperty: nextParentProperty,
15101
+ childrenProperty: nextChildrenProperty
15102
+ });
15103
+ }
15104
+ registerTreeRelations(ctor, {
15105
+ ...current,
15106
+ parentProperty: nextParentProperty,
15107
+ childrenProperty: nextChildrenProperty
15108
+ });
15109
+ }
15110
+ function getTreeMetadata(target) {
15111
+ return target[TREE_METADATA_KEY];
15112
+ }
15113
+ function setTreeMetadata(target, metadata) {
15114
+ target[TREE_METADATA_KEY] = metadata;
15115
+ }
15116
+ var registerTreeRelations = (target, metadata) => {
15117
+ const entityMeta = getEntityMetadata(target);
15118
+ if (!entityMeta) return;
15119
+ if (metadata.parentProperty && !entityMeta.relations[metadata.parentProperty]) {
15120
+ addRelationMetadata(target, metadata.parentProperty, {
15121
+ kind: RelationKinds.BelongsTo,
15122
+ propertyKey: metadata.parentProperty,
15123
+ target: () => target,
15124
+ foreignKey: metadata.config.parentKey
15125
+ });
15126
+ if (entityMeta.table && !entityMeta.table.relations[metadata.parentProperty]) {
15127
+ entityMeta.table.relations[metadata.parentProperty] = belongsTo(
15128
+ entityMeta.table,
15129
+ metadata.config.parentKey
15130
+ );
15131
+ }
15132
+ }
15133
+ if (metadata.childrenProperty && !entityMeta.relations[metadata.childrenProperty]) {
15134
+ addRelationMetadata(target, metadata.childrenProperty, {
15135
+ kind: RelationKinds.HasMany,
15136
+ propertyKey: metadata.childrenProperty,
15137
+ target: () => target,
15138
+ foreignKey: metadata.config.parentKey
15139
+ });
15140
+ if (entityMeta.table && !entityMeta.table.relations[metadata.childrenProperty]) {
15141
+ entityMeta.table.relations[metadata.childrenProperty] = hasMany(
15142
+ entityMeta.table,
15143
+ metadata.config.parentKey
15144
+ );
15145
+ }
15146
+ }
15147
+ };
15148
+ function setTreeMetadataOnClass(target, metadata) {
15149
+ target[TREE_METADATA_KEY] = metadata;
15150
+ }
15151
+ function hasTreeBehavior(target) {
15152
+ return getTreeMetadata(target) !== void 0;
15153
+ }
15154
+ function getTreeConfig(target) {
15155
+ return getTreeMetadata(target)?.config;
15156
+ }
15157
+ var TreeEntityRegistry = class {
15158
+ entities = /* @__PURE__ */ new Map();
15159
+ tableNames = /* @__PURE__ */ new Map();
15160
+ /**
15161
+ * Registers an entity class with its table name.
15162
+ */
15163
+ register(entityClass, tableName) {
15164
+ this.entities.set(tableName, entityClass);
15165
+ this.tableNames.set(entityClass, tableName);
15166
+ }
15167
+ /**
15168
+ * Gets the entity class for a table name.
15169
+ */
15170
+ getByTableName(tableName) {
15171
+ return this.entities.get(tableName);
15172
+ }
15173
+ /**
15174
+ * Gets the table name for an entity class.
15175
+ */
15176
+ getTableName(entityClass) {
15177
+ return this.tableNames.get(entityClass);
15178
+ }
15179
+ /**
15180
+ * Checks if a table has tree behavior.
15181
+ */
15182
+ isTreeTable(tableName) {
15183
+ const entity = this.entities.get(tableName);
15184
+ return entity ? hasTreeBehavior(entity) : false;
15185
+ }
15186
+ /**
15187
+ * Gets all registered tree entities.
15188
+ */
15189
+ getAll() {
15190
+ const result = [];
15191
+ for (const [tableName, entityClass] of this.entities) {
15192
+ const metadata = getTreeMetadata(entityClass);
15193
+ if (metadata) {
15194
+ result.push({ entityClass, tableName, metadata });
15195
+ }
15196
+ }
15197
+ return result;
15198
+ }
15199
+ /**
15200
+ * Clears the registry.
15201
+ */
15202
+ clear() {
15203
+ this.entities.clear();
15204
+ this.tableNames.clear();
15205
+ }
15206
+ };
15207
+ var treeEntityRegistry = new TreeEntityRegistry();
15208
+ function getTreeBounds(entity, config) {
15209
+ const data = entity;
15210
+ const lft = data[config.leftKey];
15211
+ const rght = data[config.rightKey];
15212
+ if (typeof lft !== "number" || typeof rght !== "number") {
15213
+ return null;
15214
+ }
15215
+ return { lft, rght };
15216
+ }
15217
+ function getTreeParentId(entity, config) {
15218
+ return entity[config.parentKey];
15219
+ }
15220
+ function setTreeBounds(entity, config, lft, rght, depth) {
15221
+ const data = entity;
15222
+ data[config.leftKey] = lft;
15223
+ data[config.rightKey] = rght;
15224
+ if (config.depthKey && depth !== void 0) {
15225
+ data[config.depthKey] = depth;
15226
+ }
15227
+ }
15228
+ function setTreeParentId(entity, config, parentId) {
15229
+ entity[config.parentKey] = parentId;
15230
+ }
15231
+
14972
15232
  // src/decorators/entity.ts
14973
15233
  var toSnakeCase2 = (value) => {
14974
15234
  return value.replace(/([a-z0-9])([A-Z])/g, "$1_$2").replace(/[^a-z0-9_]+/gi, "_").replace(/__+/g, "_").replace(/^_|_$/g, "").toLowerCase();
@@ -15012,6 +15272,7 @@ function Entity(options = {}) {
15012
15272
  addRelationMetadata(ctor, entry.propertyName, relationCopy);
15013
15273
  }
15014
15274
  }
15275
+ syncTreeEntityMetadata(ctor, bag?.tree);
15015
15276
  return ctor;
15016
15277
  };
15017
15278
  }
@@ -17938,208 +18199,6 @@ function extractReusableSchemas(schema, existing = {}, prefix = "") {
17938
18199
  return existing;
17939
18200
  }
17940
18201
 
17941
- // src/tree/tree-types.ts
17942
- var DEFAULT_TREE_CONFIG = {
17943
- parentKey: "parentId",
17944
- leftKey: "lft",
17945
- rightKey: "rght",
17946
- cascadeCallbacks: false
17947
- };
17948
- function isTreeConfig(value) {
17949
- if (typeof value !== "object" || value === null) return false;
17950
- const obj = value;
17951
- return typeof obj.parentKey === "string" && typeof obj.leftKey === "string" && typeof obj.rightKey === "string";
17952
- }
17953
- function resolveTreeConfig(partial) {
17954
- return {
17955
- parentKey: partial.parentKey ?? DEFAULT_TREE_CONFIG.parentKey,
17956
- leftKey: partial.leftKey ?? DEFAULT_TREE_CONFIG.leftKey,
17957
- rightKey: partial.rightKey ?? DEFAULT_TREE_CONFIG.rightKey,
17958
- depthKey: partial.depthKey,
17959
- scope: partial.scope,
17960
- cascadeCallbacks: partial.cascadeCallbacks ?? DEFAULT_TREE_CONFIG.cascadeCallbacks,
17961
- recoverOrder: partial.recoverOrder
17962
- };
17963
- }
17964
- function validateTreeTable(table, config) {
17965
- const missingColumns = [];
17966
- const warnings = [];
17967
- const requiredColumns = [config.parentKey, config.leftKey, config.rightKey];
17968
- for (const col2 of requiredColumns) {
17969
- if (!(col2 in table.columns)) {
17970
- missingColumns.push(col2);
17971
- }
17972
- }
17973
- if (config.depthKey && !(config.depthKey in table.columns)) {
17974
- warnings.push(`Optional depth column '${config.depthKey}' not found in table '${table.name}'`);
17975
- }
17976
- if (config.scope) {
17977
- for (const scopeCol of config.scope) {
17978
- if (!(scopeCol in table.columns)) {
17979
- missingColumns.push(scopeCol);
17980
- }
17981
- }
17982
- }
17983
- const leftCol = table.columns[config.leftKey];
17984
- const rightCol = table.columns[config.rightKey];
17985
- if (leftCol && leftCol.type !== "int" && leftCol.type !== "integer") {
17986
- warnings.push(`Left column '${config.leftKey}' should be an integer type`);
17987
- }
17988
- if (rightCol && rightCol.type !== "int" && rightCol.type !== "integer") {
17989
- warnings.push(`Right column '${config.rightKey}' should be an integer type`);
17990
- }
17991
- return {
17992
- valid: missingColumns.length === 0,
17993
- missingColumns,
17994
- warnings
17995
- };
17996
- }
17997
- function getTreeColumns(table, config) {
17998
- const parentColumn = table.columns[config.parentKey];
17999
- const leftColumn = table.columns[config.leftKey];
18000
- const rightColumn = table.columns[config.rightKey];
18001
- const depthColumn = config.depthKey ? table.columns[config.depthKey] : void 0;
18002
- const scopeColumns = (config.scope ?? []).map((s) => table.columns[s]).filter(Boolean);
18003
- if (!parentColumn || !leftColumn || !rightColumn) {
18004
- throw new Error(
18005
- `Table '${table.name}' is missing required tree columns. Required: ${config.parentKey}, ${config.leftKey}, ${config.rightKey}`
18006
- );
18007
- }
18008
- return {
18009
- parentColumn,
18010
- leftColumn,
18011
- rightColumn,
18012
- depthColumn,
18013
- scopeColumns
18014
- };
18015
- }
18016
-
18017
- // src/tree/tree-decorator.ts
18018
- var TREE_METADATA_KEY = /* @__PURE__ */ Symbol("metal-orm:tree");
18019
- function Tree(options = {}) {
18020
- return function(target) {
18021
- const config = resolveTreeConfig(options);
18022
- const metadata = { config };
18023
- setTreeMetadataOnClass(target, metadata);
18024
- return target;
18025
- };
18026
- }
18027
- function TreeParent() {
18028
- return function(_value, context) {
18029
- const propertyName = String(context.name);
18030
- context.addInitializer(function() {
18031
- const constructor = this.constructor;
18032
- const metadata = getTreeMetadata(constructor);
18033
- if (metadata) {
18034
- metadata.parentProperty = propertyName;
18035
- setTreeMetadata(constructor, metadata);
18036
- }
18037
- });
18038
- };
18039
- }
18040
- function TreeChildren() {
18041
- return function(_value, context) {
18042
- const propertyName = String(context.name);
18043
- context.addInitializer(function() {
18044
- const constructor = this.constructor;
18045
- const metadata = getTreeMetadata(constructor);
18046
- if (metadata) {
18047
- metadata.childrenProperty = propertyName;
18048
- setTreeMetadata(constructor, metadata);
18049
- }
18050
- });
18051
- };
18052
- }
18053
- function getTreeMetadata(target) {
18054
- return target[TREE_METADATA_KEY];
18055
- }
18056
- function setTreeMetadata(target, metadata) {
18057
- target[TREE_METADATA_KEY] = metadata;
18058
- }
18059
- function setTreeMetadataOnClass(target, metadata) {
18060
- target[TREE_METADATA_KEY] = metadata;
18061
- }
18062
- function hasTreeBehavior(target) {
18063
- return getTreeMetadata(target) !== void 0;
18064
- }
18065
- function getTreeConfig(target) {
18066
- return getTreeMetadata(target)?.config;
18067
- }
18068
- var TreeEntityRegistry = class {
18069
- entities = /* @__PURE__ */ new Map();
18070
- tableNames = /* @__PURE__ */ new Map();
18071
- /**
18072
- * Registers an entity class with its table name.
18073
- */
18074
- register(entityClass, tableName) {
18075
- this.entities.set(tableName, entityClass);
18076
- this.tableNames.set(entityClass, tableName);
18077
- }
18078
- /**
18079
- * Gets the entity class for a table name.
18080
- */
18081
- getByTableName(tableName) {
18082
- return this.entities.get(tableName);
18083
- }
18084
- /**
18085
- * Gets the table name for an entity class.
18086
- */
18087
- getTableName(entityClass) {
18088
- return this.tableNames.get(entityClass);
18089
- }
18090
- /**
18091
- * Checks if a table has tree behavior.
18092
- */
18093
- isTreeTable(tableName) {
18094
- const entity = this.entities.get(tableName);
18095
- return entity ? hasTreeBehavior(entity) : false;
18096
- }
18097
- /**
18098
- * Gets all registered tree entities.
18099
- */
18100
- getAll() {
18101
- const result = [];
18102
- for (const [tableName, entityClass] of this.entities) {
18103
- const metadata = getTreeMetadata(entityClass);
18104
- if (metadata) {
18105
- result.push({ entityClass, tableName, metadata });
18106
- }
18107
- }
18108
- return result;
18109
- }
18110
- /**
18111
- * Clears the registry.
18112
- */
18113
- clear() {
18114
- this.entities.clear();
18115
- this.tableNames.clear();
18116
- }
18117
- };
18118
- var treeEntityRegistry = new TreeEntityRegistry();
18119
- function getTreeBounds(entity, config) {
18120
- const data = entity;
18121
- const lft = data[config.leftKey];
18122
- const rght = data[config.rightKey];
18123
- if (typeof lft !== "number" || typeof rght !== "number") {
18124
- return null;
18125
- }
18126
- return { lft, rght };
18127
- }
18128
- function getTreeParentId(entity, config) {
18129
- return entity[config.parentKey];
18130
- }
18131
- function setTreeBounds(entity, config, lft, rght, depth) {
18132
- const data = entity;
18133
- data[config.leftKey] = lft;
18134
- data[config.rightKey] = rght;
18135
- if (config.depthKey && depth !== void 0) {
18136
- data[config.depthKey] = depth;
18137
- }
18138
- }
18139
- function setTreeParentId(entity, config, parentId) {
18140
- entity[config.parentKey] = parentId;
18141
- }
18142
-
18143
18202
  // src/dto/openapi/generators/tree.ts
18144
18203
  function threadedNodeToOpenApiSchema(target, options) {
18145
18204
  const componentName = options?.componentName;
@@ -20164,6 +20223,7 @@ var TagIndex = class {
20164
20223
  sub,
20165
20224
  substr,
20166
20225
  sum,
20226
+ syncTreeEntityMetadata,
20167
20227
  synchronizeSchema,
20168
20228
  tableRef,
20169
20229
  tan,