fastify-auto-sqlapi 0.0.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 (132) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +422 -0
  3. package/dist/bin/generate-schema.d.ts +3 -0
  4. package/dist/bin/generate-schema.d.ts.map +1 -0
  5. package/dist/bin/generate-schema.js +122 -0
  6. package/dist/bin/generate-tables.d.ts +3 -0
  7. package/dist/bin/generate-tables.d.ts.map +1 -0
  8. package/dist/bin/generate-tables.js +135 -0
  9. package/dist/bin/utils.d.ts +18 -0
  10. package/dist/bin/utils.d.ts.map +1 -0
  11. package/dist/bin/utils.js +54 -0
  12. package/dist/index.d.ts +34 -0
  13. package/dist/index.d.ts.map +1 -0
  14. package/dist/index.js +48 -0
  15. package/dist/lib/adapters/mysql-adapter.d.ts +9 -0
  16. package/dist/lib/adapters/mysql-adapter.d.ts.map +1 -0
  17. package/dist/lib/adapters/mysql-adapter.js +21 -0
  18. package/dist/lib/adapters/pg-adapter.d.ts +9 -0
  19. package/dist/lib/adapters/pg-adapter.d.ts.map +1 -0
  20. package/dist/lib/adapters/pg-adapter.js +11 -0
  21. package/dist/lib/cli/config.d.ts +3 -0
  22. package/dist/lib/cli/config.d.ts.map +1 -0
  23. package/dist/lib/cli/config.js +29 -0
  24. package/dist/lib/cli/mysql-introspect.d.ts +16 -0
  25. package/dist/lib/cli/mysql-introspect.d.ts.map +1 -0
  26. package/dist/lib/cli/mysql-introspect.js +98 -0
  27. package/dist/lib/cli/pg-introspect.d.ts +4 -0
  28. package/dist/lib/cli/pg-introspect.d.ts.map +1 -0
  29. package/dist/lib/cli/pg-introspect.js +36 -0
  30. package/dist/lib/cli/schema-codegen.d.ts +8 -0
  31. package/dist/lib/cli/schema-codegen.d.ts.map +1 -0
  32. package/dist/lib/cli/schema-codegen.js +100 -0
  33. package/dist/lib/cli/tables-codegen.d.ts +18 -0
  34. package/dist/lib/cli/tables-codegen.d.ts.map +1 -0
  35. package/dist/lib/cli/tables-codegen.js +249 -0
  36. package/dist/lib/condition-methods.d.ts +11 -0
  37. package/dist/lib/condition-methods.d.ts.map +1 -0
  38. package/dist/lib/condition-methods.js +20 -0
  39. package/dist/lib/db.d.ts +28 -0
  40. package/dist/lib/db.d.ts.map +1 -0
  41. package/dist/lib/db.js +224 -0
  42. package/dist/lib/dialect.d.ts +17 -0
  43. package/dist/lib/dialect.d.ts.map +1 -0
  44. package/dist/lib/dialect.js +56 -0
  45. package/dist/lib/engine/bulk/bulk-delete.d.ts +3 -0
  46. package/dist/lib/engine/bulk/bulk-delete.d.ts.map +1 -0
  47. package/dist/lib/engine/bulk/bulk-delete.js +30 -0
  48. package/dist/lib/engine/bulk/bulk-upsert.d.ts +3 -0
  49. package/dist/lib/engine/bulk/bulk-upsert.d.ts.map +1 -0
  50. package/dist/lib/engine/bulk/bulk-upsert.js +74 -0
  51. package/dist/lib/engine/rest/delete.d.ts +3 -0
  52. package/dist/lib/engine/rest/delete.d.ts.map +1 -0
  53. package/dist/lib/engine/rest/delete.js +25 -0
  54. package/dist/lib/engine/rest/get.d.ts +3 -0
  55. package/dist/lib/engine/rest/get.d.ts.map +1 -0
  56. package/dist/lib/engine/rest/get.js +32 -0
  57. package/dist/lib/engine/rest/insert.d.ts +3 -0
  58. package/dist/lib/engine/rest/insert.d.ts.map +1 -0
  59. package/dist/lib/engine/rest/insert.js +58 -0
  60. package/dist/lib/engine/rest/update.d.ts +3 -0
  61. package/dist/lib/engine/rest/update.d.ts.map +1 -0
  62. package/dist/lib/engine/rest/update.js +110 -0
  63. package/dist/lib/engine/search/search.d.ts +3 -0
  64. package/dist/lib/engine/search/search.d.ts.map +1 -0
  65. package/dist/lib/engine/search/search.js +300 -0
  66. package/dist/lib/engine/write-helpers.d.ts +8 -0
  67. package/dist/lib/engine/write-helpers.d.ts.map +1 -0
  68. package/dist/lib/engine/write-helpers.js +83 -0
  69. package/dist/lib/naming.d.ts +6 -0
  70. package/dist/lib/naming.d.ts.map +1 -0
  71. package/dist/lib/naming.js +23 -0
  72. package/dist/lib/schema/bulk-delete.d.ts +10 -0
  73. package/dist/lib/schema/bulk-delete.d.ts.map +1 -0
  74. package/dist/lib/schema/bulk-delete.js +16 -0
  75. package/dist/lib/schema/bulk-upsert.d.ts +5 -0
  76. package/dist/lib/schema/bulk-upsert.d.ts.map +1 -0
  77. package/dist/lib/schema/bulk-upsert.js +43 -0
  78. package/dist/lib/schema/helpers.d.ts +14 -0
  79. package/dist/lib/schema/helpers.d.ts.map +1 -0
  80. package/dist/lib/schema/helpers.js +33 -0
  81. package/dist/lib/schema/insert.d.ts +5 -0
  82. package/dist/lib/schema/insert.d.ts.map +1 -0
  83. package/dist/lib/schema/insert.js +43 -0
  84. package/dist/lib/schema/search.d.ts +14 -0
  85. package/dist/lib/schema/search.d.ts.map +1 -0
  86. package/dist/lib/schema/search.js +99 -0
  87. package/dist/lib/schema/update.d.ts +5 -0
  88. package/dist/lib/schema/update.d.ts.map +1 -0
  89. package/dist/lib/schema/update.js +47 -0
  90. package/dist/lib/setup-swagger.d.ts +4 -0
  91. package/dist/lib/setup-swagger.d.ts.map +1 -0
  92. package/dist/lib/setup-swagger.js +27 -0
  93. package/dist/lib/sql-api.d.ts +49 -0
  94. package/dist/lib/sql-api.d.ts.map +1 -0
  95. package/dist/lib/sql-api.js +124 -0
  96. package/dist/lib/table-helpers.d.ts +17 -0
  97. package/dist/lib/table-helpers.d.ts.map +1 -0
  98. package/dist/lib/table-helpers.js +34 -0
  99. package/dist/lib/tenant.d.ts +19 -0
  100. package/dist/lib/tenant.d.ts.map +1 -0
  101. package/dist/lib/tenant.js +110 -0
  102. package/dist/routes/auto/bulk-delete.routes.d.ts +4 -0
  103. package/dist/routes/auto/bulk-delete.routes.d.ts.map +1 -0
  104. package/dist/routes/auto/bulk-delete.routes.js +27 -0
  105. package/dist/routes/auto/bulk-upsert.routes.d.ts +4 -0
  106. package/dist/routes/auto/bulk-upsert.routes.d.ts.map +1 -0
  107. package/dist/routes/auto/bulk-upsert.routes.js +25 -0
  108. package/dist/routes/auto/delete.routes.d.ts +4 -0
  109. package/dist/routes/auto/delete.routes.d.ts.map +1 -0
  110. package/dist/routes/auto/delete.routes.js +29 -0
  111. package/dist/routes/auto/get.routes.d.ts +4 -0
  112. package/dist/routes/auto/get.routes.d.ts.map +1 -0
  113. package/dist/routes/auto/get.routes.js +26 -0
  114. package/dist/routes/auto/insert.routes.d.ts +4 -0
  115. package/dist/routes/auto/insert.routes.d.ts.map +1 -0
  116. package/dist/routes/auto/insert.routes.js +29 -0
  117. package/dist/routes/auto/plugin.d.ts +11 -0
  118. package/dist/routes/auto/plugin.d.ts.map +1 -0
  119. package/dist/routes/auto/plugin.js +81 -0
  120. package/dist/routes/auto/route-helpers.d.ts +7 -0
  121. package/dist/routes/auto/route-helpers.d.ts.map +1 -0
  122. package/dist/routes/auto/route-helpers.js +12 -0
  123. package/dist/routes/auto/search.routes.d.ts +4 -0
  124. package/dist/routes/auto/search.routes.d.ts.map +1 -0
  125. package/dist/routes/auto/search.routes.js +50 -0
  126. package/dist/routes/auto/update.routes.d.ts +4 -0
  127. package/dist/routes/auto/update.routes.d.ts.map +1 -0
  128. package/dist/routes/auto/update.routes.js +30 -0
  129. package/dist/types.d.ts +240 -0
  130. package/dist/types.d.ts.map +1 -0
  131. package/dist/types.js +7 -0
  132. package/package.json +106 -0
@@ -0,0 +1,17 @@
1
+ export type DialectName = 'postgres' | 'mysql' | 'mariadb';
2
+ export interface SqlDialect {
3
+ name: DialectName;
4
+ /** Quote identifier: "id" (postgres) or `id` (mysql/mariadb) */
5
+ qi(id: string): string;
6
+ /** Placeholder: $1 (postgres) or ? (mysql/mariadb) */
7
+ ph(index: number): string;
8
+ /** ConditionBuilder dialect name */
9
+ cbDialect: 'postgres' | 'mysql';
10
+ supportsReturning: boolean;
11
+ /** ON CONFLICT / ON DUPLICATE KEY syntax */
12
+ upsertSql(quotedConflictKeys: string, quotedUpdateSet: string): string;
13
+ /** RETURNING pk_col suffix or empty string */
14
+ returningPk(quotedPkCol: string): string;
15
+ }
16
+ export declare function getDialect(name: DialectName): SqlDialect;
17
+ //# sourceMappingURL=dialect.d.ts.map
@@ -0,0 +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"}
@@ -0,0 +1,56 @@
1
+ const PostgresDialect = {
2
+ name: 'postgres',
3
+ qi: (id) => `"${id.replace(/"/g, '""')}"`,
4
+ ph: (i) => `$${i}`,
5
+ cbDialect: 'postgres',
6
+ supportsReturning: true,
7
+ upsertSql(quotedConflictKeys, quotedUpdateSet) {
8
+ if (!quotedUpdateSet)
9
+ return `ON CONFLICT (${quotedConflictKeys}) DO NOTHING`;
10
+ return `ON CONFLICT (${quotedConflictKeys}) DO UPDATE SET ${quotedUpdateSet}`;
11
+ },
12
+ returningPk(quotedPkCol) {
13
+ return ` RETURNING ${quotedPkCol}`;
14
+ },
15
+ };
16
+ const MysqlDialect = {
17
+ name: 'mysql',
18
+ qi: (id) => `\`${id.replace(/`/g, '``')}\``,
19
+ ph: () => '?',
20
+ cbDialect: 'mysql',
21
+ supportsReturning: false,
22
+ upsertSql(_quotedConflictKeys, quotedUpdateSet) {
23
+ if (!quotedUpdateSet)
24
+ return `ON DUPLICATE KEY UPDATE ${_quotedConflictKeys.split(', ')[0]} = ${_quotedConflictKeys.split(', ')[0]}`;
25
+ return `ON DUPLICATE KEY UPDATE ${quotedUpdateSet}`;
26
+ },
27
+ returningPk() {
28
+ return '';
29
+ },
30
+ };
31
+ const MariadbDialect = {
32
+ name: 'mariadb',
33
+ qi: (id) => `\`${id.replace(/`/g, '``')}\``,
34
+ ph: () => '?',
35
+ cbDialect: 'mysql',
36
+ supportsReturning: true,
37
+ upsertSql(_quotedConflictKeys, quotedUpdateSet) {
38
+ if (!quotedUpdateSet)
39
+ return `ON DUPLICATE KEY UPDATE ${_quotedConflictKeys.split(', ')[0]} = ${_quotedConflictKeys.split(', ')[0]}`;
40
+ return `ON DUPLICATE KEY UPDATE ${quotedUpdateSet}`;
41
+ },
42
+ returningPk(quotedPkCol) {
43
+ return ` RETURNING ${quotedPkCol}`;
44
+ },
45
+ };
46
+ const dialects = {
47
+ postgres: PostgresDialect,
48
+ mysql: MysqlDialect,
49
+ mariadb: MariadbDialect,
50
+ };
51
+ export function getDialect(name) {
52
+ const d = dialects[name];
53
+ if (!d)
54
+ throw new Error(`Unknown dialect: ${name}`);
55
+ return d;
56
+ }
@@ -0,0 +1,3 @@
1
+ import type { BulkDeleteParams, BulkDeleteResult } from '../../../types.js';
2
+ export declare function bulkDeleteEngine(params: BulkDeleteParams): Promise<BulkDeleteResult[]>;
3
+ //# sourceMappingURL=bulk-delete.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bulk-delete.d.ts","sourceRoot":"","sources":["../../../../src/lib/engine/bulk/bulk-delete.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAE5E,wBAAsB,gBAAgB,CAAC,MAAM,EAAE,gBAAgB,GAAG,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAiC5F"}
@@ -0,0 +1,30 @@
1
+ import { ConditionBuilder } from 'node-condition-builder';
2
+ import { buildTenantDeleteWhere } from '../../tenant.js';
3
+ import { primaryAsString } from '../../../types.js';
4
+ export async function bulkDeleteEngine(params) {
5
+ const { db, tableConf, ids, tenant } = params;
6
+ if (!ids.length)
7
+ return [];
8
+ const pk = primaryAsString(tableConf.primary);
9
+ const pkCol = tableConf.Schema.col(pk);
10
+ const tableName = tableConf.Schema.tableName;
11
+ let where;
12
+ let values;
13
+ if (tenant) {
14
+ ({ where, values } = buildTenantDeleteWhere(db, tableName, pkCol, ids, tenant));
15
+ }
16
+ else {
17
+ const cb = new ConditionBuilder('AND');
18
+ cb.isIn(db.qi(pkCol), ids);
19
+ where = cb.build(1, db.ph);
20
+ values = cb.getValues();
21
+ }
22
+ const result = await db.query(`DELETE FROM ${db.qi(tableName)} WHERE ${where}`, values);
23
+ // Return PK-only for each deleted record
24
+ if (result.affectedRows === ids.length) {
25
+ return ids.map((id) => ({ main: { [pk]: id } }));
26
+ }
27
+ // Partial delete: best effort (no RETURNING on MySQL)
28
+ const deletedCount = result.affectedRows;
29
+ return ids.slice(0, deletedCount).map((id) => ({ main: { [pk]: id } }));
30
+ }
@@ -0,0 +1,3 @@
1
+ import type { BulkUpsertParams, BulkUpsertResult } from '../../../types.js';
2
+ export declare function bulkUpsertEngine(params: BulkUpsertParams): Promise<BulkUpsertResult[]>;
3
+ //# sourceMappingURL=bulk-upsert.d.ts.map
@@ -0,0 +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"}
@@ -0,0 +1,74 @@
1
+ import { camelcaseObject, snakecaseRecord } from '../../naming.js';
2
+ import { removeExcludedFields, processSecondaries, processDeletions } from '../write-helpers.js';
3
+ import { injectTenantValue, validateTenantFK } from '../../tenant.js';
4
+ import { primaryAsString } from '../../../types.js';
5
+ export async function bulkUpsertEngine(params) {
6
+ const { db, tableConf, dbTables, request, items, tenant } = params;
7
+ if (!items.length)
8
+ return [];
9
+ 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;
16
+ });
17
+ // 1b. Tenant: inject or validate FK
18
+ if (tenant) {
19
+ if ('through' in tenant.scope) {
20
+ // Indirect: collect all FK values, validate in batch
21
+ const scope = tenant.scope;
22
+ const fkCol = scope.through.localField;
23
+ const fkValues = preparedMains.map((rec) => rec[fkCol]);
24
+ await validateTenantFK(db, scope, tenant.ids, fkValues);
25
+ }
26
+ else {
27
+ for (const rec of preparedMains) {
28
+ injectTenantValue(rec, tenant.scope, tenant.ids);
29
+ }
30
+ }
31
+ }
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);
40
+ let pkRows;
41
+ if (upsertKeys) {
42
+ const conflictCols = upsertKeys.map((k) => tableConf.Schema.col(k));
43
+ pkRows = await db.bulkInsertOrUpdate(tableConf.Schema.tableName, preparedMains, conflictCols, pkCol);
44
+ }
45
+ else {
46
+ pkRows = await db.bulkInsert(tableConf.Schema.tableName, preparedMains, pkCol);
47
+ }
48
+ const pksCamel = pkRows.map((r) => camelcaseObject(r));
49
+ // 4. Process secondaries and deletions per item
50
+ const results = [];
51
+ for (let i = 0; i < items.length; i++) {
52
+ 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 };
57
+ if (item.secondaries && Object.keys(item.secondaries).length > 0) {
58
+ const sec = await processSecondaries(db, tableConf, dbTables, mainForFK, item.secondaries);
59
+ if (Object.keys(sec).length > 0)
60
+ result.secondaries = sec;
61
+ }
62
+ if (item.deletions && Object.keys(item.deletions).length > 0) {
63
+ const del = await processDeletions(db, tableConf, item.deletions);
64
+ if (Object.keys(del).length > 0)
65
+ result.deletions = del;
66
+ }
67
+ // 5. afterInsert hook per item
68
+ if (tableConf.afterInsert) {
69
+ await tableConf.afterInsert(db, request, mainForFK, result.secondaries);
70
+ }
71
+ results.push(result);
72
+ }
73
+ return results;
74
+ }
@@ -0,0 +1,3 @@
1
+ import type { DeleteParams, DeleteResult } from '../../../types.js';
2
+ export declare function deleteEngine(params: DeleteParams): Promise<DeleteResult>;
3
+ //# sourceMappingURL=delete.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"delete.d.ts","sourceRoot":"","sources":["../../../../src/lib/engine/rest/delete.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAE,YAAY,EAAY,MAAM,mBAAmB,CAAC;AAE9E,wBAAsB,YAAY,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,YAAY,CAAC,CA8B9E"}
@@ -0,0 +1,25 @@
1
+ import { buildTenantDeleteWhere } from '../../tenant.js';
2
+ import { primaryAsString } from '../../../types.js';
3
+ export async function deleteEngine(params) {
4
+ const { db, tableConf, id, tenant } = params;
5
+ const pk = primaryAsString(tableConf.primary);
6
+ const pkCol = tableConf.Schema.col(pk);
7
+ const tableName = tableConf.Schema.tableName;
8
+ if (!tenant) {
9
+ const affectedRows = await db.delete(tableName, { [pkCol]: id });
10
+ if (affectedRows === 0) {
11
+ const err = new Error(`Record not found: ${id}`);
12
+ err.statusCode = 404;
13
+ throw err;
14
+ }
15
+ return { main: { [pk]: id } };
16
+ }
17
+ const { where, values } = buildTenantDeleteWhere(db, tableName, pkCol, id, tenant);
18
+ const result = await db.query(`DELETE FROM ${db.qi(tableName)} WHERE ${where}`, values);
19
+ if (result.affectedRows === 0) {
20
+ const err = new Error(`Record not found: ${id}`);
21
+ err.statusCode = 404;
22
+ throw err;
23
+ }
24
+ return { main: { [pk]: id } };
25
+ }
@@ -0,0 +1,3 @@
1
+ import type { GetParams, GetResult } from '../../../types.js';
2
+ export declare function getEngine(params: GetParams): Promise<GetResult>;
3
+ //# sourceMappingURL=get.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"get.d.ts","sourceRoot":"","sources":["../../../../src/lib/engine/rest/get.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAuB,MAAM,mBAAmB,CAAC;AAEnF,wBAAsB,SAAS,CAAC,MAAM,EAAE,SAAS,GAAG,OAAO,CAAC,SAAS,CAAC,CAiCrE"}
@@ -0,0 +1,32 @@
1
+ import { ConditionBuilder } from 'node-condition-builder';
2
+ import { camelcaseObject } from '../../naming.js';
3
+ import { buildTenantCondition, buildTenantJoin } from '../../tenant.js';
4
+ import { primaryAsString } from '../../../types.js';
5
+ export async function getEngine(params) {
6
+ const { db, tableConf, id, tenant } = params;
7
+ const pkCol = tableConf.Schema.col(primaryAsString(tableConf.primary));
8
+ const cb = new ConditionBuilder('AND');
9
+ cb.isEqual(db.qi(pkCol), id);
10
+ const joins = [];
11
+ if (tenant) {
12
+ cb.append(buildTenantCondition(db, tenant.scope, tenant.ids));
13
+ if ('through' in tenant.scope) {
14
+ joins.push(buildTenantJoin(db, tenant.scope, tableConf.Schema.tableName));
15
+ }
16
+ }
17
+ const where = cb.build(1, db.ph);
18
+ const values = cb.getValues();
19
+ const rows = await db.select({
20
+ tableName: tableConf.Schema.tableName,
21
+ where,
22
+ values,
23
+ limit: '1',
24
+ joins: joins.length > 0 ? joins : undefined,
25
+ });
26
+ if (rows.length === 0) {
27
+ const err = new Error(`Record not found: ${id}`);
28
+ err.statusCode = 404;
29
+ throw err;
30
+ }
31
+ return { main: camelcaseObject(rows[0]) };
32
+ }
@@ -0,0 +1,3 @@
1
+ import type { InsertParams, InsertResult } from '../../../types.js';
2
+ export declare function insertEngine(params: InsertParams): Promise<InsertResult>;
3
+ //# sourceMappingURL=insert.d.ts.map
@@ -0,0 +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"}
@@ -0,0 +1,58 @@
1
+ import { camelcaseObject, snakecaseRecord } from '../../naming.js';
2
+ import { removeExcludedFields, processSecondaries } from '../write-helpers.js';
3
+ import { injectTenantValue, validateTenantFK } from '../../tenant.js';
4
+ import { primaryAsString } from '../../../types.js';
5
+ export async function insertEngine(params) {
6
+ const { db, tableConf, dbTables, request, record, secondaries, tenant } = params;
7
+ const pk = primaryAsString(tableConf.primary);
8
+ const pkCol = tableConf.Schema.col(pk);
9
+ // 1. Prepare main record
10
+ let mainRecord = snakecaseRecord(record);
11
+ mainRecord = removeExcludedFields(mainRecord, tableConf);
12
+ // 1b. Tenant: inject or validate
13
+ if (tenant) {
14
+ if ('through' in tenant.scope) {
15
+ // Indirect: validate FK belongs to tenant
16
+ const scope = tenant.scope;
17
+ const fkCol = scope.through.localField;
18
+ const fkValue = mainRecord[fkCol];
19
+ await validateTenantFK(db, scope, tenant.ids, [fkValue]);
20
+ }
21
+ else {
22
+ injectTenantValue(mainRecord, tenant.scope, tenant.ids);
23
+ }
24
+ }
25
+ // 2. beforeInsert hook
26
+ if (tableConf.beforeInsert) {
27
+ await tableConf.beforeInsert(db, request, mainRecord);
28
+ }
29
+ // 3. Insert main → returns PK-only
30
+ let pkResult;
31
+ const upsertKeys = tableConf.upsertMap?.get(tableConf.Schema);
32
+ if (upsertKeys) {
33
+ const conflictCols = upsertKeys.map((k) => tableConf.Schema.col(k));
34
+ pkResult = await db.insertOrUpdate(tableConf.Schema.tableName, mainRecord, conflictCols, pkCol);
35
+ }
36
+ else {
37
+ pkResult = await db.insert(tableConf.Schema.tableName, mainRecord, pkCol);
38
+ }
39
+ const mainPkCamel = camelcaseObject(pkResult);
40
+ // 4. Secondaries: need full record for FK auto-fill
41
+ let secondaryResults;
42
+ if (secondaries && Object.keys(secondaries).length > 0) {
43
+ // Merge input record + PK for FK auto-fill
44
+ const mainForFK = { ...camelcaseObject(mainRecord), ...mainPkCamel };
45
+ secondaryResults = await processSecondaries(db, tableConf, dbTables, mainForFK, secondaries);
46
+ }
47
+ // 5. afterInsert hook
48
+ if (tableConf.afterInsert) {
49
+ const mainForHook = { ...camelcaseObject(mainRecord), ...mainPkCamel };
50
+ await tableConf.afterInsert(db, request, mainForHook, secondaryResults);
51
+ }
52
+ // 6. Return PK-only
53
+ const result = { main: mainPkCamel };
54
+ if (secondaryResults && Object.keys(secondaryResults).length > 0) {
55
+ result.secondaries = secondaryResults;
56
+ }
57
+ return result;
58
+ }
@@ -0,0 +1,3 @@
1
+ import type { UpdateParams, UpdateResult } from '../../../types.js';
2
+ export declare function updateEngine(params: UpdateParams): Promise<UpdateResult>;
3
+ //# sourceMappingURL=update.d.ts.map
@@ -0,0 +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"}
@@ -0,0 +1,110 @@
1
+ import { camelcaseObject, snakecaseRecord } from '../../naming.js';
2
+ import { processSecondaries, processDeletions } from '../write-helpers.js';
3
+ import { stripTenantColumn, buildTenantCondition, buildTenantJoin } from '../../tenant.js';
4
+ import { ConditionBuilder } from 'node-condition-builder';
5
+ import { primaryAsString } from '../../../types.js';
6
+ export async function updateEngine(params) {
7
+ const { db, tableConf, dbTables, request, record, secondaries, deletions, tenant } = params;
8
+ // 1. Prepare: extract PK, snakecase, build update fields
9
+ const pk = primaryAsString(tableConf.primary);
10
+ const pkCol = tableConf.Schema.col(pk);
11
+ const snaked = snakecaseRecord(record);
12
+ const pkValue = snaked[pkCol];
13
+ if (pkValue == null) {
14
+ const error = new Error(`Primary key "${pk}" is required`);
15
+ error.statusCode = 400;
16
+ throw error;
17
+ }
18
+ const updateFields = { ...snaked };
19
+ delete updateFields[pkCol];
20
+ // 1b. Tenant: strip tenant column from update fields + validate ownership
21
+ if (tenant) {
22
+ stripTenantColumn(updateFields, tenant.scope);
23
+ if ('through' in tenant.scope) {
24
+ // Indirect: verify the record belongs to tenant via subquery
25
+ const scope = tenant.scope;
26
+ const tableName = tableConf.Schema.tableName;
27
+ const cb = new ConditionBuilder('AND');
28
+ cb.isEqual(`${db.qi(tableName)}.${db.qi(pkCol)}`, pkValue);
29
+ cb.append(buildTenantCondition(db, scope, tenant.ids));
30
+ const checkWhere = cb.build(1, db.ph);
31
+ const checkValues = cb.getValues();
32
+ const joinSql = buildTenantJoin(db, scope, tableName);
33
+ const checkSql = `SELECT 1 FROM ${db.qi(tableName)} ${joinSql} WHERE ${checkWhere} LIMIT 1`;
34
+ const checkResult = await db.query(checkSql, checkValues);
35
+ if (checkResult.rows.length === 0) {
36
+ const error = new Error('Record not found');
37
+ error.statusCode = 404;
38
+ throw error;
39
+ }
40
+ }
41
+ }
42
+ // 2. beforeUpdate hook
43
+ if (tableConf.beforeUpdate) {
44
+ await tableConf.beforeUpdate(db, request, updateFields);
45
+ }
46
+ // 3. Update main
47
+ const hasFieldsToUpdate = Object.keys(updateFields).length > 0;
48
+ let extraCondition;
49
+ if (tenant && !('through' in tenant.scope)) {
50
+ // Direct: add tenant to WHERE
51
+ extraCondition = new ConditionBuilder('AND');
52
+ if (tenant.ids.length === 1) {
53
+ extraCondition.isEqual(tenant.scope.column, tenant.ids[0]);
54
+ }
55
+ else {
56
+ extraCondition.isIn(tenant.scope.column, tenant.ids);
57
+ }
58
+ }
59
+ if (hasFieldsToUpdate) {
60
+ const affectedRows = await db.update(tableConf.Schema.tableName, updateFields, { [pkCol]: pkValue }, extraCondition);
61
+ if (affectedRows === 0) {
62
+ const error = new Error(`Record not found`);
63
+ error.statusCode = 404;
64
+ throw error;
65
+ }
66
+ }
67
+ else {
68
+ // No fields to update: verify the record exists (for secondaries/deletions)
69
+ let whereSql = `${db.qi(tableConf.Schema.tableName)}.${db.qi(pkCol)} = ${db.ph(1)}`;
70
+ const whereValues = [pkValue];
71
+ if (extraCondition) {
72
+ whereSql += ` AND ${extraCondition.build(2, db.ph)}`;
73
+ whereValues.push(...extraCondition.getValues());
74
+ }
75
+ const rows = await db.select({
76
+ tableName: tableConf.Schema.tableName,
77
+ where: whereSql,
78
+ values: whereValues,
79
+ limit: '1',
80
+ });
81
+ if (rows.length === 0) {
82
+ const error = new Error(`Record not found`);
83
+ error.statusCode = 404;
84
+ throw error;
85
+ }
86
+ }
87
+ // Build the main response (PK-only)
88
+ const mainResult = { [pk]: pkValue };
89
+ // 4. Secondaries (upsert/insert with FK auto-fill)
90
+ let secondaryResults;
91
+ if (secondaries && Object.keys(secondaries).length > 0) {
92
+ // Merge input fields + PK for FK auto-fill
93
+ const mainForFK = { ...camelcaseObject(snaked) };
94
+ secondaryResults = await processSecondaries(db, tableConf, dbTables, mainForFK, secondaries);
95
+ }
96
+ // 5. Deletions
97
+ let deletionResults;
98
+ if (deletions && Object.keys(deletions).length > 0) {
99
+ deletionResults = await processDeletions(db, tableConf, deletions);
100
+ }
101
+ // 6. Return PK-only
102
+ const result = { main: mainResult };
103
+ if (secondaryResults && Object.keys(secondaryResults).length > 0) {
104
+ result.secondaries = secondaryResults;
105
+ }
106
+ if (deletionResults && Object.keys(deletionResults).length > 0) {
107
+ result.deletions = deletionResults;
108
+ }
109
+ return result;
110
+ }
@@ -0,0 +1,3 @@
1
+ import type { DbTables, SearchParams, SearchResult } from '../../../types.js';
2
+ export declare function searchEngine(dbTables: DbTables, params: SearchParams): Promise<SearchResult>;
3
+ //# sourceMappingURL=search.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"search.d.ts","sourceRoot":"","sources":["../../../../src/lib/engine/search/search.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EACV,QAAQ,EAER,YAAY,EACZ,YAAY,EASb,MAAM,mBAAmB,CAAC;AA+V3B,wBAAsB,YAAY,CAChC,QAAQ,EAAE,QAAQ,EAClB,MAAM,EAAE,YAAY,GACnB,OAAO,CAAC,YAAY,CAAC,CAyDvB"}