forge-sql-orm 2.0.12 → 2.0.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -24
- package/dist/ForgeSQLORM.js +51 -19
- package/dist/ForgeSQLORM.js.map +1 -1
- package/dist/ForgeSQLORM.mjs +51 -19
- package/dist/ForgeSQLORM.mjs.map +1 -1
- package/dist/core/ForgeSQLCrudOperations.d.ts.map +1 -1
- package/dist/core/ForgeSQLORM.d.ts.map +1 -1
- package/dist/lib/drizzle/extensions/selectAliased.d.ts.map +1 -1
- package/dist/utils/sqlUtils.d.ts +1 -1
- package/dist/utils/sqlUtils.d.ts.map +1 -1
- package/package.json +8 -18
- package/src/core/ForgeSQLCrudOperations.ts +1 -1
- package/src/core/ForgeSQLORM.ts +5 -4
- package/src/lib/drizzle/extensions/selectAliased.ts +56 -58
- package/src/lib/drizzle/extensions/types.d.ts +13 -13
- package/src/utils/sqlUtils.ts +96 -47
- package/dist-cli/cli.js +0 -965
- package/dist-cli/cli.js.map +0 -1
- package/dist-cli/cli.mjs +0 -964
- package/dist-cli/cli.mjs.map +0 -1
- package/dist-cli/forgeSqlOrmCLI.js +0 -19
|
@@ -1,74 +1,72 @@
|
|
|
1
|
-
import {MySqlRemoteDatabase} from "drizzle-orm/mysql-proxy";
|
|
2
|
-
import type {SelectedFields} from "drizzle-orm/mysql-core/query-builders/select.types";
|
|
3
|
-
import {applyFromDriverTransform, mapSelectFieldsWithAlias} from "../../..";
|
|
4
|
-
import {MySqlSelectBuilder} from "drizzle-orm/mysql-core";
|
|
5
|
-
import {MySqlRemotePreparedQueryHKT} from "drizzle-orm/mysql-proxy";
|
|
1
|
+
import { MySqlRemoteDatabase } from "drizzle-orm/mysql-proxy";
|
|
2
|
+
import type { SelectedFields } from "drizzle-orm/mysql-core/query-builders/select.types";
|
|
3
|
+
import { applyFromDriverTransform, mapSelectFieldsWithAlias } from "../../..";
|
|
4
|
+
import { MySqlSelectBuilder } from "drizzle-orm/mysql-core";
|
|
5
|
+
import { MySqlRemotePreparedQueryHKT } from "drizzle-orm/mysql-proxy";
|
|
6
6
|
|
|
7
7
|
function createAliasedSelectBuilder<TSelection extends SelectedFields>(
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
db: MySqlRemoteDatabase<any>,
|
|
9
|
+
fields: TSelection,
|
|
10
|
+
selectFn: (selections: any) => MySqlSelectBuilder<TSelection, MySqlRemotePreparedQueryHKT>,
|
|
11
11
|
): MySqlSelectBuilder<TSelection, MySqlRemotePreparedQueryHKT> {
|
|
12
|
-
|
|
13
|
-
|
|
12
|
+
const { selections, aliasMap } = mapSelectFieldsWithAlias(fields);
|
|
13
|
+
const builder = selectFn(selections);
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
15
|
+
const wrapBuilder = (rawBuilder: any): any => {
|
|
16
|
+
return new Proxy(rawBuilder, {
|
|
17
|
+
get(target, prop, receiver) {
|
|
18
|
+
if (prop === "execute") {
|
|
19
|
+
return async (...args: any[]) => {
|
|
20
|
+
const rows = await target.execute(...args);
|
|
21
|
+
return applyFromDriverTransform(rows, selections, aliasMap);
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
24
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
onrejected,
|
|
33
|
-
);
|
|
34
|
-
}
|
|
25
|
+
if (prop === "then") {
|
|
26
|
+
return (onfulfilled: any, onrejected: any) =>
|
|
27
|
+
target.execute().then((rows: unknown[]) => {
|
|
28
|
+
const transformed = applyFromDriverTransform(rows, selections, aliasMap);
|
|
29
|
+
return onfulfilled?.(transformed);
|
|
30
|
+
}, onrejected);
|
|
31
|
+
}
|
|
35
32
|
|
|
36
|
-
|
|
33
|
+
const value = Reflect.get(target, prop, receiver);
|
|
37
34
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
35
|
+
if (typeof value === "function") {
|
|
36
|
+
return (...args: any[]) => {
|
|
37
|
+
const result = value.apply(target, args);
|
|
41
38
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
39
|
+
if (typeof result === "object" && result !== null && "execute" in result) {
|
|
40
|
+
return wrapBuilder(result);
|
|
41
|
+
}
|
|
45
42
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
43
|
+
return result;
|
|
44
|
+
};
|
|
45
|
+
}
|
|
49
46
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
47
|
+
return value;
|
|
48
|
+
},
|
|
49
|
+
});
|
|
50
|
+
};
|
|
54
51
|
|
|
55
|
-
|
|
52
|
+
return wrapBuilder(builder);
|
|
56
53
|
}
|
|
57
54
|
|
|
58
55
|
export function patchDbWithSelectAliased(db: MySqlRemoteDatabase<any>): MySqlRemoteDatabase<any> & {
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
56
|
+
selectAliased: <TSelection extends SelectedFields>(
|
|
57
|
+
fields: TSelection,
|
|
58
|
+
) => MySqlSelectBuilder<TSelection, MySqlRemotePreparedQueryHKT>;
|
|
59
|
+
selectAliasedDistinct: <TSelection extends SelectedFields>(
|
|
60
|
+
fields: TSelection,
|
|
61
|
+
) => MySqlSelectBuilder<TSelection, MySqlRemotePreparedQueryHKT>;
|
|
62
|
+
} {
|
|
63
|
+
db.selectAliased = function <TSelection extends SelectedFields>(fields: TSelection) {
|
|
64
|
+
return createAliasedSelectBuilder(db, fields, (selections) => db.select(selections));
|
|
65
|
+
};
|
|
68
66
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
67
|
+
db.selectAliasedDistinct = function <TSelection extends SelectedFields>(fields: TSelection) {
|
|
68
|
+
return createAliasedSelectBuilder(db, fields, (selections) => db.selectDistinct(selections));
|
|
69
|
+
};
|
|
72
70
|
|
|
73
|
-
|
|
74
|
-
}
|
|
71
|
+
return db;
|
|
72
|
+
}
|
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
import { SelectedFields } from
|
|
2
|
-
import {MySqlSelectBuilder} from "drizzle-orm/mysql-core";
|
|
3
|
-
import {MySqlRemotePreparedQueryHKT} from "drizzle-orm/mysql-proxy";
|
|
1
|
+
import { SelectedFields } from "drizzle-orm";
|
|
2
|
+
import { MySqlSelectBuilder } from "drizzle-orm/mysql-core";
|
|
3
|
+
import { MySqlRemotePreparedQueryHKT } from "drizzle-orm/mysql-proxy";
|
|
4
4
|
|
|
5
|
-
declare module
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
}
|
|
5
|
+
declare module "drizzle-orm/mysql-proxy" {
|
|
6
|
+
interface MySqlRemoteDatabase<> {
|
|
7
|
+
selectAliased<TSelection extends SelectedFields>(
|
|
8
|
+
fields: TSelection,
|
|
9
|
+
): MySqlSelectBuilder<TSelection, MySqlRemotePreparedQueryHKT>;
|
|
10
|
+
selectAliasedDistinct<TSelection extends SelectedFields>(
|
|
11
|
+
fields: TSelection,
|
|
12
|
+
): MySqlSelectBuilder<TSelection, MySqlRemotePreparedQueryHKT>;
|
|
13
|
+
}
|
|
14
|
+
}
|
package/src/utils/sqlUtils.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import moment from "moment";
|
|
2
|
-
import {AnyColumn, Column, isTable, SQL, sql, StringChunk} from "drizzle-orm";
|
|
3
|
-
import {AnyMySqlTable, MySqlCustomColumn} from "drizzle-orm/mysql-core/index";
|
|
2
|
+
import { AnyColumn, Column, isTable, SQL, sql, StringChunk } from "drizzle-orm";
|
|
3
|
+
import { AnyMySqlTable, MySqlCustomColumn } from "drizzle-orm/mysql-core/index";
|
|
4
4
|
import { PrimaryKeyBuilder } from "drizzle-orm/mysql-core/primary-keys";
|
|
5
5
|
import { AnyIndexBuilder } from "drizzle-orm/mysql-core/indexes";
|
|
6
6
|
import { CheckBuilder } from "drizzle-orm/mysql-core/checks";
|
|
@@ -8,8 +8,7 @@ import { ForeignKeyBuilder } from "drizzle-orm/mysql-core/foreign-keys";
|
|
|
8
8
|
import { UniqueConstraintBuilder } from "drizzle-orm/mysql-core/unique-constraint";
|
|
9
9
|
import type { SelectedFields } from "drizzle-orm/mysql-core/query-builders/select.types";
|
|
10
10
|
import { MySqlTable } from "drizzle-orm/mysql-core";
|
|
11
|
-
import {
|
|
12
|
-
import {isSQLWrapper} from "drizzle-orm/sql/sql";
|
|
11
|
+
import { isSQLWrapper } from "drizzle-orm/sql/sql";
|
|
13
12
|
|
|
14
13
|
/**
|
|
15
14
|
* Interface representing table metadata information
|
|
@@ -48,11 +47,11 @@ interface ConfigBuilderData {
|
|
|
48
47
|
* @returns Date object
|
|
49
48
|
*/
|
|
50
49
|
export const parseDateTime = (value: string, format: string): Date => {
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
50
|
+
const m = moment(value, format, true);
|
|
51
|
+
if (!m.isValid()) {
|
|
52
|
+
return moment(value).toDate();
|
|
53
|
+
}
|
|
54
|
+
return m.toDate();
|
|
56
55
|
};
|
|
57
56
|
|
|
58
57
|
/**
|
|
@@ -61,8 +60,8 @@ export const parseDateTime = (value: string, format: string): Date => {
|
|
|
61
60
|
* @returns The extracted alias or the original query if no alias found
|
|
62
61
|
*/
|
|
63
62
|
export function extractAlias(query: string): string {
|
|
64
|
-
|
|
65
|
-
|
|
63
|
+
const match = query.match(/\bas\s+(['"`]?)([\w*]+)\1$/i);
|
|
64
|
+
return match ? match[2] : query;
|
|
66
65
|
}
|
|
67
66
|
|
|
68
67
|
/**
|
|
@@ -189,8 +188,8 @@ export function getTableMetadata(table: AnyMySqlTable): MetadataInfo {
|
|
|
189
188
|
builders.foreignKeys = processForeignKeys(table, foreignKeysSymbol, extraSymbol);
|
|
190
189
|
|
|
191
190
|
// Process extra configuration if available
|
|
192
|
-
|
|
193
|
-
|
|
191
|
+
if (extraSymbol) {
|
|
192
|
+
// @ts-ignore
|
|
194
193
|
const extraConfigBuilder = table[extraSymbol];
|
|
195
194
|
if (extraConfigBuilder && typeof extraConfigBuilder === "function") {
|
|
196
195
|
const configBuilderData = extraConfigBuilder(table);
|
|
@@ -231,7 +230,7 @@ export function getTableMetadata(table: AnyMySqlTable): MetadataInfo {
|
|
|
231
230
|
}
|
|
232
231
|
}
|
|
233
232
|
|
|
234
|
-
|
|
233
|
+
return {
|
|
235
234
|
tableName: nameSymbol ? (table as any)[nameSymbol] : "",
|
|
236
235
|
columns: columnsSymbol ? ((table as any)[columnsSymbol] as Record<string, AnyColumn>) : {},
|
|
237
236
|
...builders,
|
|
@@ -261,15 +260,15 @@ export function generateDropTableStatements(tables: AnyMySqlTable[]): string[] {
|
|
|
261
260
|
|
|
262
261
|
type AliasColumnMap = Record<string, AnyColumn>;
|
|
263
262
|
|
|
264
|
-
function mapSelectTableToAlias(table: MySqlTable, aliasMap: AliasColumnMap): any {
|
|
263
|
+
function mapSelectTableToAlias(table: MySqlTable,uniqPrefix:string, aliasMap: AliasColumnMap): any {
|
|
265
264
|
const { columns, tableName } = getTableMetadata(table);
|
|
266
265
|
const selectionsTableFields: Record<string, unknown> = {};
|
|
267
266
|
Object.keys(columns).forEach((name) => {
|
|
268
267
|
const column = columns[name] as AnyColumn;
|
|
269
|
-
const uniqName = `a_${tableName}_${column.name}
|
|
268
|
+
const uniqName = `a_${uniqPrefix}_${tableName}_${column.name}`.toLowerCase();
|
|
270
269
|
const fieldAlias = sql.raw(uniqName);
|
|
271
270
|
selectionsTableFields[name] = sql`${column} as \`${fieldAlias}\``;
|
|
272
|
-
aliasMap[uniqName]= column;
|
|
271
|
+
aliasMap[uniqName] = column;
|
|
273
272
|
});
|
|
274
273
|
return selectionsTableFields;
|
|
275
274
|
}
|
|
@@ -278,13 +277,19 @@ function isDrizzleColumn(column: any): boolean {
|
|
|
278
277
|
return column && typeof column === "object" && "table" in column;
|
|
279
278
|
}
|
|
280
279
|
|
|
281
|
-
export function mapSelectAllFieldsToAlias(
|
|
280
|
+
export function mapSelectAllFieldsToAlias(
|
|
281
|
+
selections: any,
|
|
282
|
+
name: string,
|
|
283
|
+
uniqName:string,
|
|
284
|
+
fields: any,
|
|
285
|
+
aliasMap: AliasColumnMap,
|
|
286
|
+
): any {
|
|
282
287
|
if (isTable(fields)) {
|
|
283
|
-
selections[name] = mapSelectTableToAlias(fields as MySqlTable, aliasMap);
|
|
288
|
+
selections[name] = mapSelectTableToAlias(fields as MySqlTable, uniqName, aliasMap);
|
|
284
289
|
} else if (isDrizzleColumn(fields)) {
|
|
285
290
|
const column = fields as Column;
|
|
286
|
-
const
|
|
287
|
-
let aliasName = sql.raw(
|
|
291
|
+
const uniqAliasName = `a_${uniqName}_${column.name}`.toLowerCase();
|
|
292
|
+
let aliasName = sql.raw(uniqAliasName);
|
|
288
293
|
selections[name] = sql`${column} as \`${aliasName}\``;
|
|
289
294
|
aliasMap[uniqName] = column;
|
|
290
295
|
} else if (isSQLWrapper(fields)) {
|
|
@@ -292,7 +297,7 @@ export function mapSelectAllFieldsToAlias(selections: any, name: string, fields:
|
|
|
292
297
|
} else {
|
|
293
298
|
const innerSelections: any = {};
|
|
294
299
|
Object.entries(fields).forEach(([iname, ifields]) => {
|
|
295
|
-
mapSelectAllFieldsToAlias(innerSelections, iname, ifields, aliasMap);
|
|
300
|
+
mapSelectAllFieldsToAlias(innerSelections, iname, `${uniqName}_${iname}`, ifields, aliasMap);
|
|
296
301
|
});
|
|
297
302
|
selections[name] = innerSelections;
|
|
298
303
|
}
|
|
@@ -307,40 +312,41 @@ export function mapSelectFieldsWithAlias<TSelection extends SelectedFields>(
|
|
|
307
312
|
const aliasMap: AliasColumnMap = {};
|
|
308
313
|
const selections: any = {};
|
|
309
314
|
Object.entries(fields).forEach(([name, fields]) => {
|
|
310
|
-
mapSelectAllFieldsToAlias(selections, name, fields, aliasMap);
|
|
315
|
+
mapSelectAllFieldsToAlias(selections, name,name, fields, aliasMap);
|
|
311
316
|
});
|
|
312
|
-
return {selections, aliasMap};
|
|
317
|
+
return { selections, aliasMap };
|
|
313
318
|
}
|
|
314
319
|
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
isSQLWrapper(value) && 'queryChunks' in value;
|
|
321
|
-
if (isSQL){
|
|
322
|
-
const sql = value as SQL;
|
|
320
|
+
function getAliasFromDrizzleAlias(value: unknown): string | undefined {
|
|
321
|
+
const isSQL =
|
|
322
|
+
value !== null && typeof value === "object" && isSQLWrapper(value) && "queryChunks" in value;
|
|
323
|
+
if (isSQL) {
|
|
324
|
+
const sql = value as SQL;
|
|
323
325
|
const queryChunks = sql.queryChunks;
|
|
324
|
-
if (queryChunks.length>3){
|
|
325
|
-
const aliasNameChunk = queryChunks[queryChunks.length-2];
|
|
326
|
-
if (isSQLWrapper(aliasNameChunk) &&
|
|
327
|
-
const
|
|
326
|
+
if (queryChunks.length > 3) {
|
|
327
|
+
const aliasNameChunk = queryChunks[queryChunks.length - 2];
|
|
328
|
+
if (isSQLWrapper(aliasNameChunk) && "queryChunks" in aliasNameChunk) {
|
|
329
|
+
const aliasNameChunkSql = aliasNameChunk as SQL;
|
|
328
330
|
if (aliasNameChunkSql && aliasNameChunkSql.queryChunks.length === 1) {
|
|
329
331
|
const queryChunksStringChunc = aliasNameChunkSql.queryChunks[0];
|
|
330
|
-
if (queryChunksStringChunc &&
|
|
332
|
+
if (queryChunksStringChunc && "value" in queryChunksStringChunc) {
|
|
331
333
|
const values = (queryChunksStringChunc as StringChunk).value;
|
|
332
|
-
if (values && values.length===1){
|
|
333
|
-
return values[0]
|
|
334
|
+
if (values && values.length === 1) {
|
|
335
|
+
return values[0];
|
|
334
336
|
}
|
|
335
337
|
}
|
|
336
338
|
}
|
|
337
339
|
}
|
|
338
340
|
}
|
|
339
341
|
}
|
|
340
|
-
return undefined
|
|
342
|
+
return undefined;
|
|
341
343
|
}
|
|
342
344
|
|
|
343
|
-
function transformValue(
|
|
345
|
+
function transformValue(
|
|
346
|
+
value: unknown,
|
|
347
|
+
alias: string,
|
|
348
|
+
aliasMap: Record<string, AnyColumn>,
|
|
349
|
+
): unknown {
|
|
344
350
|
const column = aliasMap[alias];
|
|
345
351
|
if (!column) return value;
|
|
346
352
|
|
|
@@ -353,16 +359,24 @@ function transformValue(value: unknown, alias: string, aliasMap: Record<string,
|
|
|
353
359
|
return value;
|
|
354
360
|
}
|
|
355
361
|
|
|
356
|
-
function transformObject(
|
|
362
|
+
function transformObject(
|
|
363
|
+
obj: Record<string, unknown>,
|
|
364
|
+
selections: Record<string, unknown>,
|
|
365
|
+
aliasMap: Record<string, AnyColumn>,
|
|
366
|
+
): Record<string, unknown> {
|
|
357
367
|
const result: Record<string, unknown> = {};
|
|
358
368
|
|
|
359
369
|
for (const [key, value] of Object.entries(obj)) {
|
|
360
370
|
const selection = selections[key];
|
|
361
371
|
const alias = getAliasFromDrizzleAlias(selection);
|
|
362
372
|
if (alias && aliasMap[alias]) {
|
|
363
|
-
|
|
364
|
-
} else if (selection && typeof selection ===
|
|
365
|
-
result[key] = transformObject(
|
|
373
|
+
result[key] = transformValue(value, alias, aliasMap);
|
|
374
|
+
} else if (selection && typeof selection === "object" && !isSQLWrapper(selection)) {
|
|
375
|
+
result[key] = transformObject(
|
|
376
|
+
value as Record<string, unknown>,
|
|
377
|
+
selection as Record<string, unknown>,
|
|
378
|
+
aliasMap,
|
|
379
|
+
);
|
|
366
380
|
} else {
|
|
367
381
|
result[key] = value;
|
|
368
382
|
}
|
|
@@ -374,9 +388,44 @@ function transformObject(obj: Record<string, unknown>, selections: Record<string
|
|
|
374
388
|
export function applyFromDriverTransform<T, TSelection>(
|
|
375
389
|
rows: T[],
|
|
376
390
|
selections: TSelection,
|
|
377
|
-
aliasMap: Record<string, AnyColumn
|
|
391
|
+
aliasMap: Record<string, AnyColumn>,
|
|
378
392
|
): T[] {
|
|
379
393
|
return rows.map((row) => {
|
|
380
|
-
|
|
394
|
+
const transformed = transformObject(
|
|
395
|
+
row as Record<string, unknown>,
|
|
396
|
+
selections as Record<string, unknown>,
|
|
397
|
+
aliasMap,
|
|
398
|
+
) as Record<string, unknown>;
|
|
399
|
+
|
|
400
|
+
return processNullBranches(transformed) as unknown as T;
|
|
381
401
|
});
|
|
382
402
|
}
|
|
403
|
+
|
|
404
|
+
function processNullBranches(obj: Record<string, unknown>): Record<string, unknown> | null {
|
|
405
|
+
if (obj === null || typeof obj !== 'object' || obj===undefined) {
|
|
406
|
+
return obj;
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
const result: Record<string, unknown> = {};
|
|
410
|
+
let allNull = true;
|
|
411
|
+
|
|
412
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
413
|
+
if (value === null || value===undefined) {
|
|
414
|
+
result[key] = null;
|
|
415
|
+
continue;
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
if (typeof value === 'object' && (value !== null || value !== undefined) ) {
|
|
419
|
+
const processed = processNullBranches(value as Record<string, unknown>);
|
|
420
|
+
result[key] = processed;
|
|
421
|
+
if (processed !== null) {
|
|
422
|
+
allNull = false;
|
|
423
|
+
}
|
|
424
|
+
} else {
|
|
425
|
+
result[key] = value;
|
|
426
|
+
allNull = false;
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
return allNull ? null : result;
|
|
431
|
+
}
|