forge-sql-orm 2.0.9 → 2.0.11

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 (41) hide show
  1. package/README.md +67 -42
  2. package/dist/ForgeSQLORM.js +162 -16
  3. package/dist/ForgeSQLORM.js.map +1 -1
  4. package/dist/ForgeSQLORM.mjs +165 -19
  5. package/dist/ForgeSQLORM.mjs.map +1 -1
  6. package/dist/core/ForgeSQLCrudOperations.d.ts.map +1 -1
  7. package/dist/core/ForgeSQLORM.d.ts.map +1 -1
  8. package/dist/core/ForgeSQLQueryBuilder.d.ts +32 -0
  9. package/dist/core/ForgeSQLQueryBuilder.d.ts.map +1 -1
  10. package/dist/core/SystemTables.d.ts +8 -6
  11. package/dist/core/SystemTables.d.ts.map +1 -1
  12. package/dist/index.d.ts +1 -0
  13. package/dist/index.d.ts.map +1 -1
  14. package/dist/lib/drizzle/extensions/selectAliased.d.ts +9 -0
  15. package/dist/lib/drizzle/extensions/selectAliased.d.ts.map +1 -0
  16. package/dist/utils/sqlUtils.d.ts +8 -4
  17. package/dist/utils/sqlUtils.d.ts.map +1 -1
  18. package/dist/webtriggers/applyMigrationsWebTrigger.d.ts +23 -0
  19. package/dist/webtriggers/applyMigrationsWebTrigger.d.ts.map +1 -1
  20. package/dist/webtriggers/dropMigrationWebTrigger.d.ts +20 -4
  21. package/dist/webtriggers/dropMigrationWebTrigger.d.ts.map +1 -1
  22. package/dist/webtriggers/fetchSchemaWebTrigger.d.ts +28 -0
  23. package/dist/webtriggers/fetchSchemaWebTrigger.d.ts.map +1 -0
  24. package/dist/webtriggers/index.d.ts +1 -0
  25. package/dist/webtriggers/index.d.ts.map +1 -1
  26. package/dist-cli/cli.js +5 -0
  27. package/dist-cli/cli.js.map +1 -1
  28. package/dist-cli/cli.mjs +8 -3
  29. package/dist-cli/cli.mjs.map +1 -1
  30. package/package.json +10 -10
  31. package/src/core/ForgeSQLCrudOperations.ts +0 -425
  32. package/src/core/ForgeSQLORM.ts +0 -228
  33. package/src/core/ForgeSQLQueryBuilder.ts +0 -319
  34. package/src/core/ForgeSQLSelectOperations.ts +0 -93
  35. package/src/core/SystemTables.ts +0 -7
  36. package/src/index.ts +0 -10
  37. package/src/utils/forgeDriver.ts +0 -39
  38. package/src/utils/sqlUtils.ts +0 -306
  39. package/src/webtriggers/applyMigrationsWebTrigger.ts +0 -26
  40. package/src/webtriggers/dropMigrationWebTrigger.ts +0 -35
  41. package/src/webtriggers/index.ts +0 -25
@@ -1,306 +0,0 @@
1
- import moment from "moment";
2
- import { AnyColumn, Column, isTable, sql } from "drizzle-orm";
3
- import { AnyMySqlTable } from "drizzle-orm/mysql-core/index";
4
- import { PrimaryKeyBuilder } from "drizzle-orm/mysql-core/primary-keys";
5
- import { AnyIndexBuilder } from "drizzle-orm/mysql-core/indexes";
6
- import { CheckBuilder } from "drizzle-orm/mysql-core/checks";
7
- import { ForeignKeyBuilder } from "drizzle-orm/mysql-core/foreign-keys";
8
- import { UniqueConstraintBuilder } from "drizzle-orm/mysql-core/unique-constraint";
9
- import type { SelectedFields } from "drizzle-orm/mysql-core/query-builders/select.types";
10
- import { MySqlTable } from "drizzle-orm/mysql-core";
11
- import { getTableName } from "drizzle-orm/table";
12
- import { isSQLWrapper } from "drizzle-orm/sql/sql";
13
-
14
- /**
15
- * Interface representing table metadata information
16
- */
17
- export interface MetadataInfo {
18
- /** The name of the table */
19
- tableName: string;
20
- /** Record of column names and their corresponding column definitions */
21
- columns: Record<string, AnyColumn>;
22
- /** Array of index builders */
23
- indexes: AnyIndexBuilder[];
24
- /** Array of check constraint builders */
25
- checks: CheckBuilder[];
26
- /** Array of foreign key builders */
27
- foreignKeys: ForeignKeyBuilder[];
28
- /** Array of primary key builders */
29
- primaryKeys: PrimaryKeyBuilder[];
30
- /** Array of unique constraint builders */
31
- uniqueConstraints: UniqueConstraintBuilder[];
32
- /** Array of all extra builders */
33
- extras: any[];
34
- }
35
-
36
- /**
37
- * Interface for config builder data
38
- */
39
- interface ConfigBuilderData {
40
- value?: any;
41
- [key: string]: any;
42
- }
43
-
44
- /**
45
- * Parses a date string into a Date object using the specified format
46
- * @param value - The date string to parse
47
- * @param format - The format to use for parsing
48
- * @returns Date object
49
- */
50
- export const parseDateTime = (value: string, format: string): Date => {
51
- const m = moment(value, format, true);
52
- if (!m.isValid()) {
53
- return moment(value).toDate();
54
- }
55
- return m.toDate();
56
- };
57
-
58
- /**
59
- * Extracts the alias from a SQL query
60
- * @param query - The SQL query to extract the alias from
61
- * @returns The extracted alias or the original query if no alias found
62
- */
63
- export function extractAlias(query: string): string {
64
- const match = query.match(/\bas\s+(['"`]?)([\w*]+)\1$/i);
65
- return match ? match[2] : query;
66
- }
67
-
68
- /**
69
- * Gets primary keys from the schema.
70
- * @template T - The type of the table schema
71
- * @param {T} table - The table schema
72
- * @returns {[string, AnyColumn][]} Array of primary key name and column pairs
73
- */
74
- export function getPrimaryKeys<T extends AnyMySqlTable>(table: T): [string, AnyColumn][] {
75
- const { columns, primaryKeys } = getTableMetadata(table);
76
-
77
- // First try to find primary keys in columns
78
- const columnPrimaryKeys = Object.entries(columns).filter(([, column]) => column.primary) as [
79
- string,
80
- AnyColumn,
81
- ][];
82
-
83
- if (columnPrimaryKeys.length > 0) {
84
- return columnPrimaryKeys;
85
- }
86
-
87
- // If no primary keys found in columns, check primary key builders
88
- if (Array.isArray(primaryKeys) && primaryKeys.length > 0) {
89
- // Collect all primary key columns from all primary key builders
90
- const primaryKeyColumns = new Set<[string, AnyColumn]>();
91
-
92
- primaryKeys.forEach((primaryKeyBuilder) => {
93
- // Get primary key columns from each builder
94
- Object.entries(columns)
95
- .filter(([, column]) => {
96
- // @ts-ignore - PrimaryKeyBuilder has internal columns property
97
- return primaryKeyBuilder.columns.includes(column);
98
- })
99
- .forEach(([name, column]) => {
100
- primaryKeyColumns.add([name, column]);
101
- });
102
- });
103
-
104
- return Array.from(primaryKeyColumns);
105
- }
106
-
107
- return [];
108
- }
109
-
110
- /**
111
- * Processes foreign keys from both foreignKeysSymbol and extraSymbol
112
- * @param table - The table schema
113
- * @param foreignKeysSymbol - Symbol for foreign keys
114
- * @param extraSymbol - Symbol for extra configuration
115
- * @returns Array of foreign key builders
116
- */
117
- function processForeignKeys(
118
- table: AnyMySqlTable,
119
- foreignKeysSymbol: symbol | undefined,
120
- extraSymbol: symbol | undefined,
121
- ): ForeignKeyBuilder[] {
122
- const foreignKeys: ForeignKeyBuilder[] = [];
123
-
124
- // Process foreign keys from foreignKeysSymbol
125
- if (foreignKeysSymbol) {
126
- // @ts-ignore
127
- const fkArray: any[] = table[foreignKeysSymbol];
128
- if (fkArray) {
129
- fkArray.forEach((fk) => {
130
- if (fk.reference) {
131
- const item = fk.reference(fk);
132
- foreignKeys.push(item);
133
- }
134
- });
135
- }
136
- }
137
-
138
- // Process foreign keys from extraSymbol
139
- if (extraSymbol) {
140
- // @ts-ignore
141
- const extraConfigBuilder = table[extraSymbol];
142
- if (extraConfigBuilder && typeof extraConfigBuilder === "function") {
143
- const configBuilderData = extraConfigBuilder(table);
144
- if (configBuilderData) {
145
- const configBuilders = Array.isArray(configBuilderData)
146
- ? configBuilderData
147
- : Object.values(configBuilderData).map(
148
- (item) => (item as ConfigBuilderData).value || item,
149
- );
150
-
151
- configBuilders.forEach((builder) => {
152
- if (!builder?.constructor) return;
153
-
154
- const builderName = builder.constructor.name.toLowerCase();
155
- if (builderName.includes("foreignkeybuilder")) {
156
- foreignKeys.push(builder);
157
- }
158
- });
159
- }
160
- }
161
- }
162
-
163
- return foreignKeys;
164
- }
165
-
166
- /**
167
- * Extracts table metadata from the schema.
168
- * @param {AnyMySqlTable} table - The table schema
169
- * @returns {MetadataInfo} Object containing table metadata
170
- */
171
- export function getTableMetadata(table: AnyMySqlTable): MetadataInfo {
172
- const symbols = Object.getOwnPropertySymbols(table);
173
- const nameSymbol = symbols.find((s) => s.toString().includes("Name"));
174
- const columnsSymbol = symbols.find((s) => s.toString().includes("Columns"));
175
- const foreignKeysSymbol = symbols.find((s) => s.toString().includes("ForeignKeys)"));
176
- const extraSymbol = symbols.find((s) => s.toString().includes("ExtraConfigBuilder"));
177
-
178
- // Initialize builders arrays
179
- const builders = {
180
- indexes: [] as AnyIndexBuilder[],
181
- checks: [] as CheckBuilder[],
182
- foreignKeys: [] as ForeignKeyBuilder[],
183
- primaryKeys: [] as PrimaryKeyBuilder[],
184
- uniqueConstraints: [] as UniqueConstraintBuilder[],
185
- extras: [] as any[],
186
- };
187
-
188
- // Process foreign keys
189
- builders.foreignKeys = processForeignKeys(table, foreignKeysSymbol, extraSymbol);
190
-
191
- // Process extra configuration if available
192
- if (extraSymbol) {
193
- // @ts-ignore
194
- const extraConfigBuilder = table[extraSymbol];
195
- if (extraConfigBuilder && typeof extraConfigBuilder === "function") {
196
- const configBuilderData = extraConfigBuilder(table);
197
- if (configBuilderData) {
198
- // Convert configBuilderData to array if it's an object
199
- const configBuilders = Array.isArray(configBuilderData)
200
- ? configBuilderData
201
- : Object.values(configBuilderData).map(
202
- (item) => (item as ConfigBuilderData).value || item,
203
- );
204
-
205
- // Process each builder
206
- configBuilders.forEach((builder) => {
207
- if (!builder?.constructor) return;
208
-
209
- const builderName = builder.constructor.name.toLowerCase();
210
-
211
- // Map builder types to their corresponding arrays
212
- const builderMap = {
213
- indexbuilder: builders.indexes,
214
- checkbuilder: builders.checks,
215
- primarykeybuilder: builders.primaryKeys,
216
- uniqueconstraintbuilder: builders.uniqueConstraints,
217
- };
218
-
219
- // Add builder to appropriate array if it matches any type
220
- for (const [type, array] of Object.entries(builderMap)) {
221
- if (builderName.includes(type)) {
222
- array.push(builder);
223
- break;
224
- }
225
- }
226
-
227
- // Always add to extras array
228
- builders.extras.push(builder);
229
- });
230
- }
231
- }
232
- }
233
-
234
- return {
235
- tableName: nameSymbol ? (table as any)[nameSymbol] : "",
236
- columns: columnsSymbol ? ((table as any)[columnsSymbol] as Record<string, AnyColumn>) : {},
237
- ...builders,
238
- };
239
- }
240
-
241
- /**
242
- * Generates SQL statements to drop tables
243
- * @param tables - Array of table schemas
244
- * @returns Array of SQL statements for dropping tables
245
- */
246
- export function generateDropTableStatements(tables: AnyMySqlTable[]): string[] {
247
- const dropStatements: string[] = [];
248
-
249
- tables.forEach((table) => {
250
- const tableMetadata = getTableMetadata(table);
251
- if (tableMetadata.tableName) {
252
- dropStatements.push(`DROP TABLE IF EXISTS \`${tableMetadata.tableName}\`;`);
253
- }
254
- });
255
-
256
- // Add statement to clear migrations table
257
- dropStatements.push(`DELETE FROM __migrations;`);
258
-
259
- return dropStatements;
260
- }
261
-
262
- export function mapSelectTableToAlias(table: MySqlTable): any {
263
- const { columns, tableName } = getTableMetadata(table);
264
- const selectionsTableFields: Record<string, unknown> = {};
265
- Object.keys(columns).forEach((name) => {
266
- const column = columns[name] as AnyColumn;
267
- const fieldAlias = sql.raw(`${tableName}_${column.name}`);
268
- selectionsTableFields[name] = sql`${column} as \`${fieldAlias}\``;
269
- });
270
- return selectionsTableFields;
271
- }
272
-
273
- function isDrizzleColumn(column: any): boolean {
274
- return column && typeof column === "object" && "table" in column;
275
- }
276
-
277
- export function mapSelectAllFieldsToAlias(selections: any, name: string, fields: any): any {
278
- if (isTable(fields)) {
279
- selections[name] = mapSelectTableToAlias(fields as MySqlTable);
280
- } else if (isDrizzleColumn(fields)) {
281
- const column = fields as Column;
282
- let aliasName = sql.raw(`${getTableName(column.table)}_${column.name}`);
283
- selections[name] = sql`${column} as \`${aliasName}\``;
284
- } else if (isSQLWrapper(fields)) {
285
- selections[name] = fields;
286
- } else {
287
- const innerSelections: any = {};
288
- Object.entries(fields).forEach(([iname, ifields]) => {
289
- mapSelectAllFieldsToAlias(innerSelections, iname, ifields);
290
- });
291
- selections[name] = innerSelections;
292
- }
293
- return selections;
294
- }
295
- export function mapSelectFieldsWithAlias<TSelection extends SelectedFields>(
296
- fields: TSelection,
297
- ): TSelection {
298
- if (!fields) {
299
- throw new Error("fields is empty");
300
- }
301
- const selections: any = {};
302
- Object.entries(fields).forEach(([name, fields]) => {
303
- mapSelectAllFieldsToAlias(selections, name, fields);
304
- });
305
- return selections;
306
- }
@@ -1,26 +0,0 @@
1
- import { migrationRunner, sql } from "@forge/sql";
2
- import { MigrationRunner } from "@forge/sql/out/migration";
3
-
4
- export const applySchemaMigrations = async (
5
- migration: (migrationRunner: MigrationRunner) => Promise<MigrationRunner>,
6
- ) => {
7
- console.log("Provisioning the database");
8
- await sql._provision();
9
- console.info("Running schema migrations");
10
- const migrations = await migration(migrationRunner);
11
- const successfulMigrations = await migrations.run();
12
- console.info("Migrations applied:", successfulMigrations);
13
-
14
- const migrationHistory = (await migrationRunner.list())
15
- .map((y) => `${y.id}, ${y.name}, ${y.migratedAt.toUTCString()}`)
16
- .join("\n");
17
-
18
- console.info("Migrations history:\nid, name, migrated_at\n", migrationHistory);
19
-
20
- return {
21
- headers: { "Content-Type": ["application/json"] },
22
- statusCode: 200,
23
- statusText: "OK",
24
- body: "Migrations successfully executed",
25
- };
26
- };
@@ -1,35 +0,0 @@
1
- import { sql } from "@forge/sql";
2
- import { AnyMySqlTable } from "drizzle-orm/mysql-core";
3
- import { generateDropTableStatements as generateStatements } from "../utils/sqlUtils";
4
- import { getHttpResponse, TriggerResponse } from "./index";
5
-
6
- /**
7
- * ⚠️ WARNING: This web trigger will permanently delete all data in the specified tables.
8
- * DO NOT use this in production environment as it will cause irreversible data loss.
9
- *
10
- * Generates SQL statements to drop tables and executes them
11
- * @param tables - Array of table schemas to drop
12
- * @returns Trigger response with execution status and list of dropped tables
13
- */
14
- export async function dropSchemaMigrations(
15
- tables: AnyMySqlTable[],
16
- ): Promise<TriggerResponse<string>> {
17
- try {
18
- // Generate drop statements
19
- const dropStatements = generateStatements(tables);
20
-
21
- // Execute each statement
22
- for (const statement of dropStatements) {
23
- console.warn(statement);
24
- await sql.executeDDL(statement);
25
- }
26
-
27
- return getHttpResponse<string>(
28
- 200,
29
- "⚠️ All data in these tables has been permanently deleted. This operation cannot be undone.",
30
- );
31
- } catch (error: unknown) {
32
- const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
33
- return getHttpResponse<string>(500, errorMessage);
34
- }
35
- }
@@ -1,25 +0,0 @@
1
- export * from "./dropMigrationWebTrigger";
2
- export * from "./applyMigrationsWebTrigger";
3
-
4
- export interface TriggerResponse<BODY> {
5
- body?: BODY;
6
- headers?: Record<string, string[]>;
7
- statusCode: number;
8
- statusText?: string;
9
- }
10
-
11
- export const getHttpResponse = <Body>(statusCode: number, body: Body): TriggerResponse<Body> => {
12
- let statusText = "";
13
- if (statusCode === 200) {
14
- statusText = "Ok";
15
- } else {
16
- statusText = "Bad Request";
17
- }
18
-
19
- return {
20
- headers: { "Content-Type": ["application/json"] },
21
- statusCode,
22
- statusText,
23
- body,
24
- };
25
- };