@donkeylabs/cli 0.1.1 → 0.4.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.
Files changed (47) hide show
  1. package/package.json +2 -2
  2. package/src/client/base.ts +481 -0
  3. package/src/commands/generate.ts +242 -41
  4. package/templates/starter/src/index.ts +19 -30
  5. package/templates/starter/src/routes/health/handlers/ping.ts +22 -0
  6. package/templates/starter/src/routes/health/index.ts +16 -2
  7. package/templates/sveltekit-app/bun.lock +4 -4
  8. package/templates/sveltekit-app/donkeylabs.config.ts +1 -0
  9. package/templates/sveltekit-app/package.json +3 -3
  10. package/templates/sveltekit-app/src/lib/api.ts +230 -56
  11. package/templates/sveltekit-app/src/routes/+page.server.ts +2 -2
  12. package/templates/sveltekit-app/src/routes/+page.svelte +235 -96
  13. package/templates/sveltekit-app/src/server/index.ts +25 -117
  14. package/templates/sveltekit-app/src/server/routes/cache/handlers/delete.ts +15 -0
  15. package/templates/sveltekit-app/src/server/routes/cache/handlers/get.ts +15 -0
  16. package/templates/sveltekit-app/src/server/routes/cache/handlers/keys.ts +15 -0
  17. package/templates/sveltekit-app/src/server/routes/cache/handlers/set.ts +15 -0
  18. package/templates/sveltekit-app/src/server/routes/cache/index.ts +46 -0
  19. package/templates/sveltekit-app/src/server/routes/counter/handlers/decrement.ts +17 -0
  20. package/templates/sveltekit-app/src/server/routes/counter/handlers/get.ts +17 -0
  21. package/templates/sveltekit-app/src/server/routes/counter/handlers/increment.ts +17 -0
  22. package/templates/sveltekit-app/src/server/routes/counter/handlers/reset.ts +17 -0
  23. package/templates/sveltekit-app/src/server/routes/counter/index.ts +39 -0
  24. package/templates/sveltekit-app/src/server/routes/cron/handlers/list.ts +17 -0
  25. package/templates/sveltekit-app/src/server/routes/cron/index.ts +24 -0
  26. package/templates/sveltekit-app/src/server/routes/events/handlers/emit.ts +15 -0
  27. package/templates/sveltekit-app/src/server/routes/events/index.ts +19 -0
  28. package/templates/sveltekit-app/src/server/routes/index.ts +8 -0
  29. package/templates/sveltekit-app/src/server/routes/jobs/handlers/enqueue.ts +15 -0
  30. package/templates/sveltekit-app/src/server/routes/jobs/handlers/stats.ts +15 -0
  31. package/templates/sveltekit-app/src/server/routes/jobs/index.ts +28 -0
  32. package/templates/sveltekit-app/src/server/routes/ratelimit/handlers/check.ts +15 -0
  33. package/templates/sveltekit-app/src/server/routes/ratelimit/handlers/reset.ts +15 -0
  34. package/templates/sveltekit-app/src/server/routes/ratelimit/index.ts +29 -0
  35. package/templates/sveltekit-app/src/server/routes/sse/handlers/broadcast.ts +15 -0
  36. package/templates/sveltekit-app/src/server/routes/sse/handlers/clients.ts +15 -0
  37. package/templates/sveltekit-app/src/server/routes/sse/index.ts +28 -0
  38. package/templates/sveltekit-app/{svelte.config.js → svelte.config.ts} +3 -2
  39. package/templates/starter/CLAUDE.md +0 -144
  40. package/templates/starter/src/client.test.ts +0 -7
  41. package/templates/starter/src/db.ts +0 -9
  42. package/templates/starter/src/routes/health/ping/index.ts +0 -13
  43. package/templates/starter/src/routes/health/ping/models/model.ts +0 -23
  44. package/templates/starter/src/routes/health/ping/schema.ts +0 -14
  45. package/templates/starter/src/routes/health/ping/tests/integ.test.ts +0 -20
  46. package/templates/starter/src/routes/health/ping/tests/unit.test.ts +0 -21
  47. package/templates/starter/src/test-ctx.ts +0 -24
@@ -1,144 +0,0 @@
1
- # {{PROJECT_NAME}}
2
-
3
- Built with @donkeylabs/server - a type-safe RPC framework for Bun.
4
-
5
- ## Project Structure
6
-
7
- ```
8
- src/
9
- ├── index.ts # Server entry point
10
- ├── db.ts # Database setup
11
- ├── routes/ # Route handlers
12
- │ └── health/
13
- │ ├── index.ts # Route definitions
14
- │ └── ping/
15
- │ ├── handler.ts
16
- │ ├── models/
17
- │ │ └── model.ts # Input/output schemas
18
- │ └── tests/
19
- │ ├── unit.test.ts
20
- │ └── integ.test.ts
21
- └── plugins/ # Business logic plugins
22
- └── example/
23
- ├── index.ts # Plugin definition
24
- ├── schema.ts # DB types
25
- └── migrations/ # SQL migrations
26
- docs/ # Framework documentation
27
- ```
28
-
29
- ## Quick Start
30
-
31
- See `src/routes/health/ping/` for a complete example with handler, model, and tests.
32
-
33
- ## Plugins
34
-
35
- Plugins encapsulate business logic with optional database schemas.
36
-
37
- ```ts
38
- import { createPlugin } from "@donkeylabs/server";
39
-
40
- export const notesPlugin = createPlugin.define({
41
- name: "notes",
42
- service: async (ctx) => ({
43
- async create(title: string) {
44
- return ctx.db.insertInto("notes").values({ title }).execute();
45
- },
46
- }),
47
- });
48
- ```
49
-
50
- → See **docs/plugins.md** for schemas, migrations, and dependencies.
51
-
52
- ## Routes
53
-
54
- Routes are organized by feature in `src/routes/`. Each route has:
55
- - `handler.ts` - Handler logic
56
- - `models/model.ts` - Zod schemas for input/output
57
- - `tests/unit.test.ts` - Unit tests
58
- - `tests/integ.test.ts` - Integration tests
59
-
60
- ```ts
61
- // routes/notes/index.ts
62
- import { createRouter } from "@donkeylabs/server";
63
- import { createHandler } from "./create/handler";
64
-
65
- export const notesRouter = createRouter("notes")
66
- .route("create").typed(createHandler);
67
- ```
68
-
69
- → See **docs/router.md** and **src/routes/health/** for examples.
70
-
71
- ## Errors
72
-
73
- Use built-in error factories for proper HTTP responses.
74
-
75
- ```ts
76
- throw ctx.errors.NotFound("User not found");
77
- throw ctx.errors.BadRequest("Invalid email");
78
- ```
79
-
80
- → See **docs/errors.md** for all error types and custom errors.
81
-
82
- ## Core Services
83
-
84
- Available via `ctx.core`. **Only use what you need.**
85
-
86
- | Service | Purpose | Docs |
87
- |---------|---------|------|
88
- | `logger` | Structured logging | docs/logger.md |
89
- | `cache` | In-memory key-value cache | docs/cache.md |
90
- | `events` | Pub/sub between plugins | docs/events.md |
91
- | `jobs` | Background job queue | docs/jobs.md |
92
- | `cron` | Scheduled tasks | docs/cron.md |
93
- | `sse` | Server-sent events | docs/sse.md |
94
- | `rateLimiter` | Request rate limiting | docs/rate-limiter.md |
95
-
96
- → See **docs/core-services.md** for overview.
97
-
98
- ## Middleware
99
-
100
- Add authentication, logging, or other cross-cutting concerns.
101
-
102
- → See **docs/middleware.md** for usage patterns.
103
-
104
- ## API Client
105
-
106
- Generate a typed client for your frontend:
107
-
108
- ```sh
109
- bun run gen:client --output ./frontend/src/lib/api
110
- ```
111
-
112
- → See **docs/api-client.md** for client configuration and usage.
113
-
114
- ## Svelte 5 Frontend
115
-
116
- Build type-safe frontends with Svelte 5 and SvelteKit.
117
-
118
- ```svelte
119
- <script lang="ts">
120
- import { api } from "$lib/api";
121
- let items = $state<Item[]>([]);
122
-
123
- $effect(() => {
124
- api.items.list({}).then((r) => items = r.items);
125
- });
126
- </script>
127
- ```
128
-
129
- → See **docs/svelte-frontend.md** for patterns and SSE integration.
130
-
131
- ## CLI Commands
132
-
133
- ```sh
134
- bun run dev # Start with hot reload
135
- bun run test # Run tests
136
- bun run gen:types # Generate types after adding plugins
137
- ```
138
-
139
- ## Guidelines
140
-
141
- - **Keep it simple** - don't add services you don't need
142
- - **One concern per plugin** - auth, notes, billing as separate plugins
143
- - **Minimal logging** - log errors and key events, not every call
144
- - **Read the docs** - check docs/*.md before implementing something complex
@@ -1,7 +0,0 @@
1
- import { createApiClient } from "$server/client"
2
-
3
-
4
- const client = createApiClient("http://localhost:3000", {})
5
-
6
- client.api.health.ping({}).then(console.log)
7
-
@@ -1,9 +0,0 @@
1
- import { Kysely } from "kysely";
2
- import { BunSqliteDialect } from "kysely-bun-sqlite";
3
- import { Database } from "bun:sqlite";
4
-
5
- export const db = new Kysely<any>({
6
- dialect: new BunSqliteDialect({
7
- database: new Database("app.db"),
8
- }),
9
- });
@@ -1,13 +0,0 @@
1
- // Route definition with full type inference
2
- import { createRoute } from "@donkeylabs/server";
3
- import { Input, Output } from "./schema";
4
- import { PingModel } from "./models/model";
5
-
6
- export const pingRoute = createRoute.typed({
7
- input: Input,
8
- output: Output,
9
- handle: async (input, ctx) => {
10
- const model = new PingModel(ctx);
11
- return model.handle(input);
12
- },
13
- });
@@ -1,23 +0,0 @@
1
- // After running `bun run gen:types`, use typed Handler:
2
- import { Handler } from "@donkeylabs/server";
3
- import type { Health } from "$server/routes";
4
- import { AppContext } from "$server/context";
5
-
6
- /**
7
- * Model class with handler logic.
8
- */
9
- export class PingModel implements Handler<Health.Ping> {
10
- ctx: AppContext;
11
-
12
- constructor(ctx: AppContext) {
13
- this.ctx = ctx;
14
- }
15
-
16
- handle(input: Health.Ping.Input): Health.Ping.Output {
17
- return {
18
- status: "ok",
19
- timestamp: new Date().toISOString(),
20
- echo: input.echo,
21
- };
22
- }
23
- }
@@ -1,14 +0,0 @@
1
- import { z } from "zod";
2
-
3
- export const Input = z.object({
4
- echo: z.string().optional(),
5
- });
6
-
7
- export const Output = z.object({
8
- status: z.literal("ok"),
9
- timestamp: z.string(),
10
- echo: z.string().optional(),
11
- });
12
-
13
- export type Input = z.infer<typeof Input>;
14
- export type Output = z.infer<typeof Output>;
@@ -1,20 +0,0 @@
1
- import { describe, it, expect } from "bun:test";
2
- import type { Output } from "../models/model";
3
-
4
- const BASE_URL = "http://localhost:3000";
5
-
6
- describe("health/ping integration", () => {
7
- // Note: Server must be running for integration tests
8
- // Run with: bun run dev & bun test src/routes/health/ping/tests/integ.test.ts
9
-
10
- it("POST /health.ping returns ok", async () => {
11
- const res = await fetch(`${BASE_URL}/health.ping`, {
12
- method: "POST",
13
- headers: { "Content-Type": "application/json" },
14
- body: JSON.stringify({}),
15
- });
16
- expect(res.ok).toBe(true);
17
- const data = (await res.json()) as Output;
18
- expect(data.status).toBe("ok");
19
- });
20
- });
@@ -1,21 +0,0 @@
1
- import { describe, it, expect } from "bun:test";
2
- import { PingModel } from "../models/model";
3
- import { Input } from "../schema";
4
-
5
- describe("health/ping model", () => {
6
- it("returns ok status", () => {
7
- const input = Input.parse({});
8
- const ctx = {} as any;
9
- const model = new PingModel(ctx);
10
- const result = model.handle(input);
11
- expect(result.status).toBe("ok");
12
- });
13
-
14
- it("echoes input", () => {
15
- const input = Input.parse({ echo: "hello" });
16
- const ctx = {} as any;
17
- const model = new PingModel(ctx);
18
- const result = model.handle(input);
19
- expect(result.echo).toBe("hello");
20
- });
21
- });
@@ -1,24 +0,0 @@
1
- /// <reference path="../.@donkeylabs/server/registry.d.ts" />
2
- // Test that ctx.plugins.stats is typed correctly
3
- import type { ServerContext, InferService } from "@donkeylabs/server";
4
- import { statsPlugin } from "./plugins/stats";
5
- import type { StatsService } from "./plugins/stats";
6
-
7
- // Verify InferService works correctly
8
- type InferredService = InferService<typeof statsPlugin>;
9
-
10
- // This type assertion verifies that InferredService equals StatsService
11
- const _typeCheck: InferredService extends StatsService
12
- ? StatsService extends InferredService
13
- ? true
14
- : false
15
- : false = true;
16
-
17
- // Verify ctx.plugins.stats is typed as StatsService
18
- declare const ctx: ServerContext;
19
- const stats: StatsService = ctx.plugins.stats; // Should compile without error
20
-
21
- // These methods should all work
22
- stats.recordRequest("test", 100);
23
- stats.getStats();
24
- stats.reset();