@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 +455 -0
- package/docs/database.md +815 -0
- package/docs/plugins.md +121 -0
- package/docs/testing.md +430 -0
- package/package.json +2 -1
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
|
+
```
|