@danieljvdm/dev-kit 0.11.3 → 0.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. package/README.md +111 -78
  2. package/dev-kit.example.jsonc +0 -4
  3. package/package.json +10 -6
  4. package/schema/dev-kit.schema.json +1 -46
  5. package/skills/build-effect-apis/SKILL.md +77 -0
  6. package/skills/build-effect-apis/agents/openai.yaml +4 -0
  7. package/skills/build-effect-apis/references/cloudflare-workers.md +71 -0
  8. package/skills/build-effect-apis/references/effect-atom-client.md +161 -0
  9. package/skills/build-effect-apis/references/effect-atom-lifecycle.md +78 -0
  10. package/skills/build-effect-apis/references/effect-atom-testing.md +74 -0
  11. package/skills/build-effect-apis/references/runtime-assembly.md +56 -0
  12. package/skills/build-effect-apis/references/server-and-middleware.md +174 -0
  13. package/skills/build-effect-apis/references/shared-contracts.md +108 -0
  14. package/skills/build-effect-apis/references/tanstack-start.md +86 -0
  15. package/skills/build-effect-apis/references/verification.md +50 -0
  16. package/skills/build-effect-clis/SKILL.md +61 -0
  17. package/skills/build-effect-clis/agents/openai.yaml +4 -0
  18. package/skills/build-effect-clis/references/command-design.md +93 -0
  19. package/skills/build-effect-clis/references/entrypoints-and-testing.md +59 -0
  20. package/skills/build-effect-clis/references/processes-and-platform.md +73 -0
  21. package/skills/dev-kit/SKILL.md +77 -48
  22. package/skills/effect-architecture-audit/SKILL.md +26 -0
  23. package/skills/effect-architecture-audit/agents/openai.yaml +4 -0
  24. package/skills/effect-architecture-audit/references/service-and-boundary-audit.md +150 -0
  25. package/skills/effect-ts/SKILL.md +21 -256
  26. package/skills/effect-ts/agents/openai.yaml +3 -3
  27. package/skills/testing/SKILL.md +5 -0
  28. package/src/catalog-manager.ts +16 -17
  29. package/src/catalog.ts +71 -16
  30. package/src/effect-source.ts +46 -24
  31. package/src/effect-tsgo.ts +86 -24
  32. package/src/gitignore.ts +5 -5
  33. package/src/index.ts +3 -6
  34. package/src/manifest.ts +0 -34
  35. package/src/node-symbolic-link.ts +2 -2
  36. package/src/oxfmt.js +5 -0
  37. package/src/oxfmt.ts +5 -0
  38. package/src/oxlint.js +5 -0
  39. package/src/oxlint.ts +5 -0
  40. package/src/package-skill-source.ts +51 -59
  41. package/src/path-digest.ts +7 -7
  42. package/src/project-package.ts +8 -7
  43. package/src/project-process-lock.ts +17 -12
  44. package/src/project-state.ts +1 -1
  45. package/src/skill-manager.ts +18 -16
  46. package/src/skill-selector.ts +12 -0
  47. package/src/sync.ts +181 -131
  48. package/src/tool-ignore-patterns.js +9 -0
  49. package/src/tool-ignore-patterns.ts +15 -0
  50. package/src/vendor.ts +67 -61
  51. package/src/vite-plus-dependency.ts +10 -11
  52. package/src/vite-plus-hooks.ts +24 -14
  53. package/src/vite-plus-quality.ts +21 -172
  54. package/src/vite-plus.js +81 -0
  55. package/src/vite-plus.ts +102 -0
  56. package/templates/AGENTS.md +1 -1
  57. package/skills/effect-ts/UPSTREAM.md +0 -28
  58. package/skills/effect-ts/references/atom-cache-lifecycle.md +0 -78
  59. package/skills/effect-ts/references/atom-http-and-invalidation.md +0 -97
  60. package/skills/effect-ts/references/atom-tanstack-start.md +0 -69
  61. package/skills/effect-ts/references/atom-testing.md +0 -67
  62. package/skills/effect-ts/references/audit-services.md +0 -144
  63. package/skills/effect-ts/references/features.md +0 -525
  64. package/skills/effect-ts/references/guide-atom-data-fetching.md +0 -44
  65. package/skills/effect-ts/references/guide-cli.md +0 -107
  66. package/skills/effect-ts/references/guide-datetime.md +0 -72
  67. package/skills/effect-ts/references/guide-effect.md +0 -440
  68. package/skills/effect-ts/references/guide-error-handling.md +0 -565
  69. package/skills/effect-ts/references/guide-http-boundaries.md +0 -55
  70. package/skills/effect-ts/references/guide-layers.md +0 -989
  71. package/skills/effect-ts/references/guide-observability.md +0 -746
  72. package/skills/effect-ts/references/guide-retries.md +0 -434
  73. package/skills/effect-ts/references/guide-schedule.md +0 -343
  74. package/skills/effect-ts/references/guide-schema.md +0 -664
  75. package/skills/effect-ts/references/guide-sql.md +0 -536
  76. package/skills/effect-ts/references/guide-testing.md +0 -532
  77. package/skills/effect-ts/references/guide-type-safety-and-boundaries.md +0 -131
  78. package/skills/effect-ts/references/version-and-source.md +0 -86
  79. package/templates/vite-plus/vite.config.ts +0 -22
@@ -1,664 +0,0 @@
1
- # Schema Guide
2
-
3
- This guide covers Schema APIs and the application modeling policies built on
4
- them.
5
-
6
- Key source files:
7
-
8
- - `packages/effect/src/Schema.ts`
9
- - `packages/effect/src/SchemaTransformation.ts`
10
- - `packages/effect/src/SchemaGetter.ts`
11
- - `packages/effect/src/SchemaIssue.ts`
12
- - `packages/effect/src/JsonSchema.ts`
13
-
14
- Representative repo usage:
15
-
16
- - `packages/tools/ai-codegen/src/Config.ts`
17
- - `packages/platform-node/test/fixtures/rpc-schemas.ts`
18
- - `packages/platform-browser/test/IndexedDbQueryBuilder.test.ts`
19
- - `packages/tools/openapi-generator/`
20
-
21
- ## Mental Model
22
-
23
- Schema is the standard way to:
24
-
25
- - define data shapes
26
- - validate unknown input
27
- - encode typed values back to serialized form
28
- - transform between encoded and decoded representations
29
- - attach metadata and constraints
30
-
31
- The repo uses Schema pervasively for:
32
-
33
- - protocol payloads
34
- - configuration
35
- - HTTP and RPC contracts
36
- - database row decoding
37
- - error types
38
- - derived tooling such as JSON Schema and arbitrary generation
39
-
40
- ## Preferred Rule
41
-
42
- Prefer Schema-based types whenever data crosses a boundary or should be validated, transformed, documented, or encoded.
43
-
44
- Typical boundaries:
45
-
46
- - HTTP requests and responses
47
- - RPC payloads
48
- - database rows
49
- - config files
50
- - worker messages
51
- - persisted data
52
- - domain errors
53
-
54
- ## Application Model Ownership
55
-
56
- Make Schema the source of truth for application data. Export the schema value
57
- and derive its decoded TypeScript type from `.Type` under the same name.
58
-
59
- ```ts
60
- export const ArtifactId = Schema.NonEmptyString.pipe(Schema.brand("@acme/ArtifactId"));
61
- export type ArtifactId = typeof ArtifactId.Type;
62
-
63
- export const GenerateInput = Schema.Struct({
64
- artifactId: ArtifactId,
65
- prompt: Schema.String,
66
- });
67
- export type GenerateInput = typeof GenerateInput.Type;
68
- ```
69
-
70
- Use schema classes for named reusable models when their validated construction,
71
- methods, or identity are useful. Use `Schema.Struct` for ordinary record-shaped
72
- contracts and inline fragments. Do not maintain a parallel interface or
73
- handwritten structural alias for fields already owned by a schema.
74
-
75
- Export schemas for every data-bearing service input and result, including
76
- intermediate application results that have not yet crossed a network boundary.
77
- Service interfaces should refer to the schema-derived types so the schema
78
- remains reusable for fixtures, persistence, and future transports.
79
-
80
- Interfaces remain appropriate for runtime capabilities containing functions,
81
- resources, and behavior rather than serializable application data.
82
-
83
- Give each semantically distinct identifier its own branded schema and reuse it
84
- for every corresponding field and parameter. During a change, inventory every
85
- added or modified `id`, `*Id`, and `*Ids` field; each should resolve to its
86
- semantic brand rather than a plain primitive.
87
-
88
- Decode external `unknown` input once at the earliest boundary that owns it.
89
- Pass the decoded type through internal services, and encode with the same
90
- schema when writing an external representation. Use `.Encoded` only in code
91
- that explicitly handles the encoded form.
92
-
93
- ## What A Schema Actually Is
94
-
95
- A schema is not just a static shape.
96
-
97
- It is a contract between:
98
-
99
- - the decoded in-memory value you want to work with
100
- - the encoded representation that comes from or goes to some boundary
101
-
102
- This is the most important thing many implementations get wrong.
103
-
104
- Do not think of Schema as “a typed struct definition.”
105
- Think of it as:
106
-
107
- - validation
108
- - decoding
109
- - encoding
110
- - transformation
111
- - metadata
112
- - reuse across boundaries
113
-
114
- Because of that, schemas should not be duplicated unless there is a real semantic difference.
115
-
116
- If two schemas describe the same logical model but differ only because one boundary encodes a field differently, prefer one schema with transformations instead of two parallel schemas.
117
-
118
- ## Avoid Duplicating Schemas
119
-
120
- Do not create multiple parallel schemas for the same logical entity unless they truly represent different models.
121
-
122
- Bad pattern:
123
-
124
- ```ts
125
- const Todo = Schema.Struct({
126
- id: Schema.Number,
127
- title: Schema.String,
128
- completed: Schema.Boolean,
129
- });
130
-
131
- const TodoSql = Schema.Struct({
132
- id: Schema.Number,
133
- title: Schema.String,
134
- completed: Schema.BooleanFromBit,
135
- });
136
- ```
137
-
138
- This is usually a sign that transformations are not being used properly.
139
-
140
- If the model is still “Todo”, do not define a second schema just because one boundary stores `completed` as a bit.
141
-
142
- Prefer deriving or transforming the representation instead.
143
-
144
- Why duplication is bad:
145
-
146
- - the same model is now maintained in multiple places
147
- - fields drift over time
148
- - boundary logic gets copied instead of centralized
149
- - refactors become error-prone
150
-
151
- Only duplicate schemas when there is a real semantic difference, for example:
152
-
153
- - a creation payload really is a different model from a persisted entity
154
- - a public API contract intentionally differs from an internal domain model
155
- - a projection or partial view is intentionally a different type
156
-
157
- If the difference is only encoding, use a transformation.
158
-
159
- ## Prefer `Class` Variants Over `Struct` Variants When Possible
160
-
161
- When a schema represents a named domain model, reusable payload, or long-lived API shape, prefer `Schema.Class`, `Schema.TaggedClass`, or `Schema.TaggedErrorClass` over a bare `Schema.Struct`.
162
-
163
- Prefer:
164
-
165
- ```ts
166
- import { Schema } from "effect";
167
-
168
- export class User extends Schema.Class<User>("User")({
169
- id: Schema.String,
170
- name: Schema.String,
171
- }) {}
172
- ```
173
-
174
- Over:
175
-
176
- ```ts
177
- import { Schema } from "effect";
178
-
179
- export const User = Schema.Struct({
180
- id: Schema.String,
181
- name: Schema.String,
182
- });
183
- ```
184
-
185
- Why `Class` variants are usually better:
186
-
187
- - the schema has a stable, named identity
188
- - reusable models are easier to recognize in code and traces
189
- - constructors and validation are packaged together
190
- - extension patterns are clearer
191
- - named schemas read better in contracts and tooling output
192
-
193
- Use `Struct` when:
194
-
195
- - the shape is local and anonymous
196
- - it is a small inline request or response shape
197
- - introducing a class would add unnecessary ceremony
198
- - the schema is primarily a one-off composition fragment
199
-
200
- Good rule of thumb:
201
-
202
- - reusable named model: `Class`
203
- - reusable tagged union member: `TaggedClass`
204
- - reusable error payload: `TaggedErrorClass`
205
- - small inline object shape: `Struct`
206
-
207
- ## One Logical Model, Multiple Representations
208
-
209
- The right Schema mindset is:
210
-
211
- - one logical model
212
- - multiple encoded forms when needed
213
- - transformations connecting them
214
-
215
- For example, a `Todo` may be:
216
-
217
- - a boolean in memory
218
- - a bit in SQL
219
- - a string in some external API
220
-
221
- That does not automatically mean you need three separate top-level schemas.
222
-
223
- Prefer:
224
-
225
- - one main schema for the logical model
226
- - transformed field schemas or transformed object schemas for boundary-specific encoding
227
- - derived request/result schemas when the shape is actually different
228
-
229
- ## Common Schema Building Blocks
230
-
231
- Common primitives and collections used throughout the repo:
232
-
233
- - `Schema.String`
234
- - `Schema.Number`
235
- - `Schema.Boolean`
236
- - `Schema.BigInt`
237
- - `Schema.Array(...)`
238
- - `Schema.Record(key, value)`
239
- - `Schema.Tuple([...])`
240
- - `Schema.Struct({...})`
241
- - `Schema.Union([...])`
242
-
243
- Example:
244
-
245
- ```ts
246
- const Todo = Schema.Struct({
247
- id: Schema.Number,
248
- title: Schema.String,
249
- completed: Schema.Boolean,
250
- });
251
- ```
252
-
253
- ## `Class`, `TaggedClass`, and `TaggedErrorClass`
254
-
255
- ### `Schema.Class`
256
-
257
- Use for named reusable schema-backed models.
258
-
259
- ```ts
260
- class Product extends Schema.Class<Product>("Product")({
261
- id: Schema.String,
262
- price: Schema.Number,
263
- }) {}
264
- ```
265
-
266
- ### Constructor Rule
267
-
268
- When constructing schema classes, prefer `X.make(...)` over `new X(...)`.
269
-
270
- Prefer:
271
-
272
- ```ts
273
- const todo = Todo.make({
274
- id: 1,
275
- title: "write docs",
276
- completed: false,
277
- });
278
- ```
279
-
280
- Over:
281
-
282
- ```ts
283
- const todo = new Todo({
284
- id: 1,
285
- title: "write docs",
286
- completed: false,
287
- });
288
- ```
289
-
290
- Why:
291
-
292
- - it is the intended schema-class construction style
293
- - it makes schema-backed construction explicit
294
- - it keeps the codebase consistent
295
- - it reads better across `Class`, `TaggedClass`, and `TaggedErrorClass`
296
-
297
- Use this rule consistently for:
298
-
299
- - `Schema.Class`
300
- - `Schema.TaggedClass`
301
- - `Schema.TaggedErrorClass`
302
-
303
- ### `Schema.TaggedClass`
304
-
305
- Use for members of tagged unions.
306
-
307
- ```ts
308
- class Circle extends Schema.TaggedClass<Circle>()("Circle", {
309
- radius: Schema.Number,
310
- }) {}
311
-
312
- class Rectangle extends Schema.TaggedClass<Rectangle>()("Rectangle", {
313
- width: Schema.Number,
314
- height: Schema.Number,
315
- }) {}
316
- ```
317
-
318
- ### `Schema.TaggedErrorClass`
319
-
320
- Use for schema-backed typed errors.
321
-
322
- ```ts
323
- class NotFound extends Schema.TaggedErrorClass<NotFound>()("NotFound", {
324
- id: Schema.String,
325
- }) {}
326
- ```
327
-
328
- ## Optional Fields
329
-
330
- Be precise about optionality.
331
-
332
- Important rule from the canonical docs:
333
-
334
- - `Schema.optional(schema)` means `T | undefined`
335
- - `Schema.optionalKey(schema)` means an exact optional property in a struct
336
-
337
- Prefer `optionalKey` for object fields.
338
-
339
- Prefer:
340
-
341
- ```ts
342
- const Query = Schema.Struct({
343
- search: Schema.optionalKey(Schema.String),
344
- });
345
- ```
346
-
347
- Use `optional` when the value itself should be `A | undefined`, not just an omitted field.
348
-
349
- ## Unions
350
-
351
- Use `Schema.Union([...])` for ordinary unions.
352
-
353
- ```ts
354
- const Id = Schema.Union([Schema.String, Schema.Number]);
355
- ```
356
-
357
- Prefer tagged unions for domain variants.
358
-
359
- ```ts
360
- class Created extends Schema.TaggedClass<Created>()("Created", {
361
- id: Schema.String,
362
- }) {}
363
-
364
- class Deleted extends Schema.TaggedClass<Deleted>()("Deleted", {
365
- id: Schema.String,
366
- }) {}
367
-
368
- const TodoEvent = Schema.Union([Created, Deleted]);
369
- ```
370
-
371
- Why:
372
-
373
- - decoding and branching are clearer
374
- - `_tag`-based matching aligns with Effect code style
375
-
376
- ## Recursive Schemas
377
-
378
- Use `Schema.suspend` for recursive schemas.
379
-
380
- ```ts
381
- type Tree = {
382
- readonly name: string;
383
- readonly children: ReadonlyArray<Tree>;
384
- };
385
-
386
- const Tree: Schema.Schema<Tree> = Schema.Struct({
387
- name: Schema.String,
388
- children: Schema.Array(Schema.suspend((): Schema.Schema<Tree> => Tree)),
389
- });
390
- ```
391
-
392
- Use it whenever a schema refers to itself, directly or indirectly.
393
-
394
- Without `suspend`, recursive definitions will not work correctly.
395
-
396
- ## Transformations
397
-
398
- Transformations are one of the most important Schema features.
399
-
400
- Use them when decoded and encoded shapes differ.
401
-
402
- This is the main tool that avoids needless schema duplication.
403
-
404
- If your instinct is “I need another schema because this boundary encodes the same value differently”, stop and first ask whether this should be one schema with a transformation instead.
405
-
406
- ### `Schema.decodeTo`
407
-
408
- Use `decodeTo` when you want one schema to decode into another schema's type.
409
-
410
- ```ts
411
- const TrimmedString = Schema.String.pipe(
412
- Schema.decodeTo(Schema.String, {
413
- decode: (value) => value.trim(),
414
- encode: (value) => value,
415
- }),
416
- );
417
- ```
418
-
419
- The canonical docs explicitly note that `decodeTo` is curried and should be used with `pipe`.
420
-
421
- ### `Schema.encodeTo`
422
-
423
- Use `encodeTo` when the reverse direction reads more clearly.
424
-
425
- ### `SchemaTransformation.transformOrFail`
426
-
427
- Use `transformOrFail` when the transformation itself is effectful or may fail.
428
-
429
- ```ts
430
- import * as Effect from "effect/Effect";
431
- import * as Schema from "effect/Schema";
432
- import * as SchemaTransformation from "effect/SchemaTransformation";
433
-
434
- const VerifiedString = Schema.String.pipe(
435
- Schema.decodeTo(
436
- Schema.String,
437
- SchemaTransformation.transformOrFail({
438
- decode: (value) => Effect.succeed(value.trim()),
439
- encode: (value) => Effect.succeed(value),
440
- }),
441
- ),
442
- );
443
- ```
444
-
445
- Use this when:
446
-
447
- - validation depends on services or effects
448
- - decoding can fail with structured issues
449
- - encoding also needs logic beyond identity
450
-
451
- ## Field-Level Transformations
452
-
453
- Very often, the right answer is not a second object schema but a transformed field schema.
454
-
455
- Example shape:
456
-
457
- ```ts
458
- const Completed = Schema.BooleanFromBit;
459
-
460
- const Todo = Schema.Struct({
461
- id: Schema.Number,
462
- title: Schema.String,
463
- completed: Completed,
464
- });
465
- ```
466
-
467
- In this pattern:
468
-
469
- - the logical model still has `completed: boolean`
470
- - the encoded SQL-facing representation can still be a bit
471
- - the transformation lives at the field where it belongs
472
-
473
- This is usually better than defining `Todo` and `TodoSql` as separate object schemas.
474
-
475
- ## Object-Level Transformations
476
-
477
- Use object-level transformations when the whole object encoding differs, not just one field.
478
-
479
- Good use cases:
480
-
481
- - external keys differ from internal keys
482
- - several fields need coordinated transformation
483
- - the encoded shape is a structurally different representation of the same model
484
-
485
- Still prefer a single logical schema plus a transformation pipeline over maintaining multiple duplicated top-level schemas.
486
-
487
- ## Rename Keys
488
-
489
- Schema supports key renaming through struct transformations.
490
-
491
- The canonical `Schema.ts` implements key renaming by mapping fields and using
492
- decode/encode transformations with renamed key maps.
493
-
494
- Use key renaming when:
495
-
496
- - external payload keys differ from internal keys
497
- - you want stable internal names while honoring external contract names
498
-
499
- Preferred pattern:
500
-
501
- - keep the internal decoded shape idiomatic
502
- - use schema-level transformation or field-mapping to adapt external keys
503
-
504
- This is another example of avoiding duplication. If the only difference is key naming, do not define a second schema just to rename fields manually later.
505
-
506
- In practice, use struct field mapping helpers and transformation composition rather than manual post-parse object rewriting.
507
-
508
- ## Opaque And Branded Types
509
-
510
- Use opaque or branded schemas when a value should stay distinct from its structural base type.
511
-
512
- ### `Schema.brand`
513
-
514
- Use `brand` for refined nominal distinctions.
515
-
516
- ```ts
517
- const UserId = Schema.String.pipe(Schema.brand("UserId"));
518
- ```
519
-
520
- This is useful for:
521
-
522
- - IDs
523
- - validated domain scalars
524
- - preventing accidental interchange of same-shaped values
525
-
526
- ### `Schema.Opaque`
527
-
528
- Use `Opaque` when you want an opaque schema-backed type with the same structure as its underlying schema.
529
-
530
- This is especially useful when the type should remain distinct at the type level without changing its runtime shape.
531
-
532
- ## Picking, Omitting, Partial Shapes, And Mutability
533
-
534
- Common struct operations include:
535
-
536
- - `pick`
537
- - `omit`
538
- - `partial`
539
- - `mutable`
540
-
541
- Use them to derive variations instead of redefining near-identical schemas manually.
542
-
543
- Good examples:
544
-
545
- - request subset from a domain model
546
- - patch/update payloads
547
- - mutable representations for specific adapters
548
-
549
- Prefer deriving from one source schema rather than maintaining parallel copies.
550
-
551
- This is the second major tool for avoiding duplication:
552
-
553
- - use transformations when encoded and decoded representations differ
554
- - use derivation when one schema is a subset, superset, or variation of another
555
-
556
- ## Constraints And Validation
557
-
558
- Use schema checks and filters for validation.
559
-
560
- Examples from the module docs include:
561
-
562
- - `isMinLength`
563
- - `isGreaterThan`
564
- - `isPattern`
565
- - `isUUID`
566
-
567
- Attach them with `.check(...)`.
568
-
569
- Use this when:
570
-
571
- - the validation is intrinsic to the schema
572
- - the rule belongs to the data contract
573
-
574
- For business-rule validation that depends on services or current state, prefer effectful logic outside the schema or use effectful transformations.
575
-
576
- ## Decoding And Encoding
577
-
578
- Common operations:
579
-
580
- - `Schema.decodeUnknownSync`
581
- - `Schema.decodeUnknownEffect`
582
- - `Schema.decodeUnknownExit`
583
- - `Schema.encodeUnknownSync`
584
- - `Schema.encodeUnknownEffect`
585
-
586
- Preferred rule:
587
-
588
- - use `decodeUnknownEffect` and `encodeUnknownEffect` in Effect code
589
- - avoid throwing sync decode APIs in application flows unless you are intentionally at a sync boundary
590
-
591
- Good pattern:
592
-
593
- ```ts
594
- const decodeUser = Schema.decodeUnknownEffect(User);
595
- ```
596
-
597
- ## Schema Metadata And Derived Tooling
598
-
599
- Schema is also used for:
600
-
601
- - annotations and documentation metadata
602
- - JSON Schema generation
603
- - arbitrary generation for tests
604
- - derived equivalence
605
-
606
- Useful operations from the module docs:
607
-
608
- - `.annotate(...)`
609
- - `Schema.toJsonSchemaDocument(...)`
610
- - `Schema.toArbitrary(...)`
611
- - `Schema.toEquivalence(...)`
612
-
613
- Use annotations when the schema participates in:
614
-
615
- - API docs
616
- - codegen
617
- - contract generation
618
-
619
- ## Common Repo Patterns
620
-
621
- Patterns visible in the canonical source:
622
-
623
- - `Schema.Class` for named reusable contract types
624
- - `Schema.Struct` for inline shapes and anonymous fragments
625
- - `Schema.Union` for alternative payloads
626
- - `Schema.optionalKey` for request/query/body optional fields
627
- - `Schema.suspend` for recursive generated schemas
628
- - `Schema.decodeTo` and `transformOrFail` for non-trivial decode/encode logic
629
- - `Schema.TaggedErrorClass` for typed error payloads
630
-
631
- ## Best Practices
632
-
633
- 1. Prefer `Class` variants over plain `Struct` for named reusable schemas.
634
- 2. Prefer tagged variants for unions and errors.
635
- 3. Prefer `optionalKey` for optional object properties.
636
- 4. Do not duplicate schemas unless there is a real semantic difference.
637
- 5. Prefer schema-level transformations over ad hoc post-parse object rewriting.
638
- 6. Prefer deriving schema variants with `pick`, `omit`, `partial`, and `mutable` instead of duplicating definitions.
639
- 7. Prefer field-level transformations when only a field encoding differs.
640
- 8. Prefer branded or opaque types for important domain identifiers.
641
- 9. Prefer `decodeUnknownEffect` in application code.
642
- 10. Keep internal decoded shapes idiomatic and use schema transforms for external representation differences.
643
- 11. Give every data-bearing service input and result an exported schema.
644
- 12. Audit changed identifier fields for the correct semantic brand.
645
- 13. Decode external data once at its earliest owning boundary.
646
-
647
- ## Anti-Patterns
648
-
649
- - using plain `Struct` for every reusable domain model even when `Class` would give a clearer named type
650
- - duplicating whole schemas when only one field encoding differs
651
- - creating `Foo` and `FooSql` schemas for the same logical model when a transformation would do
652
- - using `optional` when you actually want an optional key
653
- - duplicating near-identical schemas instead of deriving variants
654
- - rewriting keys manually after decode instead of using schema transformations
655
- - hand-validating external data after decode when the constraint belongs in the schema
656
- - exposing unvalidated external payloads deep into business logic
657
-
658
- ## Good Repo Examples To Study
659
-
660
- - `packages/tools/ai-codegen/src/Config.ts`
661
- - `packages/platform-node/test/fixtures/rpc-schemas.ts`
662
- - `packages/platform-browser/test/IndexedDbQueryBuilder.test.ts`
663
- - `packages/tools/openapi-generator/src/JsonSchemaGenerator.ts`
664
- - `packages/effect/src/Schema.ts`