@donkeylabs/server 0.1.3 → 0.1.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.
Files changed (55) hide show
  1. package/examples/starter/node_modules/@donkeylabs/server/README.md +15 -0
  2. package/examples/starter/node_modules/@donkeylabs/server/cli/commands/generate.ts +461 -0
  3. package/examples/starter/node_modules/@donkeylabs/server/cli/commands/init.ts +476 -0
  4. package/examples/starter/node_modules/@donkeylabs/server/cli/commands/interactive.ts +223 -0
  5. package/examples/starter/node_modules/@donkeylabs/server/cli/commands/plugin.ts +192 -0
  6. package/examples/starter/node_modules/@donkeylabs/server/cli/donkeylabs +106 -0
  7. package/examples/starter/node_modules/@donkeylabs/server/cli/index.ts +100 -0
  8. package/examples/starter/node_modules/@donkeylabs/server/context.d.ts +17 -0
  9. package/examples/starter/node_modules/@donkeylabs/server/docs/api-client.md +520 -0
  10. package/examples/starter/node_modules/@donkeylabs/server/docs/cache.md +437 -0
  11. package/examples/starter/node_modules/@donkeylabs/server/docs/cli.md +353 -0
  12. package/examples/starter/node_modules/@donkeylabs/server/docs/core-services.md +338 -0
  13. package/examples/starter/node_modules/@donkeylabs/server/docs/cron.md +465 -0
  14. package/examples/starter/node_modules/@donkeylabs/server/docs/errors.md +303 -0
  15. package/examples/starter/node_modules/@donkeylabs/server/docs/events.md +460 -0
  16. package/examples/starter/node_modules/@donkeylabs/server/docs/handlers.md +549 -0
  17. package/examples/starter/node_modules/@donkeylabs/server/docs/jobs.md +556 -0
  18. package/examples/starter/node_modules/@donkeylabs/server/docs/logger.md +316 -0
  19. package/examples/starter/node_modules/@donkeylabs/server/docs/middleware.md +682 -0
  20. package/examples/starter/node_modules/@donkeylabs/server/docs/plugins.md +524 -0
  21. package/examples/starter/node_modules/@donkeylabs/server/docs/project-structure.md +493 -0
  22. package/examples/starter/node_modules/@donkeylabs/server/docs/rate-limiter.md +525 -0
  23. package/examples/starter/node_modules/@donkeylabs/server/docs/router.md +566 -0
  24. package/examples/starter/node_modules/@donkeylabs/server/docs/sse.md +542 -0
  25. package/examples/starter/node_modules/@donkeylabs/server/docs/svelte-frontend.md +324 -0
  26. package/examples/starter/node_modules/@donkeylabs/server/mcp/donkeylabs-mcp +3238 -0
  27. package/examples/starter/node_modules/@donkeylabs/server/mcp/server.ts +3238 -0
  28. package/examples/starter/node_modules/@donkeylabs/server/package.json +77 -0
  29. package/examples/starter/node_modules/@donkeylabs/server/registry.d.ts +11 -0
  30. package/examples/starter/node_modules/@donkeylabs/server/src/client/base.ts +481 -0
  31. package/examples/starter/node_modules/@donkeylabs/server/src/client/index.ts +150 -0
  32. package/examples/starter/node_modules/@donkeylabs/server/src/core/cache.ts +183 -0
  33. package/examples/starter/node_modules/@donkeylabs/server/src/core/cron.ts +255 -0
  34. package/examples/starter/node_modules/@donkeylabs/server/src/core/errors.ts +320 -0
  35. package/examples/starter/node_modules/@donkeylabs/server/src/core/events.ts +163 -0
  36. package/examples/starter/node_modules/@donkeylabs/server/src/core/index.ts +94 -0
  37. package/examples/starter/node_modules/@donkeylabs/server/src/core/jobs.ts +334 -0
  38. package/examples/starter/node_modules/@donkeylabs/server/src/core/logger.ts +131 -0
  39. package/examples/starter/node_modules/@donkeylabs/server/src/core/rate-limiter.ts +193 -0
  40. package/examples/starter/node_modules/@donkeylabs/server/src/core/sse.ts +210 -0
  41. package/examples/starter/node_modules/@donkeylabs/server/src/core.ts +428 -0
  42. package/examples/starter/node_modules/@donkeylabs/server/src/handlers.ts +87 -0
  43. package/examples/starter/node_modules/@donkeylabs/server/src/harness.ts +70 -0
  44. package/examples/starter/node_modules/@donkeylabs/server/src/index.ts +38 -0
  45. package/examples/starter/node_modules/@donkeylabs/server/src/middleware.ts +34 -0
  46. package/examples/starter/node_modules/@donkeylabs/server/src/registry.ts +13 -0
  47. package/examples/starter/node_modules/@donkeylabs/server/src/router.ts +155 -0
  48. package/examples/starter/node_modules/@donkeylabs/server/src/server.ts +234 -0
  49. package/examples/starter/node_modules/@donkeylabs/server/templates/init/donkeylabs.config.ts.template +14 -0
  50. package/examples/starter/node_modules/@donkeylabs/server/templates/init/index.ts.template +41 -0
  51. package/examples/starter/node_modules/@donkeylabs/server/templates/plugin/index.ts.template +25 -0
  52. package/examples/starter/src/routes/health/ping/models/model.ts +11 -7
  53. package/package.json +3 -3
  54. package/examples/starter/node_modules/.svelte2tsx-language-server-files/svelte-native-jsx.d.ts +0 -32
  55. package/examples/starter/node_modules/.svelte2tsx-language-server-files/svelte-shims-v4.d.ts +0 -290
@@ -0,0 +1,303 @@
1
+ # Error System
2
+
3
+ The error system provides throwable HTTP errors that are automatically caught by the server and converted to proper HTTP responses with status codes and JSON error bodies.
4
+
5
+ ## Quick Start
6
+
7
+ ```ts
8
+ // In any route handler
9
+ const router = createRouter("users")
10
+ .route("get").typed({
11
+ input: z.object({ id: z.number() }),
12
+ output: z.object({ name: z.string() }),
13
+ handle: async (input, ctx) => {
14
+ const user = await ctx.db.selectFrom("users")
15
+ .where("id", "=", input.id)
16
+ .selectAll()
17
+ .executeTakeFirst();
18
+
19
+ if (!user) {
20
+ // Throws 404 with JSON body: { error: "NOT_FOUND", message: "User not found" }
21
+ throw ctx.errors.NotFound("User not found", { userId: input.id });
22
+ }
23
+
24
+ return user;
25
+ }
26
+ });
27
+ ```
28
+
29
+ ## Available Errors
30
+
31
+ All standard HTTP errors are available via `ctx.errors`:
32
+
33
+ | Method | Status | Code | Description |
34
+ |--------|--------|------|-------------|
35
+ | `BadRequest()` | 400 | BAD_REQUEST | Invalid request data |
36
+ | `Unauthorized()` | 401 | UNAUTHORIZED | Authentication required |
37
+ | `Forbidden()` | 403 | FORBIDDEN | Not allowed to access |
38
+ | `NotFound()` | 404 | NOT_FOUND | Resource not found |
39
+ | `MethodNotAllowed()` | 405 | METHOD_NOT_ALLOWED | HTTP method not supported |
40
+ | `Conflict()` | 409 | CONFLICT | Resource conflict |
41
+ | `Gone()` | 410 | GONE | Resource no longer available |
42
+ | `UnprocessableEntity()` | 422 | UNPROCESSABLE_ENTITY | Validation failed |
43
+ | `TooManyRequests()` | 429 | TOO_MANY_REQUESTS | Rate limited |
44
+ | `InternalServer()` | 500 | INTERNAL_SERVER_ERROR | Server error |
45
+ | `NotImplemented()` | 501 | NOT_IMPLEMENTED | Feature not implemented |
46
+ | `BadGateway()` | 502 | BAD_GATEWAY | Upstream error |
47
+ | `ServiceUnavailable()` | 503 | SERVICE_UNAVAILABLE | Service down |
48
+ | `GatewayTimeout()` | 504 | GATEWAY_TIMEOUT | Upstream timeout |
49
+
50
+ ## Error Response Format
51
+
52
+ All errors are returned as JSON with consistent structure:
53
+
54
+ ```json
55
+ {
56
+ "error": "NOT_FOUND",
57
+ "message": "User not found",
58
+ "details": { "userId": 123 }
59
+ }
60
+ ```
61
+
62
+ The `details` field is optional and only included if provided.
63
+
64
+ ## Error Factory Signature
65
+
66
+ Each error factory has the same signature:
67
+
68
+ ```ts
69
+ ctx.errors.NotFound(
70
+ message?: string, // Custom message (uses default if omitted)
71
+ details?: Record<string, any>, // Additional context
72
+ cause?: Error // Original error that caused this
73
+ ): HttpError
74
+ ```
75
+
76
+ ## Custom Errors
77
+
78
+ ### Creating Custom Errors at Runtime
79
+
80
+ Use `ctx.errors.custom()` for one-off custom errors:
81
+
82
+ ```ts
83
+ throw ctx.errors.custom(418, "IM_A_TEAPOT", "I'm a teapot");
84
+ ```
85
+
86
+ ### Registering Custom Errors
87
+
88
+ Register reusable custom errors that appear on `ctx.errors`:
89
+
90
+ ```ts
91
+ // During server setup
92
+ const server = new AppServer({ db, port: 3000 });
93
+
94
+ // Access errors service
95
+ server.getCore().errors.register("PaymentRequired", {
96
+ status: 402,
97
+ code: "PAYMENT_REQUIRED",
98
+ defaultMessage: "Payment is required",
99
+ });
100
+
101
+ // Now available everywhere
102
+ throw ctx.errors.PaymentRequired("Subscription expired");
103
+ ```
104
+
105
+ ### Plugin Custom Errors
106
+
107
+ Plugins can define custom errors that are automatically registered:
108
+
109
+ ```ts
110
+ export const paymentPlugin = createPlugin.define({
111
+ name: "payment",
112
+ customErrors: {
113
+ PaymentFailed: {
114
+ status: 402,
115
+ code: "PAYMENT_FAILED",
116
+ defaultMessage: "Payment processing failed",
117
+ },
118
+ InsufficientFunds: {
119
+ status: 402,
120
+ code: "INSUFFICIENT_FUNDS",
121
+ defaultMessage: "Insufficient funds",
122
+ },
123
+ CardDeclined: {
124
+ status: 402,
125
+ code: "CARD_DECLINED",
126
+ defaultMessage: "Card was declined",
127
+ },
128
+ },
129
+ service: async (ctx) => ({
130
+ charge: async (amount: number) => {
131
+ if (amount > 10000) {
132
+ throw ctx.core.errors.InsufficientFunds("Maximum charge amount exceeded");
133
+ }
134
+ // ...
135
+ },
136
+ }),
137
+ });
138
+ ```
139
+
140
+ After plugin initialization, these errors are available on `ctx.errors`:
141
+
142
+ ```ts
143
+ // In any route handler
144
+ throw ctx.errors.CardDeclined("Please try a different card");
145
+ ```
146
+
147
+ ## Type Augmentation for Custom Errors
148
+
149
+ For TypeScript autocomplete on custom errors, augment the `ErrorFactories` interface:
150
+
151
+ ```ts
152
+ // In your plugin or types file
153
+ declare module "../core/errors" {
154
+ interface ErrorFactories {
155
+ PaymentFailed: ErrorFactory;
156
+ InsufficientFunds: ErrorFactory;
157
+ CardDeclined: ErrorFactory;
158
+ }
159
+ }
160
+ ```
161
+
162
+ ## Validation Errors
163
+
164
+ For Zod validation failures, use `createValidationError`:
165
+
166
+ ```ts
167
+ import { createValidationError } from "./core/errors";
168
+
169
+ // Convert Zod errors to HTTP error
170
+ const result = schema.safeParse(data);
171
+ if (!result.success) {
172
+ throw createValidationError(result.error.issues);
173
+ }
174
+ ```
175
+
176
+ Response format:
177
+
178
+ ```json
179
+ {
180
+ "error": "BAD_REQUEST",
181
+ "message": "Validation Failed",
182
+ "details": {
183
+ "issues": [
184
+ { "path": ["email"], "message": "Invalid email" },
185
+ { "path": ["password"], "message": "Too short" }
186
+ ]
187
+ }
188
+ }
189
+ ```
190
+
191
+ ## Client-Side Error Handling
192
+
193
+ The API client provides typed error handling:
194
+
195
+ ```ts
196
+ import { createApiClient, ApiError, ValidationError, ErrorCodes } from "./client";
197
+
198
+ const api = createApiClient("http://localhost:3000");
199
+
200
+ try {
201
+ await api.users.get({ id: 999 });
202
+ } catch (error) {
203
+ if (error instanceof ValidationError) {
204
+ // Handle validation errors with field details
205
+ console.log(error.getFieldErrors("email"));
206
+ console.log(error.hasFieldError("password"));
207
+ } else if (error instanceof ApiError) {
208
+ // Handle other API errors
209
+ if (error.is(ErrorCodes.NOT_FOUND)) {
210
+ console.log("User not found");
211
+ } else if (error.is(ErrorCodes.UNAUTHORIZED)) {
212
+ // Redirect to login
213
+ }
214
+
215
+ // Access error properties
216
+ console.log(error.status); // 404
217
+ console.log(error.code); // "NOT_FOUND"
218
+ console.log(error.message); // "User not found"
219
+ console.log(error.details); // { userId: 999 }
220
+ }
221
+ }
222
+ ```
223
+
224
+ ## Error Checking Utility
225
+
226
+ Check if any error is an HttpError:
227
+
228
+ ```ts
229
+ try {
230
+ await someOperation();
231
+ } catch (error) {
232
+ if (ctx.errors.isHttpError(error)) {
233
+ // Safe to access error.status, error.code, etc.
234
+ console.log(error.status, error.code);
235
+ } else {
236
+ // Regular JavaScript error
237
+ throw error;
238
+ }
239
+ }
240
+ ```
241
+
242
+ ## Best Practices
243
+
244
+ ### Use Specific Error Types
245
+
246
+ ```ts
247
+ // Good - specific error type
248
+ throw ctx.errors.NotFound("User not found");
249
+
250
+ // Avoid - generic error
251
+ throw ctx.errors.BadRequest("User not found");
252
+ ```
253
+
254
+ ### Include Helpful Details
255
+
256
+ ```ts
257
+ // Good - includes context for debugging
258
+ throw ctx.errors.NotFound("User not found", {
259
+ userId: input.id,
260
+ searchedIn: "users",
261
+ });
262
+
263
+ // Less helpful
264
+ throw ctx.errors.NotFound("Not found");
265
+ ```
266
+
267
+ ### Chain Errors for Debugging
268
+
269
+ ```ts
270
+ try {
271
+ await externalService.call();
272
+ } catch (e) {
273
+ throw ctx.errors.BadGateway(
274
+ "External service failed",
275
+ { service: "payment-gateway" },
276
+ e as Error // Preserve original error
277
+ );
278
+ }
279
+ ```
280
+
281
+ ### Handle in Middleware
282
+
283
+ ```ts
284
+ export const errorLoggingMiddleware = createMiddleware({
285
+ name: "errorLogger",
286
+ execute: async (req, ctx, next) => {
287
+ try {
288
+ return await next();
289
+ } catch (error) {
290
+ if (ctx.core.errors.isHttpError(error)) {
291
+ ctx.core.logger.warn("HTTP error", {
292
+ status: error.status,
293
+ code: error.code,
294
+ message: error.message,
295
+ });
296
+ } else {
297
+ ctx.core.logger.error("Unhandled error", { error });
298
+ }
299
+ throw error; // Re-throw for server to handle
300
+ }
301
+ },
302
+ });
303
+ ```