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,232 @@
1
+ import { eq } from '../core/ast/expression.js';
2
+ import type { Dialect, CompiledQuery } from '../core/dialect/abstract.js';
3
+ import { InsertQueryBuilder } from '../query-builder/insert.js';
4
+ import { UpdateQueryBuilder } from '../query-builder/update.js';
5
+ import { DeleteQueryBuilder } from '../query-builder/delete.js';
6
+ import { findPrimaryKey } from '../query-builder/hydration-planner.js';
7
+ import type { TableDef, TableHooks } from '../schema/table.js';
8
+ import type { DbExecutor } from './db-executor.js';
9
+ import { IdentityMap } from './identity-map.js';
10
+ import { EntityStatus } from './runtime-types.js';
11
+ import type { TrackedEntity } from './runtime-types.js';
12
+
13
+ export class UnitOfWork {
14
+ private readonly trackedEntities = new Map<any, TrackedEntity>();
15
+
16
+ constructor(
17
+ private readonly dialect: Dialect,
18
+ private readonly executor: DbExecutor,
19
+ private readonly identityMap: IdentityMap,
20
+ private readonly hookContext: () => unknown
21
+ ) {}
22
+
23
+ get identityBuckets(): Map<string, Map<string, TrackedEntity>> {
24
+ return this.identityMap.bucketsMap;
25
+ }
26
+
27
+ getTracked(): TrackedEntity[] {
28
+ return Array.from(this.trackedEntities.values());
29
+ }
30
+
31
+ getEntity(table: TableDef, pk: string | number): any | undefined {
32
+ return this.identityMap.getEntity(table, pk);
33
+ }
34
+
35
+ getEntitiesForTable(table: TableDef): TrackedEntity[] {
36
+ return this.identityMap.getEntitiesForTable(table);
37
+ }
38
+
39
+ findTracked(entity: any): TrackedEntity | undefined {
40
+ return this.trackedEntities.get(entity);
41
+ }
42
+
43
+ setEntity(table: TableDef, pk: string | number, entity: any): void {
44
+ if (pk === null || pk === undefined) return;
45
+ let tracked = this.trackedEntities.get(entity);
46
+ if (!tracked) {
47
+ tracked = {
48
+ table,
49
+ entity,
50
+ pk,
51
+ status: EntityStatus.Managed,
52
+ original: this.createSnapshot(table, entity)
53
+ };
54
+ this.trackedEntities.set(entity, tracked);
55
+ } else {
56
+ tracked.pk = pk;
57
+ }
58
+
59
+ this.registerIdentity(tracked);
60
+ }
61
+
62
+ trackNew(table: TableDef, entity: any, pk?: string | number): void {
63
+ const tracked: TrackedEntity = {
64
+ table,
65
+ entity,
66
+ pk: pk ?? null,
67
+ status: EntityStatus.New,
68
+ original: null
69
+ };
70
+ this.trackedEntities.set(entity, tracked);
71
+ if (pk != null) {
72
+ this.registerIdentity(tracked);
73
+ }
74
+ }
75
+
76
+ trackManaged(table: TableDef, pk: string | number, entity: any): void {
77
+ const tracked: TrackedEntity = {
78
+ table,
79
+ entity,
80
+ pk,
81
+ status: EntityStatus.Managed,
82
+ original: this.createSnapshot(table, entity)
83
+ };
84
+ this.trackedEntities.set(entity, tracked);
85
+ this.registerIdentity(tracked);
86
+ }
87
+
88
+ markDirty(entity: any): void {
89
+ const tracked = this.trackedEntities.get(entity);
90
+ if (!tracked) return;
91
+ if (tracked.status === EntityStatus.New || tracked.status === EntityStatus.Removed) return;
92
+ tracked.status = EntityStatus.Dirty;
93
+ }
94
+
95
+ markRemoved(entity: any): void {
96
+ const tracked = this.trackedEntities.get(entity);
97
+ if (!tracked) return;
98
+ tracked.status = EntityStatus.Removed;
99
+ }
100
+
101
+ async flush(): Promise<void> {
102
+ const toFlush = Array.from(this.trackedEntities.values());
103
+ for (const tracked of toFlush) {
104
+ switch (tracked.status) {
105
+ case EntityStatus.New:
106
+ await this.flushInsert(tracked);
107
+ break;
108
+ case EntityStatus.Dirty:
109
+ await this.flushUpdate(tracked);
110
+ break;
111
+ case EntityStatus.Removed:
112
+ await this.flushDelete(tracked);
113
+ break;
114
+ default:
115
+ break;
116
+ }
117
+ }
118
+ }
119
+
120
+ private async flushInsert(tracked: TrackedEntity): Promise<void> {
121
+ await this.runHook(tracked.table.hooks?.beforeInsert, tracked);
122
+
123
+ const payload = this.extractColumns(tracked.table, tracked.entity);
124
+ const builder = new InsertQueryBuilder(tracked.table).values(payload);
125
+ const compiled = builder.compile(this.dialect);
126
+ await this.executeCompiled(compiled);
127
+
128
+ tracked.status = EntityStatus.Managed;
129
+ tracked.original = this.createSnapshot(tracked.table, tracked.entity);
130
+ tracked.pk = this.getPrimaryKeyValue(tracked);
131
+ this.registerIdentity(tracked);
132
+
133
+ await this.runHook(tracked.table.hooks?.afterInsert, tracked);
134
+ }
135
+
136
+ private async flushUpdate(tracked: TrackedEntity): Promise<void> {
137
+ if (tracked.pk == null) return;
138
+ const changes = this.computeChanges(tracked);
139
+ if (!Object.keys(changes).length) {
140
+ tracked.status = EntityStatus.Managed;
141
+ return;
142
+ }
143
+
144
+ await this.runHook(tracked.table.hooks?.beforeUpdate, tracked);
145
+
146
+ const pkColumn = tracked.table.columns[findPrimaryKey(tracked.table)];
147
+ if (!pkColumn) return;
148
+
149
+ const builder = new UpdateQueryBuilder(tracked.table)
150
+ .set(changes)
151
+ .where(eq(pkColumn, tracked.pk));
152
+
153
+ const compiled = builder.compile(this.dialect);
154
+ await this.executeCompiled(compiled);
155
+
156
+ tracked.status = EntityStatus.Managed;
157
+ tracked.original = this.createSnapshot(tracked.table, tracked.entity);
158
+ this.registerIdentity(tracked);
159
+
160
+ await this.runHook(tracked.table.hooks?.afterUpdate, tracked);
161
+ }
162
+
163
+ private async flushDelete(tracked: TrackedEntity): Promise<void> {
164
+ if (tracked.pk == null) return;
165
+ await this.runHook(tracked.table.hooks?.beforeDelete, tracked);
166
+
167
+ const pkColumn = tracked.table.columns[findPrimaryKey(tracked.table)];
168
+ if (!pkColumn) return;
169
+
170
+ const builder = new DeleteQueryBuilder(tracked.table).where(eq(pkColumn, tracked.pk));
171
+ const compiled = builder.compile(this.dialect);
172
+ await this.executeCompiled(compiled);
173
+
174
+ tracked.status = EntityStatus.Detached;
175
+ this.trackedEntities.delete(tracked.entity);
176
+ this.identityMap.remove(tracked);
177
+
178
+ await this.runHook(tracked.table.hooks?.afterDelete, tracked);
179
+ }
180
+
181
+ private async runHook(
182
+ hook: TableHooks[keyof TableHooks] | undefined,
183
+ tracked: TrackedEntity
184
+ ): Promise<void> {
185
+ if (!hook) return;
186
+ await hook(this.hookContext() as any, tracked.entity);
187
+ }
188
+
189
+ private computeChanges(tracked: TrackedEntity): Record<string, unknown> {
190
+ const snapshot = tracked.original ?? {};
191
+ const changes: Record<string, unknown> = {};
192
+ for (const column of Object.keys(tracked.table.columns)) {
193
+ const current = tracked.entity[column];
194
+ if (snapshot[column] !== current) {
195
+ changes[column] = current;
196
+ }
197
+ }
198
+ return changes;
199
+ }
200
+
201
+ private extractColumns(table: TableDef, entity: any): Record<string, unknown> {
202
+ const payload: Record<string, unknown> = {};
203
+ for (const column of Object.keys(table.columns)) {
204
+ payload[column] = entity[column];
205
+ }
206
+ return payload;
207
+ }
208
+
209
+ private async executeCompiled(compiled: CompiledQuery): Promise<void> {
210
+ await this.executor.executeSql(compiled.sql, compiled.params);
211
+ }
212
+
213
+ private registerIdentity(tracked: TrackedEntity): void {
214
+ if (tracked.pk == null) return;
215
+ this.identityMap.register(tracked);
216
+ }
217
+
218
+ private createSnapshot(table: TableDef, entity: any): Record<string, any> {
219
+ const snapshot: Record<string, any> = {};
220
+ for (const column of Object.keys(table.columns)) {
221
+ snapshot[column] = entity[column];
222
+ }
223
+ return snapshot;
224
+ }
225
+
226
+ private getPrimaryKeyValue(tracked: TrackedEntity): string | number | null {
227
+ const key = findPrimaryKey(tracked.table);
228
+ const val = tracked.entity[key];
229
+ if (val === undefined || val === null) return null;
230
+ return val;
231
+ }
232
+ }
@@ -1,78 +1,78 @@
1
- import { ColumnDef } from '../../schema/column';
2
- import { CaseExpressionNode, ColumnNode, FunctionNode, WindowFunctionNode } from '../../ast/expression';
3
- import { SelectQueryNode } from '../../ast/query';
4
- import { buildColumnNode } from '../query-ast-service';
5
- import { SelectQueryBuilderContext, SelectQueryBuilderEnvironment } from '../select-query-builder-deps';
6
-
7
- /**
8
- * Type for column selection input
9
- */
10
- type ColumnSelectionInput = Record<string, ColumnDef | FunctionNode | CaseExpressionNode | WindowFunctionNode>;
11
-
12
- /**
13
- * Handles column selection operations for the query builder
14
- */
15
- export class ColumnSelector {
16
- /**
17
- * Creates a new ColumnSelector instance
18
- * @param env - Query builder environment
19
- */
20
- constructor(private readonly env: SelectQueryBuilderEnvironment) {}
21
-
22
- /**
23
- * Selects columns for the query
24
- * @param context - Current query context
25
- * @param columns - Columns to select
26
- * @returns Updated query context with selected columns
27
- */
28
- select(context: SelectQueryBuilderContext, columns: ColumnSelectionInput): SelectQueryBuilderContext {
29
- const astService = this.env.deps.createQueryAstService(this.env.table, context.state);
30
- const { state: nextState, addedColumns } = astService.select(columns);
31
- return {
32
- state: nextState,
33
- hydration: context.hydration.onColumnsSelected(nextState, addedColumns)
34
- };
35
- }
36
-
37
- /**
38
- * Selects raw column expressions
39
- * @param context - Current query context
40
- * @param columns - Raw column expressions
41
- * @returns Updated query context with raw column selections
42
- */
43
- selectRaw(context: SelectQueryBuilderContext, columns: string[]): SelectQueryBuilderContext {
44
- const astService = this.env.deps.createQueryAstService(this.env.table, context.state);
45
- const nextState = astService.selectRaw(columns).state;
46
- return { state: nextState, hydration: context.hydration };
47
- }
48
-
49
- /**
50
- * Selects a subquery as a column
51
- * @param context - Current query context
52
- * @param alias - Alias for the subquery
53
- * @param query - Subquery to select
54
- * @returns Updated query context with subquery selection
55
- */
56
- selectSubquery(
57
- context: SelectQueryBuilderContext,
58
- alias: string,
59
- query: SelectQueryNode
60
- ): SelectQueryBuilderContext {
61
- const astService = this.env.deps.createQueryAstService(this.env.table, context.state);
62
- const nextState = astService.selectSubquery(alias, query);
63
- return { state: nextState, hydration: context.hydration };
64
- }
65
-
66
- /**
67
- * Adds DISTINCT clause to the query
68
- * @param context - Current query context
69
- * @param columns - Columns to make distinct
70
- * @returns Updated query context with DISTINCT clause
71
- */
72
- distinct(context: SelectQueryBuilderContext, columns: (ColumnDef | ColumnNode)[]): SelectQueryBuilderContext {
73
- const nodes = columns.map(col => buildColumnNode(this.env.table, col));
74
- const astService = this.env.deps.createQueryAstService(this.env.table, context.state);
75
- const nextState = astService.withDistinct(nodes);
76
- return { state: nextState, hydration: context.hydration };
77
- }
78
- }
1
+ import { ColumnDef } from '../schema/column.js';
2
+ import { CaseExpressionNode, ColumnNode, FunctionNode, WindowFunctionNode } from '../core/ast/expression.js';
3
+ import { SelectQueryNode } from '../core/ast/query.js';
4
+ import { buildColumnNode } from '../core/ast/builders.js';
5
+ import { SelectQueryBuilderContext, SelectQueryBuilderEnvironment } from './select-query-builder-deps.js';
6
+
7
+ /**
8
+ * Type for column selection input
9
+ */
10
+ type ColumnSelectionInput = Record<string, ColumnDef | FunctionNode | CaseExpressionNode | WindowFunctionNode>;
11
+
12
+ /**
13
+ * Handles column selection operations for the query builder
14
+ */
15
+ export class ColumnSelector {
16
+ /**
17
+ * Creates a new ColumnSelector instance
18
+ * @param env - Query builder environment
19
+ */
20
+ constructor(private readonly env: SelectQueryBuilderEnvironment) {}
21
+
22
+ /**
23
+ * Selects columns for the query
24
+ * @param context - Current query context
25
+ * @param columns - Columns to select
26
+ * @returns Updated query context with selected columns
27
+ */
28
+ select(context: SelectQueryBuilderContext, columns: ColumnSelectionInput): SelectQueryBuilderContext {
29
+ const astService = this.env.deps.createQueryAstService(this.env.table, context.state);
30
+ const { state: nextState, addedColumns } = astService.select(columns);
31
+ return {
32
+ state: nextState,
33
+ hydration: context.hydration.onColumnsSelected(nextState, addedColumns)
34
+ };
35
+ }
36
+
37
+ /**
38
+ * Selects raw column expressions
39
+ * @param context - Current query context
40
+ * @param columns - Raw column expressions
41
+ * @returns Updated query context with raw column selections
42
+ */
43
+ selectRaw(context: SelectQueryBuilderContext, columns: string[]): SelectQueryBuilderContext {
44
+ const astService = this.env.deps.createQueryAstService(this.env.table, context.state);
45
+ const nextState = astService.selectRaw(columns).state;
46
+ return { state: nextState, hydration: context.hydration };
47
+ }
48
+
49
+ /**
50
+ * Selects a subquery as a column
51
+ * @param context - Current query context
52
+ * @param alias - Alias for the subquery
53
+ * @param query - Subquery to select
54
+ * @returns Updated query context with subquery selection
55
+ */
56
+ selectSubquery(
57
+ context: SelectQueryBuilderContext,
58
+ alias: string,
59
+ query: SelectQueryNode
60
+ ): SelectQueryBuilderContext {
61
+ const astService = this.env.deps.createQueryAstService(this.env.table, context.state);
62
+ const nextState = astService.selectSubquery(alias, query);
63
+ return { state: nextState, hydration: context.hydration };
64
+ }
65
+
66
+ /**
67
+ * Adds DISTINCT clause to the query
68
+ * @param context - Current query context
69
+ * @param columns - Columns to make distinct
70
+ * @returns Updated query context with DISTINCT clause
71
+ */
72
+ distinct(context: SelectQueryBuilderContext, columns: (ColumnDef | ColumnNode)[]): SelectQueryBuilderContext {
73
+ const nodes = columns.map(col => buildColumnNode(this.env.table, col));
74
+ const astService = this.env.deps.createQueryAstService(this.env.table, context.state);
75
+ const nextState = astService.withDistinct(nodes);
76
+ return { state: nextState, hydration: context.hydration };
77
+ }
78
+ }
@@ -1,42 +1,38 @@
1
- import { TableDef } from '../schema/table';
2
- import { ColumnNode, ExpressionNode } from '../ast/expression';
3
- import { TableNode, DeleteQueryNode } from '../ast/query';
4
-
5
- const createTableNode = (table: TableDef): TableNode => ({
6
- type: 'Table',
7
- name: table.name
8
- });
9
-
10
- /**
11
- * Maintains immutable state for DELETE queries
12
- */
13
- export class DeleteQueryState {
14
- public readonly table: TableDef;
15
- public readonly ast: DeleteQueryNode;
16
-
17
- constructor(table: TableDef, ast?: DeleteQueryNode) {
18
- this.table = table;
19
- this.ast = ast ?? {
20
- type: 'DeleteQuery',
21
- from: createTableNode(table)
22
- };
23
- }
24
-
25
- private clone(nextAst: DeleteQueryNode): DeleteQueryState {
26
- return new DeleteQueryState(this.table, nextAst);
27
- }
28
-
29
- withWhere(expr: ExpressionNode): DeleteQueryState {
30
- return this.clone({
31
- ...this.ast,
32
- where: expr
33
- });
34
- }
35
-
36
- withReturning(columns: ColumnNode[]): DeleteQueryState {
37
- return this.clone({
38
- ...this.ast,
39
- returning: [...columns]
40
- });
41
- }
42
- }
1
+ import { TableDef } from '../schema/table.js';
2
+ import { ColumnNode, ExpressionNode } from '../core/ast/expression.js';
3
+ import { TableNode, DeleteQueryNode } from '../core/ast/query.js';
4
+ import { createTableNode } from '../core/ast/builders.js';
5
+
6
+ /**
7
+ * Maintains immutable state for DELETE queries
8
+ */
9
+ export class DeleteQueryState {
10
+ public readonly table: TableDef;
11
+ public readonly ast: DeleteQueryNode;
12
+
13
+ constructor(table: TableDef, ast?: DeleteQueryNode) {
14
+ this.table = table;
15
+ this.ast = ast ?? {
16
+ type: 'DeleteQuery',
17
+ from: createTableNode(table)
18
+ };
19
+ }
20
+
21
+ private clone(nextAst: DeleteQueryNode): DeleteQueryState {
22
+ return new DeleteQueryState(this.table, nextAst);
23
+ }
24
+
25
+ withWhere(expr: ExpressionNode): DeleteQueryState {
26
+ return this.clone({
27
+ ...this.ast,
28
+ where: expr
29
+ });
30
+ }
31
+
32
+ withReturning(columns: ColumnNode[]): DeleteQueryState {
33
+ return this.clone({
34
+ ...this.ast,
35
+ returning: [...columns]
36
+ });
37
+ }
38
+ }
@@ -1,57 +1,46 @@
1
- import { TableDef } from '../schema/table';
2
- import { ColumnDef } from '../schema/column';
3
- import { ColumnNode, ExpressionNode } from '../ast/expression';
4
- import { CompiledQuery, DeleteCompiler } from '../dialect/abstract';
5
- import { DeleteQueryNode } from '../ast/query';
6
- import { DeleteQueryState } from './delete-query-state';
7
-
8
- const buildColumnNode = (table: TableDef, column: ColumnDef | ColumnNode): ColumnNode => {
9
- if ((column as ColumnNode).type === 'Column') {
10
- return column as ColumnNode;
11
- }
12
- const def = column as ColumnDef;
13
- return {
14
- type: 'Column',
15
- table: def.table || table.name,
16
- name: def.name
17
- };
18
- };
19
-
20
- /**
21
- * Builder for DELETE queries
22
- */
23
- export class DeleteQueryBuilder<T> {
24
- private readonly table: TableDef;
25
- private readonly state: DeleteQueryState;
26
-
27
- constructor(table: TableDef, state?: DeleteQueryState) {
28
- this.table = table;
29
- this.state = state ?? new DeleteQueryState(table);
30
- }
31
-
32
- private clone(state: DeleteQueryState): DeleteQueryBuilder<T> {
33
- return new DeleteQueryBuilder(this.table, state);
34
- }
35
-
36
- where(expr: ExpressionNode): DeleteQueryBuilder<T> {
37
- return this.clone(this.state.withWhere(expr));
38
- }
39
-
40
- returning(...columns: (ColumnDef | ColumnNode)[]): DeleteQueryBuilder<T> {
41
- if (!columns.length) return this;
42
- const nodes = columns.map(column => buildColumnNode(this.table, column));
43
- return this.clone(this.state.withReturning(nodes));
44
- }
45
-
46
- compile(compiler: DeleteCompiler): CompiledQuery {
47
- return compiler.compileDelete(this.state.ast);
48
- }
49
-
50
- toSql(compiler: DeleteCompiler): string {
51
- return this.compile(compiler).sql;
52
- }
53
-
54
- getAST(): DeleteQueryNode {
55
- return this.state.ast;
56
- }
57
- }
1
+ import { TableDef } from '../schema/table.js';
2
+ import { ColumnDef } from '../schema/column.js';
3
+ import { ColumnNode, ExpressionNode } from '../core/ast/expression.js';
4
+ import { CompiledQuery, DeleteCompiler } from '../core/dialect/abstract.js';
5
+ import { DeleteQueryNode } from '../core/ast/query.js';
6
+ import { DeleteQueryState } from './delete-query-state.js';
7
+ import { buildColumnNode } from '../core/ast/builders.js';
8
+
9
+ /**
10
+ * Builder for DELETE queries
11
+ */
12
+ export class DeleteQueryBuilder<T> {
13
+ private readonly table: TableDef;
14
+ private readonly state: DeleteQueryState;
15
+
16
+ constructor(table: TableDef, state?: DeleteQueryState) {
17
+ this.table = table;
18
+ this.state = state ?? new DeleteQueryState(table);
19
+ }
20
+
21
+ private clone(state: DeleteQueryState): DeleteQueryBuilder<T> {
22
+ return new DeleteQueryBuilder(this.table, state);
23
+ }
24
+
25
+ where(expr: ExpressionNode): DeleteQueryBuilder<T> {
26
+ return this.clone(this.state.withWhere(expr));
27
+ }
28
+
29
+ returning(...columns: (ColumnDef | ColumnNode)[]): DeleteQueryBuilder<T> {
30
+ if (!columns.length) return this;
31
+ const nodes = columns.map(column => buildColumnNode(this.table, column));
32
+ return this.clone(this.state.withReturning(nodes));
33
+ }
34
+
35
+ compile(compiler: DeleteCompiler): CompiledQuery {
36
+ return compiler.compileDelete(this.state.ast);
37
+ }
38
+
39
+ toSql(compiler: DeleteCompiler): string {
40
+ return this.compile(compiler).sql;
41
+ }
42
+
43
+ getAST(): DeleteQueryNode {
44
+ return this.state.ast;
45
+ }
46
+ }