@donkeylabs/cli 0.1.0 → 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 (53) hide show
  1. package/package.json +2 -2
  2. package/src/client/base.ts +481 -0
  3. package/src/commands/generate.ts +262 -53
  4. package/src/index.ts +0 -0
  5. package/templates/starter/package.json +3 -3
  6. package/templates/starter/src/index.ts +19 -30
  7. package/templates/starter/src/routes/health/handlers/ping.ts +22 -0
  8. package/templates/starter/src/routes/health/index.ts +16 -2
  9. package/templates/sveltekit-app/bun.lock +547 -0
  10. package/templates/sveltekit-app/donkeylabs.config.ts +2 -0
  11. package/templates/sveltekit-app/package.json +10 -8
  12. package/templates/sveltekit-app/scripts/watch-server.ts +55 -0
  13. package/templates/sveltekit-app/src/lib/api.ts +195 -81
  14. package/templates/sveltekit-app/src/routes/+page.server.ts +3 -3
  15. package/templates/sveltekit-app/src/routes/+page.svelte +235 -96
  16. package/templates/sveltekit-app/src/server/index.ts +29 -247
  17. package/templates/sveltekit-app/src/server/plugins/demo/index.ts +144 -0
  18. package/templates/sveltekit-app/src/server/routes/cache/handlers/delete.ts +15 -0
  19. package/templates/sveltekit-app/src/server/routes/cache/handlers/get.ts +15 -0
  20. package/templates/sveltekit-app/src/server/routes/cache/handlers/keys.ts +15 -0
  21. package/templates/sveltekit-app/src/server/routes/cache/handlers/set.ts +15 -0
  22. package/templates/sveltekit-app/src/server/routes/cache/index.ts +46 -0
  23. package/templates/sveltekit-app/src/server/routes/counter/handlers/decrement.ts +17 -0
  24. package/templates/sveltekit-app/src/server/routes/counter/handlers/get.ts +17 -0
  25. package/templates/sveltekit-app/src/server/routes/counter/handlers/increment.ts +17 -0
  26. package/templates/sveltekit-app/src/server/routes/counter/handlers/reset.ts +17 -0
  27. package/templates/sveltekit-app/src/server/routes/counter/index.ts +39 -0
  28. package/templates/sveltekit-app/src/server/routes/cron/handlers/list.ts +17 -0
  29. package/templates/sveltekit-app/src/server/routes/cron/index.ts +24 -0
  30. package/templates/sveltekit-app/src/server/routes/events/handlers/emit.ts +15 -0
  31. package/templates/sveltekit-app/src/server/routes/events/index.ts +19 -0
  32. package/templates/sveltekit-app/src/server/routes/index.ts +8 -0
  33. package/templates/sveltekit-app/src/server/routes/jobs/handlers/enqueue.ts +15 -0
  34. package/templates/sveltekit-app/src/server/routes/jobs/handlers/stats.ts +15 -0
  35. package/templates/sveltekit-app/src/server/routes/jobs/index.ts +28 -0
  36. package/templates/sveltekit-app/src/server/routes/ratelimit/handlers/check.ts +15 -0
  37. package/templates/sveltekit-app/src/server/routes/ratelimit/handlers/reset.ts +15 -0
  38. package/templates/sveltekit-app/src/server/routes/ratelimit/index.ts +29 -0
  39. package/templates/sveltekit-app/src/server/routes/sse/handlers/broadcast.ts +15 -0
  40. package/templates/sveltekit-app/src/server/routes/sse/handlers/clients.ts +15 -0
  41. package/templates/sveltekit-app/src/server/routes/sse/index.ts +28 -0
  42. package/templates/sveltekit-app/{svelte.config.js → svelte.config.ts} +4 -5
  43. package/templates/sveltekit-app/tsconfig.json +4 -9
  44. package/templates/sveltekit-app/vite.config.ts +2 -1
  45. package/templates/starter/CLAUDE.md +0 -144
  46. package/templates/starter/src/client.test.ts +0 -7
  47. package/templates/starter/src/db.ts +0 -9
  48. package/templates/starter/src/routes/health/ping/index.ts +0 -13
  49. package/templates/starter/src/routes/health/ping/models/model.ts +0 -23
  50. package/templates/starter/src/routes/health/ping/schema.ts +0 -14
  51. package/templates/starter/src/routes/health/ping/tests/integ.test.ts +0 -20
  52. package/templates/starter/src/routes/health/ping/tests/unit.test.ts +0 -21
  53. package/templates/starter/src/test-ctx.ts +0 -24
@@ -1,263 +1,45 @@
1
1
  // Server entry for @donkeylabs/adapter-sveltekit
2
- import { AppServer, createPlugin, createRouter } from "@donkeylabs/server";
2
+ import { AppServer } from "@donkeylabs/server";
3
3
  import { Kysely } from "kysely";
4
4
  import { BunSqliteDialect } from "kysely-bun-sqlite";
5
5
  import { Database } from "bun:sqlite";
6
- import { z } from "zod";
6
+ import { demoPlugin } from "./plugins/demo";
7
+
8
+ // Import routes
9
+ import {
10
+ counterRoutes,
11
+ cacheRoutes,
12
+ jobsRoutes,
13
+ cronRoutes,
14
+ ratelimitRoutes,
15
+ eventsRoutes,
16
+ sseRoutes,
17
+ } from "./routes";
7
18
 
8
19
  // Simple in-memory database
9
20
  const db = new Kysely<{}>({
10
21
  dialect: new BunSqliteDialect({ database: new Database(":memory:") }),
11
22
  });
12
23
 
13
- // Random event messages for SSE demo
14
- const eventMessages = [
15
- "User logged in",
16
- "New order placed",
17
- "Payment received",
18
- "Item shipped",
19
- "Review submitted",
20
- "Comment added",
21
- "File uploaded",
22
- "Task completed",
23
- "Alert triggered",
24
- "Sync finished",
25
- ];
26
-
27
- // Demo plugin with all core service integrations
28
- const demoPlugin = createPlugin.define({
29
- name: "demo",
30
- service: async (ctx) => {
31
- let counter = 0;
32
-
33
- return {
34
- // Counter
35
- getCounter: () => counter,
36
- increment: () => ++counter,
37
- decrement: () => --counter,
38
- reset: () => { counter = 0; return counter; },
39
-
40
- // Cache helpers
41
- cacheSet: async (key: string, value: any, ttl?: number) => {
42
- await ctx.core.cache.set(key, value, ttl);
43
- return { success: true };
44
- },
45
- cacheGet: async (key: string) => {
46
- const value = await ctx.core.cache.get(key);
47
- const exists = await ctx.core.cache.has(key);
48
- return { value, exists };
49
- },
50
- cacheDelete: async (key: string) => {
51
- await ctx.core.cache.delete(key);
52
- return { success: true };
53
- },
54
- cacheKeys: async () => {
55
- const keys = await ctx.core.cache.keys();
56
- return { keys, size: keys.length };
57
- },
58
-
59
- // Jobs helpers
60
- enqueueJob: async (name: string, data: any, delay?: number) => {
61
- let jobId: string;
62
- if (delay && delay > 0) {
63
- const runAt = new Date(Date.now() + delay);
64
- jobId = await ctx.core.jobs.schedule(name, data, runAt);
65
- } else {
66
- jobId = await ctx.core.jobs.enqueue(name, data);
67
- }
68
- return { jobId };
69
- },
70
- getJobStats: async () => {
71
- const pending = await ctx.core.jobs.getByName("demo-job", "pending");
72
- const running = await ctx.core.jobs.getByName("demo-job", "running");
73
- const completed = await ctx.core.jobs.getByName("demo-job", "completed");
74
- return {
75
- pending: pending.length,
76
- running: running.length,
77
- completed: completed.length,
78
- };
79
- },
80
-
81
- // Cron helpers
82
- getCronTasks: () => ctx.core.cron.list().map(t => ({
83
- id: t.id,
84
- name: t.name,
85
- expression: t.expression,
86
- enabled: t.enabled,
87
- lastRun: t.lastRun?.toISOString(),
88
- nextRun: t.nextRun?.toISOString(),
89
- })),
90
-
91
- // Rate limiter helpers
92
- checkRateLimit: async (key: string, limit: number, window: number) => {
93
- return ctx.core.rateLimiter.check(key, limit, window);
94
- },
95
- resetRateLimit: async (key: string) => {
96
- await ctx.core.rateLimiter.reset(key);
97
- return { success: true };
98
- },
99
-
100
- // Events helpers (internal pub/sub)
101
- emitEvent: async (event: string, data: any) => {
102
- await ctx.core.events.emit(event, data);
103
- return { success: true };
104
- },
105
-
106
- // SSE broadcast
107
- broadcast: (channel: string, event: string, data: any) => {
108
- ctx.core.sse.broadcast(channel, event, data);
109
- return { success: true };
110
- },
111
- getSSEClients: () => ({
112
- total: ctx.core.sse.getClients().length,
113
- byChannel: ctx.core.sse.getClientsByChannel("events").length,
114
- }),
115
- };
116
- },
117
- init: async (ctx) => {
118
- // Register job handler for demo
119
- ctx.core.jobs.register("demo-job", async (data) => {
120
- ctx.core.logger.info("Demo job executed", { data });
121
- // Broadcast job completion via SSE
122
- ctx.core.sse.broadcast("events", "job-completed", {
123
- id: Date.now(),
124
- message: `Job completed: ${data.message || "No message"}`,
125
- timestamp: new Date().toISOString(),
126
- });
127
- });
128
-
129
- // Schedule cron job to broadcast SSE events every 5 seconds
130
- ctx.core.cron.schedule("*/5 * * * * *", () => {
131
- const message = eventMessages[Math.floor(Math.random() * eventMessages.length)];
132
- ctx.core.sse.broadcast("events", "cron-event", {
133
- id: Date.now(),
134
- message,
135
- timestamp: new Date().toISOString(),
136
- source: "cron",
137
- });
138
- }, { name: "sse-broadcaster" });
139
-
140
- // Listen for internal events and broadcast to SSE
141
- ctx.core.events.on("demo.*", (data) => {
142
- ctx.core.sse.broadcast("events", "internal-event", {
143
- id: Date.now(),
144
- message: `Internal event: ${JSON.stringify(data)}`,
145
- timestamp: new Date().toISOString(),
146
- source: "events",
147
- });
148
- });
149
-
150
- ctx.core.logger.info("Demo plugin initialized with all core services");
151
- },
152
- });
153
-
154
- // Create routes
155
- const api = createRouter("api");
156
-
157
- // Counter routes
158
- api.route("counter.get").typed({
159
- handle: async (_input, ctx) => ({ count: ctx.plugins.demo.getCounter() }),
160
- });
161
-
162
- api.route("counter.increment").typed({
163
- handle: async (_input, ctx) => ({ count: ctx.plugins.demo.increment() }),
164
- });
165
-
166
- api.route("counter.decrement").typed({
167
- handle: async (_input, ctx) => ({ count: ctx.plugins.demo.decrement() }),
168
- });
169
-
170
- api.route("counter.reset").typed({
171
- handle: async (_input, ctx) => ({ count: ctx.plugins.demo.reset() }),
172
- });
173
-
174
- // Cache routes
175
- api.route("cache.set").typed({
176
- input: z.object({
177
- key: z.string(),
178
- value: z.any(),
179
- ttl: z.number().optional()
180
- }),
181
- handle: async (input, ctx) => ctx.plugins.demo.cacheSet(input.key, input.value, input.ttl),
182
- });
183
-
184
- api.route("cache.get").typed({
185
- input: z.object({ key: z.string() }),
186
- handle: async (input, ctx) => ctx.plugins.demo.cacheGet(input.key),
187
- });
188
-
189
- api.route("cache.delete").typed({
190
- input: z.object({ key: z.string() }),
191
- handle: async (input, ctx) => ctx.plugins.demo.cacheDelete(input.key),
192
- });
193
-
194
- api.route("cache.keys").typed({
195
- handle: async (_input, ctx) => ctx.plugins.demo.cacheKeys(),
196
- });
197
-
198
- // Jobs routes
199
- api.route("jobs.enqueue").typed({
200
- input: z.object({
201
- name: z.string().default("demo-job"),
202
- data: z.any().default({}),
203
- delay: z.number().optional()
204
- }),
205
- handle: async (input, ctx) => ctx.plugins.demo.enqueueJob(input.name, input.data, input.delay),
206
- });
207
-
208
- api.route("jobs.stats").typed({
209
- handle: async (_input, ctx) => ctx.plugins.demo.getJobStats(),
210
- });
211
-
212
- // Cron routes
213
- api.route("cron.list").typed({
214
- handle: async (_input, ctx) => ({ tasks: ctx.plugins.demo.getCronTasks() }),
215
- });
216
-
217
- // Rate limiter routes
218
- api.route("ratelimit.check").typed({
219
- input: z.object({
220
- key: z.string().default("demo"),
221
- limit: z.number().default(5),
222
- window: z.number().default(60000)
223
- }),
224
- handle: async (input, ctx) => ctx.plugins.demo.checkRateLimit(input.key, input.limit, input.window),
225
- });
226
-
227
- api.route("ratelimit.reset").typed({
228
- input: z.object({ key: z.string().default("demo") }),
229
- handle: async (input, ctx) => ctx.plugins.demo.resetRateLimit(input.key),
230
- });
231
-
232
- // Events routes (internal pub/sub)
233
- api.route("events.emit").typed({
234
- input: z.object({
235
- event: z.string().default("demo.test"),
236
- data: z.any().default({ test: true })
237
- }),
238
- handle: async (input, ctx) => ctx.plugins.demo.emitEvent(input.event, input.data),
239
- });
240
-
241
- // SSE routes
242
- api.route("sse.broadcast").typed({
243
- input: z.object({
244
- channel: z.string().default("events"),
245
- event: z.string().default("manual"),
246
- data: z.any()
247
- }),
248
- handle: async (input, ctx) => ctx.plugins.demo.broadcast(input.channel, input.event, input.data),
249
- });
250
-
251
- api.route("sse.clients").typed({
252
- handle: async (_input, ctx) => ctx.plugins.demo.getSSEClients(),
253
- });
254
-
255
- // Create server
24
+ // Create server with auto type generation in dev mode
256
25
  export const server = new AppServer({
257
26
  db,
258
27
  port: 0, // Port managed by adapter
28
+ generateTypes: {
29
+ output: "./src/lib/api.ts",
30
+ },
259
31
  });
260
32
 
261
- // Register plugin and routes
33
+ // Register plugin
262
34
  server.registerPlugin(demoPlugin);
263
- server.use(api);
35
+
36
+ // Register all routes
37
+ server.use(counterRoutes);
38
+ server.use(cacheRoutes);
39
+ server.use(jobsRoutes);
40
+ server.use(cronRoutes);
41
+ server.use(ratelimitRoutes);
42
+ server.use(eventsRoutes);
43
+ server.use(sseRoutes);
44
+
45
+
@@ -0,0 +1,144 @@
1
+ // Demo plugin with all core service integrations
2
+ import { createPlugin } from "@donkeylabs/server";
3
+
4
+
5
+ // Random event messages for SSE demo
6
+ const eventMessages = [
7
+ "User logged in",
8
+ "New order placed",
9
+ "Payment received",
10
+ "Item shipped",
11
+ "Review submitted",
12
+ "Comment added",
13
+ "File uploaded",
14
+ "Task completed",
15
+ "Alert triggered",
16
+ "Sync finished",
17
+ ];
18
+
19
+
20
+ export const demoPlugin = createPlugin.define({
21
+ name: "demo",
22
+ service: async (ctx) => {
23
+ let counter = 0;
24
+
25
+ return {
26
+ // Counter
27
+ getCounter: () => counter,
28
+ increment: () => ++counter,
29
+ decrement: () => --counter,
30
+ reset: () => { counter = 0; return counter; },
31
+
32
+ // Cache helpers
33
+ cacheSet: async (key: string, value: any, ttl?: number) => {
34
+ await ctx.core.cache.set(key, value, ttl);
35
+ return { success: true };
36
+ },
37
+ cacheGet: async (key: string) => {
38
+ const value = await ctx.core.cache.get(key);
39
+ const exists = await ctx.core.cache.has(key);
40
+ return { value, exists };
41
+ },
42
+ cacheDelete: async (key: string) => {
43
+ await ctx.core.cache.delete(key);
44
+ return { success: true };
45
+ },
46
+ cacheKeys: async () => {
47
+ const keys = await ctx.core.cache.keys();
48
+ return { keys, size: keys.length };
49
+ },
50
+
51
+ // Jobs helpers
52
+ enqueueJob: async (name: string, data: any, delay?: number) => {
53
+ let jobId: string;
54
+ if (delay && delay > 0) {
55
+ const runAt = new Date(Date.now() + delay);
56
+ jobId = await ctx.core.jobs.schedule(name, data, runAt);
57
+ } else {
58
+ jobId = await ctx.core.jobs.enqueue(name, data);
59
+ }
60
+ return { jobId };
61
+ },
62
+ getJobStats: async () => {
63
+ const pending = await ctx.core.jobs.getByName("demo-job", "pending");
64
+ const running = await ctx.core.jobs.getByName("demo-job", "running");
65
+ const completed = await ctx.core.jobs.getByName("demo-job", "completed");
66
+ return {
67
+ pending: pending.length,
68
+ running: running.length,
69
+ completed: completed.length,
70
+ };
71
+ },
72
+
73
+ // Cron helpers
74
+ getCronTasks: () => ctx.core.cron.list().map(t => ({
75
+ id: t.id,
76
+ name: t.name,
77
+ expression: t.expression,
78
+ enabled: t.enabled,
79
+ lastRun: t.lastRun?.toISOString(),
80
+ nextRun: t.nextRun?.toISOString(),
81
+ })),
82
+
83
+ // Rate limiter helpers
84
+ checkRateLimit: async (key: string, limit: number, window: number) => {
85
+ return ctx.core.rateLimiter.check(key, limit, window);
86
+ },
87
+ resetRateLimit: async (key: string) => {
88
+ await ctx.core.rateLimiter.reset(key);
89
+ return { success: true };
90
+ },
91
+
92
+ // Events helpers (internal pub/sub)
93
+ emitEvent: async (event: string, data: any) => {
94
+ await ctx.core.events.emit(event, data);
95
+ return { success: true };
96
+ },
97
+
98
+ // SSE broadcast
99
+ broadcast: (channel: string, event: string, data: any) => {
100
+ ctx.core.sse.broadcast(channel, event, data);
101
+ return { success: true };
102
+ },
103
+ getSSEClients: () => ({
104
+ total: ctx.core.sse.getClients().length,
105
+ byChannel: ctx.core.sse.getClientsByChannel("events").length,
106
+ }),
107
+ };
108
+ },
109
+ init: async (ctx) => {
110
+ // Register job handler for demo
111
+ ctx.core.jobs.register("demo-job", async (data) => {
112
+ ctx.core.logger.info("Demo job executed", { data });
113
+ // Broadcast job completion via SSE
114
+ ctx.core.sse.broadcast("events", "job-completed", {
115
+ id: Date.now(),
116
+ message: `Job completed: ${data.message || "No message"}`,
117
+ timestamp: new Date().toISOString(),
118
+ });
119
+ });
120
+
121
+ // Schedule cron job to broadcast SSE events every 5 seconds
122
+ ctx.core.cron.schedule("*/5 * * * * *", () => {
123
+ const message = eventMessages[Math.floor(Math.random() * eventMessages.length)];
124
+ ctx.core.sse.broadcast("events", "cron-event", {
125
+ id: Date.now(),
126
+ message,
127
+ timestamp: new Date().toISOString(),
128
+ source: "cron",
129
+ });
130
+ }, { name: "sse-broadcaster" });
131
+
132
+ // Listen for internal events and broadcast to SSE
133
+ ctx.core.events.on("demo.*", (data) => {
134
+ ctx.core.sse.broadcast("events", "internal-event", {
135
+ id: Date.now(),
136
+ message: `Internal event: ${JSON.stringify(data)}`,
137
+ timestamp: new Date().toISOString(),
138
+ source: "events",
139
+ });
140
+ });
141
+
142
+ ctx.core.logger.info("Demo plugin initialized with all core services");
143
+ },
144
+ });
@@ -0,0 +1,15 @@
1
+
2
+ import type { Handler, AppContext } from "$lib/api";
3
+ import type { Routes } from "$lib/api";
4
+
5
+ export class DeleteCacheHandler implements Handler<Routes.Api.Cache.Delete> {
6
+ ctx: AppContext;
7
+
8
+ constructor(ctx: AppContext) {
9
+ this.ctx = ctx;
10
+ }
11
+
12
+ handle(input: Routes.Api.Cache.Delete.Input): Routes.Api.Cache.Delete.Output {
13
+ return this.ctx.plugins.demo.cacheDelete(input.key);
14
+ }
15
+ }
@@ -0,0 +1,15 @@
1
+
2
+ import type { Handler, AppContext } from "$lib/api";
3
+ import type { Routes } from "$lib/api";
4
+
5
+ export class GetCacheHandler implements Handler<Routes.Api.Cache.Get> {
6
+ ctx: AppContext;
7
+
8
+ constructor(ctx: AppContext) {
9
+ this.ctx = ctx;
10
+ }
11
+
12
+ handle(input: Routes.Api.Cache.Get.Input): Routes.Api.Cache.Get.Output {
13
+ return this.ctx.plugins.demo.cacheGet(input.key);
14
+ }
15
+ }
@@ -0,0 +1,15 @@
1
+
2
+ import type { Handler, AppContext } from "$lib/api";
3
+ import type { Routes } from "$lib/api";
4
+
5
+ export class KeysCacheHandler implements Handler<Routes.Api.Cache.Keys> {
6
+ ctx: AppContext;
7
+
8
+ constructor(ctx: AppContext) {
9
+ this.ctx = ctx;
10
+ }
11
+
12
+ handle(input: Routes.Api.Cache.Keys.Input): Routes.Api.Cache.Keys.Output {
13
+ return this.ctx.plugins.demo.cacheKeys();
14
+ }
15
+ }
@@ -0,0 +1,15 @@
1
+
2
+ import type { Handler, AppContext } from "$lib/api";
3
+ import type { Routes } from "$lib/api";
4
+
5
+ export class SetCacheHandler implements Handler<Routes.Api.Cache.Set> {
6
+ ctx: AppContext;
7
+
8
+ constructor(ctx: AppContext) {
9
+ this.ctx = ctx;
10
+ }
11
+
12
+ handle(input: Routes.Api.Cache.Set.Input): Routes.Api.Cache.Set.Output {
13
+ return this.ctx.plugins.demo.cacheSet(input.key, input.value, input.ttl);
14
+ }
15
+ }
@@ -0,0 +1,46 @@
1
+
2
+ import { createRouter, defineRoute } from "@donkeylabs/server";
3
+ import { z } from "zod";
4
+ import { SetCacheHandler } from "./handlers/set";
5
+ import { GetCacheHandler } from "./handlers/get";
6
+ import { DeleteCacheHandler } from "./handlers/delete";
7
+ import { KeysCacheHandler } from "./handlers/keys";
8
+
9
+ const router = createRouter("api");
10
+
11
+ router.route("cache.set").typed(
12
+ defineRoute({
13
+ input: z.object({
14
+ key: z.string(),
15
+ value: z.any(),
16
+ ttl: z.number().optional()
17
+ }),
18
+ output: z.object({ success: z.boolean() }),
19
+ handle: SetCacheHandler,
20
+ })
21
+ );
22
+
23
+ router.route("cache.get").typed(
24
+ defineRoute({
25
+ input: z.object({ key: z.string() }),
26
+ output: z.object({ value: z.any().optional(), exists: z.boolean() }),
27
+ handle: GetCacheHandler,
28
+ })
29
+ );
30
+
31
+ router.route("cache.delete").typed(
32
+ defineRoute({
33
+ input: z.object({ key: z.string() }),
34
+ output: z.object({ success: z.boolean() }),
35
+ handle: DeleteCacheHandler,
36
+ })
37
+ );
38
+
39
+ router.route("cache.keys").typed(
40
+ defineRoute({
41
+ output: z.object({ keys: z.array(z.string()) }),
42
+ handle: KeysCacheHandler,
43
+ })
44
+ );
45
+
46
+ export default router;
@@ -0,0 +1,17 @@
1
+
2
+ import type { Handler, AppContext } from "$lib/api";
3
+ import type { Routes } from "$lib/api";
4
+
5
+ export class DecrementCounterHandler implements Handler<Routes.Api.Counter.Decrement> {
6
+ ctx: AppContext;
7
+
8
+ constructor(ctx: AppContext) {
9
+ this.ctx = ctx;
10
+ }
11
+
12
+ handle(input: Routes.Api.Counter.Decrement.Input): Routes.Api.Counter.Decrement.Output {
13
+ return {
14
+ count: this.ctx.plugins.demo.decrement(),
15
+ };
16
+ }
17
+ }
@@ -0,0 +1,17 @@
1
+
2
+ import type { Handler, AppContext } from "$lib/api";
3
+ import type { Routes } from "$lib/api";
4
+
5
+ export class GetCounterHandler implements Handler<Routes.Api.Counter.Get> {
6
+ ctx: AppContext;
7
+
8
+ constructor(ctx: AppContext) {
9
+ this.ctx = ctx;
10
+ }
11
+
12
+ handle(input: Routes.Api.Counter.Get.Input): Routes.Api.Counter.Get.Output {
13
+ return {
14
+ count: this.ctx.plugins.demo.getCounter(),
15
+ };
16
+ }
17
+ }
@@ -0,0 +1,17 @@
1
+
2
+ import type { Handler, AppContext } from "$lib/api";
3
+ import type { Routes } from "$lib/api";
4
+
5
+ export class IncrementCounterHandler implements Handler<Routes.Api.Counter.Increment> {
6
+ ctx: AppContext;
7
+
8
+ constructor(ctx: AppContext) {
9
+ this.ctx = ctx;
10
+ }
11
+
12
+ handle(input: Routes.Api.Counter.Increment.Input): Routes.Api.Counter.Increment.Output {
13
+ return {
14
+ count: this.ctx.plugins.demo.increment(),
15
+ };
16
+ }
17
+ }
@@ -0,0 +1,17 @@
1
+
2
+ import type { Handler, AppContext } from "$lib/api";
3
+ import type { Routes } from "$lib/api";
4
+
5
+ export class ResetCounterHandler implements Handler<Routes.Api.Counter.Reset> {
6
+ ctx: AppContext;
7
+
8
+ constructor(ctx: AppContext) {
9
+ this.ctx = ctx;
10
+ }
11
+
12
+ handle(input: Routes.Api.Counter.Reset.Input): Routes.Api.Counter.Reset.Output {
13
+ return {
14
+ count: this.ctx.plugins.demo.reset(),
15
+ };
16
+ }
17
+ }
@@ -0,0 +1,39 @@
1
+
2
+ import { createRouter, defineRoute } from "@donkeylabs/server";
3
+ import { z } from "zod";
4
+ import { GetCounterHandler } from "./handlers/get";
5
+ import { IncrementCounterHandler } from "./handlers/increment";
6
+ import { DecrementCounterHandler } from "./handlers/decrement";
7
+ import { ResetCounterHandler } from "./handlers/reset";
8
+
9
+ const router = createRouter("api");
10
+
11
+ router.route("counter.get").typed(
12
+ defineRoute({
13
+ output: z.object({ count: z.number() }),
14
+ handle: GetCounterHandler,
15
+ })
16
+ );
17
+
18
+ router.route("counter.increment").typed(
19
+ defineRoute({
20
+ output: z.object({ count: z.number() }),
21
+ handle: IncrementCounterHandler,
22
+ })
23
+ );
24
+
25
+ router.route("counter.decrement").typed(
26
+ defineRoute({
27
+ output: z.object({ count: z.number() }),
28
+ handle: DecrementCounterHandler,
29
+ })
30
+ );
31
+
32
+ router.route("counter.reset").typed(
33
+ defineRoute({
34
+ output: z.object({ count: z.number() }),
35
+ handle: ResetCounterHandler,
36
+ })
37
+ );
38
+
39
+ export default router;
@@ -0,0 +1,17 @@
1
+
2
+ import type { Handler, AppContext } from "$lib/api";
3
+ import type { Routes } from "$lib/api";
4
+
5
+ export class ListCronHandler implements Handler<Routes.Api.Cron.List> {
6
+ ctx: AppContext;
7
+
8
+ constructor(ctx: AppContext) {
9
+ this.ctx = ctx;
10
+ }
11
+
12
+ handle(input: Routes.Api.Cron.List.Input): Routes.Api.Cron.List.Output {
13
+ return {
14
+ tasks: this.ctx.plugins.demo.getCronTasks()
15
+ };
16
+ }
17
+ }