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
@@ -1,8 +1,45 @@
1
+ import type { FastifyRequest } from 'fastify';
1
2
  import type { QueryClient } from '../db.js';
2
3
  import type { ITable, DbTables, JoinDefinition } from '../../types.js';
3
- export declare function findWriteJoin(tableConf: ITable, joinTableName: string): JoinDefinition | undefined;
4
+ export declare function findWriteJoin(tableConf: ITable, alias: string): JoinDefinition | undefined;
4
5
  export declare function removeExcludedFields(record: Record<string, unknown>, tableConf: ITable): Record<string, unknown>;
5
6
  export declare function findSecondaryTableConf(dbTables: DbTables, joinTableName: string): ITable | undefined;
7
+ interface PrepareCtx {
8
+ db: QueryClient;
9
+ tableConf: ITable;
10
+ request: FastifyRequest;
11
+ }
12
+ /**
13
+ * Prepare a single record for INSERT/UPSERT:
14
+ * 1. shallow camelCase copy of `input`
15
+ * 2. runValidation (camelCase)
16
+ * 3. beforeInsert hook (caller can mutate the camelCase copy)
17
+ * 4. snakecaseRecord → DB format
18
+ * 5. removeExcludedFields
19
+ *
20
+ * Returns both `camel` (input + hook mutations, used for FK auto-fill / afterInsert) and
21
+ * `snake` (DB-ready record). The original `input` is left intact.
22
+ *
23
+ * Pass `secondaries` so the validate hook receives them; pass undefined to skip
24
+ * secondaries-aware validation (e.g. when a bulk-level validator already ran).
25
+ */
26
+ export declare function prepareInsertRecord(ctx: PrepareCtx, input: Record<string, unknown>, secondaries?: Record<string, Record<string, unknown>[]>, options?: {
27
+ skipValidate?: boolean;
28
+ }): Promise<{
29
+ camel: Record<string, unknown>;
30
+ snake: Record<string, unknown>;
31
+ }>;
6
32
  export declare function processSecondaries(db: QueryClient, tableConf: ITable, dbTables: DbTables, mainRecord: Record<string, unknown>, secondaries: Record<string, Record<string, unknown>[]>): Promise<Record<string, Record<string, unknown>[]>>;
7
- export declare function processDeletions(db: QueryClient, tableConf: ITable, deletions: Record<string, Record<string, unknown>[]>): Promise<Record<string, Record<string, unknown>[]>>;
33
+ /**
34
+ * Process per-alias deletion requests for a write join.
35
+ *
36
+ * The FK from `mainRecord` (matching `joinDef.mainField` → `joinDef.joinField`) is
37
+ * auto-injected into every deletion record. This means the consumer can provide just
38
+ * the PK (e.g. `{ id: 5 }`) and the engine will run
39
+ * `DELETE FROM child WHERE id = 5 AND fk_to_main = <main.id>` — both ergonomic
40
+ * (no need to repeat the FK) and safer (cannot accidentally delete a row that
41
+ * doesn't belong to this main).
42
+ */
43
+ export declare function processDeletions(db: QueryClient, tableConf: ITable, mainRecord: Record<string, unknown>, deletions: Record<string, Record<string, unknown>[]>): Promise<Record<string, Record<string, unknown>[]>>;
44
+ export {};
8
45
  //# sourceMappingURL=write-helpers.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"write-helpers.d.ts","sourceRoot":"","sources":["../../../src/lib/engine/write-helpers.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAC5C,OAAO,KAAK,EACV,MAAM,EACN,QAAQ,EACR,cAAc,EAEf,MAAM,gBAAgB,CAAC;AAExB,wBAAgB,aAAa,CAC3B,SAAS,EAAE,MAAM,EACjB,aAAa,EAAE,MAAM,GACpB,cAAc,GAAG,SAAS,CAI5B;AAED,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/B,SAAS,EAAE,MAAM,GAChB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAQzB;AAED,wBAAgB,sBAAsB,CACpC,QAAQ,EAAE,QAAQ,EAClB,aAAa,EAAE,MAAM,GACpB,MAAM,GAAG,SAAS,CAKpB;AAED,wBAAsB,kBAAkB,CACtC,EAAE,EAAE,WAAW,EACf,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,QAAQ,EAClB,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACnC,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC,GACrD,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC,CAAC,CA2DpD;AAED,wBAAsB,gBAAgB,CACpC,EAAE,EAAE,WAAW,EACf,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC,GACnD,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC,CAAC,CAwBpD"}
1
+ {"version":3,"file":"write-helpers.d.ts","sourceRoot":"","sources":["../../../src/lib/engine/write-helpers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAE9C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAE5C,OAAO,KAAK,EACV,MAAM,EACN,QAAQ,EACR,cAAc,EAEf,MAAM,gBAAgB,CAAC;AAExB,wBAAgB,aAAa,CAC3B,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,MAAM,GACZ,cAAc,GAAG,SAAS,CAE5B;AAED,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/B,SAAS,EAAE,MAAM,GAChB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAQzB;AAED,wBAAgB,sBAAsB,CACpC,QAAQ,EAAE,QAAQ,EAClB,aAAa,EAAE,MAAM,GACpB,MAAM,GAAG,SAAS,CAKpB;AAED,UAAU,UAAU;IAClB,EAAE,EAAE,WAAW,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,cAAc,CAAC;CACzB;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAsB,mBAAmB,CACvC,GAAG,EAAE,UAAU,EACf,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC9B,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC,EACvD,OAAO,CAAC,EAAE;IAAE,YAAY,CAAC,EAAE,OAAO,CAAA;CAAE,GACnC,OAAO,CAAC;IAAE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,CAAC,CAe7E;AAED,wBAAsB,kBAAkB,CACtC,EAAE,EAAE,WAAW,EACf,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,QAAQ,EAClB,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACnC,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC,GACrD,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC,CAAC,CA2DpD;AAED;;;;;;;;;GASG;AACH,wBAAsB,gBAAgB,CACpC,EAAE,EAAE,WAAW,EACf,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACnC,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC,GACnD,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC,CAAC,CA+BpD"}
@@ -1,6 +1,7 @@
1
1
  import { camelcaseObject, snakecaseRecord } from '../naming.js';
2
- export function findWriteJoin(tableConf, joinTableName) {
3
- return tableConf.allowedWriteJoins?.find(([joinSchema]) => joinSchema.tableName === joinTableName);
2
+ import { runValidation } from './validate.js';
3
+ export function findWriteJoin(tableConf, alias) {
4
+ return tableConf.allowedWriteJoins?.find((j) => j.alias === alias);
4
5
  }
5
6
  export function removeExcludedFields(record, tableConf) {
6
7
  if (!tableConf.excludeFromCreation?.length)
@@ -19,17 +20,43 @@ export function findSecondaryTableConf(dbTables, joinTableName) {
19
20
  }
20
21
  return undefined;
21
22
  }
23
+ /**
24
+ * Prepare a single record for INSERT/UPSERT:
25
+ * 1. shallow camelCase copy of `input`
26
+ * 2. runValidation (camelCase)
27
+ * 3. beforeInsert hook (caller can mutate the camelCase copy)
28
+ * 4. snakecaseRecord → DB format
29
+ * 5. removeExcludedFields
30
+ *
31
+ * Returns both `camel` (input + hook mutations, used for FK auto-fill / afterInsert) and
32
+ * `snake` (DB-ready record). The original `input` is left intact.
33
+ *
34
+ * Pass `secondaries` so the validate hook receives them; pass undefined to skip
35
+ * secondaries-aware validation (e.g. when a bulk-level validator already ran).
36
+ */
37
+ export async function prepareInsertRecord(ctx, input, secondaries, options) {
38
+ const camel = { ...input };
39
+ if (!options?.skipValidate) {
40
+ await runValidation(ctx.db, ctx.request, ctx.tableConf, camel, secondaries);
41
+ }
42
+ if (ctx.tableConf.beforeInsert) {
43
+ await ctx.tableConf.beforeInsert(ctx.db, ctx.request, camel);
44
+ }
45
+ let snake = snakecaseRecord(camel, ctx.tableConf.Schema);
46
+ snake = removeExcludedFields(snake, ctx.tableConf);
47
+ return { camel, snake };
48
+ }
22
49
  export async function processSecondaries(db, tableConf, dbTables, mainRecord, secondaries) {
23
50
  const results = {};
24
- for (const [joinTableName, records] of Object.entries(secondaries)) {
51
+ for (const [alias, records] of Object.entries(secondaries)) {
25
52
  if (!records?.length)
26
53
  continue;
27
- const joinDef = findWriteJoin(tableConf, joinTableName);
54
+ const joinDef = findWriteJoin(tableConf, alias);
28
55
  if (!joinDef)
29
56
  continue;
30
- const [joinSchema, joinField, mainField] = joinDef;
57
+ const { joinSchema, joinField, mainField } = joinDef;
31
58
  const joinCol = joinSchema.col(joinField);
32
- const secondaryTableConf = findSecondaryTableConf(dbTables, joinTableName);
59
+ const secondaryTableConf = findSecondaryTableConf(dbTables, joinSchema.tableName);
33
60
  const secondaryPk = secondaryTableConf?.primary || joinField;
34
61
  const secondaryPkCol = Array.isArray(secondaryPk)
35
62
  ? secondaryPk.map((f) => joinSchema.col(f))
@@ -56,28 +83,44 @@ export async function processSecondaries(db, tableConf, dbTables, mainRecord, se
56
83
  else {
57
84
  pkRows = await db.bulkInsert(joinSchema.tableName, preparedRecords, secondaryPkCol);
58
85
  }
59
- results[joinTableName] = pkRows.map((r) => camelcaseObject(r, joinSchema));
86
+ results[alias] = pkRows.map((r) => camelcaseObject(r, joinSchema));
60
87
  }
61
88
  return results;
62
89
  }
63
- export async function processDeletions(db, tableConf, deletions) {
90
+ /**
91
+ * Process per-alias deletion requests for a write join.
92
+ *
93
+ * The FK from `mainRecord` (matching `joinDef.mainField` → `joinDef.joinField`) is
94
+ * auto-injected into every deletion record. This means the consumer can provide just
95
+ * the PK (e.g. `{ id: 5 }`) and the engine will run
96
+ * `DELETE FROM child WHERE id = 5 AND fk_to_main = <main.id>` — both ergonomic
97
+ * (no need to repeat the FK) and safer (cannot accidentally delete a row that
98
+ * doesn't belong to this main).
99
+ */
100
+ export async function processDeletions(db, tableConf, mainRecord, deletions) {
64
101
  const results = {};
65
- for (const [joinTableName, records] of Object.entries(deletions)) {
102
+ for (const [alias, records] of Object.entries(deletions)) {
66
103
  if (!records?.length)
67
104
  continue;
68
- const joinDef = findWriteJoin(tableConf, joinTableName);
105
+ const joinDef = findWriteJoin(tableConf, alias);
69
106
  if (!joinDef)
70
107
  continue;
71
- const [joinSchema] = joinDef;
108
+ const { joinSchema, joinField, mainField } = joinDef;
109
+ const joinCol = joinSchema.col(joinField);
110
+ const mainValue = Array.isArray(mainField)
111
+ ? mainRecord[mainField[0]]
112
+ : mainRecord[mainField];
72
113
  const deletedRows = [];
73
114
  for (const rec of records) {
74
115
  const snaked = snakecaseRecord(rec, joinSchema);
116
+ // Auto-inject FK to main: scopes the DELETE to children owned by this main.
117
+ snaked[joinCol] = mainValue;
75
118
  const affectedRows = await db.delete(joinSchema.tableName, snaked);
76
119
  if (affectedRows > 0) {
77
120
  deletedRows.push(camelcaseObject(snaked, joinSchema));
78
121
  }
79
122
  }
80
- results[joinTableName] = deletedRows;
123
+ results[alias] = deletedRows;
81
124
  }
82
125
  return results;
83
126
  }
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Build a plain Error decorated with `statusCode` (and optionally `validationErrors`),
3
+ * which Fastify reads to emit the corresponding HTTP response.
4
+ *
5
+ * Using a function instead of a custom class keeps the throw sites lightweight
6
+ * and matches the existing convention across the codebase.
7
+ */
8
+ export declare function httpError(statusCode: number, message: string): Error & {
9
+ statusCode: number;
10
+ };
11
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/lib/errors.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,wBAAgB,SAAS,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,KAAK,GAAG;IAAE,UAAU,EAAE,MAAM,CAAA;CAAE,CAI7F"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Build a plain Error decorated with `statusCode` (and optionally `validationErrors`),
3
+ * which Fastify reads to emit the corresponding HTTP response.
4
+ *
5
+ * Using a function instead of a custom class keeps the throw sites lightweight
6
+ * and matches the existing convention across the codebase.
7
+ */
8
+ export function httpError(statusCode, message) {
9
+ const err = new Error(message);
10
+ err.statusCode = statusCode;
11
+ return err;
12
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"bulk-upsert.d.ts","sourceRoot":"","sources":["../../../src/lib/schema/bulk-upsert.ts"],"names":[],"mappings":"AAAA,OAAO,EAAQ,KAAK,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAEvD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAI/C,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,oGAiCxE;AAED,wBAAgB,uBAAuB,CAAC,QAAQ,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,oGA8B5E"}
1
+ {"version":3,"file":"bulk-upsert.d.ts","sourceRoot":"","sources":["../../../src/lib/schema/bulk-upsert.ts"],"names":[],"mappings":"AAAA,OAAO,EAAQ,KAAK,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAEvD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAS/C,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,oGAgBxE;AAED,wBAAgB,uBAAuB,CAAC,QAAQ,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,oGAa5E"}
@@ -1,7 +1,6 @@
1
1
  import { Type } from '@sinclair/typebox';
2
2
  import { primaryAsString } from '../../types.js';
3
- import { findSecondaryTableConf } from '../engine/write-helpers.js';
4
- import { pkSchema, buildSecondaryFields, applySchemaOverrides } from './helpers.js';
3
+ import { pkSchema, applySchemaOverrides, attachWriteJoinSections, writeJoinBodyFields, writeJoinResponseFields, } from './helpers.js';
5
4
  export function BulkUpsertTableBody(dbTables, tableName) {
6
5
  const tableConf = dbTables[tableName];
7
6
  const schema = tableConf.Schema;
@@ -9,18 +8,10 @@ export function BulkUpsertTableBody(dbTables, tableName) {
9
8
  const itemProperties = {
10
9
  main: mainSchema,
11
10
  };
12
- if (tableConf.allowedWriteJoins?.length) {
13
- const secondaryProperties = {};
14
- const deletionProperties = {};
15
- for (const [joinSchema, joinField] of tableConf.allowedWriteJoins) {
16
- const secondaryTableConf = findSecondaryTableConf(dbTables, joinSchema.tableName);
17
- const joinFields = buildSecondaryFields(joinSchema, joinField, secondaryTableConf);
18
- secondaryProperties[joinSchema.tableName] = Type.Array(Type.Object(joinFields));
19
- deletionProperties[joinSchema.tableName] = Type.Array(Type.Partial(Type.Object(joinSchema.fields)));
20
- }
21
- itemProperties.secondaries = Type.Optional(Type.Partial(Type.Object(secondaryProperties)));
22
- itemProperties.deletions = Type.Optional(Type.Partial(Type.Object(deletionProperties)));
23
- }
11
+ attachWriteJoinSections(itemProperties, tableConf, dbTables, {
12
+ withDeletions: true,
13
+ secondaryFields: writeJoinBodyFields,
14
+ });
24
15
  return Type.Array(Type.Object(itemProperties));
25
16
  }
26
17
  export function BulkUpsertTableResponse(dbTables, tableName) {
@@ -28,16 +19,9 @@ export function BulkUpsertTableResponse(dbTables, tableName) {
28
19
  const responseProperties = {
29
20
  main: Type.Object(pkSchema(tableConf, tableConf.Schema, primaryAsString(tableConf.primary))),
30
21
  };
31
- if (tableConf.allowedWriteJoins?.length) {
32
- const secondaryProperties = {};
33
- const deletionProperties = {};
34
- for (const [joinSchema, joinField] of tableConf.allowedWriteJoins) {
35
- const secondaryTableConf = findSecondaryTableConf(dbTables, joinSchema.tableName);
36
- secondaryProperties[joinSchema.tableName] = Type.Array(Type.Object(pkSchema(secondaryTableConf, joinSchema, joinField)));
37
- deletionProperties[joinSchema.tableName] = Type.Array(Type.Partial(Type.Object(joinSchema.fields)));
38
- }
39
- responseProperties.secondaries = Type.Optional(Type.Partial(Type.Object(secondaryProperties)));
40
- responseProperties.deletions = Type.Optional(Type.Partial(Type.Object(deletionProperties)));
41
- }
22
+ attachWriteJoinSections(responseProperties, tableConf, dbTables, {
23
+ withDeletions: true,
24
+ secondaryFields: writeJoinResponseFields,
25
+ });
42
26
  return Type.Array(Type.Object(responseProperties));
43
27
  }
@@ -1,5 +1,5 @@
1
1
  import { type TSchema } from '@sinclair/typebox';
2
- import type { ITable, SchemaDefinition } from '../../types.js';
2
+ import type { ITable, SchemaDefinition, DbTables, JoinDefinition } from '../../types.js';
3
3
  /**
4
4
  * Build a PK-only schema for response shapes (single or composite PK).
5
5
  * Falls back to `fallback` field if tableConf is undefined.
@@ -16,4 +16,31 @@ export declare function buildSecondaryFields(joinSchema: SchemaDefinition, joinF
16
16
  * Replaces matching field schemas with the override.
17
17
  */
18
18
  export declare function applySchemaOverrides(fields: Record<string, TSchema>, tableConf: ITable): Record<string, TSchema>;
19
+ type JoinFieldsBuilder = (join: JoinDefinition, secondaryConf: ITable | undefined) => Record<string, TSchema>;
20
+ /**
21
+ * Maps each writeJoin alias to a Type.Array(Type.Object(...)) using the provided fields builder.
22
+ * When `itemsPartial` is true, wraps each item with Type.Partial — used for deletion sub-schemas
23
+ * where every field is a matcher (the engine auto-injects the FK to main).
24
+ */
25
+ export declare function buildJoinAliasMap(tableConf: ITable, dbTables: DbTables, build: JoinFieldsBuilder, options?: {
26
+ itemsPartial?: boolean;
27
+ }): Record<string, TSchema>;
28
+ /**
29
+ * Attach `secondaries` (and optionally `deletions`) sections to the given target object,
30
+ * iterating once over `tableConf.allowedWriteJoins`. Mutates `target` in-place.
31
+ * Used by body and response schema builders for insert/update/bulk-upsert.
32
+ *
33
+ * Deletions are emitted with every field Optional (Type.Partial) because the engine
34
+ * auto-injects the FK to main and the consumer typically just provides the PK (or a small
35
+ * subset of fields) to identify which child rows to delete.
36
+ */
37
+ export declare function attachWriteJoinSections(target: Record<string, TSchema>, tableConf: ITable, dbTables: DbTables, options: {
38
+ withDeletions: boolean;
39
+ secondaryFields: JoinFieldsBuilder;
40
+ }): void;
41
+ /** JoinFieldsBuilder for body schemas: includes all join fields with FK + excluded made Optional. */
42
+ export declare const writeJoinBodyFields: JoinFieldsBuilder;
43
+ /** JoinFieldsBuilder for response schemas: PK only. */
44
+ export declare const writeJoinResponseFields: JoinFieldsBuilder;
45
+ export {};
19
46
  //# sourceMappingURL=helpers.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../../../src/lib/schema/helpers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAQ,KAAK,OAAO,EAAE,MAAM,mBAAmB,CAAC;AACvD,OAAO,KAAK,EAAE,MAAM,EAAE,gBAAgB,EAAY,MAAM,gBAAgB,CAAC;AAEzE;;;GAGG;AACH,wBAAgB,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,EAAE,MAAM,EAAE,gBAAgB,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAQ3H;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAClC,UAAU,EAAE,gBAAgB,EAC5B,SAAS,EAAE,MAAM,EACjB,kBAAkB,EAAE,MAAM,GAAG,SAAS,GACrC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAgBzB;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/B,SAAS,EAAE,MAAM,GAChB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CASzB"}
1
+ {"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../../../src/lib/schema/helpers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAQ,KAAK,OAAO,EAAE,MAAM,mBAAmB,CAAC;AACvD,OAAO,KAAK,EAAE,MAAM,EAAE,gBAAgB,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAGzF;;;GAGG;AACH,wBAAgB,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,EAAE,MAAM,EAAE,gBAAgB,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAQ3H;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAClC,UAAU,EAAE,gBAAgB,EAC5B,SAAS,EAAE,MAAM,EACjB,kBAAkB,EAAE,MAAM,GAAG,SAAS,GACrC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAgBzB;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/B,SAAS,EAAE,MAAM,GAChB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CASzB;AAED,KAAK,iBAAiB,GAAG,CACvB,IAAI,EAAE,cAAc,EACpB,aAAa,EAAE,MAAM,GAAG,SAAS,KAC9B,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAE7B;;;;GAIG;AACH,wBAAgB,iBAAiB,CAC/B,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,QAAQ,EAClB,KAAK,EAAE,iBAAiB,EACxB,OAAO,GAAE;IAAE,YAAY,CAAC,EAAE,OAAO,CAAA;CAAO,GACvC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CASzB;AAED;;;;;;;;GAQG;AACH,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/B,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,QAAQ,EAClB,OAAO,EAAE;IAAE,aAAa,EAAE,OAAO,CAAC;IAAC,eAAe,EAAE,iBAAiB,CAAA;CAAE,GACtE,IAAI,CAmBN;AAED,qGAAqG;AACrG,eAAO,MAAM,mBAAmB,EAAE,iBACmB,CAAC;AAEtD,uDAAuD;AACvD,eAAO,MAAM,uBAAuB,EAAE,iBACG,CAAC"}
@@ -1,4 +1,5 @@
1
1
  import { Type } from '@sinclair/typebox';
2
+ import { findSecondaryTableConf } from '../engine/write-helpers.js';
2
3
  /**
3
4
  * Build a PK-only schema for response shapes (single or composite PK).
4
5
  * Falls back to `fallback` field if tableConf is undefined.
@@ -46,3 +47,40 @@ export function applySchemaOverrides(fields, tableConf) {
46
47
  }
47
48
  return result;
48
49
  }
50
+ /**
51
+ * Maps each writeJoin alias to a Type.Array(Type.Object(...)) using the provided fields builder.
52
+ * When `itemsPartial` is true, wraps each item with Type.Partial — used for deletion sub-schemas
53
+ * where every field is a matcher (the engine auto-injects the FK to main).
54
+ */
55
+ export function buildJoinAliasMap(tableConf, dbTables, build, options = {}) {
56
+ const out = {};
57
+ if (!tableConf.allowedWriteJoins?.length)
58
+ return out;
59
+ for (const j of tableConf.allowedWriteJoins) {
60
+ const sc = findSecondaryTableConf(dbTables, j.joinSchema.tableName);
61
+ const item = Type.Object(build(j, sc));
62
+ out[j.alias] = Type.Array(options.itemsPartial ? Type.Partial(item) : item);
63
+ }
64
+ return out;
65
+ }
66
+ /**
67
+ * Attach `secondaries` (and optionally `deletions`) sections to the given target object,
68
+ * iterating once over `tableConf.allowedWriteJoins`. Mutates `target` in-place.
69
+ * Used by body and response schema builders for insert/update/bulk-upsert.
70
+ *
71
+ * Deletions are emitted with every field Optional (Type.Partial) because the engine
72
+ * auto-injects the FK to main and the consumer typically just provides the PK (or a small
73
+ * subset of fields) to identify which child rows to delete.
74
+ */
75
+ export function attachWriteJoinSections(target, tableConf, dbTables, options) {
76
+ if (!tableConf.allowedWriteJoins?.length)
77
+ return;
78
+ target.secondaries = Type.Optional(Type.Partial(Type.Object(buildJoinAliasMap(tableConf, dbTables, options.secondaryFields))));
79
+ if (options.withDeletions) {
80
+ target.deletions = Type.Optional(Type.Partial(Type.Object(buildJoinAliasMap(tableConf, dbTables, ({ joinSchema }) => joinSchema.fields, { itemsPartial: true }))));
81
+ }
82
+ }
83
+ /** JoinFieldsBuilder for body schemas: includes all join fields with FK + excluded made Optional. */
84
+ export const writeJoinBodyFields = (j, sc) => buildSecondaryFields(j.joinSchema, j.joinField, sc);
85
+ /** JoinFieldsBuilder for response schemas: PK only. */
86
+ export const writeJoinResponseFields = (j, sc) => pkSchema(sc, j.joinSchema, j.joinField);
@@ -1 +1 @@
1
- {"version":3,"file":"insert.d.ts","sourceRoot":"","sources":["../../../src/lib/schema/insert.ts"],"names":[],"mappings":"AAAA,OAAO,EAAQ,KAAK,OAAO,EAAgB,MAAM,mBAAmB,CAAC;AAErE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAI/C,wBAAgB,eAAe,CAAC,QAAQ,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CA+B9E;AAED,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAuBlF"}
1
+ {"version":3,"file":"insert.d.ts","sourceRoot":"","sources":["../../../src/lib/schema/insert.ts"],"names":[],"mappings":"AAAA,OAAO,EAAQ,KAAK,OAAO,EAAgB,MAAM,mBAAmB,CAAC;AAErE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAS/C,wBAAgB,eAAe,CAAC,QAAQ,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAsB9E;AAED,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAalF"}
@@ -1,7 +1,6 @@
1
1
  import { Type } from '@sinclair/typebox';
2
2
  import { primaryAsString } from '../../types.js';
3
- import { findSecondaryTableConf } from '../engine/write-helpers.js';
4
- import { pkSchema, buildSecondaryFields, applySchemaOverrides } from './helpers.js';
3
+ import { pkSchema, applySchemaOverrides, attachWriteJoinSections, writeJoinBodyFields, writeJoinResponseFields, } from './helpers.js';
5
4
  export function InsertTableBody(dbTables, tableName) {
6
5
  const tableConf = dbTables[tableName];
7
6
  const schema = tableConf.Schema;
@@ -15,15 +14,10 @@ export function InsertTableBody(dbTables, tableName) {
15
14
  const bodyProperties = {
16
15
  main: Type.Object(mainFields),
17
16
  };
18
- if (tableConf.allowedWriteJoins?.length) {
19
- const secondaryProperties = {};
20
- for (const [joinSchema, joinField] of tableConf.allowedWriteJoins) {
21
- const secondaryTableConf = findSecondaryTableConf(dbTables, joinSchema.tableName);
22
- const joinFields = buildSecondaryFields(joinSchema, joinField, secondaryTableConf);
23
- secondaryProperties[joinSchema.tableName] = Type.Array(Type.Object(joinFields));
24
- }
25
- bodyProperties.secondaries = Type.Optional(Type.Partial(Type.Object(secondaryProperties)));
26
- }
17
+ attachWriteJoinSections(bodyProperties, tableConf, dbTables, {
18
+ withDeletions: false,
19
+ secondaryFields: writeJoinBodyFields,
20
+ });
27
21
  return Type.Object(bodyProperties);
28
22
  }
29
23
  export function InsertTableResponse(dbTables, tableName) {
@@ -31,13 +25,9 @@ export function InsertTableResponse(dbTables, tableName) {
31
25
  const responseProperties = {
32
26
  main: Type.Object(pkSchema(tableConf, tableConf.Schema, primaryAsString(tableConf.primary))),
33
27
  };
34
- if (tableConf.allowedWriteJoins?.length) {
35
- const secondaryProperties = {};
36
- for (const [joinSchema, joinField] of tableConf.allowedWriteJoins) {
37
- const secondaryTableConf = findSecondaryTableConf(dbTables, joinSchema.tableName);
38
- secondaryProperties[joinSchema.tableName] = Type.Array(Type.Object(pkSchema(secondaryTableConf, joinSchema, joinField)));
39
- }
40
- responseProperties.secondaries = Type.Optional(Type.Partial(Type.Object(secondaryProperties)));
41
- }
28
+ attachWriteJoinSections(responseProperties, tableConf, dbTables, {
29
+ withDeletions: false,
30
+ secondaryFields: writeJoinResponseFields,
31
+ });
42
32
  return Type.Object(responseProperties);
43
33
  }
@@ -1 +1 @@
1
- {"version":3,"file":"search.d.ts","sourceRoot":"","sources":["../../../src/lib/schema/search.ts"],"names":[],"mappings":"AAAA,OAAO,EAAQ,KAAK,OAAO,EAAgB,MAAM,mBAAmB,CAAC;AAErE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAU/C,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAsElF;AAED,eAAO,MAAM,sBAAsB;;;;;;;;EAQjC,CAAC;AAEH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAiClF"}
1
+ {"version":3,"file":"search.d.ts","sourceRoot":"","sources":["../../../src/lib/schema/search.ts"],"names":[],"mappings":"AAAA,OAAO,EAAQ,KAAK,OAAO,EAAgB,MAAM,mBAAmB,CAAC;AAErE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAY/C,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAuHlF;AAED,eAAO,MAAM,sBAAsB;;;;;;;;EASjC,CAAC;AAEH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAmElF"}