@martinffx/atelier 2.0.0 → 3.0.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 (81) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/README.md +94 -253
  3. package/package.json +1 -1
  4. package/skills/{spec-orchestrator → atelier-orchestrator}/SKILL.md +3 -3
  5. package/skills/atelier-setup/SKILL.md +80 -0
  6. package/skills/code-review/references/reviewers.md +3 -3
  7. package/skills/code-review/references/rq.md +6 -6
  8. package/skills/spec-brainstorm/SKILL.md +5 -5
  9. package/skills/spec-implement/SKILL.md +2 -3
  10. package/skills/spec-plan/SKILL.md +21 -29
  11. package/skills/spec-plan/references/plan_template.md +77 -0
  12. package/skills/python-architecture/SKILL.md +0 -148
  13. package/skills/python-architecture/references/data-modeling.md +0 -558
  14. package/skills/python-architecture/references/ddd.md +0 -569
  15. package/skills/python-architecture/references/functional-core.md +0 -337
  16. package/skills/python-build-tools/SKILL.md +0 -153
  17. package/skills/python-build-tools/references/basedpyright.md +0 -321
  18. package/skills/python-build-tools/references/ruff.md +0 -225
  19. package/skills/python-build-tools/references/uv.md +0 -257
  20. package/skills/python-fastapi/SKILL.md +0 -184
  21. package/skills/python-fastapi/references/api-design.md +0 -563
  22. package/skills/python-fastapi/references/dependencies.md +0 -479
  23. package/skills/python-fastapi/references/middleware.md +0 -475
  24. package/skills/python-fastapi/references/validation.md +0 -497
  25. package/skills/python-modern-python/SKILL.md +0 -201
  26. package/skills/python-modern-python/references/async.md +0 -76
  27. package/skills/python-modern-python/references/pattern-matching.md +0 -89
  28. package/skills/python-modern-python/references/typing.md +0 -96
  29. package/skills/python-monorepo/SKILL.md +0 -146
  30. package/skills/python-monorepo/references/docker.md +0 -515
  31. package/skills/python-monorepo/references/namespace-packages.md +0 -426
  32. package/skills/python-monorepo/references/workspace-config.md +0 -341
  33. package/skills/python-sqlalchemy/SKILL.md +0 -119
  34. package/skills/python-sqlalchemy/references/async.md +0 -51
  35. package/skills/python-sqlalchemy/references/models.md +0 -67
  36. package/skills/python-sqlalchemy/references/queries.md +0 -57
  37. package/skills/python-temporal/SKILL.md +0 -128
  38. package/skills/python-temporal/references/patterns.md +0 -87
  39. package/skills/python-temporal/references/testing.md +0 -54
  40. package/skills/python-testing/SKILL.md +0 -161
  41. package/skills/python-testing/references/boundaries.md +0 -449
  42. package/skills/python-testing/references/mocking.md +0 -449
  43. package/skills/python-testing/references/pytest.md +0 -524
  44. package/skills/typescript-api-design/SKILL.md +0 -396
  45. package/skills/typescript-api-design/references/error-responses.md +0 -469
  46. package/skills/typescript-build-tools/SKILL.md +0 -447
  47. package/skills/typescript-build-tools/references/biome.md +0 -436
  48. package/skills/typescript-build-tools/references/bun.md +0 -432
  49. package/skills/typescript-build-tools/references/turborepo.md +0 -426
  50. package/skills/typescript-build-tools/references/vitest.md +0 -518
  51. package/skills/typescript-drizzle-orm/SKILL.md +0 -398
  52. package/skills/typescript-drizzle-orm/references/cloudflare.md +0 -392
  53. package/skills/typescript-drizzle-orm/references/entity-pattern.md +0 -412
  54. package/skills/typescript-drizzle-orm/references/postgresql.md +0 -191
  55. package/skills/typescript-drizzle-orm/references/repository-pattern.md +0 -479
  56. package/skills/typescript-drizzle-orm/references/sqlite.md +0 -291
  57. package/skills/typescript-dynamodb-toolbox/SKILL.md +0 -370
  58. package/skills/typescript-dynamodb-toolbox/references/entity-layer.md +0 -574
  59. package/skills/typescript-dynamodb-toolbox/references/error-handling.md +0 -495
  60. package/skills/typescript-dynamodb-toolbox/references/modeling.md +0 -332
  61. package/skills/typescript-dynamodb-toolbox/references/repository-layer.md +0 -631
  62. package/skills/typescript-dynamodb-toolbox/references/testing.md +0 -718
  63. package/skills/typescript-dynamodb-toolbox/references/transactions.md +0 -423
  64. package/skills/typescript-effect-ts/SKILL.md +0 -341
  65. package/skills/typescript-effect-ts/references/core.md +0 -412
  66. package/skills/typescript-effect-ts/references/error-handling.md +0 -468
  67. package/skills/typescript-effect-ts/references/resources.md +0 -476
  68. package/skills/typescript-effect-ts/references/schema.md +0 -536
  69. package/skills/typescript-effect-ts/references/services.md +0 -461
  70. package/skills/typescript-fastify/SKILL.md +0 -405
  71. package/skills/typescript-fastify/references/plugins.md +0 -329
  72. package/skills/typescript-fastify/references/typeid.md +0 -315
  73. package/skills/typescript-functional-patterns/SKILL.md +0 -402
  74. package/skills/typescript-functional-patterns/references/adts.md +0 -497
  75. package/skills/typescript-functional-patterns/references/branded-types.md +0 -542
  76. package/skills/typescript-functional-patterns/references/migration-guide.md +0 -695
  77. package/skills/typescript-functional-patterns/references/option-result.md +0 -585
  78. package/skills/typescript-testing/SKILL.md +0 -353
  79. package/skills/typescript-testing/references/mocking.md +0 -675
  80. package/skills/typescript-testing/references/msw.md +0 -852
  81. package/skills/typescript-testing/references/snapshot-testing.md +0 -774
@@ -1,468 +0,0 @@
1
- # Error Handling in Effect
2
-
3
- Effect provides comprehensive error handling with typed errors tracked in the type system. Errors are values, not exceptions, enabling exhaustive handling and composition.
4
-
5
- ## Error Types
6
-
7
- ### Expected Errors (E channel)
8
-
9
- Typed errors that are part of the function contract:
10
-
11
- ```typescript
12
- import { Effect } from "effect"
13
-
14
- class UserNotFound extends Error {
15
- readonly _tag = "UserNotFound"
16
- constructor(readonly userId: string) {
17
- super(`User not found: ${userId}`)
18
- }
19
- }
20
-
21
- class ValidationError extends Error {
22
- readonly _tag = "ValidationError"
23
- constructor(readonly field: string, readonly message: string) {
24
- super(`${field}: ${message}`)
25
- }
26
- }
27
-
28
- // Error type is tracked: Effect<User, UserNotFound | ValidationError, never>
29
- const getUser = (id: string): Effect.Effect<User, UserNotFound | ValidationError> =>
30
- Effect.gen(function* () {
31
- if (!isValidId(id)) {
32
- return yield* Effect.fail(new ValidationError("id", "Invalid format"))
33
- }
34
- const user = yield* fetchFromDb(id)
35
- if (!user) {
36
- return yield* Effect.fail(new UserNotFound(id))
37
- }
38
- return user
39
- })
40
- ```
41
-
42
- ### Defects (Unexpected Errors)
43
-
44
- Untyped errors for bugs and invariant violations:
45
-
46
- ```typescript
47
- import { Effect } from "effect"
48
-
49
- // Die creates a defect - not tracked in E type
50
- const assertPositive = (n: number) =>
51
- n > 0
52
- ? Effect.succeed(n)
53
- : Effect.die(`Expected positive number, got ${n}`)
54
-
55
- // Defects propagate but aren't in the error type
56
- const program: Effect.Effect<number> = assertPositive(-1)
57
- // Type says never fails, but will throw at runtime
58
- ```
59
-
60
- ### Tagged Error Pattern
61
-
62
- Always use `_tag` for discriminated union handling:
63
-
64
- ```typescript
65
- // Define error classes with _tag
66
- class NetworkError extends Error {
67
- readonly _tag = "NetworkError" as const
68
- constructor(readonly cause: unknown) {
69
- super("Network error")
70
- }
71
- }
72
-
73
- class TimeoutError extends Error {
74
- readonly _tag = "TimeoutError" as const
75
- constructor(readonly ms: number) {
76
- super(`Timeout after ${ms}ms`)
77
- }
78
- }
79
-
80
- class ParseError extends Error {
81
- readonly _tag = "ParseError" as const
82
- constructor(readonly input: string) {
83
- super(`Failed to parse: ${input}`)
84
- }
85
- }
86
-
87
- type FetchError = NetworkError | TimeoutError | ParseError
88
- ```
89
-
90
- ## Handling Errors
91
-
92
- ### catchAll - Handle All Errors
93
-
94
- ```typescript
95
- import { Effect } from "effect"
96
-
97
- const getUser = (id: string): Effect.Effect<User, UserNotFound> => // ...
98
-
99
- // Handle any error, must return same success type
100
- const recovered = getUser("123").pipe(
101
- Effect.catchAll((error) =>
102
- Effect.succeed({ id: "anonymous", name: "Guest" })
103
- )
104
- )
105
- // Effect<User, never, never> - error type becomes never
106
- ```
107
-
108
- ### catchTag - Handle Specific Error by Tag
109
-
110
- ```typescript
111
- import { Effect } from "effect"
112
-
113
- const fetchData = (): Effect.Effect<Data, NetworkError | ParseError> => // ...
114
-
115
- // Handle only NetworkError
116
- const withRetry = fetchData().pipe(
117
- Effect.catchTag("NetworkError", (error) =>
118
- Effect.sync(() => console.log("Retrying...")).pipe(
119
- Effect.flatMap(() => fetchData())
120
- )
121
- )
122
- )
123
- // Effect<Data, ParseError, never> - NetworkError handled
124
-
125
- // Handle multiple specific errors
126
- const handled = fetchData().pipe(
127
- Effect.catchTags({
128
- NetworkError: (e) => Effect.succeed(defaultData),
129
- ParseError: (e) => Effect.succeed(emptyData)
130
- })
131
- )
132
- // Effect<Data, never, never>
133
- ```
134
-
135
- ### catchSome - Handle Conditionally
136
-
137
- ```typescript
138
- import { Effect, Option } from "effect"
139
-
140
- const selective = effect.pipe(
141
- Effect.catchSome((error) => {
142
- if (error._tag === "Retryable") {
143
- return Option.some(retryEffect)
144
- }
145
- return Option.none() // Don't handle, propagate
146
- })
147
- )
148
- ```
149
-
150
- ### orElse - Fallback Effect
151
-
152
- ```typescript
153
- import { Effect } from "effect"
154
-
155
- // If first fails, try second
156
- const withFallback = primarySource().pipe(
157
- Effect.orElse(() => fallbackSource())
158
- )
159
-
160
- // Provide default on failure
161
- const withDefault = effect.pipe(
162
- Effect.orElseSucceed(() => defaultValue)
163
- )
164
-
165
- // Fail with different error
166
- const remapped = effect.pipe(
167
- Effect.orElseFail(() => new DifferentError())
168
- )
169
- ```
170
-
171
- ### mapError - Transform Error
172
-
173
- ```typescript
174
- import { Effect } from "effect"
175
-
176
- // Transform error type
177
- const wrapped = lowLevelOperation().pipe(
178
- Effect.mapError((error) => new HighLevelError(error))
179
- )
180
-
181
- // Add context to errors
182
- const withContext = dbQuery(sql).pipe(
183
- Effect.mapError((error) => ({
184
- ...error,
185
- query: sql,
186
- timestamp: Date.now()
187
- }))
188
- )
189
- ```
190
-
191
- ## Either and Exit
192
-
193
- ### Using Either
194
-
195
- ```typescript
196
- import { Effect, Either } from "effect"
197
-
198
- // Convert Effect to Either (errors become Left)
199
- const asEither = effect.pipe(Effect.either)
200
- // Effect<Either<E, A>, never, R>
201
-
202
- // Handle without short-circuiting
203
- const program = Effect.gen(function* () {
204
- const result = yield* Effect.either(mayFailEffect)
205
-
206
- if (Either.isLeft(result)) {
207
- console.log("Failed:", result.left)
208
- return defaultValue
209
- }
210
-
211
- return result.right
212
- })
213
- ```
214
-
215
- ### Using Exit
216
-
217
- ```typescript
218
- import { Effect, Exit } from "effect"
219
-
220
- // Get full exit status
221
- const exit = await Effect.runPromiseExit(effect)
222
-
223
- if (Exit.isSuccess(exit)) {
224
- console.log("Success:", exit.value)
225
- } else {
226
- // Cause contains full error information
227
- console.log("Failure:", Cause.pretty(exit.cause))
228
- }
229
- ```
230
-
231
- ## Cause - Rich Error Information
232
-
233
- Effect tracks not just errors but their full cause:
234
-
235
- ```typescript
236
- import { Effect, Cause } from "effect"
237
-
238
- // Cause types:
239
- // - Fail: Expected error in E channel
240
- // - Die: Unexpected defect
241
- // - Interrupt: Fiber was interrupted
242
- // - Sequential: Multiple causes in sequence
243
- // - Parallel: Multiple causes in parallel
244
-
245
- const program = effect.pipe(
246
- Effect.catchAllCause((cause) => {
247
- // Full cause tree available
248
- const failures = Cause.failures(cause) // E[]
249
- const defects = Cause.defects(cause) // unknown[]
250
-
251
- // Pretty print
252
- console.error(Cause.pretty(cause))
253
-
254
- return Effect.succeed(fallback)
255
- })
256
- )
257
- ```
258
-
259
- ## Retry Patterns
260
-
261
- ### Basic Retry
262
-
263
- ```typescript
264
- import { Effect, Schedule } from "effect"
265
-
266
- // Retry up to 3 times
267
- const retried = effect.pipe(
268
- Effect.retry(Schedule.recurs(3))
269
- )
270
-
271
- // Retry with delay
272
- const retriedWithDelay = effect.pipe(
273
- Effect.retry(
274
- Schedule.recurs(3).pipe(
275
- Schedule.addDelay(() => "1 second")
276
- )
277
- )
278
- )
279
- ```
280
-
281
- ### Exponential Backoff
282
-
283
- ```typescript
284
- import { Effect, Schedule } from "effect"
285
-
286
- const exponentialRetry = effect.pipe(
287
- Effect.retry(
288
- Schedule.exponential("100 millis").pipe(
289
- Schedule.jittered, // Add randomness
290
- Schedule.compose(Schedule.recurs(5)), // Max 5 retries
291
- Schedule.whileOutput(delay => delay < "30 seconds") // Cap delay
292
- )
293
- )
294
- )
295
- ```
296
-
297
- ### Conditional Retry
298
-
299
- ```typescript
300
- import { Effect, Schedule } from "effect"
301
-
302
- // Only retry specific errors
303
- const selectiveRetry = effect.pipe(
304
- Effect.retry({
305
- schedule: Schedule.recurs(3),
306
- while: (error) => error._tag === "Retryable"
307
- })
308
- )
309
-
310
- // Retry until condition
311
- const retryUntilSuccess = effect.pipe(
312
- Effect.retry({
313
- schedule: Schedule.spaced("1 second"),
314
- until: (error) => error._tag === "PermanentFailure"
315
- })
316
- )
317
- ```
318
-
319
- ## Timeouts
320
-
321
- ```typescript
322
- import { Effect, Duration } from "effect"
323
-
324
- // Fail with TimeoutException after duration
325
- const withTimeout = effect.pipe(
326
- Effect.timeout(Duration.seconds(30))
327
- )
328
- // Effect<A, E | TimeoutException, R>
329
-
330
- // Return Option.none on timeout
331
- const withTimeoutOption = effect.pipe(
332
- Effect.timeoutOption(Duration.seconds(30))
333
- )
334
- // Effect<Option<A>, E, R>
335
-
336
- // Provide fallback on timeout
337
- const withTimeoutFallback = effect.pipe(
338
- Effect.timeoutFail({
339
- duration: Duration.seconds(30),
340
- onTimeout: () => new CustomTimeoutError()
341
- })
342
- )
343
- ```
344
-
345
- ## Error Accumulation
346
-
347
- ### Collect All Errors
348
-
349
- ```typescript
350
- import { Effect } from "effect"
351
-
352
- const validations = [
353
- validateEmail(email),
354
- validatePassword(password),
355
- validateAge(age)
356
- ]
357
-
358
- // Stop at first error (default)
359
- const failFast = Effect.all(validations)
360
-
361
- // Collect all errors
362
- const allErrors = Effect.all(validations, { mode: "validate" })
363
- // Effect<[A, B, C], [E, E, E], R>
364
- ```
365
-
366
- ### Custom Error Accumulation
367
-
368
- ```typescript
369
- import { Effect, Either } from "effect"
370
-
371
- const validateForm = Effect.gen(function* () {
372
- const results = yield* Effect.all([
373
- Effect.either(validateEmail(email)),
374
- Effect.either(validatePassword(password)),
375
- Effect.either(validateAge(age))
376
- ])
377
-
378
- const errors = results
379
- .filter(Either.isLeft)
380
- .map(r => r.left)
381
-
382
- if (errors.length > 0) {
383
- return yield* Effect.fail(new ValidationErrors(errors))
384
- }
385
-
386
- return {
387
- email: (results[0] as Either.Right<string>).right,
388
- password: (results[1] as Either.Right<string>).right,
389
- age: (results[2] as Either.Right<number>).right
390
- }
391
- })
392
- ```
393
-
394
- ## Best Practices
395
-
396
- ### 1. Use Tagged Error Classes
397
-
398
- ```typescript
399
- // Good - tagged for catchTag
400
- class UserNotFound extends Error {
401
- readonly _tag = "UserNotFound" as const
402
- constructor(readonly userId: string) {
403
- super(`User not found: ${userId}`)
404
- }
405
- }
406
-
407
- // Avoid - generic errors
408
- const bad = Effect.fail(new Error("User not found"))
409
- ```
410
-
411
- ### 2. Be Specific with Error Types
412
-
413
- ```typescript
414
- // Good - specific error types
415
- type GetUserError = UserNotFound | DatabaseError | ValidationError
416
-
417
- const getUser = (id: string): Effect.Effect<User, GetUserError> => // ...
418
-
419
- // Avoid - union with string/Error
420
- const bad = (id: string): Effect.Effect<User, string | Error> => // ...
421
- ```
422
-
423
- ### 3. Handle Errors at Appropriate Level
424
-
425
- ```typescript
426
- // Low-level: expose specific errors
427
- const queryDb = (sql: string): Effect.Effect<Row[], DbError> => // ...
428
-
429
- // Mid-level: wrap and add context
430
- const getUser = (id: string): Effect.Effect<User, UserError> =>
431
- queryDb(`SELECT * FROM users WHERE id = '${id}'`).pipe(
432
- Effect.mapError(e => new UserError("Failed to fetch user", e))
433
- )
434
-
435
- // High-level: handle or provide fallback
436
- const getUserOrGuest = (id: string): Effect.Effect<User> =>
437
- getUser(id).pipe(
438
- Effect.catchAll(() => Effect.succeed(guestUser))
439
- )
440
- ```
441
-
442
- ### 4. Use Cause for Debugging
443
-
444
- ```typescript
445
- const program = effect.pipe(
446
- Effect.catchAllCause((cause) => {
447
- // Log full cause for debugging
448
- console.error(Cause.pretty(cause))
449
-
450
- // But expose clean error to caller
451
- return Effect.fail(new ServiceUnavailable())
452
- })
453
- )
454
- ```
455
-
456
- ### 5. Don't Swallow Defects
457
-
458
- ```typescript
459
- // Good - handle expected errors, let defects propagate
460
- const safe = effect.pipe(
461
- Effect.catchAll((error) => Effect.succeed(fallback))
462
- )
463
-
464
- // Careful - this catches defects too
465
- const dangerous = effect.pipe(
466
- Effect.catchAllCause((cause) => Effect.succeed(fallback))
467
- )
468
- ```