@kuindji/typed-sql 0.1.0
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/LICENSE +21 -0
- package/README.md +227 -0
- package/dist/builder/assemble.d.ts +13 -0
- package/dist/builder/assemble.d.ts.map +1 -0
- package/dist/builder/assemble.js +86 -0
- package/dist/builder/assemble.js.map +1 -0
- package/dist/builder/condition-tree.d.ts +27 -0
- package/dist/builder/condition-tree.d.ts.map +1 -0
- package/dist/builder/condition-tree.js +91 -0
- package/dist/builder/condition-tree.js.map +1 -0
- package/dist/builder/conditional-sql.d.ts +80 -0
- package/dist/builder/conditional-sql.d.ts.map +1 -0
- package/dist/builder/conditional-sql.js +88 -0
- package/dist/builder/conditional-sql.js.map +1 -0
- package/dist/builder/db.d.ts +76 -0
- package/dist/builder/db.d.ts.map +1 -0
- package/dist/builder/db.js +12 -0
- package/dist/builder/db.js.map +1 -0
- package/dist/builder/delete.d.ts +39 -0
- package/dist/builder/delete.d.ts.map +1 -0
- package/dist/builder/delete.js +33 -0
- package/dist/builder/delete.js.map +1 -0
- package/dist/builder/extract-params.d.ts +97 -0
- package/dist/builder/extract-params.d.ts.map +1 -0
- package/dist/builder/extract-params.js +2 -0
- package/dist/builder/extract-params.js.map +1 -0
- package/dist/builder/index.d.ts +23 -0
- package/dist/builder/index.d.ts.map +1 -0
- package/dist/builder/index.js +14 -0
- package/dist/builder/index.js.map +1 -0
- package/dist/builder/insert.d.ts +51 -0
- package/dist/builder/insert.d.ts.map +1 -0
- package/dist/builder/insert.js +39 -0
- package/dist/builder/insert.js.map +1 -0
- package/dist/builder/mutate.d.ts +28 -0
- package/dist/builder/mutate.d.ts.map +1 -0
- package/dist/builder/mutate.js +17 -0
- package/dist/builder/mutate.js.map +1 -0
- package/dist/builder/params.d.ts +22 -0
- package/dist/builder/params.d.ts.map +1 -0
- package/dist/builder/params.js +65 -0
- package/dist/builder/params.js.map +1 -0
- package/dist/builder/return-type.d.ts +39 -0
- package/dist/builder/return-type.d.ts.map +1 -0
- package/dist/builder/return-type.js +2 -0
- package/dist/builder/return-type.js.map +1 -0
- package/dist/builder/scanner.d.ts +49 -0
- package/dist/builder/scanner.d.ts.map +1 -0
- package/dist/builder/scanner.js +240 -0
- package/dist/builder/scanner.js.map +1 -0
- package/dist/builder/select.d.ts +76 -0
- package/dist/builder/select.d.ts.map +1 -0
- package/dist/builder/select.js +240 -0
- package/dist/builder/select.js.map +1 -0
- package/dist/builder/sql-tag.d.ts +319 -0
- package/dist/builder/sql-tag.d.ts.map +1 -0
- package/dist/builder/sql-tag.js +3 -0
- package/dist/builder/sql-tag.js.map +1 -0
- package/dist/builder/sql.d.ts +17 -0
- package/dist/builder/sql.d.ts.map +1 -0
- package/dist/builder/sql.js +36 -0
- package/dist/builder/sql.js.map +1 -0
- package/dist/builder/state.d.ts +53 -0
- package/dist/builder/state.d.ts.map +1 -0
- package/dist/builder/state.js +18 -0
- package/dist/builder/state.js.map +1 -0
- package/dist/builder/update.d.ts +60 -0
- package/dist/builder/update.d.ts.map +1 -0
- package/dist/builder/update.js +40 -0
- package/dist/builder/update.js.map +1 -0
- package/dist/builder/write-assemble.d.ts +5 -0
- package/dist/builder/write-assemble.d.ts.map +1 -0
- package/dist/builder/write-assemble.js +57 -0
- package/dist/builder/write-assemble.js.map +1 -0
- package/dist/builder/write-state.d.ts +39 -0
- package/dist/builder/write-state.d.ts.map +1 -0
- package/dist/builder/write-state.js +6 -0
- package/dist/builder/write-state.js.map +1 -0
- package/dist/builder/write-tag.d.ts +91 -0
- package/dist/builder/write-tag.d.ts.map +1 -0
- package/dist/builder/write-tag.js +2 -0
- package/dist/builder/write-tag.js.map +1 -0
- package/dist/columns.d.ts +33 -0
- package/dist/columns.d.ts.map +1 -0
- package/dist/columns.js +2 -0
- package/dist/columns.js.map +1 -0
- package/dist/expressions.d.ts +71 -0
- package/dist/expressions.d.ts.map +1 -0
- package/dist/expressions.js +2 -0
- package/dist/expressions.js.map +1 -0
- package/dist/index.d.ts +22 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -0
- package/dist/parsing/extract.d.ts +47 -0
- package/dist/parsing/extract.d.ts.map +1 -0
- package/dist/parsing/extract.js +2 -0
- package/dist/parsing/extract.js.map +1 -0
- package/dist/parsing/normalize.d.ts +44 -0
- package/dist/parsing/normalize.d.ts.map +1 -0
- package/dist/parsing/normalize.js +2 -0
- package/dist/parsing/normalize.js.map +1 -0
- package/dist/parsing/pg-literals.d.ts +37 -0
- package/dist/parsing/pg-literals.d.ts.map +1 -0
- package/dist/parsing/pg-literals.js +2 -0
- package/dist/parsing/pg-literals.js.map +1 -0
- package/dist/parsing/split.d.ts +100 -0
- package/dist/parsing/split.d.ts.map +1 -0
- package/dist/parsing/split.js +2 -0
- package/dist/parsing/split.js.map +1 -0
- package/dist/parsing/string-utils.d.ts +29 -0
- package/dist/parsing/string-utils.d.ts.map +1 -0
- package/dist/parsing/string-utils.js +2 -0
- package/dist/parsing/string-utils.js.map +1 -0
- package/dist/parsing/tokenize.d.ts +27 -0
- package/dist/parsing/tokenize.d.ts.map +1 -0
- package/dist/parsing/tokenize.js +2 -0
- package/dist/parsing/tokenize.js.map +1 -0
- package/dist/parsing.d.ts +7 -0
- package/dist/parsing.d.ts.map +1 -0
- package/dist/parsing.js +9 -0
- package/dist/parsing.js.map +1 -0
- package/dist/partial.d.ts +30 -0
- package/dist/partial.d.ts.map +1 -0
- package/dist/partial.js +10 -0
- package/dist/partial.js.map +1 -0
- package/dist/schema.d.ts +28 -0
- package/dist/schema.d.ts.map +1 -0
- package/dist/schema.js +2 -0
- package/dist/schema.js.map +1 -0
- package/dist/tables.d.ts +34 -0
- package/dist/tables.d.ts.map +1 -0
- package/dist/tables.js +2 -0
- package/dist/tables.js.map +1 -0
- package/dist/utils.d.ts +13 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +3 -0
- package/dist/utils.js.map +1 -0
- package/dist/validation/cte.d.ts +54 -0
- package/dist/validation/cte.d.ts.map +1 -0
- package/dist/validation/cte.js +2 -0
- package/dist/validation/cte.js.map +1 -0
- package/dist/validation/dispatch.d.ts +31 -0
- package/dist/validation/dispatch.d.ts.map +1 -0
- package/dist/validation/dispatch.js +2 -0
- package/dist/validation/dispatch.js.map +1 -0
- package/dist/validation/joins.d.ts +16 -0
- package/dist/validation/joins.d.ts.map +1 -0
- package/dist/validation/joins.js +2 -0
- package/dist/validation/joins.js.map +1 -0
- package/dist/validation/return-derived.d.ts +67 -0
- package/dist/validation/return-derived.d.ts.map +1 -0
- package/dist/validation/return-derived.js +5 -0
- package/dist/validation/return-derived.js.map +1 -0
- package/dist/validation/return-types.d.ts +41 -0
- package/dist/validation/return-types.d.ts.map +1 -0
- package/dist/validation/return-types.js +2 -0
- package/dist/validation/return-types.js.map +1 -0
- package/dist/validation/validate-columns.d.ts +63 -0
- package/dist/validation/validate-columns.d.ts.map +1 -0
- package/dist/validation/validate-columns.js +2 -0
- package/dist/validation/validate-columns.js.map +1 -0
- package/dist/validation.d.ts +7 -0
- package/dist/validation.d.ts.map +1 -0
- package/dist/validation.js +9 -0
- package/dist/validation.js.map +1 -0
- package/package.json +64 -0
- package/src/builder/assemble.ts +100 -0
- package/src/builder/condition-tree.ts +162 -0
- package/src/builder/conditional-sql.ts +325 -0
- package/src/builder/db.ts +281 -0
- package/src/builder/delete.ts +57 -0
- package/src/builder/extract-params.ts +507 -0
- package/src/builder/index.ts +58 -0
- package/src/builder/insert.ts +75 -0
- package/src/builder/mutate.ts +55 -0
- package/src/builder/params.ts +95 -0
- package/src/builder/return-type.ts +66 -0
- package/src/builder/scanner.ts +254 -0
- package/src/builder/select.ts +470 -0
- package/src/builder/sql-tag.ts +422 -0
- package/src/builder/sql.ts +51 -0
- package/src/builder/state.ts +55 -0
- package/src/builder/update.ts +77 -0
- package/src/builder/write-assemble.ts +52 -0
- package/src/builder/write-state.ts +43 -0
- package/src/builder/write-tag.ts +119 -0
- package/src/columns.ts +336 -0
- package/src/expressions.ts +745 -0
- package/src/index.ts +81 -0
- package/src/parsing/extract.ts +260 -0
- package/src/parsing/normalize.ts +243 -0
- package/src/parsing/pg-literals.ts +289 -0
- package/src/parsing/split.ts +288 -0
- package/src/parsing/string-utils.ts +172 -0
- package/src/parsing/tokenize.ts +321 -0
- package/src/parsing.ts +8 -0
- package/src/partial.ts +241 -0
- package/src/schema.ts +130 -0
- package/src/tables.ts +278 -0
- package/src/utils.ts +43 -0
- package/src/validation/cte.ts +198 -0
- package/src/validation/dispatch.ts +312 -0
- package/src/validation/joins.ts +198 -0
- package/src/validation/return-derived.ts +253 -0
- package/src/validation/return-types.ts +271 -0
- package/src/validation/validate-columns.ts +489 -0
- package/src/validation.ts +8 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Ivan Kuindzhi
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
# @kuindji/typed-sql
|
|
2
|
+
|
|
3
|
+
**A compile-time SQL validator and result-type inferrer for TypeScript.**
|
|
4
|
+
|
|
5
|
+
You write SQL as a normal TypeScript string. The library parses and checks it
|
|
6
|
+
**entirely in the type system** — against a schema you describe as a type — and
|
|
7
|
+
infers the shape of the rows the query returns. Nothing runs at runtime for the
|
|
8
|
+
validation/inference: the work happens while `tsc` type-checks your code.
|
|
9
|
+
|
|
10
|
+
```ts
|
|
11
|
+
import type { ValidateSQL, GetReturnType, DatabaseSchema } from "@kuindji/typed-sql";
|
|
12
|
+
|
|
13
|
+
type Schema = {
|
|
14
|
+
defaultSchema: "public";
|
|
15
|
+
schemas: {
|
|
16
|
+
public: {
|
|
17
|
+
users: { id: number; email: string; name: string | null };
|
|
18
|
+
};
|
|
19
|
+
};
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
type Ok = ValidateSQL<"select id, email from users", Schema>; // true
|
|
23
|
+
type Bad = ValidateSQL<"select id, nope from users", Schema>; // false
|
|
24
|
+
type Rows = GetReturnType<"select id, name from users", Schema>; // { id: number; name: string | null }
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
> **Target dialect: PostgreSQL.** Quoted identifiers (`"camelCase"`), `::` casts,
|
|
28
|
+
> `coalesce`, `distinct on`, `returning`, etc. are interpreted with Postgres
|
|
29
|
+
> semantics.
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
## What it IS
|
|
34
|
+
|
|
35
|
+
- **A type-level SQL parser.** Validation and row-type inference run in the
|
|
36
|
+
TypeScript type system at compile time. The "parser" is a tower of conditional
|
|
37
|
+
types, not runtime code.
|
|
38
|
+
- **A schema-checked SQL guard.** Given a `DatabaseSchema` type, it confirms that
|
|
39
|
+
tables, columns, aliases, and references in a query actually exist, and rejects
|
|
40
|
+
ones that don't.
|
|
41
|
+
- **A result-type inferrer.** `GetReturnType<Q, Schema>` produces the row object a
|
|
42
|
+
`SELECT`/`RETURNING` query yields, including join nullability and casts.
|
|
43
|
+
- **A small runtime query builder** (`createSelectQuery`, `createConditionTree`,
|
|
44
|
+
conditional-SQL helpers) that assembles a SQL **string + ordered params** and
|
|
45
|
+
carries the inferred result type alongside it.
|
|
46
|
+
|
|
47
|
+
## What it is NOT
|
|
48
|
+
|
|
49
|
+
- **Not a runtime SQL parser or engine.** It does not parse SQL at runtime, does
|
|
50
|
+
not execute queries, and does not connect to a database. `createSelectFn(driver)`
|
|
51
|
+
takes **your** executor and just hands it the assembled `(sql, params)` — you
|
|
52
|
+
bring the database client.
|
|
53
|
+
- **Not an ORM.** No models, no migrations, no relations, no lazy loading, no
|
|
54
|
+
query DSL that hides SQL. You write SQL; it checks SQL.
|
|
55
|
+
- **Not a complete SQL grammar.** The parser is **intentionally shallow**. Many
|
|
56
|
+
constructs are recognized just enough to extract tables/columns/result shape;
|
|
57
|
+
anything it doesn't model is passed through leniently rather than rejected.
|
|
58
|
+
- **Not a linter / style enforcer.** It checks *existence and shape*, not
|
|
59
|
+
formatting, performance, or SQL best practices.
|
|
60
|
+
- **Not a precise expression type-checker.** It does not attempt full SQL type
|
|
61
|
+
inference. Ambiguous expressions are deliberately typed `unknown` (see below).
|
|
62
|
+
|
|
63
|
+
---
|
|
64
|
+
|
|
65
|
+
## Usage
|
|
66
|
+
|
|
67
|
+
### 1. Describe your schema as a type
|
|
68
|
+
|
|
69
|
+
```ts
|
|
70
|
+
type DatabaseSchema = {
|
|
71
|
+
defaultSchema: string;
|
|
72
|
+
schemas: Record<string /* schema */, Record<string /* table */, Record<string /* column */, /* TS type */ unknown>>>;
|
|
73
|
+
};
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
- A **nullable** column is encoded as `T | null` (e.g. `name: string | null`).
|
|
77
|
+
- Table/column/schema name matching is **case-insensitive**.
|
|
78
|
+
- Column types can be anything: scalars, `"a" | "b"` enums, arrays, nested
|
|
79
|
+
JSON-shaped objects, `Record<string, unknown>`.
|
|
80
|
+
|
|
81
|
+
### 2. Validate and infer over plain SQL
|
|
82
|
+
|
|
83
|
+
```ts
|
|
84
|
+
type Valid = ValidateSQL<"update users set name = $1 where id = $2", Schema>; // true | false
|
|
85
|
+
type Row = GetReturnType<"select id, name from users where id = $1", Schema>;
|
|
86
|
+
|
|
87
|
+
// DML helpers
|
|
88
|
+
type InsertCols = GetInsertTableColumns<"insert into users ...", Schema>;
|
|
89
|
+
type UpdateCols = GetUpdateTableColumns<"update users set ...", Schema>;
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### 3. Or build queries with the runtime builder
|
|
93
|
+
|
|
94
|
+
```ts
|
|
95
|
+
import { createSelectQuery, createSelectFn } from "@kuindji/typed-sql";
|
|
96
|
+
|
|
97
|
+
const q = createSelectQuery<Schema>()
|
|
98
|
+
.from("users u")
|
|
99
|
+
.select("u.id")
|
|
100
|
+
.where("u.id = :id")
|
|
101
|
+
.withParams({ id: 42 });
|
|
102
|
+
|
|
103
|
+
q.toString(); // "SELECT u.id FROM users u WHERE u.id = $1"
|
|
104
|
+
[...q.getParams()]; // [42] ← named params expanded to $1, $2… in order
|
|
105
|
+
|
|
106
|
+
// Wire YOUR driver. The library never touches the DB itself.
|
|
107
|
+
const select = createSelectFn<Schema>((sql, params) => pg.query(sql, params));
|
|
108
|
+
const rows = await select(q); // rows typed from the builder's inferred result
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### Write builders (INSERT / UPDATE / DELETE) with typed params
|
|
112
|
+
|
|
113
|
+
```ts
|
|
114
|
+
import { createInsertQuery, createMutateFn, createSql } from "@kuindji/typed-sql";
|
|
115
|
+
|
|
116
|
+
const q = createInsertQuery<Schema>()
|
|
117
|
+
.into("orders")
|
|
118
|
+
.value("userId", ":uid") // :uid typed to orders.userId's exact (branded) type
|
|
119
|
+
.value("amount", ":amt")
|
|
120
|
+
.valueIf(hasNote, "note", ":note") // conditional → :note optional in withParams
|
|
121
|
+
.returning("id")
|
|
122
|
+
.withParams({ uid, amt, ...(hasNote ? { note } : {}) });
|
|
123
|
+
|
|
124
|
+
q.toString(); // "insert into orders (userId, amount) values ($1, $2) returning id"
|
|
125
|
+
[...q.getParams()]; // [uid, amt]
|
|
126
|
+
|
|
127
|
+
// Raw typed SQL:
|
|
128
|
+
const sql = createSql<Schema>();
|
|
129
|
+
const d = sql("delete from orders where id = :id").withParams({ id });
|
|
130
|
+
|
|
131
|
+
// Executor — bring your driver; it returns the RETURNING rows (or [] when none):
|
|
132
|
+
const mutate = createMutateFn<Schema>((s, p) => pool.query(s, p).then(r => r.rows));
|
|
133
|
+
const rows = await mutate(q); // typed from RETURNING
|
|
134
|
+
|
|
135
|
+
// Passing a plain string where a branded column is expected is a compile error.
|
|
136
|
+
// Multi-row VALUES is rejected in the typed path — use the untyped driver call.
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
---
|
|
140
|
+
|
|
141
|
+
## Behavior notes
|
|
142
|
+
|
|
143
|
+
A few deliberate behaviors you'll observe when using the library:
|
|
144
|
+
|
|
145
|
+
- **Ambiguous expressions type as `unknown`.** The inferrer types an expression
|
|
146
|
+
only when its type is unambiguous — `CASE` and unmodeled functions are
|
|
147
|
+
`unknown` rather than a guess. `||` (string concat) → `string`. An unaliased
|
|
148
|
+
function/aggregate projection is named after the function (`count(*)` →
|
|
149
|
+
`{ count: number }`); an unaliased `CASE` is named `case`.
|
|
150
|
+
- **Projected literals widen to their base type** — `select 'GBP' as cur` →
|
|
151
|
+
`{ cur: string }`, `select 42 as n` → `{ n: number }`, not `{ cur: "GBP" }` /
|
|
152
|
+
`{ n: 42 }`. Locked literal types reject every other value in mutable
|
|
153
|
+
bindings, `useState`, props, etc.; add an explicit cast at the call site when
|
|
154
|
+
you want the literal back.
|
|
155
|
+
- **Validation is intentionally lenient.** The parser models the common shape of
|
|
156
|
+
real queries, not the full SQL grammar, and biases toward **never rejecting
|
|
157
|
+
valid SQL** — which means some invalid constructs may pass as `true`. Very
|
|
158
|
+
large/complex queries may fall back to `unknown`/`true` rather than failing
|
|
159
|
+
(TypeScript's recursion limits put a hard ceiling on type-level parsing).
|
|
160
|
+
- **Join nullability:** outer joins add `| null` to columns sourced from the
|
|
161
|
+
nullable side (`left join … x` ⇒ `x.col` becomes `T | null`). This applies
|
|
162
|
+
inside `coalesce(...)` too: the result is nullable only if **every** argument
|
|
163
|
+
is (Postgres semantics), so `coalesce(x, '')` stays non-null.
|
|
164
|
+
|
|
165
|
+
---
|
|
166
|
+
|
|
167
|
+
## Conditional builder methods (`*If`) — runtime vs type-level
|
|
168
|
+
|
|
169
|
+
The builder's `*If` methods — `selectIf`, `whereIf`, `joinIf`, `groupByIf`,
|
|
170
|
+
`havingIf`, `orderByIf`, `limitIf`, `offsetIf`, and `applyIf` — take a **runtime
|
|
171
|
+
boolean** as their first argument. This creates a deliberate gap between what runs
|
|
172
|
+
and what the types say:
|
|
173
|
+
|
|
174
|
+
- **Runtime:** the fragment is included in the emitted SQL **only if the condition
|
|
175
|
+
is truthy** at call time. `selectIf(false, "name")` adds nothing to the query.
|
|
176
|
+
- **Type-level:** TypeScript cannot see a runtime boolean's value, so the inferred
|
|
177
|
+
result type does **not** branch on it. It infers from the **maximal** query —
|
|
178
|
+
every `*If` fragment treated as present — and then marks columns that *might* be
|
|
179
|
+
absent as **optional**.
|
|
180
|
+
|
|
181
|
+
Per method:
|
|
182
|
+
|
|
183
|
+
- **`selectIf` / `applyIf` that introduce a column** → that column becomes an
|
|
184
|
+
**optional property** in the result row (`name?: T`, i.e. `T | undefined` at the
|
|
185
|
+
use site). Unconditional `select`/`apply` columns stay **required**, regardless
|
|
186
|
+
of call order.
|
|
187
|
+
- **If there is _no_ unconditional `select` at all**, the all-false runtime path
|
|
188
|
+
emits `SELECT *`, so the whole row falls back to `Partial<…>` — **every** column
|
|
189
|
+
optional.
|
|
190
|
+
- **Clause-only `*If`** (`whereIf`, `joinIf`, `groupByIf`, `havingIf`, `orderByIf`,
|
|
191
|
+
`limitIf`, `offsetIf`) conditionally changes the **SQL text** at runtime but does
|
|
192
|
+
**not** change the result column set — the type is computed as if the clause is
|
|
193
|
+
present.
|
|
194
|
+
|
|
195
|
+
```ts
|
|
196
|
+
const dyn: boolean = /* computed at runtime */;
|
|
197
|
+
const q = createSelectQuery<Schema>()
|
|
198
|
+
.from("users")
|
|
199
|
+
.select("id") // unconditional → required
|
|
200
|
+
.selectIf(dyn, "name"); // conditional → optional
|
|
201
|
+
|
|
202
|
+
type Row = BuilderReturnType<typeof q>;
|
|
203
|
+
// { id: number; name?: string } ← id required; name is `string | undefined`
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
## Two kinds of "maybe missing": `| null` vs optional (`| undefined`)
|
|
207
|
+
|
|
208
|
+
These look similar but mean **different** things:
|
|
209
|
+
|
|
210
|
+
| | Source | Type shape | Meaning |
|
|
211
|
+
|---|---|---|---|
|
|
212
|
+
| **`\| null`** | `LEFT`/outer join (nullable side) | `col: T \| null` — **key always present** | The column is in every row, but its **value** can be SQL `NULL` (the join didn't match). |
|
|
213
|
+
| **optional (`\| undefined`)** | `selectIf` / `applyIf` conditional projection | `col?: T` — **key may be absent** | The column may **not be in the result object at all**, because it wasn't selected at runtime. |
|
|
214
|
+
|
|
215
|
+
A left-joined column that is *also* conditionally selected is **both**:
|
|
216
|
+
`col?: T | null`.
|
|
217
|
+
|
|
218
|
+
---
|
|
219
|
+
|
|
220
|
+
## Contributing
|
|
221
|
+
|
|
222
|
+
Contributing or reviewing? See [CONTRIBUTING.md](./CONTRIBUTING.md) for the
|
|
223
|
+
design contracts, internals, and things that look like bugs but are intended.
|
|
224
|
+
|
|
225
|
+
## License
|
|
226
|
+
|
|
227
|
+
MIT © Ivan Kuindzhi
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { RuntimeSelectState } from "./state.js";
|
|
2
|
+
/**
|
|
3
|
+
* Assemble a SQL string from runtime builder state.
|
|
4
|
+
*
|
|
5
|
+
* - Uses user-provided fragments as-is (no parsing/normalization).
|
|
6
|
+
* - Inserts SQL keywords in uppercase.
|
|
7
|
+
* - Skips empty clauses; defaults to SELECT * when no select fragments.
|
|
8
|
+
* - Expands :name params to $n (first-appearance order; arrays expand).
|
|
9
|
+
*
|
|
10
|
+
* Ported from the predecessor package; byte-identical output.
|
|
11
|
+
*/
|
|
12
|
+
export declare function assembleSelectSQL(state: RuntimeSelectState): string;
|
|
13
|
+
//# sourceMappingURL=assemble.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"assemble.d.ts","sourceRoot":"","sources":["../../src/builder/assemble.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAErD;;;;;;;;;GASG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,kBAAkB,GAAG,MAAM,CAqFnE"}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
// src/builder/assemble.ts
|
|
2
|
+
import { expandNamedParams } from "./params.js";
|
|
3
|
+
/**
|
|
4
|
+
* Assemble a SQL string from runtime builder state.
|
|
5
|
+
*
|
|
6
|
+
* - Uses user-provided fragments as-is (no parsing/normalization).
|
|
7
|
+
* - Inserts SQL keywords in uppercase.
|
|
8
|
+
* - Skips empty clauses; defaults to SELECT * when no select fragments.
|
|
9
|
+
* - Expands :name params to $n (first-appearance order; arrays expand).
|
|
10
|
+
*
|
|
11
|
+
* Ported from the predecessor package; byte-identical output.
|
|
12
|
+
*/
|
|
13
|
+
export function assembleSelectSQL(state) {
|
|
14
|
+
const parts = [];
|
|
15
|
+
const cteIds = Object.keys(state.cteSql);
|
|
16
|
+
if (cteIds.length > 0) {
|
|
17
|
+
const withParts = cteIds.map(id => state.cteSql[id]).join(", ");
|
|
18
|
+
parts.push(`WITH ${withParts}`);
|
|
19
|
+
}
|
|
20
|
+
const selectIds = Object.keys(state.selectSql);
|
|
21
|
+
if (selectIds.length === 0) {
|
|
22
|
+
parts.push("SELECT *");
|
|
23
|
+
}
|
|
24
|
+
else {
|
|
25
|
+
const selectFragments = [];
|
|
26
|
+
for (const id of selectIds) {
|
|
27
|
+
const cols = state.selectSql[id];
|
|
28
|
+
if (cols && cols.length > 0) {
|
|
29
|
+
selectFragments.push(cols.join(", "));
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
const selectSql = selectFragments.length > 0
|
|
33
|
+
? selectFragments.join(", ")
|
|
34
|
+
: "*";
|
|
35
|
+
parts.push(state.distinct ? `SELECT DISTINCT ${selectSql}` : `SELECT ${selectSql}`);
|
|
36
|
+
}
|
|
37
|
+
if (state.fromSql) {
|
|
38
|
+
parts.push(`FROM ${state.fromSql}`);
|
|
39
|
+
}
|
|
40
|
+
for (const join of state.joins) {
|
|
41
|
+
const sql = state.joinSql[join.id];
|
|
42
|
+
if (sql) {
|
|
43
|
+
parts.push(sql);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
const whereParts = Object.keys(state.whereSql)
|
|
47
|
+
.map(id => state.whereSql[id])
|
|
48
|
+
.filter(Boolean);
|
|
49
|
+
if (whereParts.length > 0) {
|
|
50
|
+
parts.push(`WHERE ${whereParts.join(" AND ")}`);
|
|
51
|
+
}
|
|
52
|
+
const groupParts = Object.keys(state.groupBySql)
|
|
53
|
+
.map(id => state.groupBySql[id])
|
|
54
|
+
.filter(Boolean);
|
|
55
|
+
if (groupParts.length > 0) {
|
|
56
|
+
parts.push(`GROUP BY ${groupParts.join(", ")}`);
|
|
57
|
+
}
|
|
58
|
+
const havingParts = Object.keys(state.havingSql)
|
|
59
|
+
.map(id => state.havingSql[id])
|
|
60
|
+
.filter(Boolean);
|
|
61
|
+
if (havingParts.length > 0) {
|
|
62
|
+
parts.push(`HAVING ${havingParts.join(" AND ")}`);
|
|
63
|
+
}
|
|
64
|
+
const orderParts = Object.keys(state.orderBySql)
|
|
65
|
+
.map(id => state.orderBySql[id])
|
|
66
|
+
.filter(Boolean);
|
|
67
|
+
if (orderParts.length > 0) {
|
|
68
|
+
parts.push(`ORDER BY ${orderParts.join(", ")}`);
|
|
69
|
+
}
|
|
70
|
+
if (typeof state.limit === "number") {
|
|
71
|
+
parts.push(`LIMIT ${state.limit}`);
|
|
72
|
+
}
|
|
73
|
+
if (typeof state.offset === "number") {
|
|
74
|
+
parts.push(`OFFSET ${state.offset}`);
|
|
75
|
+
}
|
|
76
|
+
if (state.unionSql) {
|
|
77
|
+
parts.push(state.unionSql);
|
|
78
|
+
}
|
|
79
|
+
const sql = parts.join(" ");
|
|
80
|
+
const namedParams = state.namedParams;
|
|
81
|
+
if (namedParams && Object.keys(namedParams).length > 0) {
|
|
82
|
+
return expandNamedParams(sql, namedParams);
|
|
83
|
+
}
|
|
84
|
+
return sql;
|
|
85
|
+
}
|
|
86
|
+
//# sourceMappingURL=assemble.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"assemble.js","sourceRoot":"","sources":["../../src/builder/assemble.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export type ConditionTreePart = string | ConditionTreeState;
|
|
2
|
+
export interface ConditionTreeState {
|
|
3
|
+
readonly operator: "and" | "or";
|
|
4
|
+
readonly parts: ReadonlyArray<{
|
|
5
|
+
readonly id: string;
|
|
6
|
+
readonly condition: string | ConditionTreeState;
|
|
7
|
+
}>;
|
|
8
|
+
}
|
|
9
|
+
type UppercaseOperator<Op extends "and" | "or"> = Op extends "and" ? "AND" : "OR";
|
|
10
|
+
type AppendCondition<Current extends string, Part extends string, Op extends "and" | "or"> = string extends Current | Part ? string : Part extends "()" ? Current : Current extends "()" ? `(${Part})` : Current extends `(${infer Body})` ? `(${Body} ${UppercaseOperator<Op>} ${Part})` : string;
|
|
11
|
+
export declare class ConditionTreeBuilder<Op extends "and" | "or" = "and" | "or", Expr extends string = string> {
|
|
12
|
+
private readonly state;
|
|
13
|
+
private constructor();
|
|
14
|
+
static create<Op extends "and" | "or">(operator: Op): ConditionTreeBuilder<Op, "()">;
|
|
15
|
+
getState(): ConditionTreeState;
|
|
16
|
+
isEmpty(): boolean;
|
|
17
|
+
add<Part extends string | ConditionTreeBuilder<any, any>, Id extends string | undefined = undefined>(part: Part, id?: Id): ConditionTreeBuilder<Op, Id extends string ? string : AppendCondition<Expr, Part extends ConditionTreeBuilder<any, infer P extends string> ? P : Part extends string ? Part : string, Op>>;
|
|
18
|
+
remove(id: string): ConditionTreeBuilder<Op, string>;
|
|
19
|
+
when<Next extends ConditionTreeBuilder<any, any>>(condition: boolean, ifTrue: (b: ConditionTreeBuilder<Op, Expr>) => Next, ifFalse?: (b: ConditionTreeBuilder<Op, Expr>) => Next): ConditionTreeBuilder<Op, Expr> | Next;
|
|
20
|
+
toString(): Expr;
|
|
21
|
+
private static renderPart;
|
|
22
|
+
private static isConditionTreeState;
|
|
23
|
+
private static generateId;
|
|
24
|
+
}
|
|
25
|
+
export declare function createConditionTree<Op extends "and" | "or">(operator: Op): ConditionTreeBuilder<Op, "()">;
|
|
26
|
+
export {};
|
|
27
|
+
//# sourceMappingURL=condition-tree.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"condition-tree.d.ts","sourceRoot":"","sources":["../../src/builder/condition-tree.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,iBAAiB,GAAG,MAAM,GAAG,kBAAkB,CAAC;AAE5D,MAAM,WAAW,kBAAkB;IAC/B,QAAQ,CAAC,QAAQ,EAAE,KAAK,GAAG,IAAI,CAAC;IAChC,QAAQ,CAAC,KAAK,EAAE,aAAa,CAAC;QAC1B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;QACpB,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,kBAAkB,CAAC;KACnD,CAAC,CAAC;CACN;AAED,KAAK,iBAAiB,CAAC,EAAE,SAAS,KAAK,GAAG,IAAI,IAAI,EAAE,SAAS,KAAK,GAAG,KAAK,GAAG,IAAI,CAAC;AAElF,KAAK,eAAe,CAChB,OAAO,SAAS,MAAM,EACtB,IAAI,SAAS,MAAM,EACnB,EAAE,SAAS,KAAK,GAAG,IAAI,IACvB,MAAM,SAAS,OAAO,GAAG,IAAI,GAAG,MAAM,GAGpC,IAAI,SAAS,IAAI,GAAG,OAAO,GAC3B,OAAO,SAAS,IAAI,GAAG,IAAI,IAAI,GAAG,GAClC,OAAO,SAAS,IAAI,MAAM,IAAI,GAAG,GAC7B,IAAI,IAAI,IAAI,iBAAiB,CAAC,EAAE,CAAC,IAAI,IAAI,GAAG,GAChD,MAAM,CAAC;AAEb,qBAAa,oBAAoB,CAC7B,EAAE,SAAS,KAAK,GAAG,IAAI,GAAG,KAAK,GAAG,IAAI,EACtC,IAAI,SAAS,MAAM,GAAG,MAAM;IAE5B,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAqB;IAE3C,OAAO;IAIP,MAAM,CAAC,MAAM,CAAC,EAAE,SAAS,KAAK,GAAG,IAAI,EACjC,QAAQ,EAAE,EAAE,GACb,oBAAoB,CAAC,EAAE,EAAE,IAAI,CAAC;IAIjC,QAAQ,IAAI,kBAAkB;IAS9B,OAAO,IAAI,OAAO;IAIlB,GAAG,CAAC,IAAI,SAAS,MAAM,GAAG,oBAAoB,CAAC,GAAG,EAAE,GAAG,CAAC,EAAE,EAAE,SAAS,MAAM,GAAG,SAAS,GAAG,SAAS,EAC/F,IAAI,EAAE,IAAI,EACV,EAAE,CAAC,EAAE,EAAE,GACR,oBAAoB,CACnB,EAAE,EAIF,EAAE,SAAS,MAAM,GACX,MAAM,GACN,eAAe,CACb,IAAI,EACJ,IAAI,SAAS,oBAAoB,CAAC,GAAG,EAAE,MAAM,CAAC,SAAS,MAAM,CAAC,GAAG,CAAC,GAC5D,IAAI,SAAS,MAAM,GAAG,IAAI,GAC1B,MAAM,EACZ,EAAE,CACL,CACR;IA2BD,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,oBAAoB,CAAC,EAAE,EAAE,MAAM,CAAC;IAWpD,IAAI,CAAC,IAAI,SAAS,oBAAoB,CAAC,GAAG,EAAE,GAAG,CAAC,EAC5C,SAAS,EAAE,OAAO,EAClB,MAAM,EAAE,CAAC,CAAC,EAAE,oBAAoB,CAAC,EAAE,EAAE,IAAI,CAAC,KAAK,IAAI,EACnD,OAAO,CAAC,EAAE,CAAC,CAAC,EAAE,oBAAoB,CAAC,EAAE,EAAE,IAAI,CAAC,KAAK,IAAI,GACtD,oBAAoB,CAAC,EAAE,EAAE,IAAI,CAAC,GAAG,IAAI;IAOxC,QAAQ,IAAI,IAAI;IAYhB,OAAO,CAAC,MAAM,CAAC,UAAU;IAOzB,OAAO,CAAC,MAAM,CAAC,oBAAoB;IAWnC,OAAO,CAAC,MAAM,CAAC,UAAU;CAG5B;AAED,wBAAgB,mBAAmB,CAAC,EAAE,SAAS,KAAK,GAAG,IAAI,EACvD,QAAQ,EAAE,EAAE,GACb,oBAAoB,CAAC,EAAE,EAAE,IAAI,CAAC,CAEhC"}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
// src/builder/condition-tree.ts
|
|
2
|
+
export class ConditionTreeBuilder {
|
|
3
|
+
state;
|
|
4
|
+
constructor(state) {
|
|
5
|
+
this.state = state;
|
|
6
|
+
}
|
|
7
|
+
static create(operator) {
|
|
8
|
+
return new ConditionTreeBuilder({ operator, parts: [] });
|
|
9
|
+
}
|
|
10
|
+
getState() {
|
|
11
|
+
return this.state;
|
|
12
|
+
}
|
|
13
|
+
// True when this tree contributes no SQL: it holds no parts (so toString()
|
|
14
|
+
// would render the bare "()"). Consumers and the SELECT builder use this to
|
|
15
|
+
// skip empty trees entirely — legacy parity, where an empty condition tree
|
|
16
|
+
// (e.g. an empty status[] filter built as an empty OR-tree) was a no-op
|
|
17
|
+
// rather than an invalid `WHERE ()`.
|
|
18
|
+
isEmpty() {
|
|
19
|
+
return this.state.parts.length === 0;
|
|
20
|
+
}
|
|
21
|
+
add(part, id) {
|
|
22
|
+
// Skip an empty child tree: adding it would otherwise render a bare
|
|
23
|
+
// "()" inside this expression (e.g. "(a = 1 AND ())"), an invalid
|
|
24
|
+
// fragment. Legacy Query.ts trees skipped empty children too. Returning
|
|
25
|
+
// `this` keeps the builder unchanged (no part, no id slot consumed).
|
|
26
|
+
if (part instanceof ConditionTreeBuilder && part.isEmpty()) {
|
|
27
|
+
return this;
|
|
28
|
+
}
|
|
29
|
+
const partId = id ?? ConditionTreeBuilder.generateId();
|
|
30
|
+
const condition = part instanceof ConditionTreeBuilder ? part.getState() : part;
|
|
31
|
+
const existingIndex = this.state.parts.findIndex(p => p.id === partId);
|
|
32
|
+
const nextParts = existingIndex === -1
|
|
33
|
+
? [...this.state.parts, { id: partId, condition }]
|
|
34
|
+
: this.state.parts.map((p, idx) => idx === existingIndex ? { id: partId, condition } : p);
|
|
35
|
+
return new ConditionTreeBuilder({
|
|
36
|
+
operator: this.state.operator,
|
|
37
|
+
parts: nextParts,
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
// remove() drops a part; the rendered literal can no longer be reconstructed
|
|
41
|
+
// from `Expr` alone (the type doesn't track parts as a tuple), so widen to
|
|
42
|
+
// `string`. A `string`-typed tree fragment is accepted-but-untyped by
|
|
43
|
+
// ValidQueryBuilder's allow-unknown path (spec Open Questions) — no
|
|
44
|
+
// rejection, only reduced BuilderSQL precision for that one query.
|
|
45
|
+
remove(id) {
|
|
46
|
+
const nextParts = this.state.parts.filter(p => p.id !== id);
|
|
47
|
+
if (nextParts.length === this.state.parts.length) {
|
|
48
|
+
return this;
|
|
49
|
+
}
|
|
50
|
+
return new ConditionTreeBuilder({
|
|
51
|
+
operator: this.state.operator,
|
|
52
|
+
parts: nextParts,
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
when(condition, ifTrue, ifFalse) {
|
|
56
|
+
if (condition) {
|
|
57
|
+
return ifTrue(this);
|
|
58
|
+
}
|
|
59
|
+
return ifFalse ? ifFalse(this) : this;
|
|
60
|
+
}
|
|
61
|
+
toString() {
|
|
62
|
+
if (this.state.parts.length === 0) {
|
|
63
|
+
return "()";
|
|
64
|
+
}
|
|
65
|
+
const op = this.state.operator.toUpperCase();
|
|
66
|
+
const rendered = this.state.parts
|
|
67
|
+
.map(part => ConditionTreeBuilder.renderPart(part.condition))
|
|
68
|
+
.filter(s => s.length > 0)
|
|
69
|
+
.join(` ${op} `);
|
|
70
|
+
return `(${rendered})`;
|
|
71
|
+
}
|
|
72
|
+
static renderPart(condition) {
|
|
73
|
+
if (ConditionTreeBuilder.isConditionTreeState(condition)) {
|
|
74
|
+
return new ConditionTreeBuilder(condition).toString();
|
|
75
|
+
}
|
|
76
|
+
return String(condition ?? "").trim();
|
|
77
|
+
}
|
|
78
|
+
static isConditionTreeState(value) {
|
|
79
|
+
return (typeof value === "object"
|
|
80
|
+
&& value !== null
|
|
81
|
+
&& value.operator !== undefined
|
|
82
|
+
&& Array.isArray(value.parts));
|
|
83
|
+
}
|
|
84
|
+
static generateId() {
|
|
85
|
+
return `cond_${Math.random().toString(36).slice(2, 10)}`;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
export function createConditionTree(operator) {
|
|
89
|
+
return ConditionTreeBuilder.create(operator);
|
|
90
|
+
}
|
|
91
|
+
//# sourceMappingURL=condition-tree.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"condition-tree.js","sourceRoot":"","sources":["../../src/builder/condition-tree.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import type { DatabaseSchema } from "../schema.js";
|
|
2
|
+
import type { GetReturnType, ValidateSQL } from "../index.js";
|
|
3
|
+
import type { QueryParamValue } from "./params.js";
|
|
4
|
+
export interface ConditionalSQLOptions {
|
|
5
|
+
/** If true, preserves conditional comment markers in output (debugging). */
|
|
6
|
+
preserveMarkers?: boolean;
|
|
7
|
+
}
|
|
8
|
+
export interface ConditionalSQLOutput {
|
|
9
|
+
/** The processed SQL string with conditions applied. */
|
|
10
|
+
sql: string;
|
|
11
|
+
/** The parameter values in order of appearance. */
|
|
12
|
+
params: QueryParamValue[];
|
|
13
|
+
}
|
|
14
|
+
/** Process conditional blocks in a SQL template (innermost-first, iterative). */
|
|
15
|
+
export declare function processConditionalSQL(template: string, conditions: Record<string, unknown>): string;
|
|
16
|
+
/** Convert :name placeholders to positional $n; return processed SQL + values. */
|
|
17
|
+
export declare function processParams(sql: string, params: Record<string, QueryParamValue>): ConditionalSQLOutput;
|
|
18
|
+
/** Process a template with both conditions and parameters. */
|
|
19
|
+
export declare function conditionalSQL(template: string, conditions: Record<string, unknown>, params?: Record<string, QueryParamValue>): ConditionalSQLOutput;
|
|
20
|
+
/** Normalize whitespace in SQL (collapse spaces, tidy commas/parens). */
|
|
21
|
+
export declare function normalizeWhitespace(sql: string): string;
|
|
22
|
+
/** Get a nested value type using a dot-notation path. */
|
|
23
|
+
export type GetPath<T, Path extends string> = Path extends `${infer Key}.${infer Rest}` ? Key extends keyof T ? GetPath<T[Key], Rest> : undefined : Path extends keyof T ? T[Path] : undefined;
|
|
24
|
+
/** Type-level truthiness; resolves to `boolean` for non-literal wide types. */
|
|
25
|
+
export type IsTruthy<T> = [
|
|
26
|
+
T
|
|
27
|
+
] extends [false | 0 | "" | null | undefined] ? false : [T] extends [never] ? false : [T] extends [boolean] ? ([boolean] extends [T] ? boolean : true) : [T] extends [string] ? ([string] extends [T] ? boolean : true) : [T] extends [number] ? ([number] extends [T] ? boolean : true) : true;
|
|
28
|
+
/** Evaluate a condition string against a data type (supports `!` negation). */
|
|
29
|
+
export type EvalCondition<Cond extends string, Data> = Cond extends `!${infer Key}` ? IsTruthy<GetPath<Data, Key>> extends true ? false : IsTruthy<GetPath<Data, Key>> extends false ? true : boolean : IsTruthy<GetPath<Data, Cond>>;
|
|
30
|
+
/** Check if a string contains a specific pattern. */
|
|
31
|
+
type Contains<S extends string, Pattern extends string> = S extends `${string}${Pattern}${string}` ? true : false;
|
|
32
|
+
/** Check if any condition in the template has an indeterminate (wide) type. */
|
|
33
|
+
type HasIndeterminateCondition<Template extends string, Data extends Record<string, unknown>> = Template extends `${string}/*if:${infer Cond}*/${infer Rest}` ? EvalCondition<Cond, Data> extends boolean ? boolean extends EvalCondition<Cond, Data> ? true : HasIndeterminateCondition<Rest, Data> : HasIndeterminateCondition<Rest, Data> : false;
|
|
34
|
+
/** Process the innermost conditional block (inside-out). */
|
|
35
|
+
type ProcessInnermost<Template extends string, Data extends Record<string, unknown>> = Template extends `${infer Before}/*if:${infer Cond}*/${infer Content}/*endif*/${infer After}` ? Contains<Content, "/*if:"> extends true ? `${Before}/*if:${Cond}*/${ProcessInnermost<`${Content}/*endif*/${After}`, Data>}` : EvalCondition<Cond, Data> extends true ? `${Before}${Content}${After}` : EvalCondition<Cond, Data> extends false ? `${Before}${After}` : string : Template;
|
|
36
|
+
/** Recursively process all conditional blocks until none remain. */
|
|
37
|
+
export type ProcessConditionalSQL<Template extends string, Data extends Record<string, unknown>, Depth extends number[] = []> = HasIndeterminateCondition<Template, Data> extends true ? string : Depth["length"] extends 20 ? Template : Contains<Template, "/*if:"> extends true ? ProcessConditionalSQL<ProcessInnermost<Template, Data>, Data, [
|
|
38
|
+
...Depth,
|
|
39
|
+
0
|
|
40
|
+
]> : Template;
|
|
41
|
+
/** Force every condition value true (maximal column set). */
|
|
42
|
+
export type AllConditionsTrue<Data extends Record<string, unknown>> = {
|
|
43
|
+
[K in keyof Data]: Data[K] extends Record<string, unknown> ? AllConditionsTrue<Data[K]> : true;
|
|
44
|
+
};
|
|
45
|
+
/** Force every condition value false (minimal column set). */
|
|
46
|
+
export type AllConditionsFalse<Data extends Record<string, unknown>> = {
|
|
47
|
+
[K in keyof Data]: Data[K] extends Record<string, unknown> ? AllConditionsFalse<Data[K]> : false;
|
|
48
|
+
};
|
|
49
|
+
/** Marker: columns from conditional SELECT clauses are `T | undefined`. */
|
|
50
|
+
export type ConditionalColumn<T> = T | undefined;
|
|
51
|
+
/** Marker: columns from conditional LEFT JOINs are `T | null | undefined`. */
|
|
52
|
+
export type ConditionalLeftJoinColumn<T> = T | null | undefined;
|
|
53
|
+
/** Extract :name parameter names from a SQL string. */
|
|
54
|
+
export type ExtractParamNames<SQL extends string, Acc extends string[] = []> = SQL extends `${string}:${infer Name}${infer Rest}` ? Name extends `${infer ParamName}${" " | "," | ")" | "\n" | "\t"}` ? ExtractParamNames<Rest, [...Acc, ParamName]> : ExtractParamNames<Rest, [...Acc, Name]> : Acc;
|
|
55
|
+
/** Validate that all required params are provided. */
|
|
56
|
+
export type ValidateParams<SQL extends string, Params extends Record<string, unknown>> = ExtractParamNames<SQL> extends infer Names extends string[] ? Names[number] extends keyof Params ? true : `Missing parameter: ${Exclude<Names[number], keyof Params>}` : true;
|
|
57
|
+
type Flatten<T> = {
|
|
58
|
+
[K in keyof T]: T[K];
|
|
59
|
+
} & {};
|
|
60
|
+
/**
|
|
61
|
+
* Result type for a conditional SQL query, rewired onto the new core.
|
|
62
|
+
* 1. all conditions TRUE -> full column set (GetReturnType<FullSQL>)
|
|
63
|
+
* 2. all conditions FALSE -> base column set (GetReturnType<BaseSQL>)
|
|
64
|
+
* 3. columns in full but not base -> `| undefined`
|
|
65
|
+
*/
|
|
66
|
+
export type ConditionalQueryResult<Template extends string, Conditions extends Record<string, unknown>, Schema extends DatabaseSchema> = ProcessConditionalSQL<Template, AllConditionsTrue<Conditions>> extends infer FullSQL extends string ? ProcessConditionalSQL<Template, AllConditionsFalse<Conditions>> extends infer BaseSQL extends string ? GetReturnType<FullSQL, Schema> extends infer Full ? GetReturnType<BaseSQL, Schema> extends infer Base ? MergeConditionalResults<Full, Base> : Full : {} : {} : {};
|
|
67
|
+
export type MergeConditionalResults<Full, Base> = Flatten<{
|
|
68
|
+
[K in keyof Full as K extends keyof Base ? K : never]: Full[K];
|
|
69
|
+
} & {
|
|
70
|
+
[K in keyof Full as K extends keyof Base ? never : K]: Full[K] | undefined;
|
|
71
|
+
}>;
|
|
72
|
+
export type ProcessedSQL<Template extends string, Conditions extends Record<string, unknown>> = ProcessConditionalSQL<Template, Conditions>;
|
|
73
|
+
export type ValidateConditionalSQL<Template extends string, Conditions extends Record<string, unknown>, Schema extends DatabaseSchema> = ProcessConditionalSQL<Template, AllConditionsTrue<Conditions>> extends infer FullSQL extends string ? ValidateSQL<FullSQL, Schema> : false;
|
|
74
|
+
export interface TypedConditionalSQLOutput<Result> extends ConditionalSQLOutput {
|
|
75
|
+
readonly __resultType?: Result;
|
|
76
|
+
}
|
|
77
|
+
export declare function createConditionalQuery<Schema extends DatabaseSchema>(): <Template extends string, Conditions extends Record<string, unknown>, Params extends Record<string, QueryParamValue> = {}>(template: Template, conditions: Conditions, params?: Params) => TypedConditionalSQLOutput<ConditionalQueryResult<Template, Conditions, Schema>>;
|
|
78
|
+
export declare function withConditions<StaticConditions extends Record<string, unknown>>(queryFn: ReturnType<typeof createConditionalQuery>): <Template extends string, Params extends Record<string, QueryParamValue> = {}>(template: Template, conditions: StaticConditions, params?: Params) => TypedConditionalSQLOutput<ConditionalQueryResult<Template, StaticConditions, DatabaseSchema>>;
|
|
79
|
+
export {};
|
|
80
|
+
//# sourceMappingURL=conditional-sql.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"conditional-sql.d.ts","sourceRoot":"","sources":["../../src/builder/conditional-sql.ts"],"names":[],"mappings":"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"}
|