@starbemtech/star-db-query-builder 1.3.1 → 1.4.1

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 (76) hide show
  1. package/.claude/skills/star-db-query-builder/SKILL.md +104 -0
  2. package/CHANGELOG.md +107 -55
  3. package/LICENSE +21 -0
  4. package/README.md +92 -1391
  5. package/bin/install-skill.js +53 -0
  6. package/dist/src/core/repository.d.ts +125 -10
  7. package/dist/src/core/repository.js +336 -43
  8. package/dist/src/core/repository.js.map +1 -1
  9. package/dist/src/core/types.d.ts +29 -6
  10. package/dist/src/core/utils.d.ts +119 -0
  11. package/dist/src/core/utils.js +353 -82
  12. package/dist/src/core/utils.js.map +1 -1
  13. package/dist/src/db/IDatabaseClient.d.ts +14 -4
  14. package/dist/src/db/initDb.d.ts +60 -50
  15. package/dist/src/db/initDb.js +96 -64
  16. package/dist/src/db/initDb.js.map +1 -1
  17. package/dist/src/db/mysqlClient.d.ts +3 -8
  18. package/dist/src/db/mysqlClient.js +10 -11
  19. package/dist/src/db/mysqlClient.js.map +1 -1
  20. package/dist/src/db/pgClient.js +1 -2
  21. package/dist/src/db/pgClient.js.map +1 -1
  22. package/dist/src/monitor/monitor.js +7 -0
  23. package/dist/src/monitor/monitor.js.map +1 -1
  24. package/package.json +32 -24
  25. package/.github/workflows/publish.yml +0 -118
  26. package/.prettierignore +0 -3
  27. package/.prettierrc +0 -5
  28. package/ARCHITECTURE.md +0 -313
  29. package/coverage/base.css +0 -224
  30. package/coverage/block-navigation.js +0 -87
  31. package/coverage/favicon.png +0 -0
  32. package/coverage/index.html +0 -131
  33. package/coverage/lcov-report/base.css +0 -224
  34. package/coverage/lcov-report/block-navigation.js +0 -87
  35. package/coverage/lcov-report/favicon.png +0 -0
  36. package/coverage/lcov-report/index.html +0 -131
  37. package/coverage/lcov-report/mysqlClient.ts.html +0 -685
  38. package/coverage/lcov-report/pgClient.ts.html +0 -823
  39. package/coverage/lcov-report/prettify.css +0 -1
  40. package/coverage/lcov-report/prettify.js +0 -2
  41. package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
  42. package/coverage/lcov-report/sorter.js +0 -210
  43. package/coverage/lcov.info +0 -533
  44. package/coverage/mysqlClient.ts.html +0 -685
  45. package/coverage/pgClient.ts.html +0 -823
  46. package/coverage/prettify.css +0 -1
  47. package/coverage/prettify.js +0 -2
  48. package/coverage/sort-arrow-sprite.png +0 -0
  49. package/coverage/sorter.js +0 -210
  50. package/dist/src/setupTests.d.ts +0 -26
  51. package/dist/src/setupTests.js +0 -43
  52. package/dist/src/setupTests.js.map +0 -1
  53. package/docs/INDEX.md +0 -145
  54. package/docs/methods/findFirst.md +0 -394
  55. package/docs/methods/findMany.md +0 -587
  56. package/docs/methods/insert.md +0 -536
  57. package/docs/methods/insertMany.md +0 -627
  58. package/docs/methods/joins.md +0 -781
  59. package/docs/methods/rawQuery.md +0 -284
  60. package/docs/methods/transactions.md +0 -737
  61. package/eslint.config.mjs +0 -77
  62. package/index.ts +0 -16
  63. package/jest.config.ts +0 -194
  64. package/scripts/release.sh +0 -123
  65. package/src/core/repository.ts +0 -865
  66. package/src/core/types.ts +0 -97
  67. package/src/core/utils.ts +0 -357
  68. package/src/db/IDatabaseClient.ts +0 -16
  69. package/src/db/__tests__/mysqlClient.test.ts +0 -262
  70. package/src/db/__tests__/pgClient.test.ts +0 -260
  71. package/src/db/initDb.ts +0 -181
  72. package/src/db/mysqlClient.ts +0 -200
  73. package/src/db/pgClient.ts +0 -246
  74. package/src/monitor/monitor.ts +0 -16
  75. package/src/setupTests.ts +0 -45
  76. package/tsconfig.test.json +0 -21
@@ -0,0 +1,53 @@
1
+ #!/usr/bin/env node
2
+
3
+ // Copies this package's bundled Claude Code skill into the consuming repo's
4
+ // .claude/skills/ so agents get correct star-db-query-builder usage guidance
5
+ // without a manual copy-paste. Run from the consuming repo's root:
6
+ //
7
+ // npx star-db-query-builder-install-skill
8
+ //
9
+ // Safe to re-run after upgrading the package — it always overwrites with the
10
+ // version bundled in the currently installed package.
11
+
12
+ const fs = require('fs')
13
+ const path = require('path')
14
+
15
+ const SKILL_NAME = 'star-db-query-builder'
16
+ const SOURCE = path.join(__dirname, '..', '.claude', 'skills', SKILL_NAME, 'SKILL.md')
17
+ const TARGET_DIR = path.join(process.cwd(), '.claude', 'skills', SKILL_NAME)
18
+ const TARGET = path.join(TARGET_DIR, 'SKILL.md')
19
+
20
+ function main() {
21
+ if (!fs.existsSync(SOURCE)) {
22
+ console.error(
23
+ `[star-db-query-builder] Could not find bundled skill at ${SOURCE}. ` +
24
+ 'Is @starbemtech/star-db-query-builder installed correctly?'
25
+ )
26
+ process.exitCode = 1
27
+ return
28
+ }
29
+
30
+ const alreadyExists = fs.existsSync(TARGET)
31
+ const previous = alreadyExists ? fs.readFileSync(TARGET, 'utf8') : null
32
+ const next = fs.readFileSync(SOURCE, 'utf8')
33
+
34
+ if (previous === next) {
35
+ console.log(`[star-db-query-builder] Skill already up to date at ${path.relative(process.cwd(), TARGET)}`)
36
+ return
37
+ }
38
+
39
+ fs.mkdirSync(TARGET_DIR, { recursive: true })
40
+ fs.writeFileSync(TARGET, next)
41
+
42
+ console.log(
43
+ `[star-db-query-builder] ${alreadyExists ? 'Updated' : 'Installed'} skill at ${path.relative(
44
+ process.cwd(),
45
+ TARGET
46
+ )}`
47
+ )
48
+ if (!alreadyExists) {
49
+ console.log('[star-db-query-builder] Commit this file so the rest of the team gets it too.')
50
+ }
51
+ }
52
+
53
+ main()
@@ -1,5 +1,5 @@
1
- import { QueryParams, RawQueryParams } from './types';
2
- import { ITransactionClient } from '../db/IDatabaseClient';
1
+ import { QueryParams, RawQueryParams, CursorPageResult, Conditions } from './types';
2
+ import { IDatabaseClient, ITransactionClient } from '../db/IDatabaseClient';
3
3
  /**
4
4
  * Finds the first record in the specified table
5
5
  *
@@ -18,7 +18,7 @@ import { ITransactionClient } from '../db/IDatabaseClient';
18
18
  * tableName: 'users',
19
19
  * dbClient: dbClient,
20
20
  * select: ['id', 'name', 'email'],
21
- * where: { status: 'active' },
21
+ * where: { status: { operator: '=', value: 'active' } },
22
22
  * groupBy: ['status'],
23
23
  * orderBy: [{ field: 'created_at', direction: 'DESC' }],
24
24
  * })
@@ -28,7 +28,7 @@ import { ITransactionClient } from '../db/IDatabaseClient';
28
28
  * tableName: 'users',
29
29
  * dbClient: dbClient,
30
30
  * select: ['id', 'name', 'email'],
31
- * where: { status: 'active' },
31
+ * where: { status: { operator: '=', value: 'active' } },
32
32
  * groupBy: ['status'],
33
33
  * orderBy: [{ field: 'created_at', direction: 'DESC' }],
34
34
  * })
@@ -52,7 +52,7 @@ export declare const findFirst: <T>({ tableName, dbClient, select, where, groupB
52
52
  * tableName: 'users',
53
53
  * dbClient: dbClient,
54
54
  * select: ['id', 'name', 'email'],
55
- * where: { status: 'active' },
55
+ * where: { status: { operator: '=', value: 'active' } },
56
56
  * groupBy: ['status'],
57
57
  * orderBy: [{ field: 'created_at', direction: 'DESC' }],
58
58
  * limit: 10,
@@ -61,6 +61,51 @@ export declare const findFirst: <T>({ tableName, dbClient, select, where, groupB
61
61
  * })
62
62
  */
63
63
  export declare const findMany: <T>({ tableName, dbClient, select, where, groupBy, orderBy, limit, offset, unaccent, }: QueryParams<T>) => Promise<T[]>;
64
+ /**
65
+ * Finds multiple records in the specified table using keyset (cursor)
66
+ * pagination instead of offset/limit
67
+ *
68
+ * Unlike offset-based pagination, keyset pagination stays O(1) regardless of
69
+ * how deep the page is (no `OFFSET N` row-skipping) and does not skip/repeat
70
+ * rows when the underlying data changes between pages. It requires
71
+ * `cursorField` to be a strictly ordered, indexed column — `id` (if
72
+ * sequential) or `created_at` are typical choices; a plain UUID `id` works
73
+ * for uniqueness but its ordering is arbitrary, so prefer a monotonically
74
+ * increasing column when the page order matters to callers.
75
+ *
76
+ * @template T - The type of the records to be returned
77
+ * @param params - Query parameters including table name, database client, select fields, where conditions, cursor field, cursor value, direction, page size and unaccent
78
+ * @returns Promise<CursorPageResult<T>> - The page of records plus the cursor to request the next page (`null` when there is no next page)
79
+ *
80
+ * @throws {Error} When table name is not provided
81
+ * @throws {Error} When database client is not provided
82
+ * @throws {Error} When direction is not ASC or DESC
83
+ *
84
+ * @example
85
+ * // First page
86
+ * const page1 = await findManyCursor({
87
+ * tableName: 'users',
88
+ * dbClient: dbClient,
89
+ * where: { status: { operator: '=', value: 'active' } },
90
+ * cursorField: 'created_at',
91
+ * limit: 20,
92
+ * })
93
+ *
94
+ * @example
95
+ * // Next page
96
+ * const page2 = await findManyCursor({
97
+ * tableName: 'users',
98
+ * dbClient: dbClient,
99
+ * cursorField: 'created_at',
100
+ * cursor: page1.nextCursor,
101
+ * limit: 20,
102
+ * })
103
+ */
104
+ export declare const findManyCursor: <T>({ tableName, dbClient, select, where, cursorField, cursor, direction, limit, unaccent, }: QueryParams<T> & {
105
+ cursorField?: string;
106
+ cursor?: string | number;
107
+ direction?: "ASC" | "DESC";
108
+ }) => Promise<CursorPageResult<T>>;
64
109
  /**
65
110
  * Inserts a new record into the specified table
66
111
  *
@@ -115,6 +160,44 @@ export declare const insertMany: <P, R>({ tableName, dbClient, data, returning,
115
160
  data: P[];
116
161
  returning?: string[];
117
162
  }) => Promise<R[]>;
163
+ /**
164
+ * Inserts a record, or updates it in place if it collides with an existing
165
+ * unique/primary key
166
+ *
167
+ * Builds `INSERT ... ON CONFLICT (...) DO UPDATE SET ...` for pg and
168
+ * `INSERT ... ON DUPLICATE KEY UPDATE ...` for mysql. `conflictFields` must
169
+ * name columns actually covered by a unique or primary key constraint on
170
+ * `tableName` — this function does not create or verify that constraint, it
171
+ * only builds SQL that assumes it exists. For mysql, `conflictFields` is not
172
+ * part of the generated SQL (`ON DUPLICATE KEY UPDATE` relies on the table's
173
+ * own constraint) — it is used only to re-select the row afterwards, since
174
+ * mysql has no `RETURNING`.
175
+ *
176
+ * @template P - The type of the data to be upserted
177
+ * @template R - The type of the record to be returned
178
+ * @param params - Query parameters including table name, database client, data, conflict target fields, optional fields to update on conflict (defaults to every field in data), and optional returning fields
179
+ * @returns Promise<R> - The inserted or updated record
180
+ *
181
+ * @throws {Error} When table name is not provided
182
+ * @throws {Error} When database client is not provided
183
+ * @throws {Error} When data object is not provided
184
+ * @throws {Error} When conflictFields is not provided or empty
185
+ *
186
+ * @example
187
+ * const record = await upsert({
188
+ * tableName: 'users',
189
+ * dbClient: dbClient,
190
+ * data: { email: 'john.doe@example.com', name: 'John Doe' },
191
+ * conflictFields: ['email'],
192
+ * returning: ['id', 'name', 'email'],
193
+ * })
194
+ */
195
+ export declare const upsert: <P, R>({ tableName, dbClient, data, conflictFields, updateFields, returning, }: QueryParams<R> & {
196
+ data: P;
197
+ conflictFields: string[];
198
+ updateFields?: string[];
199
+ returning?: string[];
200
+ }) => Promise<R>;
118
201
  /**
119
202
  * Updates a record in the specified table
120
203
  *
@@ -164,7 +247,7 @@ export declare const update: <P, R>({ tableName, dbClient, id, data, returning,
164
247
  * tableName: 'users',
165
248
  * dbClient: dbClient,
166
249
  * data: { name: 'John Doe', email: 'john.doe@example.com' },
167
- * where: { status: 'active' },
250
+ * where: { status: { operator: '=', value: 'active' } },
168
251
  * returning: ['id', 'name', 'email'],
169
252
  * })
170
253
  */
@@ -247,7 +330,7 @@ export declare const deleteMany: <T>({ tableName, dbClient, ids, field, permanen
247
330
  * dbClient: dbClient,
248
331
  * select: ['id', 'name', 'email'],
249
332
  * joins: [{ type: 'INNER', table: 'orders', on: 'users.id = orders.user_id' }],
250
- * where: { status: 'active' },
333
+ * where: { status: { operator: '=', value: 'active' } },
251
334
  * groupBy: ['status'],
252
335
  * orderBy: [{ field: 'created_at', direction: 'DESC' }],
253
336
  * limit: 10,
@@ -255,7 +338,39 @@ export declare const deleteMany: <T>({ tableName, dbClient, ids, field, permanen
255
338
  * unaccent: true,
256
339
  * })
257
340
  */
258
- export declare const joins: <T>({ tableName, dbClient, select, joins, where, groupBy, orderBy, limit, offset, unaccent, }: QueryParams<T>) => Promise<T[]>;
341
+ export declare const joins: <T, W = T>({ tableName, dbClient, select, joins, where, groupBy, orderBy, limit, offset, unaccent, }: Omit<QueryParams<T>, "where"> & {
342
+ /**
343
+ * `W` (defaults to `T`) is a separate generic from the result row type `T`:
344
+ * a join's `where` routinely qualifies columns from joined tables (e.g.
345
+ * `'orders.total'`), which are never keys of `T`. `Conditions<T | W>`'s
346
+ * `OR`/`AND`/`JOINS` already accept a dotted qualified-column key
347
+ * structurally (see `QualifiedCondition` in `./types`), so this only
348
+ * matters when `T` is passed explicitly and a qualified column is needed
349
+ * at the top level of `where` (not inside `AND`/`OR`/`JOINS`) — pass `W`
350
+ * explicitly too in that case:
351
+ * `joins<Row, Row & { 'orders.total'?: unknown }>(...)`.
352
+ */
353
+ where?: Conditions<W>;
354
+ }) => Promise<T[]>;
355
+ /**
356
+ * Error thrown by `rawQuery` when the underlying driver query fails.
357
+ *
358
+ * Carries over the original driver error as `cause` plus the common
359
+ * cross-driver fields consumers actually branch on (e.g. Postgres'
360
+ * `code: '23505'` for a unique violation, `detail`, `constraint`; mysql2's
361
+ * `errno`/`sqlState`) — `rawQuery` used to wrap failures in a plain `Error`
362
+ * built from just `error.message`, which discarded all of this and left
363
+ * callers with no way to distinguish a conflict from a syntax error.
364
+ */
365
+ export declare class RawQueryError extends Error {
366
+ cause?: unknown;
367
+ code?: string;
368
+ detail?: string;
369
+ constraint?: string;
370
+ errno?: number;
371
+ sqlState?: string;
372
+ constructor(message: string, cause: unknown);
373
+ }
259
374
  /**
260
375
  * Executa uma query SQL raw diretamente no banco de dados
261
376
  * @param params - Parâmetros da query raw
@@ -341,7 +456,7 @@ export declare const rawQuery: <T = any>({ dbClient, sql, params, }: RawQueryPar
341
456
  * console.error('Transaction failed:', error)
342
457
  * }
343
458
  */
344
- export declare const withTransaction: <T>(dbClient: any, transactionFn: (tx: ITransactionClient) => Promise<T>) => Promise<T>;
459
+ export declare const withTransaction: <T>(dbClient: IDatabaseClient, transactionFn: (tx: ITransactionClient) => Promise<T>) => Promise<T>;
345
460
  /**
346
461
  * Creates a transaction client for manual transaction management
347
462
  * @param dbClient - Database client instance
@@ -371,4 +486,4 @@ export declare const withTransaction: <T>(dbClient: any, transactionFn: (tx: ITr
371
486
  * throw error
372
487
  * }
373
488
  */
374
- export declare const beginTransaction: (dbClient: any) => Promise<ITransactionClient>;
489
+ export declare const beginTransaction: (dbClient: IDatabaseClient) => Promise<ITransactionClient>;