fastify-auto-sqlapi 0.0.5 → 0.1.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 (119) hide show
  1. package/AGENTS.md +15 -2
  2. package/AGENTS_BACKEND.md +144 -20
  3. package/AGENTS_FRONTEND.md +161 -67
  4. package/README.md +47 -26
  5. package/dist/bin/generate-schema.js +9 -26
  6. package/dist/bin/generate-tables.js +7 -30
  7. package/dist/bin/utils.d.ts +28 -0
  8. package/dist/bin/utils.d.ts.map +1 -1
  9. package/dist/bin/utils.js +69 -0
  10. package/dist/index.d.ts +1 -1
  11. package/dist/index.d.ts.map +1 -1
  12. package/dist/lib/cli/load-dependency.d.ts +8 -0
  13. package/dist/lib/cli/load-dependency.d.ts.map +1 -0
  14. package/dist/lib/cli/load-dependency.js +21 -0
  15. package/dist/lib/cli/mysql-introspect.d.ts.map +1 -1
  16. package/dist/lib/cli/mysql-introspect.js +2 -18
  17. package/dist/lib/cli/pg-introspect.d.ts.map +1 -1
  18. package/dist/lib/cli/pg-introspect.js +2 -17
  19. package/dist/lib/db.d.ts +1 -0
  20. package/dist/lib/db.d.ts.map +1 -1
  21. package/dist/lib/db.js +33 -55
  22. package/dist/lib/dialect.d.ts +17 -0
  23. package/dist/lib/dialect.d.ts.map +1 -1
  24. package/dist/lib/dialect.js +32 -0
  25. package/dist/lib/engine/bulk/bulk-upsert.d.ts.map +1 -1
  26. package/dist/lib/engine/bulk/bulk-upsert.js +15 -43
  27. package/dist/lib/engine/rest/delete.d.ts.map +1 -1
  28. package/dist/lib/engine/rest/delete.js +5 -10
  29. package/dist/lib/engine/rest/get.d.ts.map +1 -1
  30. package/dist/lib/engine/rest/get.js +3 -5
  31. package/dist/lib/engine/rest/insert.d.ts.map +1 -1
  32. package/dist/lib/engine/rest/insert.js +11 -31
  33. package/dist/lib/engine/rest/update.d.ts.map +1 -1
  34. package/dist/lib/engine/rest/update.js +13 -46
  35. package/dist/lib/engine/search/search.d.ts.map +1 -1
  36. package/dist/lib/engine/search/search.js +553 -229
  37. package/dist/lib/engine/write-helpers.d.ts +39 -2
  38. package/dist/lib/engine/write-helpers.d.ts.map +1 -1
  39. package/dist/lib/engine/write-helpers.js +55 -12
  40. package/dist/lib/errors.d.ts +11 -0
  41. package/dist/lib/errors.d.ts.map +1 -0
  42. package/dist/lib/errors.js +12 -0
  43. package/dist/lib/schema/bulk-upsert.d.ts.map +1 -1
  44. package/dist/lib/schema/bulk-upsert.js +9 -25
  45. package/dist/lib/schema/helpers.d.ts +28 -1
  46. package/dist/lib/schema/helpers.d.ts.map +1 -1
  47. package/dist/lib/schema/helpers.js +38 -0
  48. package/dist/lib/schema/insert.d.ts.map +1 -1
  49. package/dist/lib/schema/insert.js +9 -19
  50. package/dist/lib/schema/search.d.ts.map +1 -1
  51. package/dist/lib/schema/search.js +138 -50
  52. package/dist/lib/schema/update.d.ts.map +1 -1
  53. package/dist/lib/schema/update.js +9 -25
  54. package/dist/lib/sql-api.d.ts +6 -6
  55. package/dist/lib/sql-api.d.ts.map +1 -1
  56. package/dist/lib/sql-api.js +8 -8
  57. package/dist/lib/table-helpers.d.ts +6 -1
  58. package/dist/lib/table-helpers.d.ts.map +1 -1
  59. package/dist/lib/table-helpers.js +42 -2
  60. package/dist/lib/tenant.d.ts +23 -0
  61. package/dist/lib/tenant.d.ts.map +1 -1
  62. package/dist/lib/tenant.js +57 -9
  63. package/dist/routes/auto/bulk-delete.routes.d.ts.map +1 -1
  64. package/dist/routes/auto/bulk-delete.routes.js +17 -23
  65. package/dist/routes/auto/bulk-upsert.routes.d.ts.map +1 -1
  66. package/dist/routes/auto/bulk-upsert.routes.js +16 -22
  67. package/dist/routes/auto/delete.routes.d.ts.map +1 -1
  68. package/dist/routes/auto/delete.routes.js +18 -24
  69. package/dist/routes/auto/get.routes.d.ts.map +1 -1
  70. package/dist/routes/auto/get.routes.js +16 -23
  71. package/dist/routes/auto/insert.routes.d.ts.map +1 -1
  72. package/dist/routes/auto/insert.routes.js +19 -26
  73. package/dist/routes/auto/route-helpers.d.ts +27 -2
  74. package/dist/routes/auto/route-helpers.d.ts.map +1 -1
  75. package/dist/routes/auto/route-helpers.js +39 -1
  76. package/dist/routes/auto/search.routes.d.ts.map +1 -1
  77. package/dist/routes/auto/search.routes.js +47 -47
  78. package/dist/routes/auto/update.routes.d.ts.map +1 -1
  79. package/dist/routes/auto/update.routes.js +20 -27
  80. package/dist/types/cli.d.ts +22 -0
  81. package/dist/types/cli.d.ts.map +1 -0
  82. package/dist/types/cli.js +1 -0
  83. package/dist/types/computed.d.ts +29 -0
  84. package/dist/types/computed.d.ts.map +1 -0
  85. package/dist/types/computed.js +1 -0
  86. package/dist/types/conditions.d.ts +16 -0
  87. package/dist/types/conditions.d.ts.map +1 -0
  88. package/dist/types/conditions.js +1 -0
  89. package/dist/types/db.d.ts +22 -0
  90. package/dist/types/db.d.ts.map +1 -0
  91. package/dist/types/db.js +1 -0
  92. package/dist/types/dml.d.ts +64 -0
  93. package/dist/types/dml.d.ts.map +1 -0
  94. package/dist/types/dml.js +1 -0
  95. package/dist/types/join.d.ts +44 -0
  96. package/dist/types/join.d.ts.map +1 -0
  97. package/dist/types/join.js +1 -0
  98. package/dist/types/plugin.d.ts +20 -0
  99. package/dist/types/plugin.d.ts.map +1 -0
  100. package/dist/types/plugin.js +1 -0
  101. package/dist/types/schema.d.ts +10 -0
  102. package/dist/types/schema.d.ts.map +1 -0
  103. package/dist/types/schema.js +1 -0
  104. package/dist/types/search.d.ts +45 -0
  105. package/dist/types/search.d.ts.map +1 -0
  106. package/dist/types/search.js +1 -0
  107. package/dist/types/table.d.ts +52 -0
  108. package/dist/types/table.d.ts.map +1 -0
  109. package/dist/types/table.js +7 -0
  110. package/dist/types/tenant.d.ts +19 -0
  111. package/dist/types/tenant.d.ts.map +1 -0
  112. package/dist/types/tenant.js +1 -0
  113. package/dist/types/validation.d.ts +21 -0
  114. package/dist/types/validation.d.ts.map +1 -0
  115. package/dist/types/validation.js +1 -0
  116. package/dist/types.d.ts +12 -277
  117. package/dist/types.d.ts.map +1 -1
  118. package/dist/types.js +12 -7
  119. package/package.json +3 -3
@@ -5,26 +5,13 @@ import { loadConfig } from '../lib/cli/config.js';
5
5
  import { buildConnectionString, introspectTables } from '../lib/cli/pg-introspect.js';
6
6
  import { buildMysqlConnectionConfig, introspectMysqlTables } from '../lib/cli/mysql-introspect.js';
7
7
  import { buildTableMap, generateSchemaFile } from '../lib/cli/schema-codegen.js';
8
- import { loadEnvFile, CONSOLE_COLORS, display, displayAsTableRow, error } from './utils.js';
9
- function parseCliArgs() {
10
- const args = process.argv.slice(2);
11
- const result = {};
12
- for (let i = 0; i < args.length; i++) {
13
- if (args[i] === '--output' && i + 1 < args.length) {
14
- result.output = args[++i];
15
- }
16
- else if (args[i] === '--tables' && i + 1 < args.length) {
17
- result.tables = args[++i].split(',').map((t) => t.trim()).filter(Boolean);
18
- }
19
- else if (args[i] === '--dialect' && i + 1 < args.length) {
20
- result.dialect = args[++i];
21
- }
22
- }
23
- return result;
24
- }
25
- async function main() {
26
- display('++++++ fastify-auto-sqlapi: generating schemas ++++++', CONSOLE_COLORS.yellow);
27
- const cliArgs = parseCliArgs();
8
+ import { loadEnvFile, CONSOLE_COLORS, display, displayAsTableRow, parseArgs, runCli } from './utils.js';
9
+ await runCli('fastify-auto-sqlapi: generating schemas', async () => {
10
+ const cliArgs = parseArgs({
11
+ output: { type: 'value' },
12
+ tables: { type: 'list' },
13
+ dialect: { type: 'value' },
14
+ });
28
15
  const config = await loadConfig();
29
16
  loadEnvFile(config.envFile);
30
17
  const dialect = (cliArgs.dialect || config.dialect || 'postgres');
@@ -51,7 +38,7 @@ async function main() {
51
38
  const tableMap = buildTableMap(rows);
52
39
  // Filter tables if --tables flag is provided
53
40
  const tableNames = cliArgs.tables;
54
- if (tableNames) {
41
+ if (tableNames.length) {
55
42
  for (const schemaName of Object.keys(tableMap)) {
56
43
  if (!tableNames.includes(tableMap[schemaName].name)) {
57
44
  delete tableMap[schemaName];
@@ -94,7 +81,7 @@ async function main() {
94
81
  }
95
82
  }
96
83
  // Remove orphan Schema*.ts files (only when generating all tables)
97
- if (!tableNames) {
84
+ if (!tableNames.length) {
98
85
  const existingFiles = fs.readdirSync(schemasDir);
99
86
  for (const file of existingFiles) {
100
87
  if (file.startsWith('Schema') && file.endsWith('.ts') && !generatedFiles.has(file)) {
@@ -115,8 +102,4 @@ async function main() {
115
102
  if (!somethingTouched) {
116
103
  display('All schemas are already up to date.', CONSOLE_COLORS.magenta);
117
104
  }
118
- }
119
- main().catch((e) => {
120
- error(`Error: ${e.message}`);
121
- process.exit(1);
122
105
  });
@@ -3,7 +3,7 @@ import * as fs from 'node:fs';
3
3
  import * as path from 'node:path';
4
4
  import { loadConfig } from '../lib/cli/config.js';
5
5
  import { parseSchemaFile, generateSingleTableFile, generateDbTablesIndex } from '../lib/cli/tables-codegen.js';
6
- import { loadEnvFile, CONSOLE_COLORS, display, displayAsTableRow, error } from './utils.js';
6
+ import { loadEnvFile, CONSOLE_COLORS, display, displayAsTableRow, error, parseArgs, runCli } from './utils.js';
7
7
  function printHelp() {
8
8
  console.log('');
9
9
  display('Usage:', CONSOLE_COLORS.yellow);
@@ -22,35 +22,16 @@ function printHelp() {
22
22
  display('Table*.ts files are only created if they do not already exist.', CONSOLE_COLORS.magenta);
23
23
  display('dbTables.ts is always regenerated to include all schemas.', CONSOLE_COLORS.magenta);
24
24
  }
25
- function parseCliArgs() {
26
- const args = process.argv.slice(2);
27
- const result = { all: false, tables: [] };
28
- for (let i = 0; i < args.length; i++) {
29
- if (args[i] === '--output' && i + 1 < args.length) {
30
- result.output = args[++i];
31
- }
32
- else if (args[i] === '--all') {
33
- result.all = true;
34
- }
35
- else if (!args[i].startsWith('--')) {
36
- // Split by comma to support both "a b" and "a,b" and "a, b"
37
- for (const part of args[i].split(',')) {
38
- const trimmed = part.trim();
39
- if (trimmed)
40
- result.tables.push(trimmed);
41
- }
42
- }
43
- }
44
- return result;
45
- }
46
- async function main() {
47
- const cliArgs = parseCliArgs();
25
+ await runCli('fastify-auto-sqlapi: generating tables template', async () => {
26
+ const cliArgs = parseArgs({
27
+ output: { type: 'value' },
28
+ all: { type: 'flag' },
29
+ tables: { type: 'list' },
30
+ });
48
31
  if (!cliArgs.all && cliArgs.tables.length === 0) {
49
- display('++++++ fastify-auto-sqlapi: generate tables template ++++++', CONSOLE_COLORS.yellow);
50
32
  printHelp();
51
33
  process.exit(1);
52
34
  }
53
- display('++++++ fastify-auto-sqlapi: generating tables template ++++++', CONSOLE_COLORS.yellow);
54
35
  const config = await loadConfig();
55
36
  loadEnvFile(config.envFile);
56
37
  const outputDir = path.resolve(process.cwd(), cliArgs.output || config.outputDir);
@@ -128,8 +109,4 @@ async function main() {
128
109
  }
129
110
  console.log('');
130
111
  display('Edit the generated Table*.ts files to customize your table configuration.', CONSOLE_COLORS.magenta);
131
- }
132
- main().catch((e) => {
133
- error(`Error: ${e.message}`);
134
- process.exit(1);
135
112
  });
@@ -15,4 +15,32 @@ export declare const CONSOLE_COLORS: {
15
15
  export declare function displayAsTableRow(pref: string, value: string, distance: number, color?: number): void;
16
16
  export declare function display(text: string, color?: number): void;
17
17
  export declare function error(text: string): void;
18
+ type ArgSpec = {
19
+ type: 'value';
20
+ } | {
21
+ type: 'flag';
22
+ } | {
23
+ type: 'list';
24
+ };
25
+ type ArgValue<S extends ArgSpec> = S['type'] extends 'flag' ? boolean : S['type'] extends 'list' ? string[] : string | undefined;
26
+ /**
27
+ * Tiny declarative argv parser for the bundled CLIs.
28
+ * - `value` → recognized as `--name X`, last value wins
29
+ * - `flag` → recognized as `--name`, default false
30
+ * - `list` → comma-separated `--name a,b,c`; can also accept positional comma-/space- separated tokens
31
+ *
32
+ * Positional tokens (non-`--`) are accumulated into `positional`. List specs auto-merge those tokens
33
+ * (split on ',') into the list as well, supporting both "a b" and "a,b" CLI shapes.
34
+ */
35
+ export declare function parseArgs<S extends Record<string, ArgSpec>>(spec: S, argv?: string[]): {
36
+ [K in keyof S]: ArgValue<S[K]>;
37
+ } & {
38
+ positional: string[];
39
+ };
40
+ /**
41
+ * CLI entry-point wrapper: prints a header, awaits `fn`, and turns thrown errors into a red message
42
+ * with a non-zero exit code (without aborting the process synchronously, so unfinished stdout flushes).
43
+ */
44
+ export declare function runCli(name: string, fn: () => Promise<void>): Promise<void>;
45
+ export {};
18
46
  //# sourceMappingURL=utils.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/bin/utils.ts"],"names":[],"mappings":"AAGA;;;GAGG;AACH,wBAAgB,WAAW,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAqBlD;AAED,eAAO,MAAM,cAAc;;;;;;;;CAQjB,CAAC;AAEX,wBAAgB,iBAAiB,CAC/B,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,MAAM,EAChB,KAAK,GAAE,MAAU,GAChB,IAAI,CAQN;AAED,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,GAAE,MAAU,GAAG,IAAI,CAE7D;AAED,wBAAgB,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAExC"}
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/bin/utils.ts"],"names":[],"mappings":"AAGA;;;GAGG;AACH,wBAAgB,WAAW,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAqBlD;AAED,eAAO,MAAM,cAAc;;;;;;;;CAQjB,CAAC;AAEX,wBAAgB,iBAAiB,CAC/B,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,MAAM,EAChB,KAAK,GAAE,MAAU,GAChB,IAAI,CAQN;AAED,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,GAAE,MAAU,GAAG,IAAI,CAE7D;AAED,wBAAgB,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAExC;AAED,KAAK,OAAO,GACR;IAAE,IAAI,EAAE,OAAO,CAAA;CAAE,GACjB;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAChB;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAErB,KAAK,QAAQ,CAAC,CAAC,SAAS,OAAO,IAC7B,CAAC,CAAC,MAAM,CAAC,SAAS,MAAM,GAAG,OAAO,GAClC,CAAC,CAAC,MAAM,CAAC,SAAS,MAAM,GAAG,MAAM,EAAE,GACnC,MAAM,GAAG,SAAS,CAAC;AAErB;;;;;;;;GAQG;AACH,wBAAgB,SAAS,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACzD,IAAI,EAAE,CAAC,EACP,IAAI,WAAwB,GAC3B;KAAG,CAAC,IAAI,MAAM,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;CAAE,GAAG;IAAE,UAAU,EAAE,MAAM,EAAE,CAAA;CAAE,CAyC/D;AAED;;;GAGG;AACH,wBAAsB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAQjF"}
package/dist/bin/utils.js CHANGED
@@ -52,3 +52,72 @@ export function display(text, color = 0) {
52
52
  export function error(text) {
53
53
  display(text, CONSOLE_COLORS.red);
54
54
  }
55
+ /**
56
+ * Tiny declarative argv parser for the bundled CLIs.
57
+ * - `value` → recognized as `--name X`, last value wins
58
+ * - `flag` → recognized as `--name`, default false
59
+ * - `list` → comma-separated `--name a,b,c`; can also accept positional comma-/space- separated tokens
60
+ *
61
+ * Positional tokens (non-`--`) are accumulated into `positional`. List specs auto-merge those tokens
62
+ * (split on ',') into the list as well, supporting both "a b" and "a,b" CLI shapes.
63
+ */
64
+ export function parseArgs(spec, argv = process.argv.slice(2)) {
65
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
66
+ const out = { positional: [] };
67
+ for (const [name, def] of Object.entries(spec)) {
68
+ out[name] = def.type === 'flag' ? false : def.type === 'list' ? [] : undefined;
69
+ }
70
+ for (let i = 0; i < argv.length; i++) {
71
+ const token = argv[i];
72
+ if (token.startsWith('--')) {
73
+ const name = token.slice(2);
74
+ const def = spec[name];
75
+ if (!def)
76
+ continue;
77
+ if (def.type === 'flag') {
78
+ out[name] = true;
79
+ }
80
+ else if (def.type === 'value' && i + 1 < argv.length) {
81
+ out[name] = argv[++i];
82
+ }
83
+ else if (def.type === 'list' && i + 1 < argv.length) {
84
+ const next = argv[++i];
85
+ for (const part of next.split(',')) {
86
+ const t = part.trim();
87
+ if (t)
88
+ out[name].push(t);
89
+ }
90
+ }
91
+ }
92
+ else {
93
+ out.positional.push(token);
94
+ // Auto-distribute positional values into the (single) list spec, if any
95
+ for (const [n, def] of Object.entries(spec)) {
96
+ if (def.type === 'list') {
97
+ for (const part of token.split(',')) {
98
+ const t = part.trim();
99
+ if (t)
100
+ out[n].push(t);
101
+ }
102
+ break;
103
+ }
104
+ }
105
+ }
106
+ }
107
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
108
+ return out;
109
+ }
110
+ /**
111
+ * CLI entry-point wrapper: prints a header, awaits `fn`, and turns thrown errors into a red message
112
+ * with a non-zero exit code (without aborting the process synchronously, so unfinished stdout flushes).
113
+ */
114
+ export async function runCli(name, fn) {
115
+ display(`++++++ ${name} ++++++`, CONSOLE_COLORS.yellow);
116
+ try {
117
+ await fn();
118
+ }
119
+ catch (e) {
120
+ error(`Error: ${e.message ?? String(e)}`);
121
+ process.exitCode = 1;
122
+ }
123
+ }
package/dist/index.d.ts CHANGED
@@ -30,5 +30,5 @@ export { BulkDeleteTableBody, BulkDeleteTableResponse } from './lib/schema/bulk-
30
30
  export { default as bulkDeleteRoutes } from './routes/auto/bulk-delete.routes.js';
31
31
  export { Type, type Static } from '@sinclair/typebox';
32
32
  export { ConditionBuilder, Expression } from 'node-condition-builder';
33
- export type { SqlApiConfig, ColumnInfo, TableMap, Queryable, SqlResult, DbRecord, DbRecordValue, SelectOptions, SchemaDefinition, JoinDefinition, ITable, DbTables, SqlApiPluginOptions, SwaggerOptions, SearchParams, SearchResult, Paginator, PaginationResult, TableFilterFn, ExtendedConditionFn, FilterRecord, AggregationRequest, JoinGroupRequest, GetParams, GetResult, DeleteParams, DeleteResult, InsertParams, InsertResult, BulkUpsertItem, BulkUpsertParams, BulkUpsertResult, BulkDeleteParams, BulkDeleteResult, ConditionMethod, SearchCondition, UpdateParams, UpdateResult, TenantId, TenantScope, TenantScopeDirect, TenantScopeIndirect, TenantContext, ValidationError, ValidatorFn, BulkValidatorItem, BulkValidatorFn, } from './types.js';
33
+ export type { SqlApiConfig, ColumnInfo, TableMap, Queryable, SqlResult, DbRecord, DbRecordValue, SelectOptions, SchemaDefinition, JoinDefinition, ITable, DbTables, SqlApiPluginOptions, SwaggerOptions, SearchParams, SearchResult, Paginator, PaginationResult, TableFilterFn, ExtendedConditionFn, FilterRecord, AggregationRequest, JoinGroupRequest, ComputedFieldFn, ComputedFieldContext, ComputedFieldExpr, GetParams, GetResult, DeleteParams, DeleteResult, InsertParams, InsertResult, BulkUpsertItem, BulkUpsertParams, BulkUpsertResult, BulkDeleteParams, BulkDeleteResult, ConditionMethod, SearchCondition, UpdateParams, UpdateResult, TenantId, TenantScope, TenantScopeDirect, TenantScopeIndirect, TenantContext, ValidationError, ValidatorFn, BulkValidatorItem, BulkValidatorFn, } from './types.js';
34
34
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EACL,WAAW,EACX,YAAY,EACZ,YAAY,EACZ,eAAe,EACf,eAAe,GAChB,MAAM,iBAAiB,CAAC;AAGzB,OAAO,EAAE,WAAW,EAAE,iBAAiB,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAG1E,OAAO,EAAE,UAAU,EAAE,KAAK,UAAU,EAAE,KAAK,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAGjF,OAAO,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAC3D,OAAO,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AAGjE,OAAO,EAAE,eAAe,EAAE,WAAW,EAAE,aAAa,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAGxH,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAGhD,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AACxD,YAAY,EAAE,aAAa,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAIlH,OAAO,EAAE,OAAO,IAAI,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAGvE,OAAO,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAGtD,OAAO,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAC7D,OAAO,EAAE,mBAAmB,EAAE,sBAAsB,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;AAC1G,OAAO,EAAE,OAAO,IAAI,YAAY,EAAE,MAAM,gCAAgC,CAAC;AAGzE,OAAO,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAC3D,OAAO,EAAE,eAAe,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;AAC9E,OAAO,EAAE,OAAO,IAAI,YAAY,EAAE,MAAM,gCAAgC,CAAC;AAGzE,OAAO,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AACrD,OAAO,EAAE,OAAO,IAAI,SAAS,EAAE,MAAM,6BAA6B,CAAC;AAGnE,OAAO,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAC3D,OAAO,EAAE,OAAO,IAAI,YAAY,EAAE,MAAM,gCAAgC,CAAC;AAGzE,OAAO,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAC3D,OAAO,EAAE,eAAe,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;AAC9E,OAAO,EAAE,OAAO,IAAI,YAAY,EAAE,MAAM,gCAAgC,CAAC;AAGzE,OAAO,EAAE,gBAAgB,EAAE,MAAM,kCAAkC,CAAC;AACpE,OAAO,EAAE,mBAAmB,EAAE,uBAAuB,EAAE,MAAM,6BAA6B,CAAC;AAC3F,OAAO,EAAE,OAAO,IAAI,gBAAgB,EAAE,MAAM,qCAAqC,CAAC;AAGlF,OAAO,EAAE,gBAAgB,EAAE,MAAM,kCAAkC,CAAC;AACpE,OAAO,EAAE,mBAAmB,EAAE,uBAAuB,EAAE,MAAM,6BAA6B,CAAC;AAC3F,OAAO,EAAE,OAAO,IAAI,gBAAgB,EAAE,MAAM,qCAAqC,CAAC;AAGlF,OAAO,EAAE,IAAI,EAAE,KAAK,MAAM,EAAE,MAAM,mBAAmB,CAAC;AACtD,OAAO,EAAE,gBAAgB,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAGtE,YAAY,EACV,YAAY,EACZ,UAAU,EACV,QAAQ,EACR,SAAS,EACT,SAAS,EACT,QAAQ,EACR,aAAa,EACb,aAAa,EACb,gBAAgB,EAChB,cAAc,EACd,MAAM,EACN,QAAQ,EACR,mBAAmB,EACnB,cAAc,EACd,YAAY,EACZ,YAAY,EACZ,SAAS,EACT,gBAAgB,EAChB,aAAa,EACb,mBAAmB,EACnB,YAAY,EACZ,kBAAkB,EAClB,gBAAgB,EAChB,SAAS,EACT,SAAS,EACT,YAAY,EACZ,YAAY,EACZ,YAAY,EACZ,YAAY,EACZ,cAAc,EACd,gBAAgB,EAChB,gBAAgB,EAChB,gBAAgB,EAChB,gBAAgB,EAChB,eAAe,EACf,eAAe,EACf,YAAY,EACZ,YAAY,EACZ,QAAQ,EACR,WAAW,EACX,iBAAiB,EACjB,mBAAmB,EACnB,aAAa,EACb,eAAe,EACf,WAAW,EACX,iBAAiB,EACjB,eAAe,GAChB,MAAM,YAAY,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Load an optional peer dependency by trying the consumer's CWD first
3
+ * (so it works when installed via `npm install`/`npm link`), then falling
4
+ * back to the plugin's own `node_modules` (so it works in the plugin's own tests).
5
+ * Throws a friendly error if neither succeeds.
6
+ */
7
+ export declare function loadOptionalDependency<T = unknown>(modulePath: string, installHint: string): T;
8
+ //# sourceMappingURL=load-dependency.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"load-dependency.d.ts","sourceRoot":"","sources":["../../../src/lib/cli/load-dependency.ts"],"names":[],"mappings":"AAEA;;;;;GAKG;AACH,wBAAgB,sBAAsB,CAAC,CAAC,GAAG,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,CAAC,CAW9F"}
@@ -0,0 +1,21 @@
1
+ import { createRequire } from 'node:module';
2
+ /**
3
+ * Load an optional peer dependency by trying the consumer's CWD first
4
+ * (so it works when installed via `npm install`/`npm link`), then falling
5
+ * back to the plugin's own `node_modules` (so it works in the plugin's own tests).
6
+ * Throws a friendly error if neither succeeds.
7
+ */
8
+ export function loadOptionalDependency(modulePath, installHint) {
9
+ try {
10
+ return createRequire(process.cwd() + '/noop.js')(modulePath);
11
+ }
12
+ catch {
13
+ /* fall through to local require */
14
+ }
15
+ try {
16
+ return createRequire(import.meta.url)(modulePath);
17
+ }
18
+ catch {
19
+ throw new Error(`${modulePath} is required. Install it with: ${installHint}`);
20
+ }
21
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"mysql-introspect.d.ts","sourceRoot":"","sources":["../../../src/lib/cli/mysql-introspect.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAEjD,wBAAgB,0BAA0B,IAAI;IAC5C,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;CAClB,CAmBA;AAkDD,wBAAsB,qBAAqB,CACzC,gBAAgB,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,EAClG,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,UAAU,EAAE,CAAC,CAuCvB"}
1
+ {"version":3,"file":"mysql-introspect.d.ts","sourceRoot":"","sources":["../../../src/lib/cli/mysql-introspect.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAEjD,wBAAgB,0BAA0B,IAAI;IAC5C,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;CAClB,CAmBA;AAkDD,wBAAsB,qBAAqB,CACzC,gBAAgB,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,EAClG,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,UAAU,EAAE,CAAC,CAyBvB"}
@@ -1,4 +1,4 @@
1
- import { createRequire } from 'node:module';
1
+ import { loadOptionalDependency } from './load-dependency.js';
2
2
  export function buildMysqlConnectionConfig() {
3
3
  if (process.env.DATABASE_URL) {
4
4
  const url = new URL(process.env.DATABASE_URL);
@@ -66,24 +66,8 @@ function mapMysqlType(dataType) {
66
66
  }
67
67
  }
68
68
  export async function introspectMysqlTables(connectionConfig, schema) {
69
- // Dynamic import: mysql2 is optional peer dependency.
70
- // Try the consumer's CWD first (so it works when installed), then fall back to
71
- // the plugin's own node_modules (so it works in the plugin's own tests).
72
69
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
73
- let mysql2;
74
- try {
75
- const require = createRequire(process.cwd() + '/noop.js');
76
- mysql2 = require('mysql2/promise');
77
- }
78
- catch {
79
- try {
80
- const require = createRequire(import.meta.url);
81
- mysql2 = require('mysql2/promise');
82
- }
83
- catch {
84
- throw new Error('mysql2 is required for MySQL/MariaDB introspection. Install it with: npm install mysql2');
85
- }
86
- }
70
+ const mysql2 = loadOptionalDependency('mysql2/promise', 'npm install mysql2');
87
71
  const connection = await mysql2.createConnection(connectionConfig);
88
72
  try {
89
73
  const [rows] = await connection.query(`SELECT table_name, column_name, data_type, column_default, is_nullable
@@ -1 +1 @@
1
- {"version":3,"file":"pg-introspect.d.ts","sourceRoot":"","sources":["../../../src/lib/cli/pg-introspect.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAEjD,wBAAgB,qBAAqB,IAAI,MAAM,CAY9C;AAED,wBAAsB,gBAAgB,CACpC,gBAAgB,EAAE,MAAM,EACxB,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,UAAU,EAAE,CAAC,CAkCvB"}
1
+ {"version":3,"file":"pg-introspect.d.ts","sourceRoot":"","sources":["../../../src/lib/cli/pg-introspect.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAEjD,wBAAgB,qBAAqB,IAAI,MAAM,CAY9C;AAED,wBAAsB,gBAAgB,CACpC,gBAAgB,EAAE,MAAM,EACxB,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,UAAU,EAAE,CAAC,CAqBvB"}
@@ -1,4 +1,4 @@
1
- import { createRequire } from 'node:module';
1
+ import { loadOptionalDependency } from './load-dependency.js';
2
2
  export function buildConnectionString() {
3
3
  if (process.env.DATABASE_URL) {
4
4
  return process.env.DATABASE_URL;
@@ -11,23 +11,8 @@ export function buildConnectionString() {
11
11
  return `postgres://${user}:${password}@${host}:${port}/${db}`;
12
12
  }
13
13
  export async function introspectTables(connectionString, schema) {
14
- // Dynamic require from cwd so it works with npm link
15
14
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
16
- let pg;
17
- try {
18
- const require = createRequire(process.cwd() + '/noop.js');
19
- pg = require('pg');
20
- }
21
- catch {
22
- try {
23
- // Fallback: resolve from the package's own node_modules
24
- const require = createRequire(import.meta.url);
25
- pg = require('pg');
26
- }
27
- catch {
28
- throw new Error('pg is required for PostgreSQL introspection. Install it with: npm install pg');
29
- }
30
- }
15
+ const pg = loadOptionalDependency('pg', 'npm install pg');
31
16
  const client = new pg.Client({ connectionString });
32
17
  try {
33
18
  await client.connect();
package/dist/lib/db.d.ts CHANGED
@@ -13,6 +13,7 @@ export declare class QueryClient {
13
13
  get qi(): (id: string) => string;
14
14
  get ph(): (i: number) => string;
15
15
  get dialectName(): string;
16
+ get dateTrunc(): SqlDialect['dateTrunc'];
16
17
  setDebug(mode: boolean): void;
17
18
  expression(value: string): Expression;
18
19
  query<T = Record<string, unknown>>(text: string, values?: unknown[]): Promise<SqlResult<T>>;
@@ -1 +1 @@
1
- {"version":3,"file":"db.d.ts","sourceRoot":"","sources":["../../src/lib/db.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AACpD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC/D,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AACjF,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAK/C,sCAAsC;AACtC,eAAO,MAAM,WAAW,GAAI,GAAG,MAAM,WAA0B,CAAC;AAEhE,qBAAa,WAAW;;IACtB,OAAO,CAAC,MAAM,CAAY;IAC1B,OAAO,CAAC,OAAO,CAAa;IAC5B,OAAO,CAAC,KAAK,CAAS;gBAEV,MAAM,EAAE,SAAS,EAAE,OAAO,CAAC,EAAE,UAAU;IAKnD,IAAI,EAAE,IAAI,CAAC,EAAE,EAAE,MAAM,KAAK,MAAM,CAE/B;IAED,IAAI,EAAE,IAAI,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,CAE9B;IAED,IAAI,WAAW,IAAI,MAAM,CAExB;IAED,QAAQ,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI;IAI7B,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,UAAU;IAIrC,KAAK,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/B,IAAI,EAAE,MAAM,EACZ,MAAM,CAAC,EAAE,OAAO,EAAE,GACjB,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;IA6FlB,MAAM,CACV,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,QAAQ,EAChB,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,GACvB,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAoB7B,cAAc,CAClB,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,QAAQ,EAChB,YAAY,EAAE,MAAM,EAAE,EACtB,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,GACvB,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAuB7B,UAAU,CACd,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,QAAQ,EAAE,EACnB,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,EACxB,SAAS,SAAqB,GAC7B,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC;IA+B/B,kBAAkB,CACtB,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,QAAQ,EAAE,EACnB,YAAY,EAAE,MAAM,EAAE,EACtB,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,EACxB,SAAS,SAAqB,GAC7B,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC;IAiC/B,MAAM,CACV,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,QAAQ,EAChB,KAAK,EAAE,QAAQ,EACf,cAAc,CAAC,EAAE,gBAAgB,GAChC,OAAO,CAAC,MAAM,CAAC;IAuBZ,MAAM,CACV,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,QAAQ,GACd,OAAO,CAAC,MAAM,CAAC;IAYZ,MAAM,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,EACxC,SAAS,EACT,OAAa,EACb,KAAK,EACL,MAAM,EACN,KAAY,EACZ,OAAY,EACZ,KAAU,EACV,QAAgB,GACjB,EAAE,aAAa,GAAG,OAAO,CAAC,CAAC,EAAE,CAAC;CAchC;AAED,wBAAgB,iBAAiB,CAC/B,IAAI,EAAE,SAAS,EACf,WAAW,CAAC,EAAE,MAAM,GACnB,WAAW,CAGb"}
1
+ {"version":3,"file":"db.d.ts","sourceRoot":"","sources":["../../src/lib/db.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AACpD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC/D,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AACjF,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAQ/C,sCAAsC;AACtC,eAAO,MAAM,WAAW,GAAI,GAAG,MAAM,WAA0B,CAAC;AAEhE,qBAAa,WAAW;;IACtB,OAAO,CAAC,MAAM,CAAY;IAC1B,OAAO,CAAC,OAAO,CAAa;IAC5B,OAAO,CAAC,KAAK,CAAS;gBAEV,MAAM,EAAE,SAAS,EAAE,OAAO,CAAC,EAAE,UAAU;IAKnD,IAAI,EAAE,IAAI,CAAC,EAAE,EAAE,MAAM,KAAK,MAAM,CAE/B;IAED,IAAI,EAAE,IAAI,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,CAE9B;IAED,IAAI,WAAW,IAAI,MAAM,CAExB;IAED,IAAI,SAAS,IAAI,UAAU,CAAC,WAAW,CAAC,CAEvC;IAED,QAAQ,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI;IAI7B,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,UAAU;IAIrC,KAAK,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/B,IAAI,EAAE,MAAM,EACZ,MAAM,CAAC,EAAE,OAAO,EAAE,GACjB,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;IAmGlB,MAAM,CACV,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,QAAQ,EAChB,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,GACvB,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAgB7B,cAAc,CAClB,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,QAAQ,EAChB,YAAY,EAAE,MAAM,EAAE,EACtB,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,GACvB,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAkB7B,UAAU,CACd,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,QAAQ,EAAE,EACnB,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,EACxB,SAAS,SAAqB,GAC7B,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC;IAoB/B,kBAAkB,CACtB,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,QAAQ,EAAE,EACnB,YAAY,EAAE,MAAM,EAAE,EACtB,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,EACxB,SAAS,SAAqB,GAC7B,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC;IAuB/B,MAAM,CACV,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,QAAQ,EAChB,KAAK,EAAE,QAAQ,EACf,cAAc,CAAC,EAAE,gBAAgB,GAChC,OAAO,CAAC,MAAM,CAAC;IAuBZ,MAAM,CACV,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,QAAQ,GACd,OAAO,CAAC,MAAM,CAAC;IAYZ,MAAM,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,EACxC,SAAS,EACT,OAAa,EACb,KAAK,EACL,MAAM,EACN,KAAY,EACZ,OAAY,EACZ,KAAU,EACV,QAAgB,GACjB,EAAE,aAAa,GAAG,OAAO,CAAC,CAAC,EAAE,CAAC;CAchC;AAED,wBAAgB,iBAAiB,CAC/B,IAAI,EAAE,SAAS,EACf,WAAW,CAAC,EAAE,MAAM,GACnB,WAAW,CAGb"}
package/dist/lib/db.js CHANGED
@@ -1,5 +1,8 @@
1
1
  import { Expression } from 'node-condition-builder';
2
2
  import { getDialect } from './dialect.js';
3
+ // Default chunk size for bulk INSERT/UPSERT. Picked to keep parameter count well under
4
+ // PostgreSQL's hard limit of 65535 bind parameters per statement (≈ 130 columns × 500 rows).
5
+ // Override per-call via the `chunkSize` argument when working with extremely wide tables.
3
6
  const DEFAULT_CHUNK_SIZE = 500;
4
7
  /** @deprecated Use db.qi() instead */
5
8
  export const escapeIdent = (f) => f.replace(/"/g, '""');
@@ -20,6 +23,9 @@ export class QueryClient {
20
23
  get dialectName() {
21
24
  return this.dialect.name;
22
25
  }
26
+ get dateTrunc() {
27
+ return this.dialect.dateTrunc.bind(this.dialect);
28
+ }
23
29
  setDebug(mode) {
24
30
  this.debug = mode;
25
31
  }
@@ -73,26 +79,14 @@ export class QueryClient {
73
79
  }
74
80
  return { values, rows: rowPlaceholders.join(', ') };
75
81
  }
76
- // Build the update SET for upsert: dialect-specific
77
- #upsertUpdateSet(fields, conflictKeys) {
78
- const updateCols = fields.filter((f) => !conflictKeys.includes(f));
79
- if (!updateCols.length)
80
- return '';
81
- if (this.dialect.name === 'postgres') {
82
- return updateCols
83
- .map((f) => `${this.dialect.qi(f)} = EXCLUDED.${this.dialect.qi(f)}`)
84
- .join(', ');
85
- }
86
- // MySQL/MariaDB: VALUES(col) syntax
87
- return updateCols
88
- .map((f) => `${this.dialect.qi(f)} = VALUES(${this.dialect.qi(f)})`)
89
- .join(', ');
90
- }
91
82
  #quotedPkCols(pkCol) {
92
83
  const cols = Array.isArray(pkCol) ? pkCol : [pkCol];
93
84
  return cols.map((c) => this.dialect.qi(c)).join(', ');
94
85
  }
95
- #mysqlPkResult(pkCol, record, insertId) {
86
+ #insertHead(table, fields) {
87
+ return `INSERT INTO ${this.dialect.qi(table)} (${this.#q(fields)})`;
88
+ }
89
+ #mysqlPkRow(pkCol, record, insertId) {
96
90
  const cols = Array.isArray(pkCol) ? pkCol : [pkCol];
97
91
  const result = {};
98
92
  for (const col of cols) {
@@ -106,57 +100,51 @@ export class QueryClient {
106
100
  return { [pkCol]: insertId };
107
101
  return result;
108
102
  }
103
+ /**
104
+ * Single source of truth for "extract PK rows from an insert/upsert result". When the
105
+ * dialect supports RETURNING (postgres/mariadb), returns `result.rows` directly. Otherwise
106
+ * (mysql) synthesizes PK rows from `insertId` + the input records (assumes
107
+ * `auto_increment_increment=1` for multi-row inserts).
108
+ */
109
+ #extractPkRows(result, pkCol, inputs) {
110
+ if (this.dialect.supportsReturning)
111
+ return result.rows;
112
+ const firstId = result.insertId;
113
+ return inputs.map((rec, j) => this.#mysqlPkRow(pkCol, rec, typeof firstId === 'number' ? firstId + j : undefined));
114
+ }
109
115
  async insert(table, record, pkCol) {
110
116
  const values = [];
111
117
  const { fields, placeholders } = this.#params(record, values);
112
118
  if (!fields.length)
113
119
  throw new Error('Cannot execute empty insert');
114
120
  const returning = this.dialect.returningPk(this.#quotedPkCols(pkCol));
115
- const result = await this.query(`INSERT INTO ${this.dialect.qi(table)} (${this.#q(fields)})
121
+ const result = await this.query(`${this.#insertHead(table, fields)}
116
122
  VALUES (${placeholders.join(', ')})${returning}`, values);
117
- if (this.dialect.supportsReturning) {
118
- return result.rows[0];
119
- }
120
- return this.#mysqlPkResult(pkCol, record, result.insertId);
123
+ return this.#extractPkRows(result, pkCol, [record])[0];
121
124
  }
122
125
  async insertOrUpdate(table, record, conflictKeys, pkCol) {
123
126
  const values = [];
124
127
  const { fields, placeholders } = this.#params(record, values);
125
128
  if (!fields.length)
126
129
  throw new Error('Cannot execute empty insert');
127
- const updateSet = this.#upsertUpdateSet(fields, conflictKeys);
128
- const upsertClause = this.dialect.upsertSql(this.#q(conflictKeys), updateSet);
130
+ const upsertClause = this.dialect.upsertSql(this.#q(conflictKeys), this.dialect.upsertUpdateSet(fields, conflictKeys));
129
131
  const returning = this.dialect.returningPk(this.#quotedPkCols(pkCol));
130
- const result = await this.query(`INSERT INTO ${this.dialect.qi(table)} (${this.#q(fields)})
132
+ const result = await this.query(`${this.#insertHead(table, fields)}
131
133
  VALUES (${placeholders.join(', ')})
132
134
  ${upsertClause}${returning}`, values);
133
- if (this.dialect.supportsReturning) {
134
- return result.rows[0];
135
- }
136
- return this.#mysqlPkResult(pkCol, record, result.insertId);
135
+ return this.#extractPkRows(result, pkCol, [record])[0];
137
136
  }
138
137
  async bulkInsert(table, records, pkCol, chunkSize = DEFAULT_CHUNK_SIZE) {
139
138
  if (!records.length)
140
139
  return [];
141
140
  const fields = Object.keys(records[0]);
142
- const results = [];
143
141
  const returning = this.dialect.returningPk(this.#quotedPkCols(pkCol));
142
+ const results = [];
144
143
  for (let i = 0; i < records.length; i += chunkSize) {
145
144
  const chunk = records.slice(i, i + chunkSize);
146
145
  const { values, rows } = this.#bulkValues(fields, chunk);
147
- const result = await this.query(`INSERT INTO ${this.dialect.qi(table)} (${this.#q(fields)}) VALUES ${rows}${returning}`, values);
148
- if (this.dialect.supportsReturning) {
149
- results.push(...result.rows);
150
- }
151
- else {
152
- // MySQL: insertId is the FIRST generated ID of the multi-row insert.
153
- // Subsequent rows follow sequentially assuming auto_increment_increment=1 (default).
154
- const firstId = result.insertId;
155
- for (let j = 0; j < chunk.length; j++) {
156
- const id = typeof firstId === 'number' ? firstId + j : undefined;
157
- results.push(this.#mysqlPkResult(pkCol, chunk[j], id));
158
- }
159
- }
146
+ const result = await this.query(`${this.#insertHead(table, fields)} VALUES ${rows}${returning}`, values);
147
+ results.push(...this.#extractPkRows(result, pkCol, chunk));
160
148
  }
161
149
  return results;
162
150
  }
@@ -164,26 +152,16 @@ export class QueryClient {
164
152
  if (!records.length)
165
153
  return [];
166
154
  const fields = Object.keys(records[0]);
167
- const updateSet = this.#upsertUpdateSet(fields, conflictKeys);
168
- const upsertClause = this.dialect.upsertSql(this.#q(conflictKeys), updateSet);
155
+ const upsertClause = this.dialect.upsertSql(this.#q(conflictKeys), this.dialect.upsertUpdateSet(fields, conflictKeys));
169
156
  const returning = this.dialect.returningPk(this.#quotedPkCols(pkCol));
170
157
  const results = [];
171
158
  for (let i = 0; i < records.length; i += chunkSize) {
172
159
  const chunk = records.slice(i, i + chunkSize);
173
160
  const { values, rows } = this.#bulkValues(fields, chunk);
174
- const result = await this.query(`INSERT INTO ${this.dialect.qi(table)} (${this.#q(fields)})
161
+ const result = await this.query(`${this.#insertHead(table, fields)}
175
162
  VALUES ${rows}
176
163
  ${upsertClause}${returning}`, values);
177
- if (this.dialect.supportsReturning) {
178
- results.push(...result.rows);
179
- }
180
- else {
181
- const firstId = result.insertId;
182
- for (let j = 0; j < chunk.length; j++) {
183
- const id = typeof firstId === 'number' ? firstId + j : undefined;
184
- results.push(this.#mysqlPkResult(pkCol, chunk[j], id));
185
- }
186
- }
164
+ results.push(...this.#extractPkRows(result, pkCol, chunk));
187
165
  }
188
166
  return results;
189
167
  }
@@ -1,4 +1,5 @@
1
1
  export type DialectName = 'postgres' | 'mysql' | 'mariadb';
2
+ export type TruncateUnit = 'year' | 'quarter' | 'month' | 'day' | 'hour';
2
3
  export interface SqlDialect {
3
4
  name: DialectName;
4
5
  /** Quote identifier: "id" (postgres) or `id` (mysql/mariadb) */
@@ -12,6 +13,22 @@ export interface SqlDialect {
12
13
  upsertSql(quotedConflictKeys: string, quotedUpdateSet: string): string;
13
14
  /** RETURNING pk_col suffix or empty string */
14
15
  returningPk(quotedPkCol: string): string;
16
+ /**
17
+ * Build the SET body for an upsert: the column list (separated by ', ') that the dialect
18
+ * uses to copy "incoming row" values into a conflicting row.
19
+ * - postgres: `"col" = EXCLUDED."col"`
20
+ * - mysql/mariadb: `` `col` = VALUES(`col`) ``
21
+ * Returns '' when there is nothing to update (i.e. all `fields` are conflict keys).
22
+ */
23
+ upsertUpdateSet(fields: string[], conflictKeys: string[]): string;
24
+ /**
25
+ * Truncate a timestamp/date column to the start of the given unit and
26
+ * return it as an ISO-style string. The output is always a STRING:
27
+ * - year/quarter/month/day → 'YYYY-MM-DD' (e.g. '2026-04-01')
28
+ * - hour → 'YYYY-MM-DDTHH:00:00'
29
+ * `qualifiedCol` is a fully-qualified, already-quoted column expression.
30
+ */
31
+ dateTrunc(unit: TruncateUnit, qualifiedCol: string): string;
15
32
  }
16
33
  export declare function getDialect(name: DialectName): SqlDialect;
17
34
  //# sourceMappingURL=dialect.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"dialect.d.ts","sourceRoot":"","sources":["../../src/lib/dialect.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,WAAW,GAAG,UAAU,GAAG,OAAO,GAAG,SAAS,CAAC;AAE3D,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,WAAW,CAAC;IAClB,gEAAgE;IAChE,EAAE,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAAC;IACvB,sDAAsD;IACtD,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAAC;IAC1B,oCAAoC;IACpC,SAAS,EAAE,UAAU,GAAG,OAAO,CAAC;IAChC,iBAAiB,EAAE,OAAO,CAAC;IAC3B,4CAA4C;IAC5C,SAAS,CAAC,kBAAkB,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,GAAG,MAAM,CAAC;IACvE,8CAA8C;IAC9C,WAAW,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAAC;CAC1C;AAqDD,wBAAgB,UAAU,CAAC,IAAI,EAAE,WAAW,GAAG,UAAU,CAIxD"}
1
+ {"version":3,"file":"dialect.d.ts","sourceRoot":"","sources":["../../src/lib/dialect.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,WAAW,GAAG,UAAU,GAAG,OAAO,GAAG,SAAS,CAAC;AAE3D,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,SAAS,GAAG,OAAO,GAAG,KAAK,GAAG,MAAM,CAAC;AAEzE,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,WAAW,CAAC;IAClB,gEAAgE;IAChE,EAAE,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAAC;IACvB,sDAAsD;IACtD,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAAC;IAC1B,oCAAoC;IACpC,SAAS,EAAE,UAAU,GAAG,OAAO,CAAC;IAChC,iBAAiB,EAAE,OAAO,CAAC;IAC3B,4CAA4C;IAC5C,SAAS,CAAC,kBAAkB,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,GAAG,MAAM,CAAC;IACvE,8CAA8C;IAC9C,WAAW,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAAC;IACzC;;;;;;OAMG;IACH,eAAe,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,YAAY,EAAE,MAAM,EAAE,GAAG,MAAM,CAAC;IAClE;;;;;;OAMG;IACH,SAAS,CAAC,IAAI,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,GAAG,MAAM,CAAC;CAC7D;AAqFD,wBAAgB,UAAU,CAAC,IAAI,EAAE,WAAW,GAAG,UAAU,CAIxD"}