@donkeylabs/server 0.1.0 → 0.1.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/cli/commands/init.ts +201 -12
- package/package.json +1 -1
package/cli/commands/init.ts
CHANGED
|
@@ -4,9 +4,10 @@
|
|
|
4
4
|
* Initialize a new @donkeylabs/server project with proper database setup.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import { mkdir, writeFile, readFile, readdir } from "node:fs/promises";
|
|
8
|
-
import { join, resolve } from "node:path";
|
|
7
|
+
import { mkdir, writeFile, readFile, readdir, copyFile } from "node:fs/promises";
|
|
8
|
+
import { join, resolve, dirname } from "node:path";
|
|
9
9
|
import { existsSync } from "node:fs";
|
|
10
|
+
import { fileURLToPath } from "node:url";
|
|
10
11
|
import pc from "picocolors";
|
|
11
12
|
import prompts from "prompts";
|
|
12
13
|
|
|
@@ -96,6 +97,14 @@ export async function initCommand(args: string[]): Promise<void> {
|
|
|
96
97
|
);
|
|
97
98
|
console.log(pc.green(" Created:"), "src/index.ts");
|
|
98
99
|
|
|
100
|
+
// Copy docs and create CLAUDE.md for AI assistants
|
|
101
|
+
await copyDocsToProject(targetDir);
|
|
102
|
+
await writeFile(
|
|
103
|
+
join(targetDir, "CLAUDE.md"),
|
|
104
|
+
generateClaudeMd(options.projectName)
|
|
105
|
+
);
|
|
106
|
+
console.log(pc.green(" Created:"), "CLAUDE.md + docs/");
|
|
107
|
+
|
|
99
108
|
// Update .gitignore
|
|
100
109
|
const gitignorePath = join(targetDir, ".gitignore");
|
|
101
110
|
const gitignoreContent = existsSync(gitignorePath)
|
|
@@ -130,11 +139,12 @@ export async function initCommand(args: string[]): Promise<void> {
|
|
|
130
139
|
|
|
131
140
|
// Update package.json
|
|
132
141
|
const pkgPath = join(targetDir, "package.json");
|
|
133
|
-
let pkg: any = { name: options.projectName, type: "module", scripts: {} };
|
|
142
|
+
let pkg: any = { name: options.projectName, type: "module", scripts: {}, dependencies: {} };
|
|
134
143
|
|
|
135
144
|
if (existsSync(pkgPath)) {
|
|
136
145
|
pkg = JSON.parse(await readFile(pkgPath, "utf-8"));
|
|
137
146
|
pkg.scripts = pkg.scripts || {};
|
|
147
|
+
pkg.dependencies = pkg.dependencies || {};
|
|
138
148
|
}
|
|
139
149
|
|
|
140
150
|
pkg.name = pkg.name || options.projectName;
|
|
@@ -143,8 +153,20 @@ export async function initCommand(args: string[]): Promise<void> {
|
|
|
143
153
|
pkg.scripts.start = "bun src/index.ts";
|
|
144
154
|
pkg.scripts["gen:types"] = "donkeylabs generate";
|
|
145
155
|
|
|
156
|
+
// Add dependencies
|
|
157
|
+
pkg.dependencies["@donkeylabs/server"] = "latest";
|
|
158
|
+
pkg.dependencies["kysely"] = "^0.27.0";
|
|
159
|
+
pkg.dependencies["zod"] = "^3.24.0";
|
|
160
|
+
if (options.useDatabase) {
|
|
161
|
+
pkg.dependencies["kysely-bun-sqlite"] = "^0.3.0";
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// Add dev dependencies
|
|
165
|
+
pkg.devDependencies = pkg.devDependencies || {};
|
|
166
|
+
pkg.devDependencies["@types/bun"] = "latest";
|
|
167
|
+
|
|
146
168
|
await writeFile(pkgPath, JSON.stringify(pkg, null, 2) + "\n");
|
|
147
|
-
console.log(pc.green("
|
|
169
|
+
console.log(pc.green(" Created:"), "package.json");
|
|
148
170
|
|
|
149
171
|
// Print next steps
|
|
150
172
|
console.log(`
|
|
@@ -152,17 +174,13 @@ ${pc.bold(pc.green("Success!"))} Project initialized.
|
|
|
152
174
|
|
|
153
175
|
${pc.bold("Next steps:")}
|
|
154
176
|
1. Install dependencies:
|
|
155
|
-
${pc.cyan(
|
|
177
|
+
${pc.cyan("bun install")}
|
|
156
178
|
${options.useDatabase ? `
|
|
157
179
|
2. Set up your database:
|
|
158
180
|
${pc.cyan(`cp .env.example .env`)}
|
|
159
|
-
${pc.dim("# Edit .env with your database path")}
|
|
160
181
|
` : ""}
|
|
161
182
|
${options.useDatabase ? "3" : "2"}. Start development:
|
|
162
183
|
${pc.cyan("bun run dev")}
|
|
163
|
-
|
|
164
|
-
${options.useDatabase ? "4" : "3"}. Generate types after adding plugins:
|
|
165
|
-
${pc.cyan("bun run gen:types")}
|
|
166
184
|
`);
|
|
167
185
|
}
|
|
168
186
|
|
|
@@ -281,7 +299,178 @@ function generateTsConfig(): string {
|
|
|
281
299
|
`;
|
|
282
300
|
}
|
|
283
301
|
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
302
|
+
// Docs to copy to user projects (subset relevant for users)
|
|
303
|
+
const USER_DOCS = [
|
|
304
|
+
"plugins.md",
|
|
305
|
+
"router.md",
|
|
306
|
+
"handlers.md",
|
|
307
|
+
"core-services.md",
|
|
308
|
+
"errors.md",
|
|
309
|
+
"cache.md",
|
|
310
|
+
"logger.md",
|
|
311
|
+
"events.md",
|
|
312
|
+
"jobs.md",
|
|
313
|
+
"cron.md",
|
|
314
|
+
"sse.md",
|
|
315
|
+
"rate-limiter.md",
|
|
316
|
+
"middleware.md",
|
|
317
|
+
"api-client.md",
|
|
318
|
+
"svelte-frontend.md",
|
|
319
|
+
];
|
|
320
|
+
|
|
321
|
+
async function copyDocsToProject(targetDir: string): Promise<void> {
|
|
322
|
+
const docsDir = join(targetDir, "docs");
|
|
323
|
+
await mkdir(docsDir, { recursive: true });
|
|
324
|
+
|
|
325
|
+
// Find the package's docs directory
|
|
326
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
327
|
+
const __dirname = dirname(__filename);
|
|
328
|
+
const packageDocsDir = resolve(__dirname, "../../docs");
|
|
329
|
+
|
|
330
|
+
for (const doc of USER_DOCS) {
|
|
331
|
+
const srcPath = join(packageDocsDir, doc);
|
|
332
|
+
const destPath = join(docsDir, doc);
|
|
333
|
+
|
|
334
|
+
if (existsSync(srcPath)) {
|
|
335
|
+
await copyFile(srcPath, destPath);
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
function generateClaudeMd(projectName: string): string {
|
|
341
|
+
return `# ${projectName}
|
|
342
|
+
|
|
343
|
+
Built with @donkeylabs/server - a type-safe RPC framework for Bun.
|
|
344
|
+
|
|
345
|
+
## Project Structure
|
|
346
|
+
|
|
347
|
+
\`\`\`
|
|
348
|
+
src/
|
|
349
|
+
├── index.ts # Server entry + routes (start here)
|
|
350
|
+
├── db.ts # Database setup
|
|
351
|
+
└── plugins/ # Your plugins
|
|
352
|
+
└── example/
|
|
353
|
+
├── index.ts # Plugin definition
|
|
354
|
+
├── schema.ts # DB types (if needed)
|
|
355
|
+
└── migrations/ # SQL migrations
|
|
356
|
+
docs/ # Framework documentation
|
|
357
|
+
\`\`\`
|
|
358
|
+
|
|
359
|
+
## Quick Start
|
|
360
|
+
|
|
361
|
+
See \`src/index.ts\` for a working example with routes.
|
|
362
|
+
|
|
363
|
+
## Plugins
|
|
364
|
+
|
|
365
|
+
Plugins encapsulate business logic with optional database schemas.
|
|
366
|
+
|
|
367
|
+
\`\`\`ts
|
|
368
|
+
import { createPlugin } from "@donkeylabs/server";
|
|
369
|
+
|
|
370
|
+
export const notesPlugin = createPlugin.define({
|
|
371
|
+
name: "notes",
|
|
372
|
+
service: async (ctx) => ({
|
|
373
|
+
async create(title: string) {
|
|
374
|
+
return ctx.db.insertInto("notes").values({ title }).execute();
|
|
375
|
+
},
|
|
376
|
+
}),
|
|
377
|
+
});
|
|
378
|
+
\`\`\`
|
|
379
|
+
|
|
380
|
+
→ See **docs/plugins.md** for schemas, migrations, and dependencies.
|
|
381
|
+
|
|
382
|
+
## Routes
|
|
383
|
+
|
|
384
|
+
Routes handle HTTP requests and call plugin services.
|
|
385
|
+
|
|
386
|
+
\`\`\`ts
|
|
387
|
+
const router = createRouter("notes")
|
|
388
|
+
.route("create").typed({
|
|
389
|
+
input: z.object({ title: z.string() }),
|
|
390
|
+
handle: async (input, ctx) => ctx.plugins.notes.create(input.title),
|
|
391
|
+
});
|
|
392
|
+
\`\`\`
|
|
393
|
+
|
|
394
|
+
→ See **docs/router.md** for typed handlers, raw handlers, and middleware.
|
|
395
|
+
|
|
396
|
+
## Handlers
|
|
397
|
+
|
|
398
|
+
Two types: \`typed\` (validated JSON) and \`raw\` (full Request/Response).
|
|
399
|
+
|
|
400
|
+
→ See **docs/handlers.md** for input/output validation and error handling.
|
|
401
|
+
|
|
402
|
+
## Errors
|
|
403
|
+
|
|
404
|
+
Use built-in error factories for proper HTTP responses.
|
|
405
|
+
|
|
406
|
+
\`\`\`ts
|
|
407
|
+
throw ctx.errors.NotFound("User not found");
|
|
408
|
+
throw ctx.errors.BadRequest("Invalid email");
|
|
409
|
+
\`\`\`
|
|
410
|
+
|
|
411
|
+
→ See **docs/errors.md** for all error types and custom errors.
|
|
412
|
+
|
|
413
|
+
## Core Services
|
|
414
|
+
|
|
415
|
+
Available via \`ctx.core\`. **Only use what you need.**
|
|
416
|
+
|
|
417
|
+
| Service | Purpose | Docs |
|
|
418
|
+
|---------|---------|------|
|
|
419
|
+
| \`logger\` | Structured logging | docs/logger.md |
|
|
420
|
+
| \`cache\` | In-memory key-value cache | docs/cache.md |
|
|
421
|
+
| \`events\` | Pub/sub between plugins | docs/events.md |
|
|
422
|
+
| \`jobs\` | Background job queue | docs/jobs.md |
|
|
423
|
+
| \`cron\` | Scheduled tasks | docs/cron.md |
|
|
424
|
+
| \`sse\` | Server-sent events | docs/sse.md |
|
|
425
|
+
| \`rateLimiter\` | Request rate limiting | docs/rate-limiter.md |
|
|
426
|
+
|
|
427
|
+
→ See **docs/core-services.md** for overview.
|
|
428
|
+
|
|
429
|
+
## Middleware
|
|
430
|
+
|
|
431
|
+
Add authentication, logging, or other cross-cutting concerns.
|
|
432
|
+
|
|
433
|
+
→ See **docs/middleware.md** for usage patterns.
|
|
434
|
+
|
|
435
|
+
## API Client
|
|
436
|
+
|
|
437
|
+
Generate a typed client for your frontend:
|
|
438
|
+
|
|
439
|
+
\`\`\`sh
|
|
440
|
+
bun run gen:client --output ./frontend/src/lib/api
|
|
441
|
+
\`\`\`
|
|
442
|
+
|
|
443
|
+
→ See **docs/api-client.md** for client configuration and usage.
|
|
444
|
+
|
|
445
|
+
## Svelte 5 Frontend
|
|
446
|
+
|
|
447
|
+
Build type-safe frontends with Svelte 5 and SvelteKit.
|
|
448
|
+
|
|
449
|
+
\`\`\`svelte
|
|
450
|
+
<script lang="ts">
|
|
451
|
+
import { api } from "$lib/api";
|
|
452
|
+
let items = $state<Item[]>([]);
|
|
453
|
+
|
|
454
|
+
$effect(() => {
|
|
455
|
+
api.items.list({}).then((r) => items = r.items);
|
|
456
|
+
});
|
|
457
|
+
</script>
|
|
458
|
+
\`\`\`
|
|
459
|
+
|
|
460
|
+
→ See **docs/svelte-frontend.md** for patterns and SSE integration.
|
|
461
|
+
|
|
462
|
+
## CLI Commands
|
|
463
|
+
|
|
464
|
+
\`\`\`sh
|
|
465
|
+
bun run dev # Start with hot reload
|
|
466
|
+
bun run gen:types # Generate types after adding plugins
|
|
467
|
+
\`\`\`
|
|
468
|
+
|
|
469
|
+
## Guidelines
|
|
470
|
+
|
|
471
|
+
- **Keep it simple** - don't add services you don't need
|
|
472
|
+
- **One concern per plugin** - auth, notes, billing as separate plugins
|
|
473
|
+
- **Minimal logging** - log errors and key events, not every call
|
|
474
|
+
- **Read the docs** - check docs/*.md before implementing something complex
|
|
475
|
+
`;
|
|
287
476
|
}
|