@zio.dev/zio-blocks 0.0.33 → 0.0.51

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 (150) hide show
  1. package/guides/compile-time-resource-safety-with-scope.md +16 -17
  2. package/guides/getting-started-with-mux.md +1507 -0
  3. package/guides/query-dsl-extending.md +161 -102
  4. package/guides/query-dsl-fluent-builder.md +217 -157
  5. package/guides/query-dsl-reified-optics.md +12 -10
  6. package/guides/query-dsl-sql.md +246 -165
  7. package/guides/telemetry-guide.md +1069 -0
  8. package/guides/zio-schema-migration.md +29 -22
  9. package/index.md +292 -50
  10. package/package.json +1 -1
  11. package/plans/config-follow-up-prs.md +188 -0
  12. package/plans/config-pr-assessment-roadmap.md +310 -0
  13. package/reference/MuxDataFlow.jsx +250 -0
  14. package/reference/async.md +651 -0
  15. package/reference/chunk.md +3533 -308
  16. package/reference/codegen/case-class.md +436 -0
  17. package/reference/codegen/emitter-config.md +383 -0
  18. package/reference/codegen/examples.md +664 -0
  19. package/reference/codegen/field.md +316 -0
  20. package/reference/codegen/index.md +317 -0
  21. package/reference/codegen/scala-emitter.md +392 -0
  22. package/reference/codegen/scala-file.md +276 -0
  23. package/reference/codegen/sealed-trait.md +408 -0
  24. package/reference/codegen/type-definition.md +340 -0
  25. package/reference/codegen/type-ref.md +201 -0
  26. package/reference/combinators.md +347 -117
  27. package/reference/config.md +158 -0
  28. package/reference/context.md +4 -4
  29. package/reference/datastar.md +346 -0
  30. package/reference/docs.md +1461 -345
  31. package/reference/endpoint/auth-type.md +146 -0
  32. package/reference/endpoint/endpoint.md +297 -0
  33. package/reference/endpoint/http-codec.md +249 -0
  34. package/reference/endpoint/index.md +825 -0
  35. package/reference/endpoint/path-codec.md +237 -0
  36. package/reference/endpoint/route-pattern.md +196 -0
  37. package/reference/endpoint/route-tree.md +111 -0
  38. package/reference/endpoint/segment-codec.md +212 -0
  39. package/reference/html.md +1120 -0
  40. package/reference/htmx/attribute-values.md +359 -0
  41. package/reference/htmx/hx-encoding.md +111 -0
  42. package/reference/htmx/hx-params.md +204 -0
  43. package/reference/htmx/hx-swap.md +276 -0
  44. package/reference/htmx/hx-sync.md +251 -0
  45. package/reference/htmx/hx-target.md +314 -0
  46. package/reference/htmx/hx-trigger.md +457 -0
  47. package/reference/htmx/hx-url-update.md +239 -0
  48. package/reference/htmx/index.md +855 -0
  49. package/reference/http-model/index.md +47 -0
  50. package/reference/http-model/model.md +1481 -0
  51. package/reference/http-model/schema.md +747 -0
  52. package/reference/maybe.md +826 -0
  53. package/reference/media-type.md +2 -2
  54. package/reference/mux.mdx +823 -0
  55. package/reference/openapi.md +1351 -0
  56. package/reference/resource-management/defer-handle.md +1 -1
  57. package/reference/resource-management/resource.md +31 -2
  58. package/reference/resource-management/scope.md +28 -12
  59. package/reference/resource-management/wire.md +3 -7
  60. package/reference/ringbuffer/MpmcDiagram.jsx +717 -0
  61. package/reference/ringbuffer/MpscDiagram.jsx +618 -0
  62. package/reference/ringbuffer/SpmcDiagram.jsx +680 -0
  63. package/reference/ringbuffer/SpscDiagram.jsx +677 -0
  64. package/reference/ringbuffer/advanced.mdx +109 -0
  65. package/reference/ringbuffer/index.mdx +145 -0
  66. package/reference/ringbuffer/mpmc.mdx +151 -0
  67. package/reference/ringbuffer/mpsc.mdx +132 -0
  68. package/reference/ringbuffer/spmc.mdx +108 -0
  69. package/reference/ringbuffer/spsc.mdx +344 -0
  70. package/reference/{allows.md → schema/allows.md} +4 -4
  71. package/reference/{binding-resolver.md → schema/binding-resolver.md} +1 -1
  72. package/reference/{binding.md → schema/binding.md} +2 -3
  73. package/reference/schema/built-in-codecs/avro.md +451 -0
  74. package/reference/schema/built-in-codecs/bson.md +480 -0
  75. package/reference/schema/built-in-codecs/csv.md +564 -0
  76. package/reference/schema/built-in-codecs/index.md +77 -0
  77. package/reference/schema/built-in-codecs/json/index.md +295 -0
  78. package/reference/schema/built-in-codecs/json/json-config.md +217 -0
  79. package/reference/{json-patch.md → schema/built-in-codecs/json/json-patch.md} +5 -5
  80. package/reference/{json-schema.md → schema/built-in-codecs/json/json-schema.md} +14 -47
  81. package/reference/schema/built-in-codecs/json/json-selection.md +322 -0
  82. package/reference/{json.md → schema/built-in-codecs/json/json.md} +32 -64
  83. package/reference/schema/built-in-codecs/messagepack.md +508 -0
  84. package/reference/schema/built-in-codecs/thrift.md +433 -0
  85. package/reference/schema/built-in-codecs/toon.md +1078 -0
  86. package/reference/{xml.md → schema/built-in-codecs/xml.md} +13 -9
  87. package/reference/schema/built-in-codecs/yaml.md +552 -0
  88. package/reference/{codec.md → schema/codec.md} +10 -10
  89. package/reference/{dynamic-optic.md → schema/dynamic-optic.md} +151 -5
  90. package/reference/{dynamic-schema.md → schema/dynamic-schema.md} +8 -8
  91. package/reference/schema/format.md +92 -0
  92. package/reference/schema/index.md +50 -0
  93. package/reference/schema/migration.md +297 -0
  94. package/reference/{modifier.md → schema/modifier.md} +58 -7
  95. package/reference/{optics.md → schema/optics.md} +2 -2
  96. package/reference/{patch.md → schema/patch.md} +1 -1
  97. package/{path-interpolator.md → reference/schema/path-interpolator.md} +165 -72
  98. package/reference/{schema-evolution → schema/schema-evolution}/as.md +8 -8
  99. package/reference/{schema-evolution → schema/schema-evolution}/index.md +2 -2
  100. package/reference/{schema-evolution → schema/schema-evolution}/into.md +8 -8
  101. package/reference/{schema-expr.md → schema/schema-expr.md} +110 -175
  102. package/reference/{schema.md → schema/schema.md} +12 -0
  103. package/reference/{structural-types.md → schema/structural-types.md} +1 -1
  104. package/reference/{type-class-derivation.md → schema/type-class-derivation.md} +63 -1
  105. package/reference/smithy.md +533 -0
  106. package/reference/sql/db-codec-deriver.md +71 -0
  107. package/reference/sql/db-codec.md +687 -0
  108. package/reference/sql/db-con.md +271 -0
  109. package/reference/sql/db-connection.md +153 -0
  110. package/reference/sql/db-param-writer.md +77 -0
  111. package/reference/sql/db-param.md +66 -0
  112. package/reference/sql/db-result-reader.md +146 -0
  113. package/reference/sql/db-tx.md +82 -0
  114. package/reference/sql/db-value.md +41 -0
  115. package/reference/sql/ddl.md +85 -0
  116. package/reference/sql/frag.md +254 -0
  117. package/reference/sql/index.md +341 -0
  118. package/reference/sql/repo.md +600 -0
  119. package/reference/sql/sql-dialect.md +73 -0
  120. package/reference/sql/sql-logger.md +62 -0
  121. package/reference/sql/sql-name-mapper.md +70 -0
  122. package/reference/sql/table-metadata.md +134 -0
  123. package/reference/sql/table.md +448 -0
  124. package/reference/sql/transactor-zio.md +399 -0
  125. package/reference/sql/transactor.md +353 -0
  126. package/reference/sql-zio.md +112 -0
  127. package/reference/streams/concurrent-operators.md +106 -0
  128. package/reference/streams/index.md +653 -0
  129. package/reference/streams/pipeline.md +718 -0
  130. package/reference/streams/reader.md +1284 -0
  131. package/reference/streams/scala-2-compatibility.md +55 -0
  132. package/reference/streams/sink.md +1426 -0
  133. package/reference/streams/stream.md +2526 -0
  134. package/reference/streams/writer.md +1045 -0
  135. package/reference/streams/zero-boxing.md +275 -0
  136. package/reference/telemetry.md +693 -0
  137. package/reference/typeid.md +5 -19
  138. package/sidebars.js +238 -43
  139. package/reference/formats.md +0 -694
  140. package/reference/http-model.md +0 -1716
  141. package/reference/streams.md +0 -989
  142. package/ringbuffer.md +0 -249
  143. /package/reference/{json-differ.md → schema/built-in-codecs/json/json-differ.md} +0 -0
  144. /package/reference/{dynamic-value.md → schema/dynamic-value.md} +0 -0
  145. /package/reference/{lazy.md → schema/lazy.md} +0 -0
  146. /package/reference/{reflect.md → schema/reflect.md} +0 -0
  147. /package/reference/{registers.md → schema/registers.md} +0 -0
  148. /package/reference/{schema-error.md → schema/schema-error.md} +0 -0
  149. /package/reference/{syntax.md → schema/syntax.md} +0 -0
  150. /package/reference/{validation.md → schema/validation.md} +0 -0
@@ -0,0 +1,297 @@
1
+ ---
2
+ id: migration
3
+ title: "Migration"
4
+ ---
5
+
6
+ `Migration[A, B]` is ZIO Blocks Schema's typed API for evolving data from one schema version to another. It wraps a fully serializable [`DynamicMigration`](#dynamicmigration) core with typed source and target schemas, giving you a builder-based workflow for adding fields, dropping fields, renaming fields, changing types, migrating nested values, transforming collections and maps, and composing migrations.
7
+
8
+ ## Overview
9
+
10
+ The migration system is split into three layers:
11
+
12
+ - **`Migration[A, B]`** — typed migration between source type `A` and target type `B`
13
+ - **`DynamicMigration`** — pure runtime representation built from serializable actions
14
+ - **`MigrationBuilder[A, B, Changeset]`** — macro-validated builder that accumulates actions and checks that the target shape is fully handled before `build`
15
+
16
+ ```
17
+ Migration[A, B]
18
+ ├── sourceSchema: Schema[A]
19
+ ├── targetSchema: Schema[B]
20
+ └── dynamicMigration: DynamicMigration
21
+ └── actions: Chunk[MigrationAction]
22
+ ├── AddField / DropField / RenameField
23
+ ├── TransformField / ChangeFieldType
24
+ ├── MandateField / OptionalizeField
25
+ ├── RenameCase / TransformCase
26
+ ├── MigrateField
27
+ ├── TransformElements / TransformKeys / TransformValues
28
+ └── Irreversible
29
+ ```
30
+
31
+ `Migration` is the user-facing entry point. `DynamicMigration` is the transport-friendly representation you can inspect, serialize, or apply dynamically.
32
+
33
+ ## Creating a Migration
34
+
35
+ The normal entry point is `Migration.newBuilder[A, B]`:
36
+
37
+ ```scala
38
+ import zio.blocks.schema._
39
+ import zio.blocks.schema.migration._
40
+
41
+ case class PersonV1(name: String)
42
+ case class PersonV2(name: String, age: Int)
43
+
44
+ object PersonV1 {
45
+ implicit val schema: Schema[PersonV1] = Schema.derived
46
+ }
47
+
48
+ object PersonV2 {
49
+ implicit val schema: Schema[PersonV2] = Schema.derived
50
+ }
51
+
52
+ val migration: Migration[PersonV1, PersonV2] =
53
+ Migration
54
+ .newBuilder[PersonV1, PersonV2]
55
+ .addField(_.age, SchemaExpr.literal(0))
56
+ .build
57
+ ```
58
+
59
+ Applying the migration converts the source value to [`DynamicValue`](./dynamic-value.md), applies the dynamic actions, then converts the result back into the target type:
60
+
61
+ ```scala
62
+ import zio.blocks.schema._
63
+ import zio.blocks.schema.migration._
64
+
65
+ case class PersonV1(name: String)
66
+ case class PersonV2(name: String, age: Int)
67
+
68
+ object PersonV1 {
69
+ implicit val schema: Schema[PersonV1] = Schema.derived
70
+ }
71
+
72
+ object PersonV2 {
73
+ implicit val schema: Schema[PersonV2] = Schema.derived
74
+ }
75
+
76
+ val migration = Migration
77
+ .newBuilder[PersonV1, PersonV2]
78
+ .addField(_.age, SchemaExpr.literal(0))
79
+ .build
80
+
81
+ val result = migration(PersonV1("Alice"))
82
+ // Right(PersonV2("Alice", 0))
83
+ ```
84
+
85
+ ## Core Operations
86
+
87
+ ### `Migration#apply`
88
+
89
+ Transforms a typed value:
90
+
91
+ ```scala
92
+ def apply(value: A): Either[SchemaError, B]
93
+ ```
94
+
95
+ ### `Migration#reverse`
96
+
97
+ Returns the **structural reverse** of the migration:
98
+
99
+ ```scala
100
+ def reverse: Migration[B, A]
101
+ ```
102
+
103
+ This is best-effort at runtime. Actions that lose information (for example `TransformField`, `ChangeFieldType`, `OptionalizeField`, or collection-wide transforms) reverse to `Irreversible`, which causes reverse execution to fail with a descriptive error instead of silently fabricating data.
104
+
105
+ ### Composition
106
+
107
+ Migrations compose with `++` or `andThen`:
108
+
109
+ ```scala
110
+ import zio.blocks.schema._
111
+ import zio.blocks.schema.migration._
112
+
113
+ case class PersonV1(name: String)
114
+ case class PersonV2(name: String, age: Int)
115
+ case class PersonV3(name: String, age: Int, city: String)
116
+
117
+ object PersonV1 { implicit val schema: Schema[PersonV1] = Schema.derived }
118
+ object PersonV2 { implicit val schema: Schema[PersonV2] = Schema.derived }
119
+ object PersonV3 { implicit val schema: Schema[PersonV3] = Schema.derived }
120
+
121
+ val migration = Migration
122
+ .newBuilder[PersonV1, PersonV2]
123
+ .addField(_.age, SchemaExpr.literal(0))
124
+ .build
125
+
126
+ val addCity = Migration
127
+ .newBuilder[PersonV2, PersonV3]
128
+ .addField(_.city, SchemaExpr.literal(""))
129
+ .build
130
+
131
+ val combined: Migration[PersonV1, PersonV3] = migration ++ addCity
132
+ ```
133
+
134
+ ## Builder Operations
135
+
136
+ `MigrationBuilder` uses selector expressions such as `_.field`, `_.nested.field`, `_.items.each`, and `_.data.eachValue`. The builder's third type parameter, `Changeset`, tracks which operations have already been applied so `build` can validate completeness.
137
+
138
+ ### Record operations
139
+
140
+ - `addField(_.target, default)`
141
+ - `dropField(_.source, defaultForReverse)`
142
+ - `renameField(_.from, _.to)`
143
+ - `transformField(_.from, _.to, expr)`
144
+ - `mandateField(_.optionalSource, _.target, default)`
145
+ - `optionalizeField(_.source, _.optionalTarget)`
146
+ - `changeFieldType(_.source, expr)`
147
+
148
+ ```scala
149
+ import zio.blocks.schema._
150
+ import zio.blocks.schema.migration._
151
+
152
+ case class UserV1(firstName: String, nickname: Option[String], age: Int)
153
+ case class UserV2(fullName: String, nickname: String, age: String)
154
+
155
+ object UserV1 {
156
+ implicit val schema: Schema[UserV1] = Schema.derived
157
+ }
158
+
159
+ object UserV2 {
160
+ implicit val schema: Schema[UserV2] = Schema.derived
161
+ }
162
+
163
+ val userMigration = Migration
164
+ .newBuilder[UserV1, UserV2]
165
+ .renameField(_.firstName, _.fullName)
166
+ .mandateField(_.nickname, _.nickname, SchemaExpr.literal("anonymous"))
167
+ .changeFieldType(_.age, SchemaExpr.literal("30"))
168
+ .build
169
+ ```
170
+
171
+ `transformField` and `changeFieldType` evaluate their `SchemaExpr` against the currently focused field value, not the entire root record. In practice, that means the expression should be written in terms of the value being replaced.
172
+
173
+ ### Nested migration with `migrateField`
174
+
175
+ When a nested value has its own migration, use `migrateField`:
176
+
177
+ ```scala
178
+ import zio.blocks.schema._
179
+ import zio.blocks.schema.migration._
180
+
181
+ case class AddressV1(street: String, city: String)
182
+ case class AddressV2(street: String, city: String, zip: String)
183
+ case class PersonWithAddressV1(name: String, address: AddressV1)
184
+ case class PersonWithAddressV2(name: String, address: AddressV2)
185
+
186
+ object AddressV1 { implicit val schema: Schema[AddressV1] = Schema.derived }
187
+ object AddressV2 { implicit val schema: Schema[AddressV2] = Schema.derived }
188
+ object PersonWithAddressV1 { implicit val schema: Schema[PersonWithAddressV1] = Schema.derived }
189
+ object PersonWithAddressV2 { implicit val schema: Schema[PersonWithAddressV2] = Schema.derived }
190
+
191
+ val addressMigration = Migration
192
+ .newBuilder[AddressV1, AddressV2]
193
+ .addField(_.zip, SchemaExpr.literal("00000"))
194
+ .build
195
+
196
+ val personMigration = Migration
197
+ .newBuilder[PersonWithAddressV1, PersonWithAddressV2]
198
+ .migrateField(_.address, addressMigration)
199
+ .build
200
+ ```
201
+
202
+ There is no `transformNested` builder method in the current API. Nested structural changes are expressed by building a migration for the nested type and applying it with `migrateField`.
203
+
204
+ ### Enum and collection operations
205
+
206
+ - `renameCase(from, to)`
207
+ - `transformCase(caseName)(...)`
208
+ - `transformElements(_.items, expr)`
209
+ - `transformKeys(_.data, expr)`
210
+ - `transformValues(_.data, expr)`
211
+
212
+ Collection and map transforms are map-like operations. The `SchemaExpr` is evaluated once per matched value:
213
+
214
+ - `transformElements` evaluates against each collection element
215
+ - `transformKeys` evaluates against each map key
216
+ - `transformValues` evaluates against each map value
217
+
218
+ For example, `transformElements(_.scores, expr)` runs `expr` separately for every element in `scores`, and `transformValues(_.metadata, expr)` runs it separately for every map value.
219
+
220
+ ## `build`
221
+
222
+ ```scala
223
+ def build(using ev: MigrationComplete[A, B, Changeset]): Migration[A, B]
224
+ ```
225
+
226
+ `build` runs macro validation. It checks that every field needed to transform `A` into `B` is either:
227
+
228
+ - auto-mapped by identical source/target structure, or
229
+ - explicitly handled by one of the builder operations
230
+
231
+ If the migration is incomplete, the code fails to compile.
232
+
233
+ This validated `build` step is the public completion path for `MigrationBuilder`. If you need unvalidated or dynamically assembled migrations, work at the `DynamicMigration` layer and wrap it with `Migration.fromDynamic`.
234
+
235
+ ## `DynamicMigration`
236
+
237
+ `DynamicMigration` is the untyped runtime representation:
238
+
239
+ ```scala
240
+ final case class DynamicMigration(actions: Chunk[MigrationAction]) {
241
+ def apply(value: DynamicValue): Either[SchemaError, DynamicValue]
242
+ def ++(that: DynamicMigration): DynamicMigration
243
+ def andThen(that: DynamicMigration): DynamicMigration
244
+ def reverse: DynamicMigration
245
+ def isEmpty: Boolean
246
+ def size: Int
247
+ }
248
+ ```
249
+
250
+ Use it when you need to inspect, serialize, or apply migrations without keeping the original Scala types around.
251
+
252
+ ## `MigrationAction`
253
+
254
+ `MigrationAction` is the serializable ADT used by `DynamicMigration`.
255
+
256
+ | Action | Purpose | Reverse behavior |
257
+ |---|---|---|
258
+ | `AddField` | add a field with a default expression | `DropField` |
259
+ | `DropField` | remove a field | `AddField` using stored reverse default |
260
+ | `RenameField` | rename a record field | structural inverse rename |
261
+ | `TransformField` | replace a field by evaluating an expression against the current field value | `Irreversible` |
262
+ | `MandateField` | convert `Option[A]` to `A` with default for `None` | `OptionalizeField` |
263
+ | `OptionalizeField` | wrap a value in `Some` | `Irreversible` |
264
+ | `ChangeFieldType` | replace a field with a converted value computed from the current field value | `Irreversible` |
265
+ | `RenameCase` | rename enum case | inverse rename |
266
+ | `TransformCase` | run actions inside a specific case | reverse inner actions in reverse order |
267
+ | `MigrateField` | apply a nested `DynamicMigration` to a field | reverse nested migration |
268
+ | `TransformElements` | replace each collection element by evaluating the expression on that element | `Irreversible` |
269
+ | `TransformKeys` | replace each map key by evaluating the expression on that key | `Irreversible` |
270
+ | `TransformValues` | replace each map value by evaluating the expression on that value | `Irreversible` |
271
+ | `Irreversible` | explicit non-invertible sentinel | itself |
272
+
273
+ ## Errors
274
+
275
+ Migration execution returns [`SchemaError`](./schema-error.md), with migration-specific kinds including:
276
+
277
+ - path not found
278
+ - type mismatch
279
+ - missing default
280
+ - transform failure
281
+ - field/case not found
282
+ - invalid value
283
+ - mandate failure
284
+
285
+ Errors carry a [`DynamicOptic`](./dynamic-optic.md) path so failures can be traced to the precise field or nested location that failed.
286
+
287
+ ## Relationship to Other Schema-Evolution APIs
288
+
289
+ `Into` and `As` derive structural conversions automatically. `Migration` is lower-level and more explicit:
290
+
291
+ - use [`Into`](./schema-evolution/into.md) for one-way derived schema evolution
292
+ - use [`As`](./schema-evolution/as.md) for bidirectional derived evolution
293
+ - use `Migration` when you need explicit, inspectable, serializable transformation steps
294
+
295
+ :::tip
296
+ If you want a practical migration walkthrough rather than a reference, start with [`MigrationSpec`](https://github.com/zio/zio-blocks/blob/main/schema/shared/src/test/scala/zio/blocks/schema/migration/MigrationSpec.scala) and [`DynamicMigrationSpec`](https://github.com/zio/zio-blocks/blob/main/schema/shared/src/test/scala/zio/blocks/schema/migration/DynamicMigrationSpec.scala) in the repository until a dedicated migration guide lands.
297
+ :::
@@ -11,7 +11,7 @@ Modifiers are designed to be **pure data** values that can be serialized, making
11
11
  sealed trait Modifier extends StaticAnnotation
12
12
  object Modifier {
13
13
  sealed trait Term extends Modifier
14
- // ... term modifiers (transient, rename, alias, config) ...
14
+ // ... term modifiers (transient, encodeTransient, rename, alias, id, config) ...
15
15
  sealed trait Reflect extends Modifier
16
16
  // ... reflect modifiers (config) ...
17
17
  }
@@ -119,12 +119,14 @@ Modifiers are organized into two main categories:
119
119
  ```
120
120
  Modifier
121
121
  ├── Modifier.Term (annotates record fields and variant cases)
122
- │ ├── transient() : exclude from serialization
123
- │ ├── rename(name) : change serialized name
124
- │ ├── alias(name) : add alternative name
125
- │ └── config(key, val) : attach key-value metadata
122
+ │ ├── transient() : exclude from serialization
123
+ │ ├── encodeTransient() : exclude from encoding only
124
+ │ ├── rename(name) : change serialized name
125
+ │ ├── alias(name) : add alternative name
126
+ │ ├── id() : mark a primary-key field
127
+ │ └── config(key, val) : attach key-value metadata
126
128
  └── Modifier.Reflect (annotates reflect values / types)
127
- └── config(key, val) : attach key-value metadata
129
+ └── config(key, val) : attach key-value metadata
128
130
  ```
129
131
 
130
132
  As you can see, `config` is the only modifier that extends both `Term` and `Reflect`, allowing it to be used on both fields and types.
@@ -197,6 +199,23 @@ With this configuration:
197
199
 
198
200
  This pattern is particularly useful when migrating data formats without breaking compatibility with existing data.
199
201
 
202
+ ### id
203
+
204
+ The `id` modifier marks a record field as its primary key. It is interpreted by
205
+ `zio-blocks-sql` when deriving a `Repo[E, ID]`, and takes precedence over
206
+ name- and type-based ID discovery.
207
+
208
+ ```scala
209
+ import zio.blocks.schema._
210
+
211
+ case class User(
212
+ email: String,
213
+ @Modifier.id() externalKey: Long
214
+ )
215
+ ```
216
+
217
+ Mark exactly one field with `@Modifier.id()` for a derived repository.
218
+
200
219
  ### config
201
220
 
202
221
  The `config` modifier attaches arbitrary key-value metadata to a term (record fields or variant cases) or a type itself. The convention for keys is `<format>.<property>`, allowing format-specific configuration.
@@ -218,7 +237,7 @@ The `config` modifier extends both `Term` and `Reflect`, making it usable on bot
218
237
 
219
238
  ## Reflect Modifiers
220
239
 
221
- Reflect modifiers annotate reflect values (types themselves). Currently, only `config` is a reflect modifier.
240
+ Reflect modifiers annotate reflect values (types themselves). Alongside `config`, JSON derivation also understands dedicated reflect modifiers for discriminator selection, field naming, case naming, and rejecting extra fields.
222
241
 
223
242
  ### config
224
243
 
@@ -236,6 +255,33 @@ object Person {
236
255
  }
237
256
  ```
238
257
 
258
+ ### JSON reflect modifiers
259
+
260
+ These reflect modifiers are consumed by `JsonCodecDeriver` and let you keep JSON-specific settings next to the annotated type instead of passing them programmatically at every call site.
261
+
262
+ ```scala
263
+ import zio.blocks.schema._
264
+
265
+ @Modifier.discriminator("type")
266
+ @Modifier.caseNaming("snake_case")
267
+ sealed trait Event
268
+
269
+ @Modifier.noExtraFields()
270
+ @Modifier.fieldNaming("snake_case")
271
+ case class UserProfile(
272
+ @Modifier.rename("userId") id: String,
273
+ @Modifier.encodeTransient() checksum: Int = 0,
274
+ displayName: String
275
+ )
276
+ ```
277
+
278
+ - `Modifier.discriminator(name)` overrides the JSON discriminator field for sealed hierarchies.
279
+ - `Modifier.noExtraFields()` rejects unknown object fields during decoding.
280
+ - `Modifier.fieldNaming(strategy)` applies a naming strategy to direct record fields.
281
+ - `Modifier.caseNaming(strategy)` applies a naming strategy to variant case names.
282
+
283
+ For field-level JSON behavior, `Modifier.encodeTransient()` skips a field during encoding but still accepts it during decoding; like `transient`, it requires a default value when the field is neither optional nor a collection.
284
+
239
285
  Or add multiple modifiers at once:
240
286
 
241
287
  ```scala
@@ -305,8 +351,13 @@ import zio.blocks.schema._
305
351
 
306
352
  // Schema instances for individual modifiers
307
353
  Schema[Modifier.transient]
354
+ Schema[Modifier.encodeTransient]
308
355
  Schema[Modifier.rename]
309
356
  Schema[Modifier.alias]
357
+ Schema[Modifier.discriminator]
358
+ Schema[Modifier.noExtraFields]
359
+ Schema[Modifier.fieldNaming]
360
+ Schema[Modifier.caseNaming]
310
361
  Schema[Modifier.config]
311
362
 
312
363
  // Schema instances for modifier traits
@@ -6,7 +6,7 @@ title: "Optics"
6
6
  Optics are a fundamental feature of ZIO Blocks that enable type-safe, composable access and modification of nested data structures. What sets ZIO Blocks apart is its implementation of **reflective optics** — a novel construct that combines the operational capabilities of traditional optics with embedded structural metadata, enabling both data manipulation AND introspection.
7
7
 
8
8
  :::tip
9
- For a practical walkthrough of building query DSLs with optics, see the [Writing a Query DSL with Reified Optics](../guides/query-dsl-reified-optics.md) guide.
9
+ For a practical walkthrough of building query DSLs with optics, see the [Writing a Query DSL with Reified Optics](../../guides/query-dsl-reified-optics.md) guide.
10
10
  :::
11
11
 
12
12
  ## What Are Optics?
@@ -148,7 +148,7 @@ val updatedAddress: Address = Address.street.replace(address, "456 Elm St")
148
148
 
149
149
  While manual lens construction gives you fine-grained control, ZIO Blocks provides macro-based derivation as the **preferred approach** for creating lenses.
150
150
 
151
- The `optic` macro inside the `CompanionOptics` trait creates a lens using intuitive selector syntax that mirrors standard Scala field access:
151
+ The `optic` macro inside the `CompanionOptics` trait creates a lens using intuitive selector syntax that mirrors standard Scala field access. In the rest of the schema docs you may also see the symbolic `$(_.field)` form; it is the same optic-construction API presented with symbolic syntax instead of the named `optic(_.field)` helper.
152
152
 
153
153
  ```scala
154
154
  import zio.blocks.schema.optic
@@ -16,7 +16,7 @@ A `Patch[S]` represents a sequence of operations that transform a value of type
16
16
  - **Schema Evolution** — Patches work with the schema system, adapting as data structures evolve
17
17
 
18
18
  :::note
19
- For **untyped JSON patching** without a schema, use [`JsonPatch`](./json-patch.md) instead. `JsonPatch` is optimized for diff-and-apply workflows on raw JSON values and provides compact delta representations without requiring typed optics.
19
+ For **untyped JSON patching** without a schema, use [`JsonPatch`](./built-in-codecs/json/json-patch.md) instead. `JsonPatch` is optimized for diff-and-apply workflows on raw JSON values and provides compact delta representations without requiring typed optics.
20
20
  :::
21
21
 
22
22
  ```scala