@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.
Files changed (81) hide show
  1. package/CHANGELOG.md +21 -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,461 +0,0 @@
1
- # Services and Dependency Injection in Effect
2
-
3
- Effect provides a powerful dependency injection system using `Context.Tag` for service definitions and `Layer` for service implementations and composition.
4
-
5
- ## Context.Tag - Defining Services
6
-
7
- A Tag uniquely identifies a service and its interface:
8
-
9
- ```typescript
10
- import { Context, Effect } from "effect"
11
-
12
- // Define service interface and tag together
13
- class Database extends Context.Tag("Database")<
14
- Database,
15
- {
16
- readonly query: (sql: string) => Effect.Effect<unknown[]>
17
- readonly execute: (sql: string) => Effect.Effect<void>
18
- }
19
- >() {}
20
-
21
- // Alternative: separate interface
22
- interface ILogger {
23
- log: (message: string) => Effect.Effect<void>
24
- error: (message: string) => Effect.Effect<void>
25
- }
26
-
27
- class Logger extends Context.Tag("Logger")<Logger, ILogger>() {}
28
- ```
29
-
30
- ### Tag Naming
31
-
32
- Always provide a unique string identifier:
33
-
34
- ```typescript
35
- // Good - unique string key
36
- class UserService extends Context.Tag("UserService")<...>() {}
37
- class OrderService extends Context.Tag("OrderService")<...>() {}
38
-
39
- // Avoid - no identifier (can cause issues with hot reload)
40
- class BadService extends Context.Tag()<...>() {}
41
- ```
42
-
43
- ## Using Services
44
-
45
- ### Access Service in Effect.gen
46
-
47
- ```typescript
48
- import { Effect } from "effect"
49
-
50
- // Yield the tag to get the service
51
- const getUsers = Effect.gen(function* () {
52
- const db = yield* Database
53
- const rows = yield* db.query("SELECT * FROM users")
54
- return rows
55
- })
56
- // Effect<unknown[], never, Database>
57
-
58
- // Multiple services
59
- const logAndQuery = Effect.gen(function* () {
60
- const db = yield* Database
61
- const logger = yield* Logger
62
-
63
- yield* logger.log("Querying users...")
64
- const users = yield* db.query("SELECT * FROM users")
65
- yield* logger.log(`Found ${users.length} users`)
66
-
67
- return users
68
- })
69
- // Effect<unknown[], never, Database | Logger>
70
- ```
71
-
72
- ### Access Service with pipe
73
-
74
- ```typescript
75
- import { Effect } from "effect"
76
-
77
- const getUsers = Database.pipe(
78
- Effect.flatMap(db => db.query("SELECT * FROM users"))
79
- )
80
-
81
- // Or use Effect.serviceWith
82
- const getUsers2 = Effect.serviceWith(Database, db =>
83
- db.query("SELECT * FROM users")
84
- )
85
- ```
86
-
87
- ## Layer - Providing Services
88
-
89
- Layers are recipes for building services with lifecycle management.
90
-
91
- ### Simple Layer (No Dependencies)
92
-
93
- ```typescript
94
- import { Layer } from "effect"
95
-
96
- // Layer.succeed for services with no effects or dependencies
97
- const DatabaseLive = Layer.succeed(Database, {
98
- query: (sql) => Effect.sync(() => {
99
- console.log(`Query: ${sql}`)
100
- return []
101
- }),
102
- execute: (sql) => Effect.sync(() => {
103
- console.log(`Execute: ${sql}`)
104
- })
105
- })
106
- // Layer<Database, never, never>
107
-
108
- const LoggerLive = Layer.succeed(Logger, {
109
- log: (msg) => Effect.sync(() => console.log(msg)),
110
- error: (msg) => Effect.sync(() => console.error(msg))
111
- })
112
- ```
113
-
114
- ### Layer with Effects
115
-
116
- ```typescript
117
- import { Layer, Effect } from "effect"
118
-
119
- // Layer.effect when construction requires effects
120
- const DatabaseLive = Layer.effect(
121
- Database,
122
- Effect.sync(() => {
123
- console.log("Initializing database connection...")
124
- return {
125
- query: (sql) => Effect.sync(() => []),
126
- execute: (sql) => Effect.sync(() => {})
127
- }
128
- })
129
- )
130
- ```
131
-
132
- ### Layer with Dependencies
133
-
134
- ```typescript
135
- import { Layer, Effect } from "effect"
136
-
137
- // Service that depends on another service
138
- class UserRepository extends Context.Tag("UserRepository")<
139
- UserRepository,
140
- {
141
- readonly findById: (id: string) => Effect.Effect<User | null>
142
- readonly save: (user: User) => Effect.Effect<void>
143
- }
144
- >() {}
145
-
146
- // Layer that requires Database
147
- const UserRepositoryLive = Layer.effect(
148
- UserRepository,
149
- Effect.gen(function* () {
150
- const db = yield* Database // Dependency
151
-
152
- return {
153
- findById: (id) => db.query(`SELECT * FROM users WHERE id = '${id}'`).pipe(
154
- Effect.map(rows => rows[0] as User | null)
155
- ),
156
- save: (user) => db.execute(`INSERT INTO users ...`)
157
- }
158
- })
159
- )
160
- // Layer<UserRepository, never, Database>
161
- ```
162
-
163
- ### Layer with Resources
164
-
165
- ```typescript
166
- import { Layer, Effect } from "effect"
167
-
168
- // Layer.scoped for services that need cleanup
169
- const ConnectionPoolLive = Layer.scoped(
170
- ConnectionPool,
171
- Effect.acquireRelease(
172
- // Acquire
173
- Effect.sync(() => {
174
- console.log("Creating connection pool...")
175
- return { getConnection: () => Effect.succeed(conn) }
176
- }),
177
- // Release
178
- (pool) => Effect.sync(() => {
179
- console.log("Closing connection pool...")
180
- })
181
- )
182
- )
183
- ```
184
-
185
- ## Composing Layers
186
-
187
- ### Merge (Combine Independent Layers)
188
-
189
- ```typescript
190
- import { Layer } from "effect"
191
-
192
- // Combine layers that don't depend on each other
193
- const AppLayer = Layer.merge(DatabaseLive, LoggerLive)
194
- // Layer<Database | Logger, never, never>
195
- ```
196
-
197
- ### Provide (Layer Dependencies)
198
-
199
- ```typescript
200
- import { Layer } from "effect"
201
-
202
- // UserRepositoryLive requires Database
203
- // Provide it using Layer.provide
204
-
205
- const UserRepositoryWithDb = UserRepositoryLive.pipe(
206
- Layer.provide(DatabaseLive)
207
- )
208
- // Layer<UserRepository, never, never>
209
-
210
- // Build full app layer
211
- const AppLayer = Layer.merge(
212
- Layer.merge(DatabaseLive, LoggerLive),
213
- UserRepositoryWithDb
214
- )
215
- ```
216
-
217
- ### Sequential Composition
218
-
219
- ```typescript
220
- import { Layer } from "effect"
221
-
222
- // provideMerge: provide dependencies and include them in output
223
- const AppLayer = UserRepositoryLive.pipe(
224
- Layer.provideMerge(DatabaseLive)
225
- )
226
- // Layer<UserRepository | Database, never, never>
227
- ```
228
-
229
- ## Running Effects with Layers
230
-
231
- ### Provide Layer to Effect
232
-
233
- ```typescript
234
- import { Effect, Layer } from "effect"
235
-
236
- const program = Effect.gen(function* () {
237
- const db = yield* Database
238
- const logger = yield* Logger
239
- yield* logger.log("Starting...")
240
- return yield* db.query("SELECT 1")
241
- })
242
-
243
- const AppLayer = Layer.merge(DatabaseLive, LoggerLive)
244
-
245
- // Provide layer and run
246
- const runnable = Effect.provide(program, AppLayer)
247
- await Effect.runPromise(runnable)
248
- ```
249
-
250
- ### Provide Multiple Layers
251
-
252
- ```typescript
253
- const program = Effect.gen(function* () {
254
- const userRepo = yield* UserRepository
255
- const logger = yield* Logger
256
- // ...
257
- })
258
-
259
- // Provide layers
260
- const runnable = program.pipe(
261
- Effect.provide(UserRepositoryLive),
262
- Effect.provide(DatabaseLive),
263
- Effect.provide(LoggerLive)
264
- )
265
- ```
266
-
267
- ## Testing with Layers
268
-
269
- ### Test Implementations
270
-
271
- ```typescript
272
- import { Layer } from "effect"
273
-
274
- // Test implementation
275
- const DatabaseTest = Layer.succeed(Database, {
276
- query: (sql) => Effect.succeed([
277
- { id: "1", name: "Test User" }
278
- ]),
279
- execute: (sql) => Effect.succeed(undefined)
280
- })
281
-
282
- // Use in tests
283
- const TestLayer = Layer.merge(DatabaseTest, LoggerLive)
284
-
285
- test("should get users", async () => {
286
- const result = await Effect.runPromise(
287
- getUsers.pipe(Effect.provide(TestLayer))
288
- )
289
- expect(result).toHaveLength(1)
290
- })
291
- ```
292
-
293
- ### Partial Mocks
294
-
295
- ```typescript
296
- import { Layer } from "effect"
297
-
298
- // Override specific methods
299
- const DatabasePartialMock = Layer.succeed(Database, {
300
- ...realDatabase, // spread real implementation
301
- query: (sql) => Effect.succeed([mockData]) // override specific method
302
- })
303
- ```
304
-
305
- ## Service Patterns
306
-
307
- ### Service with Configuration
308
-
309
- ```typescript
310
- import { Context, Effect, Layer, Config } from "effect"
311
-
312
- interface DatabaseConfig {
313
- host: string
314
- port: number
315
- database: string
316
- }
317
-
318
- class DatabaseConfigService extends Context.Tag("DatabaseConfig")<
319
- DatabaseConfigService,
320
- DatabaseConfig
321
- >() {}
322
-
323
- // Layer that reads from config
324
- const DatabaseConfigLive = Layer.effect(
325
- DatabaseConfigService,
326
- Effect.gen(function* () {
327
- const host = yield* Config.string("DB_HOST")
328
- const port = yield* Config.number("DB_PORT")
329
- const database = yield* Config.string("DB_NAME")
330
- return { host, port, database }
331
- })
332
- )
333
- ```
334
-
335
- ### Factory Services
336
-
337
- ```typescript
338
- import { Context, Effect } from "effect"
339
-
340
- // Service that creates other things
341
- class UserFactory extends Context.Tag("UserFactory")<
342
- UserFactory,
343
- {
344
- readonly create: (data: CreateUserInput) => Effect.Effect<User, ValidationError>
345
- }
346
- >() {}
347
-
348
- const UserFactoryLive = Layer.succeed(UserFactory, {
349
- create: (data) => Effect.gen(function* () {
350
- // Validate
351
- if (!data.email.includes("@")) {
352
- return yield* Effect.fail(new ValidationError("Invalid email"))
353
- }
354
- // Create
355
- return {
356
- id: crypto.randomUUID(),
357
- ...data,
358
- createdAt: new Date()
359
- }
360
- })
361
- })
362
- ```
363
-
364
- ### Service Composition
365
-
366
- ```typescript
367
- import { Context, Effect, Layer } from "effect"
368
-
369
- // High-level service composed from others
370
- class UserService extends Context.Tag("UserService")<
371
- UserService,
372
- {
373
- readonly register: (data: RegisterInput) => Effect.Effect<User, RegisterError>
374
- readonly getProfile: (id: string) => Effect.Effect<UserProfile, NotFoundError>
375
- }
376
- >() {}
377
-
378
- const UserServiceLive = Layer.effect(
379
- UserService,
380
- Effect.gen(function* () {
381
- const userRepo = yield* UserRepository
382
- const emailService = yield* EmailService
383
- const logger = yield* Logger
384
-
385
- return {
386
- register: (data) => Effect.gen(function* () {
387
- yield* logger.log(`Registering user: ${data.email}`)
388
- const user = yield* userRepo.save({
389
- id: crypto.randomUUID(),
390
- ...data
391
- })
392
- yield* emailService.sendWelcome(user.email)
393
- return user
394
- }),
395
- getProfile: (id) => userRepo.findById(id).pipe(
396
- Effect.flatMap(user =>
397
- user ? Effect.succeed(toProfile(user)) : Effect.fail(new NotFoundError(id))
398
- )
399
- )
400
- }
401
- })
402
- )
403
- // Layer<UserService, never, UserRepository | EmailService | Logger>
404
- ```
405
-
406
- ## Layer Lifecycle
407
-
408
- ### Scoped Layers with Finalization
409
-
410
- ```typescript
411
- import { Layer, Effect, Scope } from "effect"
412
-
413
- const DatabasePoolLive = Layer.scoped(
414
- DatabasePool,
415
- Effect.acquireRelease(
416
- // Acquire: runs when layer is built
417
- Effect.sync(() => {
418
- console.log("Creating pool with 10 connections")
419
- return createPool({ size: 10 })
420
- }),
421
- // Release: runs when scope closes
422
- (pool) => Effect.sync(() => {
423
- console.log("Closing all connections")
424
- pool.close()
425
- })
426
- )
427
- )
428
-
429
- // Resources are cleaned up when program exits
430
- const program = Effect.scoped(
431
- Effect.gen(function* () {
432
- const pool = yield* DatabasePool
433
- // Use pool...
434
- })
435
- )
436
- ```
437
-
438
- ### Layer Memoization
439
-
440
- ```typescript
441
- import { Layer } from "effect"
442
-
443
- // Layers are memoized by default - built once, shared
444
- const AppLayer = Layer.merge(
445
- DatabaseLive,
446
- Layer.merge(UserRepoLive, OrderRepoLive) // Both use same Database
447
- )
448
-
449
- // To disable memoization:
450
- const FreshLayer = Layer.fresh(DatabaseLive)
451
- ```
452
-
453
- ## Best Practices
454
-
455
- 1. **Use string identifiers for tags** - Prevents issues with module reloading
456
- 2. **Keep service interfaces small** - Single responsibility per service
457
- 3. **Use Layer.scoped for resources** - Ensures proper cleanup
458
- 4. **Test with mock layers** - Easy to substitute implementations
459
- 5. **Build layers bottom-up** - Satisfy dependencies before composing
460
- 6. **Separate live and test layers** - Keep production/test implementations distinct
461
- 7. **Use Config for external configuration** - Don't hardcode in layers