@martinffx/atelier 2.0.0 → 3.0.1
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.
- package/CHANGELOG.md +21 -0
- package/README.md +94 -253
- package/package.json +1 -1
- package/skills/{spec-orchestrator → atelier-orchestrator}/SKILL.md +3 -3
- package/skills/atelier-setup/SKILL.md +80 -0
- package/skills/code-review/references/reviewers.md +3 -3
- package/skills/code-review/references/rq.md +6 -6
- package/skills/spec-brainstorm/SKILL.md +5 -5
- package/skills/spec-implement/SKILL.md +2 -3
- package/skills/spec-plan/SKILL.md +21 -29
- package/skills/spec-plan/references/plan_template.md +77 -0
- package/skills/python-architecture/SKILL.md +0 -148
- package/skills/python-architecture/references/data-modeling.md +0 -558
- package/skills/python-architecture/references/ddd.md +0 -569
- package/skills/python-architecture/references/functional-core.md +0 -337
- package/skills/python-build-tools/SKILL.md +0 -153
- package/skills/python-build-tools/references/basedpyright.md +0 -321
- package/skills/python-build-tools/references/ruff.md +0 -225
- package/skills/python-build-tools/references/uv.md +0 -257
- package/skills/python-fastapi/SKILL.md +0 -184
- package/skills/python-fastapi/references/api-design.md +0 -563
- package/skills/python-fastapi/references/dependencies.md +0 -479
- package/skills/python-fastapi/references/middleware.md +0 -475
- package/skills/python-fastapi/references/validation.md +0 -497
- package/skills/python-modern-python/SKILL.md +0 -201
- package/skills/python-modern-python/references/async.md +0 -76
- package/skills/python-modern-python/references/pattern-matching.md +0 -89
- package/skills/python-modern-python/references/typing.md +0 -96
- package/skills/python-monorepo/SKILL.md +0 -146
- package/skills/python-monorepo/references/docker.md +0 -515
- package/skills/python-monorepo/references/namespace-packages.md +0 -426
- package/skills/python-monorepo/references/workspace-config.md +0 -341
- package/skills/python-sqlalchemy/SKILL.md +0 -119
- package/skills/python-sqlalchemy/references/async.md +0 -51
- package/skills/python-sqlalchemy/references/models.md +0 -67
- package/skills/python-sqlalchemy/references/queries.md +0 -57
- package/skills/python-temporal/SKILL.md +0 -128
- package/skills/python-temporal/references/patterns.md +0 -87
- package/skills/python-temporal/references/testing.md +0 -54
- package/skills/python-testing/SKILL.md +0 -161
- package/skills/python-testing/references/boundaries.md +0 -449
- package/skills/python-testing/references/mocking.md +0 -449
- package/skills/python-testing/references/pytest.md +0 -524
- package/skills/typescript-api-design/SKILL.md +0 -396
- package/skills/typescript-api-design/references/error-responses.md +0 -469
- package/skills/typescript-build-tools/SKILL.md +0 -447
- package/skills/typescript-build-tools/references/biome.md +0 -436
- package/skills/typescript-build-tools/references/bun.md +0 -432
- package/skills/typescript-build-tools/references/turborepo.md +0 -426
- package/skills/typescript-build-tools/references/vitest.md +0 -518
- package/skills/typescript-drizzle-orm/SKILL.md +0 -398
- package/skills/typescript-drizzle-orm/references/cloudflare.md +0 -392
- package/skills/typescript-drizzle-orm/references/entity-pattern.md +0 -412
- package/skills/typescript-drizzle-orm/references/postgresql.md +0 -191
- package/skills/typescript-drizzle-orm/references/repository-pattern.md +0 -479
- package/skills/typescript-drizzle-orm/references/sqlite.md +0 -291
- package/skills/typescript-dynamodb-toolbox/SKILL.md +0 -370
- package/skills/typescript-dynamodb-toolbox/references/entity-layer.md +0 -574
- package/skills/typescript-dynamodb-toolbox/references/error-handling.md +0 -495
- package/skills/typescript-dynamodb-toolbox/references/modeling.md +0 -332
- package/skills/typescript-dynamodb-toolbox/references/repository-layer.md +0 -631
- package/skills/typescript-dynamodb-toolbox/references/testing.md +0 -718
- package/skills/typescript-dynamodb-toolbox/references/transactions.md +0 -423
- package/skills/typescript-effect-ts/SKILL.md +0 -341
- package/skills/typescript-effect-ts/references/core.md +0 -412
- package/skills/typescript-effect-ts/references/error-handling.md +0 -468
- package/skills/typescript-effect-ts/references/resources.md +0 -476
- package/skills/typescript-effect-ts/references/schema.md +0 -536
- package/skills/typescript-effect-ts/references/services.md +0 -461
- package/skills/typescript-fastify/SKILL.md +0 -405
- package/skills/typescript-fastify/references/plugins.md +0 -329
- package/skills/typescript-fastify/references/typeid.md +0 -315
- package/skills/typescript-functional-patterns/SKILL.md +0 -402
- package/skills/typescript-functional-patterns/references/adts.md +0 -497
- package/skills/typescript-functional-patterns/references/branded-types.md +0 -542
- package/skills/typescript-functional-patterns/references/migration-guide.md +0 -695
- package/skills/typescript-functional-patterns/references/option-result.md +0 -585
- package/skills/typescript-testing/SKILL.md +0 -353
- package/skills/typescript-testing/references/mocking.md +0 -675
- package/skills/typescript-testing/references/msw.md +0 -852
- package/skills/typescript-testing/references/snapshot-testing.md +0 -774
|
@@ -1,412 +0,0 @@
|
|
|
1
|
-
# Effect Core Concepts
|
|
2
|
-
|
|
3
|
-
The Effect type is the foundation of the Effect-TS library. It represents a computation that can succeed, fail, or require dependencies.
|
|
4
|
-
|
|
5
|
-
## The Effect Type
|
|
6
|
-
|
|
7
|
-
```typescript
|
|
8
|
-
Effect<Success, Error, Requirements>
|
|
9
|
-
```
|
|
10
|
-
|
|
11
|
-
- **Success (A)**: The type of value produced on success
|
|
12
|
-
- **Error (E)**: The type of error that may occur (use `never` if cannot fail)
|
|
13
|
-
- **Requirements (R)**: The context/services required to run (use `never` if none)
|
|
14
|
-
|
|
15
|
-
### Type Examples
|
|
16
|
-
|
|
17
|
-
```typescript
|
|
18
|
-
import { Effect } from "effect"
|
|
19
|
-
|
|
20
|
-
// Succeeds with number, cannot fail, no requirements
|
|
21
|
-
type PureComputation = Effect.Effect<number>
|
|
22
|
-
// Equivalent to: Effect.Effect<number, never, never>
|
|
23
|
-
|
|
24
|
-
// May fail with Error, no requirements
|
|
25
|
-
type FallibleComputation = Effect.Effect<string, Error>
|
|
26
|
-
// Equivalent to: Effect.Effect<string, Error, never>
|
|
27
|
-
|
|
28
|
-
// Requires Database service, may fail with DbError
|
|
29
|
-
type ServiceComputation = Effect.Effect<User, DbError, Database>
|
|
30
|
-
```
|
|
31
|
-
|
|
32
|
-
## Creating Effects
|
|
33
|
-
|
|
34
|
-
### From Values (Synchronous)
|
|
35
|
-
|
|
36
|
-
```typescript
|
|
37
|
-
import { Effect } from "effect"
|
|
38
|
-
|
|
39
|
-
// Always succeeds with given value
|
|
40
|
-
const succeed = Effect.succeed(42)
|
|
41
|
-
// Effect<number, never, never>
|
|
42
|
-
|
|
43
|
-
// Lazy evaluation - thunk called when effect runs
|
|
44
|
-
const sync = Effect.sync(() => {
|
|
45
|
-
console.log("Computing...")
|
|
46
|
-
return Date.now()
|
|
47
|
-
})
|
|
48
|
-
// Effect<number, never, never>
|
|
49
|
-
|
|
50
|
-
// Suspend effect creation (useful for recursion)
|
|
51
|
-
const suspend = Effect.suspend(() => Effect.succeed(42))
|
|
52
|
-
```
|
|
53
|
-
|
|
54
|
-
### From Failures
|
|
55
|
-
|
|
56
|
-
```typescript
|
|
57
|
-
import { Effect } from "effect"
|
|
58
|
-
|
|
59
|
-
// Typed error (recoverable)
|
|
60
|
-
const fail = Effect.fail(new Error("Something went wrong"))
|
|
61
|
-
// Effect<never, Error, never>
|
|
62
|
-
|
|
63
|
-
// Defect (unrecoverable, not in error type)
|
|
64
|
-
const die = Effect.die("Unexpected error")
|
|
65
|
-
// Effect<never, never, never> - defects aren't tracked in E
|
|
66
|
-
|
|
67
|
-
// Die if condition not met
|
|
68
|
-
const dieIf = Effect.dieIf(value === null, "Value was null")
|
|
69
|
-
```
|
|
70
|
-
|
|
71
|
-
### From Try/Catch
|
|
72
|
-
|
|
73
|
-
```typescript
|
|
74
|
-
import { Effect } from "effect"
|
|
75
|
-
|
|
76
|
-
// Wrap throwing code
|
|
77
|
-
const tryEffect = Effect.try({
|
|
78
|
-
try: () => JSON.parse(invalidJson),
|
|
79
|
-
catch: (error) => new ParseError(String(error))
|
|
80
|
-
})
|
|
81
|
-
// Effect<unknown, ParseError, never>
|
|
82
|
-
|
|
83
|
-
// Simpler form (wraps with UnknownException)
|
|
84
|
-
const trySimple = Effect.try(() => JSON.parse(json))
|
|
85
|
-
// Effect<unknown, UnknownException, never>
|
|
86
|
-
```
|
|
87
|
-
|
|
88
|
-
### From Promises
|
|
89
|
-
|
|
90
|
-
```typescript
|
|
91
|
-
import { Effect } from "effect"
|
|
92
|
-
|
|
93
|
-
// From promise with error mapping
|
|
94
|
-
const tryPromise = Effect.tryPromise({
|
|
95
|
-
try: () => fetch("/api/users").then(r => r.json()),
|
|
96
|
-
catch: (error) => new FetchError(String(error))
|
|
97
|
-
})
|
|
98
|
-
// Effect<unknown, FetchError, never>
|
|
99
|
-
|
|
100
|
-
// From promise (wraps rejection with UnknownException)
|
|
101
|
-
const promise = Effect.promise(() => fetch("/api/users"))
|
|
102
|
-
// Effect<Response, UnknownException, never>
|
|
103
|
-
```
|
|
104
|
-
|
|
105
|
-
### From Nullable Values
|
|
106
|
-
|
|
107
|
-
```typescript
|
|
108
|
-
import { Effect } from "effect"
|
|
109
|
-
|
|
110
|
-
// Convert nullable to Effect
|
|
111
|
-
const fromNullable = Effect.fromNullable(value)
|
|
112
|
-
// Effect<T, NoSuchElementException, never>
|
|
113
|
-
|
|
114
|
-
// With custom error
|
|
115
|
-
const fromNullableWithError = Effect.fromNullable(value).pipe(
|
|
116
|
-
Effect.mapError(() => new ValueNotFound())
|
|
117
|
-
)
|
|
118
|
-
```
|
|
119
|
-
|
|
120
|
-
### From Either/Option
|
|
121
|
-
|
|
122
|
-
```typescript
|
|
123
|
-
import { Effect, Either, Option } from "effect"
|
|
124
|
-
|
|
125
|
-
// From Either
|
|
126
|
-
const either: Either.Either<number, string> = Either.right(42)
|
|
127
|
-
const fromEither = Effect.fromEither(either)
|
|
128
|
-
// Effect<number, string, never>
|
|
129
|
-
|
|
130
|
-
// From Option
|
|
131
|
-
const option: Option.Option<number> = Option.some(42)
|
|
132
|
-
const fromOption = Effect.fromOption(option)
|
|
133
|
-
// Effect<number, NoSuchElementException, never>
|
|
134
|
-
```
|
|
135
|
-
|
|
136
|
-
## Running Effects
|
|
137
|
-
|
|
138
|
-
### Synchronous Execution
|
|
139
|
-
|
|
140
|
-
```typescript
|
|
141
|
-
import { Effect } from "effect"
|
|
142
|
-
|
|
143
|
-
const program = Effect.succeed(42)
|
|
144
|
-
|
|
145
|
-
// Run synchronously - throws if async or fails
|
|
146
|
-
const result = Effect.runSync(program) // 42
|
|
147
|
-
|
|
148
|
-
// Run with exit status
|
|
149
|
-
const exit = Effect.runSyncExit(program)
|
|
150
|
-
// Exit<number, never>
|
|
151
|
-
```
|
|
152
|
-
|
|
153
|
-
**Warning**: `runSync` throws if the effect:
|
|
154
|
-
- Contains async operations
|
|
155
|
-
- Fails with an error
|
|
156
|
-
- Requires services not provided
|
|
157
|
-
|
|
158
|
-
### Promise-based Execution
|
|
159
|
-
|
|
160
|
-
```typescript
|
|
161
|
-
import { Effect } from "effect"
|
|
162
|
-
|
|
163
|
-
const program = Effect.succeed(42)
|
|
164
|
-
|
|
165
|
-
// Returns Promise that rejects on failure
|
|
166
|
-
const promise = Effect.runPromise(program)
|
|
167
|
-
// Promise<number>
|
|
168
|
-
|
|
169
|
-
// Returns Promise<Exit> (never rejects)
|
|
170
|
-
const exitPromise = Effect.runPromiseExit(program)
|
|
171
|
-
// Promise<Exit<number, never>>
|
|
172
|
-
```
|
|
173
|
-
|
|
174
|
-
### Fiber-based Execution
|
|
175
|
-
|
|
176
|
-
```typescript
|
|
177
|
-
import { Effect } from "effect"
|
|
178
|
-
|
|
179
|
-
// Fork to run in background
|
|
180
|
-
const fiber = Effect.runFork(program)
|
|
181
|
-
|
|
182
|
-
// Join to await result
|
|
183
|
-
const result = await Effect.runPromise(Fiber.join(fiber))
|
|
184
|
-
|
|
185
|
-
// Interrupt if needed
|
|
186
|
-
await Effect.runPromise(Fiber.interrupt(fiber))
|
|
187
|
-
```
|
|
188
|
-
|
|
189
|
-
## Effect.gen Generators
|
|
190
|
-
|
|
191
|
-
Generators provide async/await-like syntax with full type safety:
|
|
192
|
-
|
|
193
|
-
```typescript
|
|
194
|
-
import { Effect } from "effect"
|
|
195
|
-
|
|
196
|
-
// Basic generator pattern
|
|
197
|
-
const program = Effect.gen(function* () {
|
|
198
|
-
const a = yield* Effect.succeed(1)
|
|
199
|
-
const b = yield* Effect.succeed(2)
|
|
200
|
-
return a + b
|
|
201
|
-
})
|
|
202
|
-
// Effect<number, never, never>
|
|
203
|
-
|
|
204
|
-
// With error handling
|
|
205
|
-
const withErrors = Effect.gen(function* () {
|
|
206
|
-
const user = yield* fetchUser(userId)
|
|
207
|
-
// If fetchUser fails, error propagates automatically
|
|
208
|
-
const posts = yield* fetchPosts(user.id)
|
|
209
|
-
return { user, posts }
|
|
210
|
-
})
|
|
211
|
-
```
|
|
212
|
-
|
|
213
|
-
### Generator vs Pipe Style
|
|
214
|
-
|
|
215
|
-
```typescript
|
|
216
|
-
// Generator style - more readable for sequential operations
|
|
217
|
-
const genStyle = Effect.gen(function* () {
|
|
218
|
-
const user = yield* getUser(id)
|
|
219
|
-
const profile = yield* getProfile(user.id)
|
|
220
|
-
const settings = yield* getSettings(user.id)
|
|
221
|
-
return { user, profile, settings }
|
|
222
|
-
})
|
|
223
|
-
|
|
224
|
-
// Pipe style - useful for transformations
|
|
225
|
-
const pipeStyle = getUser(id).pipe(
|
|
226
|
-
Effect.flatMap(user => getProfile(user.id).pipe(
|
|
227
|
-
Effect.flatMap(profile => getSettings(user.id).pipe(
|
|
228
|
-
Effect.map(settings => ({ user, profile, settings }))
|
|
229
|
-
))
|
|
230
|
-
))
|
|
231
|
-
)
|
|
232
|
-
|
|
233
|
-
// Both produce the same result
|
|
234
|
-
```
|
|
235
|
-
|
|
236
|
-
### Early Return in Generators
|
|
237
|
-
|
|
238
|
-
```typescript
|
|
239
|
-
import { Effect } from "effect"
|
|
240
|
-
|
|
241
|
-
const program = Effect.gen(function* () {
|
|
242
|
-
const user = yield* getUser(id)
|
|
243
|
-
|
|
244
|
-
// Early return with fail
|
|
245
|
-
if (!user.isActive) {
|
|
246
|
-
return yield* Effect.fail(new UserInactive(id))
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
const data = yield* loadUserData(user)
|
|
250
|
-
return data
|
|
251
|
-
})
|
|
252
|
-
```
|
|
253
|
-
|
|
254
|
-
## Pipe and Composition
|
|
255
|
-
|
|
256
|
-
### Basic Pipe
|
|
257
|
-
|
|
258
|
-
```typescript
|
|
259
|
-
import { Effect, pipe } from "effect"
|
|
260
|
-
|
|
261
|
-
// Using .pipe method
|
|
262
|
-
const result = Effect.succeed(1).pipe(
|
|
263
|
-
Effect.map(n => n + 1),
|
|
264
|
-
Effect.map(n => n * 2)
|
|
265
|
-
)
|
|
266
|
-
|
|
267
|
-
// Using pipe function
|
|
268
|
-
const result2 = pipe(
|
|
269
|
-
Effect.succeed(1),
|
|
270
|
-
Effect.map(n => n + 1),
|
|
271
|
-
Effect.map(n => n * 2)
|
|
272
|
-
)
|
|
273
|
-
```
|
|
274
|
-
|
|
275
|
-
### Common Operators
|
|
276
|
-
|
|
277
|
-
```typescript
|
|
278
|
-
import { Effect } from "effect"
|
|
279
|
-
|
|
280
|
-
// Transform success value
|
|
281
|
-
const mapped = effect.pipe(
|
|
282
|
-
Effect.map(value => value.toUpperCase())
|
|
283
|
-
)
|
|
284
|
-
|
|
285
|
-
// Chain effects
|
|
286
|
-
const chained = effect.pipe(
|
|
287
|
-
Effect.flatMap(value => anotherEffect(value))
|
|
288
|
-
)
|
|
289
|
-
|
|
290
|
-
// Transform error
|
|
291
|
-
const mappedError = effect.pipe(
|
|
292
|
-
Effect.mapError(error => new WrappedError(error))
|
|
293
|
-
)
|
|
294
|
-
|
|
295
|
-
// Transform both
|
|
296
|
-
const mapBoth = effect.pipe(
|
|
297
|
-
Effect.mapBoth({
|
|
298
|
-
onSuccess: value => value.toUpperCase(),
|
|
299
|
-
onFailure: error => new WrappedError(error)
|
|
300
|
-
})
|
|
301
|
-
)
|
|
302
|
-
|
|
303
|
-
// Run side effect without changing value
|
|
304
|
-
const tapped = effect.pipe(
|
|
305
|
-
Effect.tap(value => Effect.sync(() => console.log(value)))
|
|
306
|
-
)
|
|
307
|
-
|
|
308
|
-
// Run side effect on error
|
|
309
|
-
const tappedError = effect.pipe(
|
|
310
|
-
Effect.tapError(error => Effect.sync(() => console.error(error)))
|
|
311
|
-
)
|
|
312
|
-
```
|
|
313
|
-
|
|
314
|
-
### Combining Multiple Effects
|
|
315
|
-
|
|
316
|
-
```typescript
|
|
317
|
-
import { Effect } from "effect"
|
|
318
|
-
|
|
319
|
-
const effects = [effect1, effect2, effect3]
|
|
320
|
-
|
|
321
|
-
// Run all, collect results (fails fast on first error)
|
|
322
|
-
const all = Effect.all(effects)
|
|
323
|
-
// Effect<[A1, A2, A3], E, R>
|
|
324
|
-
|
|
325
|
-
// Run all, collect only successes
|
|
326
|
-
const allSuccesses = Effect.allSuccesses(effects)
|
|
327
|
-
// Effect<A[], never, R>
|
|
328
|
-
|
|
329
|
-
// Run first to succeed
|
|
330
|
-
const race = Effect.race(effect1, effect2)
|
|
331
|
-
|
|
332
|
-
// Run both, return tuple
|
|
333
|
-
const zip = Effect.zip(effect1, effect2)
|
|
334
|
-
// Effect<[A1, A2], E1 | E2, R1 | R2>
|
|
335
|
-
|
|
336
|
-
// Run both, combine results
|
|
337
|
-
const zipWith = Effect.zipWith(
|
|
338
|
-
effect1,
|
|
339
|
-
effect2,
|
|
340
|
-
(a, b) => a + b
|
|
341
|
-
)
|
|
342
|
-
```
|
|
343
|
-
|
|
344
|
-
### Concurrency Control
|
|
345
|
-
|
|
346
|
-
```typescript
|
|
347
|
-
import { Effect } from "effect"
|
|
348
|
-
|
|
349
|
-
const tasks = [task1, task2, task3, task4, task5]
|
|
350
|
-
|
|
351
|
-
// Sequential (one at a time)
|
|
352
|
-
const sequential = Effect.all(tasks, { concurrency: 1 })
|
|
353
|
-
|
|
354
|
-
// Parallel (all at once)
|
|
355
|
-
const parallel = Effect.all(tasks, { concurrency: "unbounded" })
|
|
356
|
-
|
|
357
|
-
// Limited parallelism
|
|
358
|
-
const limited = Effect.all(tasks, { concurrency: 3 })
|
|
359
|
-
|
|
360
|
-
// Inherit from context
|
|
361
|
-
const inherited = Effect.all(tasks, { concurrency: "inherit" })
|
|
362
|
-
```
|
|
363
|
-
|
|
364
|
-
## Interop with Async/Await
|
|
365
|
-
|
|
366
|
-
### Using Effect in Async Functions
|
|
367
|
-
|
|
368
|
-
```typescript
|
|
369
|
-
// Call Effect from async function
|
|
370
|
-
async function main() {
|
|
371
|
-
const result = await Effect.runPromise(myEffect)
|
|
372
|
-
console.log(result)
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
// Handle exit for error details
|
|
376
|
-
async function mainWithExit() {
|
|
377
|
-
const exit = await Effect.runPromiseExit(myEffect)
|
|
378
|
-
if (Exit.isSuccess(exit)) {
|
|
379
|
-
console.log("Success:", exit.value)
|
|
380
|
-
} else {
|
|
381
|
-
console.error("Failure:", exit.cause)
|
|
382
|
-
}
|
|
383
|
-
}
|
|
384
|
-
```
|
|
385
|
-
|
|
386
|
-
### Wrapping Async Functions
|
|
387
|
-
|
|
388
|
-
```typescript
|
|
389
|
-
import { Effect } from "effect"
|
|
390
|
-
|
|
391
|
-
// Wrap existing async function
|
|
392
|
-
const fetchUserEffect = (id: string) =>
|
|
393
|
-
Effect.tryPromise({
|
|
394
|
-
try: () => fetchUser(id), // existing async function
|
|
395
|
-
catch: (error) => new FetchError(String(error))
|
|
396
|
-
})
|
|
397
|
-
|
|
398
|
-
// Use in Effect.gen
|
|
399
|
-
const program = Effect.gen(function* () {
|
|
400
|
-
const user = yield* fetchUserEffect("123")
|
|
401
|
-
return user
|
|
402
|
-
})
|
|
403
|
-
```
|
|
404
|
-
|
|
405
|
-
## Best Practices
|
|
406
|
-
|
|
407
|
-
1. **Keep effects lazy** - Don't run effects until necessary
|
|
408
|
-
2. **Use generators for sequential code** - More readable than nested flatMap
|
|
409
|
-
3. **Prefer specific error types** - Use tagged classes over generic Error
|
|
410
|
-
4. **Run at the edge** - Call `runPromise`/`runSync` only at application boundaries
|
|
411
|
-
5. **Compose with pipe** - Build complex effects from simple ones
|
|
412
|
-
6. **Use appropriate concurrency** - Don't default to unbounded parallelism
|