@kuindji/typed-sql 0.3.0 → 0.4.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/README.md +11 -3
- package/dist/columns.d.ts +11 -3
- package/dist/columns.d.ts.map +1 -1
- package/dist/expressions.d.ts +73 -8
- package/dist/expressions.d.ts.map +1 -1
- package/dist/parsing/extract.d.ts +13 -9
- package/dist/parsing/extract.d.ts.map +1 -1
- package/dist/parsing/normalize.d.ts +3 -1
- package/dist/parsing/normalize.d.ts.map +1 -1
- package/dist/parsing/pg-literals.d.ts +10 -2
- package/dist/parsing/pg-literals.d.ts.map +1 -1
- package/dist/parsing/split.d.ts +27 -3
- package/dist/parsing/split.d.ts.map +1 -1
- package/dist/parsing/string-utils.d.ts +2 -4
- package/dist/parsing/string-utils.d.ts.map +1 -1
- package/dist/parsing/tokenize.d.ts +27 -17
- package/dist/parsing/tokenize.d.ts.map +1 -1
- package/dist/partial.d.ts +6 -6
- package/dist/partial.d.ts.map +1 -1
- package/dist/tables.d.ts +58 -13
- package/dist/tables.d.ts.map +1 -1
- package/dist/validation/dispatch.d.ts +7 -5
- package/dist/validation/dispatch.d.ts.map +1 -1
- package/dist/validation/joins.d.ts +3 -3
- package/dist/validation/joins.d.ts.map +1 -1
- package/dist/validation/validate-columns.d.ts +14 -14
- package/dist/validation/validate-columns.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/columns.ts +168 -32
- package/src/expressions.ts +550 -57
- package/src/parsing/extract.ts +72 -32
- package/src/parsing/normalize.ts +54 -8
- package/src/parsing/pg-literals.ts +32 -10
- package/src/parsing/split.ts +236 -72
- package/src/parsing/string-utils.ts +15 -15
- package/src/parsing/tokenize.ts +224 -146
- package/src/partial.ts +9 -15
- package/src/tables.ts +546 -214
- package/src/validation/dispatch.ts +58 -52
- package/src/validation/joins.ts +15 -19
- package/src/validation/validate-columns.ts +54 -64
package/src/parsing/extract.ts
CHANGED
|
@@ -36,28 +36,40 @@ export type FirstTopLevelReturningTail<S extends string> =
|
|
|
36
36
|
? FirstTopLevelReturningTailWalk<S>
|
|
37
37
|
: S extends `${string} returning ${infer After}` ? After : "";
|
|
38
38
|
|
|
39
|
+
// Quote-jump twin of `HasReturningQuoteAwareWalk` (validation/dispatch.ts):
|
|
40
|
+
// find the leftmost ` returning `; if no quote opens before it, its tail is the
|
|
41
|
+
// answer. Otherwise jump the leftmost quote span whole (`'…'` or `"…"`,
|
|
42
|
+
// whichever opens first — the other quote kind inside the span is data) and
|
|
43
|
+
// re-test the remainder. O(quote spans) instead of O(chars); an unterminated
|
|
44
|
+
// quote swallows the rest, exactly like the old walk-to-EOF inside a literal.
|
|
39
45
|
type FirstTopLevelReturningTailWalk<
|
|
40
46
|
S extends string,
|
|
41
|
-
InString extends boolean = false,
|
|
42
|
-
InDString extends boolean = false,
|
|
43
47
|
Steps extends any[] = []
|
|
44
48
|
> = string extends S
|
|
45
49
|
? ""
|
|
46
|
-
: Steps["length"] extends
|
|
50
|
+
: Steps["length"] extends 400
|
|
47
51
|
? S extends `${string} returning ${infer After}` ? After : ""
|
|
48
|
-
:
|
|
49
|
-
?
|
|
50
|
-
?
|
|
52
|
+
: S extends `${infer Before} returning ${infer After}`
|
|
53
|
+
? Before extends `${string}'${string}` | `${string}"${string}`
|
|
54
|
+
? FtrtQuoteJump<S, Steps>
|
|
55
|
+
: After
|
|
56
|
+
: "";
|
|
57
|
+
|
|
58
|
+
type FtrtQuoteJump<S extends string, Steps extends any[]> =
|
|
59
|
+
S extends `${infer P}'${infer R}`
|
|
60
|
+
? P extends `${string}"${string}`
|
|
61
|
+
? FtrtDQuoteJump<S, Steps>
|
|
62
|
+
: R extends `${string}'${infer R2}`
|
|
63
|
+
? FirstTopLevelReturningTailWalk<R2, [any, ...Steps]>
|
|
51
64
|
: ""
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
: "";
|
|
65
|
+
: FtrtDQuoteJump<S, Steps>;
|
|
66
|
+
|
|
67
|
+
type FtrtDQuoteJump<S extends string, Steps extends any[]> =
|
|
68
|
+
S extends `${string}"${infer R}`
|
|
69
|
+
? R extends `${string}"${infer R2}`
|
|
70
|
+
? FirstTopLevelReturningTailWalk<R2, [any, ...Steps]>
|
|
71
|
+
: ""
|
|
72
|
+
: "";
|
|
61
73
|
|
|
62
74
|
// Given a string whose first non-skipped char is `(`, consume the first
|
|
63
75
|
// balanced parenthesised group (quote-aware) and return its inner content plus
|
|
@@ -72,6 +84,15 @@ type FirstTopLevelReturningTailWalk<
|
|
|
72
84
|
// every CHUNK steps and a driver that re-invokes it with a fresh step counter —
|
|
73
85
|
// resetting TS's per-chain tail-recursion count, so arbitrarily long groups split
|
|
74
86
|
// losslessly. Mirrors the `SplitTopLevel` worker/driver above.
|
|
87
|
+
//
|
|
88
|
+
// Struct-jump, not per-char (the old walk minted one growing-`Acc` string PER
|
|
89
|
+
// CHARACTER across entire CTE/subquery bodies). Each step advances to the
|
|
90
|
+
// LEFTMOST of the three state chars `'` `(` `)` (double quotes were never
|
|
91
|
+
// tracked here — preserved), copying the whole run before it in a single mint;
|
|
92
|
+
// inside a `'…'` literal it jumps straight to the closing quote (`''` escapes
|
|
93
|
+
// exit+re-enter across two jumps; an unterminated quote at EOF copies the rest
|
|
94
|
+
// verbatim). The `Steps` cap counts JUMPS and yields `{ __c: [...] }` to the
|
|
95
|
+
// driver, so arbitrarily paren-dense inputs still complete losslessly.
|
|
75
96
|
type SplitBalancedParenWorker<
|
|
76
97
|
S extends string,
|
|
77
98
|
Depth extends any[] = [],
|
|
@@ -80,23 +101,42 @@ type SplitBalancedParenWorker<
|
|
|
80
101
|
Steps extends any[] = []
|
|
81
102
|
> = Steps["length"] extends 350
|
|
82
103
|
? { __c: [S, Depth, Acc, InString] }
|
|
83
|
-
:
|
|
84
|
-
?
|
|
85
|
-
? SplitBalancedParenWorker<
|
|
86
|
-
:
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
104
|
+
: InString extends true
|
|
105
|
+
? S extends `${infer P}'${infer R}`
|
|
106
|
+
? SplitBalancedParenWorker<R, Depth, `${Acc}${P}'`, false, [any, ...Steps]>
|
|
107
|
+
: { inner: `${Acc}${S}`; rest: "" }
|
|
108
|
+
: S extends `${infer P}'${infer R}`
|
|
109
|
+
? P extends `${string}(${string}` | `${string})${string}`
|
|
110
|
+
? SbpParenJump<S, Depth, Acc, Steps>
|
|
111
|
+
: SplitBalancedParenWorker<R, Depth, `${Acc}${P}'`, true, [any, ...Steps]>
|
|
112
|
+
: SbpParenJump<S, Depth, Acc, Steps>;
|
|
113
|
+
|
|
114
|
+
// Leftmost of `(` / `)` (the caller guarantees no `'` occurs before either).
|
|
115
|
+
type SbpParenJump<
|
|
116
|
+
S extends string,
|
|
117
|
+
Depth extends any[],
|
|
118
|
+
Acc extends string,
|
|
119
|
+
Steps extends any[]
|
|
120
|
+
> = S extends `${infer P}(${infer R}`
|
|
121
|
+
? P extends `${string})${string}`
|
|
122
|
+
? SbpCloseJump<S, Depth, Acc, Steps>
|
|
123
|
+
: Depth["length"] extends 0
|
|
124
|
+
// the group-opening `(` itself is consumed, not copied into `inner`
|
|
125
|
+
? SplitBalancedParenWorker<R, [any], `${Acc}${P}`, false, [any, ...Steps]>
|
|
126
|
+
: SplitBalancedParenWorker<R, [any, ...Depth], `${Acc}${P}(`, false, [any, ...Steps]>
|
|
127
|
+
: SbpCloseJump<S, Depth, Acc, Steps>;
|
|
128
|
+
|
|
129
|
+
type SbpCloseJump<
|
|
130
|
+
S extends string,
|
|
131
|
+
Depth extends any[],
|
|
132
|
+
Acc extends string,
|
|
133
|
+
Steps extends any[]
|
|
134
|
+
> = S extends `${infer P})${infer R}`
|
|
135
|
+
? Depth extends [any, any, ...infer D extends any[]]
|
|
136
|
+
? SplitBalancedParenWorker<R, [any, ...D], `${Acc}${P})`, false, [any, ...Steps]>
|
|
137
|
+
// depth ≤ 1: this `)` closes the group (or is an unmatched top-level `)`)
|
|
138
|
+
: { inner: `${Acc}${P}`; rest: R }
|
|
139
|
+
: { inner: `${Acc}${S}`; rest: "" };
|
|
100
140
|
|
|
101
141
|
export type SplitBalancedParen<S extends string> =
|
|
102
142
|
SplitBalancedParenDrive<SplitBalancedParenWorker<S>>;
|
package/src/parsing/normalize.ts
CHANGED
|
@@ -295,18 +295,64 @@ export type ReplaceWhitespace<S extends string> =
|
|
|
295
295
|
? ReplaceWhitespaceRuns<S>
|
|
296
296
|
: S;
|
|
297
297
|
|
|
298
|
-
|
|
298
|
+
// Acc-carrying worker: the finished prefix moves into `Acc` and is NEVER
|
|
299
|
+
// rescanned or reminted (the old form rebuilt the FULL string once per line
|
|
300
|
+
// break and re-matched it from the start). Because consumed segments leave the
|
|
301
|
+
// scan, the worker must take the LEFTMOST of `\n`/`\t`/`\r` each step — the
|
|
302
|
+
// old rebuild-and-rescan form got that for free by always re-matching `\n`
|
|
303
|
+
// first over the whole string. Same pairwise-narrowing cascade as
|
|
304
|
+
// `MtcStructJump`. The step cap counts whitespace RUNS (each consumed whole by
|
|
305
|
+
// `ConsumeWsRun`), so it covers at least as much input as the old
|
|
306
|
+
// per-occurrence cap; on cap the remainder is left as-is, as before.
|
|
307
|
+
type ReplaceWhitespaceRuns<S extends string, Acc extends string = "", Steps extends any[] = []> =
|
|
299
308
|
string extends S
|
|
300
309
|
? S
|
|
301
310
|
: Steps["length"] extends 1500
|
|
302
|
-
? S
|
|
311
|
+
? `${Acc}${S}`
|
|
303
312
|
: S extends `${infer A}\n${infer B}`
|
|
304
|
-
?
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
313
|
+
? A extends `${string}\t${string}` | `${string}\r${string}`
|
|
314
|
+
? RwrTabCr<S, Acc, Steps>
|
|
315
|
+
: ReplaceWhitespaceRuns<ConsumeWsRun<B>, `${Acc}${A} `, [any, ...Steps]>
|
|
316
|
+
: RwrTabCr<S, Acc, Steps>;
|
|
317
|
+
|
|
318
|
+
// Leftmost of `\t` / `\r` (caller ruled out an earlier `\n`).
|
|
319
|
+
type RwrTabCr<S extends string, Acc extends string, Steps extends any[]> =
|
|
320
|
+
S extends `${infer A}\t${infer B}`
|
|
321
|
+
? A extends `${string}\r${string}`
|
|
322
|
+
? S extends `${infer A2}\r${infer B2}`
|
|
323
|
+
? ReplaceWhitespaceRuns<ConsumeWsRun<B2>, `${Acc}${A2} `, [any, ...Steps]>
|
|
324
|
+
: `${Acc}${S}`
|
|
325
|
+
: ReplaceWhitespaceRuns<ConsumeWsRun<B>, `${Acc}${A} `, [any, ...Steps]>
|
|
326
|
+
: S extends `${infer A2}\r${infer B2}`
|
|
327
|
+
? ReplaceWhitespaceRuns<ConsumeWsRun<B2>, `${Acc}${A2} `, [any, ...Steps]>
|
|
328
|
+
: `${Acc}${S}`;
|
|
329
|
+
|
|
330
|
+
// Eat the whole whitespace run FOLLOWING a consumed line break before the full
|
|
331
|
+
// string is rebuilt. A formatted query's `\n␣␣␣␣` indentation otherwise survives
|
|
332
|
+
// as a multi-space run that costs `ReplaceWhitespaceRuns` extra full-string
|
|
333
|
+
// remints (one per `\n` of a blank line) plus one more full remint per run in
|
|
334
|
+
// `CollapseSpaces`. Peeling here works on the TAIL only — far cheaper mints —
|
|
335
|
+
// and leaves `CollapseSpaces` a no-op for these runs. Equivalence: both forms
|
|
336
|
+
// reduce every whitespace run that touches a line break to a single space, and
|
|
337
|
+
// runs NOT touching a line break are still collapsed by the unchanged
|
|
338
|
+
// `CollapseSpaces` pass. A capped-out leftover (`\t`/`\n`/`\r` beyond the
|
|
339
|
+
// budget) is still caught by the outer loop's own branches.
|
|
340
|
+
type ConsumeWsRun<S extends string, Steps extends any[] = []> =
|
|
341
|
+
Steps["length"] extends 64
|
|
342
|
+
? S
|
|
343
|
+
: S extends ` ${infer R}` // 16 spaces
|
|
344
|
+
? ConsumeWsRun<R, [any, ...Steps]>
|
|
345
|
+
: S extends ` ${infer R}` // 4 spaces
|
|
346
|
+
? ConsumeWsRun<R, [any, ...Steps]>
|
|
347
|
+
: S extends ` ${infer R}`
|
|
348
|
+
? ConsumeWsRun<R, [any, ...Steps]>
|
|
349
|
+
: S extends `\t${infer R}`
|
|
350
|
+
? ConsumeWsRun<R, [any, ...Steps]>
|
|
351
|
+
: S extends `\n${infer R}`
|
|
352
|
+
? ConsumeWsRun<R, [any, ...Steps]>
|
|
353
|
+
: S extends `\r${infer R}`
|
|
354
|
+
? ConsumeWsRun<R, [any, ...Steps]>
|
|
355
|
+
: S;
|
|
310
356
|
|
|
311
357
|
// Cheap "is this string longer than ~500 chars" check: drop 10 chars per step
|
|
312
358
|
// for up to 50 steps. If content survives all 50 drops the string exceeds the
|
|
@@ -170,16 +170,38 @@ export type RewriteExtractCall<S extends string> =
|
|
|
170
170
|
: RewriteExtractWalk<S>
|
|
171
171
|
: S;
|
|
172
172
|
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
173
|
+
// Tail-recursive accumulator walk + chunked driver. Template matching is
|
|
174
|
+
// LEFTMOST, so `Pre` can never contain another ` extract(` — it is appended to
|
|
175
|
+
// `Acc` verbatim and only `Rest` is recursed. (The previous version recursed
|
|
176
|
+
// into BOTH `Pre` and `Rest` building a nested template, so its step cap had to
|
|
177
|
+
// stay tiny — 24 — and a 50-projection report query with 25+ EXTRACTs bailed
|
|
178
|
+
// half-rewritten: the surviving inner ` from ` tokens then fed the tables
|
|
179
|
+
// collector bogus sources like `min(ua.col`, flipping ValidateSQL to a false
|
|
180
|
+
// rejection.) The driver re-invokes the worker with a fresh step counter every
|
|
181
|
+
// 64 rewrites, so any realistic number of EXTRACTs completes losslessly.
|
|
182
|
+
export type RewriteExtractWalk<S extends string> =
|
|
183
|
+
RewExDrive<RewExWorker<S>>;
|
|
184
|
+
|
|
185
|
+
type RewExDrive<R> =
|
|
186
|
+
[R] extends [never]
|
|
187
|
+
? never
|
|
188
|
+
: R extends { __c: [infer S extends string, infer Acc extends string] }
|
|
189
|
+
? RewExDrive<RewExWorker<S, Acc, []>>
|
|
190
|
+
: R;
|
|
191
|
+
|
|
192
|
+
type RewExWorker<
|
|
193
|
+
S extends string,
|
|
194
|
+
Acc extends string = "",
|
|
195
|
+
Steps extends any[] = []
|
|
196
|
+
> = Steps["length"] extends 64
|
|
197
|
+
? { __c: [S, Acc] }
|
|
198
|
+
: S extends `${infer Pre} extract(${infer AfterOpen}`
|
|
199
|
+
? SplitBalancedParen<`(${AfterOpen}`> extends { inner: infer Inner extends string; rest: infer Rest extends string }
|
|
200
|
+
? Inner extends `${infer _Field} from ${infer Source}`
|
|
201
|
+
? RewExWorker<Rest, `${Acc}${Pre} extract(${Trim<Source>})`, [any, ...Steps]>
|
|
202
|
+
: RewExWorker<Rest, `${Acc}${Pre} extract(${Inner})`, [any, ...Steps]>
|
|
203
|
+
: `${Acc}${S}`
|
|
204
|
+
: `${Acc}${S}`;
|
|
183
205
|
|
|
184
206
|
// As `RewriteExtractWalk`, but an ` extract(` whose prefix has an odd number of
|
|
185
207
|
// single quotes sits INSIDE a string literal and is left verbatim (the literal's
|
package/src/parsing/split.ts
CHANGED
|
@@ -13,6 +13,15 @@ import type { SqlReserved } from "./tokenize.js";
|
|
|
13
13
|
// `{ __c: [...] }` marker. `SplitTopLevel` (the driver below) re-invokes the
|
|
14
14
|
// worker on the remainder with a fresh step counter, which resets TS's internal
|
|
15
15
|
// tail-recursion count per chunk — so arbitrarily long lists split losslessly.
|
|
16
|
+
// Segment-jump, not per-char (the old walk minted one growing-`Cur` string PER
|
|
17
|
+
// CHARACTER and the tail at every step). Each step advances to the LEFTMOST of
|
|
18
|
+
// the five state chars `,` `'` `"` `(` `)`, copying the whole run before it into
|
|
19
|
+
// `Cur` in a single mint; inside a quote it jumps straight to the closing quote
|
|
20
|
+
// (the other quote kind inside a span is data, preserving the old InQ/InDQ
|
|
21
|
+
// suppression; `''` escapes exit+re-enter across two jumps; an unterminated
|
|
22
|
+
// quote at EOF copies the rest verbatim). The `Steps` cap counts JUMPS and
|
|
23
|
+
// yields `{ __c: [...] }` to the driver as before. Mirrors `MtcStructJump`
|
|
24
|
+
// (tokenize.ts) and `SbpParenJump` (extract.ts).
|
|
16
25
|
type SplitTopLevelWorker<
|
|
17
26
|
S extends string,
|
|
18
27
|
Depth extends any[] = [],
|
|
@@ -21,36 +30,89 @@ type SplitTopLevelWorker<
|
|
|
21
30
|
Steps extends any[] = [],
|
|
22
31
|
InQ extends boolean = false,
|
|
23
32
|
InDQ extends boolean = false
|
|
24
|
-
|
|
33
|
+
// CHUNK = 120 JUMPS, not chars: since the round-7 struct-jump rewrite each
|
|
34
|
+
// jump costs ~4 conditional evaluations through the StlStructJump* helper
|
|
35
|
+
// chain, so the old 450-jump chunk could burn >1000 tail counts inside ONE
|
|
36
|
+
// chunk (TS2589) on a 50-projection select list. 120 jumps ≈ 500 tail counts.
|
|
37
|
+
> = Steps["length"] extends 120
|
|
25
38
|
? { __c: [S, Depth, Acc, Cur, InQ, InDQ] }
|
|
26
39
|
: string extends CleanIdent<S>
|
|
27
40
|
? [...Acc, `${Cur}string`]
|
|
28
|
-
:
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
// Suppressed while inside a double-quoted identifier.
|
|
33
|
-
? InDQ extends true
|
|
34
|
-
? SplitTopLevelWorker<Rest, Depth, Acc, `${Cur}${C}`, [any, ...Steps], InQ, InDQ>
|
|
35
|
-
: SplitTopLevelWorker<Rest, Depth, Acc, `${Cur}${C}`, [any, ...Steps], InQ extends true ? false : true, InDQ>
|
|
36
|
-
: InQ extends true
|
|
37
|
-
? SplitTopLevelWorker<Rest, Depth, Acc, `${Cur}${C}`, [any, ...Steps], InQ, InDQ>
|
|
38
|
-
: C extends `"`
|
|
39
|
-
// A double quote toggles "inside quoted identifier": commas and
|
|
40
|
-
// parens inside `"..."` are part of the identifier, kept verbatim.
|
|
41
|
-
? SplitTopLevelWorker<Rest, Depth, Acc, `${Cur}${C}`, [any, ...Steps], InQ, InDQ extends true ? false : true>
|
|
41
|
+
: InQ extends true
|
|
42
|
+
? S extends `${infer P}'${infer R}`
|
|
43
|
+
? SplitTopLevelWorker<R, Depth, Acc, `${Cur}${P}'`, [any, ...Steps], false, InDQ>
|
|
44
|
+
: [...Acc, `${Cur}${S}`]
|
|
42
45
|
: InDQ extends true
|
|
43
|
-
?
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
:
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
46
|
+
? S extends `${infer P}"${infer R}`
|
|
47
|
+
? SplitTopLevelWorker<R, Depth, Acc, `${Cur}${P}"`, [any, ...Steps], InQ, false>
|
|
48
|
+
: [...Acc, `${Cur}${S}`]
|
|
49
|
+
: S extends `${infer P},${infer R}`
|
|
50
|
+
// a structural char in the run before the first comma → it is
|
|
51
|
+
// leftmost; defer to the struct jump
|
|
52
|
+
? StlHasStruct<P> extends true
|
|
53
|
+
? StlStructJump<S, Depth, Acc, Cur, Steps>
|
|
54
|
+
: Depth["length"] extends 0
|
|
55
|
+
? SplitTopLevelWorker<R, Depth, [...Acc, `${Cur}${P}`], "", [any, ...Steps], false, false>
|
|
56
|
+
: SplitTopLevelWorker<R, Depth, Acc, `${Cur}${P},`, [any, ...Steps], false, false>
|
|
57
|
+
: StlHasStruct<S> extends true
|
|
58
|
+
? StlStructJump<S, Depth, Acc, Cur, Steps>
|
|
59
|
+
: [...Acc, `${Cur}${S}`];
|
|
60
|
+
|
|
61
|
+
type StlHasStruct<S extends string> =
|
|
62
|
+
S extends `${string}'${string}` ? true
|
|
63
|
+
: S extends `${string}"${string}` ? true
|
|
64
|
+
: S extends `${string}(${string}` ? true
|
|
65
|
+
: S extends `${string})${string}` ? true
|
|
66
|
+
: false;
|
|
67
|
+
|
|
68
|
+
// Leftmost of `'` / `"` / `(` / `)` (the caller guarantees at least one occurs
|
|
69
|
+
// before any comma). Pairwise narrowing: split on a candidate; if an
|
|
70
|
+
// earlier-class char appears in its prefix, that one is leftmost instead.
|
|
71
|
+
type StlStructJump<
|
|
72
|
+
S extends string,
|
|
73
|
+
Depth extends any[],
|
|
74
|
+
Acc extends string[],
|
|
75
|
+
Cur extends string,
|
|
76
|
+
Steps extends any[]
|
|
77
|
+
> = S extends `${infer P}'${infer R}`
|
|
78
|
+
? P extends `${string}"${string}` | `${string}(${string}` | `${string})${string}`
|
|
79
|
+
? StlStructJump2<S, Depth, Acc, Cur, Steps>
|
|
80
|
+
: SplitTopLevelWorker<R, Depth, Acc, `${Cur}${P}'`, [any, ...Steps], true, false>
|
|
81
|
+
: StlStructJump2<S, Depth, Acc, Cur, Steps>;
|
|
82
|
+
|
|
83
|
+
type StlStructJump2<
|
|
84
|
+
S extends string,
|
|
85
|
+
Depth extends any[],
|
|
86
|
+
Acc extends string[],
|
|
87
|
+
Cur extends string,
|
|
88
|
+
Steps extends any[]
|
|
89
|
+
> = S extends `${infer P}"${infer R}`
|
|
90
|
+
? P extends `${string}(${string}` | `${string})${string}`
|
|
91
|
+
? StlStructJump3<S, Depth, Acc, Cur, Steps>
|
|
92
|
+
: SplitTopLevelWorker<R, Depth, Acc, `${Cur}${P}"`, [any, ...Steps], false, true>
|
|
93
|
+
: StlStructJump3<S, Depth, Acc, Cur, Steps>;
|
|
94
|
+
|
|
95
|
+
type StlStructJump3<
|
|
96
|
+
S extends string,
|
|
97
|
+
Depth extends any[],
|
|
98
|
+
Acc extends string[],
|
|
99
|
+
Cur extends string,
|
|
100
|
+
Steps extends any[]
|
|
101
|
+
> = S extends `${infer P}(${infer R}`
|
|
102
|
+
? P extends `${string})${string}`
|
|
103
|
+
? StlCloseJump<S, Depth, Acc, Cur, Steps>
|
|
104
|
+
: SplitTopLevelWorker<R, [any, ...Depth], Acc, `${Cur}${P}(`, [any, ...Steps], false, false>
|
|
105
|
+
: StlCloseJump<S, Depth, Acc, Cur, Steps>;
|
|
106
|
+
|
|
107
|
+
type StlCloseJump<
|
|
108
|
+
S extends string,
|
|
109
|
+
Depth extends any[],
|
|
110
|
+
Acc extends string[],
|
|
111
|
+
Cur extends string,
|
|
112
|
+
Steps extends any[]
|
|
113
|
+
> = S extends `${infer P})${infer R}`
|
|
114
|
+
? SplitTopLevelWorker<R, Depth extends [any, ...infer D] ? D : [], Acc, `${Cur}${P})`, [any, ...Steps], false, false>
|
|
115
|
+
: [...Acc, `${Cur}${S}`];
|
|
54
116
|
|
|
55
117
|
// Driver: run the worker chunk-by-chunk until it returns the finished `string[]`.
|
|
56
118
|
// Each `{ __c: state }` yield is fed back with a fresh step counter, so no single
|
|
@@ -70,55 +132,157 @@ type SplitTopLevelDrive<R> =
|
|
|
70
132
|
// Quote-aware on BOTH single quotes (`'...'` string literals) and double quotes
|
|
71
133
|
// (`"..."` quoted identifiers): a ` from ` token sitting inside a double-quoted
|
|
72
134
|
// output alias (`id as "came from import"`) is part of the identifier, not the
|
|
73
|
-
// SELECT/FROM boundary, so it must not split the list. `
|
|
74
|
-
//
|
|
75
|
-
//
|
|
76
|
-
|
|
135
|
+
// SELECT/FROM boundary, so it must not split the list. Parens and the ` from `
|
|
136
|
+
// boundary are only honoured when outside BOTH kinds of quote.
|
|
137
|
+
// Struct-jump, not per-char (the old walk minted a growing `Acc` PER CHARACTER
|
|
138
|
+
// over every subquery select-list). At depth 0 each step advances to the
|
|
139
|
+
// LEFTMOST of `'` `"` `(` `)` ` from ` (pairwise narrowing, multi-char keyword
|
|
140
|
+
// checked last); at depth > 0 the ` from ` candidate is dropped (a nested FROM
|
|
141
|
+
// is not the boundary). Quote spans are jumped quote-to-quote (the other quote
|
|
142
|
+
// kind inside a span is data, preserving the old InString/InDString
|
|
143
|
+
// suppression; an unterminated quote swallows the rest, as the old
|
|
144
|
+
// walk-to-EOF did).
|
|
145
|
+
//
|
|
146
|
+
// CHUNKED worker/driver (mirrors `SplitTopLevel`): each jump costs ~4-5
|
|
147
|
+
// conditional evaluations through the EbftJump* helper chain, so a single
|
|
148
|
+
// recursion chain crosses TS's 1000 tail-count budget at ~200+ jumps — a
|
|
149
|
+
// 50-projection SELECT list of quoted/parenthesised expressions gets there
|
|
150
|
+
// (TS2589). The worker yields its state every 120 jumps and the driver
|
|
151
|
+
// re-invokes it with a fresh step counter, so arbitrarily long select lists
|
|
152
|
+
// complete losslessly. (The pre-chunking version bailed to a lenient
|
|
153
|
+
// `ExtractBefore<S, " from ">` at a 350-jump cap — and blew TS2589 before
|
|
154
|
+
// ever reaching it on exactly the queries the cap was meant to protect.)
|
|
155
|
+
export type ExtractBeforeFromTopLevel<S extends string> =
|
|
156
|
+
EbftDrive<EbftWorker<S>>;
|
|
157
|
+
|
|
158
|
+
type EbftDrive<R> =
|
|
159
|
+
[R] extends [never]
|
|
160
|
+
? never
|
|
161
|
+
: R extends { __c: [infer S extends string, infer Depth extends any[], infer Acc extends string] }
|
|
162
|
+
? EbftDrive<EbftWorker<S, Depth, Acc, []>>
|
|
163
|
+
: R;
|
|
164
|
+
|
|
165
|
+
type EbftWorker<
|
|
77
166
|
S extends string,
|
|
78
167
|
Depth extends any[] = [],
|
|
79
|
-
InString extends boolean = false,
|
|
80
168
|
Acc extends string = "",
|
|
81
|
-
Steps extends any[] = []
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
169
|
+
Steps extends any[] = []
|
|
170
|
+
> = Steps["length"] extends 120
|
|
171
|
+
? { __c: [S, Depth, Acc] }
|
|
172
|
+
: Depth["length"] extends 0
|
|
173
|
+
? EbftJumpTop<S, Depth, Acc, Steps>
|
|
174
|
+
: EbftJumpNested<S, Depth, Acc, Steps>;
|
|
175
|
+
|
|
176
|
+
// inside `'…'`: resume after the closing quote (any depth; quotes ignore depth)
|
|
177
|
+
type EbftQuoteClose<
|
|
178
|
+
R extends string,
|
|
179
|
+
Depth extends any[],
|
|
180
|
+
Acc extends string,
|
|
181
|
+
Steps extends any[]
|
|
182
|
+
> = R extends `${infer Span}'${infer R2}`
|
|
183
|
+
? EbftWorker<R2, Depth, `${Acc}${Span}'`, [any, ...Steps]>
|
|
184
|
+
: `${Acc}${R}`;
|
|
185
|
+
|
|
186
|
+
type EbftDQuoteClose<
|
|
187
|
+
R extends string,
|
|
188
|
+
Depth extends any[],
|
|
189
|
+
Acc extends string,
|
|
190
|
+
Steps extends any[]
|
|
191
|
+
> = R extends `${infer Span}"${infer R2}`
|
|
192
|
+
? EbftWorker<R2, Depth, `${Acc}${Span}"`, [any, ...Steps]>
|
|
193
|
+
: `${Acc}${R}`;
|
|
194
|
+
|
|
195
|
+
type EbftJumpTop<
|
|
196
|
+
S extends string,
|
|
197
|
+
Depth extends any[],
|
|
198
|
+
Acc extends string,
|
|
199
|
+
Steps extends any[]
|
|
200
|
+
> = S extends `${infer P}'${infer R}`
|
|
201
|
+
? P extends `${string}"${string}` | `${string}(${string}` | `${string})${string}` | `${string} from ${string}`
|
|
202
|
+
? EbftJumpTop2<S, Depth, Acc, Steps>
|
|
203
|
+
: EbftQuoteClose<R, Depth, `${Acc}${P}'`, Steps>
|
|
204
|
+
: EbftJumpTop2<S, Depth, Acc, Steps>;
|
|
205
|
+
|
|
206
|
+
type EbftJumpTop2<
|
|
207
|
+
S extends string,
|
|
208
|
+
Depth extends any[],
|
|
209
|
+
Acc extends string,
|
|
210
|
+
Steps extends any[]
|
|
211
|
+
> = S extends `${infer P}"${infer R}`
|
|
212
|
+
? P extends `${string}(${string}` | `${string})${string}` | `${string} from ${string}`
|
|
213
|
+
? EbftJumpTop3<S, Depth, Acc, Steps>
|
|
214
|
+
: EbftDQuoteClose<R, Depth, `${Acc}${P}"`, Steps>
|
|
215
|
+
: EbftJumpTop3<S, Depth, Acc, Steps>;
|
|
216
|
+
|
|
217
|
+
type EbftJumpTop3<
|
|
218
|
+
S extends string,
|
|
219
|
+
Depth extends any[],
|
|
220
|
+
Acc extends string,
|
|
221
|
+
Steps extends any[]
|
|
222
|
+
> = S extends `${infer P}(${infer R}`
|
|
223
|
+
? P extends `${string})${string}` | `${string} from ${string}`
|
|
224
|
+
? EbftJumpTop4<S, Depth, Acc, Steps>
|
|
225
|
+
: EbftWorker<R, [any, ...Depth], `${Acc}${P}(`, [any, ...Steps]>
|
|
226
|
+
: EbftJumpTop4<S, Depth, Acc, Steps>;
|
|
227
|
+
|
|
228
|
+
type EbftJumpTop4<
|
|
229
|
+
S extends string,
|
|
230
|
+
Depth extends any[],
|
|
231
|
+
Acc extends string,
|
|
232
|
+
Steps extends any[]
|
|
233
|
+
> = S extends `${infer P})${infer R}`
|
|
234
|
+
? P extends `${string} from ${string}`
|
|
235
|
+
? EbftJumpTop5<S, Acc>
|
|
236
|
+
// an unmatched `)` at depth 0 stays at depth 0 (pop of empty = empty)
|
|
237
|
+
: EbftWorker<R, [], `${Acc}${P})`, [any, ...Steps]>
|
|
238
|
+
: EbftJumpTop5<S, Acc>;
|
|
239
|
+
|
|
240
|
+
type EbftJumpTop5<S extends string, Acc extends string> =
|
|
241
|
+
S extends `${infer P} from ${string}`
|
|
242
|
+
? `${Acc}${P}`
|
|
243
|
+
: `${Acc}${S}`;
|
|
244
|
+
|
|
245
|
+
type EbftJumpNested<
|
|
246
|
+
S extends string,
|
|
247
|
+
Depth extends any[],
|
|
248
|
+
Acc extends string,
|
|
249
|
+
Steps extends any[]
|
|
250
|
+
> = S extends `${infer P}'${infer R}`
|
|
251
|
+
? P extends `${string}"${string}` | `${string}(${string}` | `${string})${string}`
|
|
252
|
+
? EbftJumpNested2<S, Depth, Acc, Steps>
|
|
253
|
+
: EbftQuoteClose<R, Depth, `${Acc}${P}'`, Steps>
|
|
254
|
+
: EbftJumpNested2<S, Depth, Acc, Steps>;
|
|
255
|
+
|
|
256
|
+
type EbftJumpNested2<
|
|
257
|
+
S extends string,
|
|
258
|
+
Depth extends any[],
|
|
259
|
+
Acc extends string,
|
|
260
|
+
Steps extends any[]
|
|
261
|
+
> = S extends `${infer P}"${infer R}`
|
|
262
|
+
? P extends `${string}(${string}` | `${string})${string}`
|
|
263
|
+
? EbftJumpNested3<S, Depth, Acc, Steps>
|
|
264
|
+
: EbftDQuoteClose<R, Depth, `${Acc}${P}"`, Steps>
|
|
265
|
+
: EbftJumpNested3<S, Depth, Acc, Steps>;
|
|
266
|
+
|
|
267
|
+
type EbftJumpNested3<
|
|
268
|
+
S extends string,
|
|
269
|
+
Depth extends any[],
|
|
270
|
+
Acc extends string,
|
|
271
|
+
Steps extends any[]
|
|
272
|
+
> = S extends `${infer P}(${infer R}`
|
|
273
|
+
? P extends `${string})${string}`
|
|
274
|
+
? EbftJumpNested4<S, Depth, Acc, Steps>
|
|
275
|
+
: EbftWorker<R, [any, ...Depth], `${Acc}${P}(`, [any, ...Steps]>
|
|
276
|
+
: EbftJumpNested4<S, Depth, Acc, Steps>;
|
|
277
|
+
|
|
278
|
+
type EbftJumpNested4<
|
|
279
|
+
S extends string,
|
|
280
|
+
Depth extends any[],
|
|
281
|
+
Acc extends string,
|
|
282
|
+
Steps extends any[]
|
|
283
|
+
> = S extends `${infer P})${infer R}`
|
|
284
|
+
? EbftWorker<R, Depth extends [any, ...infer D] ? D : [], `${Acc}${P})`, [any, ...Steps]>
|
|
285
|
+
: `${Acc}${S}`;
|
|
122
286
|
|
|
123
287
|
// Simple comma split (no paren awareness)
|
|
124
288
|
|