@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 @@
|
|
|
1
|
+
{"version":3,"file":"paginationBuilder.d.ts","sourceRoot":"","sources":["../../../src/core/paginationBuilder.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAC7C,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAE7C,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,qBAAa,iBAAiB;IAC5B;;;OAGG;IACH,eAAe,CACb,MAAM,EAAE,WAAW,EACnB,IAAI,CAAC,EAAE,MAAM,EACb,QAAQ,CAAC,EAAE,MAAM,GAChB,gBAAgB;IA2BnB;;OAEG;IACH,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,gBAAgB,GAAG,UAAU;CAUnE"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
export class PaginationBuilder {
|
|
2
|
+
/**
|
|
3
|
+
* Computes LIMIT and OFFSET from page/pageSize params.
|
|
4
|
+
* Enforces min/max boundaries from the config.
|
|
5
|
+
*/
|
|
6
|
+
buildPagination(config, page, pageSize) {
|
|
7
|
+
const pCfg = config.pagination;
|
|
8
|
+
const enabled = pCfg?.enabled ?? true;
|
|
9
|
+
if (!enabled) {
|
|
10
|
+
return {
|
|
11
|
+
limit: Number.MAX_SAFE_INTEGER,
|
|
12
|
+
offset: 0,
|
|
13
|
+
page: 1,
|
|
14
|
+
pageSize: Number.MAX_SAFE_INTEGER,
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
const defaultSize = pCfg?.defaultPageSize ?? 10;
|
|
18
|
+
const maxSize = pCfg?.maxPageSize ?? 100;
|
|
19
|
+
const resolvedPage = Math.max(1, Math.floor(page ?? 1));
|
|
20
|
+
const resolvedSize = Math.min(maxSize, Math.max(1, Math.floor(pageSize ?? defaultSize)));
|
|
21
|
+
return {
|
|
22
|
+
limit: resolvedSize,
|
|
23
|
+
offset: (resolvedPage - 1) * resolvedSize,
|
|
24
|
+
page: resolvedPage,
|
|
25
|
+
pageSize: resolvedSize,
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Produces pagination metadata from the total row count.
|
|
30
|
+
*/
|
|
31
|
+
buildMeta(total, pagination) {
|
|
32
|
+
return {
|
|
33
|
+
total,
|
|
34
|
+
page: pagination.page,
|
|
35
|
+
pageSize: pagination.pageSize,
|
|
36
|
+
totalPages: pagination.pageSize > 0
|
|
37
|
+
? Math.ceil(total / pagination.pageSize)
|
|
38
|
+
: 0,
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=paginationBuilder.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"paginationBuilder.js","sourceRoot":"","sources":["../../../src/core/paginationBuilder.ts"],"names":[],"mappings":"AAUA,MAAM,OAAO,iBAAiB;IAC5B;;;OAGG;IACH,eAAe,CACb,MAAmB,EACnB,IAAa,EACb,QAAiB;QAEjB,MAAM,IAAI,GAAG,MAAM,CAAC,UAAU,CAAC;QAC/B,MAAM,OAAO,GAAG,IAAI,EAAE,OAAO,IAAI,IAAI,CAAC;QAEtC,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,OAAO;gBACL,KAAK,EAAE,MAAM,CAAC,gBAAgB;gBAC9B,MAAM,EAAE,CAAC;gBACT,IAAI,EAAE,CAAC;gBACP,QAAQ,EAAE,MAAM,CAAC,gBAAgB;aAClC,CAAC;QACJ,CAAC;QAED,MAAM,WAAW,GAAG,IAAI,EAAE,eAAe,IAAI,EAAE,CAAC;QAChD,MAAM,OAAO,GAAG,IAAI,EAAE,WAAW,IAAI,GAAG,CAAC;QAEzC,MAAM,YAAY,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC;QACxD,MAAM,YAAY,GAAG,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,QAAQ,IAAI,WAAW,CAAC,CAAC,CAAC,CAAC;QAEzF,OAAO;YACL,KAAK,EAAE,YAAY;YACnB,MAAM,EAAE,CAAC,YAAY,GAAG,CAAC,CAAC,GAAG,YAAY;YACzC,IAAI,EAAE,YAAY;YAClB,QAAQ,EAAE,YAAY;SACvB,CAAC;IACJ,CAAC;IAED;;OAEG;IACH,SAAS,CAAC,KAAa,EAAE,UAA4B;QACnD,OAAO;YACL,KAAK;YACL,IAAI,EAAE,UAAU,CAAC,IAAI;YACrB,QAAQ,EAAE,UAAU,CAAC,QAAQ;YAC7B,UAAU,EAAE,UAAU,CAAC,QAAQ,GAAG,CAAC;gBACjC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,GAAG,UAAU,CAAC,QAAQ,CAAC;gBACxC,CAAC,CAAC,CAAC;SACN,CAAC;IACJ,CAAC;CACF"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { Table, Column, SQL } from 'drizzle-orm';
|
|
2
|
+
import { TableConfig } from '../types/table';
|
|
3
|
+
export declare class QueryBuilder {
|
|
4
|
+
private schema;
|
|
5
|
+
constructor(schema: Record<string, unknown>);
|
|
6
|
+
buildSelect(table: Table, config: TableConfig): Record<string, SQL | Column>;
|
|
7
|
+
private collectJoinColumns;
|
|
8
|
+
/**
|
|
9
|
+
* Applies joins. Accepts optional SQL conditions map for type-safe joins.
|
|
10
|
+
* If a SQL condition exists for a join's table/alias, it's used instead of the raw string.
|
|
11
|
+
*/
|
|
12
|
+
buildJoins(query: any, config: TableConfig, sqlConditions?: Map<string, SQL>): any;
|
|
13
|
+
/**
|
|
14
|
+
* Applies raw SQL join clauses (LATERAL JOIN, etc.)
|
|
15
|
+
*/
|
|
16
|
+
applyRawJoins(query: any): any;
|
|
17
|
+
private applyJoin;
|
|
18
|
+
buildBackendConditions(config: TableConfig, context?: Record<string, any>): SQL | undefined;
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=queryBuilder.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"queryBuilder.d.ts","sourceRoot":"","sources":["../../../src/core/queryBuilder.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,EACL,MAAM,EACN,GAAG,EAIJ,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,WAAW,EAAc,MAAM,gBAAgB,CAAC;AAGzD,qBAAa,YAAY;IACX,OAAO,CAAC,MAAM;gBAAN,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAEnD,WAAW,CAAC,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,WAAW,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,GAAG,MAAM,CAAC;IAuD5E,OAAO,CAAC,kBAAkB;IAwB1B;;;OAGG;IACH,UAAU,CACR,KAAK,EAAE,GAAG,EACV,MAAM,EAAE,WAAW,EACnB,aAAa,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,GAC/B,GAAG;IASN;;OAEG;IACH,aAAa,CAAC,KAAK,EAAE,GAAG,GAAG,GAAG;IAO9B,OAAO,CAAC,SAAS;IA4BjB,sBAAsB,CACpB,MAAM,EAAE,WAAW,EACnB,OAAO,GAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAM,GAChC,GAAG,GAAG,SAAS;CAoCnB"}
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import { sql, getTableColumns, and, } from 'drizzle-orm';
|
|
2
|
+
import { applyOperator } from '../utils/operators';
|
|
3
|
+
export class QueryBuilder {
|
|
4
|
+
schema;
|
|
5
|
+
constructor(schema) {
|
|
6
|
+
this.schema = schema;
|
|
7
|
+
}
|
|
8
|
+
buildSelect(table, config) {
|
|
9
|
+
const selection = {};
|
|
10
|
+
const columns = getTableColumns(table);
|
|
11
|
+
for (const colConfig of config.columns) {
|
|
12
|
+
if (colConfig.hidden)
|
|
13
|
+
continue;
|
|
14
|
+
// Skip computed columns (they are added by the engine)
|
|
15
|
+
if (colConfig.computed)
|
|
16
|
+
continue;
|
|
17
|
+
const dbField = colConfig.field ?? colConfig.name;
|
|
18
|
+
let col;
|
|
19
|
+
if (dbField.includes('.')) {
|
|
20
|
+
// Joined column: "table.column"
|
|
21
|
+
const [tableName, colName] = dbField.split('.');
|
|
22
|
+
const joinedTable = this.schema[tableName];
|
|
23
|
+
if (joinedTable) {
|
|
24
|
+
const joinedCols = getTableColumns(joinedTable);
|
|
25
|
+
col = joinedCols[colName];
|
|
26
|
+
if (!col) {
|
|
27
|
+
// console.warn(`[QueryBuilder] Column '${colName}' not found in joined table '${tableName}'`);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
else {
|
|
31
|
+
// console.warn(`[QueryBuilder] Joined table '${tableName}' not found in schema`);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
else {
|
|
35
|
+
// Base table column
|
|
36
|
+
col = columns[dbField];
|
|
37
|
+
}
|
|
38
|
+
if (!col)
|
|
39
|
+
continue;
|
|
40
|
+
if (colConfig.dbTransform && colConfig.dbTransform.length > 0) {
|
|
41
|
+
// Apply SQL transforms (only works if col is a Column object with name)
|
|
42
|
+
const colName = col.name;
|
|
43
|
+
// TODO: This might break if col is already SQL or alias, but for now we assume Column
|
|
44
|
+
if (colName) {
|
|
45
|
+
let expressionStr = colConfig.dbTransform.reduce((acc, func) => `${func}(${acc})`, '?');
|
|
46
|
+
selection[colConfig.name] = sql.raw(expressionStr.replace('?', colName));
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
selection[colConfig.name] = col;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
selection[colConfig.name] = col;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
// Collect columns from joins
|
|
57
|
+
if (config.joins) {
|
|
58
|
+
this.collectJoinColumns(config.joins, selection);
|
|
59
|
+
}
|
|
60
|
+
return selection;
|
|
61
|
+
}
|
|
62
|
+
collectJoinColumns(joins, selection) {
|
|
63
|
+
for (const join of joins) {
|
|
64
|
+
if (join.columns) {
|
|
65
|
+
const joinedTable = this.schema[join.table];
|
|
66
|
+
if (joinedTable) {
|
|
67
|
+
const tableCols = getTableColumns(joinedTable);
|
|
68
|
+
for (const colConfig of join.columns) {
|
|
69
|
+
if (colConfig.hidden)
|
|
70
|
+
continue;
|
|
71
|
+
const colName = colConfig.field ?? colConfig.name;
|
|
72
|
+
const col = tableCols[colName];
|
|
73
|
+
if (col) {
|
|
74
|
+
// TODO: Handle name collisions?
|
|
75
|
+
selection[colConfig.name] = col;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
if (join.joins) {
|
|
81
|
+
this.collectJoinColumns(join.joins, selection);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Applies joins. Accepts optional SQL conditions map for type-safe joins.
|
|
87
|
+
* If a SQL condition exists for a join's table/alias, it's used instead of the raw string.
|
|
88
|
+
*/
|
|
89
|
+
buildJoins(query, config, sqlConditions) {
|
|
90
|
+
if (!config.joins)
|
|
91
|
+
return query;
|
|
92
|
+
for (const join of config.joins) {
|
|
93
|
+
this.applyJoin(query, join, sqlConditions);
|
|
94
|
+
}
|
|
95
|
+
return query;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Applies raw SQL join clauses (LATERAL JOIN, etc.)
|
|
99
|
+
*/
|
|
100
|
+
applyRawJoins(query) {
|
|
101
|
+
// Raw joins are appended via sql template
|
|
102
|
+
// Drizzle doesn't have a direct method, so we use a workaround
|
|
103
|
+
// The caller should apply these at the db.execute level if needed
|
|
104
|
+
return query;
|
|
105
|
+
}
|
|
106
|
+
applyJoin(query, join, sqlConditions) {
|
|
107
|
+
const joinedTable = this.schema[join.table];
|
|
108
|
+
if (!joinedTable) {
|
|
109
|
+
throw new Error(`Joined table '${join.table}' not found in schema`);
|
|
110
|
+
}
|
|
111
|
+
// Use SQL object if available, otherwise fall back to raw string
|
|
112
|
+
const key = join.alias ?? join.table;
|
|
113
|
+
const onCondition = sqlConditions?.get(key) ?? sql.raw(join.on);
|
|
114
|
+
switch (join.type) {
|
|
115
|
+
case 'left':
|
|
116
|
+
query.leftJoin(joinedTable, onCondition);
|
|
117
|
+
break;
|
|
118
|
+
case 'right':
|
|
119
|
+
query.rightJoin(joinedTable, onCondition);
|
|
120
|
+
break;
|
|
121
|
+
case 'inner':
|
|
122
|
+
query.innerJoin(joinedTable, onCondition);
|
|
123
|
+
break;
|
|
124
|
+
case 'full':
|
|
125
|
+
query.fullJoin(joinedTable, onCondition);
|
|
126
|
+
break;
|
|
127
|
+
}
|
|
128
|
+
if (join.joins) {
|
|
129
|
+
for (const nested of join.joins) {
|
|
130
|
+
this.applyJoin(query, nested, sqlConditions);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
buildBackendConditions(config, context = {}) {
|
|
135
|
+
if (!config.backendConditions || config.backendConditions.length === 0) {
|
|
136
|
+
return undefined;
|
|
137
|
+
}
|
|
138
|
+
const table = this.schema[config.base];
|
|
139
|
+
const columns = getTableColumns(table);
|
|
140
|
+
const conditions = [];
|
|
141
|
+
for (const condition of config.backendConditions) {
|
|
142
|
+
const col = columns[condition.field];
|
|
143
|
+
if (!col)
|
|
144
|
+
continue;
|
|
145
|
+
let value = condition.value;
|
|
146
|
+
if (typeof value === 'string' && value.startsWith('$')) {
|
|
147
|
+
const contextKey = value.substring(1);
|
|
148
|
+
value = contextKey
|
|
149
|
+
.split('.')
|
|
150
|
+
.reduce((obj, k) => obj?.[k], context);
|
|
151
|
+
if (value === undefined) {
|
|
152
|
+
console.warn(`[tablecraft] Context key '${contextKey}' missing.`);
|
|
153
|
+
value = null;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
const op = applyOperator(condition.operator, col, value);
|
|
157
|
+
if (op)
|
|
158
|
+
conditions.push(op);
|
|
159
|
+
}
|
|
160
|
+
return conditions.length > 0 ? and(...conditions) : undefined;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
//# sourceMappingURL=queryBuilder.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"queryBuilder.js","sourceRoot":"","sources":["../../../src/core/queryBuilder.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { SQL } from 'drizzle-orm';
|
|
2
|
+
import { TableConfig } from '../types/table';
|
|
3
|
+
export declare class RecursiveBuilder {
|
|
4
|
+
private schema;
|
|
5
|
+
constructor(schema: Record<string, unknown>);
|
|
6
|
+
/**
|
|
7
|
+
* Builds and executes a WITH RECURSIVE query for tree-structured data.
|
|
8
|
+
*
|
|
9
|
+
* Generates:
|
|
10
|
+
* WITH RECURSIVE tree AS (
|
|
11
|
+
* SELECT *, 0 AS depth FROM table WHERE parentKey IS NULL
|
|
12
|
+
* UNION ALL
|
|
13
|
+
* SELECT c.*, t.depth + 1 FROM table c
|
|
14
|
+
* INNER JOIN tree t ON c.parentKey = t.childKey
|
|
15
|
+
* WHERE t.depth < maxDepth
|
|
16
|
+
* )
|
|
17
|
+
* SELECT * FROM tree ORDER BY depth, childKey
|
|
18
|
+
*/
|
|
19
|
+
buildRecursiveSQL(config: TableConfig): SQL | undefined;
|
|
20
|
+
/**
|
|
21
|
+
* Executes the recursive query and returns results.
|
|
22
|
+
*/
|
|
23
|
+
execute(db: any, config: TableConfig): Promise<Record<string, unknown>[]>;
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=recursiveBuilder.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"recursiveBuilder.d.ts","sourceRoot":"","sources":["../../../src/core/recursiveBuilder.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,GAAG,EAIJ,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAG7C,qBAAa,gBAAgB;IACf,OAAO,CAAC,MAAM;gBAAN,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAEnD;;;;;;;;;;;;OAYG;IACH,iBAAiB,CAAC,MAAM,EAAE,WAAW,GAAG,GAAG,GAAG,SAAS;IAmDvD;;OAEG;IACG,OAAO,CAAC,EAAE,EAAE,GAAG,EAAE,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC;CAYhF"}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { sql, getTableColumns, getTableName, } from 'drizzle-orm';
|
|
2
|
+
import { applyOperator } from '../utils/operators';
|
|
3
|
+
export class RecursiveBuilder {
|
|
4
|
+
schema;
|
|
5
|
+
constructor(schema) {
|
|
6
|
+
this.schema = schema;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Builds and executes a WITH RECURSIVE query for tree-structured data.
|
|
10
|
+
*
|
|
11
|
+
* Generates:
|
|
12
|
+
* WITH RECURSIVE tree AS (
|
|
13
|
+
* SELECT *, 0 AS depth FROM table WHERE parentKey IS NULL
|
|
14
|
+
* UNION ALL
|
|
15
|
+
* SELECT c.*, t.depth + 1 FROM table c
|
|
16
|
+
* INNER JOIN tree t ON c.parentKey = t.childKey
|
|
17
|
+
* WHERE t.depth < maxDepth
|
|
18
|
+
* )
|
|
19
|
+
* SELECT * FROM tree ORDER BY depth, childKey
|
|
20
|
+
*/
|
|
21
|
+
buildRecursiveSQL(config) {
|
|
22
|
+
if (!config.recursive)
|
|
23
|
+
return undefined;
|
|
24
|
+
const table = this.schema[config.base];
|
|
25
|
+
if (!table)
|
|
26
|
+
return undefined;
|
|
27
|
+
const tableName = getTableName(table);
|
|
28
|
+
const { parentKey, childKey, maxDepth, depthAlias, pathAlias, startWith } = config.recursive;
|
|
29
|
+
// Base case: root nodes
|
|
30
|
+
let baseCondition;
|
|
31
|
+
if (startWith) {
|
|
32
|
+
const columns = getTableColumns(table);
|
|
33
|
+
const col = columns[startWith.field];
|
|
34
|
+
if (col) {
|
|
35
|
+
const op = applyOperator(startWith.operator, col, startWith.value);
|
|
36
|
+
baseCondition = op ?? sql `${sql.identifier(parentKey)} IS NULL`;
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
baseCondition = sql `${sql.identifier(parentKey)} IS NULL`;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
baseCondition = sql `${sql.identifier(parentKey)} IS NULL`;
|
|
44
|
+
}
|
|
45
|
+
// Path tracking
|
|
46
|
+
const pathSelect = pathAlias
|
|
47
|
+
? sql `, CAST(${sql.identifier(childKey)} AS TEXT) AS ${sql.identifier(pathAlias)}`
|
|
48
|
+
: sql ``;
|
|
49
|
+
const pathRecurse = pathAlias
|
|
50
|
+
? sql `, t.${sql.identifier(pathAlias)} || '/' || CAST(c.${sql.identifier(childKey)} AS TEXT) AS ${sql.identifier(pathAlias)}`
|
|
51
|
+
: sql ``;
|
|
52
|
+
const recursiveSql = sql `
|
|
53
|
+
WITH RECURSIVE tree AS (
|
|
54
|
+
SELECT *, 0 AS ${sql.identifier(depthAlias)}${pathSelect}
|
|
55
|
+
FROM ${sql.identifier(tableName)}
|
|
56
|
+
WHERE ${baseCondition}
|
|
57
|
+
|
|
58
|
+
UNION ALL
|
|
59
|
+
|
|
60
|
+
SELECT c.*, t.${sql.identifier(depthAlias)} + 1${pathRecurse}
|
|
61
|
+
FROM ${sql.identifier(tableName)} c
|
|
62
|
+
INNER JOIN tree t ON c.${sql.identifier(parentKey)} = t.${sql.identifier(childKey)}
|
|
63
|
+
WHERE t.${sql.identifier(depthAlias)} < ${maxDepth}
|
|
64
|
+
)
|
|
65
|
+
SELECT * FROM tree ORDER BY ${sql.identifier(depthAlias)}, ${sql.identifier(childKey)}
|
|
66
|
+
`;
|
|
67
|
+
return recursiveSql;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Executes the recursive query and returns results.
|
|
71
|
+
*/
|
|
72
|
+
async execute(db, config) {
|
|
73
|
+
const query = this.buildRecursiveSQL(config);
|
|
74
|
+
if (!query)
|
|
75
|
+
return [];
|
|
76
|
+
const result = await db.execute(query);
|
|
77
|
+
// db.execute returns different shapes per driver
|
|
78
|
+
// rows is typically result.rows or just result
|
|
79
|
+
if (Array.isArray(result))
|
|
80
|
+
return result;
|
|
81
|
+
if (result?.rows && Array.isArray(result.rows))
|
|
82
|
+
return result.rows;
|
|
83
|
+
return [];
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
//# sourceMappingURL=recursiveBuilder.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"recursiveBuilder.js","sourceRoot":"","sources":["../../../src/core/recursiveBuilder.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { TableConfig } from '../types/table';
|
|
2
|
+
export declare class RelationBuilder {
|
|
3
|
+
private schema;
|
|
4
|
+
constructor(schema: Record<string, unknown>);
|
|
5
|
+
/**
|
|
6
|
+
* Fetches related records and merges them into the parent data.
|
|
7
|
+
* Uses batch loading (1 query per include level, not per row).
|
|
8
|
+
*
|
|
9
|
+
* Flow:
|
|
10
|
+
* 1. Collect parent IDs from the main result
|
|
11
|
+
* 2. SELECT * FROM related WHERE foreignKey IN (parentIds)
|
|
12
|
+
* 3. Group by foreignKey
|
|
13
|
+
* 4. Merge into parent rows under the `as` key
|
|
14
|
+
* 5. Recurse for nested includes
|
|
15
|
+
*/
|
|
16
|
+
resolve(db: any, data: Record<string, unknown>[], config: TableConfig): Promise<Record<string, unknown>[]>;
|
|
17
|
+
private resolveOne;
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=relationBuilder.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"relationBuilder.d.ts","sourceRoot":"","sources":["../../../src/core/relationBuilder.ts"],"names":[],"mappings":"AASA,OAAO,EAAiB,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAG5D,qBAAa,eAAe;IACd,OAAO,CAAC,MAAM;gBAAN,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAEnD;;;;;;;;;;OAUG;IACG,OAAO,CACX,EAAE,EAAE,GAAG,EACP,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,EAC/B,MAAM,EAAE,WAAW,GAClB,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC;YAYvB,UAAU;CAkGzB"}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { getTableColumns, inArray, asc, desc, } from 'drizzle-orm';
|
|
2
|
+
import { applyOperator } from '../utils/operators';
|
|
3
|
+
export class RelationBuilder {
|
|
4
|
+
schema;
|
|
5
|
+
constructor(schema) {
|
|
6
|
+
this.schema = schema;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Fetches related records and merges them into the parent data.
|
|
10
|
+
* Uses batch loading (1 query per include level, not per row).
|
|
11
|
+
*
|
|
12
|
+
* Flow:
|
|
13
|
+
* 1. Collect parent IDs from the main result
|
|
14
|
+
* 2. SELECT * FROM related WHERE foreignKey IN (parentIds)
|
|
15
|
+
* 3. Group by foreignKey
|
|
16
|
+
* 4. Merge into parent rows under the `as` key
|
|
17
|
+
* 5. Recurse for nested includes
|
|
18
|
+
*/
|
|
19
|
+
async resolve(db, data, config) {
|
|
20
|
+
if (!config.include?.length || data.length === 0)
|
|
21
|
+
return data;
|
|
22
|
+
let result = [...data];
|
|
23
|
+
for (const inc of config.include) {
|
|
24
|
+
result = await this.resolveOne(db, result, inc);
|
|
25
|
+
}
|
|
26
|
+
return result;
|
|
27
|
+
}
|
|
28
|
+
async resolveOne(db, parentData, inc) {
|
|
29
|
+
const relatedTable = this.schema[inc.table];
|
|
30
|
+
if (!relatedTable)
|
|
31
|
+
return parentData;
|
|
32
|
+
const localKey = inc.localKey ?? 'id';
|
|
33
|
+
// 1. Collect parent IDs
|
|
34
|
+
const parentIds = [
|
|
35
|
+
...new Set(parentData
|
|
36
|
+
.map((row) => row[localKey])
|
|
37
|
+
.filter((id) => id !== null && id !== undefined)),
|
|
38
|
+
];
|
|
39
|
+
if (parentIds.length === 0)
|
|
40
|
+
return parentData;
|
|
41
|
+
// 2. Build the related query
|
|
42
|
+
const relatedColumns = getTableColumns(relatedTable);
|
|
43
|
+
const fkColumn = relatedColumns[inc.foreignKey];
|
|
44
|
+
if (!fkColumn)
|
|
45
|
+
return parentData;
|
|
46
|
+
// Build selection (specific columns or all)
|
|
47
|
+
let selection;
|
|
48
|
+
if (inc.columns?.length) {
|
|
49
|
+
selection = {};
|
|
50
|
+
for (const colName of inc.columns) {
|
|
51
|
+
if (relatedColumns[colName])
|
|
52
|
+
selection[colName] = relatedColumns[colName];
|
|
53
|
+
}
|
|
54
|
+
// Always include the foreign key for grouping
|
|
55
|
+
if (!selection[inc.foreignKey]) {
|
|
56
|
+
selection[inc.foreignKey] = fkColumn;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
selection = { ...relatedColumns };
|
|
61
|
+
}
|
|
62
|
+
let query = db.select(selection).from(relatedTable).where(inArray(fkColumn, parentIds));
|
|
63
|
+
// Apply WHERE conditions
|
|
64
|
+
if (inc.where?.length) {
|
|
65
|
+
for (const cond of inc.where) {
|
|
66
|
+
const col = relatedColumns[cond.field];
|
|
67
|
+
if (!col)
|
|
68
|
+
continue;
|
|
69
|
+
const op = applyOperator(cond.operator, col, cond.value);
|
|
70
|
+
if (op)
|
|
71
|
+
query = query.where(op);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
// Apply ORDER BY
|
|
75
|
+
if (inc.orderBy?.length) {
|
|
76
|
+
const orderClauses = [];
|
|
77
|
+
for (const s of inc.orderBy) {
|
|
78
|
+
const col = relatedColumns[s.field];
|
|
79
|
+
if (!col)
|
|
80
|
+
continue;
|
|
81
|
+
orderClauses.push(s.order === 'desc' ? desc(col) : asc(col));
|
|
82
|
+
}
|
|
83
|
+
if (orderClauses.length)
|
|
84
|
+
query = query.orderBy(...orderClauses);
|
|
85
|
+
}
|
|
86
|
+
// Apply LIMIT (per-parent limiting is complex; this is global limit)
|
|
87
|
+
if (inc.limit) {
|
|
88
|
+
query = query.limit(inc.limit * parentIds.length);
|
|
89
|
+
}
|
|
90
|
+
// 3. Execute
|
|
91
|
+
const relatedRows = await query;
|
|
92
|
+
// 4. Recursively resolve nested includes
|
|
93
|
+
let processedRows = relatedRows;
|
|
94
|
+
if (inc.include?.length) {
|
|
95
|
+
const nestedConfig = {
|
|
96
|
+
name: inc.table,
|
|
97
|
+
base: inc.table,
|
|
98
|
+
columns: [],
|
|
99
|
+
include: inc.include,
|
|
100
|
+
};
|
|
101
|
+
processedRows = await this.resolve(db, relatedRows, nestedConfig);
|
|
102
|
+
}
|
|
103
|
+
// 5. Group by foreign key
|
|
104
|
+
const grouped = new Map();
|
|
105
|
+
for (const row of processedRows) {
|
|
106
|
+
const fkValue = row[inc.foreignKey];
|
|
107
|
+
if (!grouped.has(fkValue))
|
|
108
|
+
grouped.set(fkValue, []);
|
|
109
|
+
grouped.get(fkValue).push(row);
|
|
110
|
+
}
|
|
111
|
+
// 6. Merge into parent
|
|
112
|
+
return parentData.map((parent) => ({
|
|
113
|
+
...parent,
|
|
114
|
+
[inc.as]: grouped.get(parent[localKey]) ?? [],
|
|
115
|
+
}));
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
//# sourceMappingURL=relationBuilder.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"relationBuilder.js","sourceRoot":"","sources":["../../../src/core/relationBuilder.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { TableConfig } from '../types/table';
|
|
2
|
+
import { EngineContext } from '../types/engine';
|
|
3
|
+
/**
|
|
4
|
+
* Filters columns based on the requesting user's roles.
|
|
5
|
+
* Columns with `visibleTo` are only shown to users with matching roles.
|
|
6
|
+
* Columns without `visibleTo` are visible to everyone.
|
|
7
|
+
*
|
|
8
|
+
* Returns a new config with hidden columns updated.
|
|
9
|
+
*/
|
|
10
|
+
export declare function applyRoleBasedVisibility(config: TableConfig, context: EngineContext): TableConfig;
|
|
11
|
+
//# sourceMappingURL=roleFilter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"roleFilter.d.ts","sourceRoot":"","sources":["../../../src/core/roleFilter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAC7C,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAEhD;;;;;;GAMG;AACH,wBAAgB,wBAAwB,CACtC,MAAM,EAAE,WAAW,EACnB,OAAO,EAAE,aAAa,GACrB,WAAW,CAmBb"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Filters columns based on the requesting user's roles.
|
|
3
|
+
* Columns with `visibleTo` are only shown to users with matching roles.
|
|
4
|
+
* Columns without `visibleTo` are visible to everyone.
|
|
5
|
+
*
|
|
6
|
+
* Returns a new config with hidden columns updated.
|
|
7
|
+
*/
|
|
8
|
+
export function applyRoleBasedVisibility(config, context) {
|
|
9
|
+
const userRoles = context.user?.roles ?? [];
|
|
10
|
+
const columns = config.columns.map((col) => {
|
|
11
|
+
const visibleTo = col.visibleTo;
|
|
12
|
+
// No restriction — keep as-is
|
|
13
|
+
if (!visibleTo || visibleTo.length === 0)
|
|
14
|
+
return col;
|
|
15
|
+
// Check if user has any of the required roles
|
|
16
|
+
const canSee = userRoles.some((role) => visibleTo.includes(role));
|
|
17
|
+
if (canSee)
|
|
18
|
+
return col;
|
|
19
|
+
// User can't see this column — mark as hidden
|
|
20
|
+
return { ...col, hidden: true };
|
|
21
|
+
});
|
|
22
|
+
return { ...config, columns };
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=roleFilter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"roleFilter.js","sourceRoot":"","sources":["../../../src/core/roleFilter.ts"],"names":[],"mappings":"AAGA;;;;;;GAMG;AACH,MAAM,UAAU,wBAAwB,CACtC,MAAmB,EACnB,OAAsB;IAEtB,MAAM,SAAS,GAAG,OAAO,CAAC,IAAI,EAAE,KAAK,IAAI,EAAE,CAAC;IAE5C,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE;QACzC,MAAM,SAAS,GAAI,GAAW,CAAC,SAAiC,CAAC;QAEjE,8BAA8B;QAC9B,IAAI,CAAC,SAAS,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,GAAG,CAAC;QAErD,8CAA8C;QAC9C,MAAM,MAAM,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,SAAS,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC;QAElE,IAAI,MAAM;YAAE,OAAO,GAAG,CAAC;QAEvB,8CAA8C;QAC9C,OAAO,EAAE,GAAG,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;IAClC,CAAC,CAAC,CAAC;IAEH,OAAO,EAAE,GAAG,MAAM,EAAE,OAAO,EAAE,CAAC;AAChC,CAAC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { SQL } from 'drizzle-orm';
|
|
2
|
+
import { TableConfig } from '../types/table';
|
|
3
|
+
export declare class SearchBuilder {
|
|
4
|
+
private schema;
|
|
5
|
+
constructor(schema: Record<string, unknown>);
|
|
6
|
+
/**
|
|
7
|
+
* Builds a search condition across multiple columns using OR + ILIKE.
|
|
8
|
+
*/
|
|
9
|
+
buildSearch(config: TableConfig, searchTerm: string): SQL | undefined;
|
|
10
|
+
/**
|
|
11
|
+
* Builds a PostgreSQL full-text search condition (tsvector/tsquery).
|
|
12
|
+
* This is an optional enhanced mode — only works on PostgreSQL.
|
|
13
|
+
*/
|
|
14
|
+
buildFullTextSearch(config: TableConfig, searchTerm: string, language?: string): SQL | undefined;
|
|
15
|
+
private resolveColumn;
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=searchBuilder.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"searchBuilder.d.ts","sourceRoot":"","sources":["../../../src/core/searchBuilder.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,GAAG,EAMJ,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAG7C,qBAAa,aAAa;IACZ,OAAO,CAAC,MAAM;gBAAN,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAEnD;;OAEG;IACH,WAAW,CACT,MAAM,EAAE,WAAW,EACnB,UAAU,EAAE,MAAM,GACjB,GAAG,GAAG,SAAS;IAkClB;;;OAGG;IACH,mBAAmB,CACjB,MAAM,EAAE,WAAW,EACnB,UAAU,EAAE,MAAM,EAClB,QAAQ,GAAE,MAAkB,GAC3B,GAAG,GAAG,SAAS;IAgBlB,OAAO,CAAC,aAAa;CAiBtB"}
|