@danieljvdm/dev-kit 0.11.3 → 0.12.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 (74) hide show
  1. package/README.md +70 -76
  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/dev-kit/SKILL.md +58 -46
  17. package/skills/effect-architecture-audit/SKILL.md +26 -0
  18. package/skills/effect-architecture-audit/agents/openai.yaml +4 -0
  19. package/skills/effect-architecture-audit/references/service-and-boundary-audit.md +150 -0
  20. package/skills/effect-ts/SKILL.md +21 -256
  21. package/skills/effect-ts/agents/openai.yaml +3 -3
  22. package/skills/testing/SKILL.md +5 -0
  23. package/src/catalog-manager.ts +16 -17
  24. package/src/catalog.ts +71 -16
  25. package/src/effect-source.ts +46 -24
  26. package/src/effect-tsgo.ts +49 -24
  27. package/src/gitignore.ts +5 -5
  28. package/src/index.ts +0 -6
  29. package/src/manifest.ts +0 -34
  30. package/src/node-symbolic-link.ts +2 -2
  31. package/src/oxfmt.js +5 -0
  32. package/src/oxfmt.ts +5 -0
  33. package/src/oxlint.js +5 -0
  34. package/src/oxlint.ts +5 -0
  35. package/src/package-skill-source.ts +51 -59
  36. package/src/path-digest.ts +7 -7
  37. package/src/project-package.ts +8 -7
  38. package/src/project-process-lock.ts +17 -12
  39. package/src/project-state.ts +1 -1
  40. package/src/skill-manager.ts +16 -14
  41. package/src/skill-selector.ts +12 -0
  42. package/src/sync.ts +170 -120
  43. package/src/tool-ignore-patterns.js +9 -0
  44. package/src/tool-ignore-patterns.ts +15 -0
  45. package/src/vendor.ts +67 -61
  46. package/src/vite-plus-dependency.ts +10 -11
  47. package/src/vite-plus-hooks.ts +24 -14
  48. package/src/vite-plus-quality.ts +21 -172
  49. package/src/vite-plus.js +81 -0
  50. package/src/vite-plus.ts +102 -0
  51. package/templates/AGENTS.md +1 -1
  52. package/skills/effect-ts/UPSTREAM.md +0 -28
  53. package/skills/effect-ts/references/atom-cache-lifecycle.md +0 -78
  54. package/skills/effect-ts/references/atom-http-and-invalidation.md +0 -97
  55. package/skills/effect-ts/references/atom-tanstack-start.md +0 -69
  56. package/skills/effect-ts/references/atom-testing.md +0 -67
  57. package/skills/effect-ts/references/audit-services.md +0 -144
  58. package/skills/effect-ts/references/features.md +0 -525
  59. package/skills/effect-ts/references/guide-atom-data-fetching.md +0 -44
  60. package/skills/effect-ts/references/guide-cli.md +0 -107
  61. package/skills/effect-ts/references/guide-datetime.md +0 -72
  62. package/skills/effect-ts/references/guide-effect.md +0 -440
  63. package/skills/effect-ts/references/guide-error-handling.md +0 -565
  64. package/skills/effect-ts/references/guide-http-boundaries.md +0 -55
  65. package/skills/effect-ts/references/guide-layers.md +0 -989
  66. package/skills/effect-ts/references/guide-observability.md +0 -746
  67. package/skills/effect-ts/references/guide-retries.md +0 -434
  68. package/skills/effect-ts/references/guide-schedule.md +0 -343
  69. package/skills/effect-ts/references/guide-schema.md +0 -664
  70. package/skills/effect-ts/references/guide-sql.md +0 -536
  71. package/skills/effect-ts/references/guide-testing.md +0 -532
  72. package/skills/effect-ts/references/guide-type-safety-and-boundaries.md +0 -131
  73. package/skills/effect-ts/references/version-and-source.md +0 -86
  74. package/templates/vite-plus/vite.config.ts +0 -22
@@ -1,565 +0,0 @@
1
- # Error Handling Guide
2
-
3
- This guide covers expected failures, schema-backed errors, foreign failure
4
- translation, defects, interrupts, and failure placement.
5
-
6
- Key source files:
7
-
8
- - `packages/effect/src/Data.ts`
9
- - `packages/effect/src/Schema.ts`
10
- - `packages/effect/src/Cause.ts`
11
- - `packages/effect/src/Effect.ts`
12
- - `packages/effect/src/unstable/sql/SqlError.ts`
13
-
14
- ## Mental Model
15
-
16
- Effect distinguishes three failure modes:
17
-
18
- - failure: expected, typed errors in the `E` channel of `Effect<A, E, R>`
19
- - defect: unexpected unchecked failures, represented as `Cause.Die`
20
- - interrupt: cooperative cancellation, represented as `Cause.Interrupt`
21
-
22
- This distinction is explicit in `Cause`.
23
-
24
- Repo references:
25
-
26
- - `packages/effect/src/Cause.ts`
27
- - `packages/effect/src/Effect.ts`
28
-
29
- ## Preferred Error Definition Styles
30
-
31
- Preference order:
32
-
33
- 1. use schema-based errors when possible
34
- 2. fall back to `Data.TaggedError` only when the error payload is not meaningfully serializable or schema-shaped
35
-
36
- Schema-based errors are strictly more powerful because they give you:
37
-
38
- - typed yieldable errors
39
- - schema-defined fields
40
- - encode/decode support
41
- - better protocol and boundary interoperability
42
- - stronger documentation and tooling hooks
43
-
44
- ### 1. `Schema.TaggedErrorClass` for schema-backed tagged errors
45
-
46
- Use `Schema.TaggedErrorClass` by default when the error can be described with schemas.
47
-
48
- Why:
49
-
50
- - it creates a yieldable tagged error
51
- - fields are defined with `Schema`
52
- - the error shape can participate in schema-based tooling and encode/decode flows
53
-
54
- Repo references:
55
-
56
- - `packages/effect/src/Schema.ts`
57
- - `packages/effect/src/unstable/sql/SqlError.ts`
58
-
59
- Example:
60
-
61
- ```ts
62
- import { Effect, Schema } from "effect";
63
-
64
- class InvalidPayload extends Schema.TaggedErrorClass<InvalidPayload>()("InvalidPayload", {
65
- field: Schema.String,
66
- reason: Schema.String,
67
- }) {}
68
-
69
- const validate = Effect.fail(
70
- InvalidPayload.make({
71
- field: "email",
72
- reason: "missing",
73
- }),
74
- );
75
- ```
76
-
77
- Expected application and service failures belong in the typed error channel.
78
- In generators, use an explicit control-flow exit when failing:
79
-
80
- ```ts
81
- return (
82
- yield *
83
- Effect.fail(
84
- InvalidPayload.make({
85
- field: "email",
86
- reason: "missing",
87
- }),
88
- )
89
- );
90
- ```
91
-
92
- Use this when:
93
-
94
- - the error is part of a protocol or transport boundary
95
- - the error needs a precise schema representation
96
- - the error should be serializable or documented structurally
97
-
98
- This is also a good default for domain errors when their payload is schema-friendly.
99
-
100
- ### 2. `Schema.ErrorClass` for schema-backed errors without `_tag` routing
101
-
102
- Use `Schema.ErrorClass` when you want schema-defined error objects but do not specifically need tag-based pattern matching.
103
-
104
- Repo references:
105
-
106
- - `packages/effect/src/Schema.ts`
107
- - examples across `packages/effect/src/unstable/*`
108
-
109
- Example shape from the canonical source:
110
-
111
- - `packages/effect/src/unstable/httpapi/HttpApiError.ts`
112
- - `packages/effect/src/unstable/workers/WorkerError.ts`
113
-
114
- ### 3. `Data.TaggedError` for non-serializable or lightweight domain errors
115
-
116
- Use `Data.TaggedError` when schema-based errors are not a good fit.
117
-
118
- This is mainly the fallback for:
119
-
120
- - non-serializable payloads
121
- - ad hoc in-memory-only errors
122
- - cases where schema shape would be artificial or misleading
123
-
124
- Repo reference:
125
-
126
- - `packages/effect/src/Data.ts`
127
-
128
- Example:
129
-
130
- ```ts
131
- import { Data, Effect } from "effect";
132
-
133
- class UserNotFound extends Data.TaggedError("UserNotFound")<{
134
- readonly userId: string;
135
- }> {}
136
-
137
- const loadUser = (userId: string) => Effect.fail(new UserNotFound({ userId }));
138
-
139
- const program = Effect.gen(function* () {
140
- yield* loadUser("u_123");
141
- });
142
- ```
143
-
144
- ## When To Prefer `Data.TaggedError` vs `Schema.TaggedErrorClass`
145
-
146
- Prefer `Schema.TaggedErrorClass` when:
147
-
148
- - the error can be expressed as a schema
149
- - the error payload must be described by schemas
150
- - the error crosses process, protocol, persistence, or serialization boundaries
151
- - you want the error type to participate in schema tooling
152
-
153
- Prefer `Data.TaggedError` when:
154
-
155
- - the payload is not meaningfully serializable
156
- - the payload cannot reasonably be modeled as a schema
157
- - the error is intentionally local and in-memory only
158
-
159
- ## Schema-Based Error Workflows
160
-
161
- ### Boundary validation should fail with `SchemaError`
162
-
163
- When validating external input, Effect's schema APIs return `SchemaError` in the error channel.
164
-
165
- Repo references:
166
-
167
- - `packages/effect/src/Schema.ts`
168
- - `Schema.decodeUnknownEffect`
169
- - `Schema.decodeUnknownExit`
170
- - `Schema.encodeUnknownEffect`
171
-
172
- Example:
173
-
174
- ```ts
175
- import { Effect, Schema } from "effect";
176
-
177
- const UserPayload = Schema.Struct({
178
- id: Schema.String,
179
- email: Schema.String,
180
- });
181
-
182
- const decodeUser = Schema.decodeUnknownEffect(UserPayload);
183
- ```
184
-
185
- This gives you:
186
-
187
- - success: validated typed data
188
- - failure: `Schema.SchemaError`
189
-
190
- ### Normalize `SchemaError` at the boundary
191
-
192
- For application code, it is often better to convert `SchemaError` into a domain error near the boundary.
193
-
194
- Example:
195
-
196
- ```ts
197
- import { Data, Effect, Schema } from "effect";
198
-
199
- class InvalidRequestBody extends Data.TaggedError("InvalidRequestBody")<{
200
- readonly message: string;
201
- }> {}
202
-
203
- const UserPayload = Schema.Struct({
204
- id: Schema.String,
205
- email: Schema.String,
206
- });
207
-
208
- const decodeUser = (input: unknown) =>
209
- Schema.decodeUnknownEffect(UserPayload)(input).pipe(
210
- Effect.catchTag("SchemaError", (error) =>
211
- Effect.fail(new InvalidRequestBody({ message: error.message })),
212
- ),
213
- );
214
- ```
215
-
216
- Why:
217
-
218
- - transport validation stays close to the transport layer
219
- - the rest of the application can work with domain-specific errors
220
-
221
- ### Use schema-backed errors for protocol errors
222
-
223
- The canonical source uses schema-backed errors in places like SQL, RPC, sockets, and HTTP APIs.
224
-
225
- Strong examples:
226
-
227
- - `packages/effect/src/unstable/sql/SqlError.ts`
228
- - `packages/effect/src/unstable/socket/Socket.ts`
229
- - `packages/effect/src/unstable/eventlog/EventLogMessage.ts`
230
-
231
- These are good reference points when the error contract matters externally.
232
-
233
- ## Wrapping Foreign Or Generic Errors
234
-
235
- When an error comes from a library, runtime API, or generic `Error`, prefer wrapping it in a typed error instead of leaking the foreign error directly through your domain or protocol boundary.
236
-
237
- Use `Effect.tryPromise` at external Promise boundaries and translate a
238
- recoverable rejection once, in the service or adapter that owns that boundary.
239
- Do not convert Effect-native APIs to Promise and immediately wrap them again.
240
-
241
- This is a very common pattern in the canonical source.
242
-
243
- Good examples:
244
-
245
- - `packages/effect/src/unstable/sql/SqlError.ts`
246
- - `packages/effect/src/unstable/rpc/RpcClientError.ts`
247
- - `packages/effect/src/unstable/socket/Socket.ts`
248
- - `packages/effect/src/unstable/workers/WorkerError.ts`
249
- - `packages/effect/src/unstable/persistence/Redis.ts`
250
-
251
- ### Preferred Pattern
252
-
253
- Wrap the foreign error in a schema-backed typed error and preserve the original error in a `cause` field.
254
-
255
- Prefer using:
256
-
257
- - `Schema.Defect()` when you want to preserve a generic encoded defect
258
- - `Schema.Defect({ includeStack: true })` when the stack should be preserved in
259
- the schema contract
260
-
261
- Example:
262
-
263
- ```ts
264
- import { Effect, Schema } from "effect";
265
-
266
- class TodoStorageError extends Schema.TaggedErrorClass<TodoStorageError>()("TodoStorageError", {
267
- operation: Schema.String,
268
- cause: Schema.Defect(),
269
- }) {}
270
-
271
- const makeStorageError = (operation: string) => (cause: unknown) =>
272
- TodoStorageError.make({
273
- operation,
274
- cause,
275
- });
276
-
277
- const loadTodo = (id: number) =>
278
- Effect.try({
279
- try: () => someLibraryCall(id),
280
- catch: makeStorageError("loadTodo"),
281
- });
282
- ```
283
-
284
- When stack preservation matters in the encoded schema, prefer:
285
-
286
- ```ts
287
- class WorkerFailure extends Schema.TaggedErrorClass<WorkerFailure>()("WorkerFailure", {
288
- cause: Schema.Defect({ includeStack: true }),
289
- }) {}
290
- ```
291
-
292
- ### Why This Is Preferred
293
-
294
- - the application still exposes a typed error contract
295
- - the original foreign failure is preserved for diagnostics
296
- - schema-aware transports can encode and decode the failure shape
297
- - business code does not become coupled to a raw library error type
298
-
299
- ### When To Use This Pattern
300
-
301
- Use it when:
302
-
303
- - a third-party library throws or rejects with `Error`
304
- - a runtime API returns generic failures
305
- - a lower-level subsystem failure should be surfaced through a typed domain or protocol error
306
- - you need to preserve the underlying failure for debugging without leaking the foreign type as the public error contract
307
-
308
- ### `Schema.Defect()` options
309
-
310
- Prefer `Schema.Defect()` by default.
311
-
312
- Use `Schema.Defect({ includeStack: true })` when:
313
-
314
- - stack information is part of the intended encoded error contract
315
- - the error is primarily infrastructural or diagnostic
316
- - the downstream consumer benefits from the preserved stack
317
-
318
- ### Avoid This Anti-Pattern
319
-
320
- Avoid exposing raw generic errors directly as the application error contract.
321
-
322
- Bad:
323
-
324
- ```ts
325
- const loadTodo = (id: number) =>
326
- Effect.try({
327
- try: () => someLibraryCall(id),
328
- catch: (cause) => cause as Error,
329
- });
330
- ```
331
-
332
- Why this is bad:
333
-
334
- - the error channel loses a stable typed contract
335
- - the code depends on unsafe assertions
336
- - transport and schema integration become weaker
337
- - callers must understand foreign error shapes instead of your own typed error model
338
-
339
- ## Failure Placement
340
-
341
- - Represent expected domain and service failures with `Effect.fail` and stable,
342
- schema-backed tagged errors in public error unions.
343
- - Translate external causes once, adding the domain meaning callers need.
344
- - Keep raw `Error` and `unknown` out of public expected-error unions.
345
- - Use `Effect.orDie` only at an explicit runtime boundary when failure is
346
- intentionally fatal.
347
- - Reserve synchronous throws for framework contracts, impossible invariants,
348
- defects, and thunks immediately captured by `Effect.try` or
349
- `Effect.tryPromise`.
350
- - Apply the boundary classification and audit process in
351
- [`guide-type-safety-and-boundaries.md`](guide-type-safety-and-boundaries.md)
352
- whenever catch callbacks, helpers, or public signatures introduce `unknown`,
353
- assertions, or runtime shape checks.
354
-
355
- ## Handling Failures
356
-
357
- ### Handle specific tagged errors with `Effect.catchTag`
358
-
359
- Use `catchTag` when your error type has `_tag` and you want focused recovery.
360
-
361
- Repo reference:
362
-
363
- - `packages/effect/src/Effect.ts`
364
-
365
- Example:
366
-
367
- ```ts
368
- const recovered = program.pipe(
369
- Effect.catchTag("UserNotFound", (error) => Effect.succeed({ id: error.userId, guest: true })),
370
- );
371
- ```
372
-
373
- ### Handle several tagged errors with `Effect.catchTags`
374
-
375
- Use `catchTags` when multiple domain errors should be handled together.
376
-
377
- ```ts
378
- const recovered = program.pipe(
379
- Effect.catchTags({
380
- UserNotFound: () => Effect.succeed(null),
381
- InvalidPayload: (error) => Effect.succeed({ error: error.reason }),
382
- }),
383
- );
384
- ```
385
-
386
- ### Handle predicate-based subsets with `Effect.catchIf`
387
-
388
- Use `catchIf` when matching on a predicate or refinement, not just `_tag`.
389
-
390
- ### Turn failure into a value with `Effect.match`
391
-
392
- Use `match` when you want to fully fold the typed error channel into a success value.
393
-
394
- ```ts
395
- const outcome = program.pipe(
396
- Effect.match({
397
- onFailure: (error) => ({ ok: false as const, error }),
398
- onSuccess: (value) => ({ ok: true as const, value }),
399
- }),
400
- );
401
- ```
402
-
403
- ## Handling Defects
404
-
405
- Defects are not normal domain failures.
406
-
407
- They come from:
408
-
409
- - `Effect.die`
410
- - unchecked exceptions in effectful code
411
- - invariants that were broken
412
-
413
- Repo references:
414
-
415
- - `packages/effect/src/Cause.ts`
416
- - `packages/effect/src/Effect.ts`
417
-
418
- ### Preferred rule
419
-
420
- Do not model expected business failures as defects.
421
-
422
- Use defects for:
423
-
424
- - impossible states
425
- - programmer errors
426
- - unrecoverable infrastructure corruption
427
-
428
- ### Inspect defects with `sandbox`, `catchCause`, or `matchCause`
429
-
430
- Use `sandbox` to expose `Cause<E>` in the error channel.
431
-
432
- ```ts
433
- import { Cause, Effect } from "effect";
434
-
435
- const diagnosed = program.pipe(
436
- Effect.sandbox,
437
- Effect.catchCause((cause) => {
438
- if (Cause.hasDies(cause)) {
439
- return Effect.succeed("defect");
440
- }
441
- return Effect.failCause(cause);
442
- }),
443
- );
444
- ```
445
-
446
- Use `matchCause` or `matchCauseEffect` when you need to distinguish:
447
-
448
- - typed failures
449
- - defects
450
- - interrupts
451
-
452
- ### Boundary-only recovery for defects
453
-
454
- If you recover from defects at all, do it only at clear boundaries.
455
-
456
- Examples:
457
-
458
- - worker or RPC boundary
459
- - CLI top-level runner
460
- - HTTP server adapter
461
-
462
- Typical pattern:
463
-
464
- - log or report defect details
465
- - translate to a safe external error
466
- - avoid continuing as if it were a normal domain failure
467
-
468
- ### `Effect.orDie`
469
-
470
- Use `orDie` when an error channel should be treated as unrecoverable from this point onward.
471
-
472
- That is appropriate when:
473
-
474
- - a failure has already been validated elsewhere as impossible
475
- - continuing with typed recovery would only obscure a broken invariant
476
-
477
- Do not use `orDie` just to silence a type you do not want to handle.
478
-
479
- ## Handling Interrupts
480
-
481
- Interrupts are cancellation, not business failure.
482
-
483
- Repo references:
484
-
485
- - `packages/effect/src/Cause.ts`
486
- - `packages/effect/src/Effect.ts`
487
-
488
- ### Use `Effect.interrupt` to stop work cooperatively
489
-
490
- Interrupts signal that the fiber should stop. They should not usually be translated into a domain error.
491
-
492
- ### Use `Effect.onInterrupt` for cleanup
493
-
494
- If interrupted work needs special cleanup, use `onInterrupt`.
495
-
496
- ```ts
497
- import { Console, Effect } from "effect";
498
-
499
- const program = longRunningTask.pipe(
500
- Effect.onInterrupt(() => Console.log("cleaning up after interrupt")),
501
- );
502
- ```
503
-
504
- ### Use `Cause` inspection when interrupts must be distinguished
505
-
506
- When handling full causes, use `Cause.isInterruptReason`, `Cause.hasInterrupts`, or filtering over `cause.reasons`.
507
-
508
- This is useful for:
509
-
510
- - deciding whether to suppress logs for normal cancellation
511
- - keeping retries for failure but not for cancellation
512
- - distinguishing timeout/cancel flows from real errors
513
-
514
- ### Do not treat interrupts as ordinary failures
515
-
516
- Avoid patterns that collapse all causes into a single error value too early. Interrupts often need different operational behavior.
517
-
518
- ## Recommended Patterns
519
-
520
- ### Pattern: domain errors inside the app, schema errors at the edge
521
-
522
- - decode external input with `Schema.decodeUnknownEffect`
523
- - convert `SchemaError` into a domain or transport error near the boundary
524
- - keep the rest of the application on domain errors
525
-
526
- ### Pattern: tagged errors for recovery
527
-
528
- - define domain failures with `Data.TaggedError`
529
- - recover with `catchTag` or `catchTags`
530
- - keep `_tag` names stable and descriptive
531
-
532
- ### Pattern: schema-backed errors for protocols
533
-
534
- - use `Schema.TaggedErrorClass` or `Schema.ErrorClass` when the error contract itself matters
535
- - follow examples in SQL, socket, RPC, and HTTP modules
536
-
537
- ### Pattern: only inspect `Cause` when you really need the full failure structure
538
-
539
- Use `catchCause`, `matchCause`, or `sandbox` when you must distinguish:
540
-
541
- - expected failures
542
- - defects
543
- - interrupts
544
-
545
- Otherwise prefer the simpler typed error operators.
546
-
547
- ## Anti-Patterns
548
-
549
- - using defects for expected validation or business-rule failures
550
- - converting every error immediately to `unknown` or `string`
551
- - using `orDie` to avoid proper handling of expected errors
552
- - treating interrupts as ordinary business failures
553
- - leaking `SchemaError` deep into domain code when it should be normalized at the boundary
554
-
555
- ## Good Repo Examples To Study
556
-
557
- - `packages/effect/src/Data.ts`
558
- - `packages/effect/src/Cause.ts`
559
- - `packages/effect/src/Effect.ts`
560
- - `packages/effect/src/Schema.ts`
561
- - `packages/effect/src/unstable/sql/SqlError.ts`
562
- - `packages/effect/src/unstable/http/HttpClientError.ts`
563
- - `packages/effect/src/unstable/http/HttpServerError.ts`
564
- - `packages/effect/src/unstable/socket/Socket.ts`
565
- - `packages/effect/src/unstable/httpapi/HttpApiError.ts`
@@ -1,55 +0,0 @@
1
- # Effect HTTP Boundaries
2
-
3
- Use this when changing `HttpApi` contracts, handlers, DTOs, or route boundary
4
- code.
5
-
6
- ## Contracts
7
-
8
- Prefer one endpoint per file.
9
-
10
- The endpoint file declares route params, query, payload, success, and error
11
- schemas inline. Group and index files compose or re-export only.
12
-
13
- Do not create endpoint DTO bundle files unless multiple endpoints share a real
14
- transport type.
15
-
16
- ## Handlers
17
-
18
- Handlers are thin request-boundary adapters.
19
-
20
- A handler should decode transport inputs, call one service workflow, and map the
21
- result to the response DTO.
22
-
23
- Business orchestration, persistence, rollback, and cross-service coordination
24
- belong in services, not endpoint files.
25
-
26
- Do not provide app service layers inside endpoint files unless there is a
27
- specific transport-only dependency.
28
-
29
- ## DTOs
30
-
31
- Request DTOs describe the route contract explicitly.
32
-
33
- Response DTOs describe the wire shape explicitly. If clients see `id`, the DTO
34
- uses `id`; service models can keep `publicId`.
35
-
36
- Use small route-layer mappers for service model to wire DTO conversion.
37
-
38
- Prefer named nested DTOs when fields are reused or transport-sensitive.
39
-
40
- ## Errors
41
-
42
- Expected HTTP failures belong in endpoint `error:` schemas.
43
-
44
- Prefer tagged or schema errors with HTTP status metadata.
45
-
46
- Success schemas describe success only. Do not encode error bodies in `success`.
47
-
48
- ## Transport APIs
49
-
50
- Use `HttpServerResponse` for upstream response passthrough, redirects, cookies,
51
- or non-default success status/body behavior.
52
-
53
- Do not use `HttpServerResponse.jsonUnsafe` for ordinary typed 4xx errors.
54
-
55
- Prefer Effect request and cookie APIs over manual header or cookie parsing.