effect-qb 0.17.0 → 0.20.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 (137) hide show
  1. package/README.md +11 -1
  2. package/dist/index.js +9376 -0
  3. package/dist/mysql.js +4092 -2671
  4. package/dist/postgres/metadata.js +2589 -1402
  5. package/dist/postgres.js +3953 -3583
  6. package/dist/sqlite.js +5527 -4088
  7. package/dist/standard.js +9330 -0
  8. package/package.json +9 -4
  9. package/src/casing.ts +71 -0
  10. package/src/index.ts +2 -0
  11. package/src/internal/casing.ts +89 -0
  12. package/src/internal/coercion/rules.ts +13 -1
  13. package/src/internal/column-state.d.ts +3 -3
  14. package/src/internal/column-state.ts +24 -18
  15. package/src/internal/column.ts +52 -15
  16. package/src/internal/datatypes/define.ts +7 -1
  17. package/src/internal/datatypes/enrich.ts +23 -0
  18. package/src/internal/datatypes/lookup.ts +81 -25
  19. package/src/internal/datatypes/matrix.ts +903 -0
  20. package/src/internal/datatypes/shape.ts +2 -0
  21. package/src/internal/derived-table.ts +4 -36
  22. package/src/{mysql/internal/sql-expression-renderer.ts → internal/dialect-renderers/mysql.ts} +552 -361
  23. package/src/{postgres/internal/sql-expression-renderer.ts → internal/dialect-renderers/postgres.ts} +657 -400
  24. package/src/{sqlite/internal/sql-expression-renderer.ts → internal/dialect-renderers/sqlite.ts} +505 -347
  25. package/src/internal/dialect.ts +36 -1
  26. package/src/internal/dsl-mutation-runtime.ts +12 -162
  27. package/src/internal/dsl-plan-runtime.ts +10 -138
  28. package/src/internal/dsl-query-runtime.ts +5 -79
  29. package/src/internal/dsl-transaction-ddl-runtime.ts +41 -65
  30. package/src/internal/executor.ts +66 -49
  31. package/src/internal/grouping-key.ts +87 -20
  32. package/src/internal/implication-runtime.ts +1 -1
  33. package/src/internal/json/path-access.ts +351 -0
  34. package/src/internal/predicate/runtime.ts +3 -0
  35. package/src/internal/query.d.ts +39 -12
  36. package/src/internal/query.ts +65 -26
  37. package/src/internal/renderer.ts +26 -14
  38. package/src/internal/runtime/driver-value-mapping.ts +3 -3
  39. package/src/internal/runtime/normalize.ts +12 -5
  40. package/src/internal/runtime/schema.ts +28 -38
  41. package/src/internal/runtime/value.ts +20 -23
  42. package/src/internal/scalar.d.ts +1 -1
  43. package/src/internal/scalar.ts +8 -2
  44. package/src/internal/schema-derivation.d.ts +12 -61
  45. package/src/internal/schema-derivation.ts +95 -43
  46. package/src/internal/schema-expression.ts +2 -2
  47. package/src/internal/sql-expression-renderer.ts +19 -0
  48. package/src/internal/standard-dsl.ts +6978 -0
  49. package/src/internal/table-options.ts +126 -66
  50. package/src/internal/table.ts +819 -237
  51. package/src/mysql/column-extension.ts +3 -0
  52. package/src/mysql/column.ts +14 -15
  53. package/src/mysql/datatypes/index.ts +4 -2
  54. package/src/mysql/datatypes/spec.ts +6 -176
  55. package/src/mysql/errors/normalize.ts +0 -1
  56. package/src/mysql/executor.ts +11 -11
  57. package/src/mysql/function/temporal.ts +1 -1
  58. package/src/mysql/internal/dialect.ts +9 -4
  59. package/src/mysql/internal/dsl.ts +334 -170
  60. package/src/mysql/internal/renderer.ts +6 -2
  61. package/src/mysql/json.ts +7 -2
  62. package/src/mysql/query-extension.ts +16 -0
  63. package/src/mysql/renderer.ts +37 -3
  64. package/src/mysql/type.ts +60 -0
  65. package/src/mysql.ts +7 -13
  66. package/src/postgres/check.ts +1 -0
  67. package/src/postgres/column-extension.ts +28 -0
  68. package/src/postgres/column.ts +13 -19
  69. package/src/postgres/datatypes/index.d.ts +2 -1
  70. package/src/postgres/datatypes/index.ts +4 -2
  71. package/src/postgres/datatypes/spec.ts +6 -260
  72. package/src/postgres/errors/normalize.ts +0 -1
  73. package/src/postgres/executor.ts +11 -11
  74. package/src/postgres/foreign-key.ts +24 -0
  75. package/src/postgres/function/core.ts +1 -3
  76. package/src/postgres/function/index.ts +1 -17
  77. package/src/postgres/function/temporal.ts +1 -1
  78. package/src/postgres/index.ts +1 -0
  79. package/src/postgres/internal/dialect.ts +9 -4
  80. package/src/postgres/internal/dsl.ts +328 -179
  81. package/src/postgres/internal/renderer.ts +6 -2
  82. package/src/postgres/internal/schema-ddl.ts +22 -10
  83. package/src/postgres/internal/schema-model.ts +238 -7
  84. package/src/postgres/json-extension.ts +7 -0
  85. package/src/postgres/json.ts +762 -173
  86. package/src/postgres/jsonb.ts +37 -0
  87. package/src/postgres/primary-key.ts +24 -0
  88. package/src/postgres/query-extension.ts +2 -0
  89. package/src/postgres/renderer.ts +37 -3
  90. package/src/postgres/schema-management.ts +13 -13
  91. package/src/postgres/schema.ts +106 -15
  92. package/src/postgres/table.ts +205 -538
  93. package/src/postgres/type.ts +93 -10
  94. package/src/postgres/unique.ts +32 -0
  95. package/src/postgres.ts +20 -16
  96. package/src/sqlite/column-extension.ts +3 -0
  97. package/src/sqlite/column.ts +14 -15
  98. package/src/sqlite/datatypes/index.ts +4 -2
  99. package/src/sqlite/datatypes/spec.ts +6 -94
  100. package/src/sqlite/errors/normalize.ts +0 -1
  101. package/src/sqlite/executor.ts +11 -11
  102. package/src/sqlite/function/temporal.ts +1 -1
  103. package/src/sqlite/internal/dialect.ts +9 -4
  104. package/src/sqlite/internal/dsl.ts +307 -159
  105. package/src/sqlite/internal/renderer.ts +6 -2
  106. package/src/sqlite/json.ts +8 -2
  107. package/src/sqlite/query-extension.ts +2 -0
  108. package/src/sqlite/renderer.ts +37 -3
  109. package/src/sqlite/type.ts +40 -0
  110. package/src/sqlite.ts +7 -13
  111. package/src/standard/cast.ts +113 -0
  112. package/src/standard/check.ts +17 -0
  113. package/src/standard/column.ts +163 -0
  114. package/src/standard/datatypes/index.ts +83 -0
  115. package/src/standard/datatypes/spec.ts +12 -0
  116. package/src/standard/dialect.ts +40 -0
  117. package/src/standard/foreign-key.ts +37 -0
  118. package/src/standard/function/aggregate.ts +2 -0
  119. package/src/standard/function/core.ts +2 -0
  120. package/src/standard/function/index.ts +18 -0
  121. package/src/standard/function/string.ts +2 -0
  122. package/src/standard/function/temporal.ts +78 -0
  123. package/src/standard/function/window.ts +2 -0
  124. package/src/standard/index.ts +17 -0
  125. package/src/standard/internal/renderer.ts +45 -0
  126. package/src/standard/json.ts +883 -0
  127. package/src/standard/primary-key.ts +17 -0
  128. package/src/standard/query.ts +152 -0
  129. package/src/standard/renderer.ts +49 -0
  130. package/src/standard/table.ts +151 -0
  131. package/src/standard/unique.ts +17 -0
  132. package/src/standard.ts +32 -0
  133. package/src/internal/aggregation-validation.ts +0 -57
  134. package/src/internal/table.d.ts +0 -173
  135. package/src/mysql/table.ts +0 -183
  136. package/src/postgres/cast.ts +0 -45
  137. package/src/sqlite/table.ts +0 -183
@@ -96,7 +96,7 @@ export { union_query_capabilities } from "./query-requirements.js"
96
96
  * `Pipeable.pipe(...)` plus the metadata stored under `Expression.TypeId`.
97
97
  */
98
98
  const ExpressionProto = {
99
- pipe(this: unknown) {
99
+ pipe(this: Pipeable) {
100
100
  return pipeArguments(this, arguments)
101
101
  }
102
102
  }
@@ -108,7 +108,7 @@ const ExpressionProto = {
108
108
  * chained through `.pipe(...)`.
109
109
  */
110
110
  const PlanProto = {
111
- pipe(this: unknown) {
111
+ pipe(this: Pipeable) {
112
112
  return pipeArguments(this, arguments)
113
113
  }
114
114
  }
@@ -143,6 +143,34 @@ export interface QueryState<
143
143
 
144
144
  /** Effective SQL dialect carried by an expression. */
145
145
  export type DialectOf<Value extends Expression.Any> = Value[typeof Expression.TypeId]["dialect"]
146
+ type ConcreteDialect<D> = D extends "standard" ? never : D
147
+ type IsDialectUnion<Union, All = Union> = Union extends any
148
+ ? [All] extends [Union] ? false : true
149
+ : false
150
+ export type DialectConflictError<A extends string, B extends string> = string & {
151
+ readonly _tag: "DialectConflict"
152
+ readonly left: A
153
+ readonly right: B
154
+ }
155
+ export type MergeDialect<A extends string, B extends string> =
156
+ [ConcreteDialect<A>, ConcreteDialect<B>] extends [never, never] ? "standard"
157
+ : [ConcreteDialect<A>, ConcreteDialect<B>] extends [never, infer C extends string] ? C
158
+ : [ConcreteDialect<A>, ConcreteDialect<B>] extends [infer C extends string, never] ? C
159
+ : ConcreteDialect<A> extends ConcreteDialect<B>
160
+ ? ConcreteDialect<B> extends ConcreteDialect<A>
161
+ ? A
162
+ : DialectConflictError<A, B>
163
+ : DialectConflictError<A, B>
164
+
165
+ /** Collapses the portable standard tag out of a dialect union. */
166
+ export type NormalizeDialect<Dialect extends string> =
167
+ [Dialect] extends [never]
168
+ ? never
169
+ : [Exclude<Dialect, "standard">] extends [never]
170
+ ? "standard"
171
+ : IsDialectUnion<Exclude<Dialect, "standard">> extends true
172
+ ? DialectConflictError<Exclude<Dialect, "standard">, Exclude<Dialect, "standard">>
173
+ : Exclude<Dialect, "standard">
146
174
  /** Source dependency union carried by an expression. */
147
175
  export type DependenciesOf<Value extends Expression.Any> = Expression.DependenciesOf<Value>
148
176
  /** Aggregation kind carried by an expression. */
@@ -464,15 +492,17 @@ export type ExtractRequired<Selection> = Selection extends Expression.Any
464
492
  }[keyof Selection]
465
493
  : never
466
494
 
467
- /** Walks a selection tree and unions the dialects referenced by its leaves. */
468
- export type ExtractDialect<Selection> = Selection extends Expression.Any
495
+ type ExtractDialectRaw<Selection> = Selection extends Expression.Any
469
496
  ? DialectOf<Selection>
470
497
  : Selection extends Record<string, any>
471
498
  ? {
472
- [K in keyof Selection]: ExtractDialect<Selection[K]>
499
+ [K in keyof Selection]: ExtractDialectRaw<Selection[K]>
473
500
  }[keyof Selection]
474
501
  : never
475
502
 
503
+ /** Walks a selection tree and extracts the effective dialects referenced by its leaves. */
504
+ export type ExtractDialect<Selection> = NormalizeDialect<Extract<ExtractDialectRaw<Selection>, string>>
505
+
476
506
  /**
477
507
  * Minimal table-like shape required by `from(...)` and joins.
478
508
  *
@@ -1005,8 +1035,7 @@ export type UpdateInputOfTarget<Target extends MutationTargetInput> =
1005
1035
  }>
1006
1036
  : never
1007
1037
 
1008
- /** Extracts the effective dialect from a source. */
1009
- export type SourceDialectOf<Source extends SourceLike> =
1038
+ type SourceDialectOfRaw<Source extends SourceLike> =
1010
1039
  Source extends TableLike<any, infer Dialect> ? Dialect :
1011
1040
  Source extends { readonly kind: "derived"; readonly plan: infer PlanValue extends QueryPlan<any, any, any, any, any, any, any, any, any, any> } ? PlanDialectOf<PlanValue> :
1012
1041
  Source extends { readonly kind: "cte"; readonly plan: infer PlanValue extends QueryPlan<any, any, any, any, any, any, any, any, any, any> } ? PlanDialectOf<PlanValue> :
@@ -1014,6 +1043,9 @@ export type SourceDialectOf<Source extends SourceLike> =
1014
1043
  Source extends { readonly dialect: infer Dialect extends string } ? Dialect :
1015
1044
  never
1016
1045
 
1046
+ /** Extracts the effective dialect from a source. */
1047
+ export type SourceDialectOf<Source extends SourceLike> = NormalizeDialect<SourceDialectOfRaw<Source>>
1048
+
1017
1049
  /** Extracts the base table name from a source. */
1018
1050
  export type SourceBaseNameOf<Source extends SourceLike> =
1019
1051
  Source extends TableLike<any, any> ? Source[typeof Table.TypeId]["baseName"] :
@@ -1148,6 +1180,21 @@ type DerivedProjectionIssues<Selection> =
1148
1180
  : | DerivedProjectionDuplicateAliases<Selection>
1149
1181
  | DerivedProjectionAliasMismatches<Selection>
1150
1182
 
1183
+ export type SelectionProjectionDuplicateAliases<Selection> =
1184
+ IsBroadSelection<Selection> extends true
1185
+ ? never
1186
+ : DuplicateProjectionAliases<SelectionProjectionEntries<Selection>>
1187
+
1188
+ export type SelectionProjectionAliasCollisionError<Selection> = Selection & {
1189
+ readonly __effect_qb_error__: "effect-qb: projection aliases must be unique"
1190
+ readonly __effect_qb_duplicate_projection_aliases__: SelectionProjectionDuplicateAliases<Selection>
1191
+ }
1192
+
1193
+ export type SelectionProjectionAliasCollisionConstraint<Selection> =
1194
+ [SelectionProjectionDuplicateAliases<Selection>] extends [never]
1195
+ ? unknown
1196
+ : SelectionProjectionAliasCollisionError<Selection>
1197
+
1151
1198
  export type DerivedSourceProjectionCompatibilityError<
1152
1199
  PlanValue extends QueryPlan<any, any, any, any, any, any, any, any, any, any>
1153
1200
  > = PlanValue & {
@@ -1254,7 +1301,7 @@ export type AvailableOfPlan<
1254
1301
  /** Extracts the effective dialect carried by a query plan. */
1255
1302
  export type PlanDialectOf<
1256
1303
  PlanValue extends QueryPlan<any, any, any, any, any, any, any, any, any, any, any, any>
1257
- > = QueryPlanParts<PlanValue>["dialect"]
1304
+ > = NormalizeDialect<QueryPlanParts<PlanValue>["dialect"]>
1258
1305
  /** Extracts the grouped-source phantom carried by a query plan. */
1259
1306
  export type GroupedOfPlan<
1260
1307
  PlanValue extends QueryPlan<any, any, any, any, any, any, any, any, any, any, any, any>
@@ -1465,7 +1512,7 @@ export type MergeNullabilityTuple<
1465
1512
  /** Dialect union across a tuple of expressions. */
1466
1513
  export type TupleDialect<
1467
1514
  Values extends readonly Expression.Any[]
1468
- > = Values[number] extends never ? never : DialectOf<Values[number]>
1515
+ > = Values[number] extends never ? never : NormalizeDialect<DialectOf<Values[number]>>
1469
1516
 
1470
1517
  /** Converts a union into an intersection. */
1471
1518
  type UnionToIntersection<Union> = (
@@ -2124,7 +2171,7 @@ type DialectCompatibilityError<
2124
2171
  EngineDialect extends string
2125
2172
  > = PlanValue & {
2126
2173
  readonly __effect_qb_error__: "effect-qb: plan dialect is not compatible with the target renderer or executor"
2127
- readonly __effect_qb_plan_dialect__: PlanValue[typeof RowSet.TypeId]["dialect"]
2174
+ readonly __effect_qb_plan_dialect__: PlanDialectOf<PlanValue>
2128
2175
  readonly __effect_qb_target_dialect__: EngineDialect
2129
2176
  readonly __effect_qb_hint__: "Use the matching dialect module or renderer/executor"
2130
2177
  }
@@ -2175,7 +2222,7 @@ type IsDialectCompatible<
2175
2222
  EngineDialect extends string
2176
2223
  > = [PlanDialect] extends [never]
2177
2224
  ? true
2178
- : Exclude<PlanDialect, EngineDialect> extends never
2225
+ : Exclude<PlanDialect, EngineDialect | "standard"> extends never
2179
2226
  ? true
2180
2227
  : false
2181
2228
 
@@ -2183,7 +2230,7 @@ type IsDialectCompatible<
2183
2230
  export type DialectCompatiblePlan<
2184
2231
  PlanValue extends QueryPlan<any, any, any, any, any, any, any, any, any, any>,
2185
2232
  EngineDialect extends string
2186
- > = IsDialectCompatible<PlanValue[typeof RowSet.TypeId]["dialect"], EngineDialect> extends true
2233
+ > = IsDialectCompatible<PlanDialectOf<PlanValue>, EngineDialect> extends true
2187
2234
  ? CompletePlan<PlanValue>
2188
2235
  : DialectCompatibilityError<PlanValue, EngineDialect>
2189
2236
 
@@ -2201,7 +2248,7 @@ type NestedPlanStatementError<
2201
2248
  export type DialectCompatibleNestedPlan<
2202
2249
  PlanValue extends QueryPlan<any, any, any, any, any, any, any, any, any, any>,
2203
2250
  EngineDialect extends string
2204
- > = IsDialectCompatible<PlanValue[typeof RowSet.TypeId]["dialect"], EngineDialect> extends true
2251
+ > = IsDialectCompatible<PlanDialectOf<PlanValue>, EngineDialect> extends true
2205
2252
  ? StatementOfPlan<PlanValue> extends SelectLikeStatement
2206
2253
  ? AggregationCompatiblePlan<PlanValue>
2207
2254
  : NestedPlanStatementError<PlanValue>
@@ -2412,7 +2459,7 @@ export const makeExpression = <
2412
2459
  state: {
2413
2460
  readonly runtime: Runtime
2414
2461
  readonly dbType: Db
2415
- readonly runtimeSchema?: Schema.Schema.Any
2462
+ readonly runtimeSchema?: Schema.Top
2416
2463
  readonly driverValueMapping?: Expression.DriverValueMapping
2417
2464
  readonly nullability: Nullable
2418
2465
  readonly dialect: Dialect
@@ -2427,7 +2474,7 @@ export const makeExpression = <
2427
2474
  Object.defineProperty(expression, "pipe", {
2428
2475
  configurable: true,
2429
2476
  writable: true,
2430
- value: function(this: unknown) {
2477
+ value: function(this: Pipeable) {
2431
2478
  return pipeArguments(expression, arguments)
2432
2479
  }
2433
2480
  })
@@ -2477,7 +2524,7 @@ export const makePlan = <
2477
2524
  Object.defineProperty(plan, "pipe", {
2478
2525
  configurable: true,
2479
2526
  writable: true,
2480
- value: function(this: unknown) {
2527
+ value: function(this: Pipeable) {
2481
2528
  return pipeArguments(plan, arguments)
2482
2529
  }
2483
2530
  })
@@ -2536,16 +2583,8 @@ export const extractRequiredRuntime = (selection: SelectionShape): readonly stri
2536
2583
 
2537
2584
  /** Extracts the single top-level expression from a scalar subquery selection. */
2538
2585
  export const extractSingleSelectedExpressionRuntime = (selection: SelectionShape): Expression.Any => {
2539
- const keys = Object.keys(selection)
2540
- if (keys.length !== 1) {
2541
- throw new Error("scalar subqueries must select exactly one top-level expression")
2542
- }
2543
- const record = selection as Record<string, unknown>
2544
- const value = record[keys[0]!]
2545
- if (value === null || typeof value !== "object" || !(Expression.TypeId in (value as object))) {
2546
- throw new Error("scalar subqueries must select a scalar expression")
2547
- }
2548
- return value as unknown as Expression.Any
2586
+ const record = selection as Record<string, Expression.Any>
2587
+ return record[Object.keys(record)[0]!]!
2549
2588
  }
2550
2589
 
2551
2590
  /** Converts the plan's runtime `required` metadata into a mutable string list. */
@@ -1,7 +1,6 @@
1
1
  import * as Query from "./query.js"
2
- import type * as Expression from "./scalar.js"
2
+ import * as Expression from "./scalar.js"
3
3
  import { flattenSelection, type Projection, validateProjections } from "./projections.js"
4
- import * as Plan from "./row-set.js"
5
4
 
6
5
  /** Symbol used to attach rendered-query phantom row metadata. */
7
6
  export const TypeId: unique symbol = Symbol.for("effect-qb/Renderer")
@@ -38,8 +37,7 @@ export type RowOf<Value extends RenderedQuery<any, any>> = Value[typeof TypeId][
38
37
  *
39
38
  * Renderers only accept complete, dialect-compatible plans. The returned
40
39
  * `RenderedQuery` keeps the canonical `Query.ResultRow<...>` type attached for
41
- * downstream executor layers, and the built-in renderer also performs a
42
- * matching runtime aggregate-shape validation.
40
+ * downstream executor layers.
43
41
  */
44
42
  export interface Renderer<Dialect extends string = string> {
45
43
  readonly dialect: Dialect
@@ -91,24 +89,38 @@ export function make<Dialect extends string>(
91
89
  dialect: Dialect,
92
90
  render: CustomRender<Dialect>
93
91
  ): Renderer<Dialect> {
92
+ return makeRenderer(dialect, render, true)
93
+ }
94
+
95
+ /** Internal renderer factory for built-in renderers that derive projections from typed plans. */
96
+ export function makeTrusted<Dialect extends string>(
97
+ dialect: Dialect,
98
+ render: CustomRender<Dialect>
99
+ ): Renderer<Dialect>
100
+ export function makeTrusted<Dialect extends string>(
101
+ dialect: Dialect,
102
+ render: CustomRender<Dialect>
103
+ ): Renderer<Dialect> {
104
+ return makeRenderer(dialect, render, false)
105
+ }
106
+
107
+ const makeRenderer = <Dialect extends string>(
108
+ dialect: Dialect,
109
+ render: CustomRender<Dialect>,
110
+ validate: boolean
111
+ ): Renderer<Dialect> => {
94
112
  if (typeof render !== "function") {
95
113
  throw new Error(`Renderer.make requires an explicit render implementation for dialect: ${dialect}`)
96
114
  }
97
115
  return {
98
116
  dialect,
99
117
  render(plan) {
100
- const required = Query.currentRequiredList(plan[Plan.TypeId].required)
101
- if (required.length > 0) {
102
- throw new Error(`query references sources that are not yet in scope: ${required.join(", ")}`)
103
- }
104
- const planDialect = plan[Plan.TypeId].dialect
105
- if (planDialect !== dialect) {
106
- throw new Error("effect-qb: plan dialect is not compatible with the target renderer or executor")
107
- }
108
118
  const rendered = render(plan)
109
119
  const projections = rendered.projections ?? []
110
- validateProjections(projections)
111
- validateProjectionPathsMatchSelection(plan as Query.Plan.Any, projections)
120
+ if (validate) {
121
+ validateProjections(projections)
122
+ validateProjectionPathsMatchSelection(plan as Query.Plan.Any, projections)
123
+ }
112
124
  return {
113
125
  sql: rendered.sql,
114
126
  params: rendered.params ?? [],
@@ -9,7 +9,7 @@ export type DriverValueMappings = Expression.DriverValueMappings
9
9
  export interface DriverValueContext {
10
10
  readonly dialect?: string
11
11
  readonly dbType?: Expression.DbType.Any
12
- readonly runtimeSchema?: Schema.Schema.Any
12
+ readonly runtimeSchema?: Schema.Top
13
13
  readonly driverValueMapping?: DriverValueMapping
14
14
  readonly valueMappings?: DriverValueMappings
15
15
  }
@@ -99,13 +99,13 @@ const isJsonDbType = (dbType: Expression.DbType.Any | undefined): boolean => {
99
99
  }
100
100
 
101
101
  const schemaAccepts = (
102
- schema: Schema.Schema.Any | undefined,
102
+ schema: Schema.Top | undefined,
103
103
  value: unknown
104
104
  ): boolean =>
105
105
  schema !== undefined && (Schema.is(schema) as (candidate: unknown) => boolean)(value)
106
106
 
107
107
  const encodeWithSchema = (
108
- schema: Schema.Schema.Any | undefined,
108
+ schema: Schema.Top | undefined,
109
109
  value: unknown
110
110
  ): { readonly value: unknown; readonly encoded: boolean } => {
111
111
  if (schema === undefined) {
@@ -23,6 +23,13 @@ const isPlainRecord = (value: unknown): value is Record<string, unknown> => {
23
23
 
24
24
  const pad = (value: number, width = 2): string => value.toString().padStart(width, "0")
25
25
 
26
+ const validDate = (value: Date): Date => {
27
+ if (Number.isNaN(value.getTime())) {
28
+ throw new Error("Expected a valid Date value")
29
+ }
30
+ return value
31
+ }
32
+
26
33
  const formatLocalDate = (value: Date): string =>
27
34
  `${pad(value.getUTCFullYear(), 4)}-${pad(value.getUTCMonth() + 1)}-${pad(value.getUTCDate())}`
28
35
 
@@ -125,7 +132,7 @@ const normalizeDecimalString = (value: unknown): string => {
125
132
 
126
133
  const normalizeLocalDate = (value: unknown): string => {
127
134
  if (value instanceof Date) {
128
- return formatLocalDate(value)
135
+ return formatLocalDate(validDate(value))
129
136
  }
130
137
  const raw = expectString(value, "local date").trim()
131
138
  if (isValidLocalDateString(raw)) {
@@ -143,7 +150,7 @@ const normalizeLocalDate = (value: unknown): string => {
143
150
 
144
151
  const normalizeLocalTime = (value: unknown): string => {
145
152
  if (value instanceof Date) {
146
- return formatLocalTime(value)
153
+ return formatLocalTime(validDate(value))
147
154
  }
148
155
  const raw = expectString(value, "local time").trim()
149
156
  if (isValidLocalTimeString(raw)) {
@@ -154,7 +161,7 @@ const normalizeLocalTime = (value: unknown): string => {
154
161
 
155
162
  const normalizeOffsetTime = (value: unknown): string => {
156
163
  if (value instanceof Date) {
157
- return `${formatLocalTime(value)}Z`
164
+ return `${formatLocalTime(validDate(value))}Z`
158
165
  }
159
166
  const raw = expectString(value, "offset time").trim()
160
167
  if (isValidOffsetTimeString(raw)) {
@@ -165,7 +172,7 @@ const normalizeOffsetTime = (value: unknown): string => {
165
172
 
166
173
  const normalizeLocalDateTime = (value: unknown): string => {
167
174
  if (value instanceof Date) {
168
- return formatLocalDateTime(value)
175
+ return formatLocalDateTime(validDate(value))
169
176
  }
170
177
  const raw = expectString(value, "local datetime").trim()
171
178
  const canonicalLocalDateTime = raw.replace(" ", "T")
@@ -184,7 +191,7 @@ const normalizeLocalDateTime = (value: unknown): string => {
184
191
 
185
192
  const normalizeInstant = (value: unknown): string => {
186
193
  if (value instanceof Date) {
187
- return value.toISOString()
194
+ return validDate(value).toISOString()
188
195
  }
189
196
  const raw = expectString(value, "instant").trim()
190
197
  if (!/[zZ]|[+-]\d{2}:\d{2}$/.test(raw)) {
@@ -27,7 +27,7 @@ import {
27
27
  } from "./value.js"
28
28
  import type { RuntimeTag } from "../datatypes/shape.js"
29
29
 
30
- export type RuntimeSchema = Schema.Schema<any, any, any>
30
+ export type RuntimeSchema = Schema.Top
31
31
 
32
32
  type SchemaContext = {
33
33
  readonly assumptions: PredicateFormula
@@ -38,7 +38,7 @@ const schemaCache = new WeakMap<Expression.Any, RuntimeSchema | undefined>()
38
38
  const isRecord = (value: unknown): value is Record<string, unknown> =>
39
39
  typeof value === "object" && value !== null && !Array.isArray(value)
40
40
 
41
- const FiniteNumberSchema = Schema.Number.pipe(Schema.finite())
41
+ const FiniteNumberSchema = Schema.Number.check(Schema.isFinite())
42
42
 
43
43
  const runtimeSchemaForTag = (tag: RuntimeTag): RuntimeSchema | undefined => {
44
44
  switch (tag) {
@@ -67,14 +67,11 @@ const runtimeSchemaForTag = (tag: RuntimeTag): RuntimeSchema | undefined => {
67
67
  case "decimalString":
68
68
  return DecimalStringSchema
69
69
  case "bytes":
70
- return Schema.Uint8ArrayFromSelf
70
+ return Schema.Uint8Array
71
71
  case "array":
72
72
  return Schema.Array(Schema.Unknown)
73
73
  case "record":
74
- return Schema.Record({
75
- key: Schema.String,
76
- value: Schema.Unknown
77
- })
74
+ return Schema.Record(Schema.String, Schema.Unknown)
78
75
  case "null":
79
76
  return Schema.Null
80
77
  case "unknown":
@@ -114,7 +111,7 @@ export const runtimeSchemaForDbType = (
114
111
  }
115
112
 
116
113
  const makeSchemaFromAst = (ast: SchemaAST.AST): RuntimeSchema =>
117
- Schema.make(ast)
114
+ Schema.make<RuntimeSchema>(ast)
118
115
 
119
116
  const unionAst = (asts: ReadonlyArray<SchemaAST.AST>): SchemaAST.AST | undefined => {
120
117
  if (asts.length === 0) {
@@ -123,7 +120,7 @@ const unionAst = (asts: ReadonlyArray<SchemaAST.AST>): SchemaAST.AST | undefined
123
120
  if (asts.length === 1) {
124
121
  return asts[0]
125
122
  }
126
- return SchemaAST.Union.make(asts)
123
+ return new SchemaAST.Union(asts, "anyOf")
127
124
  }
128
125
 
129
126
  const propertyAstOf = (
@@ -131,18 +128,14 @@ const propertyAstOf = (
131
128
  key: string
132
129
  ): SchemaAST.AST | undefined => {
133
130
  switch (ast._tag) {
134
- case "Transformation":
135
- return propertyAstOf(SchemaAST.typeAST(ast), key)
136
- case "Refinement":
137
- return propertyAstOf(ast.from, key)
138
131
  case "Suspend":
139
- return propertyAstOf(ast.f(), key)
140
- case "TypeLiteral": {
132
+ return propertyAstOf(ast.thunk(), key)
133
+ case "Objects": {
141
134
  const property = ast.propertySignatures.find((entry) => entry.name === key)
142
135
  if (property !== undefined) {
143
136
  return property.type
144
137
  }
145
- const index = ast.indexSignatures.find((entry) => entry.parameter._tag === "StringKeyword")
138
+ const index = ast.indexSignatures.find((entry) => entry.parameter._tag === "String")
146
139
  return index?.type
147
140
  }
148
141
  case "Union": {
@@ -162,21 +155,17 @@ const numberAstOf = (
162
155
  index: number
163
156
  ): SchemaAST.AST | undefined => {
164
157
  switch (ast._tag) {
165
- case "Transformation":
166
- return numberAstOf(SchemaAST.typeAST(ast), index)
167
- case "Refinement":
168
- return numberAstOf(ast.from, index)
169
158
  case "Suspend":
170
- return numberAstOf(ast.f(), index)
171
- case "TupleType": {
159
+ return numberAstOf(ast.thunk(), index)
160
+ case "Arrays": {
172
161
  const element = ast.elements[index]
173
162
  if (element !== undefined) {
174
- return element.type
163
+ return element
175
164
  }
176
165
  if (ast.rest.length === 0) {
177
166
  return undefined
178
167
  }
179
- return unionAst(ast.rest.map((entry) => entry.type))
168
+ return unionAst(ast.rest)
180
169
  }
181
170
  case "Union": {
182
171
  const values = ast.types.flatMap((member) => {
@@ -199,7 +188,9 @@ const schemaAstAtExactJsonPath = (
199
188
  schema: RuntimeSchema,
200
189
  segments: readonly JsonPath.CanonicalSegment[]
201
190
  ): SchemaAST.AST | undefined => {
202
- let current: SchemaAST.AST = SchemaAST.typeAST(schema.ast)
191
+ // JSON path operators return encoded JSON subvalues, so preserve field-level
192
+ // encoding links instead of walking the type-side AST.
193
+ let current: SchemaAST.AST = schema.ast
203
194
  for (const segment of segments) {
204
195
  if (segment.kind === "key") {
205
196
  const property = propertyAstOf(current, segment.key)
@@ -230,7 +221,7 @@ const unionSchemas = (schemas: ReadonlyArray<RuntimeSchema | undefined>): Runtim
230
221
  if (resolved.length === 1) {
231
222
  return resolved[0]
232
223
  }
233
- return Schema.Union(...resolved)
224
+ return Schema.Union(resolved)
234
225
  }
235
226
 
236
227
  const firstSelectedExpression = (
@@ -241,24 +232,23 @@ const firstSelectedExpression = (
241
232
  }
242
233
 
243
234
  const isJsonCompatibleAst = (ast: SchemaAST.AST): boolean => {
235
+ ast = SchemaAST.toType(ast)
244
236
  switch (ast._tag) {
245
- case "StringKeyword":
246
- case "NumberKeyword":
247
- case "BooleanKeyword":
248
- case "TupleType":
249
- case "TypeLiteral":
237
+ case "String":
238
+ case "Number":
239
+ case "Boolean":
240
+ case "Null":
241
+ case "Arrays":
242
+ case "Objects":
250
243
  return true
251
244
  case "Literal":
252
- return ast.literal === null ||
253
- typeof ast.literal === "string" ||
245
+ return typeof ast.literal === "string" ||
254
246
  typeof ast.literal === "number" ||
255
247
  typeof ast.literal === "boolean"
256
248
  case "Union":
257
249
  return ast.types.every(isJsonCompatibleAst)
258
- case "Transformation":
259
- return isJsonCompatibleAst(SchemaAST.typeAST(ast))
260
250
  case "Suspend":
261
- return isJsonCompatibleAst(ast.f())
251
+ return isJsonCompatibleAst(ast.thunk())
262
252
  default:
263
253
  return false
264
254
  }
@@ -268,7 +258,7 @@ const jsonCompatibleSchema = (schema: RuntimeSchema | undefined): RuntimeSchema
268
258
  if (schema === undefined) {
269
259
  return undefined
270
260
  }
271
- const ast = SchemaAST.typeAST(schema.ast)
261
+ const ast = SchemaAST.toType(schema.ast)
272
262
  return isJsonCompatibleAst(ast) ? schema : JsonValueSchema
273
263
  }
274
264
 
@@ -283,7 +273,7 @@ const buildStructSchema = (
283
273
  }
284
274
 
285
275
  const buildTupleSchema = (values: readonly Expression.Any[], context?: SchemaContext): RuntimeSchema =>
286
- Schema.Tuple(...values.map((value) => expressionRuntimeSchema(value, context) ?? JsonValueSchema))
276
+ Schema.Tuple(values.map((value) => expressionRuntimeSchema(value, context) ?? JsonValueSchema))
287
277
 
288
278
  const deriveCaseSchema = (
289
279
  ast: ExpressionAst.CaseNode,
@@ -19,7 +19,7 @@ const brandString = <BrandName extends string>(
19
19
  brand: BrandName
20
20
  ): Schema.Schema<string & Brand.Brand<BrandName>> =>
21
21
  Schema.String.pipe(
22
- Schema.pattern(pattern),
22
+ Schema.check(Schema.isPattern(pattern)),
23
23
  Schema.brand(brand)
24
24
  ) as unknown as Schema.Schema<string & Brand.Brand<BrandName>>
25
25
 
@@ -100,32 +100,32 @@ export const isValidInstantString = (value: string): boolean => {
100
100
  }
101
101
 
102
102
  export const LocalDateStringSchema = Schema.String.pipe(
103
- Schema.pattern(localDatePattern),
104
- Schema.filter(isValidLocalDateString),
103
+ Schema.check(Schema.isPattern(localDatePattern)),
104
+ Schema.check(Schema.makeFilter((value) => isValidLocalDateString(value))),
105
105
  Schema.brand("LocalDateString")
106
106
  ) as unknown as Schema.Schema<LocalDateString>
107
107
 
108
108
  export const LocalTimeStringSchema = Schema.String.pipe(
109
- Schema.pattern(localTimePattern),
110
- Schema.filter(isValidLocalTimeString),
109
+ Schema.check(Schema.isPattern(localTimePattern)),
110
+ Schema.check(Schema.makeFilter((value) => isValidLocalTimeString(value))),
111
111
  Schema.brand("LocalTimeString")
112
112
  ) as unknown as Schema.Schema<LocalTimeString>
113
113
 
114
114
  export const OffsetTimeStringSchema = Schema.String.pipe(
115
- Schema.pattern(offsetTimePattern),
116
- Schema.filter(isValidOffsetTimeString),
115
+ Schema.check(Schema.isPattern(offsetTimePattern)),
116
+ Schema.check(Schema.makeFilter((value) => isValidOffsetTimeString(value))),
117
117
  Schema.brand("OffsetTimeString")
118
118
  ) as unknown as Schema.Schema<OffsetTimeString>
119
119
 
120
120
  export const LocalDateTimeStringSchema = Schema.String.pipe(
121
- Schema.pattern(localDateTimePattern),
122
- Schema.filter(isValidLocalDateTimeString),
121
+ Schema.check(Schema.isPattern(localDateTimePattern)),
122
+ Schema.check(Schema.makeFilter((value) => isValidLocalDateTimeString(value))),
123
123
  Schema.brand("LocalDateTimeString")
124
124
  ) as unknown as Schema.Schema<LocalDateTimeString>
125
125
 
126
126
  export const InstantStringSchema = Schema.String.pipe(
127
- Schema.pattern(instantPattern),
128
- Schema.filter(isValidInstantString),
127
+ Schema.check(Schema.isPattern(instantPattern)),
128
+ Schema.check(Schema.makeFilter((value) => isValidInstantString(value))),
129
129
  Schema.brand("InstantString")
130
130
  ) as unknown as Schema.Schema<InstantString>
131
131
 
@@ -177,32 +177,29 @@ export const isCanonicalDecimalString = (value: string): boolean => {
177
177
  }
178
178
 
179
179
  export const BigIntStringSchema = Schema.String.pipe(
180
- Schema.filter(isCanonicalBigIntString),
180
+ Schema.check(Schema.makeFilter((value) => isCanonicalBigIntString(value))),
181
181
  Schema.brand("BigIntString")
182
182
  ) as unknown as Schema.Schema<BigIntString>
183
183
 
184
184
  export const DecimalStringSchema = Schema.String.pipe(
185
- Schema.filter(isCanonicalDecimalString),
185
+ Schema.check(Schema.makeFilter((value) => isCanonicalDecimalString(value))),
186
186
  Schema.brand("DecimalString")
187
187
  ) as unknown as Schema.Schema<DecimalString>
188
188
 
189
189
  export const JsonValueSchema: Schema.Schema<JsonValue> = Schema.suspend(() =>
190
- Schema.Union(
190
+ Schema.Union([
191
191
  Schema.String,
192
- Schema.Number.pipe(Schema.finite()),
192
+ Schema.Number.check(Schema.isFinite()),
193
193
  Schema.Boolean,
194
194
  Schema.Null,
195
195
  Schema.Array(JsonValueSchema),
196
- Schema.Record({
197
- key: Schema.String,
198
- value: JsonValueSchema
199
- })
200
- )
196
+ Schema.Record(Schema.String, JsonValueSchema)
197
+ ])
201
198
  )
202
199
 
203
- export const JsonPrimitiveSchema: Schema.Schema<JsonPrimitive> = Schema.Union(
200
+ export const JsonPrimitiveSchema: Schema.Schema<JsonPrimitive> = Schema.Union([
204
201
  Schema.String,
205
- Schema.Number.pipe(Schema.finite()),
202
+ Schema.Number.check(Schema.isFinite()),
206
203
  Schema.Boolean,
207
204
  Schema.Null
208
- )
205
+ ])
@@ -73,7 +73,7 @@ export declare namespace DbType {
73
73
  export interface State<Runtime, Db extends DbType.Any, Nullable extends Nullability, Dialect extends string, Kind extends ScalarKind, Deps extends BindingId = never> {
74
74
  readonly runtime: Runtime;
75
75
  readonly dbType: Db;
76
- readonly runtimeSchema?: Schema.Schema.Any;
76
+ readonly runtimeSchema?: Schema.Top;
77
77
  readonly nullability: Nullable;
78
78
  readonly dialect: Dialect;
79
79
  readonly kind: Kind;
@@ -50,6 +50,7 @@ export declare namespace DbType {
50
50
  readonly runtime?: RuntimeTag
51
51
  readonly compareGroup?: string
52
52
  readonly castTargets?: readonly string[]
53
+ readonly implicitTargets?: readonly string[]
53
54
  readonly traits?: DatatypeTraits
54
55
  readonly driverValueMapping?: DriverValueMapping
55
56
  }
@@ -143,7 +144,12 @@ export interface DriverValueMapping {
143
144
  readonly jsonSelectSql?: (sql: string, dbType: DbType.Any) => string
144
145
  }
145
146
 
146
- export type DriverValueMappings = Readonly<Record<string, DriverValueMapping | undefined>>
147
+ export type DriverValueMappings = Readonly<Partial<Record<string, DriverValueMapping>>>
148
+
149
+ export type DriverValueMappingsFor<
150
+ Kind extends string,
151
+ Family extends string
152
+ > = Readonly<Partial<Record<Kind | Family | RuntimeTag, DriverValueMapping>>>
147
153
 
148
154
  /** Canonical static metadata stored on an expression. */
149
155
  export interface State<
@@ -156,7 +162,7 @@ export interface State<
156
162
  > {
157
163
  readonly runtime: Runtime
158
164
  readonly dbType: Db
159
- readonly runtimeSchema?: Schema.Schema.Any
165
+ readonly runtimeSchema?: Schema.Top
160
166
  readonly driverValueMapping?: DriverValueMapping
161
167
  readonly nullability: Nullable
162
168
  readonly dialect: Dialect