fastify-auto-sqlapi 0.0.3 → 0.0.5

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 (54) hide show
  1. package/AGENTS.md +2 -2
  2. package/AGENTS_BACKEND.md +111 -15
  3. package/AGENTS_FRONTEND.md +126 -1
  4. package/README.md +135 -18
  5. package/dist/bin/generate-schema.js +3 -3
  6. package/dist/bin/generate-tables.js +1 -1
  7. package/dist/bin/utils.d.ts +1 -1
  8. package/dist/bin/utils.d.ts.map +1 -1
  9. package/dist/bin/utils.js +2 -2
  10. package/dist/index.d.ts +1 -1
  11. package/dist/index.d.ts.map +1 -1
  12. package/dist/lib/cli/mysql-introspect.d.ts.map +1 -1
  13. package/dist/lib/cli/mysql-introspect.js +9 -2
  14. package/dist/lib/cli/pg-introspect.d.ts.map +1 -1
  15. package/dist/lib/cli/pg-introspect.js +8 -1
  16. package/dist/lib/cli/schema-codegen.d.ts +1 -1
  17. package/dist/lib/cli/schema-codegen.d.ts.map +1 -1
  18. package/dist/lib/cli/schema-codegen.js +11 -4
  19. package/dist/lib/cli/tables-codegen.d.ts.map +1 -1
  20. package/dist/lib/cli/tables-codegen.js +30 -1
  21. package/dist/lib/db.d.ts.map +1 -1
  22. package/dist/lib/db.js +10 -4
  23. package/dist/lib/engine/bulk/bulk-upsert.d.ts.map +1 -1
  24. package/dist/lib/engine/bulk/bulk-upsert.js +41 -27
  25. package/dist/lib/engine/rest/get.js +1 -1
  26. package/dist/lib/engine/rest/insert.d.ts.map +1 -1
  27. package/dist/lib/engine/rest/insert.js +25 -21
  28. package/dist/lib/engine/rest/update.d.ts.map +1 -1
  29. package/dist/lib/engine/rest/update.js +42 -38
  30. package/dist/lib/engine/search/search.d.ts.map +1 -1
  31. package/dist/lib/engine/search/search.js +235 -39
  32. package/dist/lib/engine/validate.d.ts +11 -0
  33. package/dist/lib/engine/validate.d.ts.map +1 -0
  34. package/dist/lib/engine/validate.js +29 -0
  35. package/dist/lib/engine/write-helpers.js +4 -4
  36. package/dist/lib/naming.d.ts +11 -2
  37. package/dist/lib/naming.d.ts.map +1 -1
  38. package/dist/lib/naming.js +33 -5
  39. package/dist/lib/schema/bulk-upsert.js +2 -2
  40. package/dist/lib/schema/helpers.d.ts +5 -0
  41. package/dist/lib/schema/helpers.d.ts.map +1 -1
  42. package/dist/lib/schema/helpers.js +15 -0
  43. package/dist/lib/schema/insert.js +2 -2
  44. package/dist/lib/schema/search.d.ts.map +1 -1
  45. package/dist/lib/schema/search.js +16 -10
  46. package/dist/lib/schema/update.d.ts.map +1 -1
  47. package/dist/lib/schema/update.js +4 -3
  48. package/dist/lib/table-helpers.d.ts.map +1 -1
  49. package/dist/lib/table-helpers.js +9 -1
  50. package/dist/routes/auto/plugin.d.ts.map +1 -1
  51. package/dist/routes/auto/plugin.js +12 -1
  52. package/dist/types.d.ts +47 -8
  53. package/dist/types.d.ts.map +1 -1
  54. package/package.json +8 -8
@@ -4,7 +4,6 @@ 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
6
  import { loadEnvFile, CONSOLE_COLORS, display, displayAsTableRow, error } from './utils.js';
7
- loadEnvFile();
8
7
  function printHelp() {
9
8
  console.log('');
10
9
  display('Usage:', CONSOLE_COLORS.yellow);
@@ -53,6 +52,7 @@ async function main() {
53
52
  }
54
53
  display('++++++ fastify-auto-sqlapi: generating tables template ++++++', CONSOLE_COLORS.yellow);
55
54
  const config = await loadConfig();
55
+ loadEnvFile(config.envFile);
56
56
  const outputDir = path.resolve(process.cwd(), cliArgs.output || config.outputDir);
57
57
  const schemasDir = path.join(outputDir, 'schemas');
58
58
  const tablesDir = path.join(outputDir, 'tables');
@@ -2,7 +2,7 @@
2
2
  * Load .env file from cwd if it exists.
3
3
  * Does not override already-set environment variables.
4
4
  */
5
- export declare function loadEnvFile(): void;
5
+ export declare function loadEnvFile(envFile?: string): void;
6
6
  export declare const CONSOLE_COLORS: {
7
7
  readonly black: 30;
8
8
  readonly gray: 90;
@@ -1 +1 @@
1
- {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/bin/utils.ts"],"names":[],"mappings":"AAGA;;;GAGG;AACH,wBAAgB,WAAW,IAAI,IAAI,CAqBlC;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"}
package/dist/bin/utils.js CHANGED
@@ -4,8 +4,8 @@ import { resolve } from 'node:path';
4
4
  * Load .env file from cwd if it exists.
5
5
  * Does not override already-set environment variables.
6
6
  */
7
- export function loadEnvFile() {
8
- const envPath = resolve(process.cwd(), '.env');
7
+ export function loadEnvFile(envFile) {
8
+ const envPath = resolve(process.cwd(), envFile || '.env');
9
9
  if (!existsSync(envPath))
10
10
  return;
11
11
  const content = readFileSync(envPath, 'utf-8');
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, } 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, 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":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -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,CAiCvB"}
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"}
@@ -67,7 +67,8 @@ function mapMysqlType(dataType) {
67
67
  }
68
68
  export async function introspectMysqlTables(connectionConfig, schema) {
69
69
  // Dynamic import: mysql2 is optional peer dependency.
70
- // Use createRequire from cwd so it works with npm link.
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).
71
72
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
72
73
  let mysql2;
73
74
  try {
@@ -75,7 +76,13 @@ export async function introspectMysqlTables(connectionConfig, schema) {
75
76
  mysql2 = require('mysql2/promise');
76
77
  }
77
78
  catch {
78
- throw new Error('mysql2 is required for MySQL/MariaDB introspection. Install it with: npm install mysql2');
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
+ }
79
86
  }
80
87
  const connection = await mysql2.createConnection(connectionConfig);
81
88
  try {
@@ -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,CA4BvB"}
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"}
@@ -19,7 +19,14 @@ export async function introspectTables(connectionString, schema) {
19
19
  pg = require('pg');
20
20
  }
21
21
  catch {
22
- throw new Error('pg is required for PostgreSQL introspection. Install it with: npm install pg');
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
+ }
23
30
  }
24
31
  const client = new pg.Client({ connectionString });
25
32
  try {
@@ -4,5 +4,5 @@ export declare function convertColType(udtName: string, columnInfo: {
4
4
  is_nullable: string;
5
5
  }): string;
6
6
  export declare function buildTableMap(rows: ColumnInfo[]): TableMap;
7
- export declare function generateSchemaFile(schemaName: string, tableName: string, fields: Record<string, string>): string;
7
+ export declare function generateSchemaFile(schemaName: string, tableName: string, fields: Record<string, string>, colMap: Record<string, string>): string;
8
8
  //# sourceMappingURL=schema-codegen.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"schema-codegen.d.ts","sourceRoot":"","sources":["../../../src/lib/cli/schema-codegen.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAE3D,wBAAgB,cAAc,CAC5B,OAAO,EAAE,MAAM,EACf,UAAU,EAAE;IAAE,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,GACjE,MAAM,CAoDR;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE,UAAU,EAAE,GAAG,QAAQ,CAkB1D;AAED,wBAAgB,kBAAkB,CAChC,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC7B,MAAM,CAsCR"}
1
+ {"version":3,"file":"schema-codegen.d.ts","sourceRoot":"","sources":["../../../src/lib/cli/schema-codegen.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAE3D,wBAAgB,cAAc,CAC5B,OAAO,EAAE,MAAM,EACf,UAAU,EAAE;IAAE,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,GACjE,MAAM,CAoDR;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE,UAAU,EAAE,GAAG,QAAQ,CAqB1D;AAED,wBAAgB,kBAAkB,CAChC,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAC9B,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC7B,MAAM,CA2CR"}
@@ -57,20 +57,26 @@ export function buildTableMap(rows) {
57
57
  acc[schemaName] = {
58
58
  name: row.table_name,
59
59
  fields: {},
60
+ colMap: {},
60
61
  };
61
62
  }
62
- acc[schemaName].fields[toCamelCase(row.column_name)] = convertColType(row.udt_name, row);
63
+ const fieldName = toCamelCase(row.column_name);
64
+ acc[schemaName].fields[fieldName] = convertColType(row.udt_name, row);
65
+ acc[schemaName].colMap[fieldName] = row.column_name;
63
66
  return acc;
64
67
  }, {});
65
68
  }
66
- export function generateSchemaFile(schemaName, tableName, fields) {
69
+ export function generateSchemaFile(schemaName, tableName, fields, colMap) {
67
70
  const fieldsStr = JSON.stringify(fields, null, 2).replace(/"(.*?)"\s*:\s*"Type(.*?)"/g, '$1: Type$2');
71
+ const colMapStr = JSON.stringify(colMap, null, 2);
68
72
  return `// Auto-generated by fastify-auto-sqlapi. Do not edit.
69
- import { Type, toUnderscore } from "fastify-auto-sqlapi";
73
+ import { Type } from "fastify-auto-sqlapi";
70
74
  import type { Static } from "fastify-auto-sqlapi";
71
75
 
72
76
  const _Schema = ${fieldsStr};
73
77
 
78
+ const _colMap: Record<string, string> = ${colMapStr};
79
+
74
80
  const validation = Type.Object(_Schema, { $id: "${schemaName}" });
75
81
  const partialValidation = Type.Object(
76
82
  Object.fromEntries(
@@ -83,7 +89,8 @@ const partialValidation = Type.Object(
83
89
  );
84
90
 
85
91
  export const Schema = {
86
- col: (f: keyof typeof _Schema) => toUnderscore(f),
92
+ col: (f: keyof typeof _Schema) => _colMap[f as string] ?? f,
93
+ colMap: _colMap,
87
94
  fields: _Schema,
88
95
  validation,
89
96
  tableName: "${tableName}",
@@ -1 +1 @@
1
- {"version":3,"file":"tables-codegen.d.ts","sourceRoot":"","sources":["../../../src/lib/cli/tables-codegen.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,YAAY;IAC3B,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACpC;AAED,MAAM,WAAW,gBAAgB;IAC/B,gBAAgB,EAAE,MAAM,CAAC;IACzB,WAAW,EAAE,MAAM,CAAC;IACpB,eAAe,EAAE,MAAM,CAAC;IACxB,UAAU,EAAE,MAAM,CAAC;CACpB;AAID,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,YAAY,GAAG,IAAI,CA2BpE;AAgBD,wBAAgB,eAAe,CAAC,OAAO,EAAE,YAAY,EAAE,GAAG,gBAAgB,EAAE,CAwB3E;AAID,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,YAAY,EAAE,UAAU,EAAE,YAAY,EAAE,GAAG,MAAM,CAiGhG;AAID,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,YAAY,EAAE,GAAG,MAAM,CAqBrE;AAID,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,YAAY,EAAE,GAAG,MAAM,CAoFlE"}
1
+ {"version":3,"file":"tables-codegen.d.ts","sourceRoot":"","sources":["../../../src/lib/cli/tables-codegen.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,YAAY;IAC3B,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACpC;AAED,MAAM,WAAW,gBAAgB;IAC/B,gBAAgB,EAAE,MAAM,CAAC;IACzB,WAAW,EAAE,MAAM,CAAC;IACpB,eAAe,EAAE,MAAM,CAAC;IACxB,UAAU,EAAE,MAAM,CAAC;CACpB;AAID,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,YAAY,GAAG,IAAI,CA2BpE;AAgBD,wBAAgB,eAAe,CAAC,OAAO,EAAE,YAAY,EAAE,GAAG,gBAAgB,EAAE,CAwB3E;AAID,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,YAAY,EAAE,UAAU,EAAE,YAAY,EAAE,GAAG,MAAM,CAwHhG;AAID,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,YAAY,EAAE,GAAG,MAAM,CAqBrE;AAID,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,YAAY,EAAE,GAAG,MAAM,CA6FlE"}
@@ -98,6 +98,7 @@ export function generateSingleTableFile(schema, allSchemas) {
98
98
  const lines = [];
99
99
  // Imports
100
100
  lines.push(`import {defineTable, exportTableInfo, Type} from 'fastify-auto-sqlapi';`);
101
+ lines.push(`import type {ValidationError} from 'fastify-auto-sqlapi';`);
101
102
  lines.push(`import {${schema.schemaName} as Schema} from '../schemas/${schema.schemaName}';`);
102
103
  // Commented imports for related schemas
103
104
  for (const relSchema of relatedSchemas) {
@@ -140,6 +141,27 @@ export function generateSingleTableFile(schema, allSchemas) {
140
141
  lines.push(` // allowedReadJoins: [],`);
141
142
  }
142
143
  lines.push(` // upsertMap: buildUpsertRules(buildUpsertRule(Schema, ['${pk}'])),`);
144
+ // schemaOverrides: suggest email format if email field exists, otherwise minLength on first string field
145
+ const emailField = schema.fields.find(f => f.toLowerCase().includes('email'));
146
+ if (emailField) {
147
+ lines.push(` // schemaOverrides: { ${emailField}: Type.String({ format: 'email' }) },`);
148
+ }
149
+ else {
150
+ const stringField = schema.fields.find(f => f !== pk && schema.fieldTypes[f]?.includes('String'));
151
+ if (stringField) {
152
+ lines.push(` // schemaOverrides: { ${stringField}: Type.String({ minLength: 1 }) },`);
153
+ }
154
+ else {
155
+ lines.push(` // schemaOverrides: {},`);
156
+ }
157
+ }
158
+ // validate: use first non-PK field for example
159
+ const exampleField = schema.fields.find(f => f !== pk) || schema.fields[0];
160
+ lines.push(` validate: async (db, req, main, secondaries) => {`);
161
+ lines.push(` const errors: ValidationError[] = [];`);
162
+ lines.push(` // if (!main.${exampleField}) errors.push(['${exampleField}', 'required']);`);
163
+ lines.push(` return errors;`);
164
+ lines.push(` },`);
143
165
  lines.push(` // beforeInsert: async (db, req, record) => {},`);
144
166
  lines.push(` // beforeUpdate: async (db, req, fields) => {},`);
145
167
  if (childRels.length > 0) {
@@ -188,7 +210,7 @@ export function generateTablesFile(schemas) {
188
210
  const lines = [];
189
211
  // Imports
190
212
  lines.push(`import {defineTable, exportTableInfo, Type} from 'fastify-auto-sqlapi';`);
191
- lines.push(`import type { DbTables } from 'fastify-auto-sqlapi';`);
213
+ lines.push(`import type { DbTables, ValidationError } from 'fastify-auto-sqlapi';`);
192
214
  lines.push(``);
193
215
  // Schema imports
194
216
  for (const schema of schemas) {
@@ -223,6 +245,13 @@ export function generateTablesFile(schemas) {
223
245
  lines.push(` // allowedReadJoins: [],`);
224
246
  }
225
247
  lines.push(` // upsertMap: buildUpsertRules(buildUpsertRule(${schema.schemaName}, ['${pk}'])),`);
248
+ lines.push(` // schemaOverrides: {},`);
249
+ const exampleFieldLegacy = schema.fields.find(f => f !== pk) || schema.fields[0];
250
+ lines.push(` validate: async (db, req, main, secondaries) => {`);
251
+ lines.push(` const errors: ValidationError[] = [];`);
252
+ lines.push(` // if (!main.${exampleFieldLegacy}) errors.push(['${exampleFieldLegacy}', 'required']);`);
253
+ lines.push(` return errors;`);
254
+ lines.push(` },`);
226
255
  lines.push(` // beforeInsert: async (db, req, record) => {},`);
227
256
  lines.push(` // beforeUpdate: async (db, req, fields) => {},`);
228
257
  const childRels = relsByChild.get(schema.schemaName) || [];
@@ -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;IA2B/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;IA+B/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;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"}
package/dist/lib/db.js CHANGED
@@ -149,8 +149,12 @@ export class QueryClient {
149
149
  results.push(...result.rows);
150
150
  }
151
151
  else {
152
- for (const rec of chunk) {
153
- results.push(this.#mysqlPkResult(pkCol, rec, result.insertId));
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));
154
158
  }
155
159
  }
156
160
  }
@@ -174,8 +178,10 @@ export class QueryClient {
174
178
  results.push(...result.rows);
175
179
  }
176
180
  else {
177
- for (const rec of chunk) {
178
- results.push(this.#mysqlPkResult(pkCol, rec, result.insertId));
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));
179
185
  }
180
186
  }
181
187
  }
@@ -1 +1 @@
1
- {"version":3,"file":"bulk-upsert.d.ts","sourceRoot":"","sources":["../../../../src/lib/engine/bulk/bulk-upsert.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EACV,gBAAgB,EAChB,gBAAgB,EAGjB,MAAM,mBAAmB,CAAC;AAE3B,wBAAsB,gBAAgB,CAAC,MAAM,EAAE,gBAAgB,GAAG,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAuF5F"}
1
+ {"version":3,"file":"bulk-upsert.d.ts","sourceRoot":"","sources":["../../../../src/lib/engine/bulk/bulk-upsert.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EACV,gBAAgB,EAChB,gBAAgB,EAGjB,MAAM,mBAAmB,CAAC;AAE3B,wBAAsB,gBAAgB,CAAC,MAAM,EAAE,gBAAgB,GAAG,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAoG5F"}
@@ -1,23 +1,41 @@
1
- import { camelcaseObject, snakecaseRecord } from '../../naming.js';
1
+ import { snakecaseRecord } from '../../naming.js';
2
2
  import { removeExcludedFields, processSecondaries, processDeletions } from '../write-helpers.js';
3
3
  import { injectTenantValue, validateTenantFK } from '../../tenant.js';
4
+ import { runValidation, runBulkValidation } from '../validate.js';
4
5
  import { primaryAsString } from '../../../types.js';
5
6
  export async function bulkUpsertEngine(params) {
6
7
  const { db, tableConf, dbTables, request, items, tenant } = params;
7
8
  if (!items.length)
8
9
  return [];
10
+ const schema = tableConf.Schema;
9
11
  const pk = primaryAsString(tableConf.primary);
10
- const pkCol = tableConf.Schema.col(pk);
11
- // 1. Prepare all main records
12
- const preparedMains = items.map((item) => {
13
- let rec = snakecaseRecord(item.main);
14
- rec = removeExcludedFields(rec, tableConf);
15
- return rec;
12
+ const pkCol = schema.col(pk);
13
+ // 1. Mutable camelCase copies of each item.main
14
+ const inputMains = items.map((item) => ({ ...item.main }));
15
+ // 2. Custom validation (camelCase)
16
+ if (tableConf.validateBulk) {
17
+ await runBulkValidation(db, request, tableConf, items);
18
+ }
19
+ else {
20
+ for (let i = 0; i < items.length; i++) {
21
+ await runValidation(db, request, tableConf, inputMains[i], items[i].secondaries);
22
+ }
23
+ }
24
+ // 3. beforeInsert hook per record (camelCase)
25
+ if (tableConf.beforeInsert) {
26
+ for (const rec of inputMains) {
27
+ await tableConf.beforeInsert(db, request, rec);
28
+ }
29
+ }
30
+ // 4. Convert to DB format (after user mutations)
31
+ const preparedMains = inputMains.map((rec) => {
32
+ let converted = snakecaseRecord(rec, schema);
33
+ converted = removeExcludedFields(converted, tableConf);
34
+ return converted;
16
35
  });
17
- // 1b. Tenant: inject or validate FK
36
+ // 5. Tenant: inject or validate FK (enforced after user mutations)
18
37
  if (tenant) {
19
38
  if ('through' in tenant.scope) {
20
- // Indirect: collect all FK values, validate in batch
21
39
  const scope = tenant.scope;
22
40
  const fkCol = scope.through.localField;
23
41
  const fkValues = preparedMains.map((rec) => rec[fkCol]);
@@ -29,31 +47,27 @@ export async function bulkUpsertEngine(params) {
29
47
  }
30
48
  }
31
49
  }
32
- // 2. beforeInsert hook per record
33
- if (tableConf.beforeInsert) {
34
- for (const rec of preparedMains) {
35
- await tableConf.beforeInsert(db, request, rec);
36
- }
37
- }
38
- // 3. Bulk upsert all mains in one query → returns PK-only
39
- const upsertKeys = tableConf.upsertMap?.get(tableConf.Schema);
50
+ // 6. Bulk upsert all mains in one query → returns PK-only
51
+ const upsertKeys = tableConf.upsertMap?.get(schema);
40
52
  let pkRows;
41
53
  if (upsertKeys) {
42
- const conflictCols = upsertKeys.map((k) => tableConf.Schema.col(k));
43
- pkRows = await db.bulkInsertOrUpdate(tableConf.Schema.tableName, preparedMains, conflictCols, pkCol);
54
+ const conflictCols = upsertKeys.map((k) => schema.col(k));
55
+ pkRows = await db.bulkInsertOrUpdate(schema.tableName, preparedMains, conflictCols, pkCol);
44
56
  }
45
57
  else {
46
- pkRows = await db.bulkInsert(tableConf.Schema.tableName, preparedMains, pkCol);
58
+ pkRows = await db.bulkInsert(schema.tableName, preparedMains, pkCol);
47
59
  }
48
- const pksCamel = pkRows.map((r) => camelcaseObject(r));
49
- // 4. Process secondaries and deletions per item
60
+ // 7. Process secondaries, deletions, afterInsert per item (all camelCase)
50
61
  const results = [];
51
62
  for (let i = 0; i < items.length; i++) {
52
63
  const item = items[i];
53
- const mainPk = pksCamel[i];
54
- const result = { main: mainPk };
55
- // For FK auto-fill in secondaries, merge input record + PK
56
- const mainForFK = { ...camelcaseObject(preparedMains[i]), ...mainPk };
64
+ const mainPk = pkRows[i]; // PK-only (already in camelCase field name since PK uses camelCase field)
65
+ // pkRows entries come from SQL RETURNING or insertId — keys are DB column names.
66
+ // For simple PK named the same in API and DB (like 'id') this is transparent.
67
+ // If PK name differs (rare), the user should not rely on mainPk for FK auto-fill — it's the merged inputMain that drives FK.
68
+ const result = { main: { [pk]: mainPk[pkCol] ?? mainPk[pk] } };
69
+ // FK auto-fill: input camelCase + generated PK
70
+ const mainForFK = { ...inputMains[i], [pk]: mainPk[pkCol] ?? mainPk[pk] };
57
71
  if (item.secondaries && Object.keys(item.secondaries).length > 0) {
58
72
  const sec = await processSecondaries(db, tableConf, dbTables, mainForFK, item.secondaries);
59
73
  if (Object.keys(sec).length > 0)
@@ -64,7 +78,7 @@ export async function bulkUpsertEngine(params) {
64
78
  if (Object.keys(del).length > 0)
65
79
  result.deletions = del;
66
80
  }
67
- // 5. afterInsert hook per item
81
+ // 8. afterInsert hook per item (camelCase)
68
82
  if (tableConf.afterInsert) {
69
83
  await tableConf.afterInsert(db, request, mainForFK, result.secondaries);
70
84
  }
@@ -28,5 +28,5 @@ export async function getEngine(params) {
28
28
  err.statusCode = 404;
29
29
  throw err;
30
30
  }
31
- return { main: camelcaseObject(rows[0]) };
31
+ return { main: camelcaseObject(rows[0], tableConf.Schema) };
32
32
  }
@@ -1 +1 @@
1
- {"version":3,"file":"insert.d.ts","sourceRoot":"","sources":["../../../../src/lib/engine/rest/insert.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EACV,YAAY,EACZ,YAAY,EAGb,MAAM,mBAAmB,CAAC;AAE3B,wBAAsB,YAAY,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,YAAY,CAAC,CAsE9E"}
1
+ {"version":3,"file":"insert.d.ts","sourceRoot":"","sources":["../../../../src/lib/engine/rest/insert.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EACV,YAAY,EACZ,YAAY,EAGb,MAAM,mBAAmB,CAAC;AAE3B,wBAAsB,YAAY,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,YAAY,CAAC,CA2E9E"}
@@ -1,18 +1,27 @@
1
1
  import { camelcaseObject, snakecaseRecord } from '../../naming.js';
2
2
  import { removeExcludedFields, processSecondaries } from '../write-helpers.js';
3
3
  import { injectTenantValue, validateTenantFK } from '../../tenant.js';
4
+ import { runValidation } from '../validate.js';
4
5
  import { primaryAsString } from '../../../types.js';
5
6
  export async function insertEngine(params) {
6
7
  const { db, tableConf, dbTables, request, record, secondaries, tenant } = params;
8
+ const schema = tableConf.Schema;
7
9
  const pk = primaryAsString(tableConf.primary);
8
- const pkCol = tableConf.Schema.col(pk);
9
- // 1. Prepare main record
10
- let mainRecord = snakecaseRecord(record);
10
+ const pkCol = schema.col(pk);
11
+ // 1. Mutable copy of input in camelCase (user-facing format)
12
+ const inputRecord = { ...record };
13
+ // 2. Custom validation (camelCase)
14
+ await runValidation(db, request, tableConf, inputRecord, secondaries);
15
+ // 3. beforeInsert hook (camelCase — user can mutate with schema field names)
16
+ if (tableConf.beforeInsert) {
17
+ await tableConf.beforeInsert(db, request, inputRecord);
18
+ }
19
+ // 4. Convert to DB format (after all user mutations)
20
+ let mainRecord = snakecaseRecord(inputRecord, schema);
11
21
  mainRecord = removeExcludedFields(mainRecord, tableConf);
12
- // 1b. Tenant: inject or validate
22
+ // 5. Tenant: inject or validate (enforced after user mutations for security)
13
23
  if (tenant) {
14
24
  if ('through' in tenant.scope) {
15
- // Indirect: validate FK belongs to tenant
16
25
  const scope = tenant.scope;
17
26
  const fkCol = scope.through.localField;
18
27
  const fkValue = mainRecord[fkCol];
@@ -22,34 +31,29 @@ export async function insertEngine(params) {
22
31
  injectTenantValue(mainRecord, tenant.scope, tenant.ids);
23
32
  }
24
33
  }
25
- // 2. beforeInsert hook
26
- if (tableConf.beforeInsert) {
27
- await tableConf.beforeInsert(db, request, mainRecord);
28
- }
29
- // 3. Insert main → returns PK-only
34
+ // 6. Insert main → returns PK-only
30
35
  let pkResult;
31
- const upsertKeys = tableConf.upsertMap?.get(tableConf.Schema);
36
+ const upsertKeys = tableConf.upsertMap?.get(schema);
32
37
  if (upsertKeys) {
33
- const conflictCols = upsertKeys.map((k) => tableConf.Schema.col(k));
34
- pkResult = await db.insertOrUpdate(tableConf.Schema.tableName, mainRecord, conflictCols, pkCol);
38
+ const conflictCols = upsertKeys.map((k) => schema.col(k));
39
+ pkResult = await db.insertOrUpdate(schema.tableName, mainRecord, conflictCols, pkCol);
35
40
  }
36
41
  else {
37
- pkResult = await db.insert(tableConf.Schema.tableName, mainRecord, pkCol);
42
+ pkResult = await db.insert(schema.tableName, mainRecord, pkCol);
38
43
  }
39
- const mainPkCamel = camelcaseObject(pkResult);
40
- // 4. Secondaries: need full record for FK auto-fill
44
+ const mainPkCamel = camelcaseObject(pkResult, schema);
45
+ // 7. Secondaries: need full record for FK auto-fill (camelCase)
41
46
  let secondaryResults;
42
47
  if (secondaries && Object.keys(secondaries).length > 0) {
43
- // Merge input record + PK for FK auto-fill
44
- const mainForFK = { ...camelcaseObject(mainRecord), ...mainPkCamel };
48
+ const mainForFK = { ...inputRecord, ...mainPkCamel };
45
49
  secondaryResults = await processSecondaries(db, tableConf, dbTables, mainForFK, secondaries);
46
50
  }
47
- // 5. afterInsert hook
51
+ // 8. afterInsert hook (camelCase — input merged with generated PK)
48
52
  if (tableConf.afterInsert) {
49
- const mainForHook = { ...camelcaseObject(mainRecord), ...mainPkCamel };
53
+ const mainForHook = { ...inputRecord, ...mainPkCamel };
50
54
  await tableConf.afterInsert(db, request, mainForHook, secondaryResults);
51
55
  }
52
- // 6. Return PK-only
56
+ // 9. Return PK-only
53
57
  const result = { main: mainPkCamel };
54
58
  if (secondaryResults && Object.keys(secondaryResults).length > 0) {
55
59
  result.secondaries = secondaryResults;
@@ -1 +1 @@
1
- {"version":3,"file":"update.d.ts","sourceRoot":"","sources":["../../../../src/lib/engine/rest/update.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EACV,YAAY,EACZ,YAAY,EAGb,MAAM,mBAAmB,CAAC;AAE3B,wBAAsB,YAAY,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,YAAY,CAAC,CA6H9E"}
1
+ {"version":3,"file":"update.d.ts","sourceRoot":"","sources":["../../../../src/lib/engine/rest/update.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EACV,YAAY,EACZ,YAAY,EAGb,MAAM,mBAAmB,CAAC;AAE3B,wBAAsB,YAAY,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,YAAY,CAAC,CAkI9E"}