@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.
- package/CHANGELOG.md +14 -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,392 +0,0 @@
|
|
|
1
|
-
# Cloudflare Patterns with Drizzle ORM
|
|
2
|
-
|
|
3
|
-
Drizzle ORM patterns for Cloudflare D1 and Durable Objects SQLite storage.
|
|
4
|
-
|
|
5
|
-
## Cloudflare D1
|
|
6
|
-
|
|
7
|
-
D1 is Cloudflare's managed SQLite database service. Use it for shared data accessed by multiple Workers.
|
|
8
|
-
|
|
9
|
-
### D1 Connection
|
|
10
|
-
|
|
11
|
-
```typescript
|
|
12
|
-
import { drizzle } from 'drizzle-orm/d1'
|
|
13
|
-
import * as schema from './schema'
|
|
14
|
-
|
|
15
|
-
export interface Env {
|
|
16
|
-
DB: D1Database
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export default {
|
|
20
|
-
async fetch(request: Request, env: Env) {
|
|
21
|
-
const db = drizzle(env.DB, { schema })
|
|
22
|
-
|
|
23
|
-
// Use db for queries
|
|
24
|
-
const users = await db.query.users.findMany()
|
|
25
|
-
|
|
26
|
-
return Response.json(users)
|
|
27
|
-
},
|
|
28
|
-
}
|
|
29
|
-
```
|
|
30
|
-
|
|
31
|
-
### D1 Binding Configuration
|
|
32
|
-
|
|
33
|
-
```jsonc
|
|
34
|
-
// wrangler.jsonc
|
|
35
|
-
{
|
|
36
|
-
"$schema": "./node_modules/wrangler/config-schema.json",
|
|
37
|
-
"name": "my-worker",
|
|
38
|
-
"main": "./src/index.ts",
|
|
39
|
-
"compatibility_date": "2024-11-01",
|
|
40
|
-
"d1_databases": [
|
|
41
|
-
{
|
|
42
|
-
"binding": "DB",
|
|
43
|
-
"database_name": "my-database",
|
|
44
|
-
"database_id": "xxxx-xxxx-xxxx-xxxx"
|
|
45
|
-
}
|
|
46
|
-
]
|
|
47
|
-
}
|
|
48
|
-
```
|
|
49
|
-
|
|
50
|
-
### D1 Migrations Configuration
|
|
51
|
-
|
|
52
|
-
```typescript
|
|
53
|
-
// drizzle.config.ts
|
|
54
|
-
import { defineConfig } from 'drizzle-kit'
|
|
55
|
-
|
|
56
|
-
export default defineConfig({
|
|
57
|
-
schema: './src/db/schema.ts',
|
|
58
|
-
out: './drizzle',
|
|
59
|
-
dialect: 'sqlite',
|
|
60
|
-
driver: 'd1-http',
|
|
61
|
-
dbCredentials: {
|
|
62
|
-
accountId: process.env.CLOUDFLARE_ACCOUNT_ID!,
|
|
63
|
-
databaseId: process.env.CLOUDFLARE_DATABASE_ID!,
|
|
64
|
-
token: process.env.CLOUDFLARE_D1_TOKEN!,
|
|
65
|
-
},
|
|
66
|
-
})
|
|
67
|
-
```
|
|
68
|
-
|
|
69
|
-
### Running D1 Migrations
|
|
70
|
-
|
|
71
|
-
```bash
|
|
72
|
-
# Generate migration SQL files
|
|
73
|
-
npx drizzle-kit generate
|
|
74
|
-
|
|
75
|
-
# Apply migrations via wrangler
|
|
76
|
-
npx wrangler d1 migrations apply <DATABASE_NAME>
|
|
77
|
-
|
|
78
|
-
# Or apply specific migration
|
|
79
|
-
npx wrangler d1 execute <DATABASE_NAME> --file=./drizzle/0001_migration.sql
|
|
80
|
-
```
|
|
81
|
-
|
|
82
|
-
### Local D1 Development
|
|
83
|
-
|
|
84
|
-
```bash
|
|
85
|
-
# Use --local flag for development
|
|
86
|
-
npx wrangler dev --local
|
|
87
|
-
|
|
88
|
-
# Create local D1 database
|
|
89
|
-
npx wrangler d1 create <DATABASE_NAME> --local
|
|
90
|
-
|
|
91
|
-
# Apply migrations locally
|
|
92
|
-
npx wrangler d1 migrations apply <DATABASE_NAME> --local
|
|
93
|
-
```
|
|
94
|
-
|
|
95
|
-
## Durable Objects SQLite
|
|
96
|
-
|
|
97
|
-
Durable Objects with SQLite provide per-object isolated storage. Each DO instance has its own SQLite database.
|
|
98
|
-
|
|
99
|
-
### DO Connection (from budget app)
|
|
100
|
-
|
|
101
|
-
```typescript
|
|
102
|
-
import { DurableObject } from 'cloudflare:workers'
|
|
103
|
-
import { drizzle } from 'drizzle-orm/durable-sqlite'
|
|
104
|
-
import { migrate } from 'drizzle-orm/durable-sqlite/migrator'
|
|
105
|
-
import * as schema from './schema'
|
|
106
|
-
import migrations from '../drizzle/migrations'
|
|
107
|
-
|
|
108
|
-
export interface Env {
|
|
109
|
-
USER_LEDGER: DurableObjectNamespace<UserLedger>
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
export class UserLedger extends DurableObject {
|
|
113
|
-
private readonly db: ReturnType<typeof drizzle>
|
|
114
|
-
|
|
115
|
-
constructor(ctx: DurableObjectState, env: Env) {
|
|
116
|
-
super(ctx, env)
|
|
117
|
-
|
|
118
|
-
// Initialize Drizzle with DO storage
|
|
119
|
-
this.db = drizzle(ctx.storage, { schema })
|
|
120
|
-
|
|
121
|
-
// Run migrations on first instantiation
|
|
122
|
-
ctx.blockConcurrencyWhile(async () => {
|
|
123
|
-
migrate(this.db, migrations)
|
|
124
|
-
})
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
async fetch(request: Request): Promise<Response> {
|
|
128
|
-
// Use this.db for queries
|
|
129
|
-
const users = await this.db.query.users.findMany()
|
|
130
|
-
return Response.json(users)
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
```
|
|
134
|
-
|
|
135
|
-
### DO Binding Configuration
|
|
136
|
-
|
|
137
|
-
```jsonc
|
|
138
|
-
// wrangler.jsonc
|
|
139
|
-
{
|
|
140
|
-
"$schema": "./node_modules/wrangler/config-schema.json",
|
|
141
|
-
"name": "budget",
|
|
142
|
-
"main": "./workers/app.ts",
|
|
143
|
-
"compatibility_date": "2024-11-01",
|
|
144
|
-
"durable_objects": {
|
|
145
|
-
"bindings": [
|
|
146
|
-
{
|
|
147
|
-
"name": "USER_LEDGER",
|
|
148
|
-
"class_name": "UserLedger"
|
|
149
|
-
}
|
|
150
|
-
]
|
|
151
|
-
},
|
|
152
|
-
"migrations": [
|
|
153
|
-
{
|
|
154
|
-
"tag": "v1",
|
|
155
|
-
"new_sqlite_classes": ["UserLedger"]
|
|
156
|
-
}
|
|
157
|
-
]
|
|
158
|
-
}
|
|
159
|
-
```
|
|
160
|
-
|
|
161
|
-
### DO Migrations Configuration
|
|
162
|
-
|
|
163
|
-
```typescript
|
|
164
|
-
// drizzle.config.ts
|
|
165
|
-
import { defineConfig } from 'drizzle-kit'
|
|
166
|
-
|
|
167
|
-
export default defineConfig({
|
|
168
|
-
schema: './app/repositories/schema.ts',
|
|
169
|
-
out: './drizzle',
|
|
170
|
-
dialect: 'sqlite',
|
|
171
|
-
driver: 'durable-sqlite',
|
|
172
|
-
})
|
|
173
|
-
```
|
|
174
|
-
|
|
175
|
-
### Running DO Migrations
|
|
176
|
-
|
|
177
|
-
Migrations run automatically when the DO is first instantiated:
|
|
178
|
-
|
|
179
|
-
```typescript
|
|
180
|
-
constructor(ctx: DurableObjectState, env: Env) {
|
|
181
|
-
super(ctx, env)
|
|
182
|
-
this.db = drizzle(ctx.storage, { schema })
|
|
183
|
-
|
|
184
|
-
// Block concurrent requests during migration
|
|
185
|
-
ctx.blockConcurrencyWhile(async () => {
|
|
186
|
-
migrate(this.db, migrations)
|
|
187
|
-
})
|
|
188
|
-
}
|
|
189
|
-
```
|
|
190
|
-
|
|
191
|
-
### Importing Migrations
|
|
192
|
-
|
|
193
|
-
```typescript
|
|
194
|
-
// drizzle/migrations.ts (auto-generated by drizzle-kit)
|
|
195
|
-
export default [
|
|
196
|
-
{
|
|
197
|
-
sql: 'CREATE TABLE users ...',
|
|
198
|
-
batchIdx: 0,
|
|
199
|
-
},
|
|
200
|
-
// ... more migrations
|
|
201
|
-
]
|
|
202
|
-
|
|
203
|
-
// Import in DO
|
|
204
|
-
import migrations from '../drizzle/migrations'
|
|
205
|
-
migrate(this.db, migrations)
|
|
206
|
-
```
|
|
207
|
-
|
|
208
|
-
## Testing with Cloudflare Workers
|
|
209
|
-
|
|
210
|
-
### Testing D1
|
|
211
|
-
|
|
212
|
-
Use `better-sqlite3` for local testing (see [sqlite.md](./sqlite.md)):
|
|
213
|
-
|
|
214
|
-
```typescript
|
|
215
|
-
import Database from 'better-sqlite3'
|
|
216
|
-
import { drizzle } from 'drizzle-orm/better-sqlite3'
|
|
217
|
-
import * as schema from './schema'
|
|
218
|
-
|
|
219
|
-
const sqlite = new Database(':memory:')
|
|
220
|
-
const db = drizzle(sqlite, { schema })
|
|
221
|
-
```
|
|
222
|
-
|
|
223
|
-
### Testing Durable Objects (from budget app)
|
|
224
|
-
|
|
225
|
-
Use `@cloudflare/vitest-pool-workers` to run tests inside actual DO instances:
|
|
226
|
-
|
|
227
|
-
```typescript
|
|
228
|
-
// vitest.config.ts
|
|
229
|
-
import { defineWorkersConfig } from '@cloudflare/vitest-pool-workers/config'
|
|
230
|
-
|
|
231
|
-
export default defineWorkersConfig({
|
|
232
|
-
test: {
|
|
233
|
-
pool: '@cloudflare/vitest-pool-workers',
|
|
234
|
-
poolOptions: {
|
|
235
|
-
workers: {
|
|
236
|
-
wrangler: { configPath: './wrangler.jsonc' },
|
|
237
|
-
},
|
|
238
|
-
},
|
|
239
|
-
},
|
|
240
|
-
})
|
|
241
|
-
```
|
|
242
|
-
|
|
243
|
-
```typescript
|
|
244
|
-
// test/database.ts - Test helper
|
|
245
|
-
import { env, runInDurableObject } from 'cloudflare:test'
|
|
246
|
-
import { drizzle } from 'drizzle-orm/durable-sqlite'
|
|
247
|
-
import * as schema from '~/repositories/schema'
|
|
248
|
-
|
|
249
|
-
export async function runWithTestDB(
|
|
250
|
-
fn: (db: ReturnType<typeof drizzle>) => Promise<void>
|
|
251
|
-
): Promise<void> {
|
|
252
|
-
// Create a unique Durable Object instance for this test
|
|
253
|
-
const id = env.USER_LEDGER.newUniqueId()
|
|
254
|
-
const stub = env.USER_LEDGER.get(id)
|
|
255
|
-
|
|
256
|
-
return await runInDurableObject(stub, async (_instance, state) => {
|
|
257
|
-
const db = drizzle(state.storage, { schema })
|
|
258
|
-
return fn(db)
|
|
259
|
-
})
|
|
260
|
-
}
|
|
261
|
-
```
|
|
262
|
-
|
|
263
|
-
```typescript
|
|
264
|
-
// repository.test.ts
|
|
265
|
-
import { test, expect } from 'vitest'
|
|
266
|
-
import { runWithTestDB } from '../test/database'
|
|
267
|
-
import { UserRepository } from './UserRepository'
|
|
268
|
-
|
|
269
|
-
test('create user', async () => {
|
|
270
|
-
await runWithTestDB(async (db) => {
|
|
271
|
-
const repo = new UserRepository(db)
|
|
272
|
-
const user = await repo.create({
|
|
273
|
-
id: crypto.randomUUID(),
|
|
274
|
-
name: 'Alice',
|
|
275
|
-
email: 'alice@example.com',
|
|
276
|
-
})
|
|
277
|
-
|
|
278
|
-
expect(user.name).toBe('Alice')
|
|
279
|
-
})
|
|
280
|
-
})
|
|
281
|
-
```
|
|
282
|
-
|
|
283
|
-
### Key Testing Differences
|
|
284
|
-
|
|
285
|
-
| Approach | D1 | Durable Objects |
|
|
286
|
-
|----------|----|-----------------|
|
|
287
|
-
| Test Driver | `better-sqlite3` (in-memory) | `@cloudflare/vitest-pool-workers` |
|
|
288
|
-
| Test Environment | Node.js process | Miniflare DO instance |
|
|
289
|
-
| Migration | Manual `migrate()` call | Automatic via DO constructor |
|
|
290
|
-
| Isolation | Per-test database | Per-test DO instance |
|
|
291
|
-
|
|
292
|
-
## When to Use D1 vs Durable Objects
|
|
293
|
-
|
|
294
|
-
### Use D1 When:
|
|
295
|
-
|
|
296
|
-
- **Shared database** - Multiple Workers access the same data
|
|
297
|
-
- **Traditional patterns** - Standard REST API, CRUD operations
|
|
298
|
-
- **External tooling** - Need HTTP API access for migrations/admin
|
|
299
|
-
- **Familiar architecture** - Workers query database over network
|
|
300
|
-
- **Simple deployment** - Don't want to manage per-user isolation
|
|
301
|
-
|
|
302
|
-
### Use Durable Objects SQLite When:
|
|
303
|
-
|
|
304
|
-
- **Per-user isolation** - Each user has their own database
|
|
305
|
-
- **Colocated compute+storage** - Business logic runs next to data
|
|
306
|
-
- **Strong consistency** - Single-threaded execution guarantees
|
|
307
|
-
- **Real-time coordination** - WebSocket connections, presence, sessions
|
|
308
|
-
- **Stateful actors** - Per-entity business logic (e.g., game rooms, chat)
|
|
309
|
-
|
|
310
|
-
### Architecture Comparison
|
|
311
|
-
|
|
312
|
-
**D1 Architecture:**
|
|
313
|
-
```
|
|
314
|
-
Worker → (network) → D1 Database
|
|
315
|
-
Worker → (network) → D1 Database
|
|
316
|
-
Worker → (network) → D1 Database
|
|
317
|
-
```
|
|
318
|
-
|
|
319
|
-
**Durable Objects Architecture:**
|
|
320
|
-
```
|
|
321
|
-
Worker → DO Instance (compute + SQLite)
|
|
322
|
-
Worker → DO Instance (compute + SQLite)
|
|
323
|
-
Worker → DO Instance (compute + SQLite)
|
|
324
|
-
```
|
|
325
|
-
|
|
326
|
-
### Performance Considerations
|
|
327
|
-
|
|
328
|
-
- **D1**: Network latency between Worker and database
|
|
329
|
-
- Use Smart Placement to colocate Worker near D1
|
|
330
|
-
- Good for read-heavy workloads
|
|
331
|
-
|
|
332
|
-
- **DO**: Zero network latency (storage is local)
|
|
333
|
-
- Single-threaded (one request at a time per DO)
|
|
334
|
-
- Good for write-heavy, per-user workloads
|
|
335
|
-
|
|
336
|
-
### Pricing (as of 2024)
|
|
337
|
-
|
|
338
|
-
**D1:**
|
|
339
|
-
- Shared database across all requests
|
|
340
|
-
- Billed per row read/written
|
|
341
|
-
- Free tier: 5M row reads, 100K row writes/day
|
|
342
|
-
|
|
343
|
-
**Durable Objects SQLite:**
|
|
344
|
-
- Per-DO instance billing
|
|
345
|
-
- Billed per SQL query + storage
|
|
346
|
-
- Free tier: SQLite-backed DO only (no KV-backed)
|
|
347
|
-
|
|
348
|
-
## Migration Strategy
|
|
349
|
-
|
|
350
|
-
If starting with D1 and need to migrate to DO SQLite:
|
|
351
|
-
|
|
352
|
-
1. Schema is the same (both use `sqlite-core`)
|
|
353
|
-
2. Change `drizzle-orm/d1` → `drizzle-orm/durable-sqlite`
|
|
354
|
-
3. Change connection from `env.DB` → `ctx.storage`
|
|
355
|
-
4. Add DO routing logic in Worker
|
|
356
|
-
5. Migrate data (custom script needed)
|
|
357
|
-
|
|
358
|
-
```typescript
|
|
359
|
-
// Before (D1)
|
|
360
|
-
const db = drizzle(env.DB, { schema })
|
|
361
|
-
|
|
362
|
-
// After (DO)
|
|
363
|
-
const db = drizzle(ctx.storage, { schema })
|
|
364
|
-
```
|
|
365
|
-
|
|
366
|
-
## Best Practices
|
|
367
|
-
|
|
368
|
-
### D1
|
|
369
|
-
|
|
370
|
-
- Use connection pooling (handled by Workers runtime)
|
|
371
|
-
- Batch queries when possible
|
|
372
|
-
- Index frequently queried columns
|
|
373
|
-
- Use prepared statements (automatic with Drizzle)
|
|
374
|
-
- Monitor query performance via Cloudflare dashboard
|
|
375
|
-
|
|
376
|
-
### Durable Objects
|
|
377
|
-
|
|
378
|
-
- Keep DO instances lightweight (single responsibility)
|
|
379
|
-
- Use `blockConcurrencyWhile()` for initialization
|
|
380
|
-
- Run migrations on first instantiation
|
|
381
|
-
- Handle alarms for periodic cleanup
|
|
382
|
-
- Design for single-threaded execution
|
|
383
|
-
- Avoid long-running operations in constructor
|
|
384
|
-
|
|
385
|
-
### Both
|
|
386
|
-
|
|
387
|
-
- Use transactions for multi-step operations
|
|
388
|
-
- Define foreign keys for data integrity
|
|
389
|
-
- Create indexes for query performance
|
|
390
|
-
- Use type inference (`$inferSelect`, `$inferInsert`)
|
|
391
|
-
- Follow repository pattern for data access
|
|
392
|
-
- Test with actual CF runtime (`vitest-pool-workers` or `better-sqlite3`)
|