effect-qb 0.15.0 → 0.16.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "effect-qb",
3
- "version": "0.15.0",
3
+ "version": "0.16.0",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -49,7 +49,7 @@
49
49
  },
50
50
  "devDependencies": {
51
51
  "@types/bun": "latest",
52
- "@typescript/native-preview": "latest"
52
+ "@typescript/native-preview": "beta"
53
53
  },
54
54
  "dependencies": {
55
55
  "@effect/experimental": "^0.57.0",
@@ -76,6 +76,7 @@ export interface ColumnState<
76
76
  readonly defaultValue?: DdlExpression
77
77
  readonly generatedValue?: DdlExpression
78
78
  readonly ddlType?: string
79
+ readonly driverValueMapping?: Expression.DriverValueMapping
79
80
  readonly identity?: {
80
81
  readonly generation: "always" | "byDefault"
81
82
  }
@@ -137,6 +138,7 @@ export interface ColumnDefinition<
137
138
  readonly defaultValue?: DdlExpression
138
139
  readonly generatedValue?: DdlExpression
139
140
  readonly ddlType?: string
141
+ readonly driverValueMapping?: Expression.DriverValueMapping
140
142
  readonly identity?: {
141
143
  readonly generation: "always" | "byDefault"
142
144
  }
@@ -289,6 +291,7 @@ export const makeColumnDefinition = <
289
291
  runtime: undefined as Select,
290
292
  dbType: metadata.dbType,
291
293
  runtimeSchema: schema,
294
+ driverValueMapping: metadata.driverValueMapping,
292
295
  nullability: (metadata.nullable ? "maybe" : "never") as Nullable extends true ? "maybe" : "never",
293
296
  dialect: metadata.dbType.dialect,
294
297
  kind: "scalar",
@@ -308,6 +311,7 @@ export const makeColumnDefinition = <
308
311
  defaultValue: metadata.defaultValue,
309
312
  generatedValue: metadata.generatedValue,
310
313
  ddlType: metadata.ddlType,
314
+ driverValueMapping: metadata.driverValueMapping,
311
315
  identity: metadata.identity,
312
316
  enum: metadata.enum
313
317
  }
@@ -379,6 +383,7 @@ export const remapColumnDefinition = <
379
383
  runtime: undefined as Select,
380
384
  dbType: metadata.dbType,
381
385
  runtimeSchema: schema,
386
+ driverValueMapping: metadata.driverValueMapping,
382
387
  nullability: (metadata.nullable ? "maybe" : "never") as Nullable extends true ? "maybe" : "never",
383
388
  dialect: metadata.dbType.dialect
384
389
  }
@@ -397,6 +402,7 @@ export const remapColumnDefinition = <
397
402
  defaultValue: metadata.defaultValue,
398
403
  generatedValue: metadata.generatedValue,
399
404
  ddlType: metadata.ddlType,
405
+ driverValueMapping: metadata.driverValueMapping,
400
406
  identity: metadata.identity,
401
407
  enum: metadata.enum
402
408
  }
@@ -443,6 +449,7 @@ export const bindColumn = <
443
449
  runtime: undefined as SelectType<Column>,
444
450
  dbType: column.metadata.dbType,
445
451
  runtimeSchema: schema,
452
+ driverValueMapping: column.metadata.driverValueMapping,
446
453
  nullability: (column.metadata.nullable ? "maybe" : "never") as IsNullable<Column> extends true ? "maybe" : "never",
447
454
  dialect: column.metadata.dbType.dialect,
448
455
  kind: "scalar",
@@ -123,6 +123,20 @@ type DdlTypedColumn<Column extends AnyColumnDefinition> = ColumnDefinition<
123
123
  ReferencesOf<Column>
124
124
  > & PreserveBrand<Column>
125
125
 
126
+ type DriverValueMappedColumn<Column extends AnyColumnDefinition> = ColumnDefinition<
127
+ SelectType<Column>,
128
+ InsertType<Column>,
129
+ UpdateType<Column>,
130
+ Column[typeof ColumnTypeId]["dbType"],
131
+ IsNullable<Column>,
132
+ HasDefault<Column>,
133
+ IsGenerated<Column>,
134
+ IsPrimaryKey<Column>,
135
+ Column[typeof ColumnTypeId]["unique"],
136
+ ReferencesOf<Column>,
137
+ Column[typeof ColumnTypeId]["dependencies"]
138
+ > & PreserveBrand<Column>
139
+
126
140
  type GeneratedColumn<Column extends AnyColumnDefinition> = ColumnDefinition<
127
141
  SelectType<Column>,
128
142
  InsertType<Column>,
@@ -543,6 +557,14 @@ export const ddlType = <SqlType extends string>(sqlType: SqlType) =>
543
557
  ddlType: sqlType
544
558
  }) as DdlTypedColumn<Column>
545
559
 
560
+ /** Overrides how a column crosses the SQL driver boundary. */
561
+ export const driverValueMapping = (mapping: Expression.DriverValueMapping) =>
562
+ <Column extends AnyColumnDefinition>(column: Column): DriverValueMappedColumn<Column> =>
563
+ mapColumn(column, {
564
+ ...column.metadata,
565
+ driverValueMapping: mapping
566
+ }) as DriverValueMappedColumn<Column>
567
+
546
568
  /** Marks a column as a Postgres array type. */
547
569
  export const array = <Options extends ArrayOptions | undefined = undefined>(
548
570
  options?: Options
@@ -1,3 +1,7 @@
1
+ import type * as Schema from "effect/Schema"
2
+
3
+ import type * as Expression from "./scalar.js"
4
+
1
5
  /**
2
6
  * Mutable rendering state shared while serializing SQL for a concrete dialect.
3
7
  *
@@ -6,6 +10,7 @@
6
10
  */
7
11
  export interface RenderState {
8
12
  readonly params: unknown[]
13
+ readonly valueMappings?: Expression.DriverValueMappings
9
14
  readonly ctes: {
10
15
  readonly name: string
11
16
  readonly sql: string
@@ -14,6 +19,12 @@ export interface RenderState {
14
19
  readonly cteNames: Set<string>
15
20
  }
16
21
 
22
+ export interface RenderValueContext {
23
+ readonly dbType?: Expression.DbType.Any
24
+ readonly runtimeSchema?: Schema.Schema.Any
25
+ readonly driverValueMapping?: Expression.DriverValueMapping
26
+ }
27
+
17
28
  /**
18
29
  * Minimal runtime contract for a SQL dialect.
19
30
  *
@@ -24,7 +35,7 @@ export interface RenderState {
24
35
  export interface SqlDialect<Name extends string = string> {
25
36
  readonly name: Name
26
37
  quoteIdentifier(value: string): string
27
- renderLiteral(value: unknown, state: RenderState): string
38
+ renderLiteral(value: unknown, state: RenderState, context?: RenderValueContext): string
28
39
  renderTableReference(tableName: string, baseTableName: string, schemaName?: string): string
29
40
  renderConcat(values: readonly string[]): string
30
41
  }
@@ -9,7 +9,7 @@ import * as Stream from "effect/Stream"
9
9
  import * as Expression from "./scalar.js"
10
10
  import * as ExpressionAst from "./expression-ast.js"
11
11
  import { resolveImplicationScope, type ImplicationScope } from "./implication-runtime.js"
12
- import { normalizeDbValue } from "./runtime/normalize.js"
12
+ import { fromDriverValue } from "./runtime/driver-value-mapping.js"
13
13
  import { expressionRuntimeSchema } from "./runtime/schema.js"
14
14
  import { flattenSelection } from "./projections.js"
15
15
  import * as Query from "./query.js"
@@ -243,12 +243,19 @@ const decodeProjectionValue = (
243
243
  expression: Expression.Any,
244
244
  raw: unknown,
245
245
  scope: ImplicationScope,
246
- driverMode: DriverMode
246
+ driverMode: DriverMode,
247
+ valueMappings?: Expression.DriverValueMappings
247
248
  ): unknown => {
248
249
  let normalized = raw
249
250
  if (driverMode === "raw") {
250
251
  try {
251
- normalized = normalizeDbValue(expression[Expression.TypeId].dbType, raw)
252
+ normalized = fromDriverValue(raw, {
253
+ dialect: rendered.dialect,
254
+ dbType: expression[Expression.TypeId].dbType,
255
+ runtimeSchema: expression[Expression.TypeId].runtimeSchema,
256
+ driverValueMapping: expression[Expression.TypeId].driverValueMapping,
257
+ valueMappings
258
+ })
252
259
  } catch (cause) {
253
260
  throw makeRowDecodeError(rendered, projection, expression, raw, "normalize", cause)
254
261
  }
@@ -303,6 +310,7 @@ export const makeRowDecoder = (
303
310
  plan: Query.Plan.Any,
304
311
  options: {
305
312
  readonly driverMode?: DriverMode
313
+ readonly valueMappings?: Expression.DriverValueMappings
306
314
  } = {}
307
315
  ): ((row: FlatRow) => any) => {
308
316
  const projections = flattenSelection(
@@ -312,6 +320,7 @@ export const makeRowDecoder = (
312
320
  projections.map((projection) => [projection.alias, projection.expression] as const)
313
321
  )
314
322
  const driverMode = options.driverMode ?? "raw"
323
+ const valueMappings = options.valueMappings ?? rendered.valueMappings
315
324
  const scope = resolveImplicationScope(plan[Plan.TypeId].available, Query.getQueryState(plan).assumptions)
316
325
  return (row) => {
317
326
  const decoded: Record<string, unknown> = {}
@@ -326,7 +335,7 @@ export const makeRowDecoder = (
326
335
  setPath(
327
336
  decoded,
328
337
  projection.path,
329
- decodeProjectionValue(rendered, projection, expression, row[projection.alias], scope, driverMode)
338
+ decodeProjectionValue(rendered, projection, expression, row[projection.alias], scope, driverMode, valueMappings)
330
339
  )
331
340
  }
332
341
  return decoded
@@ -339,6 +348,7 @@ export const decodeChunk = (
339
348
  rows: Chunk.Chunk<FlatRow>,
340
349
  options: {
341
350
  readonly driverMode?: DriverMode
351
+ readonly valueMappings?: Expression.DriverValueMappings
342
352
  } = {}
343
353
  ): Chunk.Chunk<any> => {
344
354
  const decodeRow = makeRowDecoder(rendered, plan, options)
@@ -351,6 +361,7 @@ export const decodeRows = (
351
361
  rows: ReadonlyArray<FlatRow>,
352
362
  options: {
353
363
  readonly driverMode?: DriverMode
364
+ readonly valueMappings?: Expression.DriverValueMappings
354
365
  } = {}
355
366
  ): ReadonlyArray<any> => {
356
367
  const decodeRow = makeRowDecoder(rendered, plan, options)
@@ -1,9 +1,74 @@
1
- import type { AnalyzeFormula } from "./context.js"
1
+ import type { AnalyzeFormula, AssumeFactsFalse, AssumeFactsTrue, PredicateContext } from "./context.js"
2
2
  import type { FormulaOfPredicate } from "./normalize.js"
3
3
  import type { And, Not, PredicateFormula, TrueFormula } from "./formula.js"
4
4
 
5
5
  type ContextOf<Formula extends PredicateFormula> = AnalyzeFormula<Formula>
6
6
 
7
+ export type EmptyFacts = AnalyzeFormula<TrueFormula>
8
+
9
+ export type FactsOfFormula<
10
+ Formula extends PredicateFormula
11
+ > = AnalyzeFormula<Formula>
12
+
13
+ export interface PredicateState<
14
+ Formula extends PredicateFormula = PredicateFormula,
15
+ Facts extends PredicateContext = PredicateContext
16
+ > {
17
+ readonly formula: Formula
18
+ readonly facts: Facts
19
+ }
20
+
21
+ export type EmptyPredicateState = PredicateState<TrueFormula, EmptyFacts>
22
+
23
+ export type PredicateStateFormula<
24
+ State extends PredicateState
25
+ > = State["formula"]
26
+
27
+ export type PredicateStateFacts<
28
+ State extends PredicateState
29
+ > = State["facts"]
30
+
31
+ export type GuaranteedNonNullKeysInFacts<
32
+ Facts extends PredicateContext
33
+ > = Facts["nonNullKeys"]
34
+
35
+ export type GuaranteedNullKeysInFacts<
36
+ Facts extends PredicateContext
37
+ > = Facts["nullKeys"]
38
+
39
+ export type GuaranteedSourceNamesInFacts<
40
+ Facts extends PredicateContext
41
+ > = Facts["sourceNames"]
42
+
43
+ export type GuaranteedLiteralSetInFacts<
44
+ Facts extends PredicateContext,
45
+ Key extends string
46
+ > = [Facts["literalSets"]] extends [{ readonly [K in Key]: infer Values }]
47
+ ? Values
48
+ : never
49
+
50
+ export type GuaranteedEqLiteralInFacts<
51
+ Facts extends PredicateContext,
52
+ Key extends string
53
+ > = [Facts["eqLiterals"]] extends [{ readonly [K in Key]: infer Values }]
54
+ ? Values
55
+ : never
56
+
57
+ export type GuaranteedNeqLiteralInFacts<
58
+ Facts extends PredicateContext,
59
+ Key extends string
60
+ > = [Facts["neqLiterals"]] extends [{ readonly [K in Key]: infer Values }]
61
+ ? Values
62
+ : never
63
+
64
+ export type GuaranteedJsonLiteralSetInFacts<
65
+ Facts extends PredicateContext,
66
+ ColumnKey extends string,
67
+ Path extends string
68
+ > = [Facts["jsonLiteralSets"]] extends [{ readonly [C in ColumnKey]: { readonly [P in Path]: infer Values } }]
69
+ ? Values
70
+ : never
71
+
7
72
  export type GuaranteedNonNullKeys<
8
73
  Assumptions extends PredicateFormula
9
74
  > = ContextOf<Assumptions>["nonNullKeys"]
@@ -23,6 +88,17 @@ export type GuaranteedEqLiteral<
23
88
  ? ContextOf<Assumptions>["eqLiterals"][Key]
24
89
  : never
25
90
 
91
+ export type GuaranteedLiteralSets<
92
+ Assumptions extends PredicateFormula
93
+ > = ContextOf<Assumptions>["literalSets"]
94
+
95
+ export type GuaranteedLiteralSet<
96
+ Assumptions extends PredicateFormula,
97
+ Key extends string
98
+ > = Key extends keyof ContextOf<Assumptions>["literalSets"]
99
+ ? ContextOf<Assumptions>["literalSets"][Key]
100
+ : never
101
+
26
102
  type IsContradiction<Formula extends PredicateFormula> =
27
103
  ContextOf<Formula>["contradiction"] extends true ? true : false
28
104
 
@@ -70,6 +146,32 @@ export type AssumeFalse<
70
146
  Predicate
71
147
  > = AssumeFormulaFalse<Assumptions, FormulaOfPredicate<Predicate>>
72
148
 
149
+ export type AssumePredicateStateFormulaTrue<
150
+ State extends PredicateState,
151
+ Formula extends PredicateFormula
152
+ > = PredicateState<
153
+ AssumeFormulaTrue<PredicateStateFormula<State>, Formula>,
154
+ AssumeFactsTrue<PredicateStateFacts<State>, Formula>
155
+ >
156
+
157
+ export type AssumePredicateStateFormulaFalse<
158
+ State extends PredicateState,
159
+ Formula extends PredicateFormula
160
+ > = PredicateState<
161
+ AssumeFormulaFalse<PredicateStateFormula<State>, Formula>,
162
+ AssumeFactsFalse<PredicateStateFacts<State>, Formula>
163
+ >
164
+
165
+ export type AssumePredicateStateTrue<
166
+ State extends PredicateState,
167
+ Predicate
168
+ > = AssumePredicateStateFormulaTrue<State, FormulaOfPredicate<Predicate>>
169
+
170
+ export type AssumePredicateStateFalse<
171
+ State extends PredicateState,
172
+ Predicate
173
+ > = AssumePredicateStateFormulaFalse<State, FormulaOfPredicate<Predicate>>
174
+
73
175
  export type Contradicts<
74
176
  Assumptions extends PredicateFormula,
75
177
  Predicate
@@ -20,6 +20,12 @@ export interface NeqLiteralAtom<Key extends string, Value extends string> {
20
20
  readonly value: Value
21
21
  }
22
22
 
23
+ export interface LiteralSetAtom<Key extends string, Values extends string> {
24
+ readonly kind: "literal-set"
25
+ readonly key: Key
26
+ readonly values: readonly Values[]
27
+ }
28
+
23
29
  export interface EqColumnAtom<
24
30
  LeftKey extends string,
25
31
  RightKey extends string
@@ -39,5 +45,6 @@ export type PredicateAtom =
39
45
  | NonNullAtom<string>
40
46
  | EqLiteralAtom<string, string>
41
47
  | NeqLiteralAtom<string, string>
48
+ | LiteralSetAtom<string, string>
42
49
  | EqColumnAtom<string, string>
43
50
  | UnknownAtom<string>
@@ -1,4 +1,4 @@
1
- import type { EqColumnAtom, EqLiteralAtom, NeqLiteralAtom, NonNullAtom, NullAtom, PredicateAtom, UnknownAtom } from "./atom.js"
1
+ import type { EqColumnAtom, EqLiteralAtom, LiteralSetAtom, NeqLiteralAtom, NonNullAtom, NullAtom, PredicateAtom, UnknownAtom } from "./atom.js"
2
2
  import type { AllFormula, AnyFormula, AtomFormula, FalseFormula, NotFormula, PredicateFormula, TrueFormula } from "./formula.js"
3
3
 
4
4
  type Polarity = "positive" | "negative"
@@ -8,6 +8,8 @@ export interface Context<
8
8
  NullKeys extends string = never,
9
9
  EqLiterals = {},
10
10
  NeqLiterals = {},
11
+ LiteralSets = {},
12
+ JsonLiteralSets = {},
11
13
  SourceNames extends string = never,
12
14
  Contradiction extends boolean = false,
13
15
  Unknown extends boolean = false
@@ -16,6 +18,8 @@ export interface Context<
16
18
  readonly nullKeys: NullKeys
17
19
  readonly eqLiterals: EqLiterals
18
20
  readonly neqLiterals: NeqLiterals
21
+ readonly literalSets: LiteralSets
22
+ readonly jsonLiteralSets: JsonLiteralSets
19
23
  readonly sourceNames: SourceNames
20
24
  readonly contradiction: Contradiction
21
25
  readonly unknown: Unknown
@@ -27,6 +31,8 @@ type AnyContext = Context<
27
31
  string,
28
32
  Record<string, string>,
29
33
  Record<string, string>,
34
+ Record<string, string>,
35
+ Record<string, Record<string, string>>,
30
36
  string,
31
37
  boolean,
32
38
  boolean
@@ -84,15 +90,75 @@ type MergeNeqLiteralMaps<Left, Right> = {
84
90
  : never
85
91
  }
86
92
 
93
+ type MergeLiteralSetMaps<Left, Right> = {
94
+ readonly [K in Extract<keyof Left | keyof Right, string>]:
95
+ K extends keyof Left
96
+ ? K extends keyof Right
97
+ ? Extract<Left[K], Right[K]>
98
+ : Left[K]
99
+ : K extends keyof Right
100
+ ? Right[K]
101
+ : never
102
+ }
103
+
104
+ type MergeJsonLiteralSetPathMaps<Left, Right> = {
105
+ readonly [K in Extract<keyof Left | keyof Right, string>]:
106
+ K extends keyof Left
107
+ ? K extends keyof Right
108
+ ? Extract<Left[K], Right[K]>
109
+ : Left[K]
110
+ : K extends keyof Right
111
+ ? Right[K]
112
+ : never
113
+ }
114
+
115
+ type MergeJsonLiteralSetMaps<Left, Right> = {
116
+ readonly [K in Extract<keyof Left | keyof Right, string>]:
117
+ K extends keyof Left
118
+ ? K extends keyof Right
119
+ ? MergeJsonLiteralSetPathMaps<Left[K], Right[K]>
120
+ : Left[K]
121
+ : K extends keyof Right
122
+ ? Right[K]
123
+ : never
124
+ }
125
+
87
126
  type FilterNeverValues<Map> = {
88
127
  readonly [K in keyof Map as Map[K] extends never ? never : K]: Map[K]
89
128
  }
90
129
 
130
+ type JsonKeyParts<Key extends string> = string extends Key
131
+ ? never
132
+ : Key extends `${infer ColumnKey}#json:${infer Path}`
133
+ ? readonly [ColumnKey, Path]
134
+ : never
135
+
136
+ type LiteralSetMapAfterAdd<
137
+ LiteralSets,
138
+ Key extends string,
139
+ Values extends string
140
+ > = [JsonKeyParts<Key>] extends [never]
141
+ ? FilterNeverValues<MergeLiteralSetMaps<LiteralSets, { readonly [K in Key]: Values }>>
142
+ : LiteralSets
143
+
144
+ type JsonLiteralSetMapAfterAdd<
145
+ JsonLiteralSets,
146
+ Key extends string,
147
+ Values extends string
148
+ > = JsonKeyParts<Key> extends readonly [infer ColumnKey extends string, infer Path extends string]
149
+ ? MergeJsonLiteralSetMaps<
150
+ JsonLiteralSets,
151
+ { readonly [C in ColumnKey]: { readonly [P in Path]: Values } }
152
+ >
153
+ : JsonLiteralSets
154
+
91
155
  type MarkContradiction<Ctx extends AnyContext> = Context<
92
156
  Ctx["nonNullKeys"],
93
157
  Ctx["nullKeys"],
94
158
  Ctx["eqLiterals"],
95
159
  Ctx["neqLiterals"],
160
+ Ctx["literalSets"],
161
+ Ctx["jsonLiteralSets"],
96
162
  Ctx["sourceNames"],
97
163
  true,
98
164
  Ctx["unknown"]
@@ -103,6 +169,8 @@ type MarkUnknown<Ctx extends AnyContext> = Context<
103
169
  Ctx["nullKeys"],
104
170
  Ctx["eqLiterals"],
105
171
  Ctx["neqLiterals"],
172
+ Ctx["literalSets"],
173
+ Ctx["jsonLiteralSets"],
106
174
  Ctx["sourceNames"],
107
175
  Ctx["contradiction"],
108
176
  true
@@ -116,6 +184,8 @@ type AddNonNull<
116
184
  Ctx["nullKeys"],
117
185
  Ctx["eqLiterals"],
118
186
  Ctx["neqLiterals"],
187
+ Ctx["literalSets"],
188
+ Ctx["jsonLiteralSets"],
119
189
  Ctx["sourceNames"] | SourceNameOfKey<Key>,
120
190
  Key extends Ctx["nullKeys"] ? true : Ctx["contradiction"],
121
191
  Ctx["unknown"]
@@ -129,11 +199,35 @@ type AddNull<
129
199
  Ctx["nullKeys"] | Key,
130
200
  Ctx["eqLiterals"],
131
201
  Ctx["neqLiterals"],
202
+ Ctx["literalSets"],
203
+ Ctx["jsonLiteralSets"],
132
204
  Ctx["sourceNames"] | SourceNameOfKey<Key>,
133
205
  Key extends Ctx["nonNullKeys"] ? true : Ctx["contradiction"],
134
206
  Ctx["unknown"]
135
207
  >
136
208
 
209
+ type AddLiteralSet<
210
+ Ctx extends AnyContext,
211
+ Key extends string,
212
+ Values extends string
213
+ > = Context<
214
+ Ctx["nonNullKeys"] | Key,
215
+ Ctx["nullKeys"],
216
+ Ctx["eqLiterals"],
217
+ Ctx["neqLiterals"],
218
+ LiteralSetMapAfterAdd<Ctx["literalSets"], Key, Values>,
219
+ JsonLiteralSetMapAfterAdd<Ctx["jsonLiteralSets"], Key, Values>,
220
+ Ctx["sourceNames"] | SourceNameOfKey<Key>,
221
+ EqLiteralValueOf<Ctx["eqLiterals"], Key> extends infer EqValue
222
+ ? [EqValue] extends [never]
223
+ ? Key extends Ctx["nullKeys"] ? true : Ctx["contradiction"]
224
+ : EqValue extends Values
225
+ ? Key extends Ctx["nullKeys"] ? true : Ctx["contradiction"]
226
+ : true
227
+ : true,
228
+ Ctx["unknown"]
229
+ >
230
+
137
231
  type AddEqLiteral<
138
232
  Ctx extends AnyContext,
139
233
  Key extends string,
@@ -143,6 +237,8 @@ type AddEqLiteral<
143
237
  Ctx["nullKeys"],
144
238
  FilterNeverValues<MergeEqLiteralMaps<Ctx["eqLiterals"], { readonly [K in Key]: Value }>>,
145
239
  Ctx["neqLiterals"],
240
+ LiteralSetMapAfterAdd<Ctx["literalSets"], Key, Value>,
241
+ JsonLiteralSetMapAfterAdd<Ctx["jsonLiteralSets"], Key, Value>,
146
242
  Ctx["sourceNames"] | SourceNameOfKey<Key>,
147
243
  EqLiteralValueOf<Ctx["eqLiterals"], Key> extends never
148
244
  ? NeqLiteralValuesOf<Ctx["neqLiterals"], Key> extends infer NeqValues
@@ -169,6 +265,8 @@ type AddNeqLiteral<
169
265
  Ctx["nullKeys"],
170
266
  Ctx["eqLiterals"],
171
267
  FilterNeverValues<MergeNeqLiteralMaps<Ctx["neqLiterals"], { readonly [K in Key]: Value }>>,
268
+ Ctx["literalSets"],
269
+ Ctx["jsonLiteralSets"],
172
270
  Ctx["sourceNames"] | SourceNameOfKey<Key>,
173
271
  EqLiteralValueOf<Ctx["eqLiterals"], Key> extends infer EqValue
174
272
  ? [EqValue] extends [never]
@@ -219,11 +317,13 @@ type ApplyAtom<
219
317
  ? AddEqLiteral<Ctx, Key, Value>
220
318
  : Atom extends NeqLiteralAtom<infer Key extends string, infer Value extends string>
221
319
  ? AddNeqLiteral<Ctx, Key, Value>
222
- : Atom extends EqColumnAtom<infer Left extends string, infer Right extends string>
223
- ? ApplyEqColumn<Ctx, Left, Right>
224
- : Atom extends UnknownAtom<any>
225
- ? MarkUnknown<Ctx>
226
- : Ctx
320
+ : Atom extends LiteralSetAtom<infer Key extends string, infer Values extends string>
321
+ ? AddLiteralSet<Ctx, Key, Values>
322
+ : Atom extends EqColumnAtom<infer Left extends string, infer Right extends string>
323
+ ? ApplyEqColumn<Ctx, Left, Right>
324
+ : Atom extends UnknownAtom<any>
325
+ ? MarkUnknown<Ctx>
326
+ : Ctx
227
327
 
228
328
  type ApplyNegativeAtom<
229
329
  Ctx extends AnyContext,
@@ -237,11 +337,13 @@ type ApplyNegativeAtom<
237
337
  ? AddNeqLiteral<Ctx, Key, Value>
238
338
  : Atom extends NeqLiteralAtom<infer Key extends string, infer Value extends string>
239
339
  ? AddEqLiteral<Ctx, Key, Value>
240
- : Atom extends EqColumnAtom<infer Left extends string, infer Right extends string>
241
- ? AddNonNull<AddNonNull<Ctx, Left>, Right>
242
- : Atom extends UnknownAtom<any>
243
- ? MarkUnknown<Ctx>
244
- : Ctx
340
+ : Atom extends LiteralSetAtom<infer Key extends string, any>
341
+ ? AddNonNull<Ctx, Key>
342
+ : Atom extends EqColumnAtom<infer Left extends string, infer Right extends string>
343
+ ? AddNonNull<AddNonNull<Ctx, Left>, Right>
344
+ : Atom extends UnknownAtom<any>
345
+ ? MarkUnknown<Ctx>
346
+ : Ctx
245
347
 
246
348
  type FramesFromItems<
247
349
  Items extends readonly PredicateFormula[],
@@ -268,6 +370,27 @@ type IntersectNeqLiteralMaps<
268
370
  Extract<Left[K], Right[K]> extends never ? never : Extract<Left[K], Right[K]>
269
371
  }>
270
372
 
373
+ type UnionLiteralSetMaps<
374
+ Left,
375
+ Right
376
+ > = FilterNeverValues<{
377
+ readonly [K in Extract<keyof Left, keyof Right>]: Left[K] | Right[K]
378
+ }>
379
+
380
+ type UnionJsonLiteralSetPathMaps<
381
+ Left,
382
+ Right
383
+ > = FilterNeverValues<{
384
+ readonly [K in Extract<keyof Left, keyof Right>]: Left[K] | Right[K]
385
+ }>
386
+
387
+ type UnionJsonLiteralSetMaps<
388
+ Left,
389
+ Right
390
+ > = {
391
+ readonly [K in Extract<keyof Left, keyof Right>]: UnionJsonLiteralSetPathMaps<Left[K], Right[K]>
392
+ }
393
+
271
394
  type IntersectContexts<
272
395
  Left extends AnyContext,
273
396
  Right extends AnyContext
@@ -276,6 +399,8 @@ type IntersectContexts<
276
399
  Extract<Left["nullKeys"], Right["nullKeys"]>,
277
400
  IntersectEqLiteralMaps<Left["eqLiterals"], Right["eqLiterals"]>,
278
401
  IntersectNeqLiteralMaps<Left["neqLiterals"], Right["neqLiterals"]>,
402
+ UnionLiteralSetMaps<Left["literalSets"], Right["literalSets"]>,
403
+ UnionJsonLiteralSetMaps<Left["jsonLiteralSets"], Right["jsonLiteralSets"]>,
279
404
  Extract<Left["sourceNames"], Right["sourceNames"]>,
280
405
  Left["contradiction"] extends true
281
406
  ? Right["contradiction"]
@@ -289,17 +414,20 @@ type AnalyzeAnyBranches<
289
414
  Ctx extends AnyContext,
290
415
  Items extends readonly PredicateFormula[],
291
416
  Direction extends Polarity,
292
- Current extends AnyContext | never = never
293
- > = Items extends readonly [
417
+ Current extends AnyContext | never = never,
418
+ Seen extends readonly unknown[] = []
419
+ > = Seen["length"] extends 20
420
+ ? MarkUnknown<Ctx>
421
+ : Items extends readonly [
294
422
  infer Head extends PredicateFormula,
295
423
  ...infer Tail extends readonly PredicateFormula[]
296
424
  ]
297
425
  ? AnalyzeBranch<Ctx, Head, Direction> extends infer Branch extends AnyContext
298
426
  ? Branch["contradiction"] extends true
299
- ? AnalyzeAnyBranches<Ctx, Tail, Direction, Current>
427
+ ? AnalyzeAnyBranches<Ctx, Tail, Direction, Current, readonly [...Seen, unknown]>
300
428
  : [Current] extends [never]
301
- ? AnalyzeAnyBranches<Ctx, Tail, Direction, Branch>
302
- : AnalyzeAnyBranches<Ctx, Tail, Direction, IntersectContexts<Current, Branch>>
429
+ ? AnalyzeAnyBranches<Ctx, Tail, Direction, Branch, readonly [...Seen, unknown]>
430
+ : AnalyzeAnyBranches<Ctx, Tail, Direction, IntersectContexts<Current, Branch>, readonly [...Seen, unknown]>
303
431
  : never
304
432
  : [Current] extends [never]
305
433
  ? MarkContradiction<Ctx>
@@ -356,3 +484,15 @@ export type AnalyzeStack<
356
484
 
357
485
  export type AnalyzeFormula<Formula extends PredicateFormula> =
358
486
  AnalyzeStack<EmptyContext, readonly [Frame<Formula, "positive">]>
487
+
488
+ export type PredicateContext = AnyContext
489
+
490
+ export type AssumeFactsTrue<
491
+ Ctx extends PredicateContext,
492
+ Formula extends PredicateFormula
493
+ > = AnalyzeStack<Ctx, readonly [Frame<Formula, "positive">]>
494
+
495
+ export type AssumeFactsFalse<
496
+ Ctx extends PredicateContext,
497
+ Formula extends PredicateFormula
498
+ > = AnalyzeStack<Ctx, readonly [Frame<Formula, "negative">]>