@uql/core 3.1.0 → 3.1.2

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 (170) hide show
  1. package/CHANGELOG.md +134 -176
  2. package/README.md +413 -0
  3. package/package.json +31 -26
  4. package/dist/package.json +0 -131
  5. package/src/@types/index.d.ts +0 -1
  6. package/src/@types/jest.d.ts +0 -6
  7. package/src/browser/http/bus.spec.ts +0 -22
  8. package/src/browser/http/bus.ts +0 -17
  9. package/src/browser/http/http.spec.ts +0 -70
  10. package/src/browser/http/http.ts +0 -55
  11. package/src/browser/http/index.ts +0 -2
  12. package/src/browser/index.ts +0 -4
  13. package/src/browser/options.spec.ts +0 -37
  14. package/src/browser/options.ts +0 -18
  15. package/src/browser/querier/genericClientRepository.spec.ts +0 -105
  16. package/src/browser/querier/genericClientRepository.ts +0 -49
  17. package/src/browser/querier/httpQuerier.ts +0 -82
  18. package/src/browser/querier/index.ts +0 -3
  19. package/src/browser/querier/querier.util.spec.ts +0 -35
  20. package/src/browser/querier/querier.util.ts +0 -18
  21. package/src/browser/type/clientQuerier.ts +0 -45
  22. package/src/browser/type/clientQuerierPool.ts +0 -5
  23. package/src/browser/type/clientRepository.ts +0 -22
  24. package/src/browser/type/index.ts +0 -4
  25. package/src/browser/type/request.ts +0 -25
  26. package/src/dialect/abstractDialect.ts +0 -28
  27. package/src/dialect/abstractSqlDialect-spec.ts +0 -1309
  28. package/src/dialect/abstractSqlDialect.ts +0 -805
  29. package/src/dialect/index.ts +0 -3
  30. package/src/dialect/namingStrategy.spec.ts +0 -52
  31. package/src/dialect/queryContext.ts +0 -69
  32. package/src/entity/decorator/definition.spec.ts +0 -736
  33. package/src/entity/decorator/definition.ts +0 -265
  34. package/src/entity/decorator/entity.ts +0 -8
  35. package/src/entity/decorator/field.ts +0 -9
  36. package/src/entity/decorator/id.ts +0 -9
  37. package/src/entity/decorator/index.ts +0 -5
  38. package/src/entity/decorator/relation.spec.ts +0 -41
  39. package/src/entity/decorator/relation.ts +0 -34
  40. package/src/entity/index.ts +0 -1
  41. package/src/express/@types/express.d.ts +0 -8
  42. package/src/express/@types/index.d.ts +0 -1
  43. package/src/express/index.ts +0 -2
  44. package/src/express/querierMiddleware.ts +0 -217
  45. package/src/express/query.util.spec.ts +0 -40
  46. package/src/express/query.util.ts +0 -21
  47. package/src/index.ts +0 -9
  48. package/src/maria/index.ts +0 -3
  49. package/src/maria/mariaDialect.spec.ts +0 -207
  50. package/src/maria/mariaDialect.ts +0 -42
  51. package/src/maria/mariaQuerierPool.test.ts +0 -23
  52. package/src/maria/mariadbQuerier.test.ts +0 -23
  53. package/src/maria/mariadbQuerier.ts +0 -45
  54. package/src/maria/mariadbQuerierPool.ts +0 -21
  55. package/src/migrate/cli.ts +0 -301
  56. package/src/migrate/generator/index.ts +0 -4
  57. package/src/migrate/generator/mongoSchemaGenerator.spec.ts +0 -112
  58. package/src/migrate/generator/mongoSchemaGenerator.ts +0 -115
  59. package/src/migrate/generator/mysqlSchemaGenerator.spec.ts +0 -34
  60. package/src/migrate/generator/mysqlSchemaGenerator.ts +0 -92
  61. package/src/migrate/generator/postgresSchemaGenerator.spec.ts +0 -44
  62. package/src/migrate/generator/postgresSchemaGenerator.ts +0 -127
  63. package/src/migrate/generator/sqliteSchemaGenerator.spec.ts +0 -33
  64. package/src/migrate/generator/sqliteSchemaGenerator.ts +0 -81
  65. package/src/migrate/index.ts +0 -41
  66. package/src/migrate/introspection/index.ts +0 -4
  67. package/src/migrate/introspection/mongoIntrospector.spec.ts +0 -75
  68. package/src/migrate/introspection/mongoIntrospector.ts +0 -47
  69. package/src/migrate/introspection/mysqlIntrospector.spec.ts +0 -113
  70. package/src/migrate/introspection/mysqlIntrospector.ts +0 -278
  71. package/src/migrate/introspection/postgresIntrospector.spec.ts +0 -112
  72. package/src/migrate/introspection/postgresIntrospector.ts +0 -329
  73. package/src/migrate/introspection/sqliteIntrospector.spec.ts +0 -112
  74. package/src/migrate/introspection/sqliteIntrospector.ts +0 -296
  75. package/src/migrate/migrator-mongo.test.ts +0 -54
  76. package/src/migrate/migrator.spec.ts +0 -255
  77. package/src/migrate/migrator.test.ts +0 -94
  78. package/src/migrate/migrator.ts +0 -719
  79. package/src/migrate/namingStrategy.spec.ts +0 -22
  80. package/src/migrate/schemaGenerator-advanced.spec.ts +0 -138
  81. package/src/migrate/schemaGenerator.spec.ts +0 -190
  82. package/src/migrate/schemaGenerator.ts +0 -478
  83. package/src/migrate/storage/databaseStorage.spec.ts +0 -69
  84. package/src/migrate/storage/databaseStorage.ts +0 -100
  85. package/src/migrate/storage/index.ts +0 -2
  86. package/src/migrate/storage/jsonStorage.ts +0 -58
  87. package/src/migrate/type.ts +0 -1
  88. package/src/mongo/index.ts +0 -3
  89. package/src/mongo/mongoDialect.spec.ts +0 -251
  90. package/src/mongo/mongoDialect.ts +0 -238
  91. package/src/mongo/mongodbQuerier.test.ts +0 -45
  92. package/src/mongo/mongodbQuerier.ts +0 -256
  93. package/src/mongo/mongodbQuerierPool.test.ts +0 -25
  94. package/src/mongo/mongodbQuerierPool.ts +0 -24
  95. package/src/mysql/index.ts +0 -3
  96. package/src/mysql/mysql2Querier.test.ts +0 -20
  97. package/src/mysql/mysql2Querier.ts +0 -49
  98. package/src/mysql/mysql2QuerierPool.test.ts +0 -20
  99. package/src/mysql/mysql2QuerierPool.ts +0 -21
  100. package/src/mysql/mysqlDialect.spec.ts +0 -20
  101. package/src/mysql/mysqlDialect.ts +0 -16
  102. package/src/namingStrategy/defaultNamingStrategy.ts +0 -18
  103. package/src/namingStrategy/index.spec.ts +0 -36
  104. package/src/namingStrategy/index.ts +0 -2
  105. package/src/namingStrategy/snakeCaseNamingStrategy.ts +0 -15
  106. package/src/options.spec.ts +0 -41
  107. package/src/options.ts +0 -18
  108. package/src/postgres/index.ts +0 -3
  109. package/src/postgres/manual-types.d.ts +0 -4
  110. package/src/postgres/pgQuerier.test.ts +0 -25
  111. package/src/postgres/pgQuerier.ts +0 -45
  112. package/src/postgres/pgQuerierPool.test.ts +0 -28
  113. package/src/postgres/pgQuerierPool.ts +0 -21
  114. package/src/postgres/postgresDialect.spec.ts +0 -428
  115. package/src/postgres/postgresDialect.ts +0 -144
  116. package/src/querier/abstractQuerier-test.ts +0 -584
  117. package/src/querier/abstractQuerier.ts +0 -353
  118. package/src/querier/abstractQuerierPool-test.ts +0 -20
  119. package/src/querier/abstractQuerierPool.ts +0 -18
  120. package/src/querier/abstractSqlQuerier-spec.ts +0 -979
  121. package/src/querier/abstractSqlQuerier-test.ts +0 -21
  122. package/src/querier/abstractSqlQuerier.ts +0 -138
  123. package/src/querier/decorator/index.ts +0 -3
  124. package/src/querier/decorator/injectQuerier.spec.ts +0 -74
  125. package/src/querier/decorator/injectQuerier.ts +0 -45
  126. package/src/querier/decorator/serialized.spec.ts +0 -98
  127. package/src/querier/decorator/serialized.ts +0 -13
  128. package/src/querier/decorator/transactional.spec.ts +0 -240
  129. package/src/querier/decorator/transactional.ts +0 -56
  130. package/src/querier/index.ts +0 -4
  131. package/src/repository/genericRepository.spec.ts +0 -111
  132. package/src/repository/genericRepository.ts +0 -74
  133. package/src/repository/index.ts +0 -1
  134. package/src/sqlite/index.ts +0 -3
  135. package/src/sqlite/manual-types.d.ts +0 -4
  136. package/src/sqlite/sqliteDialect.spec.ts +0 -155
  137. package/src/sqlite/sqliteDialect.ts +0 -76
  138. package/src/sqlite/sqliteQuerier.spec.ts +0 -36
  139. package/src/sqlite/sqliteQuerier.test.ts +0 -21
  140. package/src/sqlite/sqliteQuerier.ts +0 -37
  141. package/src/sqlite/sqliteQuerierPool.test.ts +0 -12
  142. package/src/sqlite/sqliteQuerierPool.ts +0 -38
  143. package/src/test/entityMock.ts +0 -375
  144. package/src/test/index.ts +0 -3
  145. package/src/test/it.util.ts +0 -69
  146. package/src/test/spec.util.ts +0 -57
  147. package/src/type/entity.ts +0 -218
  148. package/src/type/index.ts +0 -9
  149. package/src/type/migration.ts +0 -241
  150. package/src/type/namingStrategy.ts +0 -17
  151. package/src/type/querier.ts +0 -143
  152. package/src/type/querierPool.ts +0 -26
  153. package/src/type/query.ts +0 -506
  154. package/src/type/repository.ts +0 -142
  155. package/src/type/universalQuerier.ts +0 -133
  156. package/src/type/utility.ts +0 -21
  157. package/src/util/dialect.util-extra.spec.ts +0 -96
  158. package/src/util/dialect.util.spec.ts +0 -23
  159. package/src/util/dialect.util.ts +0 -134
  160. package/src/util/index.ts +0 -5
  161. package/src/util/object.util.spec.ts +0 -29
  162. package/src/util/object.util.ts +0 -27
  163. package/src/util/raw.ts +0 -11
  164. package/src/util/sql.util-extra.spec.ts +0 -17
  165. package/src/util/sql.util.spec.ts +0 -208
  166. package/src/util/sql.util.ts +0 -104
  167. package/src/util/string.util.spec.ts +0 -46
  168. package/src/util/string.util.ts +0 -35
  169. package/tsconfig.build.json +0 -5
  170. package/tsconfig.json +0 -8
@@ -1,329 +0,0 @@
1
- import type {
2
- ColumnSchema,
3
- ForeignKeySchema,
4
- IndexSchema,
5
- QuerierPool,
6
- SchemaIntrospector,
7
- SqlQuerier,
8
- TableSchema,
9
- } from '../../type/index.js';
10
- import { isSqlQuerier } from '../../type/index.js';
11
-
12
- /**
13
- * PostgreSQL schema introspector
14
- */
15
- export class PostgresSchemaIntrospector implements SchemaIntrospector {
16
- constructor(private readonly querierPool: QuerierPool) {}
17
-
18
- async getTableSchema(tableName: string): Promise<TableSchema | undefined> {
19
- const querier = await this.getQuerier();
20
-
21
- try {
22
- const exists = await this.tableExistsInternal(querier, tableName);
23
- if (!exists) {
24
- return undefined;
25
- }
26
-
27
- const [columns, indexes, foreignKeys, primaryKey] = await Promise.all([
28
- this.getColumns(querier, tableName),
29
- this.getIndexes(querier, tableName),
30
- this.getForeignKeys(querier, tableName),
31
- this.getPrimaryKey(querier, tableName),
32
- ]);
33
-
34
- return {
35
- name: tableName,
36
- columns,
37
- primaryKey,
38
- indexes,
39
- foreignKeys,
40
- };
41
- } finally {
42
- await querier.release();
43
- }
44
- }
45
-
46
- async getTableNames(): Promise<string[]> {
47
- const querier = await this.getQuerier();
48
-
49
- try {
50
- const sql = `
51
- SELECT table_name
52
- FROM information_schema.tables
53
- WHERE table_schema = 'public'
54
- AND table_type = 'BASE TABLE'
55
- ORDER BY table_name
56
- `;
57
-
58
- const results = await querier.all<{ table_name: string }>(sql);
59
- return results.map((r: any) => r.table_name);
60
- } finally {
61
- await querier.release();
62
- }
63
- }
64
-
65
- async tableExists(tableName: string): Promise<boolean> {
66
- const querier = await this.getQuerier();
67
-
68
- try {
69
- return this.tableExistsInternal(querier, tableName);
70
- } finally {
71
- await querier.release();
72
- }
73
- }
74
-
75
- private async tableExistsInternal(querier: SqlQuerier, tableName: string): Promise<boolean> {
76
- const sql = `
77
- SELECT EXISTS (
78
- SELECT FROM information_schema.tables
79
- WHERE table_schema = 'public'
80
- AND table_name = $1
81
- ) AS exists
82
- `;
83
-
84
- const results = await querier.all<{ exists: boolean }>(sql, [tableName]);
85
- return results[0]?.exists ?? false;
86
- }
87
-
88
- private async getQuerier(): Promise<SqlQuerier> {
89
- const querier = await this.querierPool.getQuerier();
90
-
91
- if (!isSqlQuerier(querier)) {
92
- await querier.release();
93
- throw new Error('PostgresSchemaIntrospector requires a SQL-based querier');
94
- }
95
-
96
- return querier;
97
- }
98
-
99
- private async getColumns(querier: SqlQuerier, tableName: string): Promise<ColumnSchema[]> {
100
- const sql = /*sql*/ `
101
- SELECT
102
- c.column_name,
103
- c.data_type,
104
- c.udt_name,
105
- c.is_nullable,
106
- c.column_default,
107
- c.character_maximum_length,
108
- c.numeric_precision,
109
- c.numeric_scale,
110
- COALESCE(
111
- (SELECT TRUE FROM information_schema.table_constraints tc
112
- JOIN information_schema.key_column_usage kcu
113
- ON tc.constraint_name = kcu.constraint_name
114
- WHERE tc.table_name = c.table_name
115
- AND tc.constraint_type = 'PRIMARY KEY'
116
- AND kcu.column_name = c.column_name
117
- LIMIT 1),
118
- FALSE
119
- ) AS is_primary_key,
120
- COALESCE(
121
- (SELECT TRUE FROM information_schema.table_constraints tc
122
- JOIN information_schema.key_column_usage kcu
123
- ON tc.constraint_name = kcu.constraint_name
124
- WHERE tc.table_name = c.table_name
125
- AND tc.constraint_type = 'UNIQUE'
126
- AND kcu.column_name = c.column_name
127
- LIMIT 1),
128
- FALSE
129
- ) AS is_unique,
130
- pg_catalog.col_description(
131
- (SELECT oid FROM pg_catalog.pg_class WHERE relname = c.table_name),
132
- c.ordinal_position
133
- ) AS column_comment
134
- FROM information_schema.columns c
135
- WHERE c.table_schema = 'public'
136
- AND c.table_name = $1
137
- ORDER BY c.ordinal_position
138
- `;
139
-
140
- const results = await querier.all<{
141
- column_name: string;
142
- data_type: string;
143
- udt_name: string;
144
- is_nullable: string;
145
- column_default: string | null;
146
- character_maximum_length: number | null;
147
- numeric_precision: number | null;
148
- numeric_scale: number | null;
149
- is_primary_key: boolean;
150
- is_unique: boolean;
151
- column_comment: string | null;
152
- }>(sql, [tableName]);
153
-
154
- return results.map((row: any) => ({
155
- name: row.column_name,
156
- type: this.normalizeType(row.data_type, row.udt_name),
157
- nullable: row.is_nullable === 'YES',
158
- defaultValue: this.parseDefaultValue(row.column_default),
159
- isPrimaryKey: row.is_primary_key,
160
- isAutoIncrement: this.isAutoIncrement(row.column_default),
161
- isUnique: row.is_unique,
162
- length: row.character_maximum_length ?? undefined,
163
- precision: row.numeric_precision ?? undefined,
164
- scale: row.numeric_scale ?? undefined,
165
- comment: row.column_comment ?? undefined,
166
- }));
167
- }
168
-
169
- private async getIndexes(querier: SqlQuerier, tableName: string): Promise<IndexSchema[]> {
170
- const sql = /*sql*/ `
171
- SELECT
172
- i.relname AS index_name,
173
- array_agg(a.attname ORDER BY k.n) AS columns,
174
- ix.indisunique AS is_unique
175
- FROM pg_class t
176
- JOIN pg_index ix ON t.oid = ix.indrelid
177
- JOIN pg_class i ON i.oid = ix.indexrelid
178
- JOIN pg_namespace n ON n.oid = t.relnamespace
179
- CROSS JOIN LATERAL unnest(ix.indkey) WITH ORDINALITY AS k(attnum, n)
180
- JOIN pg_attribute a ON a.attrelid = t.oid AND a.attnum = k.attnum
181
- WHERE t.relname = $1
182
- AND n.nspname = 'public'
183
- AND NOT ix.indisprimary
184
- GROUP BY i.relname, ix.indisunique
185
- ORDER BY i.relname
186
- `;
187
-
188
- const results = await querier.all<{
189
- index_name: string;
190
- columns: string[];
191
- is_unique: boolean;
192
- }>(sql, [tableName]);
193
-
194
- return results.map((row: any) => ({
195
- name: row.index_name,
196
- columns: row.columns,
197
- unique: row.is_unique,
198
- }));
199
- }
200
-
201
- private async getForeignKeys(querier: SqlQuerier, tableName: string): Promise<ForeignKeySchema[]> {
202
- const sql = /*sql*/ `
203
- SELECT
204
- tc.constraint_name,
205
- array_agg(kcu.column_name ORDER BY kcu.ordinal_position) AS columns,
206
- ccu.table_name AS referenced_table,
207
- array_agg(ccu.column_name ORDER BY kcu.ordinal_position) AS referenced_columns,
208
- rc.delete_rule,
209
- rc.update_rule
210
- FROM information_schema.table_constraints tc
211
- JOIN information_schema.key_column_usage kcu
212
- ON tc.constraint_name = kcu.constraint_name
213
- AND tc.table_schema = kcu.table_schema
214
- JOIN information_schema.constraint_column_usage ccu
215
- ON ccu.constraint_name = tc.constraint_name
216
- AND ccu.table_schema = tc.table_schema
217
- JOIN information_schema.referential_constraints rc
218
- ON rc.constraint_name = tc.constraint_name
219
- AND rc.constraint_schema = tc.table_schema
220
- WHERE tc.constraint_type = 'FOREIGN KEY'
221
- AND tc.table_name = $1
222
- AND tc.table_schema = 'public'
223
- GROUP BY tc.constraint_name, ccu.table_name, rc.delete_rule, rc.update_rule
224
- ORDER BY tc.constraint_name
225
- `;
226
-
227
- const results = await querier.all<{
228
- constraint_name: string;
229
- columns: string[];
230
- referenced_table: string;
231
- referenced_columns: string[];
232
- delete_rule: string;
233
- update_rule: string;
234
- }>(sql, [tableName]);
235
-
236
- return results.map((row: any) => ({
237
- name: row.constraint_name,
238
- columns: row.columns,
239
- referencedTable: row.referenced_table,
240
- referencedColumns: row.referenced_columns,
241
- onDelete: this.normalizeReferentialAction(row.delete_rule),
242
- onUpdate: this.normalizeReferentialAction(row.update_rule),
243
- }));
244
- }
245
-
246
- private async getPrimaryKey(querier: SqlQuerier, tableName: string): Promise<string[] | undefined> {
247
- const sql = /*sql*/ `
248
- SELECT kcu.column_name
249
- FROM information_schema.table_constraints tc
250
- JOIN information_schema.key_column_usage kcu
251
- ON tc.constraint_name = kcu.constraint_name
252
- AND tc.table_schema = kcu.table_schema
253
- WHERE tc.constraint_type = 'PRIMARY KEY'
254
- AND tc.table_name = $1
255
- AND tc.table_schema = 'public'
256
- ORDER BY kcu.ordinal_position
257
- `;
258
-
259
- const results = await querier.all<{ column_name: string }>(sql, [tableName]);
260
-
261
- if (results.length === 0) {
262
- return undefined;
263
- }
264
-
265
- return results.map((r: any) => r.column_name);
266
- }
267
-
268
- private normalizeType(dataType: string, udtName: string): string {
269
- // Handle user-defined types and arrays
270
- if (dataType === 'USER-DEFINED') {
271
- return udtName.toUpperCase();
272
- }
273
- if (dataType === 'ARRAY') {
274
- return `${udtName.replace(/^_/, '')}[]`;
275
- }
276
- return dataType.toUpperCase();
277
- }
278
-
279
- private parseDefaultValue(defaultValue: string | null): unknown {
280
- if (!defaultValue) {
281
- return undefined;
282
- }
283
-
284
- // Remove type casting
285
- const cleaned = defaultValue.replace(/::[a-z_]+(\[\])?/gi, '').trim();
286
-
287
- // Check for common patterns
288
- if (cleaned.startsWith("'") && cleaned.endsWith("'")) {
289
- return cleaned.slice(1, -1);
290
- }
291
- if (cleaned === 'true' || cleaned === 'false') {
292
- return cleaned === 'true';
293
- }
294
- if (cleaned === 'NULL') {
295
- return null;
296
- }
297
- if (/^-?\d+$/.test(cleaned)) {
298
- return Number.parseInt(cleaned, 10);
299
- }
300
- if (/^-?\d+\.\d+$/.test(cleaned)) {
301
- return Number.parseFloat(cleaned);
302
- }
303
-
304
- // Return as-is for functions like CURRENT_TIMESTAMP, nextval(), etc.
305
- return defaultValue;
306
- }
307
-
308
- private isAutoIncrement(defaultValue: string | null): boolean {
309
- if (!defaultValue) {
310
- return false;
311
- }
312
- return defaultValue.includes('nextval(');
313
- }
314
-
315
- private normalizeReferentialAction(action: string): 'CASCADE' | 'SET NULL' | 'RESTRICT' | 'NO ACTION' | undefined {
316
- switch (action.toUpperCase()) {
317
- case 'CASCADE':
318
- return 'CASCADE';
319
- case 'SET NULL':
320
- return 'SET NULL';
321
- case 'RESTRICT':
322
- return 'RESTRICT';
323
- case 'NO ACTION':
324
- return 'NO ACTION';
325
- default:
326
- return undefined;
327
- }
328
- }
329
- }
@@ -1,112 +0,0 @@
1
- import { beforeEach, describe, expect, it, jest } from 'bun:test';
2
- import type { QuerierPool, SqlQuerier } from '../../type/index.js';
3
- import { SqliteSchemaIntrospector } from './sqliteIntrospector.js';
4
-
5
- describe('SqliteSchemaIntrospector', () => {
6
- let introspector: SqliteSchemaIntrospector;
7
- let pool: QuerierPool;
8
- let querier: SqlQuerier;
9
-
10
- let mockAll: ReturnType<typeof jest.fn>;
11
- let mockRun: ReturnType<typeof jest.fn>;
12
- let mockRelease: ReturnType<typeof jest.fn>;
13
- let mockGetQuerier: ReturnType<typeof jest.fn>;
14
-
15
- beforeEach(() => {
16
- mockAll = jest.fn<any>().mockResolvedValue([]);
17
- mockRun = jest.fn<any>().mockResolvedValue({});
18
- mockRelease = jest.fn<any>().mockResolvedValue(undefined);
19
-
20
- querier = {
21
- all: mockAll,
22
- run: mockRun,
23
- release: mockRelease,
24
- dialect: { escapeIdChar: '`' },
25
- } as any;
26
-
27
- mockGetQuerier = jest.fn<any>().mockResolvedValue(querier);
28
- pool = {
29
- getQuerier: mockGetQuerier,
30
- } as any;
31
-
32
- introspector = new SqliteSchemaIntrospector(pool);
33
- });
34
-
35
- it('getTableNames should return a list of tables', async () => {
36
- mockAll.mockResolvedValueOnce([{ name: 'users' }, { name: 'posts' }]);
37
-
38
- const names = await introspector.getTableNames();
39
-
40
- expect(names).toEqual(['users', 'posts']);
41
- expect(querier.all).toHaveBeenCalledWith(expect.stringContaining('sqlite_master'));
42
- });
43
-
44
- it('getTableSchema should return table details', async () => {
45
- mockAll.mockImplementation((async (sql: string, _values?: unknown[]) => {
46
- const normalizedSql = sql.replace(/\s+/g, ' ').trim();
47
- if (normalizedSql.includes('SELECT COUNT(*) as count FROM sqlite_master')) {
48
- return [{ count: 1 }];
49
- }
50
- if (normalizedSql.includes('PRAGMA table_info')) {
51
- return [
52
- { cid: 0, name: 'id', type: 'INTEGER', notnull: 1, dflt_value: null as any, pk: 1 },
53
- { cid: 1, name: 'username', type: 'VARCHAR(255)', notnull: 0, dflt_value: null as any, pk: 0 },
54
- ];
55
- }
56
- if (normalizedSql.includes('PRAGMA index_list')) {
57
- return [
58
- { seq: 0, name: 'sqlite_autoindex_users_1', unique: 1, origin: 'u', partial: 0 },
59
- { seq: 1, name: 'idx_users_username', unique: 0, origin: 'c', partial: 0 },
60
- ];
61
- }
62
- if (normalizedSql.includes('PRAGMA index_info')) {
63
- return [{ name: 'username' }];
64
- }
65
- if (normalizedSql.includes('PRAGMA foreign_key_list')) {
66
- return [
67
- {
68
- id: 0,
69
- seq: 0,
70
- table: 'users',
71
- from: 'author_id',
72
- to: 'id',
73
- on_update: 'CASCADE',
74
- on_delete: 'RESTRICT',
75
- match: 'NONE',
76
- },
77
- ];
78
- }
79
- return [];
80
- }) as any);
81
-
82
- const schema = await introspector.getTableSchema('users');
83
-
84
- expect(schema).toBeDefined();
85
- expect(schema?.name).toBe('users');
86
- expect(schema?.columns).toHaveLength(2);
87
- expect(schema?.columns[0]).toMatchObject({
88
- name: 'id',
89
- type: 'INTEGER',
90
- nullable: false,
91
- isAutoIncrement: true,
92
- isPrimaryKey: true,
93
- });
94
- expect(schema?.columns[1]).toMatchObject({
95
- name: 'username',
96
- type: 'VARCHAR',
97
- nullable: true,
98
- isUnique: true,
99
- length: 255,
100
- });
101
- expect(schema?.indexes).toHaveLength(1);
102
- expect(schema?.indexes?.[0].name).toBe('idx_users_username');
103
- });
104
-
105
- it('getTableSchema should return undefined for non-existent table', async () => {
106
- mockAll.mockResolvedValueOnce([{ count: 0 }]);
107
-
108
- const schema = await introspector.getTableSchema('non_existent');
109
-
110
- expect(schema).toBeUndefined();
111
- });
112
- });