@donkeylabs/server 0.4.2 → 0.4.4

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/CLAUDE.md ADDED
@@ -0,0 +1,455 @@
1
+ ---
2
+ description: Plugin system for Bun with type-safe handlers, core services, and auto-generated registries.
3
+ globs: "*.ts, *.tsx, *.html, *.css, *.js, *.jsx, package.json"
4
+ alwaysApply: false
5
+ ---
6
+
7
+ # @donkeylabs/server
8
+
9
+ A **type-safe plugin system** for building RPC-style APIs with Bun. Features automatic dependency resolution, database schema merging, custom handlers, middleware, and built-in core services.
10
+
11
+ ---
12
+
13
+ ## AI Assistant Instructions
14
+
15
+ **IMPORTANT: Follow these guidelines when working with this codebase.**
16
+
17
+ ### 1. Use MCP Tools First
18
+
19
+ When the `donkeylabs` MCP server is available, **always use MCP tools** instead of writing code manually:
20
+
21
+ | Task | Use MCP Tool |
22
+ |------|--------------|
23
+ | Create a plugin | `create_plugin` |
24
+ | Add a route | `add_route` |
25
+ | Add database migration | `add_migration` |
26
+ | Add service method | `add_service_method` |
27
+ | Generate types | `generate_types` |
28
+
29
+ MCP tools ensure correct file structure, naming conventions, and patterns.
30
+
31
+ ### 2. Read Docs Before Implementing
32
+
33
+ Before implementing any feature, **read the relevant documentation**:
34
+
35
+ | Feature | Read First |
36
+ |---------|------------|
37
+ | Testing | [docs/testing.md](docs/testing.md) - Test harness, unit & integration tests |
38
+ | Database queries | [docs/database.md](docs/database.md) - Use Kysely, NOT raw SQL |
39
+ | Creating plugins | [docs/plugins.md](docs/plugins.md) - Includes plugin vs route decision |
40
+ | Adding routes | [docs/router.md](docs/router.md) |
41
+ | Migrations | [docs/database.md](docs/database.md) - Use Kysely schema builder |
42
+ | Middleware | [docs/middleware.md](docs/middleware.md) |
43
+ | Background jobs | [docs/jobs.md](docs/jobs.md) |
44
+ | Cron tasks | [docs/cron.md](docs/cron.md) |
45
+
46
+ ### 3. Key Patterns to Follow
47
+
48
+ - **Plugins vs Routes**: Plugins = reusable business logic; Routes = API endpoints. See [docs/plugins.md](docs/plugins.md)
49
+ - **Kysely for DB**: Always use Kysely query builder, never raw SQL. See [docs/database.md](docs/database.md)
50
+ - **Migrations**: Use TypeScript migrations with Kysely schema builder (NOT `sql` tagged templates)
51
+ - **Type generation**: Run `donkeylabs generate` after adding plugins/migrations
52
+ - **Thin routes**: Keep route handlers thin; delegate business logic to plugin services
53
+
54
+ ### 4. Write Tests
55
+
56
+ **REQUIRED: Write tests for new functionality.** See [docs/testing.md](docs/testing.md)
57
+
58
+ ```ts
59
+ import { createTestHarness } from "@donkeylabs/server/harness";
60
+ import { myPlugin } from "./plugins/myPlugin";
61
+
62
+ const { manager, db, core } = await createTestHarness(myPlugin);
63
+ const service = manager.getServices().myPlugin;
64
+ ```
65
+
66
+ - **Unit tests**: Test plugin service methods in isolation
67
+ - **Integration tests**: Test plugins working together
68
+ - **Place tests next to code**: `plugins/users/tests/unit.test.ts`
69
+
70
+ ### 5. Verify Before Committing
71
+
72
+ **REQUIRED: Always run these checks before finishing:**
73
+
74
+ ```sh
75
+ # 1. Type check - catch type errors
76
+ bun --bun tsc --noEmit
77
+
78
+ # 2. Run tests - ensure nothing is broken
79
+ bun test
80
+
81
+ # 3. Generate types - if you added plugins/migrations
82
+ donkeylabs generate
83
+ ```
84
+
85
+ **Do NOT skip these steps.** Type errors and failing tests must be fixed before completion.
86
+
87
+ ---
88
+
89
+ ## Bun-First Development
90
+
91
+ Always use Bun instead of Node.js:
92
+
93
+ ```sh
94
+ bun <file> # Instead of node/ts-node
95
+ bun test # Instead of jest/vitest
96
+ bun install # Instead of npm/yarn/pnpm install
97
+ bun run <script> # Instead of npm run
98
+ ```
99
+
100
+ Bun automatically loads `.env` - don't use dotenv.
101
+
102
+ ---
103
+
104
+ ## Package Structure
105
+
106
+ ```
107
+ @donkeylabs/server/
108
+ ├── src/ # Library source code
109
+ │ ├── index.ts # Main exports
110
+ │ ├── core.ts # Plugin system, PluginManager, type helpers
111
+ │ ├── router.ts # Route builder, handler registry
112
+ │ ├── handlers.ts # TypedHandler, RawHandler, createHandler
113
+ │ ├── middleware.ts # Middleware system
114
+ │ ├── server.ts # AppServer, HTTP handling, core services init
115
+ │ ├── harness.ts # Test harness with in-memory DB
116
+ │ ├── client/ # API client base
117
+ │ │ └── base.ts # Client base class
118
+ │ └── core/ # Core services
119
+ │ ├── index.ts # Re-exports all services
120
+ │ ├── logger.ts # Logger service
121
+ │ ├── cache.ts # Cache service
122
+ │ ├── events.ts # Events service
123
+ │ ├── cron.ts # Cron service
124
+ │ ├── jobs.ts # Jobs service
125
+ │ ├── sse.ts # SSE service
126
+ │ ├── rate-limiter.ts # Rate limiter service
127
+ │ └── errors.ts # Error factories
128
+ ├── cli/ # CLI commands
129
+ │ ├── index.ts # CLI entry point (donkeylabs command)
130
+ │ └── commands/
131
+ │ ├── init.ts # Project scaffolding
132
+ │ ├── generate.ts # Type generation
133
+ │ └── plugin.ts # Plugin creation
134
+ ├── templates/ # Templates for init and plugin commands
135
+ │ ├── init/ # New project templates
136
+ │ └── plugin/ # Plugin scaffolding templates
137
+ ├── examples/ # Example projects
138
+ │ └── starter/ # Complete starter template
139
+ │ ├── src/index.ts
140
+ │ ├── src/plugins/ # Example plugins (stats with middleware)
141
+ │ ├── src/routes/ # Example routes with typing
142
+ │ └── donkeylabs.config.ts
143
+ ├── scripts/ # Build and generation scripts
144
+ ├── test/ # Test files
145
+ ├── registry.d.ts # Auto-generated plugin/handler registry
146
+ └── context.d.ts # Auto-generated GlobalContext type
147
+ ```
148
+
149
+ ### Generated Files (DO NOT EDIT)
150
+
151
+ - `registry.d.ts` - Plugin and handler type registry
152
+ - `context.d.ts` - Server context with merged schemas
153
+ - `.@donkeylabs/server/` - Generated types in user projects (gitignored)
154
+
155
+ ---
156
+
157
+ ## User Project Structure
158
+
159
+ After running `donkeylabs init`:
160
+
161
+ ```
162
+ my-project/
163
+ ├── src/
164
+ │ ├── index.ts # Server entry point
165
+ │ └── plugins/ # Your plugins
166
+ │ └── myPlugin/
167
+ │ ├── index.ts # Plugin definition
168
+ │ ├── schema.ts # Generated DB types
169
+ │ └── migrations/ # SQL migrations
170
+ ├── .@donkeylabs/server/ # Generated types (gitignored)
171
+ │ ├── registry.d.ts
172
+ │ └── context.d.ts
173
+ ├── donkeylabs.config.ts # Configuration file
174
+ ├── package.json
175
+ └── tsconfig.json
176
+ ```
177
+
178
+ ---
179
+
180
+ ## Quick Start
181
+
182
+ ### 1. Create a Plugin
183
+
184
+ ```ts
185
+ // src/plugins/myPlugin/index.ts
186
+ import { createPlugin } from "@donkeylabs/server";
187
+
188
+ export const myPlugin = createPlugin.define({
189
+ name: "myPlugin",
190
+ service: async (ctx) => ({
191
+ greet: (name: string) => `Hello, ${name}!`
192
+ })
193
+ });
194
+ ```
195
+
196
+ ### 2. Create Routes
197
+
198
+ ```ts
199
+ // src/index.ts
200
+ import { createRouter } from "@donkeylabs/server";
201
+ import { z } from "zod";
202
+
203
+ const router = createRouter("api")
204
+ .route("greet").typed({
205
+ input: z.object({ name: z.string() }),
206
+ handle: async (input, ctx) => {
207
+ return { message: ctx.plugins.myPlugin.greet(input.name) };
208
+ }
209
+ });
210
+ ```
211
+
212
+ ### 3. Start Server
213
+
214
+ ```ts
215
+ // src/index.ts
216
+ import { AppServer } from "@donkeylabs/server";
217
+ import { myPlugin } from "./plugins/myPlugin";
218
+
219
+ const server = new AppServer({
220
+ db: createDatabase(),
221
+ port: 3000,
222
+ });
223
+
224
+ server.registerPlugin(myPlugin);
225
+ server.use(router);
226
+ await server.start();
227
+ ```
228
+
229
+ ### 4. Make Requests
230
+
231
+ ```sh
232
+ curl -X POST http://localhost:3000/api.greet \
233
+ -H "Content-Type: application/json" \
234
+ -d '{"name": "World"}'
235
+ # {"message": "Hello, World!"}
236
+ ```
237
+
238
+ ---
239
+
240
+ ## CLI Commands
241
+
242
+ ```sh
243
+ donkeylabs # Interactive menu (context-aware)
244
+ donkeylabs init # Create new project
245
+ donkeylabs generate # Generate types from plugins
246
+ donkeylabs plugin create # Interactive plugin creation
247
+ ```
248
+
249
+ ### Interactive Mode
250
+
251
+ Running `donkeylabs` with no arguments launches an interactive menu:
252
+
253
+ **From project root:**
254
+ - Create New Plugin
255
+ - Initialize New Project
256
+ - Generate Types
257
+ - Generate Registry
258
+ - Generate Server Context
259
+
260
+ **From inside a plugin directory (`src/plugins/<name>/`):**
261
+ - Generate Schema Types
262
+ - Create Migration
263
+ - Back to Global Menu
264
+
265
+ ### Development Commands
266
+
267
+ ```sh
268
+ bun run gen:registry # Regenerate registry.d.ts
269
+ bun run gen:server # Regenerate context.d.ts
270
+ bun run cli # Interactive CLI
271
+ bun test # Run all tests
272
+ bun --bun tsc --noEmit # Type check
273
+ ```
274
+
275
+ ---
276
+
277
+ ## Server Context
278
+
279
+ Every route handler receives `ServerContext`:
280
+
281
+ ```ts
282
+ interface ServerContext {
283
+ db: Kysely<MergedSchema>; // Database with all plugin schemas
284
+ plugins: { // All plugin services
285
+ myPlugin: MyPluginService;
286
+ auth: AuthService;
287
+ // ... auto-generated
288
+ };
289
+ core: CoreServices; // Logger, cache, events, etc.
290
+ errors: Errors; // Error factories (BadRequest, NotFound, etc.)
291
+ ip: string; // Client IP address
292
+ requestId: string; // Unique request ID
293
+ user?: any; // Set by auth middleware
294
+ }
295
+ ```
296
+
297
+ ---
298
+
299
+ ## Configuration File
300
+
301
+ ```ts
302
+ // donkeylabs.config.ts
303
+ import { defineConfig } from "@donkeylabs/server";
304
+
305
+ export default defineConfig({
306
+ plugins: ["./src/plugins/**/index.ts"], // Plugin glob patterns
307
+ outDir: ".@donkeylabs/server", // Generated types directory
308
+ client: { // Optional client generation
309
+ output: "./src/client/api.ts",
310
+ },
311
+ });
312
+ ```
313
+
314
+ ---
315
+
316
+ ## Testing
317
+
318
+ ```ts
319
+ import { createTestHarness } from "@donkeylabs/server/harness";
320
+ import { myPlugin } from "./plugins/myPlugin";
321
+
322
+ const { manager, db, core } = await createTestHarness(myPlugin);
323
+
324
+ // Test with real in-memory SQLite + all core services
325
+ const service = manager.getServices().myPlugin;
326
+ expect(service.greet("Test")).toBe("Hello, Test!");
327
+ ```
328
+
329
+ ---
330
+
331
+ ## Package Exports
332
+
333
+ ```ts
334
+ // Main exports
335
+ import { createPlugin, AppServer, createRouter } from "@donkeylabs/server";
336
+
337
+ // Client base class
338
+ import { RpcClient } from "@donkeylabs/server/client";
339
+
340
+ // Test harness
341
+ import { createTestHarness } from "@donkeylabs/server/harness";
342
+ ```
343
+
344
+ ---
345
+
346
+ ## Common Issues
347
+
348
+ ### Handler autocomplete not working
349
+ 1. Run `donkeylabs generate` or `bun run gen:registry`
350
+ 2. Restart TypeScript language server (Cmd+Shift+P > "Restart TS Server")
351
+
352
+ ### Plugin types not recognized
353
+ 1. Ensure `.@donkeylabs/server` is in your tsconfig's `include` array
354
+ 2. Run `donkeylabs generate`
355
+
356
+ ### ctx.plugins shows as `any`
357
+ 1. Make sure `service` comes BEFORE `middleware` in plugin definition
358
+ 2. Run `donkeylabs generate` to regenerate types
359
+ 3. Restart TypeScript language server
360
+
361
+ ### Core services undefined
362
+ 1. Check `ServerConfig` has required `db` property
363
+ 2. Core services are auto-initialized in `AppServer` constructor
364
+
365
+ ---
366
+
367
+ ## Bun APIs
368
+
369
+ Use Bun's built-in APIs instead of npm packages:
370
+
371
+ | Use | Instead of |
372
+ |-----|------------|
373
+ | `Bun.serve()` | express, fastify |
374
+ | `bun:sqlite` | better-sqlite3 |
375
+ | `Bun.redis` | ioredis |
376
+ | `Bun.sql` | pg, postgres.js |
377
+ | `WebSocket` | ws |
378
+ | `Bun.file()` | fs.readFile |
379
+ | `Bun.$\`cmd\`` | execa |
380
+
381
+ See `node_modules/bun-types/docs/**.md` for full API documentation.
382
+
383
+ ---
384
+
385
+ ## Documentation
386
+
387
+ Detailed documentation is available in the `docs/` directory:
388
+
389
+ | Document | Description |
390
+ |----------|-------------|
391
+ | [testing.md](docs/testing.md) | Test harness, unit tests, integration tests, mocking |
392
+ | [database.md](docs/database.md) | Kysely queries, CRUD operations, joins, transactions, migrations |
393
+ | [plugins.md](docs/plugins.md) | Creating plugins, schemas, dependencies, middleware, and init hooks |
394
+ | [router.md](docs/router.md) | Routes, handlers, input/output validation, middleware chains |
395
+ | [middleware.md](docs/middleware.md) | Creating and using middleware with typed configuration |
396
+ | [handlers.md](docs/handlers.md) | Custom handlers (typed, raw, plugin handlers) |
397
+ | [core-services.md](docs/core-services.md) | Overview of all core services |
398
+ | [logger.md](docs/logger.md) | Structured logging with child loggers |
399
+ | [cache.md](docs/cache.md) | In-memory caching with TTL |
400
+ | [events.md](docs/events.md) | Pub/sub event system |
401
+ | [cron.md](docs/cron.md) | Scheduled tasks |
402
+ | [jobs.md](docs/jobs.md) | Background job queue |
403
+ | [sse.md](docs/sse.md) | Server-sent events |
404
+ | [rate-limiter.md](docs/rate-limiter.md) | Request rate limiting |
405
+ | [errors.md](docs/errors.md) | Error factories and custom errors |
406
+ | [api-client.md](docs/api-client.md) | Generated API client usage |
407
+ | [project-structure.md](docs/project-structure.md) | Recommended project organization |
408
+ | [cli.md](docs/cli.md) | CLI commands and interactive mode |
409
+ | [sveltekit-adapter.md](docs/sveltekit-adapter.md) | SvelteKit adapter integration |
410
+
411
+ ---
412
+
413
+ ## MCP Server (AI Integration)
414
+
415
+ An MCP server is available for AI assistants to create and manage plugins following project conventions.
416
+
417
+ ### Available Tools
418
+
419
+ | Tool | Description |
420
+ |------|-------------|
421
+ | `create_plugin` | Create a new plugin with correct structure |
422
+ | `add_route` | Add a route to a router with proper typing |
423
+ | `add_migration` | Create a numbered migration file |
424
+ | `add_service_method` | Add a method to a plugin's service |
425
+ | `generate_types` | Run type generation |
426
+ | `list_plugins` | List all plugins with their methods |
427
+ | `get_project_info` | Get project structure info |
428
+
429
+ ### Configuration
430
+
431
+ Add to your Claude Code MCP settings:
432
+
433
+ ```json
434
+ {
435
+ "mcpServers": {
436
+ "donkeylabs": {
437
+ "command": "bun",
438
+ "args": ["packages/mcp/src/server.ts"]
439
+ }
440
+ }
441
+ }
442
+ ```
443
+
444
+ The MCP server lives in the `packages/mcp/` directory of the monorepo.
445
+
446
+ ### Example Usage
447
+
448
+ AI can call these tools to scaffold code correctly:
449
+
450
+ ```
451
+ Tool: create_plugin
452
+ Args: { "name": "notifications", "hasSchema": true, "dependencies": ["auth"] }
453
+
454
+ Result: Creates src/plugins/notifications/ with index.ts, schema.ts, migrations/
455
+ ```