metal-orm 1.1.11 → 1.1.18

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.
Files changed (182) hide show
  1. package/README.md +758 -769
  2. package/dist/index.cjs.map +1 -1
  3. package/dist/index.js.map +1 -1
  4. package/package.json +100 -96
  5. package/scripts/generate-entities/schema.mjs +122 -122
  6. package/scripts/generate-entities/tree-detection.mjs +92 -92
  7. package/scripts/inflection/pt-br.mjs +468 -468
  8. package/scripts/run-eslint.mjs +33 -34
  9. package/scripts/show-sql-seed.sql +102 -0
  10. package/scripts/show-sql.mjs +321 -325
  11. package/src/codegen/naming-strategy.ts +54 -54
  12. package/src/codegen/typescript.ts +482 -482
  13. package/src/core/ast/adapters.ts +1 -1
  14. package/src/core/ast/builders.ts +123 -123
  15. package/src/core/ast/expression-builders.ts +745 -745
  16. package/src/core/ast/expression-nodes.ts +304 -304
  17. package/src/core/ast/expression-visitor.ts +212 -212
  18. package/src/core/ast/expression.ts +20 -20
  19. package/src/core/ast/helpers.ts +14 -14
  20. package/src/core/ast/join.ts +18 -18
  21. package/src/core/ast/query.ts +200 -200
  22. package/src/core/ddl/dialects/base-schema-dialect.ts +1 -1
  23. package/src/core/ddl/dialects/mssql-schema-dialect.ts +1 -1
  24. package/src/core/ddl/dialects/mysql-schema-dialect.ts +1 -1
  25. package/src/core/ddl/dialects/postgres-schema-dialect.ts +1 -1
  26. package/src/core/ddl/dialects/render-reference.test.ts +70 -70
  27. package/src/core/ddl/dialects/sqlite-schema-dialect.ts +1 -1
  28. package/src/core/ddl/introspect/catalogs/index.ts +5 -5
  29. package/src/core/ddl/introspect/catalogs/postgres.ts +146 -146
  30. package/src/core/ddl/introspect/context.ts +15 -15
  31. package/src/core/ddl/introspect/functions/postgres.ts +35 -35
  32. package/src/core/ddl/introspect/registry.ts +40 -40
  33. package/src/core/ddl/introspect/run-select.ts +35 -35
  34. package/src/core/ddl/introspect/utils.ts +56 -56
  35. package/src/core/ddl/naming-strategy.ts +16 -16
  36. package/src/core/ddl/schema-dialect.ts +56 -56
  37. package/src/core/ddl/schema-diff.ts +234 -234
  38. package/src/core/ddl/schema-generator.ts +1 -1
  39. package/src/core/ddl/schema-introspect.ts +25 -25
  40. package/src/core/ddl/sql-writing.ts +171 -171
  41. package/src/core/dialect/abstract.ts +541 -541
  42. package/src/core/dialect/base/cte-compiler.ts +33 -33
  43. package/src/core/dialect/base/function-table-formatter.ts +103 -103
  44. package/src/core/dialect/base/groupby-compiler.ts +21 -21
  45. package/src/core/dialect/base/join-compiler.ts +25 -25
  46. package/src/core/dialect/base/orderby-compiler.ts +35 -35
  47. package/src/core/dialect/base/pagination-strategy.ts +32 -32
  48. package/src/core/dialect/base/returning-strategy.ts +57 -57
  49. package/src/core/dialect/base/sql-dialect.ts +294 -294
  50. package/src/core/dialect/dialect-factory.ts +91 -91
  51. package/src/core/dialect/mssql/functions.ts +120 -120
  52. package/src/core/dialect/mssql/index.ts +317 -317
  53. package/src/core/dialect/mysql/functions.ts +103 -103
  54. package/src/core/dialect/mysql/index.ts +105 -105
  55. package/src/core/dialect/postgres/functions.ts +108 -108
  56. package/src/core/dialect/postgres/index.ts +91 -91
  57. package/src/core/dialect/sqlite/functions.ts +129 -129
  58. package/src/core/dialect/sqlite/index.ts +67 -67
  59. package/src/core/driver/database-driver.ts +19 -19
  60. package/src/core/driver/mssql-driver.ts +23 -23
  61. package/src/core/driver/mysql-driver.ts +23 -23
  62. package/src/core/driver/postgres-driver.ts +23 -23
  63. package/src/core/driver/sqlite-driver.ts +23 -23
  64. package/src/core/execution/db-executor.ts +92 -92
  65. package/src/core/execution/executors/better-sqlite3-executor.ts +90 -90
  66. package/src/core/execution/executors/mssql-executor.ts +184 -184
  67. package/src/core/execution/executors/mysql-executor.ts +88 -88
  68. package/src/core/execution/executors/postgres-executor.ts +26 -26
  69. package/src/core/execution/executors/sqlite-executor.ts +42 -42
  70. package/src/core/execution/pooling/pool-types.ts +30 -30
  71. package/src/core/execution/pooling/pool.ts +275 -275
  72. package/src/core/functions/array.ts +35 -35
  73. package/src/core/functions/control-flow.ts +79 -79
  74. package/src/core/functions/datetime.ts +237 -237
  75. package/src/core/functions/json.ts +70 -70
  76. package/src/core/functions/numeric.ts +297 -297
  77. package/src/core/functions/text.ts +334 -334
  78. package/src/core/functions/types.ts +33 -33
  79. package/src/core/hydration/types.ts +51 -51
  80. package/src/core/sql/sql-operator-config.ts +39 -39
  81. package/src/core/sql/sql.ts +152 -152
  82. package/src/decorators/column-decorator.ts +87 -87
  83. package/src/decorators/decorator-metadata.ts +143 -143
  84. package/src/decorators/entity.ts +93 -93
  85. package/src/decorators/index.ts +39 -39
  86. package/src/decorators/relations.ts +231 -231
  87. package/src/decorators/transformers/built-in/string-transformers.ts +231 -231
  88. package/src/decorators/transformers/transformer-decorators.ts +134 -134
  89. package/src/decorators/transformers/transformer-executor.ts +171 -171
  90. package/src/decorators/transformers/transformer-metadata.ts +73 -73
  91. package/src/decorators/validators/built-in/br-cep-validator.ts +55 -55
  92. package/src/decorators/validators/built-in/br-cnpj-validator.ts +105 -105
  93. package/src/decorators/validators/built-in/br-cpf-validator.ts +103 -103
  94. package/src/decorators/validators/country-validator-registry.ts +64 -64
  95. package/src/decorators/validators/country-validators-decorators.ts +203 -203
  96. package/src/decorators/validators/country-validators.ts +105 -105
  97. package/src/decorators/validators/validator-metadata.ts +59 -59
  98. package/src/dto/dto-types.ts +226 -226
  99. package/src/dto/filter-types.ts +141 -141
  100. package/src/dto/index.ts +95 -95
  101. package/src/dto/openapi/generators/base.ts +29 -29
  102. package/src/dto/openapi/generators/column.ts +37 -37
  103. package/src/dto/openapi/generators/dto.ts +91 -91
  104. package/src/dto/openapi/generators/filter.ts +75 -75
  105. package/src/dto/openapi/generators/nested-dto.ts +618 -618
  106. package/src/dto/openapi/generators/pagination.ts +111 -111
  107. package/src/dto/openapi/generators/relation-filter.ts +228 -228
  108. package/src/dto/openapi/index.ts +12 -12
  109. package/src/dto/openapi/types.ts +101 -101
  110. package/src/dto/openapi/utilities.ts +95 -95
  111. package/src/dto/pagination-utils.ts +150 -150
  112. package/src/dto/transform.ts +197 -197
  113. package/src/index.ts +86 -86
  114. package/src/orm/als.ts +58 -58
  115. package/src/orm/domain-event-bus.ts +119 -119
  116. package/src/orm/entity-context.ts +86 -86
  117. package/src/orm/entity-materializer.ts +159 -159
  118. package/src/orm/entity-meta.ts +97 -97
  119. package/src/orm/entity-registry.ts +39 -39
  120. package/src/orm/entity-relation-cache.ts +39 -39
  121. package/src/orm/execute.ts +194 -194
  122. package/src/orm/execution-context.ts +18 -18
  123. package/src/orm/hydration-context.ts +26 -26
  124. package/src/orm/hydration.ts +112 -112
  125. package/src/orm/identity-map.ts +61 -61
  126. package/src/orm/interceptor-pipeline.ts +16 -16
  127. package/src/orm/lazy-batch/belongs-to-many.ts +119 -119
  128. package/src/orm/lazy-batch/belongs-to.ts +108 -108
  129. package/src/orm/lazy-batch/has-many.ts +69 -69
  130. package/src/orm/lazy-batch/has-one.ts +68 -68
  131. package/src/orm/lazy-batch/shared.ts +125 -125
  132. package/src/orm/orm-session.ts +620 -620
  133. package/src/orm/orm.ts +34 -34
  134. package/src/orm/pooled-executor-factory.ts +121 -121
  135. package/src/orm/query-logger.ts +40 -40
  136. package/src/orm/relation-change-processor.ts +420 -420
  137. package/src/orm/relations/belongs-to.ts +143 -143
  138. package/src/orm/relations/has-many.ts +204 -204
  139. package/src/orm/relations/has-one.ts +174 -174
  140. package/src/orm/relations/many-to-many.ts +288 -288
  141. package/src/orm/relations/morph-many.ts +156 -156
  142. package/src/orm/relations/morph-one.ts +151 -151
  143. package/src/orm/relations/morph-to.ts +162 -162
  144. package/src/orm/runtime-types.ts +92 -92
  145. package/src/orm/save-graph.ts +561 -561
  146. package/src/orm/transaction-runner.ts +24 -24
  147. package/src/orm/unit-of-work.ts +426 -426
  148. package/src/query/index.ts +69 -69
  149. package/src/query/target.ts +46 -46
  150. package/src/query-builder/column-selector.ts +81 -81
  151. package/src/query-builder/delete.ts +141 -141
  152. package/src/query-builder/insert-query-state.ts +149 -149
  153. package/src/query-builder/insert.ts +117 -117
  154. package/src/query-builder/query-ast-service.ts +287 -287
  155. package/src/query-builder/query-resolution.ts +78 -78
  156. package/src/query-builder/raw-column-parser.ts +38 -38
  157. package/src/query-builder/relation-filter-utils.ts +153 -153
  158. package/src/query-builder/relation-join-planner.ts +10 -10
  159. package/src/query-builder/relation-manager.ts +87 -87
  160. package/src/query-builder/relation-projection-helper.ts +102 -102
  161. package/src/query-builder/relation-types.ts +9 -9
  162. package/src/query-builder/relation-utils.ts +15 -15
  163. package/src/query-builder/select/cte-facet.ts +40 -40
  164. package/src/query-builder/select/from-facet.ts +80 -80
  165. package/src/query-builder/select/join-facet.ts +62 -62
  166. package/src/query-builder/select/predicate-facet.ts +104 -104
  167. package/src/query-builder/select/projection-facet.ts +70 -70
  168. package/src/query-builder/select/relation-facet.ts +81 -81
  169. package/src/query-builder/select/setop-facet.ts +36 -36
  170. package/src/query-builder/select-helpers.ts +1 -1
  171. package/src/query-builder/select-query-builder-deps.ts +130 -130
  172. package/src/query-builder/select-query-state.ts +207 -207
  173. package/src/query-builder/update.ts +151 -151
  174. package/src/schema/column-types.ts +322 -322
  175. package/src/schema/table-guards.ts +37 -37
  176. package/src/schema/table.ts +281 -281
  177. package/src/tree/index.ts +13 -13
  178. package/src/tree/nested-set-strategy.ts +545 -545
  179. package/src/tree/tree-decorator.ts +397 -397
  180. package/src/tree/tree-manager.ts +754 -754
  181. package/src/tree/tree-query.ts +427 -427
  182. package/src/tree/tree-types.ts +299 -299
@@ -1,299 +1,299 @@
1
- /**
2
- * Tree Behavior Types
3
- *
4
- * Core type definitions for the Nested Set (MPTT) tree implementation.
5
- * These types support hierarchical data structures in relational databases.
6
- */
7
-
8
- import type { TableDef } from '../schema/table.js';
9
- import type { ColumnDef } from '../schema/column-types.js';
10
-
11
- /**
12
- * Configuration options for tree behavior.
13
- */
14
- export interface TreeConfig {
15
- /** Column name for parent reference (default: 'parentId') */
16
- parentKey: string;
17
- /** Column name for left boundary (default: 'lft') */
18
- leftKey: string;
19
- /** Column name for right boundary (default: 'rght') */
20
- rightKey: string;
21
- /** Optional column name for cached depth level */
22
- depthKey?: string;
23
- /** Columns used for scoping multiple trees in one table (e.g., ['tenantId']) */
24
- scope?: string[];
25
- /** Whether to load and delete children individually to trigger hooks (default: false) */
26
- cascadeCallbacks?: boolean;
27
- /** Custom ordering for tree recovery (default: primary key ASC) */
28
- recoverOrder?: Record<string, 'ASC' | 'DESC'>;
29
- }
30
-
31
- /**
32
- * Default tree configuration values.
33
- */
34
- export const DEFAULT_TREE_CONFIG: Readonly<Required<Omit<TreeConfig, 'depthKey' | 'scope' | 'recoverOrder'>>> = {
35
- parentKey: 'parentId',
36
- leftKey: 'lft',
37
- rightKey: 'rght',
38
- cascadeCallbacks: false,
39
- };
40
-
41
- /**
42
- * Represents a node in the tree with its nested set boundaries.
43
- * @typeParam T - The entity type
44
- */
45
- export interface TreeNode<T> {
46
- /** The underlying entity */
47
- entity: T;
48
- /** Left boundary value */
49
- lft: number;
50
- /** Right boundary value */
51
- rght: number;
52
- /** Depth level (0 = root), if available */
53
- depth?: number;
54
- /** Whether this node has no children */
55
- isLeaf: boolean;
56
- /** Whether this node has no parent */
57
- isRoot: boolean;
58
- /** Number of descendants (calculated from lft/rght) */
59
- childCount: number;
60
- }
61
-
62
- /**
63
- * Options for finding children/descendants.
64
- */
65
- export interface FindChildrenOptions {
66
- /** Only return immediate children (default: false = all descendants) */
67
- direct?: boolean;
68
- /** Nest results into a tree structure with .children property */
69
- threaded?: boolean;
70
- /** Maximum depth to fetch (undefined = unlimited) */
71
- depth?: number;
72
- }
73
-
74
- /**
75
- * Options for generating a tree list (for dropdowns/selects).
76
- */
77
- export interface TreeListOptions {
78
- /** Dot-separated path to the field used as key (default: primary key) */
79
- keyPath?: string;
80
- /** Dot-separated path to the field used as value (default: primary key) */
81
- valuePath?: string;
82
- /** Prefix string repeated per depth level (default: '__') */
83
- spacer?: string;
84
- }
85
-
86
- /**
87
- * Options for finding the path from a node to root.
88
- */
89
- export interface FindPathOptions {
90
- /** Include the node itself in the path (default: true) */
91
- includeSelf?: boolean;
92
- /** Order of results: 'asc' = root first, 'desc' = node first (default: 'asc') */
93
- order?: 'asc' | 'desc';
94
- }
95
-
96
- /**
97
- * Options for moving a node within the tree.
98
- */
99
- export interface MoveOptions {
100
- /** Position relative to target: 'before', 'after', 'firstChild', 'lastChild' */
101
- position?: 'before' | 'after' | 'firstChild' | 'lastChild';
102
- }
103
-
104
- /**
105
- * Result of a tree recovery operation.
106
- */
107
- export interface RecoverResult {
108
- /** Number of nodes processed */
109
- processed: number;
110
- /** Whether the recovery was successful */
111
- success: boolean;
112
- /** Any errors encountered */
113
- errors?: string[];
114
- }
115
-
116
- /**
117
- * Scope values for multi-tree tables.
118
- */
119
- export type TreeScope = Record<string, unknown>;
120
-
121
- /**
122
- * Internal representation of nested set boundaries for calculations.
123
- */
124
- export interface NestedSetBounds {
125
- lft: number;
126
- rght: number;
127
- }
128
-
129
- /**
130
- * Data required to insert a new node into the tree.
131
- */
132
- export interface TreeInsertData {
133
- /** Parent node ID (null for root) */
134
- parentId: unknown;
135
- /** Calculated left boundary */
136
- lft: number;
137
- /** Calculated right boundary */
138
- rght: number;
139
- /** Calculated depth (if depthKey is configured) */
140
- depth?: number;
141
- }
142
-
143
- /**
144
- * Data required to move a node within the tree.
145
- */
146
- export interface TreeMoveData {
147
- /** New parent node ID */
148
- newParentId: unknown;
149
- /** New left boundary */
150
- newLft: number;
151
- /** New right boundary */
152
- newRght: number;
153
- /** New depth (if depthKey is configured) */
154
- newDepth?: number;
155
- /** Delta to apply to lft/rght of affected nodes */
156
- delta: number;
157
- }
158
-
159
- /**
160
- * Represents a node with its children in a threaded tree structure.
161
- * @typeParam T - The entity type
162
- */
163
- export interface ThreadedNode<T> {
164
- /** The node data */
165
- node: T;
166
- /** Child nodes */
167
- children: ThreadedNode<T>[];
168
- }
169
-
170
- /**
171
- * A tree list entry for dropdown/select rendering.
172
- */
173
- export interface TreeListEntry<K = unknown, V = string> {
174
- /** The key (usually primary key) */
175
- key: K;
176
- /** The display value with depth prefix */
177
- value: V;
178
- /** The depth level */
179
- depth: number;
180
- }
181
-
182
- /**
183
- * Validates that a table has the required tree columns.
184
- * @param table - The table definition to validate
185
- * @param config - The tree configuration
186
- * @returns Validation result with any missing columns
187
- */
188
- export interface TreeValidationResult {
189
- valid: boolean;
190
- missingColumns: string[];
191
- warnings: string[];
192
- }
193
-
194
- /**
195
- * Extracts tree column definitions from a table.
196
- */
197
- export interface TreeColumns {
198
- parentColumn: ColumnDef;
199
- leftColumn: ColumnDef;
200
- rightColumn: ColumnDef;
201
- depthColumn?: ColumnDef;
202
- scopeColumns: ColumnDef[];
203
- }
204
-
205
- /**
206
- * Type guard to check if a value is a valid TreeConfig.
207
- */
208
- export function isTreeConfig(value: unknown): value is TreeConfig {
209
- if (typeof value !== 'object' || value === null) return false;
210
- const obj = value as Record<string, unknown>;
211
- return (
212
- typeof obj.parentKey === 'string' &&
213
- typeof obj.leftKey === 'string' &&
214
- typeof obj.rightKey === 'string'
215
- );
216
- }
217
-
218
- /**
219
- * Merges partial config with defaults.
220
- */
221
- export function resolveTreeConfig(partial: Partial<TreeConfig>): TreeConfig {
222
- return {
223
- parentKey: partial.parentKey ?? DEFAULT_TREE_CONFIG.parentKey,
224
- leftKey: partial.leftKey ?? DEFAULT_TREE_CONFIG.leftKey,
225
- rightKey: partial.rightKey ?? DEFAULT_TREE_CONFIG.rightKey,
226
- depthKey: partial.depthKey,
227
- scope: partial.scope,
228
- cascadeCallbacks: partial.cascadeCallbacks ?? DEFAULT_TREE_CONFIG.cascadeCallbacks,
229
- recoverOrder: partial.recoverOrder,
230
- };
231
- }
232
-
233
- /**
234
- * Validates that a table definition contains the required tree columns.
235
- */
236
- export function validateTreeTable(table: TableDef, config: TreeConfig): TreeValidationResult {
237
- const missingColumns: string[] = [];
238
- const warnings: string[] = [];
239
-
240
- const requiredColumns = [config.parentKey, config.leftKey, config.rightKey];
241
- for (const col of requiredColumns) {
242
- if (!(col in table.columns)) {
243
- missingColumns.push(col);
244
- }
245
- }
246
-
247
- if (config.depthKey && !(config.depthKey in table.columns)) {
248
- warnings.push(`Optional depth column '${config.depthKey}' not found in table '${table.name}'`);
249
- }
250
-
251
- if (config.scope) {
252
- for (const scopeCol of config.scope) {
253
- if (!(scopeCol in table.columns)) {
254
- missingColumns.push(scopeCol);
255
- }
256
- }
257
- }
258
-
259
- const leftCol = table.columns[config.leftKey];
260
- const rightCol = table.columns[config.rightKey];
261
- if (leftCol && leftCol.type !== 'int' && leftCol.type !== 'integer') {
262
- warnings.push(`Left column '${config.leftKey}' should be an integer type`);
263
- }
264
- if (rightCol && rightCol.type !== 'int' && rightCol.type !== 'integer') {
265
- warnings.push(`Right column '${config.rightKey}' should be an integer type`);
266
- }
267
-
268
- return {
269
- valid: missingColumns.length === 0,
270
- missingColumns,
271
- warnings,
272
- };
273
- }
274
-
275
- /**
276
- * Extracts tree-related columns from a table definition.
277
- */
278
- export function getTreeColumns(table: TableDef, config: TreeConfig): TreeColumns {
279
- const parentColumn = table.columns[config.parentKey];
280
- const leftColumn = table.columns[config.leftKey];
281
- const rightColumn = table.columns[config.rightKey];
282
- const depthColumn = config.depthKey ? table.columns[config.depthKey] : undefined;
283
- const scopeColumns = (config.scope ?? []).map(s => table.columns[s]).filter(Boolean);
284
-
285
- if (!parentColumn || !leftColumn || !rightColumn) {
286
- throw new Error(
287
- `Table '${table.name}' is missing required tree columns. ` +
288
- `Required: ${config.parentKey}, ${config.leftKey}, ${config.rightKey}`
289
- );
290
- }
291
-
292
- return {
293
- parentColumn,
294
- leftColumn,
295
- rightColumn,
296
- depthColumn,
297
- scopeColumns,
298
- };
299
- }
1
+ /**
2
+ * Tree Behavior Types
3
+ *
4
+ * Core type definitions for the Nested Set (MPTT) tree implementation.
5
+ * These types support hierarchical data structures in relational databases.
6
+ */
7
+
8
+ import type { TableDef } from '../schema/table.js';
9
+ import type { ColumnDef } from '../schema/column-types.js';
10
+
11
+ /**
12
+ * Configuration options for tree behavior.
13
+ */
14
+ export interface TreeConfig {
15
+ /** Column name for parent reference (default: 'parentId') */
16
+ parentKey: string;
17
+ /** Column name for left boundary (default: 'lft') */
18
+ leftKey: string;
19
+ /** Column name for right boundary (default: 'rght') */
20
+ rightKey: string;
21
+ /** Optional column name for cached depth level */
22
+ depthKey?: string;
23
+ /** Columns used for scoping multiple trees in one table (e.g., ['tenantId']) */
24
+ scope?: string[];
25
+ /** Whether to load and delete children individually to trigger hooks (default: false) */
26
+ cascadeCallbacks?: boolean;
27
+ /** Custom ordering for tree recovery (default: primary key ASC) */
28
+ recoverOrder?: Record<string, 'ASC' | 'DESC'>;
29
+ }
30
+
31
+ /**
32
+ * Default tree configuration values.
33
+ */
34
+ export const DEFAULT_TREE_CONFIG: Readonly<Required<Omit<TreeConfig, 'depthKey' | 'scope' | 'recoverOrder'>>> = {
35
+ parentKey: 'parentId',
36
+ leftKey: 'lft',
37
+ rightKey: 'rght',
38
+ cascadeCallbacks: false,
39
+ };
40
+
41
+ /**
42
+ * Represents a node in the tree with its nested set boundaries.
43
+ * @typeParam T - The entity type
44
+ */
45
+ export interface TreeNode<T> {
46
+ /** The underlying entity */
47
+ entity: T;
48
+ /** Left boundary value */
49
+ lft: number;
50
+ /** Right boundary value */
51
+ rght: number;
52
+ /** Depth level (0 = root), if available */
53
+ depth?: number;
54
+ /** Whether this node has no children */
55
+ isLeaf: boolean;
56
+ /** Whether this node has no parent */
57
+ isRoot: boolean;
58
+ /** Number of descendants (calculated from lft/rght) */
59
+ childCount: number;
60
+ }
61
+
62
+ /**
63
+ * Options for finding children/descendants.
64
+ */
65
+ export interface FindChildrenOptions {
66
+ /** Only return immediate children (default: false = all descendants) */
67
+ direct?: boolean;
68
+ /** Nest results into a tree structure with .children property */
69
+ threaded?: boolean;
70
+ /** Maximum depth to fetch (undefined = unlimited) */
71
+ depth?: number;
72
+ }
73
+
74
+ /**
75
+ * Options for generating a tree list (for dropdowns/selects).
76
+ */
77
+ export interface TreeListOptions {
78
+ /** Dot-separated path to the field used as key (default: primary key) */
79
+ keyPath?: string;
80
+ /** Dot-separated path to the field used as value (default: primary key) */
81
+ valuePath?: string;
82
+ /** Prefix string repeated per depth level (default: '__') */
83
+ spacer?: string;
84
+ }
85
+
86
+ /**
87
+ * Options for finding the path from a node to root.
88
+ */
89
+ export interface FindPathOptions {
90
+ /** Include the node itself in the path (default: true) */
91
+ includeSelf?: boolean;
92
+ /** Order of results: 'asc' = root first, 'desc' = node first (default: 'asc') */
93
+ order?: 'asc' | 'desc';
94
+ }
95
+
96
+ /**
97
+ * Options for moving a node within the tree.
98
+ */
99
+ export interface MoveOptions {
100
+ /** Position relative to target: 'before', 'after', 'firstChild', 'lastChild' */
101
+ position?: 'before' | 'after' | 'firstChild' | 'lastChild';
102
+ }
103
+
104
+ /**
105
+ * Result of a tree recovery operation.
106
+ */
107
+ export interface RecoverResult {
108
+ /** Number of nodes processed */
109
+ processed: number;
110
+ /** Whether the recovery was successful */
111
+ success: boolean;
112
+ /** Any errors encountered */
113
+ errors?: string[];
114
+ }
115
+
116
+ /**
117
+ * Scope values for multi-tree tables.
118
+ */
119
+ export type TreeScope = Record<string, unknown>;
120
+
121
+ /**
122
+ * Internal representation of nested set boundaries for calculations.
123
+ */
124
+ export interface NestedSetBounds {
125
+ lft: number;
126
+ rght: number;
127
+ }
128
+
129
+ /**
130
+ * Data required to insert a new node into the tree.
131
+ */
132
+ export interface TreeInsertData {
133
+ /** Parent node ID (null for root) */
134
+ parentId: unknown;
135
+ /** Calculated left boundary */
136
+ lft: number;
137
+ /** Calculated right boundary */
138
+ rght: number;
139
+ /** Calculated depth (if depthKey is configured) */
140
+ depth?: number;
141
+ }
142
+
143
+ /**
144
+ * Data required to move a node within the tree.
145
+ */
146
+ export interface TreeMoveData {
147
+ /** New parent node ID */
148
+ newParentId: unknown;
149
+ /** New left boundary */
150
+ newLft: number;
151
+ /** New right boundary */
152
+ newRght: number;
153
+ /** New depth (if depthKey is configured) */
154
+ newDepth?: number;
155
+ /** Delta to apply to lft/rght of affected nodes */
156
+ delta: number;
157
+ }
158
+
159
+ /**
160
+ * Represents a node with its children in a threaded tree structure.
161
+ * @typeParam T - The entity type
162
+ */
163
+ export interface ThreadedNode<T> {
164
+ /** The node data */
165
+ node: T;
166
+ /** Child nodes */
167
+ children: ThreadedNode<T>[];
168
+ }
169
+
170
+ /**
171
+ * A tree list entry for dropdown/select rendering.
172
+ */
173
+ export interface TreeListEntry<K = unknown, V = string> {
174
+ /** The key (usually primary key) */
175
+ key: K;
176
+ /** The display value with depth prefix */
177
+ value: V;
178
+ /** The depth level */
179
+ depth: number;
180
+ }
181
+
182
+ /**
183
+ * Validates that a table has the required tree columns.
184
+ * @param table - The table definition to validate
185
+ * @param config - The tree configuration
186
+ * @returns Validation result with any missing columns
187
+ */
188
+ export interface TreeValidationResult {
189
+ valid: boolean;
190
+ missingColumns: string[];
191
+ warnings: string[];
192
+ }
193
+
194
+ /**
195
+ * Extracts tree column definitions from a table.
196
+ */
197
+ export interface TreeColumns {
198
+ parentColumn: ColumnDef;
199
+ leftColumn: ColumnDef;
200
+ rightColumn: ColumnDef;
201
+ depthColumn?: ColumnDef;
202
+ scopeColumns: ColumnDef[];
203
+ }
204
+
205
+ /**
206
+ * Type guard to check if a value is a valid TreeConfig.
207
+ */
208
+ export function isTreeConfig(value: unknown): value is TreeConfig {
209
+ if (typeof value !== 'object' || value === null) return false;
210
+ const obj = value as Record<string, unknown>;
211
+ return (
212
+ typeof obj.parentKey === 'string' &&
213
+ typeof obj.leftKey === 'string' &&
214
+ typeof obj.rightKey === 'string'
215
+ );
216
+ }
217
+
218
+ /**
219
+ * Merges partial config with defaults.
220
+ */
221
+ export function resolveTreeConfig(partial: Partial<TreeConfig>): TreeConfig {
222
+ return {
223
+ parentKey: partial.parentKey ?? DEFAULT_TREE_CONFIG.parentKey,
224
+ leftKey: partial.leftKey ?? DEFAULT_TREE_CONFIG.leftKey,
225
+ rightKey: partial.rightKey ?? DEFAULT_TREE_CONFIG.rightKey,
226
+ depthKey: partial.depthKey,
227
+ scope: partial.scope,
228
+ cascadeCallbacks: partial.cascadeCallbacks ?? DEFAULT_TREE_CONFIG.cascadeCallbacks,
229
+ recoverOrder: partial.recoverOrder,
230
+ };
231
+ }
232
+
233
+ /**
234
+ * Validates that a table definition contains the required tree columns.
235
+ */
236
+ export function validateTreeTable(table: TableDef, config: TreeConfig): TreeValidationResult {
237
+ const missingColumns: string[] = [];
238
+ const warnings: string[] = [];
239
+
240
+ const requiredColumns = [config.parentKey, config.leftKey, config.rightKey];
241
+ for (const col of requiredColumns) {
242
+ if (!(col in table.columns)) {
243
+ missingColumns.push(col);
244
+ }
245
+ }
246
+
247
+ if (config.depthKey && !(config.depthKey in table.columns)) {
248
+ warnings.push(`Optional depth column '${config.depthKey}' not found in table '${table.name}'`);
249
+ }
250
+
251
+ if (config.scope) {
252
+ for (const scopeCol of config.scope) {
253
+ if (!(scopeCol in table.columns)) {
254
+ missingColumns.push(scopeCol);
255
+ }
256
+ }
257
+ }
258
+
259
+ const leftCol = table.columns[config.leftKey];
260
+ const rightCol = table.columns[config.rightKey];
261
+ if (leftCol && leftCol.type !== 'int' && leftCol.type !== 'integer') {
262
+ warnings.push(`Left column '${config.leftKey}' should be an integer type`);
263
+ }
264
+ if (rightCol && rightCol.type !== 'int' && rightCol.type !== 'integer') {
265
+ warnings.push(`Right column '${config.rightKey}' should be an integer type`);
266
+ }
267
+
268
+ return {
269
+ valid: missingColumns.length === 0,
270
+ missingColumns,
271
+ warnings,
272
+ };
273
+ }
274
+
275
+ /**
276
+ * Extracts tree-related columns from a table definition.
277
+ */
278
+ export function getTreeColumns(table: TableDef, config: TreeConfig): TreeColumns {
279
+ const parentColumn = table.columns[config.parentKey];
280
+ const leftColumn = table.columns[config.leftKey];
281
+ const rightColumn = table.columns[config.rightKey];
282
+ const depthColumn = config.depthKey ? table.columns[config.depthKey] : undefined;
283
+ const scopeColumns = (config.scope ?? []).map(s => table.columns[s]).filter(Boolean);
284
+
285
+ if (!parentColumn || !leftColumn || !rightColumn) {
286
+ throw new Error(
287
+ `Table '${table.name}' is missing required tree columns. ` +
288
+ `Required: ${config.parentKey}, ${config.leftKey}, ${config.rightKey}`
289
+ );
290
+ }
291
+
292
+ return {
293
+ parentColumn,
294
+ leftColumn,
295
+ rightColumn,
296
+ depthColumn,
297
+ scopeColumns,
298
+ };
299
+ }