@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,1351 @@
1
+ ---
2
+ id: openapi
3
+ title: "OpenAPI"
4
+ ---
5
+
6
+ `zio-blocks-openapi` is a **complete, type-safe OpenAPI 3.1 data model** for building API documentation programmatically. It provides immutable case classes and sealed traits representing every OpenAPI concept—operations, parameters, security schemes, and components—enabling you to construct OpenAPI documents in compile-time-safe Scala and export them as JSON for consumption by tools like Swagger UI, Redoc, and API validators.
7
+
8
+ Core types: `OpenAPI`, `Info`, `Paths`, `PathItem`, `Operation`, `Parameter`, `RequestBody`, `Response`, `Components`, `SchemaObject`, `SecurityScheme`, `ReferenceOr`.
9
+
10
+ ```scala
11
+ final case class OpenAPI(
12
+ openapi: String,
13
+ info: Info,
14
+ servers: Option[Chunk[Server]] = None,
15
+ paths: Option[Paths] = None,
16
+ components: Option[Components] = None,
17
+ security: Option[Chunk[SecurityRequirement]] = None
18
+ )
19
+ ```
20
+
21
+ ## Introduction
22
+
23
+ OpenAPI documents are the **lingua franca for API specifications**. They define request/response contracts, authentication methods, and data schemas in a standardized JSON or YAML format that external tools consume. Building these documents manually in JSON is error-prone; maintaining them as your API evolves is tedious.
24
+
25
+ The OpenAPI module bridges the gap by letting you author API specs as Scala code—leveraging the type system for compile-time correctness—then export to standard JSON that any OpenAPI tool understands. You get type safety during authoring plus interoperability with the entire OpenAPI ecosystem.
26
+
27
+ ## Installation
28
+
29
+ ```scala
30
+ libraryDependencies += "dev.zio" %% "zio-blocks-openapi" % "0.0.51"
31
+
32
+ // You'll also need the schema module for Schema[A] integration:
33
+ libraryDependencies += "dev.zio" %% "zio-blocks-schema" % "0.0.51"
34
+ ```
35
+
36
+ For Scala.js:
37
+
38
+ ```scala
39
+ libraryDependencies += "dev.zio" %%% "zio-blocks-openapi" % "0.0.51"
40
+ ```
41
+
42
+ Supported Scala versions: 2.13.x and 3.x.
43
+
44
+ ## How They Work Together
45
+
46
+ The OpenAPI module follows a clear workflow:
47
+
48
+ **1. Define your data types** using ZIO Blocks `Schema`:
49
+
50
+ ```scala
51
+ import zio.blocks.openapi._
52
+ import zio.blocks.docs._
53
+ import zio.blocks.chunk._
54
+
55
+ import zio.blocks.schema._
56
+
57
+ case class User(id: Int, name: String, email: String)
58
+ object User {
59
+ implicit val schema: Schema[User] = Schema.derived
60
+ }
61
+
62
+ case class ErrorResponse(code: Int, message: String)
63
+ object ErrorResponse {
64
+ implicit val schema: Schema[ErrorResponse] = Schema.derived
65
+ }
66
+ ```
67
+
68
+ **2. Create an OpenAPI document** by composing types:
69
+
70
+ ```scala
71
+ val api = OpenAPI(
72
+ openapi = "3.1.0",
73
+ info = Info(
74
+ title = "User API",
75
+ version = "1.0.0",
76
+ description = Some(md"API for managing users")
77
+ ),
78
+ paths = Some(Paths(ChunkMap(
79
+ "/users" -> PathItem(
80
+ get = Some(Operation(
81
+ summary = Some(md"List all users"),
82
+ description = Some(md"Returns a paginated list of users"),
83
+ responses = Responses(ChunkMap(
84
+ "200" -> ReferenceOr.Value(Response(
85
+ description = md"Successful response",
86
+ content = ChunkMap(
87
+ "application/json" -> MediaType(
88
+ schema = Some(ReferenceOr.Value(
89
+ Schema[List[User]].toOpenAPISchema
90
+ ))
91
+ )
92
+ )
93
+ ))
94
+ ))
95
+ ))
96
+ ),
97
+ "/users/{id}" -> PathItem(
98
+ get = Some(Operation(
99
+ summary = Some(md"Get a user by ID"),
100
+ parameters = Chunk(
101
+ ReferenceOr.Value(Parameter(
102
+ name = "id",
103
+ in = ParameterLocation.Path,
104
+ required = true,
105
+ schema = Some(ReferenceOr.Value(
106
+ Schema[Int].toOpenAPISchema
107
+ ))
108
+ ))
109
+ ),
110
+ responses = Responses(ChunkMap(
111
+ "200" -> ReferenceOr.Value(Response(
112
+ description = md"User found",
113
+ content = ChunkMap(
114
+ "application/json" -> MediaType(
115
+ schema = Some(ReferenceOr.Value(
116
+ Schema[User].toOpenAPISchema
117
+ ))
118
+ )
119
+ )
120
+ )),
121
+ "404" -> ReferenceOr.Value(Response(
122
+ description = md"User not found",
123
+ content = ChunkMap(
124
+ "application/json" -> MediaType(
125
+ schema = Some(ReferenceOr.Value(
126
+ Schema[ErrorResponse].toOpenAPISchema
127
+ ))
128
+ )
129
+ )
130
+ ))
131
+ ))
132
+ ))
133
+ )
134
+ ))),
135
+ components = Some(Components(
136
+ schemas = ChunkMap(
137
+ Schema[User].toRefSchema._2._1 -> ReferenceOr.Value(Schema[User].toRefSchema._2._2),
138
+ Schema[ErrorResponse].toRefSchema._2._1 -> ReferenceOr.Value(Schema[ErrorResponse].toRefSchema._2._2)
139
+ )
140
+ ))
141
+ )
142
+ ```
143
+
144
+ **3. Serialize to JSON** for tools to consume:
145
+
146
+ ```scala
147
+ import zio.blocks.openapi.OpenAPICodec._
148
+
149
+ val json = openAPICodec.encodeValue(api)
150
+ ```
151
+
152
+ **4. Render or serve** the JSON (e.g., to Swagger UI):
153
+
154
+ ```scala
155
+ import zio.blocks.schema.json._
156
+
157
+ val jsonString = Json.jsonCodec.encodeToString(json, WriterConfig.withIndentionStep2)
158
+ // jsonString: String = """{
159
+ // "openapi": "3.1.0",
160
+ // "info": {
161
+ // "title": "User API",
162
+ // "version": "1.0.0",
163
+ // "description": "API for managing users\n\n"
164
+ // },
165
+ // "paths": {
166
+ // "/users": {
167
+ // "get": {
168
+ // "responses": {
169
+ // "200": {
170
+ // "description": "Successful response\n\n",
171
+ // "content": {
172
+ // "application/json": {
173
+ // "schema": {
174
+ // "items": {
175
+ // "properties": {
176
+ // "id": {
177
+ // "type": "integer",
178
+ // "maximum": 2147483647,
179
+ // "minimum": -2147483648
180
+ // },
181
+ // "name": {
182
+ // "type": "string"
183
+ // },
184
+ // "email": {
185
+ // "type": "string"
186
+ // }
187
+ // },
188
+ // "type": "object",
189
+ // "required": [
190
+ // "id",
191
+ // "name",
192
+ // "email"
193
+ // ],
194
+ // "title": "User"
195
+ // },
196
+ // "type": [
197
+ // "array",
198
+ // "null"
199
+ // ],
200
+ // "title": "List[User]"
201
+ // }
202
+ // }
203
+ // }
204
+ // }
205
+ // },
206
+ // "summary": "List all users\n\n",
207
+ // ...
208
+ ```
209
+
210
+ ### Type Relationships Diagram
211
+
212
+ ```
213
+ OpenAPI (root document)
214
+ ├─ info: Info (metadata)
215
+ ├─ servers: Option[Chunk[Server]]
216
+ ├─ paths: Option[Paths] (map of path strings to PathItem)
217
+ │ └─ PathItem
218
+ │ ├─ get: Operation
219
+ │ ├─ post: Operation
220
+ │ ├─ put: Operation
221
+ │ └─ ... (other HTTP methods)
222
+ │ ├─ parameters: Chunk[ReferenceOr[Parameter]]
223
+ │ ├─ requestBody: ReferenceOr[RequestBody]
224
+ │ │ └─ content: Map[String, MediaType]
225
+ │ │ └─ schema: ReferenceOr[SchemaObject]
226
+ │ └─ responses: Responses
227
+ │ └─ Map[statusCode, ReferenceOr[Response]]
228
+ │ └─ content: Map[String, MediaType]
229
+ │ └─ schema: ReferenceOr[SchemaObject]
230
+ ├─ components: Option[Components]
231
+ │ ├─ schemas: ChunkMap[String, ReferenceOr[SchemaObject]]
232
+ │ ├─ responses: ChunkMap[String, ReferenceOr[Response]]
233
+ │ ├─ parameters: ChunkMap[String, ReferenceOr[Parameter]]
234
+ │ └─ securitySchemes: ChunkMap[String, ReferenceOr[SecurityScheme]]
235
+ └─ security: Option[Chunk[SecurityRequirement]]
236
+ ```
237
+
238
+ ## Common Patterns
239
+
240
+ ### Building Reusable Schema Components
241
+
242
+ Avoid duplicating schema definitions by moving them to `components.schemas`:
243
+
244
+ ```scala
245
+ import zio.blocks.openapi._
246
+ import zio.blocks.docs._
247
+ import zio.blocks.chunk._
248
+ import zio.blocks.schema._
249
+
250
+ case class User(id: Int, name: String, email: String)
251
+ object User {
252
+ implicit val schema: Schema[User] = Schema.derived
253
+ }
254
+
255
+ val userSchemaComponent = Schema[User].toRefSchema
256
+ // Returns: (ReferenceOr.Ref(...), ("User", SchemaObject(...)))
257
+ // Use the ref in operations, store the component in components.schemas
258
+ ```
259
+
260
+ ### Using `ReferenceOr` for Inline vs. Referenced Schemas
261
+
262
+ `ReferenceOr[A]` is a sealed trait with two cases:
263
+
264
+ - **`ReferenceOr.Ref`**: Points to a schema in `#/components/schemas/<name>`
265
+ - **`ReferenceOr.Value`**: Inline schema definition
266
+
267
+ Prefer `Ref` for reusable schemas; use `Value` for simple, one-off schemas:
268
+
269
+ ```scala
270
+ import zio.blocks.openapi._
271
+ import zio.blocks.docs._
272
+ import zio.blocks.chunk._
273
+ import zio.blocks.schema._
274
+
275
+ // Reusable: use Ref
276
+ val userRef = ReferenceOr.Ref(Reference(`$ref` = "#/components/schemas/User"))
277
+
278
+ // One-off: use Value
279
+ val simpleString = ReferenceOr.Value(Schema[String].toOpenAPISchema)
280
+ ```
281
+
282
+ ### Security Schemes
283
+
284
+ Define authentication methods in `components.securitySchemes`:
285
+
286
+ ```scala
287
+ import zio.blocks.openapi._
288
+ import zio.blocks.docs._
289
+ import zio.blocks.chunk._
290
+ import zio.blocks.schema._
291
+
292
+ val apiKeyScheme = SecurityScheme.APIKey(
293
+ name = "X-API-Key",
294
+ in = APIKeyLocation.Header,
295
+ description = Some(md"API key for authentication")
296
+ )
297
+
298
+ val oauthScheme = SecurityScheme.OAuth2(
299
+ flows = OAuthFlows(
300
+ authorizationCode = Some(OAuthFlow(
301
+ authorizationUrl = Some("https://example.com/oauth/authorize"),
302
+ tokenUrl = Some("https://example.com/oauth/token"),
303
+ scopes = ChunkMap("read" -> "Read access", "write" -> "Write access")
304
+ ))
305
+ ),
306
+ description = Some(md"OAuth 2.0 authorization")
307
+ )
308
+
309
+ val components = Components(
310
+ securitySchemes = ChunkMap(
311
+ "api_key" -> ReferenceOr.Value(apiKeyScheme),
312
+ "oauth2" -> ReferenceOr.Value(oauthScheme)
313
+ )
314
+ )
315
+ ```
316
+
317
+ ### Path Parameters vs. Query Parameters
318
+
319
+ Distinguish parameter locations using `ParameterLocation`:
320
+
321
+ ```scala
322
+ import zio.blocks.openapi._
323
+ import zio.blocks.docs._
324
+ import zio.blocks.chunk._
325
+ import zio.blocks.schema._
326
+
327
+ val pathParam = Parameter(
328
+ name = "id",
329
+ in = ParameterLocation.Path,
330
+ required = true,
331
+ schema = Some(ReferenceOr.Value(Schema[Int].toOpenAPISchema))
332
+ )
333
+
334
+ val queryParam = Parameter(
335
+ name = "limit",
336
+ in = ParameterLocation.Query,
337
+ required = false,
338
+ schema = Some(ReferenceOr.Value(Schema[Int].toOpenAPISchema))
339
+ )
340
+
341
+ val headerParam = Parameter(
342
+ name = "X-Custom-Header",
343
+ in = ParameterLocation.Header,
344
+ required = false,
345
+ schema = Some(ReferenceOr.Value(Schema[String].toOpenAPISchema))
346
+ )
347
+ ```
348
+
349
+ ## Integration Points
350
+
351
+ The OpenAPI module integrates tightly with other ZIO Blocks components:
352
+
353
+ - **Schema Integration**: All OpenAPI types have `Schema.derived` instances, enabling round-trip serialization via `DynamicValue`. Use `Schema[A].toOpenAPISchema` to convert any schema to an OpenAPI component.
354
+ - **Markdown Support**: Description fields use the in-house `Doc` type, which supports CommonMark rendering. This ensures markdown descriptions round-trip correctly.
355
+ - **JSON AST**: All codecs operate on the `Json` AST from `zio-blocks-schema`, not external JSON libraries. To render as YAML, pipe the `Json` through `zio-blocks-schema-yaml` separately.
356
+
357
+ ---
358
+
359
+ ## OpenAPI
360
+
361
+ `OpenAPI` is the root document object representing a complete OpenAPI 3.1 specification.
362
+
363
+ ### Definition
364
+
365
+ Every OpenAPI document requires:
366
+ - **`openapi`**: Version string (typically `"3.1.0"`)
367
+ - **`info`**: Metadata about the API (`Info`)
368
+
369
+ Optional top-level fields include:
370
+ - **`servers`**: Server definitions for the API (`Chunk[Server]`)
371
+ - **`paths`**: Map of endpoint paths to operations (`Paths`)
372
+ - **`components`**: Reusable schemas, responses, parameters, and other components (`Components`)
373
+ - **`security`**: Security requirements applied to the API (`Chunk[SecurityRequirement]`)
374
+
375
+ Response definitions are modeled per `Operation`, not as a top-level field on `OpenAPI`.
376
+
377
+ ### Creating an OpenAPI Document
378
+
379
+ To construct an `OpenAPI` document:
380
+
381
+ ```scala
382
+ import zio.blocks.openapi._
383
+ import zio.blocks.docs._
384
+ import zio.blocks.chunk._
385
+ import zio.blocks.schema._
386
+
387
+ val minimalApi = OpenAPI(
388
+ openapi = "3.1.0",
389
+ info = Info(title = "My API", version = "1.0.0")
390
+ )
391
+ ```
392
+
393
+ Add paths, operations, and components as shown in the "How They Work Together" section above.
394
+
395
+ ### Serialization
396
+
397
+ Encode an `OpenAPI` document to `Json` AST:
398
+
399
+ ```scala
400
+ import zio.blocks.openapi._
401
+ import zio.blocks.openapi.OpenAPICodec._
402
+ import zio.blocks.docs._
403
+ import zio.blocks.chunk._
404
+ import zio.blocks.schema._
405
+
406
+ import zio.blocks.schema.json._
407
+
408
+ val myApi = OpenAPI(openapi = "3.1.0", info = Info(title = "My API", version = "1.0.0"))
409
+ val encoded: Json = openAPICodec.encodeValue(myApi)
410
+ ```
411
+
412
+ Decode from `Json` AST back to an `OpenAPI` instance:
413
+
414
+ ```scala
415
+ import zio.blocks.openapi._
416
+ import zio.blocks.openapi.OpenAPICodec._
417
+ import zio.blocks.docs._
418
+ import zio.blocks.chunk._
419
+ import zio.blocks.schema._
420
+
421
+ import zio.blocks.schema.json._
422
+
423
+ val myApi = OpenAPI(openapi = "3.1.0", info = Info(title = "My API", version = "1.0.0"))
424
+ val encoded: Json = openAPICodec.encodeValue(myApi)
425
+ val decoded: OpenAPI = openAPICodec.decodeValue(encoded)
426
+ ```
427
+
428
+ ---
429
+
430
+ ## Info
431
+
432
+ `Info` contains metadata about the API: title, version, contact, and license.
433
+
434
+ ### Definition
435
+
436
+ Required fields:
437
+ - **`title`**: API name (e.g., `"User API"`)
438
+ - **`version`**: API version (e.g., `"1.0.0"`)
439
+
440
+ Optional fields:
441
+ - **`description`**: Markdown-formatted description (`Doc`)
442
+ - **`termsOfService`**: Terms of service URL
443
+ - **`contact`**: Contact information (`Contact`)
444
+ - **`license`**: License information (`License`)
445
+
446
+ ### Creating Info
447
+
448
+ ```scala
449
+ import zio.blocks.openapi._
450
+ import zio.blocks.docs._
451
+ import zio.blocks.chunk._
452
+ import zio.blocks.schema._
453
+
454
+ val info = Info(
455
+ title = "Pet Store API",
456
+ version = "3.0.0",
457
+ description = Some(md"API for managing a pet store"),
458
+ contact = Some(Contact(
459
+ name = Some("API Support"),
460
+ url = Some("https://example.com/support"),
461
+ email = Some("support@example.com")
462
+ )),
463
+ license = Some(License(
464
+ name = "Apache 2.0",
465
+ identifier = Some("Apache-2.0")
466
+ ))
467
+ )
468
+ ```
469
+
470
+ ---
471
+
472
+ ## Paths & PathItem
473
+
474
+ `Paths` represents the collection of URL paths and their operations. `PathItem` groups HTTP methods (GET, POST, PUT, etc.) on a single path.
475
+
476
+ ### Definition
477
+
478
+ `Paths` is a wrapper case class with two fields:
479
+ - **`paths`**: `ChunkMap[String, PathItem]`, where keys are path strings (e.g., `"/users/{id}"`)
480
+ - **`extensions`**: `ChunkMap[String, Json]`, for OpenAPI specification extensions
481
+
482
+ `PathItem` contains optional fields for each HTTP method:
483
+ - **`get`, `post`, `put`, `delete`, `patch`, `head`, `options`, `trace`**: `Operation` instances
484
+ - **`parameters`**: Path-level parameters shared by all methods on this path
485
+ - **`servers`**: Optional server overrides for this path
486
+
487
+ ### Creating Path Items
488
+
489
+ To define a path with multiple operations:
490
+
491
+ ```scala
492
+ import zio.blocks.openapi._
493
+ import zio.blocks.docs._
494
+ import zio.blocks.chunk._
495
+ import zio.blocks.schema._
496
+
497
+ val userPaths = Paths(ChunkMap(
498
+ "/users" -> PathItem(
499
+ get = Some(Operation(
500
+ summary = Some(md"List users"),
501
+ responses = Responses(ChunkMap(
502
+ "200" -> ReferenceOr.Value(Response(
503
+ description = md"User list",
504
+ content = ChunkMap(
505
+ "application/json" -> MediaType(schema = None)
506
+ )
507
+ ))
508
+ ))
509
+ )),
510
+ post = Some(Operation(
511
+ summary = Some(md"Create user"),
512
+ requestBody = Some(ReferenceOr.Value(RequestBody(
513
+ description = Some(md"User data"),
514
+ content = ChunkMap(
515
+ "application/json" -> MediaType(schema = None)
516
+ ),
517
+ required = true
518
+ ))),
519
+ responses = Responses(ChunkMap(
520
+ "201" -> ReferenceOr.Value(Response(
521
+ description = md"User created",
522
+ content = ChunkMap(
523
+ "application/json" -> MediaType(schema = None)
524
+ )
525
+ ))
526
+ ))
527
+ ))
528
+ ),
529
+ "/users/{id}" -> PathItem(
530
+ parameters = Chunk(
531
+ ReferenceOr.Value(Parameter(
532
+ name = "id",
533
+ in = ParameterLocation.Path,
534
+ required = true,
535
+ schema = Some(ReferenceOr.Value(Schema[String].toOpenAPISchema))
536
+ ))
537
+ ),
538
+ get = Some(Operation(
539
+ summary = Some(md"Get user by ID"),
540
+ responses = Responses(ChunkMap(
541
+ "200" -> ReferenceOr.Value(Response(
542
+ description = md"User found",
543
+ content = ChunkMap(
544
+ "application/json" -> MediaType(schema = None)
545
+ )
546
+ )),
547
+ "404" -> ReferenceOr.Value(Response(
548
+ description = md"User not found",
549
+ content = ChunkMap(
550
+ "application/json" -> MediaType(schema = None)
551
+ )
552
+ ))
553
+ ))
554
+ ))
555
+ )
556
+ ))
557
+ ```
558
+
559
+ ---
560
+
561
+ ## Operation
562
+
563
+ `Operation` represents a single HTTP operation (GET, POST, etc.) on a path.
564
+
565
+ ### Definition
566
+
567
+ Key fields:
568
+ - **`responses`**: Required. Map of status codes to response definitions
569
+ - **`operationId`**: Unique operation identifier
570
+ - **`summary`**: Short description
571
+ - **`description`**: Detailed markdown description
572
+ - **`parameters`**: Path, query, header, and cookie parameters
573
+ - **`requestBody`**: Request payload definition
574
+ - **`deprecated`**: Whether the operation is deprecated
575
+ - **`tags`**: Group operations in documentation (e.g., `"users"`, `"products"`)
576
+
577
+ ### Defining an Operation
578
+
579
+ With summary, description, and parameters:
580
+
581
+ ```scala
582
+ import zio.blocks.openapi._
583
+ import zio.blocks.docs._
584
+ import zio.blocks.chunk._
585
+ import zio.blocks.schema._
586
+
587
+ val getUser = Operation(
588
+ tags = Chunk("users"),
589
+ summary = Some(md"Retrieve user"),
590
+ description = Some(md"Fetches a single user by ID"),
591
+ operationId = Some("getUserById"),
592
+ parameters = Chunk(
593
+ ReferenceOr.Value(Parameter(
594
+ name = "id",
595
+ in = ParameterLocation.Path,
596
+ required = true,
597
+ schema = Some(ReferenceOr.Value(Schema[String].toOpenAPISchema)),
598
+ description = Some(md"User ID")
599
+ ))
600
+ ),
601
+ responses = Responses(ChunkMap(
602
+ "200" -> ReferenceOr.Value(Response(
603
+ description = md"User found",
604
+ content = ChunkMap(
605
+ "application/json" -> MediaType(schema = None)
606
+ )
607
+ )),
608
+ "404" -> ReferenceOr.Value(Response(
609
+ description = md"User not found",
610
+ content = ChunkMap(
611
+ "application/json" -> MediaType(schema = None)
612
+ )
613
+ ))
614
+ ))
615
+ )
616
+ ```
617
+
618
+ ---
619
+
620
+ ## Parameter
621
+
622
+ `Parameter` represents query, path, header, or cookie parameters in a request.
623
+
624
+ ### Definition
625
+
626
+ Required fields:
627
+ - **`name`**: Parameter name (e.g., `"id"`, `"limit"`)
628
+ - **`in`**: Location—`Path`, `Query`, `Header`, or `Cookie` (`ParameterLocation`)
629
+ - **`schema`**: Data type of the parameter (`ReferenceOr[SchemaObject]`)
630
+
631
+ Optional fields:
632
+ - **`description`**: Markdown description
633
+ - **`required`**: Whether the parameter is mandatory (default: `false`)
634
+ - **`deprecated`**: Whether the parameter is deprecated
635
+ - **`allowEmptyValue`**: Whether empty string values are allowed
636
+
637
+ ### Creating Parameters
638
+
639
+ Path parameter (required):
640
+
641
+ ```scala
642
+ import zio.blocks.openapi._
643
+ import zio.blocks.docs._
644
+ import zio.blocks.chunk._
645
+ import zio.blocks.schema._
646
+
647
+ val idPathParam = Parameter(
648
+ name = "id",
649
+ in = ParameterLocation.Path,
650
+ required = true,
651
+ schema = Some(ReferenceOr.Value(Schema[String].toOpenAPISchema)),
652
+ description = Some(md"User identifier")
653
+ )
654
+ ```
655
+
656
+ Query parameter (optional with default):
657
+
658
+ ```scala
659
+ import zio.blocks.openapi._
660
+ import zio.blocks.docs._
661
+ import zio.blocks.chunk._
662
+ import zio.blocks.schema._
663
+
664
+ val limitQueryParam = Parameter(
665
+ name = "limit",
666
+ in = ParameterLocation.Query,
667
+ required = false,
668
+ schema = Some(ReferenceOr.Value(Schema[Int].toOpenAPISchema)),
669
+ description = Some(md"Maximum number of results (default: 20)")
670
+ )
671
+ ```
672
+
673
+ Header parameter:
674
+
675
+ ```scala
676
+ import zio.blocks.openapi._
677
+ import zio.blocks.docs._
678
+ import zio.blocks.chunk._
679
+ import zio.blocks.schema._
680
+
681
+ val authHeaderParam = Parameter(
682
+ name = "X-API-Key",
683
+ in = ParameterLocation.Header,
684
+ required = true,
685
+ schema = Some(ReferenceOr.Value(Schema[String].toOpenAPISchema)),
686
+ description = Some(md"API key for authentication")
687
+ )
688
+ ```
689
+
690
+ ---
691
+
692
+ ## RequestBody & Response
693
+
694
+ `RequestBody` defines the structure of a request payload. `Response` defines the structure and status of a response.
695
+
696
+ ### RequestBody Definition
697
+
698
+ Key fields:
699
+ - **`content`**: Map of MIME types to `MediaType` definitions
700
+ - **`description`**: Optional markdown description
701
+ - **`required`**: Whether the request body is mandatory (default: `false`)
702
+
703
+ ### Creating a RequestBody
704
+
705
+ ```scala
706
+ import zio.blocks.openapi._
707
+ import zio.blocks.docs._
708
+ import zio.blocks.chunk._
709
+ import zio.blocks.schema._
710
+ import zio.blocks.schema.json._
711
+
712
+ case class User(name: String, email: String)
713
+ object User { implicit val schema: Schema[User] = Schema.derived }
714
+
715
+ val createUserBody = RequestBody(
716
+ description = Some(md"User data to create"),
717
+ content = ChunkMap(
718
+ "application/json" -> MediaType(
719
+ schema = Some(ReferenceOr.Value(Schema[User].toOpenAPISchema)),
720
+ example = Some(Json.Object(Chunk(
721
+ "name" -> Json.String("John Doe"),
722
+ "email" -> Json.String("john@example.com")
723
+ )))
724
+ )
725
+ ),
726
+ required = true
727
+ )
728
+ ```
729
+
730
+ ### Response Definition
731
+
732
+ Key fields:
733
+ - **`description`**: Required. Markdown description of the response
734
+ - **`content`**: Map of MIME types to `MediaType` definitions
735
+ - **`headers`**: Optional response headers
736
+ - **`links`**: Optional links to related operations
737
+
738
+ ### Creating a Response
739
+
740
+ ```scala
741
+ import zio.blocks.openapi._
742
+ import zio.blocks.docs._
743
+ import zio.blocks.chunk._
744
+ import zio.blocks.schema._
745
+
746
+ case class User2(id: Int, name: String, email: String)
747
+ object User2 { implicit val schema: Schema[User2] = Schema.derived }
748
+ case class ErrorResponse2(code: Int, message: String)
749
+ object ErrorResponse2 { implicit val schema: Schema[ErrorResponse2] = Schema.derived }
750
+
751
+ val successResponse = Response(
752
+ description = md"User successfully created",
753
+ content = ChunkMap(
754
+ "application/json" -> MediaType(
755
+ schema = Some(ReferenceOr.Value(Schema[User2].toOpenAPISchema))
756
+ )
757
+ )
758
+ )
759
+
760
+ val errorResponse = Response(
761
+ description = md"Request validation failed",
762
+ content = ChunkMap(
763
+ "application/json" -> MediaType(
764
+ schema = Some(ReferenceOr.Value(Schema[ErrorResponse2].toOpenAPISchema))
765
+ )
766
+ )
767
+ )
768
+ ```
769
+
770
+ ### Responses
771
+
772
+ `Responses` is a map of HTTP status codes to `ReferenceOr[Response]`:
773
+
774
+ ```scala
775
+ import zio.blocks.openapi._
776
+ import zio.blocks.docs._
777
+ import zio.blocks.chunk._
778
+ import zio.blocks.schema._
779
+
780
+ val ok = Response(description = md"Created", content = ChunkMap())
781
+ val err = Response(description = md"Bad request", content = ChunkMap())
782
+
783
+ val responses = Responses(ChunkMap(
784
+ "201" -> ReferenceOr.Value(ok),
785
+ "400" -> ReferenceOr.Value(err),
786
+ "401" -> ReferenceOr.Value(Response(
787
+ description = md"Unauthorized",
788
+ content = ChunkMap()
789
+ )),
790
+ "500" -> ReferenceOr.Value(Response(
791
+ description = md"Internal server error",
792
+ content = ChunkMap()
793
+ ))
794
+ ))
795
+ ```
796
+
797
+ ---
798
+
799
+ ## MediaType
800
+
801
+ `MediaType` specifies the schema and encoding for a particular MIME type in a request or response.
802
+
803
+ ### Definition
804
+
805
+ Key fields:
806
+ - **`schema`**: Data type for this MIME type (`ReferenceOr[SchemaObject]`)
807
+ - **`example`**: Example value as `Json`
808
+ - **`encoding`**: Encoding rules for multipart form data
809
+ - **`extensions`**: Custom vendor extensions (`x-*` fields)
810
+
811
+ ### Creating MediaType
812
+
813
+ With schema and example:
814
+
815
+ ```scala
816
+ import zio.blocks.openapi._
817
+ import zio.blocks.docs._
818
+ import zio.blocks.chunk._
819
+ import zio.blocks.schema._
820
+ import zio.blocks.schema.json._
821
+
822
+ case class User(id: Int, name: String, email: String)
823
+ object User { implicit val schema: Schema[User] = Schema.derived }
824
+
825
+ val jsonMedia = MediaType(
826
+ schema = Some(ReferenceOr.Value(Schema[User].toOpenAPISchema)),
827
+ example = Some(Json.Object(
828
+ "id" -> Json.Number(1),
829
+ "name" -> Json.String("Alice"),
830
+ "email" -> Json.String("alice@example.com")
831
+ ))
832
+ )
833
+ ```
834
+
835
+ For form data:
836
+
837
+ ```scala
838
+ import zio.blocks.openapi._
839
+ import zio.blocks.docs._
840
+ import zio.blocks.chunk._
841
+ import zio.blocks.schema._
842
+
843
+ val formMedia = MediaType(
844
+ schema = Some(ReferenceOr.Value(Schema[Map[String, String]].toOpenAPISchema)),
845
+ encoding = ChunkMap(
846
+ "file" -> Encoding(
847
+ contentType = Some("application/octet-stream")
848
+ )
849
+ )
850
+ )
851
+ ```
852
+
853
+ ---
854
+
855
+ ## Components
856
+
857
+ `Components` stores reusable schema and security definitions referenced throughout the document.
858
+
859
+ ### Definition
860
+
861
+ Key fields:
862
+ - **`schemas`**: Reusable schema objects (`ChunkMap[String, ReferenceOr[SchemaObject]]`)
863
+ - **`responses`**: Reusable response definitions
864
+ - **`parameters`**: Reusable parameter definitions
865
+ - **`securitySchemes`**: Authentication method definitions
866
+ - **`examples`**, **`requestBodies`**, **`headers`**, **`links`**, **`callbacks`**: Additional reusable components
867
+
868
+ ### Creating Components
869
+
870
+ ```scala
871
+ import zio.blocks.openapi._
872
+ import zio.blocks.docs._
873
+ import zio.blocks.chunk._
874
+ import zio.blocks.schema._
875
+
876
+ case class User(id: Int, name: String, email: String)
877
+ object User { implicit val schema: Schema[User] = Schema.derived }
878
+ case class ErrorResponse(code: Int, message: String)
879
+ object ErrorResponse { implicit val schema: Schema[ErrorResponse] = Schema.derived }
880
+
881
+ val components = Components(
882
+ schemas = ChunkMap(
883
+ Schema[User].toRefSchema._2._1 -> ReferenceOr.Value(Schema[User].toRefSchema._2._2),
884
+ Schema[ErrorResponse].toRefSchema._2._1 -> ReferenceOr.Value(Schema[ErrorResponse].toRefSchema._2._2)
885
+ ),
886
+ parameters = ChunkMap(
887
+ "id" -> ReferenceOr.Value(Parameter(
888
+ name = "id",
889
+ in = ParameterLocation.Path,
890
+ required = true,
891
+ schema = Some(ReferenceOr.Value(Schema[String].toOpenAPISchema))
892
+ ))
893
+ ),
894
+ responses = ChunkMap(
895
+ "NotFound" -> ReferenceOr.Value(Response(
896
+ description = md"Resource not found",
897
+ content = ChunkMap(
898
+ "application/json" -> MediaType(
899
+ schema = Some(ReferenceOr.Value(
900
+ Schema[ErrorResponse].toOpenAPISchema
901
+ ))
902
+ )
903
+ )
904
+ )),
905
+ "Unauthorized" -> ReferenceOr.Value(Response(
906
+ description = md"Unauthorized access",
907
+ content = ChunkMap()
908
+ ))
909
+ ),
910
+ securitySchemes = ChunkMap(
911
+ "api_key" -> ReferenceOr.Value(SecurityScheme.APIKey(
912
+ name = "X-API-Key",
913
+ in = APIKeyLocation.Header,
914
+ description = Some(md"API key header")
915
+ ))
916
+ )
917
+ )
918
+ ```
919
+
920
+ ---
921
+
922
+ ## SchemaObject
923
+
924
+ `SchemaObject` wraps a JSON Schema 2020-12 definition with OpenAPI-specific extensions like discriminator, XML metadata, and examples.
925
+
926
+ ### Definition
927
+
928
+ `SchemaObject` contains:
929
+ - **`jsonSchema`**: Raw JSON Schema 2020-12 as `Json` AST
930
+ - **`discriminator`**: Polymorphism discriminator for oneOf/anyOf
931
+ - **`xml`**: XML serialization metadata
932
+ - **`example`**: Example value for documentation
933
+ - **`extensions`**: Custom `x-*` fields
934
+
935
+ ### Creating SchemaObject
936
+
937
+ Directly from a `Schema[A]`:
938
+
939
+ ```scala
940
+ import zio.blocks.openapi._
941
+ import zio.blocks.docs._
942
+ import zio.blocks.chunk._
943
+ import zio.blocks.schema._
944
+
945
+ case class User(id: Int, name: String, email: String)
946
+ object User { implicit val schema: Schema[User] = Schema.derived }
947
+
948
+ val userSchema = Schema[User].toOpenAPISchema
949
+ // Returns a SchemaObject with the User type's JSON Schema
950
+ ```
951
+
952
+ Or with additional OpenAPI metadata:
953
+
954
+ ```scala
955
+ import zio.blocks.openapi._
956
+ import zio.blocks.docs._
957
+ import zio.blocks.chunk._
958
+ import zio.blocks.schema._
959
+ import zio.blocks.schema.json._
960
+
961
+ case class User(id: Int, name: String, email: String)
962
+ object User { implicit val schema: Schema[User] = Schema.derived }
963
+
964
+ val enrichedSchema = SchemaObject(
965
+ jsonSchema = Schema[User].toJsonSchema.toJson,
966
+ discriminator = None,
967
+ xml = Some(XML(
968
+ name = Some("user"),
969
+ namespace = None,
970
+ prefix = None,
971
+ attribute = false,
972
+ wrapped = false
973
+ )),
974
+ example = Some(Json.Object(
975
+ "id" -> Json.Number(1),
976
+ "name" -> Json.String("John")
977
+ )),
978
+ extensions = ChunkMap(
979
+ "x-generated" -> Json.String("true"),
980
+ "x-version" -> Json.String("1.0.0")
981
+ )
982
+ )
983
+ ```
984
+
985
+ ### Converting Schemas to SchemaObject
986
+
987
+ Use the `SchemaOps` extension methods on any `Schema[A]`:
988
+
989
+ ```scala
990
+ import zio.blocks.openapi._
991
+ import zio.blocks.docs._
992
+ import zio.blocks.chunk._
993
+ import zio.blocks.schema._
994
+
995
+ case class User(id: Int, name: String, email: String)
996
+ object User { implicit val schema: Schema[User] = Schema.derived }
997
+
998
+ val schemaObj: SchemaObject = Schema[User].toOpenAPISchema
999
+ val (ref, component) = Schema[User].toRefSchema
1000
+ // ref = ReferenceOr.Ref pointing to #/components/schemas/User
1001
+ // component = ("User", SchemaObject(...))
1002
+ ```
1003
+
1004
+ ---
1005
+
1006
+ ## ReferenceOr
1007
+
1008
+ `ReferenceOr[A]` is a sealed trait representing the OpenAPI pattern of choosing between a `$ref` and an inline value.
1009
+
1010
+ ### Definition
1011
+
1012
+ Two cases:
1013
+ - **`ReferenceOr.Ref`**: Points to a definition at `#/components/<type>/<name>`
1014
+ - **`ReferenceOr.Value`**: Inline definition without reference
1015
+
1016
+ ### Using ReferenceOr
1017
+
1018
+ Prefer `Ref` for reusable components:
1019
+
1020
+ ```scala
1021
+ import zio.blocks.openapi._
1022
+ import zio.blocks.docs._
1023
+ import zio.blocks.chunk._
1024
+ import zio.blocks.schema._
1025
+
1026
+ val userRef = ReferenceOr.Ref(Reference(`$ref` = "#/components/schemas/User"))
1027
+
1028
+ val responseRef = ReferenceOr.Ref(Reference(
1029
+ `$ref` = "#/components/responses/NotFound"
1030
+ ))
1031
+ ```
1032
+
1033
+ Use `Value` for inline, one-off definitions:
1034
+
1035
+ ```scala
1036
+ import zio.blocks.openapi._
1037
+ import zio.blocks.docs._
1038
+ import zio.blocks.chunk._
1039
+ import zio.blocks.schema._
1040
+
1041
+ case class User(id: Int, name: String, email: String)
1042
+ object User { implicit val schema: Schema[User] = Schema.derived }
1043
+
1044
+ val inlineUser = ReferenceOr.Value(Schema[User].toOpenAPISchema)
1045
+
1046
+ val inlineError = ReferenceOr.Value(Response(
1047
+ description = md"Quick error",
1048
+ content = ChunkMap()
1049
+ ))
1050
+ ```
1051
+
1052
+ ### Pattern Matching on ReferenceOr
1053
+
1054
+ ```scala
1055
+ import zio.blocks.openapi._
1056
+ import zio.blocks.docs._
1057
+ import zio.blocks.chunk._
1058
+ import zio.blocks.schema._
1059
+
1060
+ def describeRef[A](ref: ReferenceOr[A]): String = ref match {
1061
+ case ReferenceOr.Ref(r) => s"Reference to ${r.`$ref`}"
1062
+ case ReferenceOr.Value(_) => "Inline value"
1063
+ }
1064
+ ```
1065
+
1066
+ ---
1067
+
1068
+ ## SecurityScheme
1069
+
1070
+ `SecurityScheme` is a sealed trait representing different authentication methods. Variants include API Key, HTTP Basic/Bearer, OAuth 2.0, OpenID Connect, and Mutual TLS.
1071
+
1072
+ ### Definition
1073
+
1074
+ Sealed trait variants:
1075
+ - **`APIKey`**: API key in header, query, or cookie
1076
+ - **`HTTP`**: HTTP authentication (Basic, Bearer, etc.)
1077
+ - **`OAuth2`**: OAuth 2.0 authorization flows
1078
+ - **`OpenIdConnect`**: OpenID Connect discovery
1079
+ - **`MutualTLS`**: Mutual TLS certificate-based
1080
+
1081
+ ### Creating Security Schemes
1082
+
1083
+ API Key authentication:
1084
+
1085
+ ```scala
1086
+ import zio.blocks.openapi._
1087
+ import zio.blocks.docs._
1088
+ import zio.blocks.chunk._
1089
+ import zio.blocks.schema._
1090
+
1091
+ val apiKeySecurity = SecurityScheme.APIKey(
1092
+ name = "X-API-Key",
1093
+ in = APIKeyLocation.Header,
1094
+ description = Some(md"API key required in header")
1095
+ )
1096
+ ```
1097
+
1098
+ HTTP Bearer token:
1099
+
1100
+ ```scala
1101
+ import zio.blocks.openapi._
1102
+ import zio.blocks.docs._
1103
+ import zio.blocks.chunk._
1104
+ import zio.blocks.schema._
1105
+
1106
+ val bearerSecurity = SecurityScheme.HTTP(
1107
+ scheme = "bearer",
1108
+ bearerFormat = Some("JWT"),
1109
+ description = Some(md"JWT bearer token")
1110
+ )
1111
+ ```
1112
+
1113
+ OAuth 2.0:
1114
+
1115
+ ```scala
1116
+ import zio.blocks.openapi._
1117
+ import zio.blocks.docs._
1118
+ import zio.blocks.chunk._
1119
+ import zio.blocks.schema._
1120
+
1121
+ val oauthSecurity = SecurityScheme.OAuth2(
1122
+ flows = OAuthFlows(
1123
+ authorizationCode = Some(OAuthFlow(
1124
+ authorizationUrl = Some("https://example.com/oauth/authorize"),
1125
+ tokenUrl = Some("https://example.com/oauth/token"),
1126
+ scopes = ChunkMap(
1127
+ "read:users" -> "Read user data",
1128
+ "write:users" -> "Modify user data"
1129
+ )
1130
+ ))
1131
+ ),
1132
+ description = Some(md"OAuth 2.0 authorization")
1133
+ )
1134
+ ```
1135
+
1136
+ :::note
1137
+ The `OAuthFlows` type supports multiple flow types: `implicit`, `password`, `clientCredentials`, and `authorizationCode`. Choose the flow that matches your OAuth 2.0 configuration.
1138
+ :::
1139
+
1140
+ OpenID Connect:
1141
+
1142
+ ```scala
1143
+ import zio.blocks.openapi._
1144
+ import zio.blocks.docs._
1145
+ import zio.blocks.chunk._
1146
+ import zio.blocks.schema._
1147
+
1148
+ val oidcSecurity = SecurityScheme.OpenIdConnect(
1149
+ openIdConnectUrl = "https://example.com/.well-known/openid-configuration",
1150
+ description = Some(md"OpenID Connect discovery")
1151
+ )
1152
+ ```
1153
+
1154
+ ---
1155
+
1156
+ ## Discriminator
1157
+
1158
+ `Discriminator` specifies how to distinguish between different variants in a polymorphic schema (using `oneOf` or `anyOf`).
1159
+
1160
+ ### Definition
1161
+
1162
+ Key fields:
1163
+ - **`propertyName`**: Field name used to discriminate (e.g., `"type"`, `"kind"`)
1164
+ - **`mapping`**: Optional explicit mapping of discriminator values to schema references
1165
+
1166
+ ### Creating a Discriminator
1167
+
1168
+ Simple discriminator by property name:
1169
+
1170
+ ```scala
1171
+ import zio.blocks.openapi._
1172
+ import zio.blocks.docs._
1173
+ import zio.blocks.chunk._
1174
+ import zio.blocks.schema._
1175
+
1176
+ val discriminator = Discriminator(propertyName = "type")
1177
+ ```
1178
+
1179
+ With explicit value-to-schema mapping:
1180
+
1181
+ ```scala
1182
+ import zio.blocks.openapi._
1183
+ import zio.blocks.docs._
1184
+ import zio.blocks.chunk._
1185
+ import zio.blocks.schema._
1186
+
1187
+ val mappedDiscriminator = Discriminator(
1188
+ propertyName = "kind",
1189
+ mapping = ChunkMap(
1190
+ "user" -> "#/components/schemas/User",
1191
+ "admin" -> "#/components/schemas/Admin",
1192
+ "guest" -> "#/components/schemas/Guest"
1193
+ )
1194
+ )
1195
+ ```
1196
+
1197
+ ---
1198
+
1199
+ ## Server & ServerVariable
1200
+
1201
+ `Server` specifies base URLs and server-specific variables. `ServerVariable` allows parameterization of server URLs.
1202
+
1203
+ ### Definition
1204
+
1205
+ `Server` contains:
1206
+ - **`url`**: Server URL (may contain variable placeholders like `{base_path}`)
1207
+ - **`description`**: Optional description
1208
+ - **`variables`**: Map of variable names to `ServerVariable`
1209
+
1210
+ `ServerVariable` contains:
1211
+ - **`enum`**: List of allowed values
1212
+ - **`default`**: Default value
1213
+ - **`description`**: Description of the variable
1214
+
1215
+ ### Creating Servers
1216
+
1217
+ Single static server:
1218
+
1219
+ ```scala
1220
+ import zio.blocks.openapi._
1221
+ import zio.blocks.docs._
1222
+ import zio.blocks.chunk._
1223
+ import zio.blocks.schema._
1224
+
1225
+ val server = Server(
1226
+ url = "https://api.example.com",
1227
+ description = Some(md"Production API")
1228
+ )
1229
+ ```
1230
+
1231
+ Server with variables:
1232
+
1233
+ ```scala
1234
+ import zio.blocks.openapi._
1235
+ import zio.blocks.docs._
1236
+ import zio.blocks.chunk._
1237
+ import zio.blocks.schema._
1238
+
1239
+ val variableServer = Server(
1240
+ url = "https://{host}:{port}/{basePath}",
1241
+ description = Some(md"Development API with variables"),
1242
+ variables = ChunkMap(
1243
+ "host" -> ServerVariable(
1244
+ default = "localhost",
1245
+ `enum` = Chunk("localhost", "staging.example.com", "api.example.com"),
1246
+ description = Some(md"API host")
1247
+ ),
1248
+ "port" -> ServerVariable(
1249
+ default = "8080",
1250
+ `enum` = Chunk("8080", "443"),
1251
+ description = Some(md"Port number")
1252
+ ),
1253
+ "basePath" -> ServerVariable(
1254
+ default = "v1",
1255
+ `enum` = Chunk("v1", "v2"),
1256
+ description = Some(md"API version path")
1257
+ )
1258
+ )
1259
+ )
1260
+ ```
1261
+
1262
+ :::note
1263
+ `ServerVariable` contains enumerable values for each variable. The field is named to avoid the reserved `enum` keyword in Scala.
1264
+ :::
1265
+
1266
+ ---
1267
+
1268
+ ## Tag
1269
+
1270
+ `Tag` groups related operations under a heading in generated documentation.
1271
+
1272
+ ### Definition
1273
+
1274
+ Key fields:
1275
+ - **`name`**: Tag identifier (e.g., `"users"`, `"products"`)
1276
+ - **`description`**: Markdown description of the tag
1277
+ - **`externalDocs`**: Link to external documentation
1278
+
1279
+ ### Creating Tags
1280
+
1281
+ ```scala
1282
+ import zio.blocks.openapi._
1283
+ import zio.blocks.docs._
1284
+ import zio.blocks.chunk._
1285
+ import zio.blocks.schema._
1286
+
1287
+ val userTag = Tag(
1288
+ name = "users",
1289
+ description = Some(md"User management operations")
1290
+ )
1291
+
1292
+ val productsTag = Tag(
1293
+ name = "products",
1294
+ description = Some(md"Product catalog operations"),
1295
+ externalDocs = Some(ExternalDocumentation(
1296
+ url = "https://docs.example.com/products",
1297
+ description = Some(md"Full product API documentation")
1298
+ ))
1299
+ )
1300
+ ```
1301
+
1302
+ ---
1303
+
1304
+ ## Common Extension Fields
1305
+
1306
+ All types support custom `x-*` extension fields for vendor-specific metadata. These extensions are preserved during encoding/decoding:
1307
+
1308
+ ```scala
1309
+ import zio.blocks.openapi._
1310
+ import zio.blocks.docs._
1311
+ import zio.blocks.chunk._
1312
+ import zio.blocks.schema._
1313
+ import zio.blocks.schema.json._
1314
+
1315
+ val operationWithExtensions = Operation(
1316
+ summary = Some(md"Get user"),
1317
+ responses = Responses(ChunkMap(
1318
+ "200" -> ReferenceOr.Value(Response(
1319
+ description = md"User found",
1320
+ content = ChunkMap()
1321
+ ))
1322
+ )),
1323
+ extensions = ChunkMap(
1324
+ "x-internal" -> Json.Boolean(true),
1325
+ "x-rate-limit" -> Json.Number(100),
1326
+ "x-deprecated-at" -> Json.String("2024-01-01")
1327
+ )
1328
+ )
1329
+ ```
1330
+
1331
+ ---
1332
+
1333
+ ## Round-Tripping with Schema
1334
+
1335
+ All OpenAPI types have `Schema.derived` instances, enabling serialization through `DynamicValue`:
1336
+
1337
+ ```scala
1338
+ import zio.blocks.openapi._
1339
+ import zio.blocks.docs._
1340
+ import zio.blocks.chunk._
1341
+ import zio.blocks.schema._
1342
+
1343
+ val myApi = OpenAPI(openapi = "3.1.0", info = Info(title = "My API", version = "1.0.0"))
1344
+ val openAPISchema = Schema[OpenAPI]
1345
+
1346
+ val apiDynamic = openAPISchema.toDynamicValue(myApi)
1347
+
1348
+ val apiRestored = openAPISchema.fromDynamicValue(apiDynamic)
1349
+ ```
1350
+
1351
+ This enables integration with other ZIO Blocks modules that work with `DynamicValue`.