@tablecraft/engine 0.1.0-beta.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.
- package/dist/src/__tests__/inputValidator.test.d.ts +2 -0
- package/dist/src/__tests__/inputValidator.test.d.ts.map +1 -0
- package/dist/src/__tests__/inputValidator.test.js +205 -0
- package/dist/src/__tests__/inputValidator.test.js.map +1 -0
- package/dist/src/__tests__/metadataBuilder.test.d.ts +2 -0
- package/dist/src/__tests__/metadataBuilder.test.d.ts.map +1 -0
- package/dist/src/__tests__/metadataBuilder.test.js +221 -0
- package/dist/src/__tests__/metadataBuilder.test.js.map +1 -0
- package/dist/src/core/aggregationBuilder.d.ts +17 -0
- package/dist/src/core/aggregationBuilder.d.ts.map +1 -0
- package/dist/src/core/aggregationBuilder.js +81 -0
- package/dist/src/core/aggregationBuilder.js.map +1 -0
- package/dist/src/core/cursorPagination.d.ts +36 -0
- package/dist/src/core/cursorPagination.d.ts.map +1 -0
- package/dist/src/core/cursorPagination.js +88 -0
- package/dist/src/core/cursorPagination.js.map +1 -0
- package/dist/src/core/datePresets.d.ts +19 -0
- package/dist/src/core/datePresets.d.ts.map +1 -0
- package/dist/src/core/datePresets.js +96 -0
- package/dist/src/core/datePresets.js.map +1 -0
- package/dist/src/core/dialect.d.ts +17 -0
- package/dist/src/core/dialect.d.ts.map +1 -0
- package/dist/src/core/dialect.js +60 -0
- package/dist/src/core/dialect.js.map +1 -0
- package/dist/src/core/fieldSelector.d.ts +19 -0
- package/dist/src/core/fieldSelector.d.ts.map +1 -0
- package/dist/src/core/fieldSelector.js +49 -0
- package/dist/src/core/fieldSelector.js.map +1 -0
- package/dist/src/core/filterBuilder.d.ts +22 -0
- package/dist/src/core/filterBuilder.d.ts.map +1 -0
- package/dist/src/core/filterBuilder.js +112 -0
- package/dist/src/core/filterBuilder.js.map +1 -0
- package/dist/src/core/filterGroupBuilder.d.ts +28 -0
- package/dist/src/core/filterGroupBuilder.d.ts.map +1 -0
- package/dist/src/core/filterGroupBuilder.js +73 -0
- package/dist/src/core/filterGroupBuilder.js.map +1 -0
- package/dist/src/core/groupByBuilder.d.ts +23 -0
- package/dist/src/core/groupByBuilder.d.ts.map +1 -0
- package/dist/src/core/groupByBuilder.js +127 -0
- package/dist/src/core/groupByBuilder.js.map +1 -0
- package/dist/src/core/inputValidator.d.ts +8 -0
- package/dist/src/core/inputValidator.d.ts.map +1 -0
- package/dist/src/core/inputValidator.js +117 -0
- package/dist/src/core/inputValidator.js.map +1 -0
- package/dist/src/core/metadataBuilder.d.ts +91 -0
- package/dist/src/core/metadataBuilder.d.ts.map +1 -0
- package/dist/src/core/metadataBuilder.js +220 -0
- package/dist/src/core/metadataBuilder.js.map +1 -0
- package/dist/src/core/paginationBuilder.d.ts +20 -0
- package/dist/src/core/paginationBuilder.d.ts.map +1 -0
- package/dist/src/core/paginationBuilder.js +42 -0
- package/dist/src/core/paginationBuilder.js.map +1 -0
- package/dist/src/core/queryBuilder.d.ts +20 -0
- package/dist/src/core/queryBuilder.d.ts.map +1 -0
- package/dist/src/core/queryBuilder.js +163 -0
- package/dist/src/core/queryBuilder.js.map +1 -0
- package/dist/src/core/recursiveBuilder.d.ts +25 -0
- package/dist/src/core/recursiveBuilder.d.ts.map +1 -0
- package/dist/src/core/recursiveBuilder.js +86 -0
- package/dist/src/core/recursiveBuilder.js.map +1 -0
- package/dist/src/core/relationBuilder.d.ts +19 -0
- package/dist/src/core/relationBuilder.d.ts.map +1 -0
- package/dist/src/core/relationBuilder.js +118 -0
- package/dist/src/core/relationBuilder.js.map +1 -0
- package/dist/src/core/roleFilter.d.ts +11 -0
- package/dist/src/core/roleFilter.d.ts.map +1 -0
- package/dist/src/core/roleFilter.js +24 -0
- package/dist/src/core/roleFilter.js.map +1 -0
- package/dist/src/core/searchBuilder.d.ts +17 -0
- package/dist/src/core/searchBuilder.d.ts.map +1 -0
- package/dist/src/core/searchBuilder.js +71 -0
- package/dist/src/core/searchBuilder.js.map +1 -0
- package/dist/src/core/softDelete.d.ts +12 -0
- package/dist/src/core/softDelete.d.ts.map +1 -0
- package/dist/src/core/softDelete.js +29 -0
- package/dist/src/core/softDelete.js.map +1 -0
- package/dist/src/core/sortBuilder.d.ts +14 -0
- package/dist/src/core/sortBuilder.d.ts.map +1 -0
- package/dist/src/core/sortBuilder.js +58 -0
- package/dist/src/core/sortBuilder.js.map +1 -0
- package/dist/src/core/subqueryBuilder.d.ts +13 -0
- package/dist/src/core/subqueryBuilder.d.ts.map +1 -0
- package/dist/src/core/subqueryBuilder.js +47 -0
- package/dist/src/core/subqueryBuilder.js.map +1 -0
- package/dist/src/core/validator.d.ts +18 -0
- package/dist/src/core/validator.d.ts.map +1 -0
- package/dist/src/core/validator.js +88 -0
- package/dist/src/core/validator.js.map +1 -0
- package/dist/src/define.d.ts +274 -0
- package/dist/src/define.d.ts.map +1 -0
- package/dist/src/define.js +690 -0
- package/dist/src/define.js.map +1 -0
- package/dist/src/engine.d.ts +17 -0
- package/dist/src/engine.d.ts.map +1 -0
- package/dist/src/engine.js +429 -0
- package/dist/src/engine.js.map +1 -0
- package/dist/src/errors.d.ts +53 -0
- package/dist/src/errors.d.ts.map +1 -0
- package/dist/src/errors.js +80 -0
- package/dist/src/errors.js.map +1 -0
- package/dist/src/index.d.ts +37 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +41 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/types/engine.d.ts +92 -0
- package/dist/src/types/engine.d.ts.map +1 -0
- package/dist/src/types/engine.js +2 -0
- package/dist/src/types/engine.js.map +1 -0
- package/dist/src/types/table.d.ts +867 -0
- package/dist/src/types/table.d.ts.map +1 -0
- package/dist/src/types/table.js +198 -0
- package/dist/src/types/table.js.map +1 -0
- package/dist/src/utils/adapterUtils.d.ts +16 -0
- package/dist/src/utils/adapterUtils.d.ts.map +1 -0
- package/dist/src/utils/adapterUtils.js +28 -0
- package/dist/src/utils/adapterUtils.js.map +1 -0
- package/dist/src/utils/codegen.d.ts +7 -0
- package/dist/src/utils/codegen.d.ts.map +1 -0
- package/dist/src/utils/codegen.js +126 -0
- package/dist/src/utils/codegen.js.map +1 -0
- package/dist/src/utils/export.d.ts +15 -0
- package/dist/src/utils/export.d.ts.map +1 -0
- package/dist/src/utils/export.js +42 -0
- package/dist/src/utils/export.js.map +1 -0
- package/dist/src/utils/introspect.d.ts +32 -0
- package/dist/src/utils/introspect.d.ts.map +1 -0
- package/dist/src/utils/introspect.js +174 -0
- package/dist/src/utils/introspect.js.map +1 -0
- package/dist/src/utils/openapi.d.ts +6 -0
- package/dist/src/utils/openapi.d.ts.map +1 -0
- package/dist/src/utils/openapi.js +138 -0
- package/dist/src/utils/openapi.js.map +1 -0
- package/dist/src/utils/operators.d.ts +8 -0
- package/dist/src/utils/operators.d.ts.map +1 -0
- package/dist/src/utils/operators.js +70 -0
- package/dist/src/utils/operators.js.map +1 -0
- package/dist/src/utils/requestParser.d.ts +18 -0
- package/dist/src/utils/requestParser.d.ts.map +1 -0
- package/dist/src/utils/requestParser.js +126 -0
- package/dist/src/utils/requestParser.js.map +1 -0
- package/dist/src/utils/responseFormatter.d.ts +12 -0
- package/dist/src/utils/responseFormatter.d.ts.map +1 -0
- package/dist/src/utils/responseFormatter.js +106 -0
- package/dist/src/utils/responseFormatter.js.map +1 -0
- package/dist/src/utils/typedSql.d.ts +70 -0
- package/dist/src/utils/typedSql.d.ts.map +1 -0
- package/dist/src/utils/typedSql.js +102 -0
- package/dist/src/utils/typedSql.js.map +1 -0
- package/dist/test/columnMeta.test.d.ts +2 -0
- package/dist/test/columnMeta.test.d.ts.map +1 -0
- package/dist/test/columnMeta.test.js +92 -0
- package/dist/test/columnMeta.test.js.map +1 -0
- package/dist/test/core/aggregationBuilder.test.d.ts +2 -0
- package/dist/test/core/aggregationBuilder.test.d.ts.map +1 -0
- package/dist/test/core/aggregationBuilder.test.js +64 -0
- package/dist/test/core/aggregationBuilder.test.js.map +1 -0
- package/dist/test/core/filterBuilder.test.d.ts +2 -0
- package/dist/test/core/filterBuilder.test.d.ts.map +1 -0
- package/dist/test/core/filterBuilder.test.js +80 -0
- package/dist/test/core/filterBuilder.test.js.map +1 -0
- package/dist/test/core/paginationBuilder.test.d.ts +2 -0
- package/dist/test/core/paginationBuilder.test.d.ts.map +1 -0
- package/dist/test/core/paginationBuilder.test.js +63 -0
- package/dist/test/core/paginationBuilder.test.js.map +1 -0
- package/dist/test/core/queryBuilder.test.d.ts +2 -0
- package/dist/test/core/queryBuilder.test.d.ts.map +1 -0
- package/dist/test/core/queryBuilder.test.js +92 -0
- package/dist/test/core/queryBuilder.test.js.map +1 -0
- package/dist/test/core/searchBuilder.test.d.ts +2 -0
- package/dist/test/core/searchBuilder.test.d.ts.map +1 -0
- package/dist/test/core/searchBuilder.test.js +68 -0
- package/dist/test/core/searchBuilder.test.js.map +1 -0
- package/dist/test/core/softDelete.test.d.ts +2 -0
- package/dist/test/core/softDelete.test.d.ts.map +1 -0
- package/dist/test/core/softDelete.test.js +60 -0
- package/dist/test/core/softDelete.test.js.map +1 -0
- package/dist/test/core/sortBuilder.test.d.ts +2 -0
- package/dist/test/core/sortBuilder.test.d.ts.map +1 -0
- package/dist/test/core/sortBuilder.test.js +59 -0
- package/dist/test/core/sortBuilder.test.js.map +1 -0
- package/dist/test/core/subqueryBuilder.test.d.ts +2 -0
- package/dist/test/core/subqueryBuilder.test.d.ts.map +1 -0
- package/dist/test/core/subqueryBuilder.test.js +48 -0
- package/dist/test/core/subqueryBuilder.test.js.map +1 -0
- package/dist/test/core/validator.test.d.ts +2 -0
- package/dist/test/core/validator.test.d.ts.map +1 -0
- package/dist/test/core/validator.test.js +81 -0
- package/dist/test/core/validator.test.js.map +1 -0
- package/dist/test/datePresets.test.d.ts +2 -0
- package/dist/test/datePresets.test.d.ts.map +1 -0
- package/dist/test/datePresets.test.js +57 -0
- package/dist/test/datePresets.test.js.map +1 -0
- package/dist/test/errors.test.d.ts +2 -0
- package/dist/test/errors.test.d.ts.map +1 -0
- package/dist/test/errors.test.js +62 -0
- package/dist/test/errors.test.js.map +1 -0
- package/dist/test/inputValidator.test.d.ts +2 -0
- package/dist/test/inputValidator.test.d.ts.map +1 -0
- package/dist/test/inputValidator.test.js +60 -0
- package/dist/test/inputValidator.test.js.map +1 -0
- package/dist/test/metadata.test.d.ts +2 -0
- package/dist/test/metadata.test.d.ts.map +1 -0
- package/dist/test/metadata.test.js +285 -0
- package/dist/test/metadata.test.js.map +1 -0
- package/dist/test/metadataComplete.test.d.ts +2 -0
- package/dist/test/metadataComplete.test.d.ts.map +1 -0
- package/dist/test/metadataComplete.test.js +113 -0
- package/dist/test/metadataComplete.test.js.map +1 -0
- package/dist/test/roleFilter.test.d.ts +2 -0
- package/dist/test/roleFilter.test.d.ts.map +1 -0
- package/dist/test/roleFilter.test.js +53 -0
- package/dist/test/roleFilter.test.js.map +1 -0
- package/dist/test/typedSql.test.d.ts +2 -0
- package/dist/test/typedSql.test.d.ts.map +1 -0
- package/dist/test/typedSql.test.js +63 -0
- package/dist/test/typedSql.test.js.map +1 -0
- package/dist/test/utils/codegen.test.d.ts +2 -0
- package/dist/test/utils/codegen.test.d.ts.map +1 -0
- package/dist/test/utils/codegen.test.js +65 -0
- package/dist/test/utils/codegen.test.js.map +1 -0
- package/dist/test/utils/export.test.d.ts +2 -0
- package/dist/test/utils/export.test.d.ts.map +1 -0
- package/dist/test/utils/export.test.js +54 -0
- package/dist/test/utils/export.test.js.map +1 -0
- package/dist/test/utils/openapi.test.d.ts +2 -0
- package/dist/test/utils/openapi.test.d.ts.map +1 -0
- package/dist/test/utils/openapi.test.js +65 -0
- package/dist/test/utils/openapi.test.js.map +1 -0
- package/dist/test/utils/requestParser.test.d.ts +2 -0
- package/dist/test/utils/requestParser.test.d.ts.map +1 -0
- package/dist/test/utils/requestParser.test.js +89 -0
- package/dist/test/utils/requestParser.test.js.map +1 -0
- package/dist/test/utils/responseFormatter.test.d.ts +2 -0
- package/dist/test/utils/responseFormatter.test.d.ts.map +1 -0
- package/dist/test/utils/responseFormatter.test.js +79 -0
- package/dist/test/utils/responseFormatter.test.js.map +1 -0
- package/package.json +42 -0
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { sql, getTableColumns, or, ilike, } from 'drizzle-orm';
|
|
2
|
+
import { escapeLikePattern } from '../utils/operators';
|
|
3
|
+
export class SearchBuilder {
|
|
4
|
+
schema;
|
|
5
|
+
constructor(schema) {
|
|
6
|
+
this.schema = schema;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Builds a search condition across multiple columns using OR + ILIKE.
|
|
10
|
+
*/
|
|
11
|
+
buildSearch(config, searchTerm) {
|
|
12
|
+
if (!searchTerm || searchTerm.trim().length === 0) {
|
|
13
|
+
return undefined;
|
|
14
|
+
}
|
|
15
|
+
if (!config.search || !config.search.enabled) {
|
|
16
|
+
return undefined;
|
|
17
|
+
}
|
|
18
|
+
const searchFields = config.search.fields;
|
|
19
|
+
if (!searchFields || searchFields.length === 0) {
|
|
20
|
+
return undefined;
|
|
21
|
+
}
|
|
22
|
+
const table = this.schema[config.base];
|
|
23
|
+
if (!table)
|
|
24
|
+
return undefined;
|
|
25
|
+
const baseColumns = getTableColumns(table);
|
|
26
|
+
const escaped = escapeLikePattern(searchTerm.trim());
|
|
27
|
+
const conditions = [];
|
|
28
|
+
for (const fieldName of searchFields) {
|
|
29
|
+
const col = this.resolveColumn(baseColumns, fieldName);
|
|
30
|
+
if (!col)
|
|
31
|
+
continue;
|
|
32
|
+
conditions.push(ilike(col, `%${escaped}%`));
|
|
33
|
+
}
|
|
34
|
+
if (conditions.length === 0)
|
|
35
|
+
return undefined;
|
|
36
|
+
// Combine with OR — matching any field is a hit
|
|
37
|
+
return or(...conditions);
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Builds a PostgreSQL full-text search condition (tsvector/tsquery).
|
|
41
|
+
* This is an optional enhanced mode — only works on PostgreSQL.
|
|
42
|
+
*/
|
|
43
|
+
buildFullTextSearch(config, searchTerm, language = 'english') {
|
|
44
|
+
if (!searchTerm || searchTerm.trim().length === 0)
|
|
45
|
+
return undefined;
|
|
46
|
+
if (!config.search?.enabled)
|
|
47
|
+
return undefined;
|
|
48
|
+
const fields = config.search.fields;
|
|
49
|
+
if (!fields || fields.length === 0)
|
|
50
|
+
return undefined;
|
|
51
|
+
// Concatenate all search fields into a single tsvector
|
|
52
|
+
const concatParts = fields.map((f) => sql `coalesce(${sql.identifier(f)}::text, '')`);
|
|
53
|
+
const concatenated = sql.join(concatParts, sql ` || ' ' || `);
|
|
54
|
+
return sql `to_tsvector(${sql.raw(`'${language}'`)}, ${concatenated}) @@ plainto_tsquery(${sql.raw(`'${language}'`)}, ${searchTerm.trim()})`;
|
|
55
|
+
}
|
|
56
|
+
resolveColumn(baseColumns, fieldName) {
|
|
57
|
+
if (baseColumns[fieldName])
|
|
58
|
+
return baseColumns[fieldName];
|
|
59
|
+
// Joined table column: "table.column"
|
|
60
|
+
if (fieldName.includes('.')) {
|
|
61
|
+
const [tableName, colName] = fieldName.split('.');
|
|
62
|
+
const joinedTable = this.schema[tableName];
|
|
63
|
+
if (!joinedTable)
|
|
64
|
+
return undefined;
|
|
65
|
+
const cols = getTableColumns(joinedTable);
|
|
66
|
+
return cols[colName];
|
|
67
|
+
}
|
|
68
|
+
return undefined;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
//# sourceMappingURL=searchBuilder.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"searchBuilder.js","sourceRoot":"","sources":["../../../src/core/searchBuilder.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { SQL } from 'drizzle-orm';
|
|
2
|
+
import { TableConfig } from '../types/table';
|
|
3
|
+
export declare class SoftDeleteHandler {
|
|
4
|
+
private schema;
|
|
5
|
+
constructor(schema: Record<string, unknown>);
|
|
6
|
+
/**
|
|
7
|
+
* Returns a WHERE condition that excludes soft-deleted rows.
|
|
8
|
+
* Returns undefined when soft delete is disabled or overridden.
|
|
9
|
+
*/
|
|
10
|
+
buildSoftDeleteCondition(config: TableConfig, includeDeleted?: boolean): SQL | undefined;
|
|
11
|
+
}
|
|
12
|
+
//# sourceMappingURL=softDelete.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"softDelete.d.ts","sourceRoot":"","sources":["../../../src/core/softDelete.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,GAAG,EAGJ,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAE7C,qBAAa,iBAAiB;IAChB,OAAO,CAAC,MAAM;gBAAN,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAEnD;;;OAGG;IACH,wBAAwB,CACtB,MAAM,EAAE,WAAW,EACnB,cAAc,GAAE,OAAe,GAC9B,GAAG,GAAG,SAAS;CAoBnB"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { getTableColumns, isNull, } from 'drizzle-orm';
|
|
2
|
+
export class SoftDeleteHandler {
|
|
3
|
+
schema;
|
|
4
|
+
constructor(schema) {
|
|
5
|
+
this.schema = schema;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Returns a WHERE condition that excludes soft-deleted rows.
|
|
9
|
+
* Returns undefined when soft delete is disabled or overridden.
|
|
10
|
+
*/
|
|
11
|
+
buildSoftDeleteCondition(config, includeDeleted = false) {
|
|
12
|
+
if (!config.softDelete?.enabled)
|
|
13
|
+
return undefined;
|
|
14
|
+
if (includeDeleted)
|
|
15
|
+
return undefined;
|
|
16
|
+
const table = this.schema[config.base];
|
|
17
|
+
if (!table)
|
|
18
|
+
return undefined;
|
|
19
|
+
const columns = getTableColumns(table);
|
|
20
|
+
const field = config.softDelete.field ?? 'deletedAt';
|
|
21
|
+
const col = columns[field];
|
|
22
|
+
if (!col) {
|
|
23
|
+
console.warn(`[tablecraft] Soft-delete field '${field}' not found in table '${config.base}'`);
|
|
24
|
+
return undefined;
|
|
25
|
+
}
|
|
26
|
+
return isNull(col);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=softDelete.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"softDelete.js","sourceRoot":"","sources":["../../../src/core/softDelete.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,eAAe,EACf,MAAM,GACP,MAAM,aAAa,CAAC;AAGrB,MAAM,OAAO,iBAAiB;IACR;IAApB,YAAoB,MAA+B;QAA/B,WAAM,GAAN,MAAM,CAAyB;IAAG,CAAC;IAEvD;;;OAGG;IACH,wBAAwB,CACtB,MAAmB,EACnB,iBAA0B,KAAK;QAE/B,IAAI,CAAC,MAAM,CAAC,UAAU,EAAE,OAAO;YAAE,OAAO,SAAS,CAAC;QAClD,IAAI,cAAc;YAAE,OAAO,SAAS,CAAC;QAErC,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAU,CAAC;QAChD,IAAI,CAAC,KAAK;YAAE,OAAO,SAAS,CAAC;QAE7B,MAAM,OAAO,GAAG,eAAe,CAAC,KAAK,CAAC,CAAC;QACvC,MAAM,KAAK,GAAG,MAAM,CAAC,UAAU,CAAC,KAAK,IAAI,WAAW,CAAC;QACrD,MAAM,GAAG,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC;QAE3B,IAAI,CAAC,GAAG,EAAE,CAAC;YACT,OAAO,CAAC,IAAI,CACV,mCAAmC,KAAK,yBAAyB,MAAM,CAAC,IAAI,GAAG,CAChF,CAAC;YACF,OAAO,SAAS,CAAC;QACnB,CAAC;QAED,OAAO,MAAM,CAAC,GAAG,CAAC,CAAC;IACrB,CAAC;CACF"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { SQL } from 'drizzle-orm';
|
|
2
|
+
import { TableConfig } from '../types/table';
|
|
3
|
+
import { SortParam } from '../types/engine';
|
|
4
|
+
export declare class SortBuilder {
|
|
5
|
+
private schema;
|
|
6
|
+
constructor(schema: Record<string, unknown>);
|
|
7
|
+
/**
|
|
8
|
+
* Builds ORDER BY clauses from request params, falling back to defaults.
|
|
9
|
+
* Only allows sorting on columns marked sortable in the config.
|
|
10
|
+
*/
|
|
11
|
+
buildSort(config: TableConfig, params?: SortParam[]): SQL[];
|
|
12
|
+
private getDefaultSort;
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=sortBuilder.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sortBuilder.d.ts","sourceRoot":"","sources":["../../../src/core/sortBuilder.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,GAAG,EAKJ,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAC7C,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAE5C,qBAAa,WAAW;IACV,OAAO,CAAC,MAAM;gBAAN,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAEnD;;;OAGG;IACH,SAAS,CAAC,MAAM,EAAE,WAAW,EAAE,MAAM,CAAC,EAAE,SAAS,EAAE,GAAG,GAAG,EAAE;IA+C3D,OAAO,CAAC,cAAc;CAQvB"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { getTableColumns, asc, desc, } from 'drizzle-orm';
|
|
2
|
+
export class SortBuilder {
|
|
3
|
+
schema;
|
|
4
|
+
constructor(schema) {
|
|
5
|
+
this.schema = schema;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Builds ORDER BY clauses from request params, falling back to defaults.
|
|
9
|
+
* Only allows sorting on columns marked sortable in the config.
|
|
10
|
+
*/
|
|
11
|
+
buildSort(config, params) {
|
|
12
|
+
const sortParams = params && params.length > 0 ? params : this.getDefaultSort(config);
|
|
13
|
+
if (!sortParams || sortParams.length === 0)
|
|
14
|
+
return [];
|
|
15
|
+
const table = this.schema[config.base];
|
|
16
|
+
if (!table)
|
|
17
|
+
return [];
|
|
18
|
+
const columns = getTableColumns(table);
|
|
19
|
+
// Build a whitelist of sortable fields
|
|
20
|
+
const sortableFields = new Set();
|
|
21
|
+
for (const col of config.columns) {
|
|
22
|
+
if (col.sortable !== false) {
|
|
23
|
+
sortableFields.add(col.name);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
const clauses = [];
|
|
27
|
+
for (const sp of sortParams) {
|
|
28
|
+
if (!sortableFields.has(sp.field))
|
|
29
|
+
continue;
|
|
30
|
+
const colConfig = config.columns.find(c => c.name === sp.field);
|
|
31
|
+
const dbFieldName = colConfig?.field ?? sp.field;
|
|
32
|
+
let col;
|
|
33
|
+
if (dbFieldName.includes('.')) {
|
|
34
|
+
const [tableName, colName] = dbFieldName.split('.');
|
|
35
|
+
const joinedTable = this.schema[tableName];
|
|
36
|
+
if (joinedTable) {
|
|
37
|
+
col = getTableColumns(joinedTable)[colName];
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
col = columns[dbFieldName];
|
|
42
|
+
}
|
|
43
|
+
if (!col)
|
|
44
|
+
continue;
|
|
45
|
+
clauses.push(sp.order === 'desc' ? desc(col) : asc(col));
|
|
46
|
+
}
|
|
47
|
+
return clauses;
|
|
48
|
+
}
|
|
49
|
+
getDefaultSort(config) {
|
|
50
|
+
if (!config.defaultSort || config.defaultSort.length === 0)
|
|
51
|
+
return undefined;
|
|
52
|
+
return config.defaultSort.map((s) => ({
|
|
53
|
+
field: s.field,
|
|
54
|
+
order: s.order ?? 'asc',
|
|
55
|
+
}));
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=sortBuilder.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sortBuilder.js","sourceRoot":"","sources":["../../../src/core/sortBuilder.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,eAAe,EACf,GAAG,EACH,IAAI,GAEL,MAAM,aAAa,CAAC;AAIrB,MAAM,OAAO,WAAW;IACF;IAApB,YAAoB,MAA+B;QAA/B,WAAM,GAAN,MAAM,CAAyB;IAAG,CAAC;IAEvD;;;OAGG;IACH,SAAS,CAAC,MAAmB,EAAE,MAAoB;QACjD,MAAM,UAAU,GACd,MAAM,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC;QAErE,IAAI,CAAC,UAAU,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,EAAE,CAAC;QAEtD,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAU,CAAC;QAChD,IAAI,CAAC,KAAK;YAAE,OAAO,EAAE,CAAC;QAEtB,MAAM,OAAO,GAAG,eAAe,CAAC,KAAK,CAAC,CAAC;QAEvC,uCAAuC;QACvC,MAAM,cAAc,GAAG,IAAI,GAAG,EAAU,CAAC;QACzC,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;YACjC,IAAI,GAAG,CAAC,QAAQ,KAAK,KAAK,EAAE,CAAC;gBAC3B,cAAc,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;YAC/B,CAAC;QACH,CAAC;QAED,MAAM,OAAO,GAAU,EAAE,CAAC;QAE1B,KAAK,MAAM,EAAE,IAAI,UAAU,EAAE,CAAC;YAC5B,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,EAAE,CAAC,KAAK,CAAC;gBAAE,SAAS;YAE5C,MAAM,SAAS,GAAG,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,EAAE,CAAC,KAAK,CAAC,CAAC;YAChE,MAAM,WAAW,GAAG,SAAS,EAAE,KAAK,IAAI,EAAE,CAAC,KAAK,CAAC;YAEjD,IAAI,GAAuB,CAAC;YAE5B,IAAI,WAAW,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;gBAC7B,MAAM,CAAC,SAAS,EAAE,OAAO,CAAC,GAAG,WAAW,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;gBACpD,MAAM,WAAW,GAAG,IAAI,CAAC,MAAM,CAAC,SAAS,CAAU,CAAC;gBACpD,IAAI,WAAW,EAAE,CAAC;oBACd,GAAG,GAAG,eAAe,CAAC,WAAW,CAAC,CAAC,OAAO,CAAC,CAAC;gBAChD,CAAC;YACJ,CAAC;iBAAM,CAAC;gBACL,GAAG,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC;YAC9B,CAAC;YAED,IAAI,CAAC,GAAG;gBAAE,SAAS;YAEnB,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC,KAAK,KAAK,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC;QAC3D,CAAC;QAED,OAAO,OAAO,CAAC;IACjB,CAAC;IAEO,cAAc,CAAC,MAAmB;QACxC,IAAI,CAAC,MAAM,CAAC,WAAW,IAAI,MAAM,CAAC,WAAW,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,SAAS,CAAC;QAE7E,OAAO,MAAM,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YACpC,KAAK,EAAE,CAAC,CAAC,KAAK;YACd,KAAK,EAAE,CAAC,CAAC,KAAK,IAAI,KAAK;SACxB,CAAC,CAAC,CAAC;IACN,CAAC;CACF"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { SQL } from 'drizzle-orm';
|
|
2
|
+
import { TableConfig } from '../types/table';
|
|
3
|
+
export declare class SubqueryBuilder {
|
|
4
|
+
private schema;
|
|
5
|
+
constructor(schema: Record<string, unknown>);
|
|
6
|
+
/**
|
|
7
|
+
* Builds correlated subquery select expressions.
|
|
8
|
+
* Supports count, exists, and first subquery types.
|
|
9
|
+
*/
|
|
10
|
+
buildSubqueries(config: TableConfig): Record<string, SQL> | undefined;
|
|
11
|
+
private buildSingle;
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=subqueryBuilder.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"subqueryBuilder.d.ts","sourceRoot":"","sources":["../../../src/core/subqueryBuilder.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,GAAG,EAEJ,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,WAAW,EAAkB,MAAM,gBAAgB,CAAC;AAE7D,qBAAa,eAAe;IACd,OAAO,CAAC,MAAM;gBAAN,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAEnD;;;OAGG;IACH,eAAe,CAAC,MAAM,EAAE,WAAW,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,SAAS;IAuBrE,OAAO,CAAC,WAAW;CAiBpB"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { sql, } from 'drizzle-orm';
|
|
2
|
+
export class SubqueryBuilder {
|
|
3
|
+
schema;
|
|
4
|
+
constructor(schema) {
|
|
5
|
+
this.schema = schema;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Builds correlated subquery select expressions.
|
|
9
|
+
* Supports count, exists, and first subquery types.
|
|
10
|
+
*/
|
|
11
|
+
buildSubqueries(config) {
|
|
12
|
+
if (!config.subqueries || config.subqueries.length === 0) {
|
|
13
|
+
return undefined;
|
|
14
|
+
}
|
|
15
|
+
const baseTable = this.schema[config.base];
|
|
16
|
+
if (!baseTable)
|
|
17
|
+
return undefined;
|
|
18
|
+
const result = {};
|
|
19
|
+
for (const sub of config.subqueries) {
|
|
20
|
+
const subTable = this.schema[sub.table];
|
|
21
|
+
if (!subTable)
|
|
22
|
+
continue;
|
|
23
|
+
const subQuery = this.buildSingle(sub, subTable);
|
|
24
|
+
if (subQuery) {
|
|
25
|
+
result[sub.alias] = subQuery;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return Object.keys(result).length > 0 ? result : undefined;
|
|
29
|
+
}
|
|
30
|
+
buildSingle(sub, subTable) {
|
|
31
|
+
// The filter string is a raw SQL correlation condition
|
|
32
|
+
// e.g. "orders.user_id = users.id"
|
|
33
|
+
// TODO: replace with a structured, parameterised representation
|
|
34
|
+
const filterSql = sub.filter ? sql.raw(sub.filter) : sql `true`;
|
|
35
|
+
switch (sub.type) {
|
|
36
|
+
case 'count':
|
|
37
|
+
return sql `(SELECT count(*) FROM ${subTable} WHERE ${filterSql})`;
|
|
38
|
+
case 'exists':
|
|
39
|
+
return sql `EXISTS (SELECT 1 FROM ${subTable} WHERE ${filterSql})`;
|
|
40
|
+
case 'first':
|
|
41
|
+
return sql `(SELECT row_to_json(t) FROM (SELECT * FROM ${subTable} WHERE ${filterSql} LIMIT 1) t)`;
|
|
42
|
+
default:
|
|
43
|
+
return undefined;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=subqueryBuilder.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"subqueryBuilder.js","sourceRoot":"","sources":["../../../src/core/subqueryBuilder.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,GAAG,GACJ,MAAM,aAAa,CAAC;AAGrB,MAAM,OAAO,eAAe;IACN;IAApB,YAAoB,MAA+B;QAA/B,WAAM,GAAN,MAAM,CAAyB;IAAG,CAAC;IAEvD;;;OAGG;IACH,eAAe,CAAC,MAAmB;QACjC,IAAI,CAAC,MAAM,CAAC,UAAU,IAAI,MAAM,CAAC,UAAU,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACzD,OAAO,SAAS,CAAC;QACnB,CAAC;QAED,MAAM,SAAS,GAAG,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAU,CAAC;QACpD,IAAI,CAAC,SAAS;YAAE,OAAO,SAAS,CAAC;QAEjC,MAAM,MAAM,GAAwB,EAAE,CAAC;QAEvC,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,UAAU,EAAE,CAAC;YACpC,MAAM,QAAQ,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAU,CAAC;YACjD,IAAI,CAAC,QAAQ;gBAAE,SAAS;YAExB,MAAM,QAAQ,GAAG,IAAI,CAAC,WAAW,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;YACjD,IAAI,QAAQ,EAAE,CAAC;gBACb,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,QAAQ,CAAC;YAC/B,CAAC;QACH,CAAC;QAED,OAAO,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC;IAC7D,CAAC;IAEO,WAAW,CAAC,GAAmB,EAAE,QAAe;QACtD,uDAAuD;QACvD,mCAAmC;QACnC,gEAAgE;QAChE,MAAM,SAAS,GAAG,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,GAAG,CAAA,MAAM,CAAC;QAE/D,QAAQ,GAAG,CAAC,IAAI,EAAE,CAAC;YACjB,KAAK,OAAO;gBACV,OAAO,GAAG,CAAA,yBAAyB,QAAQ,UAAU,SAAS,GAAG,CAAC;YACpE,KAAK,QAAQ;gBACX,OAAO,GAAG,CAAA,yBAAyB,QAAQ,UAAU,SAAS,GAAG,CAAC;YACpE,KAAK,OAAO;gBACV,OAAO,GAAG,CAAA,8CAA8C,QAAQ,UAAU,SAAS,cAAc,CAAC;YACpG;gBACE,OAAO,SAAS,CAAC;QACrB,CAAC;IACH,CAAC;CACF"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { TableConfig } from '../types/table';
|
|
2
|
+
/**
|
|
3
|
+
* Validates a raw configuration object against the Zod schema.
|
|
4
|
+
* @param config The raw configuration object.
|
|
5
|
+
* @returns The validated TableConfig object.
|
|
6
|
+
* @throws ZodError if validation fails.
|
|
7
|
+
*/
|
|
8
|
+
export declare function validateConfig(config: unknown): TableConfig;
|
|
9
|
+
/**
|
|
10
|
+
* Validates the configuration against the actual Drizzle schema.
|
|
11
|
+
* Ensures that the base table and columns exist.
|
|
12
|
+
* @param config The validated TableConfig object.
|
|
13
|
+
* @param drizzleSchema An object containing Drizzle table definitions.
|
|
14
|
+
* @returns True if valid.
|
|
15
|
+
* @throws Error if validation fails.
|
|
16
|
+
*/
|
|
17
|
+
export declare function validateAgainstSchema(config: TableConfig, drizzleSchema: Record<string, unknown>): boolean;
|
|
18
|
+
//# sourceMappingURL=validator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validator.d.ts","sourceRoot":"","sources":["../../../src/core/validator.ts"],"names":[],"mappings":"AAAA,OAAO,EAAqB,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAGhE;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,OAAO,GAAG,WAAW,CAE3D;AAED;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,WAAW,EACnB,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GACrC,OAAO,CA+ET"}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { TableConfigSchema } from '../types/table';
|
|
2
|
+
import { getTableColumns } from 'drizzle-orm';
|
|
3
|
+
/**
|
|
4
|
+
* Validates a raw configuration object against the Zod schema.
|
|
5
|
+
* @param config The raw configuration object.
|
|
6
|
+
* @returns The validated TableConfig object.
|
|
7
|
+
* @throws ZodError if validation fails.
|
|
8
|
+
*/
|
|
9
|
+
export function validateConfig(config) {
|
|
10
|
+
return TableConfigSchema.parse(config);
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Validates the configuration against the actual Drizzle schema.
|
|
14
|
+
* Ensures that the base table and columns exist.
|
|
15
|
+
* @param config The validated TableConfig object.
|
|
16
|
+
* @param drizzleSchema An object containing Drizzle table definitions.
|
|
17
|
+
* @returns True if valid.
|
|
18
|
+
* @throws Error if validation fails.
|
|
19
|
+
*/
|
|
20
|
+
export function validateAgainstSchema(config, drizzleSchema // Using unknown here as we'll cast to Drizzle Table
|
|
21
|
+
) {
|
|
22
|
+
// 1. Verify base table exists
|
|
23
|
+
const baseTable = drizzleSchema[config.base];
|
|
24
|
+
if (!baseTable) {
|
|
25
|
+
throw new Error(`Base table '${config.base}' not found in Drizzle schema.`);
|
|
26
|
+
}
|
|
27
|
+
// Check if it's a valid Drizzle table (simplistic check)
|
|
28
|
+
// In a real app, we might check for specific Drizzle symbols or properties
|
|
29
|
+
if (typeof baseTable !== 'object' || baseTable === null) {
|
|
30
|
+
throw new Error(`Schema entry '${config.base}' is not a valid table object.`);
|
|
31
|
+
}
|
|
32
|
+
// Cast to Table to use getTableColumns
|
|
33
|
+
const table = baseTable;
|
|
34
|
+
try {
|
|
35
|
+
// 2. Verify columns exist in table
|
|
36
|
+
const tableColumns = getTableColumns(table);
|
|
37
|
+
const tableColumnNames = new Set(Object.keys(tableColumns));
|
|
38
|
+
for (const col of config.columns) {
|
|
39
|
+
// Resolve database field name (default to col.name)
|
|
40
|
+
const dbField = col.field ?? col.name;
|
|
41
|
+
// Handle joined columns "table.column"
|
|
42
|
+
if (dbField.includes('.')) {
|
|
43
|
+
const [tableName, colName] = dbField.split('.');
|
|
44
|
+
const joinedTable = drizzleSchema[tableName];
|
|
45
|
+
if (!joinedTable) {
|
|
46
|
+
throw new Error(`Table '${tableName}' referenced in column '${col.name}' not found in schema.`);
|
|
47
|
+
}
|
|
48
|
+
const joinedColumns = getTableColumns(joinedTable);
|
|
49
|
+
if (!joinedColumns[colName]) {
|
|
50
|
+
throw new Error(`Column '${colName}' referenced in '${col.name}' does not exist in table '${tableName}'.`);
|
|
51
|
+
}
|
|
52
|
+
continue; // Validated
|
|
53
|
+
}
|
|
54
|
+
// Check if column is computed (virtual)
|
|
55
|
+
if (col.computed) {
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
// Check if column is a subquery alias
|
|
59
|
+
if (config.subqueries?.some(s => s.alias === col.name)) {
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
// Check if column is an aggregation alias
|
|
63
|
+
if (config.aggregations?.some(a => a.alias === col.name)) {
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
// Validate against base table
|
|
67
|
+
if (!tableColumnNames.has(dbField)) {
|
|
68
|
+
throw new Error(`Column '${dbField}' configured in '${config.name}' does not exist in table '${config.base}'.`);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
// 3. Verify join tables (if any)
|
|
72
|
+
if (config.joins) {
|
|
73
|
+
for (const join of config.joins) {
|
|
74
|
+
const joinTable = drizzleSchema[join.table];
|
|
75
|
+
if (!joinTable) {
|
|
76
|
+
throw new Error(`Joined table '${join.table}' not found in Drizzle schema.`);
|
|
77
|
+
}
|
|
78
|
+
// Recursive validation could happen here if we wanted to validate join columns too
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
catch (e) {
|
|
83
|
+
// getTableColumns throws if it's not a valid table, catch that too
|
|
84
|
+
throw new Error(`Failed to validate against Drizzle schema: ${e.message}`);
|
|
85
|
+
}
|
|
86
|
+
return true;
|
|
87
|
+
}
|
|
88
|
+
//# sourceMappingURL=validator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validator.js","sourceRoot":"","sources":["../../../src/core/validator.ts"],"names":[],"mappings":"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"}
|