@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
@@ -0,0 +1,1032 @@
1
+ ---
2
+ id: smithy
3
+ title: "Smithy"
4
+ ---
5
+
6
+ `zio-blocks-smithy` is a **Smithy IDL parser and AST library** providing a complete representation of Smithy 2.0 API models. It enables parsing Smithy IDL text into rich data structures, querying shape definitions, and pretty-printing models back to valid IDL syntax—all without external dependencies.
7
+
8
+ ## Installation
9
+
10
+ Add the library to your build configuration:
11
+
12
+ ```scala
13
+ libraryDependencies += "dev.zio" %% "zio-blocks-smithy" % "0.0.55"
14
+ ```
15
+
16
+ Supported Scala versions: 2.13.x and 3.x
17
+
18
+ ## Quick Start
19
+
20
+ Parse Smithy IDL text into a model, query shapes, and serialize back:
21
+
22
+ ```scala
23
+ import zio.blocks.smithy._
24
+
25
+ val smithyText = """$version: "2"
26
+ namespace com.example.api
27
+
28
+ structure User {
29
+ @required
30
+ id: String
31
+ name: String
32
+ }
33
+
34
+ operation GetUser {
35
+ input: GetUserInput
36
+ output: User
37
+ }
38
+
39
+ structure GetUserInput {
40
+ @required
41
+ id: String
42
+ }
43
+ """
44
+
45
+ // Parse IDL text into a model
46
+ val result = SmithyModel.parse(smithyText)
47
+
48
+ // Access shapes and data
49
+ result match {
50
+ case Right(model) =>
51
+ model.findShape("User").foreach { userDef =>
52
+ println(s"Found shape: ${userDef.name}")
53
+ }
54
+ case Left(error) =>
55
+ println(s"Parse error: ${error.message}")
56
+ }
57
+
58
+ // Serialize back to IDL
59
+ result.foreach { model =>
60
+ val idlText = model.prettyPrint
61
+ println(idlText)
62
+ }
63
+ ```
64
+
65
+ ## Core Types
66
+
67
+ The library provides core types that work together to parse, query, and serialize Smithy models. The main types work together in a parsing → querying → serialization pipeline:
68
+
69
+ ```
70
+ Smithy IDL Text
71
+ ↓
72
+ SmithyModel.parse (public API)
73
+ ↓
74
+ SmithyModel (contains shapes, metadata, traits)
75
+ ├─ shapes: List[ShapeDefinition]
76
+ │ └─ shape: Shape (sealed trait — central type)
77
+ │ ├─ StructureShape(members: List[MemberDefinition])
78
+ │ ├─ ListShape(member: MemberDefinition)
79
+ │ ├─ MapShape(key: MemberDefinition, value: MemberDefinition)
80
+ │ ├─ ServiceShape(operations, resources, errors)
81
+ │ ├─ OperationShape(input, output, errors)
82
+ │ ├─ UnionShape(members: List[MemberDefinition])
83
+ │ ├─ EnumShape(members: List[EnumMember])
84
+ │ ├─ ResourceShape(identifiers, create, read, update, delete, list, ...)
85
+ │ ├─ StringShape, BooleanShape, IntegerShape, and 10 more simple shapes
86
+ │ └─ ... (20 subtypes in total — see the Shape Catalog below)
87
+ ├─ MemberDefinition(name: String, target: ShapeId, traits: List[TraitApplication])
88
+ ├─ TraitApplication(id: ShapeId, value: Option[NodeValue])
89
+ ├─ ShapeId (namespace + name identifier)
90
+ └─ NodeValue (metadata values: String, Number, Boolean, Array, Object, Null)
91
+ ↓
92
+ SmithyModel.prettyPrint (public API)
93
+ ↓
94
+ Smithy IDL Text
95
+ ```
96
+
97
+ The root container for a Smithy model. Contains version, namespace, shapes, metadata, and trait applications. The case class and companion object expose the following API:
98
+
99
+ ```scala
100
+ case class SmithyModel(
101
+ version: String, // Smithy version (e.g., "2")
102
+ namespace: String,
103
+ useStatements: List[ShapeId],
104
+ metadata: Map[String, NodeValue],
105
+ shapes: List[ShapeDefinition],
106
+ applyStatements: List[ApplyStatement] = Nil
107
+ ) {
108
+ def findShape(name: String): Option[ShapeDefinition]
109
+ def allShapeIds: List[ShapeId]
110
+ def prettyPrint: String
111
+ def prettyPrint(indent: Int): String
112
+ }
113
+
114
+ object SmithyModel {
115
+ def parse(input: String): Either[SmithyError, SmithyModel]
116
+ }
117
+ ```
118
+
119
+ ## Shape Catalog
120
+
121
+ `Shape` is a sealed trait with 20 subtypes, covering the shape categories the Smithy specification defines. Every shape carries a `Shape#name` and a list of applied `Shape#traits`; the families differ in what else they hold:
122
+
123
+ ```scala
124
+ sealed trait Shape {
125
+ def name: String
126
+ def traits: List[TraitApplication]
127
+ }
128
+ ```
129
+
130
+ | Family | Subtypes | What they add |
131
+ | --------- | -------: | ---------------------------------------- |
132
+ | Simple | 13 | Nothing — name and traits only |
133
+ | Enum | 2 | A member list of allowed values |
134
+ | Aggregate | 4 | Member definitions naming target shapes |
135
+ | Service | 3 | `ShapeId` references to other shapes |
136
+
137
+ A parsed shape is wrapped in a `ShapeDefinition`, which pairs the name with the shape. `Shape` also carries its own `Shape#name`, so the two agree and either can be read:
138
+
139
+ ```scala
140
+ final case class ShapeDefinition(name: String, shape: Shape)
141
+ ```
142
+
143
+ ### Simple Shapes
144
+
145
+ Thirteen shapes carry no structure beyond their name and traits. They differ only in which IDL keyword produces them and what the target protocol is expected to do with them:
146
+
147
+ | Type | IDL keyword | Represents |
148
+ | ----------------- | ------------ | --------------------------------- |
149
+ | `BlobShape` | `blob` | Arbitrary binary data |
150
+ | `BooleanShape` | `boolean` | True/false values |
151
+ | `StringShape` | `string` | UTF-8 text |
152
+ | `ByteShape` | `byte` | 8-bit signed integer |
153
+ | `ShortShape` | `short` | 16-bit signed integer |
154
+ | `IntegerShape` | `integer` | 32-bit signed integer |
155
+ | `LongShape` | `long` | 64-bit signed integer |
156
+ | `FloatShape` | `float` | Single-precision IEEE 754 |
157
+ | `DoubleShape` | `double` | Double-precision IEEE 754 |
158
+ | `BigIntegerShape` | `bigInteger` | Arbitrarily large signed integer |
159
+ | `BigDecimalShape` | `bigDecimal` | Arbitrary-precision decimal |
160
+ | `TimestampShape` | `timestamp` | A point in time |
161
+ | `DocumentShape` | `document` | Protocol-agnostic open content |
162
+
163
+ Because they share one shape, parsing them produces values that differ only in their type:
164
+
165
+ ```scala
166
+ import zio.blocks.smithy._
167
+
168
+ val simpleModel = SmithyModel.parse(
169
+ """$version: "2"
170
+ |namespace com.example
171
+ |blob Payload
172
+ |timestamp CreatedAt
173
+ |document Metadata
174
+ |""".stripMargin
175
+ ).toOption.get
176
+ ```
177
+
178
+ Each definition names the shape and holds the corresponding subtype:
179
+
180
+ ```scala
181
+ simpleModel.shapes
182
+ // res1: List[ShapeDefinition] = List(
183
+ // ShapeDefinition(
184
+ // name = "Payload",
185
+ // shape = BlobShape(name = "Payload", traits = List())
186
+ // ),
187
+ // ShapeDefinition(
188
+ // name = "CreatedAt",
189
+ // shape = TimestampShape(name = "CreatedAt", traits = List())
190
+ // ),
191
+ // ShapeDefinition(
192
+ // name = "Metadata",
193
+ // shape = DocumentShape(name = "Metadata", traits = List())
194
+ // )
195
+ // )
196
+ ```
197
+
198
+ `DocumentShape` is the one to reach for when a field's contents are not known at model time — it is the Smithy equivalent of an open JSON value, and no member list constrains it.
199
+
200
+ ### Enum Shapes
201
+
202
+ `EnumShape` and `IntEnumShape` each hold a fixed set of permitted values. They differ in the value type and in whether the value is optional:
203
+
204
+ ```scala
205
+ final case class EnumShape(
206
+ name: String,
207
+ traits: List[TraitApplication] = Nil,
208
+ members: List[EnumMember] = Nil
209
+ ) extends Shape
210
+
211
+ final case class IntEnumShape(
212
+ name: String,
213
+ traits: List[TraitApplication] = Nil,
214
+ members: List[IntEnumMember] = Nil
215
+ ) extends Shape
216
+ ```
217
+
218
+ `EnumMember` carries an `Option[String]`, because the IDL allows a bare member name; `IntEnumMember` carries a required `Int`, because an integer enum has no name to fall back on:
219
+
220
+ ```scala
221
+ final case class EnumMember(name: String, value: Option[String] = None, traits: List[TraitApplication] = Nil)
222
+ final case class IntEnumMember(name: String, value: Int, traits: List[TraitApplication] = Nil)
223
+ ```
224
+
225
+ A string enum with explicit values fills in each `value`:
226
+
227
+ ```scala
228
+ val colorModel = SmithyModel.parse(
229
+ """$version: "2"
230
+ |namespace com.example
231
+ |enum Color {
232
+ | RED = "red"
233
+ | GREEN = "green"
234
+ |}
235
+ |""".stripMargin
236
+ ).toOption.get
237
+ ```
238
+
239
+ Each member pairs the declared name with the string it maps to:
240
+
241
+ ```scala
242
+ colorModel.shapes
243
+ // res2: List[ShapeDefinition] = List(
244
+ // ShapeDefinition(
245
+ // name = "Color",
246
+ // shape = EnumShape(
247
+ // name = "Color",
248
+ // traits = List(),
249
+ // members = List(
250
+ // EnumMember(name = "RED", value = Some("red"), traits = List()),
251
+ // EnumMember(name = "GREEN", value = Some("green"), traits = List())
252
+ // )
253
+ // )
254
+ // )
255
+ // )
256
+ ```
257
+
258
+ Omitting the values leaves `value` absent rather than duplicating the name, so a consumer wanting the effective wire value reads `EnumMember#value` and falls back to `EnumMember#name`:
259
+
260
+ ```scala
261
+ val suitModel = SmithyModel.parse(
262
+ """$version: "2"
263
+ |namespace com.example
264
+ |enum Suit {
265
+ | CLUB
266
+ | HEART
267
+ |}
268
+ |""".stripMargin
269
+ ).toOption.get
270
+ ```
271
+
272
+ The distinction survives parsing, which is what lets a round trip reproduce the original document:
273
+
274
+ ```scala
275
+ suitModel.shapes
276
+ // res3: List[ShapeDefinition] = List(
277
+ // ShapeDefinition(
278
+ // name = "Suit",
279
+ // shape = EnumShape(
280
+ // name = "Suit",
281
+ // traits = List(),
282
+ // members = List(
283
+ // EnumMember(name = "CLUB", value = None, traits = List()),
284
+ // EnumMember(name = "HEART", value = None, traits = List())
285
+ // )
286
+ // )
287
+ // )
288
+ // )
289
+ ```
290
+
291
+ An integer enum requires a value for every member, so `IntEnumMember` holds an `Int` rather than an option:
292
+
293
+ ```scala
294
+ val cardModel = SmithyModel.parse(
295
+ """$version: "2"
296
+ |namespace com.example
297
+ |intEnum FaceCard {
298
+ | JACK = 11
299
+ | QUEEN = 12
300
+ |}
301
+ |""".stripMargin
302
+ ).toOption.get
303
+ ```
304
+
305
+ Reading the members gives the integers directly:
306
+
307
+ ```scala
308
+ cardModel.shapes
309
+ // res4: List[ShapeDefinition] = List(
310
+ // ShapeDefinition(
311
+ // name = "FaceCard",
312
+ // shape = IntEnumShape(
313
+ // name = "FaceCard",
314
+ // traits = List(),
315
+ // members = List(
316
+ // IntEnumMember(name = "JACK", value = 11, traits = List()),
317
+ // IntEnumMember(name = "QUEEN", value = 12, traits = List())
318
+ // )
319
+ // )
320
+ // )
321
+ // )
322
+ ```
323
+
324
+ ### Aggregate Shapes
325
+
326
+ Four shapes compose other shapes, and all of them do it through `MemberDefinition` — a name, the `ShapeId` of the target, and any traits on the member itself:
327
+
328
+ | Type | IDL keyword | Members |
329
+ | ---------------- | ----------- | ---------------------------------------------------- |
330
+ | `ListShape` | `list` | `member: MemberDefinition` |
331
+ | `MapShape` | `map` | `key` and `value`, both `MemberDefinition` |
332
+ | `StructureShape` | `structure` | `members: List[MemberDefinition]` |
333
+ | `UnionShape` | `union` | `members: List[MemberDefinition]`, one set at a time |
334
+
335
+ A structure's members name their targets by `ShapeId`, not by nested shape, so the model stays flat and a member's target is resolved by lookup:
336
+
337
+ ```scala
338
+ val userModel = SmithyModel.parse(
339
+ """$version: "2"
340
+ |namespace com.example
341
+ |structure User {
342
+ | @required
343
+ | id: String
344
+ | tags: TagList
345
+ |}
346
+ |list TagList {
347
+ | member: String
348
+ |}
349
+ |""".stripMargin
350
+ ).toOption.get
351
+ ```
352
+
353
+ Both shapes appear at the top level, and the `tags` member of `User` points at `TagList` by reference:
354
+
355
+ ```scala
356
+ userModel.shapes
357
+ // res5: List[ShapeDefinition] = List(
358
+ // ShapeDefinition(
359
+ // name = "User",
360
+ // shape = StructureShape(
361
+ // name = "User",
362
+ // traits = List(),
363
+ // members = List(
364
+ // MemberDefinition(
365
+ // name = "id",
366
+ // target = ShapeId(namespace = "", name = "String"),
367
+ // traits = List(
368
+ // TraitApplication(
369
+ // id = ShapeId(namespace = "smithy.api", name = "required"),
370
+ // value = None
371
+ // )
372
+ // )
373
+ // ),
374
+ // MemberDefinition(
375
+ // name = "tags",
376
+ // target = ShapeId(namespace = "", name = "TagList"),
377
+ // traits = List()
378
+ // )
379
+ // )
380
+ // )
381
+ // ),
382
+ // ShapeDefinition(
383
+ // name = "TagList",
384
+ // shape = ListShape(
385
+ // name = "TagList",
386
+ // traits = List(),
387
+ // member = MemberDefinition(
388
+ // name = "member",
389
+ // target = ShapeId(namespace = "", name = "String"),
390
+ // traits = List()
391
+ // )
392
+ // )
393
+ // )
394
+ // )
395
+ ```
396
+
397
+ :::note[`member` has no default, `traits` does]
398
+ `ListShape` and `MapShape` declare their `Shape#traits` parameter with a default *before* the parameters that have none, so positional construction does not work — `ListShape("TagList", member = m)` compiles while `ListShape("TagList", m)` does not. `StructureShape` and `UnionShape` default their member lists, so both forms work there.
399
+ :::
400
+
401
+ ### Service Shapes
402
+
403
+ `ServiceShape`, `OperationShape`, and `ResourceShape` describe an API rather than a value, and all of their cross-references are `ShapeId`s:
404
+
405
+ ```scala
406
+ final case class ServiceShape(
407
+ name: String,
408
+ traits: List[TraitApplication] = Nil,
409
+ version: Option[String] = None,
410
+ operations: List[ShapeId] = Nil,
411
+ resources: List[ShapeId] = Nil,
412
+ errors: List[ShapeId] = Nil
413
+ ) extends Shape
414
+
415
+ final case class OperationShape(
416
+ name: String,
417
+ traits: List[TraitApplication] = Nil,
418
+ input: Option[ShapeId] = None,
419
+ output: Option[ShapeId] = None,
420
+ errors: List[ShapeId] = Nil
421
+ ) extends Shape
422
+ ```
423
+
424
+ `ResourceShape` is the largest shape in the module, because a Smithy resource binds identifiers, five named lifecycle operations, and three further reference lists:
425
+
426
+ | Field | Type | Meaning |
427
+ | ---------------------- | ---------------------- | ------------------------------------------------- |
428
+ | `identifiers` | `Map[String, ShapeId]` | Identifier names mapped to their target shapes |
429
+ | `create` | `Option[ShapeId]` | Create lifecycle operation |
430
+ | `read` | `Option[ShapeId]` | Read lifecycle operation |
431
+ | `update` | `Option[ShapeId]` | Update lifecycle operation |
432
+ | `delete` | `Option[ShapeId]` | Delete lifecycle operation |
433
+ | `list` | `Option[ShapeId]` | List lifecycle operation |
434
+ | `operations` | `List[ShapeId]` | Instance operations that are not lifecycle ones |
435
+ | `collectionOperations` | `List[ShapeId]` | Operations on the collection rather than one item |
436
+ | `resources` | `List[ShapeId]` | Child resources |
437
+
438
+ The five lifecycle fields are separate rather than a map, which is what makes "does this resource support deletion?" a field access instead of a lookup:
439
+
440
+ ```scala
441
+ val resourceModel = SmithyModel.parse(
442
+ """$version: "2"
443
+ |namespace com.example
444
+ |resource FooResource {
445
+ | identifiers: {id: FooId}
446
+ | read: GetFoo
447
+ | list: ListFoos
448
+ |}
449
+ |""".stripMargin
450
+ ).toOption.get
451
+ ```
452
+
453
+ Unset lifecycle operations stay `None`, so an absent `create` is distinguishable from one bound to an operation:
454
+
455
+ ```scala
456
+ resourceModel.shapes
457
+ // res6: List[ShapeDefinition] = List(
458
+ // ShapeDefinition(
459
+ // name = "FooResource",
460
+ // shape = ResourceShape(
461
+ // name = "FooResource",
462
+ // traits = List(),
463
+ // identifiers = Map("id" -> ShapeId(namespace = "", name = "FooId")),
464
+ // create = None,
465
+ // read = Some(ShapeId(namespace = "", name = "GetFoo")),
466
+ // update = None,
467
+ // delete = None,
468
+ // list = Some(ShapeId(namespace = "", name = "ListFoos")),
469
+ // operations = List(),
470
+ // collectionOperations = List(),
471
+ // resources = List()
472
+ // )
473
+ // )
474
+ // )
475
+ ```
476
+
477
+ ### Shape References
478
+
479
+ Every cross-shape reference is a `ShapeRef`, a sealed trait with exactly two cases:
480
+
481
+ ```scala
482
+ sealed trait ShapeRef
483
+
484
+ final case class ShapeId(namespace: String, name: String) extends ShapeRef
485
+
486
+ object ShapeId {
487
+ final case class Member(shape: ShapeId, memberName: String) extends ShapeRef
488
+ def parse(s: String): Either[String, ShapeRef]
489
+ }
490
+ ```
491
+
492
+ `ShapeRef` exists to give `ShapeId` and `ShapeId.Member` a common supertype without a Scala 3 union type, so the same signatures compile on 2.13.
493
+
494
+ Rendering follows the IDL: a shape is `namespace#name`, and a member appends `$` and the member name:
495
+
496
+ ```scala
497
+ ShapeId("com.example", "User").toString
498
+ // res7: String = "com.example#User"
499
+ ShapeId.Member(ShapeId("com.example", "User"), "id").toString
500
+ // res8: String = "com.example#User$id"
501
+ ```
502
+
503
+ `ShapeId.parse` reads either form back, choosing the case by whether a `$` is present:
504
+
505
+ ```scala
506
+ ShapeId.parse("com.example#User")
507
+ // res9: Either[String, ShapeRef] = Right(
508
+ // ShapeId(namespace = "com.example", name = "User")
509
+ // )
510
+ ShapeId.parse("com.example#User$id")
511
+ // res10: Either[String, ShapeRef] = Right(
512
+ // Member(
513
+ // shape = ShapeId(namespace = "com.example", name = "User"),
514
+ // memberName = "id"
515
+ // )
516
+ // )
517
+ ```
518
+
519
+ Malformed input is reported rather than thrown, and the message names the rule that failed:
520
+
521
+ ```scala
522
+ ShapeId.parse("User")
523
+ // res11: Either[String, ShapeRef] = Left(
524
+ // "ShapeId must contain '#' separator, got: User"
525
+ // )
526
+ ShapeId.parse("com.example#User$id$extra")
527
+ // res12: Either[String, ShapeRef] = Left(
528
+ // "Member reference must have exactly one separator, got: com.example#User$id$extra"
529
+ // )
530
+ ```
531
+
532
+ #### Resolving a Reference
533
+
534
+ A reference names a shape; it does not contain one. Resolving means looking the target up in the model, which `SmithyModel#findShape` does by **name** rather than by `ShapeId`:
535
+
536
+ ```scala
537
+ final case class SmithyModel(...) {
538
+ def findShape(name: String): Option[ShapeDefinition]
539
+ def allShapeIds: List[ShapeId]
540
+ }
541
+ ```
542
+
543
+ Following a structure member to its target definition is therefore a lookup on the name inside the `ShapeId`:
544
+
545
+ ```scala
546
+ val tagsTarget = userModel.shapes
547
+ .collectFirst { case ShapeDefinition("User", s: StructureShape) => s }
548
+ .flatMap(_.members.find(_.name == "tags"))
549
+ .map(_.target)
550
+ ```
551
+
552
+ The member's target resolves to the `list` shape declared alongside it:
553
+
554
+ ```scala
555
+ tagsTarget
556
+ // res13: Option[ShapeId] = Some(ShapeId(namespace = "", name = "TagList"))
557
+ tagsTarget.flatMap(id => userModel.findShape(id.name))
558
+ // res14: Option[ShapeDefinition] = Some(
559
+ // ShapeDefinition(
560
+ // name = "TagList",
561
+ // shape = ListShape(
562
+ // name = "TagList",
563
+ // traits = List(),
564
+ // member = MemberDefinition(
565
+ // name = "member",
566
+ // target = ShapeId(namespace = "", name = "String"),
567
+ // traits = List()
568
+ // )
569
+ // )
570
+ // )
571
+ // )
572
+ ```
573
+
574
+ Looking up by name rather than by `ShapeId` is not a shortcut — it matches what the parser produces. An IDL target written without a namespace prefix becomes a `ShapeId` whose namespace is the **empty string**, not the model's namespace and not `smithy.api`:
575
+
576
+ ```scala
577
+ userModel.shapes.collect { case ShapeDefinition(_, s: ListShape) => s.member.target }
578
+ // res15: List[ShapeId] = List(ShapeId(namespace = "", name = "String"))
579
+ resourceModel.shapes.collect { case ShapeDefinition(_, s: ResourceShape) => s.identifiers }
580
+ // res16: List[Map[String, ShapeId]] = List(
581
+ // Map("id" -> ShapeId(namespace = "", name = "FooId"))
582
+ // )
583
+ ```
584
+
585
+ Trait identifiers are the exception: the parser resolves those against the prelude, so `@required` arrives fully qualified:
586
+
587
+ ```scala
588
+ userModel.shapes
589
+ .collect { case ShapeDefinition("User", s: StructureShape) => s }
590
+ .flatMap(_.members.flatMap(_.traits.map(_.id)))
591
+ // res17: List[ShapeId] = List(
592
+ // ShapeId(namespace = "smithy.api", name = "required")
593
+ // )
594
+ ```
595
+
596
+ Two consequences follow. A reference to a prelude shape such as `String` has no definition in the parsed model, so resolving it yields nothing — a consumer generating code has to recognize prelude names itself:
597
+
598
+ ```scala
599
+ userModel.findShape("String")
600
+ // res18: Option[ShapeDefinition] = None
601
+ ```
602
+
603
+ And a `ShapeId` taken from a parsed model does not necessarily round-trip through `ShapeId.parse`, because the renderer emits the empty namespace as a bare `#` that the parser then rejects:
604
+
605
+ ```scala
606
+ ShapeId("", "String").toString
607
+ // res19: String = "#String"
608
+ ShapeId.parse(ShapeId("", "String").toString)
609
+ // res20: Either[String, ShapeRef] = Left("ShapeId namespace cannot be empty")
610
+ ```
611
+
612
+ :::warning[Namespaces on references are not populated]
613
+ Because unprefixed targets carry an empty namespace, comparing `ShapeId#namespace` on a parsed reference tells you nothing unless the IDL spelled the namespace out. `SmithyModel#findShape` matching on name alone is consistent with that, but it also means a model that uses a `use` statement to import `other.ns#User` while declaring its own `User` cannot distinguish the two by reference alone.
614
+ :::
615
+
616
+ ## Parsing
617
+
618
+ Parse Smithy IDL text into structured models using `SmithyModel.parse`, handle errors, and validate round-trips.
619
+
620
+ ### Basic Parsing
621
+
622
+ Parse Smithy IDL text and handle the result:
623
+
624
+ ```scala
625
+ import zio.blocks.smithy._
626
+
627
+ val smithyText = """$version: "2"
628
+ namespace com.example
629
+
630
+ string Name
631
+ """
632
+
633
+ SmithyModel.parse(smithyText) match {
634
+ case Right(model) =>
635
+ println(s"Parsed ${model.shapes.length} shapes")
636
+ case Left(error) =>
637
+ println(s"Error: ${error.message}")
638
+ }
639
+ ```
640
+
641
+ ### Handling Parse Errors
642
+
643
+ Access error details including line and column information when parsing fails. `SmithyError` provides detailed context to help locate and fix issues in your Smithy definitions:
644
+
645
+ ```scala
646
+ import zio.blocks.smithy._
647
+
648
+ val invalidSmithy = """$version: "2"
649
+ namespace com.example
650
+
651
+ structure User {
652
+ invalid syntax
653
+ }
654
+ """
655
+
656
+ SmithyModel.parse(invalidSmithy) match {
657
+ case Right(_) =>
658
+ println("Unexpected success")
659
+ case Left(error) =>
660
+ println(s"Error at line ${error.line}, column ${error.column}: ${error.message}")
661
+ }
662
+ ```
663
+
664
+ ### Round-Trip Validation
665
+
666
+ Verify a model parses correctly by round-tripping (parse → serialize → parse again):
667
+
668
+ ```scala
669
+ import zio.blocks.smithy._
670
+
671
+ val original = """$version: "2"
672
+ namespace com.example
673
+
674
+ string MyString
675
+ """
676
+
677
+ val parsed = SmithyModel.parse(original)
678
+ val reprinted = parsed.map(_.prettyPrint)
679
+ val reparsed = reprinted.flatMap(SmithyModel.parse)
680
+
681
+ println(reparsed.isRight) // true if round-trip succeeds
682
+ ```
683
+
684
+ ## Querying & Traversing Shapes
685
+
686
+ Once you have a parsed model, query shapes by name, pattern match on shape types, and traverse their members.
687
+
688
+ ### Finding Shapes
689
+
690
+ Locate shapes by name or retrieve all shape identifiers:
691
+
692
+ ```scala
693
+ import zio.blocks.smithy._
694
+
695
+ val model = SmithyModel.parse("""$version: "2"
696
+ namespace example
697
+
698
+ structure User {
699
+ id: String
700
+ name: String
701
+ }
702
+ """).toOption.get
703
+
704
+ // Find by name
705
+ model.findShape("User").foreach { shapeDef =>
706
+ println(s"Found: ${shapeDef.name}")
707
+ }
708
+
709
+ // Get all shape IDs
710
+ val allIds = model.allShapeIds
711
+ println(s"Total shapes: ${allIds.length}")
712
+ ```
713
+
714
+ ### Pattern Matching on Shapes
715
+
716
+ Determine shape type and access type-specific properties:
717
+
718
+ ```scala
719
+ import zio.blocks.smithy._
720
+
721
+ val model = SmithyModel.parse("""$version: "2"
722
+ namespace example
723
+
724
+ structure User { id: String }
725
+ list UserIds { member: String }
726
+ """).toOption.get
727
+
728
+ model.findShape("User").foreach { shapeDef =>
729
+ shapeDef.shape match {
730
+ case struct: StructureShape =>
731
+ println(s"Structure with ${struct.members.length} members")
732
+ case list: ListShape =>
733
+ println(s"List of ${list.member.target}")
734
+ case _ =>
735
+ println("Other shape type")
736
+ }
737
+ }
738
+ ```
739
+
740
+ ### Traversing Members
741
+
742
+ Iterate over structure/union members and inspect their traits:
743
+
744
+ ```scala
745
+ import zio.blocks.smithy._
746
+
747
+ val model = SmithyModel.parse("""$version: "2"
748
+ namespace example
749
+
750
+ structure User {
751
+ @required
752
+ id: String
753
+ name: String
754
+ }
755
+ """).toOption.get
756
+
757
+ model.findShape("User").foreach { shapeDef =>
758
+ shapeDef.shape match {
759
+ case struct: StructureShape =>
760
+ struct.members.foreach { member =>
761
+ val required = member.traits.exists(_.id.name == "required")
762
+ println(s"${member.name}: ${member.target} (required: $required)")
763
+ }
764
+ case _ => ()
765
+ }
766
+ }
767
+ ```
768
+
769
+ ## Building Models Programmatically
770
+
771
+ Construct Smithy models in code by creating shapes, adding traits, and assembling them into a complete model.
772
+
773
+ ### Creating Shapes
774
+
775
+ Programmatically construct shapes and assemble them into a complete model:
776
+
777
+ ```scala
778
+ import zio.blocks.smithy._
779
+
780
+ val userStructure = StructureShape(
781
+ "User",
782
+ traits = Nil,
783
+ members = List(
784
+ MemberDefinition(
785
+ "id",
786
+ ShapeId("smithy.api", "String"),
787
+ traits = List(TraitApplication.required)
788
+ ),
789
+ MemberDefinition(
790
+ "name",
791
+ ShapeId("smithy.api", "String"),
792
+ traits = Nil
793
+ )
794
+ )
795
+ )
796
+
797
+ val model = SmithyModel(
798
+ version = "2",
799
+ namespace = "com.example",
800
+ useStatements = Nil,
801
+ metadata = Map.empty,
802
+ shapes = List(ShapeDefinition("User", userStructure))
803
+ )
804
+ ```
805
+
806
+ ### Adding Traits
807
+
808
+ Attach metadata traits to shapes during construction. `TraitApplication` provides companion object helper methods like `required`, `documentation`, and others for common traits:
809
+
810
+ ```scala
811
+ import zio.blocks.smithy._
812
+
813
+ val serviceShape = ServiceShape(
814
+ "UserService",
815
+ traits = List(
816
+ TraitApplication.documentation("User management API")
817
+ ),
818
+ version = Some("1.0"),
819
+ operations = List(
820
+ ShapeId("com.example", "GetUser"),
821
+ ShapeId("com.example", "CreateUser")
822
+ ),
823
+ resources = Nil,
824
+ errors = Nil
825
+ )
826
+ ```
827
+
828
+ ## Serializing Models
829
+
830
+ Convert models back to valid Smithy IDL text using `prettyPrint`, with options for custom formatting.
831
+
832
+ ### Basic Serialization
833
+
834
+ Convert a model to valid Smithy IDL text:
835
+
836
+ ```scala
837
+ import zio.blocks.smithy._
838
+
839
+ val model = SmithyModel(
840
+ version = "2",
841
+ namespace = "com.example",
842
+ useStatements = Nil,
843
+ metadata = Map.empty,
844
+ shapes = List(
845
+ ShapeDefinition("Name", StringShape("Name"))
846
+ )
847
+ )
848
+
849
+ val idlText = model.prettyPrint
850
+ println(idlText)
851
+ ```
852
+
853
+ ### Custom Indentation
854
+
855
+ Control indentation width when serializing models:
856
+
857
+ ```scala
858
+ import zio.blocks.smithy._
859
+
860
+ val model = SmithyModel(
861
+ version = "2",
862
+ namespace = "com.example",
863
+ useStatements = Nil,
864
+ metadata = Map.empty,
865
+ shapes = List(
866
+ ShapeDefinition("Data", StructureShape(
867
+ "Data",
868
+ traits = Nil,
869
+ members = List(
870
+ MemberDefinition("field1", ShapeId("smithy.api", "String")),
871
+ MemberDefinition("field2", ShapeId("smithy.api", "String"))
872
+ )
873
+ ))
874
+ )
875
+ )
876
+
877
+ val compact = model.prettyPrint(indent = 2)
878
+ val verbose = model.prettyPrint(indent = 8)
879
+ ```
880
+
881
+ ## Common Use-Cases
882
+
883
+ See how to apply Smithy parsing and querying to real-world workflows: code generation, validation, and model transformation.
884
+
885
+ ### Use-Case 1: Code Generation
886
+
887
+ Load a Smithy model and generate code for each operation:
888
+
889
+ ```scala
890
+ import zio.blocks.smithy._
891
+
892
+ val model = SmithyModel.parse("""$version: "2"
893
+ namespace api
894
+
895
+ service MyService {
896
+ operations: [GetUser, CreateUser]
897
+ }
898
+
899
+ @http(method: "GET", uri: "/users/{id}")
900
+ operation GetUser {
901
+ input: GetUserInput
902
+ output: User
903
+ }
904
+
905
+ @http(method: "POST", uri: "/users")
906
+ operation CreateUser {
907
+ input: CreateUserInput
908
+ output: User
909
+ }
910
+
911
+ structure User { id: String, name: String }
912
+ structure GetUserInput { @required id: String }
913
+ structure CreateUserInput { @required name: String }
914
+ """).toOption.get
915
+
916
+ // Generate code stubs for each operation by pattern matching:
917
+
918
+ model.shapes.foreach { shapeDef =>
919
+ shapeDef.shape match {
920
+ case op: OperationShape =>
921
+ println(s"// Generate operation: ${op.name}")
922
+ op.input.foreach(in => println(s"// input: ${in.name}"))
923
+ op.output.foreach(out => println(s"// output: ${out.name}"))
924
+ case _ => ()
925
+ }
926
+ }
927
+ ```
928
+
929
+ ### Use-Case 2: Validation & Analysis
930
+
931
+ Find deprecated shapes and analyze trait coverage:
932
+
933
+ ```scala
934
+ import zio.blocks.smithy._
935
+
936
+ val model = SmithyModel.parse("""$version: "2"
937
+ namespace example
938
+
939
+ @deprecated
940
+ structure LegacyUser { id: String }
941
+
942
+ structure ModernUser {
943
+ @required
944
+ id: String
945
+ email: String
946
+ }
947
+ """).toOption.get
948
+
949
+ // Find all deprecated shapes:
950
+
951
+ val deprecated = model.shapes.filter { shapeDef =>
952
+ shapeDef.shape.traits.exists(_.id.name == "deprecated")
953
+ }
954
+
955
+ println(s"Deprecated shapes: ${deprecated.map(_.name)}")
956
+ ```
957
+
958
+ ### Use-Case 3: Model Transformation
959
+
960
+ Parse, modify, and re-serialize a model with updated metadata:
961
+
962
+ ```scala
963
+ import zio.blocks.smithy._
964
+
965
+ val original = """$version: "2"
966
+ namespace com.example
967
+
968
+ string UserId
969
+ """
970
+
971
+ val modified = SmithyModel.parse(original).map { model =>
972
+ // Add metadata to the model:
973
+
974
+ val newMetadata = model.metadata + ("version" -> NodeValue.String("1.0"))
975
+ model.copy(metadata = newMetadata)
976
+ }
977
+
978
+ modified.foreach { model =>
979
+ println(model.prettyPrint)
980
+ }
981
+ ```
982
+
983
+ ## Running the Examples
984
+
985
+ All code from this guide is available as runnable examples in the `smithy-examples` module. Examples demonstrate different aspects of the Smithy library.
986
+
987
+ **1. Clone the repository and navigate to the project:**
988
+
989
+ ```bash
990
+ git clone https://github.com/zio/zio-blocks.git
991
+ cd zio-blocks
992
+ ```
993
+
994
+ **2. Run individual examples with sbt:**
995
+
996
+ ### Step 1: Basic Parsing and Querying
997
+
998
+ Parse Smithy IDL text, find shapes by name, and access their structure and metadata:
999
+
1000
+ ```bash
1001
+ sbt "smithy-examples/runMain smithyexample.BasicParsingAndQuerying"
1002
+ ```
1003
+
1004
+ ### Step 2: Building Models Programmatically
1005
+
1006
+ Construct Smithy models in code by creating shapes, adding traits, and assembling them into a complete model:
1007
+
1008
+ ```bash
1009
+ sbt "smithy-examples/runMain smithyexample.BuildingModelsAndTraits"
1010
+ ```
1011
+
1012
+ ### Step 3: Validation and Analysis
1013
+
1014
+ Analyze Smithy models for completeness, find deprecated shapes, check for documentation, and validate API contracts:
1015
+
1016
+ ```bash
1017
+ sbt "smithy-examples/runMain smithyexample.ValidationAndAnalysis"
1018
+ ```
1019
+
1020
+ ### Step 4: Complete Example — Book Store API
1021
+
1022
+ A comprehensive end-to-end workflow showing a complete book store API model with parsing, entity analysis, error handling, code generation, and statistics:
1023
+
1024
+ ```bash
1025
+ sbt "smithy-examples/runMain smithyexample.BookStoreAPI"
1026
+ ```
1027
+
1028
+ **3. Or compile all examples at once:**
1029
+
1030
+ ```bash
1031
+ sbt "smithy-examples/compile"
1032
+ ```