@zio.dev/zio-blocks 0.0.33 → 0.0.55

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 (215) hide show
  1. package/adr/2026-07-18-data-migration.md +123 -0
  2. package/guides/async-getting-started.md +687 -0
  3. package/guides/compile-time-resource-safety-with-scope.md +21 -16
  4. package/guides/getting-started-with-mux.md +1395 -0
  5. package/guides/query-dsl-extending.md +161 -102
  6. package/guides/query-dsl-fluent-builder.md +217 -157
  7. package/guides/query-dsl-reified-optics.md +12 -10
  8. package/guides/query-dsl-sql.md +640 -165
  9. package/guides/sql-checked-interpolation.md +173 -0
  10. package/guides/sql-transactions.md +286 -0
  11. package/guides/telemetry-guide.md +1130 -0
  12. package/guides/zio-schema-migration.md +29 -22
  13. package/index.md +248 -389
  14. package/package.json +1 -1
  15. package/plans/config-follow-up-prs.md +188 -0
  16. package/plans/config-pr-assessment-roadmap.md +310 -0
  17. package/reference/MuxDataFlow.jsx +250 -0
  18. package/reference/async.md +1499 -0
  19. package/reference/chunk.md +3533 -308
  20. package/reference/codegen/case-class.md +436 -0
  21. package/reference/codegen/emitter-config.md +383 -0
  22. package/reference/codegen/examples.md +664 -0
  23. package/reference/codegen/field.md +316 -0
  24. package/reference/codegen/index.md +317 -0
  25. package/reference/codegen/scala-emitter.md +392 -0
  26. package/reference/codegen/scala-file.md +276 -0
  27. package/reference/codegen/sealed-trait.md +408 -0
  28. package/reference/codegen/type-definition.md +340 -0
  29. package/reference/codegen/type-ref.md +201 -0
  30. package/reference/combinators.md +347 -117
  31. package/reference/config/config-decoder.md +460 -0
  32. package/reference/config/config-source.md +489 -0
  33. package/reference/config/errors.md +278 -0
  34. package/reference/config/flags.md +369 -0
  35. package/reference/config/formats.md +314 -0
  36. package/reference/config/index.md +304 -0
  37. package/reference/config/rollout.md +336 -0
  38. package/reference/context.md +9 -52
  39. package/reference/data-migration.md +269 -0
  40. package/reference/datastar/attributes.md +302 -0
  41. package/reference/datastar/events.md +234 -0
  42. package/reference/datastar/index.md +256 -0
  43. package/reference/datastar/signals.md +230 -0
  44. package/reference/datastar/sse.md +295 -0
  45. package/reference/datastar.md +346 -0
  46. package/reference/docs.md +1461 -345
  47. package/reference/endpoint/auth-type.md +146 -0
  48. package/reference/endpoint/bulk-creation.md +96 -0
  49. package/reference/endpoint/endpoint.md +297 -0
  50. package/reference/endpoint/http-codec.md +249 -0
  51. package/reference/endpoint/index.md +745 -0
  52. package/reference/endpoint/path-codec.md +225 -0
  53. package/reference/endpoint/route-pattern.md +194 -0
  54. package/reference/endpoint/route-tree.md +111 -0
  55. package/reference/endpoint/segment-codec.md +199 -0
  56. package/reference/html.md +1424 -0
  57. package/reference/htmx/attribute-values.md +359 -0
  58. package/reference/htmx/hx-encoding.md +111 -0
  59. package/reference/htmx/hx-params.md +204 -0
  60. package/reference/htmx/hx-swap.md +276 -0
  61. package/reference/htmx/hx-sync.md +251 -0
  62. package/reference/htmx/hx-target.md +314 -0
  63. package/reference/htmx/hx-trigger.md +457 -0
  64. package/reference/htmx/hx-url-update.md +239 -0
  65. package/reference/htmx/index.md +807 -0
  66. package/reference/htmx/response-headers.md +240 -0
  67. package/reference/http-model/headers.md +735 -0
  68. package/reference/http-model/index.md +49 -0
  69. package/reference/http-model/model.md +1517 -0
  70. package/reference/http-model/schema-codecs.md +522 -0
  71. package/reference/http-model/schema.md +750 -0
  72. package/reference/http-model/server-sent-event.md +341 -0
  73. package/reference/jwt.md +195 -0
  74. package/reference/maybe.md +943 -0
  75. package/reference/media-type.md +2 -2
  76. package/reference/mux.md +254 -0
  77. package/reference/mux.mdx +828 -0
  78. package/reference/openapi.md +1351 -0
  79. package/reference/projection.md +654 -0
  80. package/reference/resource-management/defer-handle.md +1 -1
  81. package/reference/resource-management/resource.md +31 -98
  82. package/reference/resource-management/scope.md +28 -220
  83. package/reference/resource-management/wire.md +5 -55
  84. package/reference/ringbuffer/MpmcDiagram.jsx +717 -0
  85. package/reference/ringbuffer/MpscDiagram.jsx +618 -0
  86. package/reference/ringbuffer/SpmcDiagram.jsx +680 -0
  87. package/reference/ringbuffer/SpscDiagram.jsx +677 -0
  88. package/reference/ringbuffer/advanced.mdx +109 -0
  89. package/reference/ringbuffer/index.mdx +145 -0
  90. package/reference/ringbuffer/mpmc.mdx +185 -0
  91. package/reference/ringbuffer/mpsc.mdx +164 -0
  92. package/reference/ringbuffer/spmc.mdx +108 -0
  93. package/reference/ringbuffer/spsc.mdx +416 -0
  94. package/reference/{allows.md → schema/allows.md} +4 -100
  95. package/reference/{binding-resolver.md → schema/binding-resolver.md} +1 -1
  96. package/reference/{binding.md → schema/binding.md} +3 -4
  97. package/reference/schema/built-in-codecs/avro.md +451 -0
  98. package/reference/schema/built-in-codecs/bson.md +510 -0
  99. package/reference/schema/built-in-codecs/csv.md +564 -0
  100. package/reference/schema/built-in-codecs/index.md +77 -0
  101. package/reference/schema/built-in-codecs/json/index.md +295 -0
  102. package/reference/schema/built-in-codecs/json/json-config.md +217 -0
  103. package/reference/{json-patch.md → schema/built-in-codecs/json/json-patch.md} +5 -5
  104. package/reference/{json-schema.md → schema/built-in-codecs/json/json-schema.md} +14 -47
  105. package/reference/schema/built-in-codecs/json/json-selection.md +322 -0
  106. package/reference/{json.md → schema/built-in-codecs/json/json.md} +32 -64
  107. package/reference/schema/built-in-codecs/messagepack.md +508 -0
  108. package/reference/schema/built-in-codecs/thrift.md +433 -0
  109. package/reference/schema/built-in-codecs/toon.md +1078 -0
  110. package/reference/{xml.md → schema/built-in-codecs/xml.md} +13 -9
  111. package/reference/schema/built-in-codecs/yaml.md +552 -0
  112. package/reference/{codec.md → schema/codec.md} +11 -11
  113. package/reference/{dynamic-optic.md → schema/dynamic-optic.md} +196 -5
  114. package/reference/{dynamic-schema.md → schema/dynamic-schema.md} +8 -8
  115. package/reference/schema/format.md +92 -0
  116. package/reference/schema/index.md +52 -0
  117. package/reference/schema/migration.md +297 -0
  118. package/reference/{modifier.md → schema/modifier.md} +58 -7
  119. package/reference/{optics.md → schema/optics.md} +2 -2
  120. package/reference/{patch.md → schema/patch.md} +1 -1
  121. package/{path-interpolator.md → reference/schema/path-interpolator.md} +167 -72
  122. package/reference/schema/reflect-transformer.md +140 -0
  123. package/reference/{schema-evolution → schema/schema-evolution}/as.md +8 -8
  124. package/reference/{schema-evolution → schema/schema-evolution}/index.md +2 -2
  125. package/reference/{schema-evolution → schema/schema-evolution}/into.md +8 -8
  126. package/reference/{schema-expr.md → schema/schema-expr.md} +110 -175
  127. package/reference/schema/schema-search.md +263 -0
  128. package/reference/{schema.md → schema/schema.md} +22 -2
  129. package/reference/{structural-types.md → schema/structural-types.md} +1 -1
  130. package/reference/{type-class-derivation.md → schema/type-class-derivation.md} +63 -1
  131. package/reference/smithy.md +1032 -0
  132. package/reference/sql/db-codec-deriver.md +71 -0
  133. package/reference/sql/db-codec.md +687 -0
  134. package/reference/sql/db-con.md +271 -0
  135. package/reference/sql/db-connection.md +153 -0
  136. package/reference/sql/db-param-writer.md +77 -0
  137. package/reference/sql/db-param.md +66 -0
  138. package/reference/sql/db-result-reader.md +148 -0
  139. package/reference/sql/db-tx.md +114 -0
  140. package/reference/sql/db-value.md +41 -0
  141. package/reference/sql/ddl.md +85 -0
  142. package/reference/sql/frag.md +288 -0
  143. package/reference/sql/index.md +341 -0
  144. package/reference/sql/repo.md +600 -0
  145. package/reference/sql/sql-dialect.md +73 -0
  146. package/reference/sql/sql-logger.md +62 -0
  147. package/reference/sql/sql-name-mapper.md +70 -0
  148. package/reference/sql/table-metadata.md +134 -0
  149. package/reference/sql/table.md +448 -0
  150. package/reference/sql/transactor-zio.md +399 -0
  151. package/reference/sql/transactor.md +363 -0
  152. package/reference/sql-zio.md +112 -0
  153. package/reference/streams/core/index.md +32 -0
  154. package/reference/streams/core/pipeline.md +854 -0
  155. package/reference/streams/core/sink.md +1404 -0
  156. package/reference/streams/core/stream.md +3236 -0
  157. package/reference/streams/execution-and-compatibility/async-execution.md +822 -0
  158. package/reference/streams/execution-and-compatibility/index.md +35 -0
  159. package/reference/streams/execution-and-compatibility/platform-differences.md +297 -0
  160. package/reference/streams/execution-and-compatibility/scala-2-compatibility.md +88 -0
  161. package/reference/streams/execution-and-compatibility/zero-boxing.md +393 -0
  162. package/reference/streams/index.md +726 -0
  163. package/reference/streams/primitives/index.md +30 -0
  164. package/reference/streams/primitives/reader.md +1992 -0
  165. package/reference/streams/primitives/writer.md +1201 -0
  166. package/reference/telemetry/common/any-value.md +90 -0
  167. package/reference/telemetry/common/attribute-key.md +87 -0
  168. package/reference/telemetry/common/attributes.md +118 -0
  169. package/reference/telemetry/common/index.md +39 -0
  170. package/reference/telemetry/common/instrumentation-scope.md +24 -0
  171. package/reference/telemetry/common/resource.md +34 -0
  172. package/reference/telemetry/index.md +311 -0
  173. package/reference/telemetry/logging/index.md +197 -0
  174. package/reference/telemetry/logging/log-enrichment.md +72 -0
  175. package/reference/telemetry/logging/log-formatter.md +100 -0
  176. package/reference/telemetry/logging/log-record-processor.md +56 -0
  177. package/reference/telemetry/logging/log-record.md +44 -0
  178. package/reference/telemetry/logging/log-writer.md +64 -0
  179. package/reference/telemetry/logging/logger-provider.md +142 -0
  180. package/reference/telemetry/logging/logger.md +83 -0
  181. package/reference/telemetry/logging/severity.md +62 -0
  182. package/reference/telemetry/metrics/index.md +150 -0
  183. package/reference/telemetry/metrics/instruments.md +183 -0
  184. package/reference/telemetry/metrics/labeled-instruments.md +74 -0
  185. package/reference/telemetry/metrics/meter-provider.md +76 -0
  186. package/reference/telemetry/metrics/meter.md +98 -0
  187. package/reference/telemetry/metrics/metric-data.md +57 -0
  188. package/reference/telemetry/otel/custom-exporter.md +216 -0
  189. package/reference/telemetry/otel/index.md +212 -0
  190. package/reference/telemetry/tracing/index.md +155 -0
  191. package/reference/telemetry/tracing/sampler.md +89 -0
  192. package/reference/telemetry/tracing/span-builder.md +57 -0
  193. package/reference/telemetry/tracing/span-context.md +39 -0
  194. package/reference/telemetry/tracing/span-data.md +32 -0
  195. package/reference/telemetry/tracing/span-kind.md +55 -0
  196. package/reference/telemetry/tracing/span-processor.md +53 -0
  197. package/reference/telemetry/tracing/span-status.md +47 -0
  198. package/reference/telemetry/tracing/span.md +117 -0
  199. package/reference/telemetry/tracing/tracer-provider.md +91 -0
  200. package/reference/telemetry/tracing/tracer.md +52 -0
  201. package/reference/typeid.md +5 -83
  202. package/sidebars.js +376 -43
  203. package/undocumented-report.md +528 -270
  204. package/reference/formats.md +0 -694
  205. package/reference/http-model.md +0 -1716
  206. package/reference/streams.md +0 -989
  207. package/ringbuffer.md +0 -249
  208. /package/reference/{json-differ.md → schema/built-in-codecs/json/json-differ.md} +0 -0
  209. /package/reference/{dynamic-value.md → schema/dynamic-value.md} +0 -0
  210. /package/reference/{lazy.md → schema/lazy.md} +0 -0
  211. /package/reference/{reflect.md → schema/reflect.md} +0 -0
  212. /package/reference/{registers.md → schema/registers.md} +0 -0
  213. /package/reference/{schema-error.md → schema/schema-error.md} +0 -0
  214. /package/reference/{syntax.md → schema/syntax.md} +0 -0
  215. /package/reference/{validation.md → schema/validation.md} +0 -0
@@ -57,12 +57,12 @@ In this guide, we solve both problems: bridge extensions eliminate `.toExpr`, an
57
57
  This guide builds on [Part 1: Expressions](./query-dsl-reified-optics.md), [Part 2: SQL Generation](./query-dsl-sql.md), and [Part 3: Extending the Expression Language](./query-dsl-extending.md).
58
58
 
59
59
  ```scala
60
- libraryDependencies += "dev.zio" %% "zio-blocks-schema" % "0.0.33"
60
+ libraryDependencies += "dev.zio" %% "zio-blocks-schema" % "0.0.55"
61
61
  ```
62
62
 
63
63
  ## Domain Setup
64
64
 
65
- We carry forward the product catalog domain and the Part 3 independent `Expr` ADT. The key additions in Part 4 are bridge extension methods, schema-driven table names, and statement builder types. The `Expr` ADT used here is the same independent design from Part 3.
65
+ We carry forward the product catalog domain and the Part 3 independent `Expr` ADT. The key additions in Part 4 are bridge extension methods, schema-driven table names, and statement builder types. As in Part 3, the public expression layer stays typed: built-in logic remains `SchemaExpr`, and the SQL builder only drops to the dynamic AST inside interpreter helpers.
66
66
 
67
67
  ```scala
68
68
  import zio.blocks.schema._
@@ -135,6 +135,7 @@ object OrderItem extends CompanionOptics[OrderItem] {
135
135
  sealed trait Expr[S, A]
136
136
 
137
137
  object Expr {
138
+ final case class Builtin[S, A](schemaExpr: SchemaExpr[S, A]) extends Expr[S, A]
138
139
  final case class Column[S, A](optic: Optic[S, A]) extends Expr[S, A]
139
140
  final case class Lit[S, A](value: A, schema: Schema[A]) extends Expr[S, A]
140
141
 
@@ -155,38 +156,7 @@ object Expr {
155
156
  def col[S, A](optic: Optic[S, A]): Expr[S, A] = Column(optic)
156
157
  def lit[S, A](value: A)(implicit schema: Schema[A]): Expr[S, A] = Lit(value, schema)
157
158
 
158
- def fromSchemaExpr[S, A](se: SchemaExpr[S, A]): Expr[S, A] = {
159
- val result = se match {
160
- case SchemaExpr.Optic(optic) => Column(optic)
161
- case l: SchemaExpr.Literal[_, _] => Lit(l.value, l.schema)
162
- case SchemaExpr.Relational(l, r, op) =>
163
- val relOp = op match {
164
- case SchemaExpr.RelationalOperator.Equal => RelOp.Equal
165
- case SchemaExpr.RelationalOperator.NotEqual => RelOp.NotEqual
166
- case SchemaExpr.RelationalOperator.LessThan => RelOp.LessThan
167
- case SchemaExpr.RelationalOperator.LessThanOrEqual => RelOp.LessThanOrEqual
168
- case SchemaExpr.RelationalOperator.GreaterThan => RelOp.GreaterThan
169
- case SchemaExpr.RelationalOperator.GreaterThanOrEqual => RelOp.GreaterThanOrEqual
170
- }
171
- Relational(fromSchemaExpr(l), fromSchemaExpr(r), relOp)
172
- case SchemaExpr.Logical(l, r, op) => op match {
173
- case SchemaExpr.LogicalOperator.And => And(fromSchemaExpr(l), fromSchemaExpr(r))
174
- case SchemaExpr.LogicalOperator.Or => Or(fromSchemaExpr(l), fromSchemaExpr(r))
175
- }
176
- case SchemaExpr.Not(inner) => Not(fromSchemaExpr(inner))
177
- case SchemaExpr.Arithmetic(l, r, op, _) =>
178
- val arithOp = op match {
179
- case SchemaExpr.ArithmeticOperator.Add => ArithOp.Add
180
- case SchemaExpr.ArithmeticOperator.Subtract => ArithOp.Subtract
181
- case SchemaExpr.ArithmeticOperator.Multiply => ArithOp.Multiply
182
- }
183
- Arithmetic(fromSchemaExpr(l), fromSchemaExpr(r), arithOp)
184
- case SchemaExpr.StringConcat(l, r) => StringConcat(fromSchemaExpr(l), fromSchemaExpr(r))
185
- case SchemaExpr.StringRegexMatch(regex, string) => StringRegexMatch(fromSchemaExpr(regex), fromSchemaExpr(string))
186
- case SchemaExpr.StringLength(string) => StringLength(fromSchemaExpr(string))
187
- }
188
- result.asInstanceOf[Expr[S, A]]
189
- }
159
+ def fromSchemaExpr[S, A](se: SchemaExpr[S, A]): Expr[S, A] = Builtin(se)
190
160
  }
191
161
 
192
162
  sealed trait RelOp
@@ -211,6 +181,9 @@ object ArithOp {
211
181
  def columnName(optic: zio.blocks.schema.Optic[_, _]): String =
212
182
  optic.toDynamic.nodes.collect { case f: DynamicOptic.Node.Field => f.name }.mkString("_")
213
183
 
184
+ def columnName(path: DynamicOptic): String =
185
+ path.nodes.collect { case f: DynamicOptic.Node.Field => f.name }.mkString("_")
186
+
214
187
  def sqlLiteral[A](value: A, schema: Schema[A]): String = {
215
188
  val dv = schema.toDynamicValue(value)
216
189
  dv match {
@@ -223,6 +196,22 @@ def sqlLiteral[A](value: A, schema: Schema[A]): String = {
223
196
  }
224
197
  }
225
198
 
199
+ def sqlLiteralDV(dv: DynamicValue): String = dv match {
200
+ case DynamicValue.Primitive(pv) =>
201
+ pv match {
202
+ case PrimitiveValue.String(s) => s"'${s.replace("'", "''")}'"
203
+ case PrimitiveValue.Boolean(b) => if (b) "TRUE" else "FALSE"
204
+ case PrimitiveValue.Int(n) => n.toString
205
+ case PrimitiveValue.Long(n) => n.toString
206
+ case PrimitiveValue.Double(n) => n.toString
207
+ case PrimitiveValue.Float(n) => n.toString
208
+ case PrimitiveValue.Short(n) => n.toString
209
+ case PrimitiveValue.Byte(n) => n.toString
210
+ case other => other.toString
211
+ }
212
+ case other => other.toString
213
+ }
214
+
226
215
  // --- Optic extension methods ---
227
216
 
228
217
  implicit final class OpticExprOps[S, A](private val optic: Optic[S, A]) {
@@ -255,8 +244,9 @@ implicit final class SchemaExprBooleanBridge[S](private val self: SchemaExpr[S,
255
244
  // --- Single unified SQL interpreter ---
256
245
 
257
246
  def exprToSql[S, A](expr: Expr[S, A]): String = expr match {
258
- case Expr.Column(optic) => columnName(optic)
259
- case Expr.Lit(value, schema) => sqlLiteral(value, schema)
247
+ case Expr.Builtin(schemaExpr) => schemaExprToSql(schemaExpr)
248
+ case Expr.Column(optic) => columnName(optic)
249
+ case Expr.Lit(value, schema) => sqlLiteral(value, schema)
260
250
  case Expr.Relational(left, right, op) =>
261
251
  val sqlOp = op match {
262
252
  case RelOp.Equal => "="
@@ -287,6 +277,46 @@ def exprToSql[S, A](expr: Expr[S, A]): String = expr match {
287
277
  case Expr.IsNull(e) => s"${exprToSql(e)} IS NULL"
288
278
  case Expr.Like(e, pattern) => s"${exprToSql(e)} LIKE '${pattern.replace("'", "''")}'"
289
279
  }
280
+
281
+ def schemaExprToSql[S, A](expr: SchemaExpr[S, A]): String =
282
+ toSqlDynamic(expr.dynamic)
283
+
284
+ def toSqlDynamic(expr: DynamicSchemaExpr): String = expr match {
285
+ case DynamicSchemaExpr.Select(path) => columnName(path)
286
+ case DynamicSchemaExpr.Literal(value, _) => sqlLiteralDV(value)
287
+ case DynamicSchemaExpr.Relational(left, right, op) =>
288
+ val sqlOp = op match {
289
+ case DynamicSchemaExpr.RelationalOperator.Equal => "="
290
+ case DynamicSchemaExpr.RelationalOperator.NotEqual => "<>"
291
+ case DynamicSchemaExpr.RelationalOperator.LessThan => "<"
292
+ case DynamicSchemaExpr.RelationalOperator.LessThanOrEqual => "<="
293
+ case DynamicSchemaExpr.RelationalOperator.GreaterThan => ">"
294
+ case DynamicSchemaExpr.RelationalOperator.GreaterThanOrEqual => ">="
295
+ }
296
+ s"(${toSqlDynamic(left)} $sqlOp ${toSqlDynamic(right)})"
297
+ case DynamicSchemaExpr.Logical(left, right, op) =>
298
+ val sqlOp = op match {
299
+ case DynamicSchemaExpr.LogicalOperator.And => "AND"
300
+ case DynamicSchemaExpr.LogicalOperator.Or => "OR"
301
+ }
302
+ s"(${toSqlDynamic(left)} $sqlOp ${toSqlDynamic(right)})"
303
+ case DynamicSchemaExpr.Not(inner) =>
304
+ s"NOT (${toSqlDynamic(inner)})"
305
+ case DynamicSchemaExpr.Arithmetic(left, right, op, _) =>
306
+ val sqlOp = op match {
307
+ case DynamicSchemaExpr.ArithmeticOperator.Add => "+"
308
+ case DynamicSchemaExpr.ArithmeticOperator.Subtract => "-"
309
+ case DynamicSchemaExpr.ArithmeticOperator.Multiply => "*"
310
+ case _ => "?"
311
+ }
312
+ s"(${toSqlDynamic(left)} $sqlOp ${toSqlDynamic(right)})"
313
+ case DynamicSchemaExpr.StringConcat(left, right) =>
314
+ s"CONCAT(${toSqlDynamic(left)}, ${toSqlDynamic(right)})"
315
+ case DynamicSchemaExpr.StringRegexMatch(regex, string) =>
316
+ s"(${toSqlDynamic(string)} LIKE ${toSqlDynamic(regex)})"
317
+ case DynamicSchemaExpr.StringLength(string) =>
318
+ s"LENGTH(${toSqlDynamic(string)})"
319
+ }
290
320
  ```
291
321
 
292
322
  ## Seamless Condition Composition
@@ -380,7 +410,7 @@ exprToSql(condition)
380
410
  // res0: String = "((((price BETWEEN 10.0 AND 500.0) AND (category = 'Electronics')) AND (rating >= 4)) AND name LIKE 'L%')"
381
411
  ```
382
412
 
383
- The first two `&&` calls stay in `SchemaExpr` land (direct method). The third `&&` encounters `between` (returns `Expr`), triggering the bridge. From that point on, everything is `Expr`.
413
+ The first `&&` already involves `between`, so the bridge activates as soon as an `Expr` enters the chain. From that point on, the whole condition is represented as `Expr`.
384
414
 
385
415
  ## Schema-Driven Table Names
386
416
 
@@ -496,51 +526,51 @@ val basicSelect = select(Product.table)
496
526
  // table = Table("products"),
497
527
  // columnList = List("name", "price"),
498
528
  // whereExpr = Some(
499
- // Relational(
500
- // left = Column(
501
- // LensImpl(
502
- // sources = Array(
503
- // Record(
504
- // fields = Vector(
505
- // Term(
506
- // name = "name",
507
- // value = Primitive(
508
- // primitiveType = String(None),
509
- // typeId = String,
510
- // primitiveBinding = Primitive(),
511
- // doc = Doc(blocks = IndexedSeq(), metadata = Map()),
512
- // modifiers = List(),
513
- // storedDefaultValue = None,
514
- // storedExamples = List()
515
- // ),
529
+ // Builtin(
530
+ // SchemaExpr(
531
+ // dynamic = Relational(
532
+ // left = Select(DynamicOptic(ArraySeq(Field("inStock")))),
533
+ // right = Literal(
534
+ // value = Primitive(Boolean(true)),
535
+ // schema = Schema(
536
+ // Primitive(
537
+ // primitiveType = Boolean(None),
538
+ // typeId = Boolean,
539
+ // primitiveBinding = Primitive(),
540
+ // doc = Doc(blocks = IndexedSeq(), metadata = Map()),
541
+ // modifiers = List(),
542
+ // storedDefaultValue = None,
543
+ // storedExamples = List()
544
+ // )
545
+ // )
546
+ // ),
547
+ // operator = Equal
548
+ // ),
549
+ // inputSchema = Schema(
550
+ // Record(
551
+ // fields = Vector(
552
+ // Term(
553
+ // name = "name",
554
+ // value = Primitive(
555
+ // primitiveType = String(None),
556
+ // typeId = String,
557
+ // primitiveBinding = Primitive(),
516
558
  // doc = Doc(blocks = IndexedSeq(), metadata = Map()),
517
- // modifiers = List()
559
+ // modifiers = List(),
560
+ // storedDefaultValue = None,
561
+ // storedExamples = List()
518
562
  // ),
519
- // Term(
520
- // name = "price",
521
- // value = Primitive(
522
- // primitiveType = Double(None),
523
- // typeId = Double,
524
- // primitiveBinding = Primitive(),
525
- // doc = Doc(blocks = IndexedSeq(), metadata = Map()),
526
- // modifiers = List(),
527
- // storedDefaultValue = None,
528
- // storedExamples = List()
529
- // ),
563
+ // doc = Doc(blocks = IndexedSeq(), metadata = Map()),
564
+ // modifiers = List()
565
+ // ),
566
+ // Term(
567
+ // name = "price",
568
+ // value = Primitive(
569
+ // primitiveType = Double(None),
570
+ // typeId = Double,
571
+ // primitiveBinding = Primitive(),
530
572
  // doc = Doc(blocks = IndexedSeq(), metadata = Map()),
531
- // modifiers = List()
532
- // ),
533
- // Term(
534
- // name = "category",
535
- // value = Primitive(
536
- // primitiveType = String(None),
537
- // typeId = String,
538
- // primitiveBinding = Primitive(),
539
- // doc = Doc(blocks = IndexedSeq(), metadata = Map()),
540
- // modifiers = List(),
541
- // storedDefaultValue = None,
542
- // storedExamples = List()
543
- // ),
573
+ // modifiers = List(),
544
574
  // ...
545
575
 
546
576
  renderSelect(basicSelect)
@@ -723,48 +753,48 @@ val multiUpdate =
723
753
  // Assignment(column = "inStock", value = "FALSE")
724
754
  // ),
725
755
  // whereExpr = Some(
726
- // Relational(
727
- // left = Column(
728
- // LensImpl(
729
- // sources = Array(
730
- // Record(
731
- // fields = Vector(
732
- // Term(
733
- // name = "name",
734
- // value = Primitive(
735
- // primitiveType = String(None),
736
- // typeId = String,
737
- // primitiveBinding = Primitive(),
738
- // doc = Doc(blocks = IndexedSeq(), metadata = Map()),
739
- // modifiers = List(),
740
- // storedDefaultValue = None,
741
- // storedExamples = List()
742
- // ),
743
- // doc = Doc(blocks = IndexedSeq(), metadata = Map()),
744
- // modifiers = List()
745
- // ),
746
- // Term(
747
- // name = "price",
748
- // value = Primitive(
749
- // primitiveType = Double(None),
750
- // typeId = Double,
751
- // primitiveBinding = Primitive(),
752
- // doc = Doc(blocks = IndexedSeq(), metadata = Map()),
753
- // modifiers = List(),
754
- // storedDefaultValue = None,
755
- // storedExamples = List()
756
- // ),
756
+ // Builtin(
757
+ // SchemaExpr(
758
+ // dynamic = Relational(
759
+ // left = Select(DynamicOptic(ArraySeq(Field("category")))),
760
+ // right = Literal(
761
+ // value = Primitive(String("Clearance")),
762
+ // schema = Schema(
763
+ // Primitive(
764
+ // primitiveType = String(None),
765
+ // typeId = String,
766
+ // primitiveBinding = Primitive(),
767
+ // doc = Doc(blocks = IndexedSeq(), metadata = Map()),
768
+ // modifiers = List(),
769
+ // storedDefaultValue = None,
770
+ // storedExamples = List()
771
+ // )
772
+ // )
773
+ // ),
774
+ // operator = Equal
775
+ // ),
776
+ // inputSchema = Schema(
777
+ // Record(
778
+ // fields = Vector(
779
+ // Term(
780
+ // name = "name",
781
+ // value = Primitive(
782
+ // primitiveType = String(None),
783
+ // typeId = String,
784
+ // primitiveBinding = Primitive(),
757
785
  // doc = Doc(blocks = IndexedSeq(), metadata = Map()),
758
- // modifiers = List()
786
+ // modifiers = List(),
787
+ // storedDefaultValue = None,
788
+ // storedExamples = List()
759
789
  // ),
760
- // Term(
761
- // name = "category",
762
- // value = Primitive(
763
- // primitiveType = String(None),
764
- // typeId = String,
765
- // primitiveBinding = Primitive(),
766
- // doc = Doc(blocks = IndexedSeq(), metadata = Map()),
767
- // modifiers = List(),
790
+ // doc = Doc(blocks = IndexedSeq(), metadata = Map()),
791
+ // modifiers = List()
792
+ // ),
793
+ // Term(
794
+ // name = "price",
795
+ // value = Primitive(
796
+ // primitiveType = Double(None),
797
+ // typeId = Double,
768
798
  // ...
769
799
 
770
800
  renderUpdate(multiUpdate)
@@ -901,7 +931,7 @@ For batch inserts, create one `InsertStmt` per row and render each separately. T
901
931
 
902
932
  ## Putting It Together
903
933
 
904
- Here is a complete example combining schema-driven table names, bridge extensions, all four statement builders, and the renderers. The `Expr` ADT, extension methods, SQL rendering, and builder types are defined in `Common.scala` and `package.scala` — the usage code stays focused on building queries:
934
+ Here is a complete example combining schema-driven table names, bridge extensions, all four statement builders, and the renderers. The `Expr` ADT, extension methods, SQL rendering, and builder types are defined in `Common.scala` and `package.scala` — the usage code stays focused on building queries with typed optics and `SchemaExpr`, not on manipulating `Dynamic*` nodes directly:
905
935
 
906
936
  ```scala
907
937
  import zio.blocks.schema._
@@ -972,6 +1002,7 @@ object OrderItem extends CompanionOptics[OrderItem] {
972
1002
  sealed trait Expr[S, A]
973
1003
 
974
1004
  object Expr {
1005
+ final case class Builtin[S, A](schemaExpr: SchemaExpr[S, A]) extends Expr[S, A]
975
1006
  final case class Column[S, A](optic: Optic[S, A]) extends Expr[S, A]
976
1007
  final case class Lit[S, A](value: A, schema: Schema[A]) extends Expr[S, A]
977
1008
 
@@ -992,38 +1023,7 @@ object Expr {
992
1023
  def col[S, A](optic: Optic[S, A]): Expr[S, A] = Column(optic)
993
1024
  def lit[S, A](value: A)(implicit schema: Schema[A]): Expr[S, A] = Lit(value, schema)
994
1025
 
995
- def fromSchemaExpr[S, A](se: SchemaExpr[S, A]): Expr[S, A] = {
996
- val result = se match {
997
- case SchemaExpr.Optic(optic) => Column(optic)
998
- case l: SchemaExpr.Literal[_, _] => Lit(l.value, l.schema)
999
- case SchemaExpr.Relational(l, r, op) =>
1000
- val relOp = op match {
1001
- case SchemaExpr.RelationalOperator.Equal => RelOp.Equal
1002
- case SchemaExpr.RelationalOperator.NotEqual => RelOp.NotEqual
1003
- case SchemaExpr.RelationalOperator.LessThan => RelOp.LessThan
1004
- case SchemaExpr.RelationalOperator.LessThanOrEqual => RelOp.LessThanOrEqual
1005
- case SchemaExpr.RelationalOperator.GreaterThan => RelOp.GreaterThan
1006
- case SchemaExpr.RelationalOperator.GreaterThanOrEqual => RelOp.GreaterThanOrEqual
1007
- }
1008
- Relational(fromSchemaExpr(l), fromSchemaExpr(r), relOp)
1009
- case SchemaExpr.Logical(l, r, op) => op match {
1010
- case SchemaExpr.LogicalOperator.And => And(fromSchemaExpr(l), fromSchemaExpr(r))
1011
- case SchemaExpr.LogicalOperator.Or => Or(fromSchemaExpr(l), fromSchemaExpr(r))
1012
- }
1013
- case SchemaExpr.Not(inner) => Not(fromSchemaExpr(inner))
1014
- case SchemaExpr.Arithmetic(l, r, op, _) =>
1015
- val arithOp = op match {
1016
- case SchemaExpr.ArithmeticOperator.Add => ArithOp.Add
1017
- case SchemaExpr.ArithmeticOperator.Subtract => ArithOp.Subtract
1018
- case SchemaExpr.ArithmeticOperator.Multiply => ArithOp.Multiply
1019
- }
1020
- Arithmetic(fromSchemaExpr(l), fromSchemaExpr(r), arithOp)
1021
- case SchemaExpr.StringConcat(l, r) => StringConcat(fromSchemaExpr(l), fromSchemaExpr(r))
1022
- case SchemaExpr.StringRegexMatch(regex, string) => StringRegexMatch(fromSchemaExpr(regex), fromSchemaExpr(string))
1023
- case SchemaExpr.StringLength(string) => StringLength(fromSchemaExpr(string))
1024
- }
1025
- result.asInstanceOf[Expr[S, A]]
1026
- }
1026
+ def fromSchemaExpr[S, A](se: SchemaExpr[S, A]): Expr[S, A] = Builtin(se)
1027
1027
  }
1028
1028
 
1029
1029
  sealed trait RelOp
@@ -1075,6 +1075,9 @@ implicit final class SchemaExprBooleanBridge[S](private val self: SchemaExpr[S,
1075
1075
  def columnName(optic: zio.blocks.schema.Optic[_, _]): String =
1076
1076
  optic.toDynamic.nodes.collect { case f: DynamicOptic.Node.Field => f.name }.mkString("_")
1077
1077
 
1078
+ def columnName(path: DynamicOptic): String =
1079
+ path.nodes.collect { case f: DynamicOptic.Node.Field => f.name }.mkString("_")
1080
+
1078
1081
  def sqlLiteral[A](value: A, schema: Schema[A]): String = {
1079
1082
  val dv = schema.toDynamicValue(value)
1080
1083
  dv match {
@@ -1087,9 +1090,26 @@ def sqlLiteral[A](value: A, schema: Schema[A]): String = {
1087
1090
  }
1088
1091
  }
1089
1092
 
1093
+ def sqlLiteralDV(dv: DynamicValue): String = dv match {
1094
+ case DynamicValue.Primitive(pv) =>
1095
+ pv match {
1096
+ case PrimitiveValue.String(s) => s"'${s.replace("'", "''")}'"
1097
+ case PrimitiveValue.Boolean(b) => if (b) "TRUE" else "FALSE"
1098
+ case PrimitiveValue.Int(n) => n.toString
1099
+ case PrimitiveValue.Long(n) => n.toString
1100
+ case PrimitiveValue.Double(n) => n.toString
1101
+ case PrimitiveValue.Float(n) => n.toString
1102
+ case PrimitiveValue.Short(n) => n.toString
1103
+ case PrimitiveValue.Byte(n) => n.toString
1104
+ case other => other.toString
1105
+ }
1106
+ case other => other.toString
1107
+ }
1108
+
1090
1109
  def exprToSql[S, A](expr: Expr[S, A]): String = expr match {
1091
- case Expr.Column(optic) => columnName(optic)
1092
- case Expr.Lit(value, schema) => sqlLiteral(value, schema)
1110
+ case Expr.Builtin(schemaExpr) => schemaExprToSql(schemaExpr)
1111
+ case Expr.Column(optic) => columnName(optic)
1112
+ case Expr.Lit(value, schema) => sqlLiteral(value, schema)
1093
1113
  case Expr.Relational(left, right, op) =>
1094
1114
  val sqlOp = op match {
1095
1115
  case RelOp.Equal => "="
@@ -1121,6 +1141,46 @@ def exprToSql[S, A](expr: Expr[S, A]): String = expr match {
1121
1141
  case Expr.Like(e, pattern) => s"${exprToSql(e)} LIKE '${pattern.replace("'", "''")}'"
1122
1142
  }
1123
1143
 
1144
+ def schemaExprToSql[S, A](expr: SchemaExpr[S, A]): String =
1145
+ toSqlDynamic(expr.dynamic)
1146
+
1147
+ def toSqlDynamic(expr: DynamicSchemaExpr): String = expr match {
1148
+ case DynamicSchemaExpr.Select(path) => columnName(path)
1149
+ case DynamicSchemaExpr.Literal(value, _) => sqlLiteralDV(value)
1150
+ case DynamicSchemaExpr.Relational(left, right, op) =>
1151
+ val sqlOp = op match {
1152
+ case DynamicSchemaExpr.RelationalOperator.Equal => "="
1153
+ case DynamicSchemaExpr.RelationalOperator.NotEqual => "<>"
1154
+ case DynamicSchemaExpr.RelationalOperator.LessThan => "<"
1155
+ case DynamicSchemaExpr.RelationalOperator.LessThanOrEqual => "<="
1156
+ case DynamicSchemaExpr.RelationalOperator.GreaterThan => ">"
1157
+ case DynamicSchemaExpr.RelationalOperator.GreaterThanOrEqual => ">="
1158
+ }
1159
+ s"(${toSqlDynamic(left)} $sqlOp ${toSqlDynamic(right)})"
1160
+ case DynamicSchemaExpr.Logical(left, right, op) =>
1161
+ val sqlOp = op match {
1162
+ case DynamicSchemaExpr.LogicalOperator.And => "AND"
1163
+ case DynamicSchemaExpr.LogicalOperator.Or => "OR"
1164
+ }
1165
+ s"(${toSqlDynamic(left)} $sqlOp ${toSqlDynamic(right)})"
1166
+ case DynamicSchemaExpr.Not(inner) =>
1167
+ s"NOT (${toSqlDynamic(inner)})"
1168
+ case DynamicSchemaExpr.Arithmetic(left, right, op, _) =>
1169
+ val sqlOp = op match {
1170
+ case DynamicSchemaExpr.ArithmeticOperator.Add => "+"
1171
+ case DynamicSchemaExpr.ArithmeticOperator.Subtract => "-"
1172
+ case DynamicSchemaExpr.ArithmeticOperator.Multiply => "*"
1173
+ case _ => "?"
1174
+ }
1175
+ s"(${toSqlDynamic(left)} $sqlOp ${toSqlDynamic(right)})"
1176
+ case DynamicSchemaExpr.StringConcat(left, right) =>
1177
+ s"CONCAT(${toSqlDynamic(left)}, ${toSqlDynamic(right)})"
1178
+ case DynamicSchemaExpr.StringRegexMatch(regex, string) =>
1179
+ s"(${toSqlDynamic(string)} LIKE ${toSqlDynamic(regex)})"
1180
+ case DynamicSchemaExpr.StringLength(string) =>
1181
+ s"LENGTH(${toSqlDynamic(string)})"
1182
+ }
1183
+
1124
1184
  // --- Statement builders ---
1125
1185
 
1126
1186
  sealed trait SortOrder
@@ -1231,7 +1291,7 @@ val q = select(Product.table)
1231
1291
  .where(
1232
1292
  Product.category.in("Electronics", "Books") &&
1233
1293
  Product.price.between(10.0, 500.0) &&
1234
- (Product.rating >= 4).toExpr
1294
+ (Product.rating >= 4)
1235
1295
  )
1236
1296
  .orderBy(Product.price, SortOrder.Desc)
1237
1297
  .limit(20)
@@ -1281,7 +1341,7 @@ println(renderSelect(orderQuery))
1281
1341
  - **[Part 1: Expressions](./query-dsl-reified-optics.md)** — Building query expressions with reified optics
1282
1342
  - **[Part 2: SQL Generation](./query-dsl-sql.md)** — Translating built-in expressions to SQL
1283
1343
  - **[Part 3: Extending the Expression Language](./query-dsl-extending.md)** — Adding custom operators beyond SchemaExpr
1284
- - **[SchemaExpr Reference](../reference/schema-expr.md)** — Full API coverage of expression types
1285
- - **[Optics Reference](../reference/optics.md)** — Lens, Prism, Optional, and Traversal
1344
+ - **[SchemaExpr Reference](../reference/schema/schema-expr.md)** — Full API coverage of expression types
1345
+ - **[Optics Reference](../reference/schema/optics.md)** — Lens, Prism, Optional, and Traversal
1286
1346
 
1287
1347
  The builder pattern shown here extends naturally to JOIN clauses (using lenses from multiple table types), subqueries (nesting `SelectStmt` in WHERE conditions), and parameterized queries (collecting `?` placeholders and parameter values during rendering). Each of these builds on the same foundation: optics for column names, `Expr` for conditions, and immutable builders for statement structure.
@@ -43,14 +43,14 @@ In this guide, we'll solve this by using ZIO Blocks' `SchemaExpr` and reified op
43
43
  Add the ZIO Blocks Schema dependency:
44
44
 
45
45
  ```scala
46
- libraryDependencies += "dev.zio" %% "zio-blocks-schema" % "0.0.33"
46
+ libraryDependencies += "dev.zio" %% "zio-blocks-schema" % "0.0.55"
47
47
  ```
48
48
 
49
49
  ```scala
50
50
  import zio.blocks.schema._
51
51
  ```
52
52
 
53
- This guide assumes familiarity with ZIO Blocks schemas and basic optics. See the [Schema](../reference/schema.md) and [Optics](../reference/optics.md) reference pages for background.
53
+ This guide assumes familiarity with ZIO Blocks schemas and basic optics. See the [Schema](../reference/schema/schema.md) and [Optics](../reference/schema/optics.md) reference pages for background.
54
54
 
55
55
  ## Defining Your Domain
56
56
 
@@ -233,9 +233,9 @@ val nameLength: SchemaExpr[Product, Int] =
233
233
 
234
234
  ```scala
235
235
  startsWithL.eval(laptop)
236
- // res12: Either[OpticCheck, Seq[Boolean]] = Right(IndexedSeq(true))
236
+ // res12: Either[OpticCheck, Seq[Boolean]] = Right(List(true))
237
237
  startsWithL.eval(pen)
238
- // res13: Either[OpticCheck, Seq[Boolean]] = Right(IndexedSeq(false))
238
+ // res13: Either[OpticCheck, Seq[Boolean]] = Right(List(false))
239
239
 
240
240
  labeledName.eval(laptop)
241
241
  // res14: Either[OpticCheck, Seq[String]] = Right(IndexedSeq("Laptop [SALE]"))
@@ -346,7 +346,9 @@ val hasExpensiveItem: SchemaExpr[Order, Boolean] =
346
346
 
347
347
  ```scala
348
348
  hasExpensiveItem.eval(order)
349
- // res21: Either[OpticCheck, Seq[Boolean]] = Right(List(false, true, false))
349
+ // res21: Either[OpticCheck, Seq[Boolean]] = Right(
350
+ // IndexedSeq(false, true, false)
351
+ // )
350
352
  ```
351
353
 
352
354
  :::tip
@@ -486,9 +488,9 @@ catalog.foreach { p =>
486
488
  - **[Part 2: SQL Generation](./query-dsl-sql.md)** -- Translating query expressions to SQL
487
489
  - **[Part 3: Extending the Expression Language](./query-dsl-extending.md)** -- Adding custom operators (IN, BETWEEN, aggregates) beyond SchemaExpr
488
490
  - **[Part 4: A Fluent SQL Builder](./query-dsl-fluent-builder.md)** -- Type-safe SELECT, UPDATE, INSERT, DELETE with seamless condition mixing
489
- - **[Optics Reference](../reference/optics.md)** -- Full API coverage of Lens, Prism, Optional, and Traversal
490
- - **[DynamicOptic Reference](../reference/dynamic-optic.md)** -- Runtime optic paths for programmatic query construction
491
- - **[Schema Reference](../reference/schema.md)** -- Schema derivation and type-level metadata
492
- - **[Path Interpolator](../path-interpolator.md)** -- String-based path construction with `p"..."` syntax
491
+ - **[Optics Reference](../reference/schema/optics.md)** -- Full API coverage of Lens, Prism, Optional, and Traversal
492
+ - **[DynamicOptic Reference](../reference/schema/dynamic-optic.md)** -- Runtime optic paths for programmatic query construction
493
+ - **[Schema Reference](../reference/schema/schema.md)** -- Schema derivation and type-level metadata
494
+ - **[Path Interpolator](../reference/schema/path-interpolator.md)** -- String-based path construction with `p"..."` syntax
493
495
 
494
- The `SchemaExpr` expression tree is a sealed trait, making it straightforward to write interpreters that translate queries to SQL, MongoDB filters, Elasticsearch queries, or any other target language. Because each optic carries its `DynamicOptic` path (via `toDynamic`), you can extract field names and paths programmatically for these translations.
496
+ `SchemaExpr` is a typed wrapper around a serializable `DynamicSchemaExpr` tree, making it straightforward to write interpreters that translate queries to SQL, MongoDB filters, Elasticsearch queries, or any other target language. Because each optic carries its `DynamicOptic` path (via `toDynamic`), you can extract field names and paths programmatically for these translations.