metal-orm 1.0.8 → 1.0.9

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 (153) hide show
  1. package/README.md +12 -1
  2. package/dist/decorators/index.cjs +2564 -0
  3. package/dist/decorators/index.cjs.map +1 -0
  4. package/dist/decorators/index.d.cts +53 -0
  5. package/dist/decorators/index.d.ts +53 -0
  6. package/dist/decorators/index.js +2530 -0
  7. package/dist/decorators/index.js.map +1 -0
  8. package/dist/index.cjs +4227 -0
  9. package/dist/index.cjs.map +1 -0
  10. package/dist/index.d.cts +701 -0
  11. package/dist/index.d.ts +701 -0
  12. package/dist/index.js +4131 -0
  13. package/dist/index.js.map +1 -0
  14. package/dist/select-654m4qy8.d.cts +1522 -0
  15. package/dist/select-654m4qy8.d.ts +1522 -0
  16. package/package.json +27 -20
  17. package/src/codegen/typescript.ts +405 -393
  18. package/src/core/ast/aggregate-functions.ts +30 -0
  19. package/src/core/ast/builders.ts +43 -0
  20. package/src/core/ast/expression-builders.ts +310 -0
  21. package/src/core/ast/expression-nodes.ts +211 -0
  22. package/src/core/ast/expression-visitor.ts +99 -0
  23. package/src/core/ast/expression.ts +5 -0
  24. package/src/{utils → core/ast}/join-node.ts +20 -20
  25. package/src/{ast → core/ast}/join.ts +18 -18
  26. package/src/{ast → core/ast}/query.ts +113 -113
  27. package/src/core/ast/window-functions.ts +140 -0
  28. package/src/{dialect → core/dialect}/abstract.ts +94 -94
  29. package/src/{dialect → core/dialect}/mssql/index.ts +31 -31
  30. package/src/{dialect → core/dialect}/mysql/index.ts +31 -31
  31. package/src/{dialect → core/dialect}/postgres/index.ts +45 -45
  32. package/src/{dialect → core/dialect}/sqlite/index.ts +45 -45
  33. package/src/{constants → core/sql}/sql-operator-config.ts +39 -39
  34. package/src/decorators/bootstrap.ts +126 -0
  35. package/src/decorators/column.ts +78 -0
  36. package/src/decorators/entity.ts +36 -0
  37. package/src/decorators/index.ts +4 -0
  38. package/src/decorators/relations.ts +107 -0
  39. package/src/global.d.ts +1 -0
  40. package/src/index.ts +22 -22
  41. package/src/orm/db-executor.ts +11 -0
  42. package/src/orm/domain-event-bus.ts +52 -0
  43. package/src/{runtime → orm}/entity-meta.ts +52 -52
  44. package/src/orm/entity-metadata.ts +140 -0
  45. package/src/{runtime → orm}/entity.ts +252 -252
  46. package/src/{runtime → orm}/execute.ts +36 -36
  47. package/src/{runtime → orm}/hydration.ts +103 -103
  48. package/src/orm/identity-map.ts +37 -0
  49. package/src/{runtime → orm}/lazy-batch.ts +205 -205
  50. package/src/orm/orm-context.ts +154 -0
  51. package/src/orm/relation-change-processor.ts +140 -0
  52. package/src/{runtime → orm}/relations/belongs-to.ts +92 -92
  53. package/src/{runtime → orm}/relations/has-many.ts +111 -111
  54. package/src/{runtime → orm}/relations/many-to-many.ts +149 -149
  55. package/src/orm/runtime-types.ts +39 -0
  56. package/src/orm/transaction-runner.ts +17 -0
  57. package/src/orm/unit-of-work.ts +232 -0
  58. package/src/{builder/operations → query-builder}/column-selector.ts +78 -78
  59. package/src/{builder → query-builder}/delete-query-state.ts +38 -42
  60. package/src/{builder → query-builder}/delete.ts +46 -57
  61. package/src/{builder → query-builder}/hydration-manager.ts +87 -87
  62. package/src/{builder → query-builder}/hydration-planner.ts +182 -182
  63. package/src/{builder → query-builder}/insert-query-state.ts +51 -62
  64. package/src/{builder → query-builder}/insert.ts +48 -59
  65. package/src/{builder → query-builder}/query-ast-service.ts +208 -226
  66. package/src/{utils → query-builder}/raw-column-parser.ts +32 -32
  67. package/src/{builder → query-builder}/relation-conditions.ts +112 -112
  68. package/src/{builder/operations → query-builder}/relation-manager.ts +82 -82
  69. package/src/{builder → query-builder}/relation-projection-helper.ts +101 -101
  70. package/src/{builder → query-builder}/relation-service.ts +284 -284
  71. package/src/{builder → query-builder}/relation-types.ts +21 -21
  72. package/src/{builder → query-builder}/relation-utils.ts +12 -12
  73. package/src/{builder → query-builder}/select-query-builder-deps.ts +112 -94
  74. package/src/{builder → query-builder}/select-query-state.ts +179 -179
  75. package/src/{builder → query-builder}/select.ts +78 -69
  76. package/src/{builder → query-builder}/update-query-state.ts +55 -59
  77. package/src/{builder → query-builder}/update.ts +50 -61
  78. package/src/schema/column.ts +25 -25
  79. package/src/schema/relation.ts +116 -116
  80. package/src/schema/table.ts +34 -34
  81. package/src/schema/types.ts +76 -76
  82. package/.github/workflows/publish-metal-orm.yml +0 -38
  83. package/ROADMAP.md +0 -125
  84. package/docs/CHANGES.md +0 -104
  85. package/docs/advanced-features.md +0 -176
  86. package/docs/api-reference.md +0 -31
  87. package/docs/dml-operations.md +0 -156
  88. package/docs/getting-started.md +0 -171
  89. package/docs/hydration.md +0 -115
  90. package/docs/index.md +0 -36
  91. package/docs/multi-dialect-support.md +0 -59
  92. package/docs/query-builder.md +0 -135
  93. package/docs/runtime.md +0 -105
  94. package/docs/schema-definition.md +0 -112
  95. package/metadata.json +0 -5
  96. package/playground/api/playground-api.ts +0 -94
  97. package/playground/index.html +0 -15
  98. package/playground/src/App.css +0 -1
  99. package/playground/src/App.tsx +0 -114
  100. package/playground/src/components/CodeDisplay.tsx +0 -43
  101. package/playground/src/components/QueryExecutor.tsx +0 -189
  102. package/playground/src/components/ResultsTable.tsx +0 -67
  103. package/playground/src/components/ResultsTabs.tsx +0 -105
  104. package/playground/src/components/ScenarioList.tsx +0 -56
  105. package/playground/src/components/logo.svg +0 -45
  106. package/playground/src/data/scenarios.ts +0 -2
  107. package/playground/src/main.tsx +0 -9
  108. package/playground/src/services/PlaygroundApiService.ts +0 -60
  109. package/postcss.config.cjs +0 -5
  110. package/sql_sql-ansi-cheatsheet-2025.md +0 -264
  111. package/src/ast/expression.ts +0 -658
  112. package/src/builder/operations/cte-manager.ts +0 -34
  113. package/src/builder/operations/filter-manager.ts +0 -68
  114. package/src/builder/operations/join-manager.ts +0 -36
  115. package/src/builder/operations/pagination-manager.ts +0 -36
  116. package/src/playground/features/playground/api/types.ts +0 -16
  117. package/src/playground/features/playground/clients/MockClient.ts +0 -17
  118. package/src/playground/features/playground/clients/SqliteClient.ts +0 -57
  119. package/src/playground/features/playground/common/IDatabaseClient.ts +0 -10
  120. package/src/playground/features/playground/data/scenarios/aggregation.ts +0 -36
  121. package/src/playground/features/playground/data/scenarios/basics.ts +0 -25
  122. package/src/playground/features/playground/data/scenarios/edge_cases.ts +0 -57
  123. package/src/playground/features/playground/data/scenarios/filtering.ts +0 -94
  124. package/src/playground/features/playground/data/scenarios/hydration.ts +0 -27
  125. package/src/playground/features/playground/data/scenarios/index.ts +0 -29
  126. package/src/playground/features/playground/data/scenarios/ordering.ts +0 -25
  127. package/src/playground/features/playground/data/scenarios/pagination.ts +0 -16
  128. package/src/playground/features/playground/data/scenarios/relationships.ts +0 -75
  129. package/src/playground/features/playground/data/scenarios/types.ts +0 -70
  130. package/src/playground/features/playground/data/schema.ts +0 -91
  131. package/src/playground/features/playground/data/seed.ts +0 -104
  132. package/src/playground/features/playground/services/QueryExecutionService.ts +0 -121
  133. package/src/runtime/orm-context.ts +0 -539
  134. package/tests/belongs-to-many.test.ts +0 -57
  135. package/tests/between.test.ts +0 -43
  136. package/tests/case-expression.test.ts +0 -58
  137. package/tests/complex-exists.test.ts +0 -230
  138. package/tests/cte.test.ts +0 -118
  139. package/tests/dml.test.ts +0 -206
  140. package/tests/exists.test.ts +0 -127
  141. package/tests/like.test.ts +0 -33
  142. package/tests/orm-runtime.test.ts +0 -254
  143. package/tests/postgres.test.ts +0 -30
  144. package/tests/right-join.test.ts +0 -89
  145. package/tests/subquery-having.test.ts +0 -193
  146. package/tests/window-function.test.ts +0 -151
  147. package/tsconfig.json +0 -30
  148. package/tsup.config.ts +0 -10
  149. package/vite.config.ts +0 -22
  150. package/vitest.config.ts +0 -14
  151. /package/src/{constants → core/sql}/sql.ts +0 -0
  152. /package/src/{runtime → orm}/als.ts +0 -0
  153. /package/src/{utils → query-builder}/relation-alias.ts +0 -0
@@ -0,0 +1,2564 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3
+ var __getOwnPropNames = Object.getOwnPropertyNames;
4
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
5
+ var __export = (target, all) => {
6
+ for (var name in all)
7
+ __defProp(target, name, { get: all[name], enumerable: true });
8
+ };
9
+ var __copyProps = (to, from, except, desc) => {
10
+ if (from && typeof from === "object" || typeof from === "function") {
11
+ for (let key of __getOwnPropNames(from))
12
+ if (!__hasOwnProp.call(to, key) && key !== except)
13
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
14
+ }
15
+ return to;
16
+ };
17
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
18
+
19
+ // src/decorators/index.ts
20
+ var decorators_exports = {};
21
+ __export(decorators_exports, {
22
+ BelongsTo: () => BelongsTo,
23
+ BelongsToMany: () => BelongsToMany,
24
+ Column: () => Column,
25
+ Entity: () => Entity,
26
+ HasMany: () => HasMany,
27
+ PrimaryKey: () => PrimaryKey,
28
+ bootstrapEntities: () => bootstrapEntities,
29
+ getTableDefFromEntity: () => getTableDefFromEntity,
30
+ selectFromEntity: () => selectFromEntity
31
+ });
32
+ module.exports = __toCommonJS(decorators_exports);
33
+
34
+ // src/schema/table.ts
35
+ var defineTable = (name, columns, relations = {}, hooks) => {
36
+ const colsWithNames = Object.entries(columns).reduce((acc, [key, def]) => {
37
+ acc[key] = { ...def, name: key, table: name };
38
+ return acc;
39
+ }, {});
40
+ return { name, columns: colsWithNames, relations, hooks };
41
+ };
42
+
43
+ // src/orm/entity-metadata.ts
44
+ var metadataMap = /* @__PURE__ */ new Map();
45
+ var ensureEntityMetadata = (target) => {
46
+ let meta = metadataMap.get(target);
47
+ if (!meta) {
48
+ meta = {
49
+ target,
50
+ tableName: target.name || "unknown",
51
+ columns: {},
52
+ relations: {}
53
+ };
54
+ metadataMap.set(target, meta);
55
+ }
56
+ return meta;
57
+ };
58
+ var getEntityMetadata = (target) => {
59
+ return metadataMap.get(target);
60
+ };
61
+ var getAllEntityMetadata = () => {
62
+ return Array.from(metadataMap.values());
63
+ };
64
+ var addColumnMetadata = (target, propertyKey, column) => {
65
+ const meta = ensureEntityMetadata(target);
66
+ meta.columns[propertyKey] = { ...column };
67
+ };
68
+ var addRelationMetadata = (target, propertyKey, relation) => {
69
+ const meta = ensureEntityMetadata(target);
70
+ meta.relations[propertyKey] = relation;
71
+ };
72
+ var setEntityTableName = (target, tableName, hooks) => {
73
+ const meta = ensureEntityMetadata(target);
74
+ if (tableName && tableName.length > 0) {
75
+ meta.tableName = tableName;
76
+ }
77
+ if (hooks) {
78
+ meta.hooks = hooks;
79
+ }
80
+ };
81
+ var buildTableDef = (meta) => {
82
+ if (meta.table) {
83
+ return meta.table;
84
+ }
85
+ const columns = Object.entries(meta.columns).reduce((acc, [key, def]) => {
86
+ acc[key] = {
87
+ ...def,
88
+ name: key,
89
+ table: meta.tableName
90
+ };
91
+ return acc;
92
+ }, {});
93
+ const table = defineTable(meta.tableName, columns, {}, meta.hooks);
94
+ meta.table = table;
95
+ return table;
96
+ };
97
+
98
+ // src/decorators/entity.ts
99
+ var toSnakeCase = (value) => {
100
+ return value.replace(/([a-z0-9])([A-Z])/g, "$1_$2").replace(/[^a-z0-9_]+/gi, "_").replace(/__+/g, "_").replace(/^_|_$/g, "").toLowerCase();
101
+ };
102
+ var deriveTableNameFromConstructor = (ctor) => {
103
+ const fallback = "unknown";
104
+ const rawName = ctor.name || fallback;
105
+ const strippedName = rawName.replace(/Entity$/i, "");
106
+ const normalized = toSnakeCase(strippedName || rawName);
107
+ if (!normalized) {
108
+ return fallback;
109
+ }
110
+ return normalized.endsWith("s") ? normalized : `${normalized}s`;
111
+ };
112
+ function Entity(options = {}) {
113
+ const decorator = (value) => {
114
+ const tableName = options.tableName ?? deriveTableNameFromConstructor(value);
115
+ setEntityTableName(value, tableName, options.hooks);
116
+ return value;
117
+ };
118
+ return decorator;
119
+ }
120
+
121
+ // src/decorators/column.ts
122
+ var normalizeColumnInput = (input) => {
123
+ const column = {
124
+ type: input.type ?? input.type,
125
+ args: input.args ?? input.args,
126
+ notNull: input.notNull ?? input.notNull,
127
+ primary: input.primary ?? input.primary
128
+ };
129
+ if (!column.type) {
130
+ throw new Error("Column decorator requires a column type");
131
+ }
132
+ return column;
133
+ };
134
+ var normalizePropertyName = (name) => {
135
+ if (typeof name === "symbol") {
136
+ return name.description ?? name.toString();
137
+ }
138
+ return name;
139
+ };
140
+ var resolveConstructor = (target) => {
141
+ if (typeof target === "function") {
142
+ return target;
143
+ }
144
+ if (target && typeof target.constructor === "function") {
145
+ return target.constructor;
146
+ }
147
+ return void 0;
148
+ };
149
+ var registerColumn = (ctor, propertyName, column) => {
150
+ const meta = ensureEntityMetadata(ctor);
151
+ if (meta.columns[propertyName]) {
152
+ return;
153
+ }
154
+ addColumnMetadata(ctor, propertyName, column);
155
+ };
156
+ function Column(definition) {
157
+ const normalized = normalizeColumnInput(definition);
158
+ const decorator = (target, propertyKey) => {
159
+ const propertyName = normalizePropertyName(propertyKey);
160
+ const ctor = resolveConstructor(target);
161
+ if (!ctor) {
162
+ throw new Error("Unable to resolve constructor when registering column metadata");
163
+ }
164
+ registerColumn(ctor, propertyName, { ...normalized });
165
+ };
166
+ return decorator;
167
+ }
168
+ function PrimaryKey(definition) {
169
+ const normalized = normalizeColumnInput(definition);
170
+ normalized.primary = true;
171
+ return Column(normalized);
172
+ }
173
+
174
+ // src/schema/relation.ts
175
+ var RelationKinds = {
176
+ /** One-to-many relationship */
177
+ HasMany: "HAS_MANY",
178
+ /** Many-to-one relationship */
179
+ BelongsTo: "BELONGS_TO",
180
+ /** Many-to-many relationship with pivot metadata */
181
+ BelongsToMany: "BELONGS_TO_MANY"
182
+ };
183
+ var hasMany = (target, foreignKey, localKey, cascade) => ({
184
+ type: RelationKinds.HasMany,
185
+ target,
186
+ foreignKey,
187
+ localKey,
188
+ cascade
189
+ });
190
+ var belongsTo = (target, foreignKey, localKey, cascade) => ({
191
+ type: RelationKinds.BelongsTo,
192
+ target,
193
+ foreignKey,
194
+ localKey,
195
+ cascade
196
+ });
197
+ var belongsToMany = (target, pivotTable, options) => ({
198
+ type: RelationKinds.BelongsToMany,
199
+ target,
200
+ pivotTable,
201
+ pivotForeignKeyToRoot: options.pivotForeignKeyToRoot,
202
+ pivotForeignKeyToTarget: options.pivotForeignKeyToTarget,
203
+ localKey: options.localKey,
204
+ targetKey: options.targetKey,
205
+ pivotPrimaryKey: options.pivotPrimaryKey,
206
+ defaultPivotColumns: options.defaultPivotColumns,
207
+ cascade: options.cascade
208
+ });
209
+
210
+ // src/decorators/relations.ts
211
+ var normalizePropertyName2 = (name) => {
212
+ if (typeof name === "symbol") {
213
+ return name.description ?? name.toString();
214
+ }
215
+ return name;
216
+ };
217
+ var resolveConstructor2 = (instanceOrCtor) => {
218
+ if (typeof instanceOrCtor === "function") {
219
+ return instanceOrCtor;
220
+ }
221
+ if (instanceOrCtor && typeof instanceOrCtor.constructor === "function") {
222
+ return instanceOrCtor.constructor;
223
+ }
224
+ return void 0;
225
+ };
226
+ var registerRelation = (ctor, propertyName, metadata) => {
227
+ addRelationMetadata(ctor, propertyName, metadata);
228
+ };
229
+ var createFieldDecorator = (metadataFactory) => {
230
+ const decorator = (target, propertyKey) => {
231
+ const propertyName = normalizePropertyName2(propertyKey);
232
+ const ctor = resolveConstructor2(target);
233
+ if (!ctor) {
234
+ throw new Error("Unable to resolve constructor when registering relation metadata");
235
+ }
236
+ registerRelation(ctor, propertyName, metadataFactory(propertyName));
237
+ };
238
+ return decorator;
239
+ };
240
+ function HasMany(options) {
241
+ return createFieldDecorator((propertyName) => ({
242
+ kind: RelationKinds.HasMany,
243
+ propertyKey: propertyName,
244
+ target: options.target,
245
+ foreignKey: options.foreignKey,
246
+ localKey: options.localKey,
247
+ cascade: options.cascade
248
+ }));
249
+ }
250
+ function BelongsTo(options) {
251
+ return createFieldDecorator((propertyName) => ({
252
+ kind: RelationKinds.BelongsTo,
253
+ propertyKey: propertyName,
254
+ target: options.target,
255
+ foreignKey: options.foreignKey,
256
+ localKey: options.localKey,
257
+ cascade: options.cascade
258
+ }));
259
+ }
260
+ function BelongsToMany(options) {
261
+ return createFieldDecorator((propertyName) => ({
262
+ kind: RelationKinds.BelongsToMany,
263
+ propertyKey: propertyName,
264
+ target: options.target,
265
+ pivotTable: options.pivotTable,
266
+ pivotForeignKeyToRoot: options.pivotForeignKeyToRoot,
267
+ pivotForeignKeyToTarget: options.pivotForeignKeyToTarget,
268
+ localKey: options.localKey,
269
+ targetKey: options.targetKey,
270
+ pivotPrimaryKey: options.pivotPrimaryKey,
271
+ defaultPivotColumns: options.defaultPivotColumns,
272
+ cascade: options.cascade
273
+ }));
274
+ }
275
+
276
+ // src/core/ast/expression-nodes.ts
277
+ var operandTypes = /* @__PURE__ */ new Set([
278
+ "Column",
279
+ "Literal",
280
+ "Function",
281
+ "JsonPath",
282
+ "ScalarSubquery",
283
+ "CaseExpression",
284
+ "WindowFunction"
285
+ ]);
286
+ var isOperandNode = (node) => node && operandTypes.has(node.type);
287
+ var isFunctionNode = (node) => node?.type === "Function";
288
+ var isCaseExpressionNode = (node) => node?.type === "CaseExpression";
289
+ var isWindowFunctionNode = (node) => node?.type === "WindowFunction";
290
+ var isExpressionSelectionNode = (node) => isFunctionNode(node) || isCaseExpressionNode(node) || isWindowFunctionNode(node);
291
+
292
+ // src/core/ast/expression-builders.ts
293
+ var toNode = (col) => {
294
+ if (isOperandNode(col)) return col;
295
+ const def = col;
296
+ return { type: "Column", table: def.table || "unknown", name: def.name };
297
+ };
298
+ var toLiteralNode = (value) => ({
299
+ type: "Literal",
300
+ value
301
+ });
302
+ var toOperand = (val) => {
303
+ if (val === null) return { type: "Literal", value: null };
304
+ if (typeof val === "string" || typeof val === "number" || typeof val === "boolean") {
305
+ return { type: "Literal", value: val };
306
+ }
307
+ return toNode(val);
308
+ };
309
+ var columnOperand = (col) => toNode(col);
310
+ var createBinaryExpression = (operator, left, right, escape) => {
311
+ const node = {
312
+ type: "BinaryExpression",
313
+ left: toNode(left),
314
+ operator,
315
+ right: toOperand(right)
316
+ };
317
+ if (escape !== void 0) {
318
+ node.escape = toLiteralNode(escape);
319
+ }
320
+ return node;
321
+ };
322
+ var eq = (left, right) => createBinaryExpression("=", left, right);
323
+ var and = (...operands) => ({
324
+ type: "LogicalExpression",
325
+ operator: "AND",
326
+ operands
327
+ });
328
+ var createInExpression = (operator, left, values) => ({
329
+ type: "InExpression",
330
+ left: toNode(left),
331
+ operator,
332
+ right: values.map((v) => toOperand(v))
333
+ });
334
+ var inList = (left, values) => createInExpression("IN", left, values);
335
+ var exists = (subquery) => ({
336
+ type: "ExistsExpression",
337
+ operator: "EXISTS",
338
+ subquery
339
+ });
340
+ var notExists = (subquery) => ({
341
+ type: "ExistsExpression",
342
+ operator: "NOT EXISTS",
343
+ subquery
344
+ });
345
+
346
+ // src/core/ast/aggregate-functions.ts
347
+ var buildAggregate = (name) => (col) => ({
348
+ type: "Function",
349
+ name,
350
+ args: [columnOperand(col)]
351
+ });
352
+ var count = buildAggregate("COUNT");
353
+ var sum = buildAggregate("SUM");
354
+ var avg = buildAggregate("AVG");
355
+
356
+ // src/query-builder/select-query-state.ts
357
+ var SelectQueryState = class _SelectQueryState {
358
+ /**
359
+ * Creates a new SelectQueryState instance
360
+ * @param table - Table definition
361
+ * @param ast - Optional existing AST
362
+ */
363
+ constructor(table, ast) {
364
+ this.table = table;
365
+ this.ast = ast ?? {
366
+ type: "SelectQuery",
367
+ from: { type: "Table", name: table.name },
368
+ columns: [],
369
+ joins: []
370
+ };
371
+ }
372
+ /**
373
+ * Creates a new SelectQueryState with updated AST
374
+ * @param nextAst - Updated AST
375
+ * @returns New SelectQueryState instance
376
+ */
377
+ clone(nextAst) {
378
+ return new _SelectQueryState(this.table, nextAst);
379
+ }
380
+ /**
381
+ * Adds columns to the query
382
+ * @param newCols - Columns to add
383
+ * @returns New SelectQueryState with added columns
384
+ */
385
+ withColumns(newCols) {
386
+ return this.clone({
387
+ ...this.ast,
388
+ columns: [...this.ast.columns ?? [], ...newCols]
389
+ });
390
+ }
391
+ /**
392
+ * Adds a join to the query
393
+ * @param join - Join node to add
394
+ * @returns New SelectQueryState with added join
395
+ */
396
+ withJoin(join) {
397
+ return this.clone({
398
+ ...this.ast,
399
+ joins: [...this.ast.joins ?? [], join]
400
+ });
401
+ }
402
+ /**
403
+ * Adds a WHERE clause to the query
404
+ * @param predicate - WHERE predicate expression
405
+ * @returns New SelectQueryState with WHERE clause
406
+ */
407
+ withWhere(predicate) {
408
+ return this.clone({
409
+ ...this.ast,
410
+ where: predicate
411
+ });
412
+ }
413
+ /**
414
+ * Adds a HAVING clause to the query
415
+ * @param predicate - HAVING predicate expression
416
+ * @returns New SelectQueryState with HAVING clause
417
+ */
418
+ withHaving(predicate) {
419
+ return this.clone({
420
+ ...this.ast,
421
+ having: predicate
422
+ });
423
+ }
424
+ /**
425
+ * Adds GROUP BY columns to the query
426
+ * @param columns - Columns to group by
427
+ * @returns New SelectQueryState with GROUP BY clause
428
+ */
429
+ withGroupBy(columns) {
430
+ return this.clone({
431
+ ...this.ast,
432
+ groupBy: [...this.ast.groupBy ?? [], ...columns]
433
+ });
434
+ }
435
+ /**
436
+ * Adds ORDER BY clauses to the query
437
+ * @param orderBy - ORDER BY nodes
438
+ * @returns New SelectQueryState with ORDER BY clause
439
+ */
440
+ withOrderBy(orderBy) {
441
+ return this.clone({
442
+ ...this.ast,
443
+ orderBy: [...this.ast.orderBy ?? [], ...orderBy]
444
+ });
445
+ }
446
+ /**
447
+ * Adds DISTINCT columns to the query
448
+ * @param columns - Columns to make distinct
449
+ * @returns New SelectQueryState with DISTINCT clause
450
+ */
451
+ withDistinct(columns) {
452
+ return this.clone({
453
+ ...this.ast,
454
+ distinct: [...this.ast.distinct ?? [], ...columns]
455
+ });
456
+ }
457
+ /**
458
+ * Adds a LIMIT clause to the query
459
+ * @param limit - Maximum number of rows to return
460
+ * @returns New SelectQueryState with LIMIT clause
461
+ */
462
+ withLimit(limit) {
463
+ return this.clone({
464
+ ...this.ast,
465
+ limit
466
+ });
467
+ }
468
+ /**
469
+ * Adds an OFFSET clause to the query
470
+ * @param offset - Number of rows to skip
471
+ * @returns New SelectQueryState with OFFSET clause
472
+ */
473
+ withOffset(offset) {
474
+ return this.clone({
475
+ ...this.ast,
476
+ offset
477
+ });
478
+ }
479
+ /**
480
+ * Adds a Common Table Expression (CTE) to the query
481
+ * @param cte - CTE node to add
482
+ * @returns New SelectQueryState with CTE
483
+ */
484
+ withCte(cte) {
485
+ return this.clone({
486
+ ...this.ast,
487
+ ctes: [...this.ast.ctes ?? [], cte]
488
+ });
489
+ }
490
+ };
491
+
492
+ // src/query-builder/hydration-manager.ts
493
+ var HydrationManager = class _HydrationManager {
494
+ /**
495
+ * Creates a new HydrationManager instance
496
+ * @param table - Table definition
497
+ * @param planner - Hydration planner
498
+ */
499
+ constructor(table, planner) {
500
+ this.table = table;
501
+ this.planner = planner;
502
+ }
503
+ /**
504
+ * Creates a new HydrationManager with updated planner
505
+ * @param nextPlanner - Updated hydration planner
506
+ * @returns New HydrationManager instance
507
+ */
508
+ clone(nextPlanner) {
509
+ return new _HydrationManager(this.table, nextPlanner);
510
+ }
511
+ /**
512
+ * Handles column selection for hydration planning
513
+ * @param state - Current query state
514
+ * @param newColumns - Newly selected columns
515
+ * @returns Updated HydrationManager with captured columns
516
+ */
517
+ onColumnsSelected(state, newColumns) {
518
+ const updated = this.planner.captureRootColumns(newColumns);
519
+ return this.clone(updated);
520
+ }
521
+ /**
522
+ * Handles relation inclusion for hydration planning
523
+ * @param state - Current query state
524
+ * @param relation - Relation definition
525
+ * @param relationName - Name of the relation
526
+ * @param aliasPrefix - Alias prefix for the relation
527
+ * @param targetColumns - Target columns to include
528
+ * @returns Updated HydrationManager with included relation
529
+ */
530
+ onRelationIncluded(state, relation, relationName, aliasPrefix, targetColumns, pivot) {
531
+ const withRoots = this.planner.captureRootColumns(state.ast.columns);
532
+ const next = withRoots.includeRelation(relation, relationName, aliasPrefix, targetColumns, pivot);
533
+ return this.clone(next);
534
+ }
535
+ /**
536
+ * Applies hydration plan to the AST
537
+ * @param ast - Query AST to modify
538
+ * @returns AST with hydration metadata
539
+ */
540
+ applyToAst(ast) {
541
+ const plan = this.planner.getPlan();
542
+ if (!plan) return ast;
543
+ return {
544
+ ...ast,
545
+ meta: {
546
+ ...ast.meta || {},
547
+ hydration: plan
548
+ }
549
+ };
550
+ }
551
+ /**
552
+ * Gets the current hydration plan
553
+ * @returns Hydration plan or undefined if none exists
554
+ */
555
+ getPlan() {
556
+ return this.planner.getPlan();
557
+ }
558
+ };
559
+
560
+ // src/query-builder/relation-alias.ts
561
+ var RELATION_SEPARATOR = "__";
562
+ var makeRelationAlias = (relationName, columnName) => `${relationName}${RELATION_SEPARATOR}${columnName}`;
563
+ var isRelationAlias = (alias) => !!alias && alias.includes(RELATION_SEPARATOR);
564
+
565
+ // src/query-builder/relation-utils.ts
566
+ var buildDefaultPivotColumns = (rel, pivotPk) => {
567
+ const excluded = /* @__PURE__ */ new Set([pivotPk, rel.pivotForeignKeyToRoot, rel.pivotForeignKeyToTarget]);
568
+ return Object.keys(rel.pivotTable.columns).filter((col) => !excluded.has(col));
569
+ };
570
+
571
+ // src/query-builder/hydration-planner.ts
572
+ var findPrimaryKey = (table) => {
573
+ const pk = Object.values(table.columns).find((c) => c.primary);
574
+ return pk?.name || "id";
575
+ };
576
+ var HydrationPlanner = class _HydrationPlanner {
577
+ /**
578
+ * Creates a new HydrationPlanner instance
579
+ * @param table - Table definition
580
+ * @param plan - Optional existing hydration plan
581
+ */
582
+ constructor(table, plan) {
583
+ this.table = table;
584
+ this.plan = plan;
585
+ }
586
+ /**
587
+ * Captures root table columns for hydration planning
588
+ * @param columns - Columns to capture
589
+ * @returns Updated HydrationPlanner with captured columns
590
+ */
591
+ captureRootColumns(columns) {
592
+ const currentPlan = this.getPlanOrDefault();
593
+ const rootCols = new Set(currentPlan.rootColumns);
594
+ let changed = false;
595
+ columns.forEach((node) => {
596
+ if (node.type !== "Column") return;
597
+ if (node.table !== this.table.name) return;
598
+ const alias = node.alias || node.name;
599
+ if (isRelationAlias(alias)) return;
600
+ if (!rootCols.has(alias)) {
601
+ rootCols.add(alias);
602
+ changed = true;
603
+ }
604
+ });
605
+ if (!changed) return this;
606
+ return new _HydrationPlanner(this.table, {
607
+ ...currentPlan,
608
+ rootColumns: Array.from(rootCols)
609
+ });
610
+ }
611
+ /**
612
+ * Includes a relation in the hydration plan
613
+ * @param rel - Relation definition
614
+ * @param relationName - Name of the relation
615
+ * @param aliasPrefix - Alias prefix for relation columns
616
+ * @param columns - Columns to include from the relation
617
+ * @returns Updated HydrationPlanner with included relation
618
+ */
619
+ includeRelation(rel, relationName, aliasPrefix, columns, pivot) {
620
+ const currentPlan = this.getPlanOrDefault();
621
+ const relations = currentPlan.relations.filter((r) => r.name !== relationName);
622
+ relations.push(this.buildRelationPlan(rel, relationName, aliasPrefix, columns, pivot));
623
+ return new _HydrationPlanner(this.table, {
624
+ ...currentPlan,
625
+ relations
626
+ });
627
+ }
628
+ /**
629
+ * Gets the current hydration plan
630
+ * @returns Current hydration plan or undefined
631
+ */
632
+ getPlan() {
633
+ return this.plan;
634
+ }
635
+ /**
636
+ * Gets the current hydration plan or creates a default one
637
+ * @returns Current hydration plan or default plan
638
+ */
639
+ getPlanOrDefault() {
640
+ return this.plan ?? buildDefaultHydrationPlan(this.table);
641
+ }
642
+ /**
643
+ * Builds a relation plan for hydration
644
+ * @param rel - Relation definition
645
+ * @param relationName - Name of the relation
646
+ * @param aliasPrefix - Alias prefix for relation columns
647
+ * @param columns - Columns to include from the relation
648
+ * @returns Hydration relation plan
649
+ */
650
+ buildRelationPlan(rel, relationName, aliasPrefix, columns, pivot) {
651
+ switch (rel.type) {
652
+ case RelationKinds.HasMany: {
653
+ const localKey = rel.localKey || findPrimaryKey(this.table);
654
+ return {
655
+ name: relationName,
656
+ aliasPrefix,
657
+ type: rel.type,
658
+ targetTable: rel.target.name,
659
+ targetPrimaryKey: findPrimaryKey(rel.target),
660
+ foreignKey: rel.foreignKey,
661
+ localKey,
662
+ columns
663
+ };
664
+ }
665
+ case RelationKinds.BelongsTo: {
666
+ const localKey = rel.localKey || findPrimaryKey(rel.target);
667
+ return {
668
+ name: relationName,
669
+ aliasPrefix,
670
+ type: rel.type,
671
+ targetTable: rel.target.name,
672
+ targetPrimaryKey: findPrimaryKey(rel.target),
673
+ foreignKey: rel.foreignKey,
674
+ localKey,
675
+ columns
676
+ };
677
+ }
678
+ case RelationKinds.BelongsToMany: {
679
+ const many = rel;
680
+ const localKey = many.localKey || findPrimaryKey(this.table);
681
+ const targetPk = many.targetKey || findPrimaryKey(many.target);
682
+ const pivotPk = many.pivotPrimaryKey || findPrimaryKey(many.pivotTable);
683
+ const pivotAliasPrefix = pivot?.aliasPrefix ?? `${aliasPrefix}_pivot`;
684
+ const pivotColumns = pivot?.columns ?? many.defaultPivotColumns ?? buildDefaultPivotColumns(many, pivotPk);
685
+ return {
686
+ name: relationName,
687
+ aliasPrefix,
688
+ type: rel.type,
689
+ targetTable: many.target.name,
690
+ targetPrimaryKey: targetPk,
691
+ foreignKey: many.pivotForeignKeyToRoot,
692
+ localKey,
693
+ columns,
694
+ pivot: {
695
+ table: many.pivotTable.name,
696
+ primaryKey: pivotPk,
697
+ aliasPrefix: pivotAliasPrefix,
698
+ columns: pivotColumns
699
+ }
700
+ };
701
+ }
702
+ }
703
+ }
704
+ };
705
+ var buildDefaultHydrationPlan = (table) => ({
706
+ rootTable: table.name,
707
+ rootPrimaryKey: findPrimaryKey(table),
708
+ rootColumns: [],
709
+ relations: []
710
+ });
711
+
712
+ // src/core/ast/builders.ts
713
+ var buildColumnNode = (table, column) => {
714
+ if (column.type === "Column") {
715
+ return column;
716
+ }
717
+ const def = column;
718
+ return {
719
+ type: "Column",
720
+ table: def.table || table.name,
721
+ name: def.name
722
+ };
723
+ };
724
+
725
+ // src/query-builder/raw-column-parser.ts
726
+ var parseRawColumn = (col, tableName, ctes) => {
727
+ if (col.includes("(")) {
728
+ const [fn, rest] = col.split("(");
729
+ const colName = rest.replace(")", "");
730
+ const [table, name] = colName.includes(".") ? colName.split(".") : [tableName, colName];
731
+ return { type: "Column", table, name, alias: col };
732
+ }
733
+ if (col.includes(".")) {
734
+ const [potentialCteName, columnName] = col.split(".");
735
+ const hasCte = ctes?.some((cte) => cte.name === potentialCteName);
736
+ if (hasCte) {
737
+ return { type: "Column", table: tableName, name: col };
738
+ }
739
+ return { type: "Column", table: potentialCteName, name: columnName };
740
+ }
741
+ return { type: "Column", table: tableName, name: col };
742
+ };
743
+
744
+ // src/query-builder/query-ast-service.ts
745
+ var QueryAstService = class {
746
+ /**
747
+ * Creates a new QueryAstService instance
748
+ * @param table - Table definition
749
+ * @param state - Current query state
750
+ */
751
+ constructor(table, state) {
752
+ this.table = table;
753
+ this.state = state;
754
+ }
755
+ /**
756
+ * Selects columns for the query
757
+ * @param columns - Columns to select (key: alias, value: column definition or expression)
758
+ * @returns Column selection result with updated state and added columns
759
+ */
760
+ select(columns) {
761
+ const existingAliases = new Set(
762
+ this.state.ast.columns.map((c) => c.alias || c.name)
763
+ );
764
+ const newCols = Object.entries(columns).reduce((acc, [alias, val]) => {
765
+ if (existingAliases.has(alias)) return acc;
766
+ if (isExpressionSelectionNode(val)) {
767
+ acc.push({ ...val, alias });
768
+ return acc;
769
+ }
770
+ const colDef = val;
771
+ acc.push({
772
+ type: "Column",
773
+ table: colDef.table || this.table.name,
774
+ name: colDef.name,
775
+ alias
776
+ });
777
+ return acc;
778
+ }, []);
779
+ const nextState = this.state.withColumns(newCols);
780
+ return { state: nextState, addedColumns: newCols };
781
+ }
782
+ /**
783
+ * Selects raw column expressions (best-effort parser for simple references/functions)
784
+ * @param cols - Raw column expressions
785
+ * @returns Column selection result with updated state and added columns
786
+ */
787
+ selectRaw(cols) {
788
+ const newCols = cols.map((col) => parseRawColumn(col, this.table.name, this.state.ast.ctes));
789
+ const nextState = this.state.withColumns(newCols);
790
+ return { state: nextState, addedColumns: newCols };
791
+ }
792
+ /**
793
+ * Adds a Common Table Expression (CTE) to the query
794
+ * @param name - Name of the CTE
795
+ * @param query - Query for the CTE
796
+ * @param columns - Optional column names for the CTE
797
+ * @param recursive - Whether the CTE is recursive
798
+ * @returns Updated query state with CTE
799
+ */
800
+ withCte(name, query, columns, recursive = false) {
801
+ const cte = {
802
+ type: "CommonTableExpression",
803
+ name,
804
+ query,
805
+ columns,
806
+ recursive
807
+ };
808
+ return this.state.withCte(cte);
809
+ }
810
+ /**
811
+ * Selects a subquery as a column
812
+ * @param alias - Alias for the subquery
813
+ * @param query - Subquery to select
814
+ * @returns Updated query state with subquery selection
815
+ */
816
+ selectSubquery(alias, query) {
817
+ const node = { type: "ScalarSubquery", query, alias };
818
+ return this.state.withColumns([node]);
819
+ }
820
+ /**
821
+ * Adds a JOIN clause to the query
822
+ * @param join - Join node to add
823
+ * @returns Updated query state with JOIN
824
+ */
825
+ withJoin(join) {
826
+ return this.state.withJoin(join);
827
+ }
828
+ /**
829
+ * Adds a WHERE clause to the query
830
+ * @param expr - Expression for the WHERE clause
831
+ * @returns Updated query state with WHERE clause
832
+ */
833
+ withWhere(expr) {
834
+ const combined = this.combineExpressions(this.state.ast.where, expr);
835
+ return this.state.withWhere(combined);
836
+ }
837
+ /**
838
+ * Adds a GROUP BY clause to the query
839
+ * @param col - Column to group by
840
+ * @returns Updated query state with GROUP BY clause
841
+ */
842
+ withGroupBy(col) {
843
+ const node = buildColumnNode(this.table, col);
844
+ return this.state.withGroupBy([node]);
845
+ }
846
+ /**
847
+ * Adds a HAVING clause to the query
848
+ * @param expr - Expression for the HAVING clause
849
+ * @returns Updated query state with HAVING clause
850
+ */
851
+ withHaving(expr) {
852
+ const combined = this.combineExpressions(this.state.ast.having, expr);
853
+ return this.state.withHaving(combined);
854
+ }
855
+ /**
856
+ * Adds an ORDER BY clause to the query
857
+ * @param col - Column to order by
858
+ * @param direction - Order direction (ASC/DESC)
859
+ * @returns Updated query state with ORDER BY clause
860
+ */
861
+ withOrderBy(col, direction) {
862
+ const node = buildColumnNode(this.table, col);
863
+ return this.state.withOrderBy([{ type: "OrderBy", column: node, direction }]);
864
+ }
865
+ /**
866
+ * Adds a DISTINCT clause to the query
867
+ * @param cols - Columns to make distinct
868
+ * @returns Updated query state with DISTINCT clause
869
+ */
870
+ withDistinct(cols) {
871
+ return this.state.withDistinct(cols);
872
+ }
873
+ /**
874
+ * Adds a LIMIT clause to the query
875
+ * @param limit - Maximum number of rows to return
876
+ * @returns Updated query state with LIMIT clause
877
+ */
878
+ withLimit(limit) {
879
+ return this.state.withLimit(limit);
880
+ }
881
+ /**
882
+ * Adds an OFFSET clause to the query
883
+ * @param offset - Number of rows to skip
884
+ * @returns Updated query state with OFFSET clause
885
+ */
886
+ withOffset(offset) {
887
+ return this.state.withOffset(offset);
888
+ }
889
+ /**
890
+ * Combines expressions with AND operator
891
+ * @param existing - Existing expression
892
+ * @param next - New expression to combine
893
+ * @returns Combined expression
894
+ */
895
+ combineExpressions(existing, next) {
896
+ return existing ? and(existing, next) : next;
897
+ }
898
+ };
899
+
900
+ // src/query-builder/relation-projection-helper.ts
901
+ var RelationProjectionHelper = class {
902
+ /**
903
+ * Creates a new RelationProjectionHelper instance
904
+ * @param table - Table definition
905
+ * @param selectColumns - Callback for selecting columns
906
+ */
907
+ constructor(table, selectColumns) {
908
+ this.table = table;
909
+ this.selectColumns = selectColumns;
910
+ }
911
+ /**
912
+ * Ensures base projection is included in the query
913
+ * @param state - Current query state
914
+ * @param hydration - Hydration manager
915
+ * @returns Relation result with updated state and hydration
916
+ */
917
+ ensureBaseProjection(state, hydration) {
918
+ const primaryKey = findPrimaryKey(this.table);
919
+ if (!this.hasBaseProjection(state)) {
920
+ return this.selectColumns(state, hydration, this.getBaseColumns());
921
+ }
922
+ if (primaryKey && !this.hasPrimarySelected(state, primaryKey) && this.table.columns[primaryKey]) {
923
+ return this.selectColumns(state, hydration, {
924
+ [primaryKey]: this.table.columns[primaryKey]
925
+ });
926
+ }
927
+ return { state, hydration };
928
+ }
929
+ /**
930
+ * Checks if base projection exists in the query
931
+ * @param state - Current query state
932
+ * @returns True if base projection exists
933
+ */
934
+ hasBaseProjection(state) {
935
+ return state.ast.columns.some((col) => !isRelationAlias(col.alias));
936
+ }
937
+ /**
938
+ * Checks if primary key is selected in the query
939
+ * @param state - Current query state
940
+ * @param primaryKey - Primary key name
941
+ * @returns True if primary key is selected
942
+ */
943
+ hasPrimarySelected(state, primaryKey) {
944
+ return state.ast.columns.some((col) => {
945
+ const alias = col.alias;
946
+ const name = alias || col.name;
947
+ return !isRelationAlias(alias) && name === primaryKey;
948
+ });
949
+ }
950
+ /**
951
+ * Gets all base columns for the table
952
+ * @returns Record of all table columns
953
+ */
954
+ getBaseColumns() {
955
+ return Object.keys(this.table.columns).reduce((acc, key) => {
956
+ acc[key] = this.table.columns[key];
957
+ return acc;
958
+ }, {});
959
+ }
960
+ };
961
+
962
+ // src/core/ast/join-node.ts
963
+ var createJoinNode = (kind, tableName, condition, relationName) => ({
964
+ type: "Join",
965
+ kind,
966
+ table: { type: "Table", name: tableName },
967
+ condition,
968
+ relationName
969
+ });
970
+
971
+ // src/query-builder/relation-conditions.ts
972
+ var assertNever = (value) => {
973
+ throw new Error(`Unhandled relation type: ${JSON.stringify(value)}`);
974
+ };
975
+ var baseRelationCondition = (root, relation) => {
976
+ const defaultLocalKey = relation.type === RelationKinds.HasMany ? findPrimaryKey(root) : findPrimaryKey(relation.target);
977
+ const localKey = relation.localKey || defaultLocalKey;
978
+ switch (relation.type) {
979
+ case RelationKinds.HasMany:
980
+ return eq(
981
+ { type: "Column", table: relation.target.name, name: relation.foreignKey },
982
+ { type: "Column", table: root.name, name: localKey }
983
+ );
984
+ case RelationKinds.BelongsTo:
985
+ return eq(
986
+ { type: "Column", table: relation.target.name, name: localKey },
987
+ { type: "Column", table: root.name, name: relation.foreignKey }
988
+ );
989
+ case RelationKinds.BelongsToMany:
990
+ throw new Error("BelongsToMany relations do not support the standard join condition builder");
991
+ default:
992
+ return assertNever(relation);
993
+ }
994
+ };
995
+ var buildBelongsToManyJoins = (root, relationName, relation, joinKind, extra) => {
996
+ const rootKey = relation.localKey || findPrimaryKey(root);
997
+ const targetKey = relation.targetKey || findPrimaryKey(relation.target);
998
+ const pivotCondition = eq(
999
+ { type: "Column", table: relation.pivotTable.name, name: relation.pivotForeignKeyToRoot },
1000
+ { type: "Column", table: root.name, name: rootKey }
1001
+ );
1002
+ const pivotJoin = createJoinNode(joinKind, relation.pivotTable.name, pivotCondition);
1003
+ let targetCondition = eq(
1004
+ { type: "Column", table: relation.target.name, name: targetKey },
1005
+ { type: "Column", table: relation.pivotTable.name, name: relation.pivotForeignKeyToTarget }
1006
+ );
1007
+ if (extra) {
1008
+ targetCondition = and(targetCondition, extra);
1009
+ }
1010
+ const targetJoin = createJoinNode(
1011
+ joinKind,
1012
+ relation.target.name,
1013
+ targetCondition,
1014
+ relationName
1015
+ );
1016
+ return [pivotJoin, targetJoin];
1017
+ };
1018
+ var buildRelationJoinCondition = (root, relation, extra) => {
1019
+ const base = baseRelationCondition(root, relation);
1020
+ return extra ? and(base, extra) : base;
1021
+ };
1022
+ var buildRelationCorrelation = (root, relation) => {
1023
+ return baseRelationCondition(root, relation);
1024
+ };
1025
+
1026
+ // src/core/sql/sql.ts
1027
+ var JOIN_KINDS = {
1028
+ /** INNER JOIN type */
1029
+ INNER: "INNER",
1030
+ /** LEFT JOIN type */
1031
+ LEFT: "LEFT",
1032
+ /** RIGHT JOIN type */
1033
+ RIGHT: "RIGHT",
1034
+ /** CROSS JOIN type */
1035
+ CROSS: "CROSS"
1036
+ };
1037
+ var ORDER_DIRECTIONS = {
1038
+ /** Ascending order */
1039
+ ASC: "ASC",
1040
+ /** Descending order */
1041
+ DESC: "DESC"
1042
+ };
1043
+
1044
+ // src/query-builder/relation-service.ts
1045
+ var RelationService = class {
1046
+ /**
1047
+ * Creates a new RelationService instance
1048
+ * @param table - Table definition
1049
+ * @param state - Current query state
1050
+ * @param hydration - Hydration manager
1051
+ */
1052
+ constructor(table, state, hydration, createQueryAstService) {
1053
+ this.table = table;
1054
+ this.state = state;
1055
+ this.hydration = hydration;
1056
+ this.createQueryAstService = createQueryAstService;
1057
+ this.projectionHelper = new RelationProjectionHelper(
1058
+ table,
1059
+ (state2, hydration2, columns) => this.selectColumns(state2, hydration2, columns)
1060
+ );
1061
+ }
1062
+ /**
1063
+ * Joins a relation to the query
1064
+ * @param relationName - Name of the relation to join
1065
+ * @param joinKind - Type of join to use
1066
+ * @param extraCondition - Additional join condition
1067
+ * @returns Relation result with updated state and hydration
1068
+ */
1069
+ joinRelation(relationName, joinKind, extraCondition) {
1070
+ const nextState = this.withJoin(this.state, relationName, joinKind, extraCondition);
1071
+ return { state: nextState, hydration: this.hydration };
1072
+ }
1073
+ /**
1074
+ * Matches records based on a relation with an optional predicate
1075
+ * @param relationName - Name of the relation to match
1076
+ * @param predicate - Optional predicate expression
1077
+ * @returns Relation result with updated state and hydration
1078
+ */
1079
+ match(relationName, predicate) {
1080
+ const joined = this.joinRelation(relationName, JOIN_KINDS.INNER, predicate);
1081
+ const pk = findPrimaryKey(this.table);
1082
+ const distinctCols = [{ type: "Column", table: this.table.name, name: pk }];
1083
+ const existingDistinct = joined.state.ast.distinct ? joined.state.ast.distinct : [];
1084
+ const nextState = this.astService(joined.state).withDistinct([...existingDistinct, ...distinctCols]);
1085
+ return { state: nextState, hydration: joined.hydration };
1086
+ }
1087
+ /**
1088
+ * Includes a relation in the query result
1089
+ * @param relationName - Name of the relation to include
1090
+ * @param options - Options for relation inclusion
1091
+ * @returns Relation result with updated state and hydration
1092
+ */
1093
+ include(relationName, options) {
1094
+ let state = this.state;
1095
+ let hydration = this.hydration;
1096
+ const relation = this.getRelation(relationName);
1097
+ const aliasPrefix = options?.aliasPrefix ?? relationName;
1098
+ const alreadyJoined = state.ast.joins.some((j) => j.relationName === relationName);
1099
+ if (!alreadyJoined) {
1100
+ const joined = this.joinRelation(relationName, options?.joinKind ?? JOIN_KINDS.LEFT, options?.filter);
1101
+ state = joined.state;
1102
+ }
1103
+ const projectionResult = this.projectionHelper.ensureBaseProjection(state, hydration);
1104
+ state = projectionResult.state;
1105
+ hydration = projectionResult.hydration;
1106
+ const targetColumns = options?.columns?.length ? options.columns : Object.keys(relation.target.columns);
1107
+ const buildTypedSelection = (columns, prefix, keys, missingMsg) => {
1108
+ return keys.reduce((acc, key) => {
1109
+ const def = columns[key];
1110
+ if (!def) {
1111
+ throw new Error(missingMsg(key));
1112
+ }
1113
+ acc[makeRelationAlias(prefix, key)] = def;
1114
+ return acc;
1115
+ }, {});
1116
+ };
1117
+ const targetSelection = buildTypedSelection(
1118
+ relation.target.columns,
1119
+ aliasPrefix,
1120
+ targetColumns,
1121
+ (key) => `Column '${key}' not found on relation '${relationName}'`
1122
+ );
1123
+ if (relation.type !== RelationKinds.BelongsToMany) {
1124
+ const relationSelectionResult2 = this.selectColumns(state, hydration, targetSelection);
1125
+ state = relationSelectionResult2.state;
1126
+ hydration = relationSelectionResult2.hydration;
1127
+ hydration = hydration.onRelationIncluded(
1128
+ state,
1129
+ relation,
1130
+ relationName,
1131
+ aliasPrefix,
1132
+ targetColumns
1133
+ );
1134
+ return { state, hydration };
1135
+ }
1136
+ const many = relation;
1137
+ const pivotAliasPrefix = options?.pivot?.aliasPrefix ?? `${aliasPrefix}_pivot`;
1138
+ const pivotPk = many.pivotPrimaryKey || findPrimaryKey(many.pivotTable);
1139
+ const pivotColumns = options?.pivot?.columns ?? many.defaultPivotColumns ?? buildDefaultPivotColumns(many, pivotPk);
1140
+ const pivotSelection = buildTypedSelection(
1141
+ many.pivotTable.columns,
1142
+ pivotAliasPrefix,
1143
+ pivotColumns,
1144
+ (key) => `Column '${key}' not found on pivot table '${many.pivotTable.name}'`
1145
+ );
1146
+ const combinedSelection = {
1147
+ ...targetSelection,
1148
+ ...pivotSelection
1149
+ };
1150
+ const relationSelectionResult = this.selectColumns(state, hydration, combinedSelection);
1151
+ state = relationSelectionResult.state;
1152
+ hydration = relationSelectionResult.hydration;
1153
+ hydration = hydration.onRelationIncluded(
1154
+ state,
1155
+ relation,
1156
+ relationName,
1157
+ aliasPrefix,
1158
+ targetColumns,
1159
+ { aliasPrefix: pivotAliasPrefix, columns: pivotColumns }
1160
+ );
1161
+ return { state, hydration };
1162
+ }
1163
+ /**
1164
+ * Applies relation correlation to a query AST
1165
+ * @param relationName - Name of the relation
1166
+ * @param ast - Query AST to modify
1167
+ * @returns Modified query AST with relation correlation
1168
+ */
1169
+ applyRelationCorrelation(relationName, ast) {
1170
+ const relation = this.getRelation(relationName);
1171
+ const correlation = buildRelationCorrelation(this.table, relation);
1172
+ const whereInSubquery = ast.where ? and(correlation, ast.where) : correlation;
1173
+ return {
1174
+ ...ast,
1175
+ where: whereInSubquery
1176
+ };
1177
+ }
1178
+ /**
1179
+ * Creates a join node for a relation
1180
+ * @param state - Current query state
1181
+ * @param relationName - Name of the relation
1182
+ * @param joinKind - Type of join to use
1183
+ * @param extraCondition - Additional join condition
1184
+ * @returns Updated query state with join
1185
+ */
1186
+ withJoin(state, relationName, joinKind, extraCondition) {
1187
+ const relation = this.getRelation(relationName);
1188
+ if (relation.type === RelationKinds.BelongsToMany) {
1189
+ const joins = buildBelongsToManyJoins(
1190
+ this.table,
1191
+ relationName,
1192
+ relation,
1193
+ joinKind,
1194
+ extraCondition
1195
+ );
1196
+ return joins.reduce((current, join) => this.astService(current).withJoin(join), state);
1197
+ }
1198
+ const condition = buildRelationJoinCondition(this.table, relation, extraCondition);
1199
+ const joinNode = createJoinNode(joinKind, relation.target.name, condition, relationName);
1200
+ return this.astService(state).withJoin(joinNode);
1201
+ }
1202
+ /**
1203
+ * Selects columns for a relation
1204
+ * @param state - Current query state
1205
+ * @param hydration - Hydration manager
1206
+ * @param columns - Columns to select
1207
+ * @returns Relation result with updated state and hydration
1208
+ */
1209
+ selectColumns(state, hydration, columns) {
1210
+ const { state: nextState, addedColumns } = this.astService(state).select(columns);
1211
+ return {
1212
+ state: nextState,
1213
+ hydration: hydration.onColumnsSelected(nextState, addedColumns)
1214
+ };
1215
+ }
1216
+ /**
1217
+ * Gets a relation definition by name
1218
+ * @param relationName - Name of the relation
1219
+ * @returns Relation definition
1220
+ * @throws Error if relation is not found
1221
+ */
1222
+ getRelation(relationName) {
1223
+ const relation = this.table.relations[relationName];
1224
+ if (!relation) {
1225
+ throw new Error(`Relation '${relationName}' not found on table '${this.table.name}'`);
1226
+ }
1227
+ return relation;
1228
+ }
1229
+ /**
1230
+ * Creates a QueryAstService instance
1231
+ * @param state - Current query state
1232
+ * @returns QueryAstService instance
1233
+ */
1234
+ astService(state = this.state) {
1235
+ return this.createQueryAstService(this.table, state);
1236
+ }
1237
+ };
1238
+
1239
+ // src/query-builder/select-query-builder-deps.ts
1240
+ var defaultCreateQueryAstService = (table, state) => new QueryAstService(table, state);
1241
+ var defaultCreateHydrationPlanner = (table) => new HydrationPlanner(table);
1242
+ var defaultCreateHydration = (table, plannerFactory) => new HydrationManager(table, plannerFactory(table));
1243
+ var resolveSelectQueryBuilderDependencies = (overrides = {}) => {
1244
+ const createQueryAstService = overrides.createQueryAstService ?? defaultCreateQueryAstService;
1245
+ const createHydrationPlanner = overrides.createHydrationPlanner ?? defaultCreateHydrationPlanner;
1246
+ const createHydration = overrides.createHydration ?? ((table) => defaultCreateHydration(table, createHydrationPlanner));
1247
+ const createRelationService = overrides.createRelationService ?? ((table, state, hydration) => new RelationService(table, state, hydration, createQueryAstService));
1248
+ return {
1249
+ createState: overrides.createState ?? ((table) => new SelectQueryState(table)),
1250
+ createHydration,
1251
+ createHydrationPlanner,
1252
+ createQueryAstService,
1253
+ createRelationService
1254
+ };
1255
+ };
1256
+ var defaultSelectQueryBuilderDependencies = resolveSelectQueryBuilderDependencies();
1257
+
1258
+ // src/query-builder/column-selector.ts
1259
+ var ColumnSelector = class {
1260
+ /**
1261
+ * Creates a new ColumnSelector instance
1262
+ * @param env - Query builder environment
1263
+ */
1264
+ constructor(env) {
1265
+ this.env = env;
1266
+ }
1267
+ /**
1268
+ * Selects columns for the query
1269
+ * @param context - Current query context
1270
+ * @param columns - Columns to select
1271
+ * @returns Updated query context with selected columns
1272
+ */
1273
+ select(context, columns) {
1274
+ const astService = this.env.deps.createQueryAstService(this.env.table, context.state);
1275
+ const { state: nextState, addedColumns } = astService.select(columns);
1276
+ return {
1277
+ state: nextState,
1278
+ hydration: context.hydration.onColumnsSelected(nextState, addedColumns)
1279
+ };
1280
+ }
1281
+ /**
1282
+ * Selects raw column expressions
1283
+ * @param context - Current query context
1284
+ * @param columns - Raw column expressions
1285
+ * @returns Updated query context with raw column selections
1286
+ */
1287
+ selectRaw(context, columns) {
1288
+ const astService = this.env.deps.createQueryAstService(this.env.table, context.state);
1289
+ const nextState = astService.selectRaw(columns).state;
1290
+ return { state: nextState, hydration: context.hydration };
1291
+ }
1292
+ /**
1293
+ * Selects a subquery as a column
1294
+ * @param context - Current query context
1295
+ * @param alias - Alias for the subquery
1296
+ * @param query - Subquery to select
1297
+ * @returns Updated query context with subquery selection
1298
+ */
1299
+ selectSubquery(context, alias, query) {
1300
+ const astService = this.env.deps.createQueryAstService(this.env.table, context.state);
1301
+ const nextState = astService.selectSubquery(alias, query);
1302
+ return { state: nextState, hydration: context.hydration };
1303
+ }
1304
+ /**
1305
+ * Adds DISTINCT clause to the query
1306
+ * @param context - Current query context
1307
+ * @param columns - Columns to make distinct
1308
+ * @returns Updated query context with DISTINCT clause
1309
+ */
1310
+ distinct(context, columns) {
1311
+ const nodes = columns.map((col) => buildColumnNode(this.env.table, col));
1312
+ const astService = this.env.deps.createQueryAstService(this.env.table, context.state);
1313
+ const nextState = astService.withDistinct(nodes);
1314
+ return { state: nextState, hydration: context.hydration };
1315
+ }
1316
+ };
1317
+
1318
+ // src/query-builder/relation-manager.ts
1319
+ var RelationManager = class {
1320
+ /**
1321
+ * Creates a new RelationManager instance
1322
+ * @param env - Query builder environment
1323
+ */
1324
+ constructor(env) {
1325
+ this.env = env;
1326
+ }
1327
+ /**
1328
+ * Matches records based on a relation with an optional predicate
1329
+ * @param context - Current query context
1330
+ * @param relationName - Name of the relation to match
1331
+ * @param predicate - Optional predicate expression
1332
+ * @returns Updated query context with relation match
1333
+ */
1334
+ match(context, relationName, predicate) {
1335
+ const result = this.createService(context).match(relationName, predicate);
1336
+ return { state: result.state, hydration: result.hydration };
1337
+ }
1338
+ /**
1339
+ * Joins a relation to the query
1340
+ * @param context - Current query context
1341
+ * @param relationName - Name of the relation to join
1342
+ * @param joinKind - Type of join to use
1343
+ * @param extraCondition - Additional join condition
1344
+ * @returns Updated query context with relation join
1345
+ */
1346
+ joinRelation(context, relationName, joinKind, extraCondition) {
1347
+ const result = this.createService(context).joinRelation(relationName, joinKind, extraCondition);
1348
+ return { state: result.state, hydration: result.hydration };
1349
+ }
1350
+ /**
1351
+ * Includes a relation in the query result
1352
+ * @param context - Current query context
1353
+ * @param relationName - Name of the relation to include
1354
+ * @param options - Options for relation inclusion
1355
+ * @returns Updated query context with included relation
1356
+ */
1357
+ include(context, relationName, options) {
1358
+ const result = this.createService(context).include(relationName, options);
1359
+ return { state: result.state, hydration: result.hydration };
1360
+ }
1361
+ /**
1362
+ * Applies relation correlation to a query AST
1363
+ * @param context - Current query context
1364
+ * @param relationName - Name of the relation
1365
+ * @param ast - Query AST to modify
1366
+ * @returns Modified query AST with relation correlation
1367
+ */
1368
+ applyRelationCorrelation(context, relationName, ast) {
1369
+ return this.createService(context).applyRelationCorrelation(relationName, ast);
1370
+ }
1371
+ /**
1372
+ * Creates a relation service instance
1373
+ * @param context - Current query context
1374
+ * @returns Relation service instance
1375
+ */
1376
+ createService(context) {
1377
+ return this.env.deps.createRelationService(this.env.table, context.state, context.hydration);
1378
+ }
1379
+ };
1380
+
1381
+ // src/orm/hydration.ts
1382
+ var hydrateRows = (rows, plan) => {
1383
+ if (!plan || !rows.length) return rows;
1384
+ const rootMap = /* @__PURE__ */ new Map();
1385
+ const relationIndex = /* @__PURE__ */ new Map();
1386
+ const getOrCreateParent = (row) => {
1387
+ const rootId = row[plan.rootPrimaryKey];
1388
+ if (rootId === void 0) return void 0;
1389
+ if (!rootMap.has(rootId)) {
1390
+ rootMap.set(rootId, createBaseRow(row, plan));
1391
+ }
1392
+ return rootMap.get(rootId);
1393
+ };
1394
+ const getRelationSeenSet = (rootId, relationName) => {
1395
+ let byRelation = relationIndex.get(rootId);
1396
+ if (!byRelation) {
1397
+ byRelation = {};
1398
+ relationIndex.set(rootId, byRelation);
1399
+ }
1400
+ let seen = byRelation[relationName];
1401
+ if (!seen) {
1402
+ seen = /* @__PURE__ */ new Set();
1403
+ byRelation[relationName] = seen;
1404
+ }
1405
+ return seen;
1406
+ };
1407
+ for (const row of rows) {
1408
+ const rootId = row[plan.rootPrimaryKey];
1409
+ if (rootId === void 0) continue;
1410
+ const parent = getOrCreateParent(row);
1411
+ if (!parent) continue;
1412
+ for (const rel of plan.relations) {
1413
+ const childPkKey = makeRelationAlias(rel.aliasPrefix, rel.targetPrimaryKey);
1414
+ const childPk = row[childPkKey];
1415
+ if (childPk === null || childPk === void 0) continue;
1416
+ const seen = getRelationSeenSet(rootId, rel.name);
1417
+ if (seen.has(childPk)) continue;
1418
+ seen.add(childPk);
1419
+ const bucket = parent[rel.name];
1420
+ bucket.push(buildChild(row, rel));
1421
+ }
1422
+ }
1423
+ return Array.from(rootMap.values());
1424
+ };
1425
+ var createBaseRow = (row, plan) => {
1426
+ const base = {};
1427
+ const baseKeys = plan.rootColumns.length ? plan.rootColumns : Object.keys(row).filter((k) => !isRelationAlias(k));
1428
+ for (const key of baseKeys) {
1429
+ base[key] = row[key];
1430
+ }
1431
+ for (const rel of plan.relations) {
1432
+ base[rel.name] = [];
1433
+ }
1434
+ return base;
1435
+ };
1436
+ var buildChild = (row, rel) => {
1437
+ const child = {};
1438
+ for (const col of rel.columns) {
1439
+ const key = makeRelationAlias(rel.aliasPrefix, col);
1440
+ child[col] = row[key];
1441
+ }
1442
+ const pivot = buildPivot(row, rel);
1443
+ if (pivot) {
1444
+ child._pivot = pivot;
1445
+ }
1446
+ return child;
1447
+ };
1448
+ var buildPivot = (row, rel) => {
1449
+ if (!rel.pivot) return void 0;
1450
+ const pivot = {};
1451
+ for (const col of rel.pivot.columns) {
1452
+ const key = makeRelationAlias(rel.pivot.aliasPrefix, col);
1453
+ pivot[col] = row[key];
1454
+ }
1455
+ const hasValue = Object.values(pivot).some((v) => v !== null && v !== void 0);
1456
+ return hasValue ? pivot : void 0;
1457
+ };
1458
+
1459
+ // src/orm/entity-meta.ts
1460
+ var ENTITY_META = Symbol("EntityMeta");
1461
+ var toKey = (value) => value === null || value === void 0 ? "" : String(value);
1462
+ var getHydrationRows = (meta, relationName, key) => {
1463
+ const map = meta.relationHydration.get(relationName);
1464
+ if (!map) return void 0;
1465
+ const rows = map.get(toKey(key));
1466
+ if (!rows) return void 0;
1467
+ return Array.isArray(rows) ? rows : void 0;
1468
+ };
1469
+ var getHydrationRecord = (meta, relationName, key) => {
1470
+ const map = meta.relationHydration.get(relationName);
1471
+ if (!map) return void 0;
1472
+ const value = map.get(toKey(key));
1473
+ if (!value) return void 0;
1474
+ if (Array.isArray(value)) {
1475
+ return value[0];
1476
+ }
1477
+ return value;
1478
+ };
1479
+ var getEntityMeta = (entity) => {
1480
+ if (!entity || typeof entity !== "object") return void 0;
1481
+ return entity[ENTITY_META];
1482
+ };
1483
+ var hasEntityMeta = (entity) => {
1484
+ return Boolean(getEntityMeta(entity));
1485
+ };
1486
+
1487
+ // src/orm/relations/has-many.ts
1488
+ var toKey2 = (value) => value === null || value === void 0 ? "" : String(value);
1489
+ var DefaultHasManyCollection = class {
1490
+ constructor(ctx, meta, root, relationName, relation, rootTable, loader, createEntity, localKey) {
1491
+ this.ctx = ctx;
1492
+ this.meta = meta;
1493
+ this.root = root;
1494
+ this.relationName = relationName;
1495
+ this.relation = relation;
1496
+ this.rootTable = rootTable;
1497
+ this.loader = loader;
1498
+ this.createEntity = createEntity;
1499
+ this.localKey = localKey;
1500
+ this.loaded = false;
1501
+ this.items = [];
1502
+ this.added = /* @__PURE__ */ new Set();
1503
+ this.removed = /* @__PURE__ */ new Set();
1504
+ this.hydrateFromCache();
1505
+ }
1506
+ async load() {
1507
+ if (this.loaded) return this.items;
1508
+ const map = await this.loader();
1509
+ const key = toKey2(this.root[this.localKey]);
1510
+ const rows = map.get(key) ?? [];
1511
+ this.items = rows.map((row) => this.createEntity(row));
1512
+ this.loaded = true;
1513
+ return this.items;
1514
+ }
1515
+ getItems() {
1516
+ return this.items;
1517
+ }
1518
+ add(data) {
1519
+ const keyValue = this.root[this.localKey];
1520
+ const childRow = {
1521
+ ...data,
1522
+ [this.relation.foreignKey]: keyValue
1523
+ };
1524
+ const entity = this.createEntity(childRow);
1525
+ this.added.add(entity);
1526
+ this.items.push(entity);
1527
+ this.ctx.registerRelationChange(
1528
+ this.root,
1529
+ this.relationKey,
1530
+ this.rootTable,
1531
+ this.relationName,
1532
+ this.relation,
1533
+ { kind: "add", entity }
1534
+ );
1535
+ return entity;
1536
+ }
1537
+ attach(entity) {
1538
+ const keyValue = this.root[this.localKey];
1539
+ entity[this.relation.foreignKey] = keyValue;
1540
+ this.ctx.markDirty(entity);
1541
+ this.items.push(entity);
1542
+ this.ctx.registerRelationChange(
1543
+ this.root,
1544
+ this.relationKey,
1545
+ this.rootTable,
1546
+ this.relationName,
1547
+ this.relation,
1548
+ { kind: "attach", entity }
1549
+ );
1550
+ }
1551
+ remove(entity) {
1552
+ this.items = this.items.filter((item) => item !== entity);
1553
+ this.removed.add(entity);
1554
+ this.ctx.registerRelationChange(
1555
+ this.root,
1556
+ this.relationKey,
1557
+ this.rootTable,
1558
+ this.relationName,
1559
+ this.relation,
1560
+ { kind: "remove", entity }
1561
+ );
1562
+ }
1563
+ clear() {
1564
+ for (const entity of [...this.items]) {
1565
+ this.remove(entity);
1566
+ }
1567
+ }
1568
+ get relationKey() {
1569
+ return `${this.rootTable.name}.${this.relationName}`;
1570
+ }
1571
+ hydrateFromCache() {
1572
+ const keyValue = this.root[this.localKey];
1573
+ if (keyValue === void 0 || keyValue === null) return;
1574
+ const rows = getHydrationRows(this.meta, this.relationName, keyValue);
1575
+ if (!rows?.length) return;
1576
+ this.items = rows.map((row) => this.createEntity(row));
1577
+ this.loaded = true;
1578
+ }
1579
+ };
1580
+
1581
+ // src/orm/relations/belongs-to.ts
1582
+ var toKey3 = (value) => value === null || value === void 0 ? "" : String(value);
1583
+ var DefaultBelongsToReference = class {
1584
+ constructor(ctx, meta, root, relationName, relation, rootTable, loader, createEntity, targetKey) {
1585
+ this.ctx = ctx;
1586
+ this.meta = meta;
1587
+ this.root = root;
1588
+ this.relationName = relationName;
1589
+ this.relation = relation;
1590
+ this.rootTable = rootTable;
1591
+ this.loader = loader;
1592
+ this.createEntity = createEntity;
1593
+ this.targetKey = targetKey;
1594
+ this.loaded = false;
1595
+ this.current = null;
1596
+ this.populateFromHydrationCache();
1597
+ }
1598
+ async load() {
1599
+ if (this.loaded) return this.current;
1600
+ const map = await this.loader();
1601
+ const fkValue = this.root[this.relation.foreignKey];
1602
+ if (fkValue === null || fkValue === void 0) {
1603
+ this.current = null;
1604
+ } else {
1605
+ const row = map.get(toKey3(fkValue));
1606
+ this.current = row ? this.createEntity(row) : null;
1607
+ }
1608
+ this.loaded = true;
1609
+ return this.current;
1610
+ }
1611
+ get() {
1612
+ return this.current;
1613
+ }
1614
+ set(data) {
1615
+ if (data === null) {
1616
+ const previous = this.current;
1617
+ this.root[this.relation.foreignKey] = null;
1618
+ this.current = null;
1619
+ this.ctx.registerRelationChange(
1620
+ this.root,
1621
+ this.relationKey,
1622
+ this.rootTable,
1623
+ this.relationName,
1624
+ this.relation,
1625
+ { kind: "remove", entity: previous }
1626
+ );
1627
+ return null;
1628
+ }
1629
+ const entity = hasEntityMeta(data) ? data : this.createEntity(data);
1630
+ const pkValue = entity[this.targetKey];
1631
+ if (pkValue !== void 0) {
1632
+ this.root[this.relation.foreignKey] = pkValue;
1633
+ }
1634
+ this.current = entity;
1635
+ this.ctx.registerRelationChange(
1636
+ this.root,
1637
+ this.relationKey,
1638
+ this.rootTable,
1639
+ this.relationName,
1640
+ this.relation,
1641
+ { kind: "attach", entity }
1642
+ );
1643
+ return entity;
1644
+ }
1645
+ get relationKey() {
1646
+ return `${this.rootTable.name}.${this.relationName}`;
1647
+ }
1648
+ populateFromHydrationCache() {
1649
+ const fkValue = this.root[this.relation.foreignKey];
1650
+ if (fkValue === void 0 || fkValue === null) return;
1651
+ const row = getHydrationRecord(this.meta, this.relationName, fkValue);
1652
+ if (!row) return;
1653
+ this.current = this.createEntity(row);
1654
+ this.loaded = true;
1655
+ }
1656
+ };
1657
+
1658
+ // src/orm/relations/many-to-many.ts
1659
+ var toKey4 = (value) => value === null || value === void 0 ? "" : String(value);
1660
+ var DefaultManyToManyCollection = class {
1661
+ constructor(ctx, meta, root, relationName, relation, rootTable, loader, createEntity, localKey) {
1662
+ this.ctx = ctx;
1663
+ this.meta = meta;
1664
+ this.root = root;
1665
+ this.relationName = relationName;
1666
+ this.relation = relation;
1667
+ this.rootTable = rootTable;
1668
+ this.loader = loader;
1669
+ this.createEntity = createEntity;
1670
+ this.localKey = localKey;
1671
+ this.loaded = false;
1672
+ this.items = [];
1673
+ this.hydrateFromCache();
1674
+ }
1675
+ async load() {
1676
+ if (this.loaded) return this.items;
1677
+ const map = await this.loader();
1678
+ const key = toKey4(this.root[this.localKey]);
1679
+ const rows = map.get(key) ?? [];
1680
+ this.items = rows.map((row) => {
1681
+ const entity = this.createEntity(row);
1682
+ if (row._pivot) {
1683
+ entity._pivot = row._pivot;
1684
+ }
1685
+ return entity;
1686
+ });
1687
+ this.loaded = true;
1688
+ return this.items;
1689
+ }
1690
+ getItems() {
1691
+ return this.items;
1692
+ }
1693
+ attach(target) {
1694
+ const entity = this.ensureEntity(target);
1695
+ const id = this.extractId(entity);
1696
+ if (id == null) return;
1697
+ if (this.items.some((item) => this.extractId(item) === id)) {
1698
+ return;
1699
+ }
1700
+ this.items.push(entity);
1701
+ this.ctx.registerRelationChange(
1702
+ this.root,
1703
+ this.relationKey,
1704
+ this.rootTable,
1705
+ this.relationName,
1706
+ this.relation,
1707
+ { kind: "attach", entity }
1708
+ );
1709
+ }
1710
+ detach(target) {
1711
+ const id = typeof target === "number" || typeof target === "string" ? target : this.extractId(target);
1712
+ if (id == null) return;
1713
+ const existing = this.items.find((item) => this.extractId(item) === id);
1714
+ if (!existing) return;
1715
+ this.items = this.items.filter((item) => this.extractId(item) !== id);
1716
+ this.ctx.registerRelationChange(
1717
+ this.root,
1718
+ this.relationKey,
1719
+ this.rootTable,
1720
+ this.relationName,
1721
+ this.relation,
1722
+ { kind: "detach", entity: existing }
1723
+ );
1724
+ }
1725
+ async syncByIds(ids) {
1726
+ await this.load();
1727
+ const targetKey = this.relation.targetKey || findPrimaryKey(this.relation.target);
1728
+ const normalized = new Set(ids.map((id) => toKey4(id)));
1729
+ const currentIds = new Set(this.items.map((item) => toKey4(this.extractId(item))));
1730
+ for (const id of normalized) {
1731
+ if (!currentIds.has(id)) {
1732
+ this.attach(id);
1733
+ }
1734
+ }
1735
+ for (const item of [...this.items]) {
1736
+ const itemId = toKey4(this.extractId(item));
1737
+ if (!normalized.has(itemId)) {
1738
+ this.detach(item);
1739
+ }
1740
+ }
1741
+ }
1742
+ ensureEntity(target) {
1743
+ if (typeof target === "number" || typeof target === "string") {
1744
+ const stub = {
1745
+ [this.targetKey]: target
1746
+ };
1747
+ return this.createEntity(stub);
1748
+ }
1749
+ return target;
1750
+ }
1751
+ extractId(entity) {
1752
+ if (entity === null || entity === void 0) return null;
1753
+ if (typeof entity === "number" || typeof entity === "string") {
1754
+ return entity;
1755
+ }
1756
+ return entity[this.targetKey] ?? null;
1757
+ }
1758
+ get relationKey() {
1759
+ return `${this.rootTable.name}.${this.relationName}`;
1760
+ }
1761
+ get targetKey() {
1762
+ return this.relation.targetKey || findPrimaryKey(this.relation.target);
1763
+ }
1764
+ hydrateFromCache() {
1765
+ const keyValue = this.root[this.localKey];
1766
+ if (keyValue === void 0 || keyValue === null) return;
1767
+ const rows = getHydrationRows(this.meta, this.relationName, keyValue);
1768
+ if (!rows?.length) return;
1769
+ this.items = rows.map((row) => {
1770
+ const entity = this.createEntity(row);
1771
+ if (row._pivot) {
1772
+ entity._pivot = row._pivot;
1773
+ }
1774
+ return entity;
1775
+ });
1776
+ this.loaded = true;
1777
+ }
1778
+ };
1779
+
1780
+ // src/orm/lazy-batch.ts
1781
+ var selectAllColumns = (table) => Object.entries(table.columns).reduce((acc, [name, def]) => {
1782
+ acc[name] = def;
1783
+ return acc;
1784
+ }, {});
1785
+ var rowsFromResults = (results) => {
1786
+ const rows = [];
1787
+ for (const result of results) {
1788
+ const { columns, values } = result;
1789
+ for (const valueRow of values) {
1790
+ const row = {};
1791
+ columns.forEach((column, idx) => {
1792
+ row[column] = valueRow[idx];
1793
+ });
1794
+ rows.push(row);
1795
+ }
1796
+ }
1797
+ return rows;
1798
+ };
1799
+ var executeQuery = async (ctx, qb) => {
1800
+ const compiled = ctx.dialect.compileSelect(qb.getAST());
1801
+ const results = await ctx.executor.executeSql(compiled.sql, compiled.params);
1802
+ return rowsFromResults(results);
1803
+ };
1804
+ var toKey5 = (value) => value === null || value === void 0 ? "" : String(value);
1805
+ var loadHasManyRelation = async (ctx, rootTable, _relationName, relation) => {
1806
+ const localKey = relation.localKey || findPrimaryKey(rootTable);
1807
+ const roots = ctx.getEntitiesForTable(rootTable);
1808
+ const keys = /* @__PURE__ */ new Set();
1809
+ for (const tracked of roots) {
1810
+ const value = tracked.entity[localKey];
1811
+ if (value !== null && value !== void 0) {
1812
+ keys.add(value);
1813
+ }
1814
+ }
1815
+ if (!keys.size) {
1816
+ return /* @__PURE__ */ new Map();
1817
+ }
1818
+ const selectMap = selectAllColumns(relation.target);
1819
+ const fb = new SelectQueryBuilder(relation.target).select(selectMap);
1820
+ const fkColumn = relation.target.columns[relation.foreignKey];
1821
+ if (!fkColumn) return /* @__PURE__ */ new Map();
1822
+ fb.where(inList(fkColumn, Array.from(keys)));
1823
+ const rows = await executeQuery(ctx, fb);
1824
+ const grouped = /* @__PURE__ */ new Map();
1825
+ for (const row of rows) {
1826
+ const fkValue = row[relation.foreignKey];
1827
+ if (fkValue === null || fkValue === void 0) continue;
1828
+ const key = toKey5(fkValue);
1829
+ const bucket = grouped.get(key) ?? [];
1830
+ bucket.push(row);
1831
+ grouped.set(key, bucket);
1832
+ }
1833
+ return grouped;
1834
+ };
1835
+ var loadBelongsToRelation = async (ctx, rootTable, _relationName, relation) => {
1836
+ const roots = ctx.getEntitiesForTable(rootTable);
1837
+ const foreignKeys = /* @__PURE__ */ new Set();
1838
+ for (const tracked of roots) {
1839
+ const value = tracked.entity[relation.foreignKey];
1840
+ if (value !== null && value !== void 0) {
1841
+ foreignKeys.add(value);
1842
+ }
1843
+ }
1844
+ if (!foreignKeys.size) {
1845
+ return /* @__PURE__ */ new Map();
1846
+ }
1847
+ const selectMap = selectAllColumns(relation.target);
1848
+ const qb = new SelectQueryBuilder(relation.target).select(selectMap);
1849
+ const targetKey = relation.localKey || findPrimaryKey(relation.target);
1850
+ const pkColumn = relation.target.columns[targetKey];
1851
+ if (!pkColumn) return /* @__PURE__ */ new Map();
1852
+ qb.where(inList(pkColumn, Array.from(foreignKeys)));
1853
+ const rows = await executeQuery(ctx, qb);
1854
+ const map = /* @__PURE__ */ new Map();
1855
+ for (const row of rows) {
1856
+ const keyValue = row[targetKey];
1857
+ if (keyValue === null || keyValue === void 0) continue;
1858
+ map.set(toKey5(keyValue), row);
1859
+ }
1860
+ return map;
1861
+ };
1862
+ var loadBelongsToManyRelation = async (ctx, rootTable, _relationName, relation) => {
1863
+ const rootKey = relation.localKey || findPrimaryKey(rootTable);
1864
+ const roots = ctx.getEntitiesForTable(rootTable);
1865
+ const rootIds = /* @__PURE__ */ new Set();
1866
+ for (const tracked of roots) {
1867
+ const value = tracked.entity[rootKey];
1868
+ if (value !== null && value !== void 0) {
1869
+ rootIds.add(value);
1870
+ }
1871
+ }
1872
+ if (!rootIds.size) {
1873
+ return /* @__PURE__ */ new Map();
1874
+ }
1875
+ const pivotSelect = selectAllColumns(relation.pivotTable);
1876
+ const pivotQb = new SelectQueryBuilder(relation.pivotTable).select(pivotSelect);
1877
+ const pivotFkCol = relation.pivotTable.columns[relation.pivotForeignKeyToRoot];
1878
+ if (!pivotFkCol) return /* @__PURE__ */ new Map();
1879
+ pivotQb.where(inList(pivotFkCol, Array.from(rootIds)));
1880
+ const pivotRows = await executeQuery(ctx, pivotQb);
1881
+ const rootLookup = /* @__PURE__ */ new Map();
1882
+ const targetIds = /* @__PURE__ */ new Set();
1883
+ for (const pivot of pivotRows) {
1884
+ const rootValue = pivot[relation.pivotForeignKeyToRoot];
1885
+ const targetValue = pivot[relation.pivotForeignKeyToTarget];
1886
+ if (rootValue === null || rootValue === void 0 || targetValue === null || targetValue === void 0) {
1887
+ continue;
1888
+ }
1889
+ const bucket = rootLookup.get(toKey5(rootValue)) ?? [];
1890
+ bucket.push({
1891
+ targetId: targetValue,
1892
+ pivot: { ...pivot }
1893
+ });
1894
+ rootLookup.set(toKey5(rootValue), bucket);
1895
+ targetIds.add(targetValue);
1896
+ }
1897
+ if (!targetIds.size) {
1898
+ return /* @__PURE__ */ new Map();
1899
+ }
1900
+ const targetSelect = selectAllColumns(relation.target);
1901
+ const targetKey = relation.targetKey || findPrimaryKey(relation.target);
1902
+ const targetPkColumn = relation.target.columns[targetKey];
1903
+ if (!targetPkColumn) return /* @__PURE__ */ new Map();
1904
+ const targetQb = new SelectQueryBuilder(relation.target).select(targetSelect);
1905
+ targetQb.where(inList(targetPkColumn, Array.from(targetIds)));
1906
+ const targetRows = await executeQuery(ctx, targetQb);
1907
+ const targetMap = /* @__PURE__ */ new Map();
1908
+ for (const row of targetRows) {
1909
+ const pkValue = row[targetKey];
1910
+ if (pkValue === null || pkValue === void 0) continue;
1911
+ targetMap.set(toKey5(pkValue), row);
1912
+ }
1913
+ const result = /* @__PURE__ */ new Map();
1914
+ for (const [rootId, entries] of rootLookup.entries()) {
1915
+ const bucket = [];
1916
+ for (const entry of entries) {
1917
+ const targetRow = targetMap.get(toKey5(entry.targetId));
1918
+ if (!targetRow) continue;
1919
+ bucket.push({
1920
+ ...targetRow,
1921
+ _pivot: entry.pivot
1922
+ });
1923
+ }
1924
+ result.set(rootId, bucket);
1925
+ }
1926
+ return result;
1927
+ };
1928
+
1929
+ // src/orm/entity.ts
1930
+ var relationLoaderCache = (meta, relationName, factory) => {
1931
+ if (meta.relationCache.has(relationName)) {
1932
+ return meta.relationCache.get(relationName);
1933
+ }
1934
+ const promise = factory().then((value) => {
1935
+ for (const tracked of meta.ctx.getEntitiesForTable(meta.table)) {
1936
+ const otherMeta = getEntityMeta(tracked.entity);
1937
+ if (!otherMeta) continue;
1938
+ otherMeta.relationHydration.set(relationName, value);
1939
+ }
1940
+ return value;
1941
+ });
1942
+ meta.relationCache.set(relationName, promise);
1943
+ for (const tracked of meta.ctx.getEntitiesForTable(meta.table)) {
1944
+ const otherMeta = getEntityMeta(tracked.entity);
1945
+ if (!otherMeta) continue;
1946
+ otherMeta.relationCache.set(relationName, promise);
1947
+ }
1948
+ return promise;
1949
+ };
1950
+ var createEntityProxy = (ctx, table, row, lazyRelations = []) => {
1951
+ const target = { ...row };
1952
+ const meta = {
1953
+ ctx,
1954
+ table,
1955
+ lazyRelations: [...lazyRelations],
1956
+ relationCache: /* @__PURE__ */ new Map(),
1957
+ relationHydration: /* @__PURE__ */ new Map(),
1958
+ relationWrappers: /* @__PURE__ */ new Map()
1959
+ };
1960
+ Object.defineProperty(target, ENTITY_META, {
1961
+ value: meta,
1962
+ enumerable: false,
1963
+ writable: false
1964
+ });
1965
+ let proxy;
1966
+ const handler = {
1967
+ get(targetObj, prop, receiver) {
1968
+ if (prop === ENTITY_META) {
1969
+ return meta;
1970
+ }
1971
+ if (prop === "$load") {
1972
+ return async (relationName) => {
1973
+ const wrapper = getRelationWrapper(meta, relationName, proxy);
1974
+ if (wrapper && typeof wrapper.load === "function") {
1975
+ return wrapper.load();
1976
+ }
1977
+ return void 0;
1978
+ };
1979
+ }
1980
+ if (typeof prop === "string" && table.relations[prop]) {
1981
+ return getRelationWrapper(meta, prop, proxy);
1982
+ }
1983
+ return Reflect.get(targetObj, prop, receiver);
1984
+ },
1985
+ set(targetObj, prop, value, receiver) {
1986
+ const result = Reflect.set(targetObj, prop, value, receiver);
1987
+ if (typeof prop === "string" && table.columns[prop]) {
1988
+ ctx.markDirty(proxy);
1989
+ }
1990
+ return result;
1991
+ }
1992
+ };
1993
+ proxy = new Proxy(target, handler);
1994
+ populateHydrationCache(proxy, row, meta);
1995
+ return proxy;
1996
+ };
1997
+ var createEntityFromRow = (ctx, table, row, lazyRelations = []) => {
1998
+ const pkName = findPrimaryKey(table);
1999
+ const pkValue = row[pkName];
2000
+ if (pkValue !== void 0 && pkValue !== null) {
2001
+ const tracked = ctx.getEntity(table, pkValue);
2002
+ if (tracked) return tracked;
2003
+ }
2004
+ const entity = createEntityProxy(ctx, table, row, lazyRelations);
2005
+ if (pkValue !== void 0 && pkValue !== null) {
2006
+ ctx.trackManaged(table, pkValue, entity);
2007
+ } else {
2008
+ ctx.trackNew(table, entity);
2009
+ }
2010
+ return entity;
2011
+ };
2012
+ var toKey6 = (value) => value === null || value === void 0 ? "" : String(value);
2013
+ var populateHydrationCache = (entity, row, meta) => {
2014
+ for (const relationName of Object.keys(meta.table.relations)) {
2015
+ const relation = meta.table.relations[relationName];
2016
+ const data = row[relationName];
2017
+ if (!Array.isArray(data)) continue;
2018
+ if (relation.type === RelationKinds.HasMany || relation.type === RelationKinds.BelongsToMany) {
2019
+ const localKey = relation.localKey || findPrimaryKey(meta.table);
2020
+ const rootValue = entity[localKey];
2021
+ if (rootValue === void 0 || rootValue === null) continue;
2022
+ const cache = /* @__PURE__ */ new Map();
2023
+ cache.set(toKey6(rootValue), data);
2024
+ meta.relationHydration.set(relationName, cache);
2025
+ meta.relationCache.set(relationName, Promise.resolve(cache));
2026
+ continue;
2027
+ }
2028
+ if (relation.type === RelationKinds.BelongsTo) {
2029
+ const targetKey = relation.localKey || findPrimaryKey(relation.target);
2030
+ const cache = /* @__PURE__ */ new Map();
2031
+ for (const item of data) {
2032
+ const pkValue = item[targetKey];
2033
+ if (pkValue === void 0 || pkValue === null) continue;
2034
+ cache.set(toKey6(pkValue), item);
2035
+ }
2036
+ if (cache.size) {
2037
+ meta.relationHydration.set(relationName, cache);
2038
+ meta.relationCache.set(relationName, Promise.resolve(cache));
2039
+ }
2040
+ }
2041
+ }
2042
+ };
2043
+ var getRelationWrapper = (meta, relationName, owner) => {
2044
+ if (meta.relationWrappers.has(relationName)) {
2045
+ return meta.relationWrappers.get(relationName);
2046
+ }
2047
+ const relation = meta.table.relations[relationName];
2048
+ if (!relation) return void 0;
2049
+ const wrapper = instantiateWrapper(meta, relationName, relation, owner);
2050
+ if (wrapper) {
2051
+ meta.relationWrappers.set(relationName, wrapper);
2052
+ }
2053
+ return wrapper;
2054
+ };
2055
+ var instantiateWrapper = (meta, relationName, relation, owner) => {
2056
+ switch (relation.type) {
2057
+ case RelationKinds.HasMany: {
2058
+ const hasMany2 = relation;
2059
+ const localKey = hasMany2.localKey || findPrimaryKey(meta.table);
2060
+ const loader = () => relationLoaderCache(
2061
+ meta,
2062
+ relationName,
2063
+ () => loadHasManyRelation(meta.ctx, meta.table, relationName, hasMany2)
2064
+ );
2065
+ return new DefaultHasManyCollection(
2066
+ meta.ctx,
2067
+ meta,
2068
+ owner,
2069
+ relationName,
2070
+ hasMany2,
2071
+ meta.table,
2072
+ loader,
2073
+ (row) => createEntityFromRow(meta.ctx, relation.target, row),
2074
+ localKey
2075
+ );
2076
+ }
2077
+ case RelationKinds.BelongsTo: {
2078
+ const belongsTo2 = relation;
2079
+ const targetKey = belongsTo2.localKey || findPrimaryKey(belongsTo2.target);
2080
+ const loader = () => relationLoaderCache(
2081
+ meta,
2082
+ relationName,
2083
+ () => loadBelongsToRelation(meta.ctx, meta.table, relationName, belongsTo2)
2084
+ );
2085
+ return new DefaultBelongsToReference(
2086
+ meta.ctx,
2087
+ meta,
2088
+ owner,
2089
+ relationName,
2090
+ belongsTo2,
2091
+ meta.table,
2092
+ loader,
2093
+ (row) => createEntityFromRow(meta.ctx, relation.target, row),
2094
+ targetKey
2095
+ );
2096
+ }
2097
+ case RelationKinds.BelongsToMany: {
2098
+ const many = relation;
2099
+ const localKey = many.localKey || findPrimaryKey(meta.table);
2100
+ const loader = () => relationLoaderCache(
2101
+ meta,
2102
+ relationName,
2103
+ () => loadBelongsToManyRelation(meta.ctx, meta.table, relationName, many)
2104
+ );
2105
+ return new DefaultManyToManyCollection(
2106
+ meta.ctx,
2107
+ meta,
2108
+ owner,
2109
+ relationName,
2110
+ many,
2111
+ meta.table,
2112
+ loader,
2113
+ (row) => createEntityFromRow(meta.ctx, relation.target, row),
2114
+ localKey
2115
+ );
2116
+ }
2117
+ default:
2118
+ return void 0;
2119
+ }
2120
+ };
2121
+
2122
+ // src/orm/execute.ts
2123
+ var flattenResults = (results) => {
2124
+ const rows = [];
2125
+ for (const result of results) {
2126
+ const { columns, values } = result;
2127
+ for (const valueRow of values) {
2128
+ const row = {};
2129
+ columns.forEach((column, idx) => {
2130
+ row[column] = valueRow[idx];
2131
+ });
2132
+ rows.push(row);
2133
+ }
2134
+ }
2135
+ return rows;
2136
+ };
2137
+ async function executeHydrated(ctx, qb) {
2138
+ const compiled = ctx.dialect.compileSelect(qb.getAST());
2139
+ const executed = await ctx.executor.executeSql(compiled.sql, compiled.params);
2140
+ const rows = flattenResults(executed);
2141
+ const hydrated = hydrateRows(rows, qb.getHydrationPlan());
2142
+ return hydrated.map(
2143
+ (row) => createEntityFromRow(ctx, qb.getTable(), row, qb.getLazyRelations())
2144
+ );
2145
+ }
2146
+
2147
+ // src/query-builder/select.ts
2148
+ var SelectQueryBuilder = class _SelectQueryBuilder {
2149
+ /**
2150
+ * Creates a new SelectQueryBuilder instance
2151
+ * @param table - Table definition to query
2152
+ * @param state - Optional initial query state
2153
+ * @param hydration - Optional hydration manager
2154
+ * @param dependencies - Optional query builder dependencies
2155
+ */
2156
+ constructor(table, state, hydration, dependencies, lazyRelations) {
2157
+ const deps = resolveSelectQueryBuilderDependencies(dependencies);
2158
+ this.env = { table, deps };
2159
+ const initialState = state ?? deps.createState(table);
2160
+ const initialHydration = hydration ?? deps.createHydration(table);
2161
+ this.context = {
2162
+ state: initialState,
2163
+ hydration: initialHydration
2164
+ };
2165
+ this.lazyRelations = new Set(lazyRelations ?? []);
2166
+ this.columnSelector = new ColumnSelector(this.env);
2167
+ this.relationManager = new RelationManager(this.env);
2168
+ }
2169
+ clone(context = this.context, lazyRelations = new Set(this.lazyRelations)) {
2170
+ return new _SelectQueryBuilder(this.env.table, context.state, context.hydration, this.env.deps, lazyRelations);
2171
+ }
2172
+ resolveQueryNode(query) {
2173
+ return typeof query.getAST === "function" ? query.getAST() : query;
2174
+ }
2175
+ createChildBuilder(table) {
2176
+ return new _SelectQueryBuilder(table, void 0, void 0, this.env.deps);
2177
+ }
2178
+ applyAst(context, mutator) {
2179
+ const astService = this.env.deps.createQueryAstService(this.env.table, context.state);
2180
+ const nextState = mutator(astService);
2181
+ return { state: nextState, hydration: context.hydration };
2182
+ }
2183
+ applyJoin(context, table, condition, kind) {
2184
+ const joinNode = createJoinNode(kind, table.name, condition);
2185
+ return this.applyAst(context, (service) => service.withJoin(joinNode));
2186
+ }
2187
+ /**
2188
+ * Selects specific columns for the query
2189
+ * @param columns - Record of column definitions, function nodes, case expressions, or window functions
2190
+ * @returns New query builder instance with selected columns
2191
+ */
2192
+ select(columns) {
2193
+ return this.clone(this.columnSelector.select(this.context, columns));
2194
+ }
2195
+ /**
2196
+ * Selects raw column expressions
2197
+ * @param cols - Column expressions as strings
2198
+ * @returns New query builder instance with raw column selections
2199
+ */
2200
+ selectRaw(...cols) {
2201
+ return this.clone(this.columnSelector.selectRaw(this.context, cols));
2202
+ }
2203
+ /**
2204
+ * Adds a Common Table Expression (CTE) to the query
2205
+ * @param name - Name of the CTE
2206
+ * @param query - Query builder or query node for the CTE
2207
+ * @param columns - Optional column names for the CTE
2208
+ * @returns New query builder instance with the CTE
2209
+ */
2210
+ with(name, query, columns) {
2211
+ const subAst = this.resolveQueryNode(query);
2212
+ const nextContext = this.applyAst(this.context, (service) => service.withCte(name, subAst, columns, false));
2213
+ return this.clone(nextContext);
2214
+ }
2215
+ /**
2216
+ * Adds a recursive Common Table Expression (CTE) to the query
2217
+ * @param name - Name of the CTE
2218
+ * @param query - Query builder or query node for the CTE
2219
+ * @param columns - Optional column names for the CTE
2220
+ * @returns New query builder instance with the recursive CTE
2221
+ */
2222
+ withRecursive(name, query, columns) {
2223
+ const subAst = this.resolveQueryNode(query);
2224
+ const nextContext = this.applyAst(this.context, (service) => service.withCte(name, subAst, columns, true));
2225
+ return this.clone(nextContext);
2226
+ }
2227
+ /**
2228
+ * Selects a subquery as a column
2229
+ * @param alias - Alias for the subquery column
2230
+ * @param sub - Query builder or query node for the subquery
2231
+ * @returns New query builder instance with the subquery selection
2232
+ */
2233
+ selectSubquery(alias, sub) {
2234
+ const query = this.resolveQueryNode(sub);
2235
+ return this.clone(this.columnSelector.selectSubquery(this.context, alias, query));
2236
+ }
2237
+ /**
2238
+ * Adds an INNER JOIN to the query
2239
+ * @param table - Table to join
2240
+ * @param condition - Join condition expression
2241
+ * @returns New query builder instance with the INNER JOIN
2242
+ */
2243
+ innerJoin(table, condition) {
2244
+ const nextContext = this.applyJoin(this.context, table, condition, JOIN_KINDS.INNER);
2245
+ return this.clone(nextContext);
2246
+ }
2247
+ /**
2248
+ * Adds a LEFT JOIN to the query
2249
+ * @param table - Table to join
2250
+ * @param condition - Join condition expression
2251
+ * @returns New query builder instance with the LEFT JOIN
2252
+ */
2253
+ leftJoin(table, condition) {
2254
+ const nextContext = this.applyJoin(this.context, table, condition, JOIN_KINDS.LEFT);
2255
+ return this.clone(nextContext);
2256
+ }
2257
+ /**
2258
+ * Adds a RIGHT JOIN to the query
2259
+ * @param table - Table to join
2260
+ * @param condition - Join condition expression
2261
+ * @returns New query builder instance with the RIGHT JOIN
2262
+ */
2263
+ rightJoin(table, condition) {
2264
+ const nextContext = this.applyJoin(this.context, table, condition, JOIN_KINDS.RIGHT);
2265
+ return this.clone(nextContext);
2266
+ }
2267
+ /**
2268
+ * Matches records based on a relationship
2269
+ * @param relationName - Name of the relationship to match
2270
+ * @param predicate - Optional predicate expression
2271
+ * @returns New query builder instance with the relationship match
2272
+ */
2273
+ match(relationName, predicate) {
2274
+ const nextContext = this.relationManager.match(this.context, relationName, predicate);
2275
+ return this.clone(nextContext);
2276
+ }
2277
+ /**
2278
+ * Joins a related table
2279
+ * @param relationName - Name of the relationship to join
2280
+ * @param joinKind - Type of join (defaults to INNER)
2281
+ * @param extraCondition - Optional additional join condition
2282
+ * @returns New query builder instance with the relationship join
2283
+ */
2284
+ joinRelation(relationName, joinKind = JOIN_KINDS.INNER, extraCondition) {
2285
+ const nextContext = this.relationManager.joinRelation(this.context, relationName, joinKind, extraCondition);
2286
+ return this.clone(nextContext);
2287
+ }
2288
+ /**
2289
+ * Includes related data in the query results
2290
+ * @param relationName - Name of the relationship to include
2291
+ * @param options - Optional include options
2292
+ * @returns New query builder instance with the relationship inclusion
2293
+ */
2294
+ include(relationName, options) {
2295
+ const nextContext = this.relationManager.include(this.context, relationName, options);
2296
+ return this.clone(nextContext);
2297
+ }
2298
+ includeLazy(relationName) {
2299
+ const nextLazy = new Set(this.lazyRelations);
2300
+ nextLazy.add(relationName);
2301
+ return this.clone(this.context, nextLazy);
2302
+ }
2303
+ getLazyRelations() {
2304
+ return Array.from(this.lazyRelations);
2305
+ }
2306
+ getTable() {
2307
+ return this.env.table;
2308
+ }
2309
+ async execute(ctx) {
2310
+ return executeHydrated(ctx, this);
2311
+ }
2312
+ /**
2313
+ * Adds a WHERE condition to the query
2314
+ * @param expr - Expression for the WHERE clause
2315
+ * @returns New query builder instance with the WHERE condition
2316
+ */
2317
+ where(expr) {
2318
+ const nextContext = this.applyAst(this.context, (service) => service.withWhere(expr));
2319
+ return this.clone(nextContext);
2320
+ }
2321
+ /**
2322
+ * Adds a GROUP BY clause to the query
2323
+ * @param col - Column definition or column node to group by
2324
+ * @returns New query builder instance with the GROUP BY clause
2325
+ */
2326
+ groupBy(col) {
2327
+ const nextContext = this.applyAst(this.context, (service) => service.withGroupBy(col));
2328
+ return this.clone(nextContext);
2329
+ }
2330
+ /**
2331
+ * Adds a HAVING condition to the query
2332
+ * @param expr - Expression for the HAVING clause
2333
+ * @returns New query builder instance with the HAVING condition
2334
+ */
2335
+ having(expr) {
2336
+ const nextContext = this.applyAst(this.context, (service) => service.withHaving(expr));
2337
+ return this.clone(nextContext);
2338
+ }
2339
+ /**
2340
+ * Adds an ORDER BY clause to the query
2341
+ * @param col - Column definition or column node to order by
2342
+ * @param direction - Order direction (defaults to ASC)
2343
+ * @returns New query builder instance with the ORDER BY clause
2344
+ */
2345
+ orderBy(col, direction = ORDER_DIRECTIONS.ASC) {
2346
+ const nextContext = this.applyAst(this.context, (service) => service.withOrderBy(col, direction));
2347
+ return this.clone(nextContext);
2348
+ }
2349
+ /**
2350
+ * Adds a DISTINCT clause to the query
2351
+ * @param cols - Columns to make distinct
2352
+ * @returns New query builder instance with the DISTINCT clause
2353
+ */
2354
+ distinct(...cols) {
2355
+ return this.clone(this.columnSelector.distinct(this.context, cols));
2356
+ }
2357
+ /**
2358
+ * Adds a LIMIT clause to the query
2359
+ * @param n - Maximum number of rows to return
2360
+ * @returns New query builder instance with the LIMIT clause
2361
+ */
2362
+ limit(n) {
2363
+ const nextContext = this.applyAst(this.context, (service) => service.withLimit(n));
2364
+ return this.clone(nextContext);
2365
+ }
2366
+ /**
2367
+ * Adds an OFFSET clause to the query
2368
+ * @param n - Number of rows to skip
2369
+ * @returns New query builder instance with the OFFSET clause
2370
+ */
2371
+ offset(n) {
2372
+ const nextContext = this.applyAst(this.context, (service) => service.withOffset(n));
2373
+ return this.clone(nextContext);
2374
+ }
2375
+ /**
2376
+ * Adds a WHERE EXISTS condition to the query
2377
+ * @param subquery - Subquery to check for existence
2378
+ * @returns New query builder instance with the WHERE EXISTS condition
2379
+ */
2380
+ whereExists(subquery) {
2381
+ const subAst = this.resolveQueryNode(subquery);
2382
+ return this.where(exists(subAst));
2383
+ }
2384
+ /**
2385
+ * Adds a WHERE NOT EXISTS condition to the query
2386
+ * @param subquery - Subquery to check for non-existence
2387
+ * @returns New query builder instance with the WHERE NOT EXISTS condition
2388
+ */
2389
+ whereNotExists(subquery) {
2390
+ const subAst = this.resolveQueryNode(subquery);
2391
+ return this.where(notExists(subAst));
2392
+ }
2393
+ /**
2394
+ * Adds a WHERE EXISTS condition based on a relationship
2395
+ * @param relationName - Name of the relationship to check
2396
+ * @param callback - Optional callback to modify the relationship query
2397
+ * @returns New query builder instance with the relationship existence check
2398
+ */
2399
+ whereHas(relationName, callback) {
2400
+ const relation = this.env.table.relations[relationName];
2401
+ if (!relation) {
2402
+ throw new Error(`Relation '${relationName}' not found on table '${this.env.table.name}'`);
2403
+ }
2404
+ let subQb = this.createChildBuilder(relation.target);
2405
+ if (callback) {
2406
+ subQb = callback(subQb);
2407
+ }
2408
+ const subAst = subQb.getAST();
2409
+ const finalSubAst = this.relationManager.applyRelationCorrelation(this.context, relationName, subAst);
2410
+ return this.where(exists(finalSubAst));
2411
+ }
2412
+ /**
2413
+ * Adds a WHERE NOT EXISTS condition based on a relationship
2414
+ * @param relationName - Name of the relationship to check
2415
+ * @param callback - Optional callback to modify the relationship query
2416
+ * @returns New query builder instance with the relationship non-existence check
2417
+ */
2418
+ whereHasNot(relationName, callback) {
2419
+ const relation = this.env.table.relations[relationName];
2420
+ if (!relation) {
2421
+ throw new Error(`Relation '${relationName}' not found on table '${this.env.table.name}'`);
2422
+ }
2423
+ let subQb = this.createChildBuilder(relation.target);
2424
+ if (callback) {
2425
+ subQb = callback(subQb);
2426
+ }
2427
+ const subAst = subQb.getAST();
2428
+ const finalSubAst = this.relationManager.applyRelationCorrelation(this.context, relationName, subAst);
2429
+ return this.where(notExists(finalSubAst));
2430
+ }
2431
+ /**
2432
+ * Compiles the query to SQL for a specific dialect
2433
+ * @param dialect - Database dialect to compile for
2434
+ * @returns Compiled query with SQL and parameters
2435
+ */
2436
+ compile(dialect) {
2437
+ return dialect.compileSelect(this.context.state.ast);
2438
+ }
2439
+ /**
2440
+ * Converts the query to SQL string for a specific dialect
2441
+ * @param dialect - Database dialect to generate SQL for
2442
+ * @returns SQL string representation of the query
2443
+ */
2444
+ toSql(dialect) {
2445
+ return this.compile(dialect).sql;
2446
+ }
2447
+ /**
2448
+ * Gets the hydration plan for the query
2449
+ * @returns Hydration plan or undefined if none exists
2450
+ */
2451
+ getHydrationPlan() {
2452
+ return this.context.hydration.getPlan();
2453
+ }
2454
+ /**
2455
+ * Gets the Abstract Syntax Tree (AST) representation of the query
2456
+ * @returns Query AST with hydration applied
2457
+ */
2458
+ getAST() {
2459
+ return this.context.hydration.applyToAst(this.context.state.ast);
2460
+ }
2461
+ };
2462
+
2463
+ // src/decorators/bootstrap.ts
2464
+ var isTableDef = (value) => {
2465
+ return typeof value === "object" && value !== null && "columns" in value;
2466
+ };
2467
+ var unwrapTarget = (target) => {
2468
+ if (typeof target === "function" && target.prototype === void 0) {
2469
+ return target();
2470
+ }
2471
+ return target;
2472
+ };
2473
+ var resolveTableTarget = (target, tableMap) => {
2474
+ const resolved = unwrapTarget(target);
2475
+ if (isTableDef(resolved)) {
2476
+ return resolved;
2477
+ }
2478
+ const table = tableMap.get(resolved);
2479
+ if (!table) {
2480
+ throw new Error(`Entity '${resolved.name}' is not registered with decorators`);
2481
+ }
2482
+ return table;
2483
+ };
2484
+ var buildRelationDefinitions = (meta, tableMap) => {
2485
+ const relations = {};
2486
+ for (const [name, relation] of Object.entries(meta.relations)) {
2487
+ switch (relation.kind) {
2488
+ case RelationKinds.HasMany: {
2489
+ relations[name] = hasMany(
2490
+ resolveTableTarget(relation.target, tableMap),
2491
+ relation.foreignKey,
2492
+ relation.localKey,
2493
+ relation.cascade
2494
+ );
2495
+ break;
2496
+ }
2497
+ case RelationKinds.BelongsTo: {
2498
+ relations[name] = belongsTo(
2499
+ resolveTableTarget(relation.target, tableMap),
2500
+ relation.foreignKey,
2501
+ relation.localKey,
2502
+ relation.cascade
2503
+ );
2504
+ break;
2505
+ }
2506
+ case RelationKinds.BelongsToMany: {
2507
+ relations[name] = belongsToMany(
2508
+ resolveTableTarget(relation.target, tableMap),
2509
+ resolveTableTarget(relation.pivotTable, tableMap),
2510
+ {
2511
+ pivotForeignKeyToRoot: relation.pivotForeignKeyToRoot,
2512
+ pivotForeignKeyToTarget: relation.pivotForeignKeyToTarget,
2513
+ localKey: relation.localKey,
2514
+ targetKey: relation.targetKey,
2515
+ pivotPrimaryKey: relation.pivotPrimaryKey,
2516
+ defaultPivotColumns: relation.defaultPivotColumns,
2517
+ cascade: relation.cascade
2518
+ }
2519
+ );
2520
+ break;
2521
+ }
2522
+ }
2523
+ }
2524
+ return relations;
2525
+ };
2526
+ var bootstrapEntities = () => {
2527
+ const metas = getAllEntityMetadata();
2528
+ const tableMap = /* @__PURE__ */ new Map();
2529
+ for (const meta of metas) {
2530
+ const table = buildTableDef(meta);
2531
+ tableMap.set(meta.target, table);
2532
+ }
2533
+ for (const meta of metas) {
2534
+ const table = meta.table;
2535
+ const relations = buildRelationDefinitions(meta, tableMap);
2536
+ table.relations = relations;
2537
+ }
2538
+ return metas.map((meta) => meta.table);
2539
+ };
2540
+ var getTableDefFromEntity = (ctor) => {
2541
+ const meta = getEntityMetadata(ctor);
2542
+ if (!meta) return void 0;
2543
+ return meta.table;
2544
+ };
2545
+ var selectFromEntity = (ctor) => {
2546
+ const table = getTableDefFromEntity(ctor);
2547
+ if (!table) {
2548
+ throw new Error("Entity metadata has not been bootstrapped");
2549
+ }
2550
+ return new SelectQueryBuilder(table);
2551
+ };
2552
+ // Annotate the CommonJS export names for ESM import in node:
2553
+ 0 && (module.exports = {
2554
+ BelongsTo,
2555
+ BelongsToMany,
2556
+ Column,
2557
+ Entity,
2558
+ HasMany,
2559
+ PrimaryKey,
2560
+ bootstrapEntities,
2561
+ getTableDefFromEntity,
2562
+ selectFromEntity
2563
+ });
2564
+ //# sourceMappingURL=index.cjs.map