@tablecraft/engine 0.1.4 → 0.1.6
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/core/cursorPagination.d.ts +7 -1
- package/dist/core/cursorPagination.d.ts.map +1 -1
- package/dist/core/cursorPagination.js +77 -12
- package/dist/core/cursorPagination.js.map +1 -1
- package/dist/core/fieldSelector.d.ts.map +1 -1
- package/dist/core/fieldSelector.js +7 -1
- package/dist/core/fieldSelector.js.map +1 -1
- package/dist/core/filterBuilder.d.ts.map +1 -1
- package/dist/core/filterBuilder.js +2 -41
- package/dist/core/filterBuilder.js.map +1 -1
- package/dist/core/inputValidator.d.ts.map +1 -1
- package/dist/core/inputValidator.js +11 -25
- package/dist/core/inputValidator.js.map +1 -1
- package/dist/core/metadataBuilder.d.ts.map +1 -1
- package/dist/core/metadataBuilder.js +9 -4
- package/dist/core/metadataBuilder.js.map +1 -1
- package/dist/core/queryBuilder.d.ts.map +1 -1
- package/dist/core/queryBuilder.js +21 -10
- package/dist/core/queryBuilder.js.map +1 -1
- package/dist/core/sortBuilder.d.ts +2 -1
- package/dist/core/sortBuilder.d.ts.map +1 -1
- package/dist/core/sortBuilder.js +29 -12
- package/dist/core/sortBuilder.js.map +1 -1
- package/dist/core/subqueryBuilder.d.ts +24 -1
- package/dist/core/subqueryBuilder.d.ts.map +1 -1
- package/dist/core/subqueryBuilder.js +73 -8
- package/dist/core/subqueryBuilder.js.map +1 -1
- package/dist/define.d.ts +79 -4
- package/dist/define.d.ts.map +1 -1
- package/dist/define.js +127 -12
- package/dist/define.js.map +1 -1
- package/dist/engine.d.ts.map +1 -1
- package/dist/engine.js +28 -4
- package/dist/engine.js.map +1 -1
- package/dist/types/table.d.ts +292 -2
- package/dist/types/table.d.ts.map +1 -1
- package/dist/types/table.js +29 -2
- package/dist/types/table.js.map +1 -1
- package/dist/utils/joinUtils.d.ts +7 -0
- package/dist/utils/joinUtils.d.ts.map +1 -1
- package/dist/utils/joinUtils.js +33 -0
- package/dist/utils/joinUtils.js.map +1 -1
- package/dist/utils/openapi.d.ts.map +1 -1
- package/dist/utils/openapi.js +25 -2
- package/dist/utils/openapi.js.map +1 -1
- package/package.json +9 -9
- package/LICENSE +0 -21
package/dist/core/sortBuilder.js
CHANGED
|
@@ -25,7 +25,10 @@ export class SortBuilder {
|
|
|
25
25
|
if (!table)
|
|
26
26
|
return [];
|
|
27
27
|
const baseColumns = getTableColumns(table);
|
|
28
|
-
// Build a whitelist of sortable fields from base columns AND join columns
|
|
28
|
+
// Build a whitelist of sortable fields from base columns AND join columns.
|
|
29
|
+
// sortable: undefined is treated as sortable (same as true) — only an
|
|
30
|
+
// explicit sortable: false disables it. This matches the introspect.ts
|
|
31
|
+
// default of sortable: true for auto-detected columns.
|
|
29
32
|
const sortableFields = new Set();
|
|
30
33
|
for (const col of config.columns) {
|
|
31
34
|
if (col.sortable !== false) {
|
|
@@ -89,31 +92,45 @@ export class SortBuilder {
|
|
|
89
92
|
*
|
|
90
93
|
* Note: This implements a "first-match-wins" strategy. If multiple joins
|
|
91
94
|
* expose a column with the same name, the first one encountered in a
|
|
92
|
-
* depth-first traversal of the join tree will be used.
|
|
95
|
+
* depth-first traversal of the join tree will be used, and a console.warn
|
|
96
|
+
* is emitted to help developers detect and resolve the collision.
|
|
93
97
|
*/
|
|
94
98
|
resolveJoinColumn(config, fieldName) {
|
|
95
99
|
if (!config.joins)
|
|
96
100
|
return undefined;
|
|
101
|
+
// Collect all matches (table name + Column) across all joins (depth-first)
|
|
102
|
+
const matches = [];
|
|
97
103
|
for (const join of config.joins) {
|
|
98
104
|
const joinColConfig = join.columns?.find((c) => c.name === fieldName);
|
|
99
105
|
if (joinColConfig) {
|
|
100
106
|
const joinedTable = this.schema[join.table];
|
|
101
|
-
if (
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
107
|
+
if (joinedTable) {
|
|
108
|
+
const joinedCols = getTableColumns(joinedTable);
|
|
109
|
+
const dbCol = joinColConfig.field ?? fieldName;
|
|
110
|
+
const column = joinedCols[dbCol];
|
|
111
|
+
if (column) {
|
|
112
|
+
matches.push({ table: join.table, column });
|
|
113
|
+
}
|
|
114
|
+
}
|
|
108
115
|
}
|
|
109
116
|
// Recurse into nested joins
|
|
110
117
|
if (join.joins) {
|
|
111
118
|
const nested = this.resolveJoinColumn({ joins: join.joins }, fieldName);
|
|
112
|
-
if (nested)
|
|
113
|
-
|
|
119
|
+
if (nested) {
|
|
120
|
+
// Treat nested match as a candidate (table name not relevant for warning)
|
|
121
|
+
matches.push({ table: join.table + '(nested)', column: nested });
|
|
122
|
+
}
|
|
114
123
|
}
|
|
115
124
|
}
|
|
116
|
-
|
|
125
|
+
if (matches.length === 0)
|
|
126
|
+
return undefined;
|
|
127
|
+
if (matches.length > 1) {
|
|
128
|
+
console.warn(`[TableCraft] Sort field '${fieldName}' exists in multiple joins ` +
|
|
129
|
+
`(${matches.map((m) => m.table).join(', ')}). ` +
|
|
130
|
+
`Using first match. ` +
|
|
131
|
+
`Use dot-syntax (e.g. '${matches[0].table}.${fieldName}') to be explicit.`);
|
|
132
|
+
}
|
|
133
|
+
return matches[0].column;
|
|
117
134
|
}
|
|
118
135
|
getDefaultSort(config) {
|
|
119
136
|
if (!config.defaultSort || config.defaultSort.length === 0)
|
|
@@ -1 +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;AAGrB,OAAO,EAAE,mBAAmB,EAAE,yBAAyB,EAAE,MAAM,oBAAoB,CAAC;AAEpF,MAAM,OAAO,WAAW;IACF;IAApB,YAAoB,MAA+B;QAA/B,WAAM,GAAN,MAAM,CAAyB;IAAG,CAAC;IAEvD;;;;;;;;;;;OAWG;IACH,SAAS,CAAC,MAAmB,EAAE,MAAoB,EAAE,cAAiC;QACpF,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,WAAW,GAAG,eAAe,CAAC,KAAK,CAAC,CAAC;QAE3C,
|
|
1
|
+
{"version":3,"file":"sortBuilder.js","sourceRoot":"","sources":["../../src/core/sortBuilder.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,13 +1,36 @@
|
|
|
1
1
|
import { SQL } from 'drizzle-orm';
|
|
2
2
|
import { TableConfig } from '../types/table';
|
|
3
|
+
import { Dialect } from './dialect';
|
|
3
4
|
export declare class SubqueryBuilder {
|
|
4
5
|
private schema;
|
|
5
6
|
constructor(schema: Record<string, unknown>);
|
|
6
7
|
/**
|
|
7
8
|
* Builds correlated subquery select expressions.
|
|
8
9
|
* Supports count, exists, and first subquery types.
|
|
10
|
+
*
|
|
11
|
+
* @param dialect - Optional dialect for feature gating. When provided,
|
|
12
|
+
* 'first' mode (which uses PostgreSQL-only `row_to_json`) will throw a
|
|
13
|
+
* DialectError on non-PostgreSQL dialects. Pass 'unknown' to skip the
|
|
14
|
+
* guard (e.g. when dialect cannot be detected).
|
|
9
15
|
*/
|
|
10
|
-
buildSubqueries(config: TableConfig): Record<string, SQL> | undefined;
|
|
16
|
+
buildSubqueries(config: TableConfig, dialect?: Dialect): Record<string, SQL> | undefined;
|
|
11
17
|
private buildSingle;
|
|
18
|
+
/**
|
|
19
|
+
* Builds the WHERE clause SQL for a subquery.
|
|
20
|
+
*
|
|
21
|
+
* Priority:
|
|
22
|
+
* 1. `filterSql` — Drizzle SQL expression (full Drizzle DX, passed through as-is)
|
|
23
|
+
* 2. `filterConditions` — structured array of conditions (typed, safe, recommended)
|
|
24
|
+
* 3. `filter` — raw SQL string (@deprecated, developer-authored only)
|
|
25
|
+
* 4. fallback — `true` (uncorrelated, scans whole table)
|
|
26
|
+
*/
|
|
27
|
+
private buildFilter;
|
|
28
|
+
/**
|
|
29
|
+
* Converts a `SubqueryCondition[]` into a single AND-combined SQL expression.
|
|
30
|
+
*
|
|
31
|
+
* Column references → emitted via sql.raw() — developer-defined, not user input
|
|
32
|
+
* Literal values → parameterized via sql`${value}` to prevent injection
|
|
33
|
+
*/
|
|
34
|
+
private buildStructuredFilter;
|
|
12
35
|
}
|
|
13
36
|
//# sourceMappingURL=subqueryBuilder.d.ts.map
|
|
@@ -1 +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,
|
|
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,EAAqC,MAAM,gBAAgB,CAAC;AAChF,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAepC,qBAAa,eAAe;IACd,OAAO,CAAC,MAAM;gBAAN,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAEnD;;;;;;;;OAQG;IACH,eAAe,CAAC,MAAM,EAAE,WAAW,EAAE,OAAO,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,SAAS;IAuBxF,OAAO,CAAC,WAAW;IAoBnB;;;;;;;;OAQG;IACH,OAAO,CAAC,WAAW;IAanB;;;;;OAKG;IACH,OAAO,CAAC,qBAAqB;CAsB9B"}
|
|
@@ -1,4 +1,16 @@
|
|
|
1
1
|
import { sql, } from 'drizzle-orm';
|
|
2
|
+
import { DialectError } from '../errors';
|
|
3
|
+
// Maps our operator enum to the SQL operator string
|
|
4
|
+
const OP_MAP = {
|
|
5
|
+
eq: '=',
|
|
6
|
+
neq: '!=',
|
|
7
|
+
gt: '>',
|
|
8
|
+
gte: '>=',
|
|
9
|
+
lt: '<',
|
|
10
|
+
lte: '<=',
|
|
11
|
+
like: 'LIKE',
|
|
12
|
+
ilike: 'ILIKE',
|
|
13
|
+
};
|
|
2
14
|
export class SubqueryBuilder {
|
|
3
15
|
schema;
|
|
4
16
|
constructor(schema) {
|
|
@@ -7,8 +19,13 @@ export class SubqueryBuilder {
|
|
|
7
19
|
/**
|
|
8
20
|
* Builds correlated subquery select expressions.
|
|
9
21
|
* Supports count, exists, and first subquery types.
|
|
22
|
+
*
|
|
23
|
+
* @param dialect - Optional dialect for feature gating. When provided,
|
|
24
|
+
* 'first' mode (which uses PostgreSQL-only `row_to_json`) will throw a
|
|
25
|
+
* DialectError on non-PostgreSQL dialects. Pass 'unknown' to skip the
|
|
26
|
+
* guard (e.g. when dialect cannot be detected).
|
|
10
27
|
*/
|
|
11
|
-
buildSubqueries(config) {
|
|
28
|
+
buildSubqueries(config, dialect) {
|
|
12
29
|
if (!config.subqueries || config.subqueries.length === 0) {
|
|
13
30
|
return undefined;
|
|
14
31
|
}
|
|
@@ -20,28 +37,76 @@ export class SubqueryBuilder {
|
|
|
20
37
|
const subTable = this.schema[sub.table];
|
|
21
38
|
if (!subTable)
|
|
22
39
|
continue;
|
|
23
|
-
const subQuery = this.buildSingle(sub, subTable);
|
|
40
|
+
const subQuery = this.buildSingle(sub, subTable, dialect);
|
|
24
41
|
if (subQuery) {
|
|
25
42
|
result[sub.alias] = subQuery;
|
|
26
43
|
}
|
|
27
44
|
}
|
|
28
45
|
return Object.keys(result).length > 0 ? result : undefined;
|
|
29
46
|
}
|
|
30
|
-
buildSingle(sub, subTable) {
|
|
31
|
-
|
|
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`;
|
|
47
|
+
buildSingle(sub, subTable, dialect) {
|
|
48
|
+
const filterSql = this.buildFilter(sub, dialect);
|
|
35
49
|
switch (sub.type) {
|
|
36
50
|
case 'count':
|
|
37
51
|
return sql `(SELECT count(*) FROM ${subTable} WHERE ${filterSql})`;
|
|
38
52
|
case 'exists':
|
|
39
53
|
return sql `EXISTS (SELECT 1 FROM ${subTable} WHERE ${filterSql})`;
|
|
40
|
-
case 'first':
|
|
54
|
+
case 'first': {
|
|
55
|
+
// 'first' uses row_to_json() which is PostgreSQL-only.
|
|
56
|
+
if (dialect && dialect !== 'unknown' && dialect !== 'postgresql') {
|
|
57
|
+
throw new DialectError('first', dialect);
|
|
58
|
+
}
|
|
41
59
|
return sql `(SELECT row_to_json(t) FROM (SELECT * FROM ${subTable} WHERE ${filterSql} LIMIT 1) t)`;
|
|
60
|
+
}
|
|
42
61
|
default:
|
|
43
62
|
return undefined;
|
|
44
63
|
}
|
|
45
64
|
}
|
|
65
|
+
/**
|
|
66
|
+
* Builds the WHERE clause SQL for a subquery.
|
|
67
|
+
*
|
|
68
|
+
* Priority:
|
|
69
|
+
* 1. `filterSql` — Drizzle SQL expression (full Drizzle DX, passed through as-is)
|
|
70
|
+
* 2. `filterConditions` — structured array of conditions (typed, safe, recommended)
|
|
71
|
+
* 3. `filter` — raw SQL string (@deprecated, developer-authored only)
|
|
72
|
+
* 4. fallback — `true` (uncorrelated, scans whole table)
|
|
73
|
+
*/
|
|
74
|
+
buildFilter(sub, dialect) {
|
|
75
|
+
if (sub.filterSql) {
|
|
76
|
+
return sub.filterSql;
|
|
77
|
+
}
|
|
78
|
+
if (sub.filterConditions && sub.filterConditions.length > 0) {
|
|
79
|
+
return this.buildStructuredFilter(sub.filterConditions, dialect);
|
|
80
|
+
}
|
|
81
|
+
if (sub.filter) {
|
|
82
|
+
return sql.raw(sub.filter);
|
|
83
|
+
}
|
|
84
|
+
return sql `true`;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Converts a `SubqueryCondition[]` into a single AND-combined SQL expression.
|
|
88
|
+
*
|
|
89
|
+
* Column references → emitted via sql.raw() — developer-defined, not user input
|
|
90
|
+
* Literal values → parameterized via sql`${value}` to prevent injection
|
|
91
|
+
*/
|
|
92
|
+
buildStructuredFilter(conditions, dialect) {
|
|
93
|
+
if (conditions.length === 0)
|
|
94
|
+
return sql `true`;
|
|
95
|
+
const parts = conditions.map((cond) => {
|
|
96
|
+
const leftSql = 'column' in cond.left ? sql.raw(cond.left.column) : sql `${cond.left.value}`;
|
|
97
|
+
const rightSql = 'column' in cond.right ? sql.raw(cond.right.column) : sql `${cond.right.value}`;
|
|
98
|
+
const op = cond.op ?? 'eq';
|
|
99
|
+
if (op === 'ilike') {
|
|
100
|
+
if (dialect && dialect !== 'unknown' && dialect !== 'postgresql') {
|
|
101
|
+
// MySQL/SQLite don't support ILIKE directly. We rewrite to LOWER() LIKE LOWER().
|
|
102
|
+
return sql `LOWER(${leftSql}) LIKE LOWER(${rightSql})`;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
const opStr = OP_MAP[op];
|
|
106
|
+
return sql `${leftSql} ${sql.raw(opStr)} ${rightSql}`;
|
|
107
|
+
});
|
|
108
|
+
// AND-combine all parts
|
|
109
|
+
return parts.reduce((acc, part) => sql `${acc} AND ${part}`);
|
|
110
|
+
}
|
|
46
111
|
}
|
|
47
112
|
//# sourceMappingURL=subqueryBuilder.js.map
|
|
@@ -1 +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
|
|
1
|
+
{"version":3,"file":"subqueryBuilder.js","sourceRoot":"","sources":["../../src/core/subqueryBuilder.ts"],"names":[],"mappings":"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"}
|
package/dist/define.d.ts
CHANGED
|
@@ -21,8 +21,8 @@ export declare const drizzleOperators: {
|
|
|
21
21
|
not: typeof not;
|
|
22
22
|
sql: typeof sql;
|
|
23
23
|
};
|
|
24
|
-
import { EngineParams, EngineContext } from './types/engine';
|
|
25
|
-
import { TableConfig, ColumnConfig, Operator, FilterExpression, ColumnFormat, DatePreset } from './types/table';
|
|
24
|
+
import type { EngineParams, EngineContext, CountMode } from './types/engine';
|
|
25
|
+
import { TableConfig, ColumnConfig, Operator, FilterExpression, ColumnFormat, DatePreset, SubqueryCondition } from './types/table';
|
|
26
26
|
type InferColumns<T> = T extends {
|
|
27
27
|
_: {
|
|
28
28
|
columns: infer C;
|
|
@@ -50,6 +50,8 @@ export interface RuntimeExtensions<T extends Table = Table> {
|
|
|
50
50
|
rawOrderBys: SQL[];
|
|
51
51
|
ctes: Map<string, SQL>;
|
|
52
52
|
sqlJoinConditions: Map<string, SQL>;
|
|
53
|
+
/** How row counting is performed. Defaults to 'exact' when not set. */
|
|
54
|
+
countMode?: CountMode;
|
|
53
55
|
hooks?: {
|
|
54
56
|
beforeQuery?: (params: any, context: any) => any;
|
|
55
57
|
afterQuery?: (data: Record<string, unknown>[], params: any, context: any) => any;
|
|
@@ -153,6 +155,7 @@ export declare class TableDefinitionBuilder<T extends Table = Table> {
|
|
|
153
155
|
computed(name: string, expression: SQL, options?: {
|
|
154
156
|
type?: ColumnConfig['type'];
|
|
155
157
|
label?: string;
|
|
158
|
+
sortable?: boolean;
|
|
156
159
|
}): this;
|
|
157
160
|
where(condition: {
|
|
158
161
|
field: string;
|
|
@@ -271,9 +274,81 @@ export declare class TableDefinitionBuilder<T extends Table = Table> {
|
|
|
271
274
|
}): this;
|
|
272
275
|
rawWhere(sqlExpr: SQL): this;
|
|
273
276
|
rawJoin(sqlExpr: SQL): this;
|
|
277
|
+
/**
|
|
278
|
+
* Add a raw SQL ORDER BY expression.
|
|
279
|
+
*
|
|
280
|
+
* **Warning**: This bypasses the sortable whitelist entirely. The expression
|
|
281
|
+
* is appended unconditionally to ORDER BY without any field validation.
|
|
282
|
+
* Ensure the SQL is safe and not derived from user-supplied input.
|
|
283
|
+
* Prefer `.sort()` or `.sortable()` for user-facing sort controls.
|
|
284
|
+
*/
|
|
274
285
|
rawOrderBy(sqlExpr: SQL): this;
|
|
275
286
|
cte(name: string, sqlExpr: SQL): this;
|
|
276
|
-
|
|
287
|
+
/**
|
|
288
|
+
* Attach a correlated subquery to every row.
|
|
289
|
+
*
|
|
290
|
+
* The `filter` parameter controls the subquery's WHERE clause and accepts
|
|
291
|
+
* **three forms** — pick whichever fits your style:
|
|
292
|
+
*
|
|
293
|
+
* ---
|
|
294
|
+
*
|
|
295
|
+
* ### 1. Drizzle `sql\`...\`` expression *(best DX — use your schema columns directly)*
|
|
296
|
+
*
|
|
297
|
+
* Import `sql` from `drizzle-orm` and write the WHERE clause exactly as you
|
|
298
|
+
* would in any Drizzle query. TableCraft passes the expression through unchanged,
|
|
299
|
+
* so the full power of Drizzle is available — joins, functions, OR logic, anything.
|
|
300
|
+
* You own the safety of the expression.
|
|
301
|
+
*
|
|
302
|
+
* ```ts
|
|
303
|
+
* import { sql } from 'drizzle-orm';
|
|
304
|
+
* import { orders, orderItems } from '../db/schema';
|
|
305
|
+
*
|
|
306
|
+
* .subquery('itemCount', orderItems, 'count',
|
|
307
|
+
* sql`${orderItems.orderId} = ${orders.id}`)
|
|
308
|
+
*
|
|
309
|
+
* // With an extra condition:
|
|
310
|
+
* .subquery('activeItemCount', orderItems, 'count',
|
|
311
|
+
* sql`${orderItems.orderId} = ${orders.id} AND ${orderItems.status} = ${'active'}`)
|
|
312
|
+
* ```
|
|
313
|
+
*
|
|
314
|
+
* ---
|
|
315
|
+
*
|
|
316
|
+
* ### 2. Structured `SubqueryCondition[]` *(typed, injection-safe)*
|
|
317
|
+
*
|
|
318
|
+
* Pass an array of condition objects. Each has `left`, `op` (default `'eq'`),
|
|
319
|
+
* and `right` operands — either `{ column: 'table.column' }` or `{ value: literal }`.
|
|
320
|
+
* Conditions are AND-combined. Literal values are parameterized automatically.
|
|
321
|
+
*
|
|
322
|
+
* ```ts
|
|
323
|
+
* // Simple column-to-column join:
|
|
324
|
+
* .subquery('itemCount', orderItems, 'count', [
|
|
325
|
+
* { left: { column: 'order_items.order_id' }, op: 'eq', right: { column: 'orders.id' } },
|
|
326
|
+
* ])
|
|
327
|
+
*
|
|
328
|
+
* // With a literal value filter:
|
|
329
|
+
* .subquery('activeItemCount', orderItems, 'count', [
|
|
330
|
+
* { left: { column: 'order_items.order_id' }, op: 'eq', right: { column: 'orders.id' } },
|
|
331
|
+
* { left: { column: 'order_items.status' }, op: 'eq', right: { value: 'active' } },
|
|
332
|
+
* ])
|
|
333
|
+
* ```
|
|
334
|
+
*
|
|
335
|
+
* ---
|
|
336
|
+
*
|
|
337
|
+
* ### 3. Raw SQL string *(@deprecated — developer-authored constants only)*
|
|
338
|
+
*
|
|
339
|
+
* Still accepted for backwards compatibility. Must be a hardcoded string authored
|
|
340
|
+
* by the developer — never derived from user input. Prefer form 1 or 2 instead.
|
|
341
|
+
*
|
|
342
|
+
* ```ts
|
|
343
|
+
* // @deprecated
|
|
344
|
+
* .subquery('itemCount', orderItems, 'count', 'order_items.order_id = orders.id')
|
|
345
|
+
* ```
|
|
346
|
+
*
|
|
347
|
+
* ---
|
|
348
|
+
*
|
|
349
|
+
* Omitting `filter` creates an uncorrelated subquery (full table scan).
|
|
350
|
+
*/
|
|
351
|
+
subquery(alias: string, table: Table, type: 'count' | 'exists' | 'first', filter?: string | SubqueryCondition[] | SQL): this;
|
|
277
352
|
softDelete(field?: string): this;
|
|
278
353
|
tenant(field?: string): this;
|
|
279
354
|
exportable(...formats: ('csv' | 'json')[]): this;
|
|
@@ -288,7 +363,7 @@ export declare class TableDefinitionBuilder<T extends Table = Table> {
|
|
|
288
363
|
* 'estimated' = PostgreSQL's reltuples — fast but approximate
|
|
289
364
|
* 'none' = skip counting entirely — fastest
|
|
290
365
|
*/
|
|
291
|
-
countMode(mode:
|
|
366
|
+
countMode(mode: CountMode): this;
|
|
292
367
|
/** Enable DISTINCT on queries */
|
|
293
368
|
distinct(): this;
|
|
294
369
|
/** Add a hook that runs before every query */
|
package/dist/define.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"define.d.ts","sourceRoot":"","sources":["../src/define.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,EACL,GAAG,EAGH,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE,UAAU,EAChC,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,SAAS,EACtC,MAAM,EAAE,SAAS,EAAE,GAAG,EAAE,EAAE,EAAE,GAAG,EAAE,GAAG,EACrC,MAAM,aAAa,CAAC;AAErB,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;CAK5B,CAAC;AAGF,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"define.d.ts","sourceRoot":"","sources":["../src/define.ts"],"names":[],"mappings":"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"}
|
package/dist/define.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { getTableName, eq, ne, gt, gte, lt, lte, like, ilike, inArray, notInArray, between, notBetween, isNull, isNotNull, exists, notExists, and, or, not, sql } from 'drizzle-orm';
|
|
1
|
+
import { SQL, getTableName, eq, ne, gt, gte, lt, lte, like, ilike, inArray, notInArray, between, notBetween, isNull, isNotNull, exists, notExists, and, or, not, sql } from 'drizzle-orm';
|
|
2
2
|
export const drizzleOperators = {
|
|
3
3
|
eq, ne, gt, gte, lt, lte,
|
|
4
4
|
like, ilike, inArray, notInArray,
|
|
@@ -309,7 +309,7 @@ export class TableDefinitionBuilder {
|
|
|
309
309
|
type: options?.type ?? 'string',
|
|
310
310
|
label: options?.label ?? name,
|
|
311
311
|
hidden: false,
|
|
312
|
-
sortable: true,
|
|
312
|
+
sortable: options?.sortable ?? true,
|
|
313
313
|
filterable: false,
|
|
314
314
|
computed: true,
|
|
315
315
|
});
|
|
@@ -540,7 +540,19 @@ export class TableDefinitionBuilder {
|
|
|
540
540
|
this._ext.rawJoins.push(sqlExpr);
|
|
541
541
|
return this;
|
|
542
542
|
}
|
|
543
|
+
/**
|
|
544
|
+
* Add a raw SQL ORDER BY expression.
|
|
545
|
+
*
|
|
546
|
+
* **Warning**: This bypasses the sortable whitelist entirely. The expression
|
|
547
|
+
* is appended unconditionally to ORDER BY without any field validation.
|
|
548
|
+
* Ensure the SQL is safe and not derived from user-supplied input.
|
|
549
|
+
* Prefer `.sort()` or `.sortable()` for user-facing sort controls.
|
|
550
|
+
*/
|
|
543
551
|
rawOrderBy(sqlExpr) {
|
|
552
|
+
if (typeof process !== 'undefined' && process.env?.NODE_ENV !== 'production') {
|
|
553
|
+
console.warn(`[TableCraft] rawOrderBy() bypasses the sortable whitelist and should not be used with user input. ` +
|
|
554
|
+
`Expression: ${sqlExpr}`);
|
|
555
|
+
}
|
|
544
556
|
this._ext.rawOrderBys.push(sqlExpr);
|
|
545
557
|
return this;
|
|
546
558
|
}
|
|
@@ -549,15 +561,121 @@ export class TableDefinitionBuilder {
|
|
|
549
561
|
return this;
|
|
550
562
|
}
|
|
551
563
|
// ──── Subqueries ────
|
|
564
|
+
/**
|
|
565
|
+
* Attach a correlated subquery to every row.
|
|
566
|
+
*
|
|
567
|
+
* The `filter` parameter controls the subquery's WHERE clause and accepts
|
|
568
|
+
* **three forms** — pick whichever fits your style:
|
|
569
|
+
*
|
|
570
|
+
* ---
|
|
571
|
+
*
|
|
572
|
+
* ### 1. Drizzle `sql\`...\`` expression *(best DX — use your schema columns directly)*
|
|
573
|
+
*
|
|
574
|
+
* Import `sql` from `drizzle-orm` and write the WHERE clause exactly as you
|
|
575
|
+
* would in any Drizzle query. TableCraft passes the expression through unchanged,
|
|
576
|
+
* so the full power of Drizzle is available — joins, functions, OR logic, anything.
|
|
577
|
+
* You own the safety of the expression.
|
|
578
|
+
*
|
|
579
|
+
* ```ts
|
|
580
|
+
* import { sql } from 'drizzle-orm';
|
|
581
|
+
* import { orders, orderItems } from '../db/schema';
|
|
582
|
+
*
|
|
583
|
+
* .subquery('itemCount', orderItems, 'count',
|
|
584
|
+
* sql`${orderItems.orderId} = ${orders.id}`)
|
|
585
|
+
*
|
|
586
|
+
* // With an extra condition:
|
|
587
|
+
* .subquery('activeItemCount', orderItems, 'count',
|
|
588
|
+
* sql`${orderItems.orderId} = ${orders.id} AND ${orderItems.status} = ${'active'}`)
|
|
589
|
+
* ```
|
|
590
|
+
*
|
|
591
|
+
* ---
|
|
592
|
+
*
|
|
593
|
+
* ### 2. Structured `SubqueryCondition[]` *(typed, injection-safe)*
|
|
594
|
+
*
|
|
595
|
+
* Pass an array of condition objects. Each has `left`, `op` (default `'eq'`),
|
|
596
|
+
* and `right` operands — either `{ column: 'table.column' }` or `{ value: literal }`.
|
|
597
|
+
* Conditions are AND-combined. Literal values are parameterized automatically.
|
|
598
|
+
*
|
|
599
|
+
* ```ts
|
|
600
|
+
* // Simple column-to-column join:
|
|
601
|
+
* .subquery('itemCount', orderItems, 'count', [
|
|
602
|
+
* { left: { column: 'order_items.order_id' }, op: 'eq', right: { column: 'orders.id' } },
|
|
603
|
+
* ])
|
|
604
|
+
*
|
|
605
|
+
* // With a literal value filter:
|
|
606
|
+
* .subquery('activeItemCount', orderItems, 'count', [
|
|
607
|
+
* { left: { column: 'order_items.order_id' }, op: 'eq', right: { column: 'orders.id' } },
|
|
608
|
+
* { left: { column: 'order_items.status' }, op: 'eq', right: { value: 'active' } },
|
|
609
|
+
* ])
|
|
610
|
+
* ```
|
|
611
|
+
*
|
|
612
|
+
* ---
|
|
613
|
+
*
|
|
614
|
+
* ### 3. Raw SQL string *(@deprecated — developer-authored constants only)*
|
|
615
|
+
*
|
|
616
|
+
* Still accepted for backwards compatibility. Must be a hardcoded string authored
|
|
617
|
+
* by the developer — never derived from user input. Prefer form 1 or 2 instead.
|
|
618
|
+
*
|
|
619
|
+
* ```ts
|
|
620
|
+
* // @deprecated
|
|
621
|
+
* .subquery('itemCount', orderItems, 'count', 'order_items.order_id = orders.id')
|
|
622
|
+
* ```
|
|
623
|
+
*
|
|
624
|
+
* ---
|
|
625
|
+
*
|
|
626
|
+
* Omitting `filter` creates an uncorrelated subquery (full table scan).
|
|
627
|
+
*/
|
|
552
628
|
subquery(alias, table, type, filter) {
|
|
553
629
|
if (!this._config.subqueries)
|
|
554
630
|
this._config.subqueries = [];
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
table: getTableName(table),
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
631
|
+
let entry;
|
|
632
|
+
if (filter === undefined || filter === null) {
|
|
633
|
+
entry = { alias, table: getTableName(table), type };
|
|
634
|
+
}
|
|
635
|
+
else if (filter instanceof SQL) {
|
|
636
|
+
// Drizzle SQL expression — stored as runtime-only filterSql (not JSON-serializable)
|
|
637
|
+
entry = { alias, table: getTableName(table), type, filterSql: filter };
|
|
638
|
+
}
|
|
639
|
+
else if (typeof filter === 'string') {
|
|
640
|
+
// @deprecated raw string — kept for backwards compatibility
|
|
641
|
+
entry = { alias, table: getTableName(table), type, filter };
|
|
642
|
+
}
|
|
643
|
+
else {
|
|
644
|
+
entry = { alias, table: getTableName(table), type, filterConditions: filter };
|
|
645
|
+
}
|
|
646
|
+
// Dedupe subquery entries by alias — replace if exists
|
|
647
|
+
const existingIdx = this._config.subqueries.findIndex(e => e.alias === alias);
|
|
648
|
+
if (existingIdx >= 0) {
|
|
649
|
+
this._config.subqueries[existingIdx] = entry;
|
|
650
|
+
}
|
|
651
|
+
else {
|
|
652
|
+
this._config.subqueries.push(entry);
|
|
653
|
+
}
|
|
654
|
+
// Register as a computed column so sorting/filtering validation passes.
|
|
655
|
+
// The actual SQL expression is built at query-time by SubqueryBuilder and
|
|
656
|
+
// merged into the sqlExpressions map by the engine.
|
|
657
|
+
//
|
|
658
|
+
// 'first' mode returns row_to_json() — a non-scalar JSON object — which
|
|
659
|
+
// cannot be used in ORDER BY. Mark it sortable: false to prevent DB errors.
|
|
660
|
+
// 'count' (integer) and 'exists' (boolean) are scalar and safe to sort.
|
|
661
|
+
const existingCol = this._config.columns.find(c => c.name === alias);
|
|
662
|
+
if (existingCol) {
|
|
663
|
+
existingCol.type = type === 'exists' ? 'boolean' : type === 'count' ? 'number' : 'json';
|
|
664
|
+
existingCol.sortable = type !== 'first';
|
|
665
|
+
existingCol.filterable = false;
|
|
666
|
+
existingCol.computed = true;
|
|
667
|
+
}
|
|
668
|
+
else {
|
|
669
|
+
this._config.columns.push({
|
|
670
|
+
name: alias,
|
|
671
|
+
type: type === 'exists' ? 'boolean' : type === 'count' ? 'number' : 'json',
|
|
672
|
+
label: alias,
|
|
673
|
+
hidden: false,
|
|
674
|
+
sortable: type !== 'first',
|
|
675
|
+
filterable: false,
|
|
676
|
+
computed: true,
|
|
677
|
+
});
|
|
678
|
+
}
|
|
561
679
|
return this;
|
|
562
680
|
}
|
|
563
681
|
// ──── Platform Features ────
|
|
@@ -599,10 +717,7 @@ export class TableDefinitionBuilder {
|
|
|
599
717
|
* 'none' = skip counting entirely — fastest
|
|
600
718
|
*/
|
|
601
719
|
countMode(mode) {
|
|
602
|
-
|
|
603
|
-
this._config._countMode = mode;
|
|
604
|
-
}
|
|
605
|
-
this._config._countMode = mode;
|
|
720
|
+
this._ext.countMode = mode;
|
|
606
721
|
return this;
|
|
607
722
|
}
|
|
608
723
|
// ──── DISTINCT ────
|