@kuindji/typed-sql 0.2.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.
Files changed (48) hide show
  1. package/README.md +11 -3
  2. package/dist/columns.d.ts +11 -3
  3. package/dist/columns.d.ts.map +1 -1
  4. package/dist/expressions.d.ts +84 -13
  5. package/dist/expressions.d.ts.map +1 -1
  6. package/dist/parsing/extract.d.ts +13 -9
  7. package/dist/parsing/extract.d.ts.map +1 -1
  8. package/dist/parsing/normalize.d.ts +9 -3
  9. package/dist/parsing/normalize.d.ts.map +1 -1
  10. package/dist/parsing/pg-literals.d.ts +10 -2
  11. package/dist/parsing/pg-literals.d.ts.map +1 -1
  12. package/dist/parsing/split.d.ts +27 -3
  13. package/dist/parsing/split.d.ts.map +1 -1
  14. package/dist/parsing/string-utils.d.ts +2 -4
  15. package/dist/parsing/string-utils.d.ts.map +1 -1
  16. package/dist/parsing/tokenize.d.ts +27 -17
  17. package/dist/parsing/tokenize.d.ts.map +1 -1
  18. package/dist/partial.d.ts +6 -6
  19. package/dist/partial.d.ts.map +1 -1
  20. package/dist/tables.d.ts +58 -13
  21. package/dist/tables.d.ts.map +1 -1
  22. package/dist/validation/dispatch.d.ts +7 -5
  23. package/dist/validation/dispatch.d.ts.map +1 -1
  24. package/dist/validation/joins.d.ts +3 -3
  25. package/dist/validation/joins.d.ts.map +1 -1
  26. package/dist/validation/return-derived.d.ts +8 -4
  27. package/dist/validation/return-derived.d.ts.map +1 -1
  28. package/dist/validation/return-derived.js.map +1 -1
  29. package/dist/validation/return-types.d.ts +1 -1
  30. package/dist/validation/return-types.d.ts.map +1 -1
  31. package/dist/validation/validate-columns.d.ts +27 -16
  32. package/dist/validation/validate-columns.d.ts.map +1 -1
  33. package/package.json +1 -1
  34. package/src/columns.ts +168 -32
  35. package/src/expressions.ts +589 -63
  36. package/src/parsing/extract.ts +72 -32
  37. package/src/parsing/normalize.ts +114 -10
  38. package/src/parsing/pg-literals.ts +32 -10
  39. package/src/parsing/split.ts +236 -72
  40. package/src/parsing/string-utils.ts +15 -15
  41. package/src/parsing/tokenize.ts +224 -146
  42. package/src/partial.ts +9 -15
  43. package/src/tables.ts +546 -183
  44. package/src/validation/dispatch.ts +58 -52
  45. package/src/validation/joins.ts +15 -19
  46. package/src/validation/return-derived.ts +60 -4
  47. package/src/validation/return-types.ts +9 -3
  48. package/src/validation/validate-columns.ts +161 -67
@@ -1,12 +1,12 @@
1
1
  // Table/column existence + qualified/unqualified ref + scope-shape validation.
2
2
  import type { AliasesInQuery, InsertTargetTable, TableKeyValid, TablesInQuery, UpdateTargetTable } from "../tables.js";
3
3
  import type { AllTrue, And, StartsWith } from "../utils.js";
4
- import type { CleanIdent, DQuoteSpaceSentinel, ExceedsLengthBudget, ExtractAliasResult, ExtractBefore, ExtractConflictColumns, ExtractConflictUpdateExcludedCols, ExtractConflictUpdateSetColumns, ExtractInsertColumns, ExtractLastWhere, ExtractReturningList, ExtractSelectList, ExtractUpdateSetColumns, ReplaceAll, SplitSelectList, StripSubqueries, TokenizeLoose, Trim } from "../parsing.js";
5
- import type { ColumnRefValidLooseWith, IsSimpleRefPart, QualifiedColumnRefs, ResolveAlias, TableKeysByName, UnqualifiedColumnRefs, UnqualifiedColumnValid } from "../columns.js";
4
+ import type { CleanIdent, DQuoteSpaceSentinel, ExceedsLengthBudget, ExtractAliasResult, ExtractBefore, ExtractConflictColumns, ExtractConflictUpdateExcludedCols, ExtractConflictUpdateSetColumns, ExtractInsertColumns, ExtractLastWhere, ExtractReturningList, ExtractSelectList, ExtractUpdateSetColumns, ReplaceAll, SplitSelectList, StripSubqueries, Trim } from "../parsing.js";
5
+ import type { ColumnRefValidLooseWith, IsSimpleRefPart, QualifiedRefScan, ResolveAlias, TableKeysByName, UnqualifiedRefScan, UnqualifiedColumnValid } from "../columns.js";
6
6
  import type { ColumnsExistInTable, RefScanBeforeOrderBy, RefScanOrderBy, RefScanSegment, SelectAliasesInQuery, SelectAliasSet } from "./return-types.js";
7
- import type { CteRow, SingleCteMatch } from "./cte.js";
7
+ import type { CteNames, CteRow, SingleCteMatch } from "./cte.js";
8
8
  import type { DatabaseSchema } from "../schema.js";
9
- import type { DerivedRenamedRow, DerivedTableMatch } from "./return-derived.js";
9
+ import type { AliasHasNoSpace, DerivedRenamedRow, DerivedTableMatch } from "./return-derived.js";
10
10
  import type { ExprsValidList } from "../expressions.js";
11
11
  import type { HasReturning, QueryKind, ValidateSQLNormalized } from "./dispatch.js";
12
12
 
@@ -105,7 +105,7 @@ export type KeyInRow<K extends string, Row> = [K] extends [keyof Row] ? true : f
105
105
 
106
106
  // Validate every column-reference candidate in an arbitrary text segment (an
107
107
  // outer WHERE predicate, or a function call's argument list) against the
108
- // CTE/derived relation's exposed `Row`. Reuses the same token walkers the core
108
+ // CTE/derived relation's exposed `Row`. Reuses the same ref-scan walkers the core
109
109
  // validator uses to surface refs, then checks each via `ProjRefInRow` (qualifier
110
110
  // must equal the relation `Name`, column must be a key of `Row`). `Tables`/
111
111
  // `Aliases` are `never`: the walkers only consult them to EXCLUDE table/alias
@@ -115,22 +115,20 @@ export type KeyInRow<K extends string, Row> = [K] extends [keyof Row] ? true : f
115
115
  export type SegRefsInRow<Seg extends string, Name extends string, Row, S extends DatabaseSchema> =
116
116
  Trim<Seg> extends ""
117
117
  ? true
118
- : TokenizeLoose<Seg> extends infer Toks extends string[]
119
- ? And<
120
- AllTrue<
121
- QualifiedColumnRefs<Toks, S, never, never> extends infer R
122
- ? R extends string ? ProjRefInRow<R, Name, Row, S> : true
123
- : true
124
- >,
125
- AllTrue<
126
- UnqualifiedColumnRefs<Toks, S, never, never> extends infer R
127
- ? R extends string ? ProjRefInRow<R, Name, Row, S> : true
128
- : true
129
- >,
130
- true,
131
- true
132
- >
133
- : true;
118
+ : And<
119
+ AllTrue<
120
+ QualifiedRefScan<Seg> extends infer R
121
+ ? R extends string ? ProjRefInRow<R, Name, Row, S> : true
122
+ : true
123
+ >,
124
+ AllTrue<
125
+ UnqualifiedRefScan<Seg, S, never, never> extends infer R
126
+ ? R extends string ? ProjRefInRow<R, Name, Row, S> : true
127
+ : true
128
+ >,
129
+ true,
130
+ true
131
+ >;
134
132
 
135
133
  // The outer query's WHERE predicate, scoped to the CTE/derived relation's exposed
136
134
  // row. Strip subquery bodies FIRST so the derived/CTE body's own WHERE is never
@@ -191,22 +189,20 @@ type ExtractOrderByExpr<S extends string> =
191
189
  export type SegRefsInRowWithAliases<Seg extends string, Name extends string, Row, S extends DatabaseSchema, SelAliases extends string> =
192
190
  Trim<Seg> extends ""
193
191
  ? true
194
- : TokenizeLoose<Seg> extends infer Toks extends string[]
195
- ? And<
196
- AllTrue<
197
- QualifiedColumnRefs<Toks, S, never, never> extends infer R
198
- ? R extends string ? RefInRowOrAlias<R, Name, Row, S, SelAliases> : true
199
- : true
200
- >,
201
- AllTrue<
202
- UnqualifiedColumnRefs<Toks, S, never, never> extends infer R
203
- ? R extends string ? RefInRowOrAlias<R, Name, Row, S, SelAliases> : true
204
- : true
205
- >,
206
- true,
207
- true
208
- >
209
- : true;
192
+ : And<
193
+ AllTrue<
194
+ QualifiedRefScan<Seg> extends infer R
195
+ ? R extends string ? RefInRowOrAlias<R, Name, Row, S, SelAliases> : true
196
+ : true
197
+ >,
198
+ AllTrue<
199
+ UnqualifiedRefScan<Seg, S, never, never> extends infer R
200
+ ? R extends string ? RefInRowOrAlias<R, Name, Row, S, SelAliases> : true
201
+ : true
202
+ >,
203
+ true,
204
+ true
205
+ >;
210
206
 
211
207
  export type RefInRowOrAlias<R extends string, Name extends string, Row, S extends DatabaseSchema, SelAliases extends string> =
212
208
  [SelAliases] extends [never]
@@ -231,9 +227,7 @@ export type AllTablesValidFor<Tables extends string, S extends DatabaseSchema> =
231
227
  export type AllColumnsValid<N extends string, S extends DatabaseSchema> =
232
228
  TablesInQuery<N, S> extends infer Tables extends string
233
229
  ? AliasesInQuery<N, S> extends infer Aliases extends string
234
- ? TokenizeLoose<RefScanSegment<N>> extends infer LooseTokens extends string[]
235
- ? AllColumnsValidFor<N, S, Tables, Aliases, LooseTokens>
236
- : false
230
+ ? AllColumnsValidFor<N, S, Tables, Aliases, RefScanSegment<N>>
237
231
  : false
238
232
  : false;
239
233
 
@@ -242,7 +236,7 @@ export type AllColumnsValidFor<
242
236
  S extends DatabaseSchema,
243
237
  Tables extends string,
244
238
  Aliases extends string,
245
- LooseTokens extends string[]
239
+ RefSeg extends string
246
240
  > = SelectAliasSet<N> extends infer SelectAliases extends string
247
241
  ? QueryKind<N> extends "update"
248
242
  // A normal (non-high-complexity) UPDATE has no subquery/CASE SET, so its
@@ -254,14 +248,14 @@ export type AllColumnsValidFor<
254
248
  ColumnsValidInUpdate<N, S>,
255
249
  ColumnsValidInInsert<N, S>,
256
250
  ColumnsValidInSelectOrReturningFor<N, S, Tables, Aliases>,
257
- QualifiedColumnRefsValidFor<N, S, Tables, Aliases, LooseTokens>,
258
- UnqualifiedColumnRefsValidFor<N, S, Tables, Aliases, LooseTokens, never>
251
+ QualifiedColumnRefsValidFor<N, S, Tables, Aliases, RefSeg>,
252
+ UnqualifiedColumnRefsValidFor<N, S, Tables, Aliases, RefSeg, never>
259
253
  >
260
254
  : And<
261
255
  ColumnsValidInSelectOrReturningFor<N, S, Tables, Aliases>,
262
256
  ColumnsValidInInsert<N, S>,
263
257
  ColumnsValidInUpdate<N, S>,
264
- QualifiedColumnRefsValidFor<N, S, Tables, Aliases, LooseTokens>,
258
+ QualifiedColumnRefsValidFor<N, S, Tables, Aliases, RefSeg>,
265
259
  // A SELECT-list alias is only resolvable in ORDER BY — NOT in
266
260
  // WHERE/GROUP/HAVING. So the unqualified ref-scan blesses the alias
267
261
  // set ONLY for the ORDER BY token slice; the FROM..(pre-order-by)
@@ -284,10 +278,10 @@ export type SelectUnqualifiedRefsScoped<
284
278
  SelectAliases extends string
285
279
  > =
286
280
  [SelectAliases] extends [never]
287
- ? UnqualifiedColumnRefsValidFor<N, S, Tables, Aliases, TokenizeLoose<RefScanSegment<N>>, never>
281
+ ? UnqualifiedColumnRefsValidFor<N, S, Tables, Aliases, RefScanSegment<N>, never>
288
282
  : And<
289
- UnqualifiedColumnRefsValidFor<N, S, Tables, Aliases, TokenizeLoose<RefScanBeforeOrderBy<N>>, never>,
290
- UnqualifiedColumnRefsValidFor<N, S, Tables, Aliases, TokenizeLoose<RefScanOrderBy<N>>, SelectAliases>,
283
+ UnqualifiedColumnRefsValidFor<N, S, Tables, Aliases, RefScanBeforeOrderBy<N>, never>,
284
+ UnqualifiedColumnRefsValidFor<N, S, Tables, Aliases, RefScanOrderBy<N>, SelectAliases>,
291
285
  true,
292
286
  true,
293
287
  true
@@ -309,7 +303,7 @@ export type ColumnsValidInSelectOrReturningFor<
309
303
  HasReturning<N> extends true
310
304
  ? [Tables] extends [never]
311
305
  ? true
312
- : ExprsValidList<SplitSelectList<ReplaceAll<ExtractReturningList<N>, DQuoteSpaceSentinel, " ">>, Tables, Aliases, S>
306
+ : ExprsValidList<SplitSelectList<ReplaceAll<ExtractReturningList<N>, DQuoteSpaceSentinel, " ">>, Tables, Aliases, S, [], SelectLocalRels<N>>
313
307
  : QueryKind<N> extends "select"
314
308
  ? [Tables] extends [never]
315
309
  ? true
@@ -318,9 +312,18 @@ export type ColumnsValidInSelectOrReturningFor<
318
312
  // The alias set restores those spaces, so restore them here too or a
319
313
  // qualifier through a space-bearing quoted alias (`"user alias".id`)
320
314
  // would never match its registered alias (round-12 regression).
321
- : ExprsValidList<SplitSelectList<ReplaceAll<ExtractSelectList<N>, DQuoteSpaceSentinel, " ">>, Tables, Aliases, S>
315
+ : ExprsValidList<SplitSelectList<ReplaceAll<ExtractSelectList<N>, DQuoteSpaceSentinel, " ">>, Tables, Aliases, S, [], SelectLocalRels<N>>
322
316
  : true;
323
317
 
318
+ // Query-local relation names whose qualified refs the projection-list validator
319
+ // must bless: a `WITH`-query's CTE names. (A CTE that reaches the core validator
320
+ // — e.g. `WITH ... SELECT ... JOIN ...` — is collected into `TablesInQuery` as a
321
+ // bogus base table for the ref-resolution, so `cte.col` in the SELECT list would
322
+ // otherwise resolve `never` and falsely reject.) Gated on the `with ` prefix so
323
+ // CTE-free queries pay nothing.
324
+ type SelectLocalRels<N extends string> =
325
+ N extends `with ${string}` ? CteNames<N> : never;
326
+
324
327
  // insert
325
328
 
326
329
  export type ColumnsValidInInsert<N extends string, S extends DatabaseSchema> =
@@ -356,9 +359,7 @@ export type ColumnsValidInUpdate<N extends string, S extends DatabaseSchema> =
356
359
  export type QualifiedColumnRefsValid<N extends string, S extends DatabaseSchema> =
357
360
  TablesInQuery<N, S> extends infer Tables extends string
358
361
  ? AliasesInQuery<N, S> extends infer Aliases extends string
359
- ? TokenizeLoose<RefScanSegment<N>> extends infer LooseTokens extends string[]
360
- ? QualifiedColumnRefsValidFor<N, S, Tables, Aliases, LooseTokens>
361
- : true
362
+ ? QualifiedColumnRefsValidFor<N, S, Tables, Aliases, RefScanSegment<N>>
362
363
  : true
363
364
  : true;
364
365
 
@@ -367,11 +368,67 @@ export type QualifiedColumnRefsValidFor<
367
368
  S extends DatabaseSchema,
368
369
  Tables extends string,
369
370
  Aliases extends string,
370
- LooseTokens extends string[]
371
- > = QualifiedColumnRefs<LooseTokens, S, Tables, Aliases> extends infer Cols
372
- ? AllTrue<Cols extends string ? ColumnRefValidLooseWith<Cols, Tables, Aliases, S> : true>
371
+ RefSeg extends string
372
+ > =
373
+ // Common path: with no CTE and no parenthesised FROM source there is no local
374
+ // relation that could qualify a ref, so skip the (per-ref) local-relation
375
+ // blessing entirely — exact prior behavior, zero added cost.
376
+ HasLocalRelations<N> extends true
377
+ ? QualifiedRefsValidWithLocal<N, S, Tables, Aliases, RefSeg, CteNames<N>>
378
+ : QualifiedRefScan<RefSeg> extends infer Cols
379
+ ? AllTrue<Cols extends string ? ColumnRefValidLooseWith<Cols, Tables, Aliases, S> : true>
380
+ : true;
381
+
382
+ // A "local relation" is a query-local name that is NOT a base table: a CTE name, or
383
+ // the alias bound to a derived / VALUES / subquery FROM source (`from (…) [as]
384
+ // x[(cols)]`). Its columns are not modeled in the schema, so a ref qualified by it
385
+ // must be accepted leniently rather than resolved against a (non-existent) base
386
+ // table. Blessing only ever turns a reject into an accept — never the reverse —
387
+ // consistent with the lenient-parser contract.
388
+ type HasLocalRelations<N extends string> =
389
+ N extends `with ${string}` ? true :
390
+ N extends `${string} from (${string}` ? true :
391
+ N extends `${string} join (${string}` ? true :
392
+ false;
393
+
394
+ type QualifiedRefsValidWithLocal<
395
+ N extends string,
396
+ S extends DatabaseSchema,
397
+ Tables extends string,
398
+ Aliases extends string,
399
+ RefSeg extends string,
400
+ Ctes extends string
401
+ > = QualifiedRefScan<RefSeg> extends infer Cols
402
+ ? AllTrue<
403
+ Cols extends string
404
+ ? IsLocalRelation<RefQualifierOf<Cols>, Ctes, N> extends true
405
+ ? true
406
+ : ColumnRefValidLooseWith<Cols, Tables, Aliases, S>
407
+ : true
408
+ >
373
409
  : true;
374
410
 
411
+ // The qualifier (text before the first `.`) of a qualified column ref.
412
+ type RefQualifierOf<Col extends string> =
413
+ Col extends `${infer Q}.${string}` ? CleanIdent<Q> : never;
414
+
415
+ type IsLocalRelation<Q extends string, Ctes extends string, N extends string> =
416
+ [Q] extends [never] ? false :
417
+ Q extends Ctes ? true :
418
+ IsDerivedSourceAlias<Q, N>;
419
+
420
+ // Detect `… ) [as] q …` / `… ) [as] q( …` — q is the alias bound to a parenthesised
421
+ // (derived / VALUES / subquery) FROM source. N is already normalized (lowercase
422
+ // outside quotes); q is the cleaned, lowercased qualifier.
423
+ type IsDerivedSourceAlias<Q extends string, N extends string> =
424
+ N extends `${string}) as ${Q} ${string}` ? true :
425
+ N extends `${string}) as ${Q}(${string}` ? true :
426
+ N extends `${string}) as ${Q}` ? true :
427
+ N extends `${string}) ${Q} ${string}` ? true :
428
+ N extends `${string}) ${Q}(${string}` ? true :
429
+ N extends `${string}) ${Q}` ? true :
430
+ false;
431
+
375
432
  // Once a table is given a range alias (`FROM products p`), PostgreSQL hides the
376
433
  // original table name as a correlation name for that query level — `products.id`
377
434
  // is then invalid; only `p.id` works. The lenient qualified-ref check accepts
@@ -403,12 +460,12 @@ export type NoAliasShadowedQualifiers<
403
460
  [AliasedTableKeys<Aliases>] extends [never]
404
461
  ? true
405
462
  // A shadowable qualifier is a `qualifier.column` token, which requires a
406
- // `.`. With no `.` anywhere, `QualifiedColumnRefs` accumulates `never` and
407
- // `AllTrue<never>` is `true` — so skip the whole-query `TokenizeLoose<N>`
408
- // re-walk (computed nowhere else) on dot-free queries. Exact-equivalent.
463
+ // `.`. With no `.` anywhere, `QualifiedRefScan` accumulates `never` and
464
+ // `AllTrue<never>` is `true` — so skip the whole-query re-scan (computed
465
+ // nowhere else) on dot-free queries. Exact-equivalent.
409
466
  : N extends `${string}.${string}`
410
467
  ? AllTrue<
411
- QualifiedColumnRefs<TokenizeLoose<N>, S, Tables, Aliases> extends infer R
468
+ QualifiedRefScan<N> extends infer R
412
469
  ? R extends `${infer Q}.${string}`
413
470
  ? QualifierShadowedByAlias<Q, Tables, Aliases, S> extends true
414
471
  ? false
@@ -456,7 +513,7 @@ export type OuterScopeUnqualifiedValid<N extends string, S extends DatabaseSchem
456
513
  S,
457
514
  OT,
458
515
  OA,
459
- TokenizeLoose<RefScanSegment<Stripped>>,
516
+ RefScanSegment<Stripped>,
460
517
  SelectAliasesInQuery<Stripped>
461
518
  >
462
519
  : true
@@ -469,10 +526,8 @@ export type OuterScopeUnqualifiedValid<N extends string, S extends DatabaseSchem
469
526
  export type UnqualifiedColumnRefsValid<N extends string, S extends DatabaseSchema> =
470
527
  TablesInQuery<N, S> extends infer Tables extends string
471
528
  ? AliasesInQuery<N, S> extends infer Aliases extends string
472
- ? TokenizeLoose<RefScanSegment<N>> extends infer LooseTokens extends string[]
473
- ? SelectAliasesInQuery<N> extends infer SelectAliases extends string
474
- ? UnqualifiedColumnRefsValidFor<N, S, Tables, Aliases, LooseTokens, SelectAliases>
475
- : true
529
+ ? SelectAliasesInQuery<N> extends infer SelectAliases extends string
530
+ ? UnqualifiedColumnRefsValidFor<N, S, Tables, Aliases, RefScanSegment<N>, SelectAliases>
476
531
  : true
477
532
  : true
478
533
  : true;
@@ -482,14 +537,53 @@ export type UnqualifiedColumnRefsValidFor<
482
537
  S extends DatabaseSchema,
483
538
  Tables extends string,
484
539
  Aliases extends string,
485
- LooseTokens extends string[],
540
+ RefSeg extends string,
486
541
  SelectAliases extends string
487
- > = UnqualifiedColumnRefs<LooseTokens, S, Tables, Aliases> extends infer Cols
542
+ > = UnqualifiedRefScan<RefSeg, S, Tables, Aliases> extends infer Cols
488
543
  ? AllTrue<
489
544
  Cols extends string
490
545
  ? CleanIdent<Cols> extends SelectAliases
491
546
  ? true
492
- : UnqualifiedColumnValid<Cols, Tables, Aliases, S>
547
+ : UnqualifiedColumnValid<Cols, Tables, Aliases, S> extends true
548
+ ? true
549
+ // A derived/VALUES source's column-alias list (`... ) as
550
+ // src(id, t)`) survives tokenization as bare identifier tokens,
551
+ // and `t` is not a schema column anywhere — bless names that
552
+ // belong to such a list. Checked ONLY after normal resolution
553
+ // fails, so valid queries never pay for the extraction, and
554
+ // blessing can only turn a reject into an accept (lenient
555
+ // contract).
556
+ : CleanIdent<Cols> extends SourceAliasListCols<N>
557
+ ? true
558
+ : false
493
559
  : true
494
560
  >
495
561
  : true;
562
+
563
+ // Every column name bound by a derived-source column-alias list — `) as p(a, b)`
564
+ // / `) p(a, b)` — plus CTE column-alias lists (`with c(x, y) as (`). Collected
565
+ // across ALL occurrences in the (normalized, single-line) query. The alias word
566
+ // itself must be space-free, mirroring `DerivedColsAfterAlias`'s guard, so a
567
+ // trailing `WHERE ... IN (...)` paren group is not misread as a column list.
568
+ type SourceAliasListCols<N extends string> =
569
+ N extends `with ${infer CteHead}(${infer CteCols}) as (${infer Rest}`
570
+ ? AliasHasNoSpace<Trim<CteHead>> extends true
571
+ ? ColNamesFromList<CteCols> | DerivedAliasListCols<Rest>
572
+ : DerivedAliasListCols<N>
573
+ : DerivedAliasListCols<N>;
574
+
575
+ type DerivedAliasListCols<N extends string, Steps extends any[] = []> =
576
+ Steps["length"] extends 15
577
+ ? never
578
+ : N extends `${string}) as ${infer Rest}`
579
+ ? Rest extends `${infer Alias}(${infer Cols})${infer Tail}`
580
+ ? AliasHasNoSpace<Trim<Alias>> extends true
581
+ ? ColNamesFromList<Cols> | DerivedAliasListCols<Tail, [any, ...Steps]>
582
+ : DerivedAliasListCols<Tail, [any, ...Steps]>
583
+ : never
584
+ : never;
585
+
586
+ type ColNamesFromList<L extends string> =
587
+ L extends `${infer A},${infer R}`
588
+ ? CleanIdent<A> | ColNamesFromList<R>
589
+ : CleanIdent<L>;