@starbemtech/star-db-query-builder 1.1.0 → 1.3.0

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 (88) hide show
  1. package/.github/workflows/publish.yml +118 -0
  2. package/ARCHITECTURE.md +313 -0
  3. package/CHANGELOG.md +67 -0
  4. package/README.md +1275 -210
  5. package/coverage/base.css +224 -0
  6. package/coverage/block-navigation.js +87 -0
  7. package/coverage/favicon.png +0 -0
  8. package/coverage/index.html +131 -0
  9. package/coverage/lcov-report/base.css +224 -0
  10. package/coverage/lcov-report/block-navigation.js +87 -0
  11. package/coverage/lcov-report/favicon.png +0 -0
  12. package/coverage/lcov-report/index.html +131 -0
  13. package/coverage/lcov-report/mysqlClient.ts.html +685 -0
  14. package/coverage/lcov-report/pgClient.ts.html +823 -0
  15. package/coverage/lcov-report/prettify.css +1 -0
  16. package/coverage/lcov-report/prettify.js +2 -0
  17. package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
  18. package/coverage/lcov-report/sorter.js +210 -0
  19. package/coverage/lcov.info +533 -0
  20. package/coverage/mysqlClient.ts.html +685 -0
  21. package/coverage/pgClient.ts.html +823 -0
  22. package/coverage/prettify.css +1 -0
  23. package/coverage/prettify.js +2 -0
  24. package/coverage/sort-arrow-sprite.png +0 -0
  25. package/coverage/sorter.js +210 -0
  26. package/dist/index.d.ts +5 -4
  27. package/dist/index.js +1 -1
  28. package/dist/index.js.map +1 -1
  29. package/dist/src/core/repository.d.ts +374 -0
  30. package/dist/src/core/repository.js +677 -0
  31. package/dist/src/core/repository.js.map +1 -0
  32. package/dist/src/{default → core}/types.d.ts +8 -0
  33. package/dist/src/{default → core}/types.js.map +1 -1
  34. package/dist/src/core/utils.d.ts +133 -0
  35. package/dist/src/{default → core}/utils.js +167 -0
  36. package/dist/src/core/utils.js.map +1 -0
  37. package/dist/src/db/IDatabaseClient.d.ts +10 -1
  38. package/dist/src/db/initDb.d.ts +120 -1
  39. package/dist/src/db/initDb.js +119 -0
  40. package/dist/src/db/initDb.js.map +1 -1
  41. package/dist/src/db/mysqlClient.d.ts +23 -1
  42. package/dist/src/db/mysqlClient.js +115 -0
  43. package/dist/src/db/mysqlClient.js.map +1 -1
  44. package/dist/src/db/pgClient.d.ts +22 -1
  45. package/dist/src/db/pgClient.js +127 -0
  46. package/dist/src/db/pgClient.js.map +1 -1
  47. package/dist/src/monitor/monitor.d.ts +6 -1
  48. package/dist/src/monitor/monitor.js +5 -0
  49. package/dist/src/monitor/monitor.js.map +1 -1
  50. package/dist/src/setupTests.d.ts +26 -0
  51. package/dist/src/setupTests.js +43 -0
  52. package/dist/src/setupTests.js.map +1 -0
  53. package/docs/INDEX.md +145 -0
  54. package/docs/methods/findFirst.md +394 -0
  55. package/docs/methods/findMany.md +587 -0
  56. package/docs/methods/insert.md +536 -0
  57. package/docs/methods/insertMany.md +627 -0
  58. package/docs/methods/joins.md +781 -0
  59. package/docs/methods/rawQuery.md +284 -0
  60. package/docs/methods/transactions.md +737 -0
  61. package/eslint.config.mjs +77 -0
  62. package/index.ts +5 -4
  63. package/jest.config.ts +23 -28
  64. package/package.json +53 -30
  65. package/scripts/release.sh +123 -0
  66. package/src/core/repository.ts +865 -0
  67. package/src/{default → core}/types.ts +10 -1
  68. package/src/{default → core}/utils.ts +168 -0
  69. package/src/db/IDatabaseClient.ts +11 -1
  70. package/src/db/__tests__/mysqlClient.test.ts +262 -0
  71. package/src/db/__tests__/pgClient.test.ts +260 -0
  72. package/src/db/initDb.ts +120 -1
  73. package/src/db/mysqlClient.ts +119 -3
  74. package/src/db/pgClient.ts +131 -3
  75. package/src/monitor/monitor.ts +5 -0
  76. package/src/setupTests.ts +45 -0
  77. package/tsconfig.test.json +21 -0
  78. package/.eslintignore +0 -4
  79. package/.eslintrc.json +0 -32
  80. package/dist/.eslintrc.json +0 -32
  81. package/dist/src/default/genericRepository.d.ts +0 -28
  82. package/dist/src/default/genericRepository.js +0 -284
  83. package/dist/src/default/genericRepository.js.map +0 -1
  84. package/dist/src/default/utils.d.ts +0 -9
  85. package/dist/src/default/utils.js.map +0 -1
  86. package/index.d.ts +0 -60
  87. package/src/default/genericRepository.ts +0 -461
  88. /package/dist/src/{default → core}/types.js +0 -0
@@ -1,461 +0,0 @@
1
- import { v4 as uuid } from 'uuid'
2
- import { QueryParams, QueryBuilder } from './types'
3
- import {
4
- createGroupByClause,
5
- createLimitClause,
6
- createOrderByClause,
7
- generatePlaceholders,
8
- createSelectFields,
9
- createWhereClause,
10
- generateSetClause,
11
- createOffsetClause,
12
- } from './utils'
13
-
14
- export const findFirst = async <T>({
15
- tableName,
16
- dbClient,
17
- select,
18
- where,
19
- groupBy,
20
- orderBy,
21
- }: QueryParams<T>): Promise<T | null> => {
22
- if (!tableName) throw new Error('Table name is required')
23
- if (!dbClient) throw new Error('DB client is required')
24
-
25
- const fields = createSelectFields(select, dbClient.clientType)
26
- const [whereClause, params] = createWhereClause<T>(
27
- where,
28
- 1,
29
- dbClient.clientType
30
- )
31
-
32
- const orderByClause = createOrderByClause(orderBy)
33
- const groupByClause = createGroupByClause(groupBy)
34
-
35
- const rows = await dbClient.query<T[]>(
36
- `SELECT ${fields} FROM ${tableName}
37
- ${whereClause.length > 7 ? whereClause : ''}
38
- ${groupByClause}
39
- ${orderByClause}
40
- `,
41
- params
42
- )
43
-
44
- return rows[0] || null
45
- }
46
-
47
- export const findMany = async <T>({
48
- tableName,
49
- dbClient,
50
- select,
51
- where,
52
- groupBy,
53
- orderBy,
54
- limit,
55
- offset,
56
- unaccent,
57
- }: QueryParams<T>): Promise<T[]> => {
58
- if (!tableName) throw new Error('Table name is required')
59
- if (!dbClient) throw new Error('DB client is required')
60
-
61
- const fields = createSelectFields(select, dbClient.clientType)
62
- const [whereClause, params] = createWhereClause(
63
- where,
64
- 1,
65
- dbClient.clientType,
66
- unaccent
67
- )
68
-
69
- const orderByClause = createOrderByClause(orderBy)
70
- const groupByClause = createGroupByClause(groupBy)
71
- const limitClause = createLimitClause(limit)
72
- const offsetClause = createOffsetClause(offset)
73
-
74
- const rows = await dbClient.query<T[]>(
75
- `SELECT ${fields} FROM ${tableName}
76
- ${whereClause.length > 7 ? whereClause : ''}
77
- ${groupByClause}
78
- ${orderByClause}
79
- ${limitClause}
80
- ${offsetClause}
81
- `,
82
- params
83
- )
84
-
85
- return rows || []
86
- }
87
-
88
- export const insert = async <P, R>({
89
- tableName,
90
- dbClient,
91
- data,
92
- returning,
93
- }: QueryParams<R> & { data: P; returning?: string[] }): Promise<R> => {
94
- if (!tableName) throw new Error('Table name is required')
95
- if (!dbClient) throw new Error('DB client is required')
96
- if (!data) throw new Error('Data object is required')
97
-
98
- const keys =
99
- dbClient.clientType === 'pg'
100
- ? Object.keys(data).map((key) =>
101
- key === 'authorization' ? `"${key}"` : key
102
- )
103
- : Object.keys(data)
104
- const values = Object.values(data)
105
-
106
- keys.unshift('id')
107
- const generatedUUID: string = uuid()
108
- values.unshift(generatedUUID)
109
-
110
- keys.push('updated_at')
111
- values.push(new Date())
112
-
113
- const placeholders = generatePlaceholders(keys, dbClient.clientType)
114
- let query = `INSERT INTO ${tableName} (${keys.join(
115
- ', '
116
- )}) VALUES (${placeholders})`
117
-
118
- if (dbClient.clientType === 'pg') {
119
- if (returning && returning.length > 0) {
120
- query += ` RETURNING ${createSelectFields(
121
- returning,
122
- dbClient.clientType
123
- )}`
124
- }
125
- }
126
-
127
- const inserted = await dbClient.query<R[]>(query, values)
128
-
129
- if (dbClient.clientType === 'mysql') {
130
- const rows = await dbClient.query<R[]>(
131
- `SELECT ${
132
- returning && returning.length > 0
133
- ? createSelectFields(returning, dbClient.clientType)
134
- : '*'
135
- } FROM ${tableName}
136
- WHERE
137
- id = ?
138
- `,
139
- [generatedUUID]
140
- )
141
-
142
- return rows[0]
143
- }
144
-
145
- return inserted[0]
146
- }
147
-
148
- export const insertMany = async <P, R>({
149
- tableName,
150
- dbClient,
151
- data,
152
- returning,
153
- }: QueryParams<R> & { data: P[]; returning?: string[] }): Promise<R[]> => {
154
- if (!tableName) throw new Error('Table name is required')
155
- if (!dbClient) throw new Error('DB client is required')
156
- if (!data || data.length === 0)
157
- throw new Error('Data array is required and cannot be empty')
158
-
159
- const firstItem = data[0] as Record<string, any>
160
- const keys =
161
- dbClient.clientType === 'pg'
162
- ? Object.keys(firstItem).map((key) =>
163
- key === 'authorization' ? `"${key}"` : key
164
- )
165
- : Object.keys(firstItem)
166
-
167
- const allKeys = ['id', ...keys, 'updated_at']
168
-
169
- let query = `INSERT INTO ${tableName} (${allKeys.join(', ')}) VALUES `
170
-
171
- const allValues: any[] = []
172
- const valueRows: string[] = []
173
- const generatedIds: string[] = []
174
-
175
- data.forEach((item, rowIndex) => {
176
- const values = Object.values(item as Record<string, any>)
177
- const generatedUUID: string = uuid()
178
- generatedIds.push(generatedUUID)
179
- const currentValues = [generatedUUID, ...values, new Date()]
180
-
181
- allValues.push(...currentValues)
182
-
183
- // Generate unique placeholders for each row
184
- if (dbClient.clientType === 'pg') {
185
- const startIndex = rowIndex * allKeys.length + 1
186
- const placeholders = allKeys
187
- .map((_, index) => `$${startIndex + index}`)
188
- .join(', ')
189
- valueRows.push(`(${placeholders})`)
190
- } else {
191
- const placeholders = allKeys.map(() => '?').join(', ')
192
- valueRows.push(`(${placeholders})`)
193
- }
194
- })
195
-
196
- query += valueRows.join(', ')
197
-
198
- if (dbClient.clientType === 'pg') {
199
- if (returning && returning.length > 0) {
200
- query += ` RETURNING ${createSelectFields(
201
- returning,
202
- dbClient.clientType
203
- )}`
204
- }
205
- }
206
-
207
- const inserted = await dbClient.query<R[]>(query, allValues)
208
-
209
- if (dbClient.clientType === 'mysql') {
210
- const placeholders = generatedIds.map(() => '?').join(', ')
211
-
212
- const rows = await dbClient.query<R[]>(
213
- `SELECT ${
214
- returning && returning.length > 0
215
- ? createSelectFields(returning, dbClient.clientType)
216
- : '*'
217
- } FROM ${tableName}
218
- WHERE id IN (${placeholders})
219
- ORDER BY FIELD(id, ${placeholders})
220
- `,
221
- [...generatedIds, ...generatedIds]
222
- )
223
-
224
- return rows
225
- }
226
-
227
- return inserted
228
- }
229
-
230
- export const update = async <P, R>({
231
- tableName,
232
- dbClient,
233
- id,
234
- data,
235
- returning,
236
- }: QueryParams<R> & { data: P; returning?: string[] }): Promise<R | void> => {
237
- if (!tableName) throw new Error('Table name is required')
238
- if (!dbClient) throw new Error('DB client is required')
239
- if (!id) throw new Error('ID is required')
240
- if (!data) throw new Error('Data object is required')
241
-
242
- const keys = Object.keys(data)
243
- const values: any[] = Object.values(data)
244
-
245
- const setClause = generateSetClause(keys, dbClient.clientType)
246
- let query = `UPDATE ${tableName} SET ${setClause} WHERE id = '${id}'`
247
-
248
- if (dbClient.clientType === 'pg') {
249
- if (returning && returning.length > 0) {
250
- query += ` RETURNING ${createSelectFields(returning, dbClient.clientType)}`
251
- }
252
- }
253
-
254
- const updated = await dbClient.query<R[]>(query, values)
255
-
256
- if (dbClient.clientType === 'mysql') {
257
- const rows = await dbClient.query<R[]>(
258
- `SELECT ${
259
- returning && returning.length > 0
260
- ? createSelectFields(returning, dbClient.clientType)
261
- : '*'
262
- } FROM ${tableName}
263
- WHERE
264
- id = ?
265
- `,
266
- [id]
267
- )
268
-
269
- return rows[0]
270
- }
271
-
272
- return updated[0]
273
- }
274
-
275
- export const updateMany = async <P, R>({
276
- tableName,
277
- dbClient,
278
- data,
279
- where,
280
- returning,
281
- }: QueryParams<R> & { data: P; returning?: string[] }): Promise<R[]> => {
282
- if (!tableName) throw new Error('Table name is required')
283
- if (!dbClient) throw new Error('DB client is required')
284
- if (!data) throw new Error('Data object is required')
285
- if (!where) throw new Error('Where condition is required')
286
-
287
- const keys = Object.keys(data)
288
- const values: any[] = Object.values(data)
289
-
290
- // Generate SET clause with correct placeholders
291
- const setClause = keys
292
- .map((key, index) =>
293
- dbClient.clientType === 'pg' ? `${key} = $${index + 1}` : `${key} = ?`
294
- )
295
- .join(', ')
296
-
297
- // Generate WHERE clause with placeholders starting after SET values
298
- const [whereClause, whereParams] = createWhereClause(
299
- where,
300
- values.length + 1,
301
- dbClient.clientType
302
- )
303
-
304
- let query = `UPDATE ${tableName} SET ${setClause} ${whereClause}`
305
-
306
- if (dbClient.clientType === 'pg') {
307
- if (returning && returning.length > 0) {
308
- query += ` RETURNING ${createSelectFields(returning, dbClient.clientType)}`
309
- }
310
- }
311
-
312
- const updated = await dbClient.query<R[]>(query, [...values, ...whereParams])
313
-
314
- if (dbClient.clientType === 'mysql') {
315
- // For MySQL, we need to fetch the updated records separately
316
- // since MySQL doesn't support RETURNING clause
317
- const rows = await dbClient.query<R[]>(
318
- `SELECT ${
319
- returning && returning.length > 0
320
- ? createSelectFields(returning, dbClient.clientType)
321
- : '*'
322
- } FROM ${tableName}
323
- ${whereClause}
324
- `,
325
- whereParams
326
- )
327
-
328
- return rows
329
- }
330
-
331
- return updated
332
- }
333
-
334
- export const deleteOne = async <T>({
335
- tableName,
336
- dbClient,
337
- id,
338
- permanently = false,
339
- }: QueryParams<T> & { permanently?: boolean }): Promise<void> => {
340
- if (!tableName) throw new Error('Table name is required')
341
- if (!dbClient) throw new Error('DB client is required')
342
- if (!id) throw new Error('ID is required')
343
-
344
- await dbClient.query(
345
- permanently
346
- ? `DELETE FROM ${tableName} WHERE id = ${
347
- dbClient.clientType === 'pg' ? '$1' : '?'
348
- }`
349
- : `UPDATE ${tableName} SET status = 'deleted' WHERE id = ${
350
- dbClient.clientType === 'pg' ? '$1' : '?'
351
- }`,
352
- [id]
353
- )
354
- }
355
-
356
- export const deleteMany = async <T>({
357
- tableName,
358
- dbClient,
359
- ids,
360
- field = 'id',
361
- permanently = false,
362
- }: QueryParams<T> & {
363
- ids: string[] | number[]
364
- field?: string
365
- permanently?: boolean
366
- }): Promise<void> => {
367
- if (!tableName) throw new Error('Table name is required')
368
- if (!dbClient) throw new Error('DB client is required')
369
- if (!ids || ids.length === 0)
370
- throw new Error('IDs are required and cannot be empty')
371
- if (!field) throw new Error('Field is required')
372
-
373
- const placeholders =
374
- dbClient.clientType === 'pg'
375
- ? ids.map((_, index) => `$${index + 1}`).join(', ')
376
- : ids.map(() => '?').join(', ')
377
-
378
- const query = permanently
379
- ? `DELETE FROM ${tableName} WHERE ${field} IN (${placeholders})`
380
- : `UPDATE ${tableName} SET status = 'deleted' WHERE ${field} IN (${placeholders})`
381
-
382
- await dbClient.query(query, ids)
383
- }
384
-
385
- export const joins = async <T>({
386
- tableName,
387
- dbClient,
388
- select,
389
- joins,
390
- where,
391
- groupBy,
392
- orderBy,
393
- limit,
394
- offset,
395
- unaccent,
396
- }: QueryParams<T>): Promise<T[]> => {
397
- if (!tableName) throw new Error('Table name is required')
398
- if (!dbClient) throw new Error('DB client is required')
399
-
400
- const fields = Array.isArray(select) ? select : []
401
- const selectFields = createSelectFields(fields, dbClient.clientType)
402
- const [whereClause, params] = createWhereClause(
403
- where,
404
- 1,
405
- dbClient.clientType,
406
- unaccent
407
- )
408
- const groupByClause = createGroupByClause(groupBy)
409
- const orderByClause = createOrderByClause(orderBy)
410
- const limitClause = createLimitClause(limit)
411
- const offsetClause = createOffsetClause(offset)
412
-
413
- const queryBuilder: QueryBuilder = {
414
- select: [selectFields],
415
- from: tableName,
416
- joins: joins,
417
- where: whereClause,
418
- groupBy: [groupByClause],
419
- orderBy: orderByClause,
420
- limit: limitClause,
421
- offset: offsetClause,
422
- }
423
-
424
- const queryString = await buildQuery(queryBuilder)
425
-
426
- const rows = await dbClient.query<T[]>(queryString, params)
427
-
428
- return rows
429
- }
430
-
431
- async function buildQuery(params: QueryBuilder): Promise<string> {
432
- let queryString = `SELECT ${params.select.join(', ')} FROM ${params.from}`
433
-
434
- if (params.joins) {
435
- for (const join of params.joins) {
436
- queryString += ` ${join.type} JOIN ${join.table} ON ${join.on}`
437
- }
438
- }
439
-
440
- if (params.where) {
441
- queryString += `${params.where}`
442
- }
443
-
444
- if (params.groupBy) {
445
- queryString += `${params.groupBy}`
446
- }
447
-
448
- if (params.orderBy) {
449
- queryString += `${params.orderBy}`
450
- }
451
-
452
- if (params.limit) {
453
- queryString += `${params.limit}`
454
- }
455
-
456
- if (params.offset) {
457
- queryString += `${params.offset}`
458
- }
459
-
460
- return queryString
461
- }
File without changes