@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,497 +0,0 @@
1
- # Algebraic Data Types (ADTs) in TypeScript
2
-
3
- Algebraic Data Types enable you to model complex domains precisely by composing types algebraically. TypeScript's union and intersection types provide the foundation for ADTs, allowing you to make illegal states unrepresentable.
4
-
5
- ## What Are ADTs?
6
-
7
- ADTs come in two flavors:
8
-
9
- 1. **Sum Types (OR)** - A value is one of several variants (tagged unions, discriminated unions)
10
- 2. **Product Types (AND)** - A value contains all fields simultaneously (objects, tuples)
11
-
12
- TypeScript supports both through union types (`|`) and object/tuple types.
13
-
14
- ## Sum Types (Tagged Unions)
15
-
16
- A sum type represents "one of several possible variants." Each variant is distinct and mutually exclusive.
17
-
18
- ### Basic Pattern
19
-
20
- ```typescript
21
- type Shape =
22
- | { kind: "circle"; radius: number }
23
- | { kind: "rectangle"; width: number; height: number }
24
- | { kind: "triangle"; base: number; height: number }
25
- ```
26
-
27
- The `kind` field is the **discriminant** (also called "tag"). It tells TypeScript which variant you have, enabling type narrowing.
28
-
29
- ### Pattern Matching with Exhaustiveness
30
-
31
- Always use `switch` with `assertNever` to ensure you handle all cases:
32
-
33
- ```typescript
34
- function area(shape: Shape): number {
35
- switch (shape.kind) {
36
- case "circle":
37
- // TypeScript knows: shape.radius exists
38
- return Math.PI * shape.radius ** 2
39
- case "rectangle":
40
- // TypeScript knows: shape.width and shape.height exist
41
- return shape.width * shape.height
42
- case "triangle":
43
- // TypeScript knows: shape.base and shape.height exist
44
- return (shape.base * shape.height) / 2
45
- default:
46
- // Compiler error if you add a new variant without handling it
47
- assertNever(shape)
48
- }
49
- }
50
-
51
- const assertNever = (x: never): never => {
52
- throw new Error(`Unhandled variant: ${JSON.stringify(x)}`)
53
- }
54
- ```
55
-
56
- **Why this matters**: If you add a new `Shape` variant (e.g., `pentagon`), TypeScript will produce a compile error in `area()` until you handle it.
57
-
58
- ### Discriminant Field Conventions
59
-
60
- Choose a consistent discriminant field name across your codebase:
61
-
62
- - `kind` - Common in domain models
63
- - `type` - Common in event systems
64
- - `_tag` - Common in functional programming libraries (fp-ts, effect-ts)
65
-
66
- ```typescript
67
- // Good: Consistent discriminant
68
- type PaymentEvent =
69
- | { kind: "initiated"; amount: Cents }
70
- | { kind: "authorized"; authCode: string }
71
- | { kind: "settled"; ledgerId: string }
72
-
73
- // Avoid: Mixed discriminants
74
- type Bad =
75
- | { kind: "a"; data: string }
76
- | { type: "b"; info: number } // Different field name!
77
- ```
78
-
79
- ## Product Types (Objects and Tuples)
80
-
81
- A product type contains multiple fields simultaneously. TypeScript objects and tuples are product types.
82
-
83
- ### Object Product Type
84
-
85
- ```typescript
86
- type User = {
87
- id: string
88
- email: string
89
- role: "admin" | "user"
90
- createdAt: Date
91
- }
92
-
93
- // User has ALL four fields
94
- const user: User = {
95
- id: "u123",
96
- email: "alice@example.com",
97
- role: "admin",
98
- createdAt: new Date(),
99
- }
100
- ```
101
-
102
- ### Tuple Product Type
103
-
104
- ```typescript
105
- type Coordinate = [number, number] // x and y
106
-
107
- const point: Coordinate = [10, 20]
108
- const [x, y] = point // Destructuring
109
- ```
110
-
111
- ## Combining Sum and Product Types
112
-
113
- Real-world domains often combine both:
114
-
115
- ```typescript
116
- type PaymentMethod =
117
- | { kind: "card"; last4: string; brand: string; expiryMonth: number; expiryYear: number }
118
- | { kind: "ach"; accountNumber: string; routingNumber: string; bankName: string }
119
- | { kind: "wallet"; provider: "apple" | "google"; deviceId: string }
120
- ```
121
-
122
- Each variant (sum type) contains multiple fields (product type).
123
-
124
- ## Real-World Example: Transaction States
125
-
126
- Model a transaction lifecycle as a state machine:
127
-
128
- ```typescript
129
- type Millis = number & { __brand: "Millis" }
130
- type Cents = number & { __brand: "Cents" }
131
-
132
- type TxnState =
133
- | { kind: "pending"; createdAt: Millis }
134
- | { kind: "authorized"; authCode: string; authorizedAt: Millis }
135
- | { kind: "settled"; ledgerId: string; settledAt: Millis }
136
- | { kind: "failed"; reason: FailureReason; failedAt: Millis }
137
- | { kind: "reversed"; originalLedgerId: string; reversedAt: Millis }
138
-
139
- type FailureReason =
140
- | { kind: "insufficient_funds" }
141
- | { kind: "invalid_account" }
142
- | { kind: "network_error"; retryable: boolean }
143
-
144
- function canReverse(state: TxnState): boolean {
145
- switch (state.kind) {
146
- case "pending":
147
- return false
148
- case "authorized":
149
- return false
150
- case "settled":
151
- return true // Only settled transactions can be reversed
152
- case "failed":
153
- return false
154
- case "reversed":
155
- return false // Already reversed
156
- default:
157
- assertNever(state)
158
- }
159
- }
160
-
161
- function isTerminal(state: TxnState): boolean {
162
- switch (state.kind) {
163
- case "pending":
164
- return false
165
- case "authorized":
166
- return false
167
- case "settled":
168
- return true
169
- case "failed":
170
- return true
171
- case "reversed":
172
- return true
173
- default:
174
- assertNever(state)
175
- }
176
- }
177
- ```
178
-
179
- **Benefits:**
180
- - Impossible states (e.g., "pending transaction with ledgerId") cannot be represented
181
- - State transitions are explicit in code
182
- - Adding new states forces you to update all pattern matches
183
-
184
- ## Real-World Example: Payment Methods
185
-
186
- ```typescript
187
- type PaymentMethod =
188
- | { kind: "card"; last4: string; brand: string }
189
- | { kind: "ach"; accountNumber: string; routingNumber: string }
190
- | { kind: "wallet"; provider: "apple" | "google" }
191
-
192
- function formatPaymentMethod(method: PaymentMethod): string {
193
- switch (method.kind) {
194
- case "card":
195
- return `${method.brand} ending in ${method.last4}`
196
- case "ach":
197
- return `Bank account ending in ${method.accountNumber.slice(-4)}`
198
- case "wallet":
199
- return `${method.provider} Pay`
200
- default:
201
- assertNever(method)
202
- }
203
- }
204
-
205
- function isCardPayment(method: PaymentMethod): boolean {
206
- return method.kind === "card"
207
- }
208
- ```
209
-
210
- ## Real-World Example: Call Session States
211
-
212
- Model telephony call session states:
213
-
214
- ```typescript
215
- type CallSession =
216
- | { state: "idle" }
217
- | { state: "ringing"; callerId: string; startedAt: Millis }
218
- | { state: "active"; callerId: string; answeredAt: Millis }
219
- | { state: "hold"; callerId: string; heldAt: Millis }
220
- | { state: "ended"; callerId: string; duration: Millis; endReason: EndReason }
221
-
222
- type EndReason =
223
- | { kind: "hangup"; initiator: "caller" | "callee" }
224
- | { kind: "timeout" }
225
- | { kind: "error"; code: string }
226
-
227
- function canTransferCall(session: CallSession): boolean {
228
- switch (session.state) {
229
- case "idle":
230
- return false
231
- case "ringing":
232
- return false
233
- case "active":
234
- return true // Only active calls can be transferred
235
- case "hold":
236
- return true // Calls on hold can be transferred
237
- case "ended":
238
- return false
239
- default:
240
- assertNever(session)
241
- }
242
- }
243
- ```
244
-
245
- ## Type Narrowing Techniques
246
-
247
- TypeScript provides several ways to narrow sum types:
248
-
249
- ### 1. Switch Statement (Recommended)
250
-
251
- ```typescript
252
- switch (shape.kind) {
253
- case "circle":
254
- return Math.PI * shape.radius ** 2
255
- // ... other cases
256
- }
257
- ```
258
-
259
- ### 2. If-Else Chain
260
-
261
- ```typescript
262
- if (shape.kind === "circle") {
263
- return Math.PI * shape.radius ** 2
264
- } else if (shape.kind === "rectangle") {
265
- return shape.width * shape.height
266
- } else if (shape.kind === "triangle") {
267
- return (shape.base * shape.height) / 2
268
- } else {
269
- assertNever(shape)
270
- }
271
- ```
272
-
273
- ### 3. Early Return Pattern
274
-
275
- ```typescript
276
- function area(shape: Shape): number {
277
- if (shape.kind === "circle") {
278
- return Math.PI * shape.radius ** 2
279
- }
280
- if (shape.kind === "rectangle") {
281
- return shape.width * shape.height
282
- }
283
- if (shape.kind === "triangle") {
284
- return (shape.base * shape.height) / 2
285
- }
286
- assertNever(shape)
287
- }
288
- ```
289
-
290
- ### 4. Type Guards
291
-
292
- ```typescript
293
- function isCircle(shape: Shape): shape is Extract<Shape, { kind: "circle" }> {
294
- return shape.kind === "circle"
295
- }
296
-
297
- if (isCircle(shape)) {
298
- console.log(shape.radius) // Type-safe
299
- }
300
- ```
301
-
302
- ## Nested Sum Types
303
-
304
- Sum types can be nested for complex domains:
305
-
306
- ```typescript
307
- type ApiResponse<T> =
308
- | { status: "success"; data: T }
309
- | { status: "error"; error: ApiError }
310
-
311
- type ApiError =
312
- | { kind: "validation"; fields: Record<string, string[]> }
313
- | { kind: "auth"; message: string }
314
- | { kind: "network"; retryable: boolean }
315
- | { kind: "server"; statusCode: number }
316
-
317
- function handleResponse<T>(response: ApiResponse<T>): void {
318
- switch (response.status) {
319
- case "success":
320
- console.log(response.data)
321
- break
322
- case "error":
323
- switch (response.error.kind) {
324
- case "validation":
325
- console.error("Validation errors:", response.error.fields)
326
- break
327
- case "auth":
328
- console.error("Auth error:", response.error.message)
329
- break
330
- case "network":
331
- if (response.error.retryable) {
332
- console.log("Retrying...")
333
- }
334
- break
335
- case "server":
336
- console.error("Server error:", response.error.statusCode)
337
- break
338
- default:
339
- assertNever(response.error)
340
- }
341
- break
342
- default:
343
- assertNever(response)
344
- }
345
- }
346
- ```
347
-
348
- ## Generic Sum Types
349
-
350
- Use generics to create reusable sum type patterns:
351
-
352
- ```typescript
353
- type RemoteData<T, E> =
354
- | { status: "not-asked" }
355
- | { status: "loading" }
356
- | { status: "success"; data: T }
357
- | { status: "failure"; error: E }
358
-
359
- // Usage
360
- type UserProfile = RemoteData<User, string>
361
-
362
- function renderProfile(profile: UserProfile): string {
363
- switch (profile.status) {
364
- case "not-asked":
365
- return "Click to load"
366
- case "loading":
367
- return "Loading..."
368
- case "success":
369
- return `Welcome, ${profile.data.email}`
370
- case "failure":
371
- return `Error: ${profile.error}`
372
- default:
373
- assertNever(profile)
374
- }
375
- }
376
- ```
377
-
378
- ## Common Patterns
379
-
380
- ### Boolean Elimination
381
-
382
- Replace boolean flags with explicit states:
383
-
384
- ```typescript
385
- // Bad: Boolean soup
386
- type Order = {
387
- id: string
388
- isPaid: boolean
389
- isShipped: boolean
390
- isCancelled: boolean
391
- }
392
- // What does { isPaid: true, isCancelled: true } mean?
393
-
394
- // Good: Explicit states
395
- type OrderStatus =
396
- | { kind: "pending" }
397
- | { kind: "paid"; paidAt: Date }
398
- | { kind: "shipped"; trackingNumber: string; shippedAt: Date }
399
- | { kind: "delivered"; deliveredAt: Date }
400
- | { kind: "cancelled"; reason: string; cancelledAt: Date }
401
-
402
- type Order = {
403
- id: string
404
- status: OrderStatus
405
- }
406
- ```
407
-
408
- ### State Transitions
409
-
410
- Encode valid state transitions in types:
411
-
412
- ```typescript
413
- type TxnState =
414
- | { kind: "pending" }
415
- | { kind: "settled"; ledgerId: string }
416
- | { kind: "failed"; reason: string }
417
-
418
- function settle(state: TxnState, ledgerId: string): TxnState {
419
- if (state.kind !== "pending") {
420
- throw new Error("Can only settle pending transactions")
421
- }
422
- return { kind: "settled", ledgerId }
423
- }
424
-
425
- function fail(state: TxnState, reason: string): TxnState {
426
- if (state.kind !== "pending") {
427
- throw new Error("Can only fail pending transactions")
428
- }
429
- return { kind: "failed", reason }
430
- }
431
- ```
432
-
433
- ## Best Practices
434
-
435
- 1. **Use consistent discriminant names** (`kind`, `type`, or `_tag`)
436
- 2. **Always include `assertNever` in default case** for exhaustiveness
437
- 3. **Prefer sum types over boolean flags** for state modeling
438
- 4. **Make illegal states unrepresentable** through type design
439
- 5. **Use nested sum types** for complex error hierarchies
440
- 6. **Document state transitions** in comments or type definitions
441
- 7. **Leverage generics** for reusable patterns (RemoteData, Result, etc.)
442
-
443
- ## Testing ADTs
444
-
445
- Test each variant and all edge cases:
446
-
447
- ```typescript
448
- describe("area", () => {
449
- it("calculates circle area", () => {
450
- const circle: Shape = { kind: "circle", radius: 10 }
451
- expect(area(circle)).toBeCloseTo(314.159)
452
- })
453
-
454
- it("calculates rectangle area", () => {
455
- const rect: Shape = { kind: "rectangle", width: 5, height: 10 }
456
- expect(area(rect)).toBe(50)
457
- })
458
-
459
- it("calculates triangle area", () => {
460
- const triangle: Shape = { kind: "triangle", base: 10, height: 5 }
461
- expect(area(triangle)).toBe(25)
462
- })
463
- })
464
- ```
465
-
466
- ## Migration from Booleans
467
-
468
- Before:
469
- ```typescript
470
- type User = {
471
- id: string
472
- isActive: boolean
473
- isBanned: boolean
474
- isEmailVerified: boolean
475
- }
476
- // What does { isActive: true, isBanned: true } mean?
477
- ```
478
-
479
- After:
480
- ```typescript
481
- type UserStatus =
482
- | { kind: "pending-verification"; email: string }
483
- | { kind: "active" }
484
- | { kind: "banned"; reason: string; bannedAt: Date }
485
- | { kind: "deactivated"; deactivatedAt: Date }
486
-
487
- type User = {
488
- id: string
489
- status: UserStatus
490
- }
491
- ```
492
-
493
- ## Further Reading
494
-
495
- - [Option & Result](./option-result.md) - Type-safe error handling patterns
496
- - [Branded Types](./branded-types.md) - Smart constructors and nominal typing
497
- - [Migration Guide](./migration-guide.md) - Step-by-step adoption playbook