@hadoku/worker-utils 0.0.0-stage → 1.6.20
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/LICENSE +15 -0
- package/README.md +334 -2
- package/dist/auth.d.ts +153 -0
- package/dist/auth.js +39 -0
- package/dist/authPage.d.ts +113 -0
- package/dist/authPage.js +833 -0
- package/dist/cloudflareWorkerLogger.d.ts +71 -0
- package/dist/cloudflareWorkerLogger.js +94 -0
- package/dist/content-level.d.ts +52 -0
- package/dist/content-level.js +28 -0
- package/dist/context.d.ts +110 -0
- package/dist/context.js +75 -0
- package/dist/cors.d.ts +123 -0
- package/dist/cors.js +113 -0
- package/dist/edge-routes.d.ts +47 -0
- package/dist/edge-routes.js +34 -0
- package/dist/identity.d.ts +477 -0
- package/dist/identity.js +239 -0
- package/dist/index.d.ts +108 -0
- package/dist/index.js +976 -0
- package/dist/masking.d.ts +108 -0
- package/dist/masking.js +90 -0
- package/dist/openapi-config.d.ts +252 -0
- package/dist/openapi-config.js +131 -0
- package/dist/openapi-schemas.d.ts +157 -0
- package/dist/openapi-schemas.js +79 -0
- package/dist/rate-limit.d.ts +39 -0
- package/dist/rate-limit.js +59 -0
- package/dist/responses.d.ts +159 -0
- package/dist/responses.js +80 -0
- package/dist/types.d.ts +166 -0
- package/dist/types.js +0 -0
- package/dist/validation.d.ts +88 -0
- package/dist/validation.js +172 -0
- package/package.json +34 -4
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import { z } from '@hono/zod-openapi';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Shared OpenAPI Zod Schemas
|
|
5
|
+
*
|
|
6
|
+
* Common schemas used across multiple worker APIs for consistent error handling,
|
|
7
|
+
* health checks, and response formats.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```typescript
|
|
11
|
+
* import { ErrorResponseSchema, createHealthResponseSchema } from '../util/openapi-schemas';
|
|
12
|
+
*
|
|
13
|
+
* // Use generic error schema
|
|
14
|
+
* const route = createRoute({
|
|
15
|
+
* responses: {
|
|
16
|
+
* 400: { content: { 'application/json': { schema: ErrorResponseSchema } } }
|
|
17
|
+
* }
|
|
18
|
+
* });
|
|
19
|
+
*
|
|
20
|
+
* // Create service-specific health schema
|
|
21
|
+
* const HealthSchema = createHealthResponseSchema('my-api');
|
|
22
|
+
* ```
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Standard error response with success: false pattern
|
|
27
|
+
* Used by: oss-recon-api
|
|
28
|
+
*/
|
|
29
|
+
declare const ErrorResponseSchema: z.ZodObject<{
|
|
30
|
+
success: z.ZodLiteral<false>;
|
|
31
|
+
error: z.ZodString;
|
|
32
|
+
}, z.core.$strip>;
|
|
33
|
+
/**
|
|
34
|
+
* Alternative error response without success field
|
|
35
|
+
* Used by: task-api, prompt-api
|
|
36
|
+
*/
|
|
37
|
+
declare const SimpleErrorResponseSchema: z.ZodObject<{
|
|
38
|
+
error: z.ZodString;
|
|
39
|
+
message: z.ZodOptional<z.ZodString>;
|
|
40
|
+
}, z.core.$strip>;
|
|
41
|
+
/**
|
|
42
|
+
* Detailed error response with success pattern
|
|
43
|
+
* Used by: prompt-api
|
|
44
|
+
*/
|
|
45
|
+
declare const DetailedErrorResponseSchema: z.ZodObject<{
|
|
46
|
+
success: z.ZodLiteral<false>;
|
|
47
|
+
error: z.ZodString;
|
|
48
|
+
message: z.ZodString;
|
|
49
|
+
}, z.core.$strip>;
|
|
50
|
+
/**
|
|
51
|
+
* Rate limit / throttle error response
|
|
52
|
+
* Used by: task-api
|
|
53
|
+
*/
|
|
54
|
+
declare const ThrottleErrorResponseSchema: z.ZodObject<{
|
|
55
|
+
error: z.ZodLiteral<"Rate limit exceeded">;
|
|
56
|
+
message: z.ZodString;
|
|
57
|
+
retryAfter: z.ZodNumber;
|
|
58
|
+
}, z.core.$strip>;
|
|
59
|
+
/**
|
|
60
|
+
* Create a service-specific health response schema
|
|
61
|
+
*
|
|
62
|
+
* @param serviceName - The name of the service (e.g., 'task-api', 'prompt-api')
|
|
63
|
+
* @param options - Optional configuration for additional checks
|
|
64
|
+
* @returns Zod schema for health response
|
|
65
|
+
*
|
|
66
|
+
* @example
|
|
67
|
+
* ```typescript
|
|
68
|
+
* const HealthSchema = createHealthResponseSchema('task-api', { includeChecks: true });
|
|
69
|
+
* ```
|
|
70
|
+
*/
|
|
71
|
+
declare function createHealthResponseSchema<T extends string>(serviceName: T, options?: {
|
|
72
|
+
includeChecks?: boolean;
|
|
73
|
+
}): z.ZodObject<{
|
|
74
|
+
status: z.ZodEnum<{
|
|
75
|
+
error: "error";
|
|
76
|
+
ok: "ok";
|
|
77
|
+
degraded: "degraded";
|
|
78
|
+
}>;
|
|
79
|
+
timestamp: z.ZodString;
|
|
80
|
+
service: z.ZodLiteral<T>;
|
|
81
|
+
}, z.core.$strip>;
|
|
82
|
+
/**
|
|
83
|
+
* Health response with success wrapper pattern
|
|
84
|
+
* Used by: oss-recon-api
|
|
85
|
+
*
|
|
86
|
+
* NOTE: these two services live in external @wolffm packages that still emit
|
|
87
|
+
* the legacy `status: 'healthy'` convention (and a {success,data} envelope),
|
|
88
|
+
* so this schema intentionally does NOT use HEALTH_STATUSES. Migrate those
|
|
89
|
+
* packages to healthCheck() to fold them onto the canonical contract.
|
|
90
|
+
*/
|
|
91
|
+
declare function createWrappedHealthResponseSchema<T extends string>(serviceName: T): z.ZodObject<{
|
|
92
|
+
success: z.ZodLiteral<true>;
|
|
93
|
+
data: z.ZodObject<{
|
|
94
|
+
status: z.ZodLiteral<"healthy">;
|
|
95
|
+
service: z.ZodLiteral<T>;
|
|
96
|
+
timestamp: z.ZodString;
|
|
97
|
+
}, z.core.$strip>;
|
|
98
|
+
}, z.core.$strip>;
|
|
99
|
+
/**
|
|
100
|
+
* Create a success response wrapper schema
|
|
101
|
+
* Used by: prompt-api, oss-recon-api
|
|
102
|
+
*
|
|
103
|
+
* @example
|
|
104
|
+
* ```typescript
|
|
105
|
+
* const UserResponseSchema = createSuccessResponseSchema(
|
|
106
|
+
* z.object({ user: UserSchema })
|
|
107
|
+
* ).openapi('UserResponse');
|
|
108
|
+
* ```
|
|
109
|
+
*/
|
|
110
|
+
declare function createSuccessResponseSchema<T extends z.ZodTypeAny>(dataSchema: T): z.ZodObject<{
|
|
111
|
+
success: z.ZodLiteral<true>;
|
|
112
|
+
data: T;
|
|
113
|
+
}, z.core.$strip>;
|
|
114
|
+
/**
|
|
115
|
+
* Create a delete confirmation response schema
|
|
116
|
+
*
|
|
117
|
+
* @example
|
|
118
|
+
* ```typescript
|
|
119
|
+
* const DeleteBoardResponseSchema = createDeleteResponseSchema('boardId', 'work-board');
|
|
120
|
+
* ```
|
|
121
|
+
*/
|
|
122
|
+
declare function createDeleteResponseSchema(idField?: string, example?: string): z.ZodObject<{
|
|
123
|
+
[x: string]: z.ZodString | z.ZodLiteral<true>;
|
|
124
|
+
deleted: z.ZodLiteral<true>;
|
|
125
|
+
}, z.core.$strip>;
|
|
126
|
+
/**
|
|
127
|
+
* Zod validation error response
|
|
128
|
+
* Matches the defaultHook error format used in OpenAPIHono
|
|
129
|
+
*/
|
|
130
|
+
declare const ValidationErrorResponseSchema: z.ZodObject<{
|
|
131
|
+
error: z.ZodLiteral<"Validation Error">;
|
|
132
|
+
message: z.ZodString;
|
|
133
|
+
}, z.core.$strip>;
|
|
134
|
+
/**
|
|
135
|
+
* Pre-built health schemas for known services
|
|
136
|
+
* Import these directly if you don't need customization
|
|
137
|
+
*/
|
|
138
|
+
declare const TaskApiHealthResponseSchema: z.ZodObject<{
|
|
139
|
+
status: z.ZodEnum<{
|
|
140
|
+
error: "error";
|
|
141
|
+
ok: "ok";
|
|
142
|
+
degraded: "degraded";
|
|
143
|
+
}>;
|
|
144
|
+
timestamp: z.ZodString;
|
|
145
|
+
service: z.ZodLiteral<"task-api-adapter">;
|
|
146
|
+
}, z.core.$strip>;
|
|
147
|
+
declare const PromptApiHealthResponseSchema: z.ZodObject<{
|
|
148
|
+
status: z.ZodEnum<{
|
|
149
|
+
error: "error";
|
|
150
|
+
ok: "ok";
|
|
151
|
+
degraded: "degraded";
|
|
152
|
+
}>;
|
|
153
|
+
timestamp: z.ZodString;
|
|
154
|
+
service: z.ZodLiteral<"prompt-api">;
|
|
155
|
+
}, z.core.$strip>;
|
|
156
|
+
|
|
157
|
+
export { DetailedErrorResponseSchema, ErrorResponseSchema, PromptApiHealthResponseSchema, SimpleErrorResponseSchema, TaskApiHealthResponseSchema, ThrottleErrorResponseSchema, ValidationErrorResponseSchema, createDeleteResponseSchema, createHealthResponseSchema, createSuccessResponseSchema, createWrappedHealthResponseSchema };
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
// openapi-schemas.ts
|
|
2
|
+
import { z } from "@hono/zod-openapi";
|
|
3
|
+
var HEALTH_STATUS_VALUES = ["ok", "degraded", "error"];
|
|
4
|
+
var ErrorResponseSchema = z.object({
|
|
5
|
+
success: z.literal(false),
|
|
6
|
+
error: z.string().openapi({ example: "Bad Request" })
|
|
7
|
+
}).openapi("ErrorResponse");
|
|
8
|
+
var SimpleErrorResponseSchema = z.object({
|
|
9
|
+
error: z.string().openapi({ example: "Bad Request" }),
|
|
10
|
+
message: z.string().optional().openapi({ example: "Missing required field: title" })
|
|
11
|
+
}).openapi("SimpleErrorResponse");
|
|
12
|
+
var DetailedErrorResponseSchema = z.object({
|
|
13
|
+
success: z.literal(false),
|
|
14
|
+
error: z.string().openapi({ example: "Validation Error" }),
|
|
15
|
+
message: z.string().openapi({ example: "Invalid input data" })
|
|
16
|
+
}).openapi("DetailedErrorResponse");
|
|
17
|
+
var ThrottleErrorResponseSchema = z.object({
|
|
18
|
+
error: z.literal("Rate limit exceeded"),
|
|
19
|
+
message: z.string().openapi({ example: "Too many requests" }),
|
|
20
|
+
retryAfter: z.number().openapi({ example: 60 })
|
|
21
|
+
}).openapi("ThrottleErrorResponse");
|
|
22
|
+
function createHealthResponseSchema(serviceName, options) {
|
|
23
|
+
const baseSchema = z.object({
|
|
24
|
+
status: z.enum(HEALTH_STATUS_VALUES).openapi({ example: "ok" }),
|
|
25
|
+
timestamp: z.string().openapi({ example: "2024-01-15T10:30:00.000Z" }),
|
|
26
|
+
service: z.literal(serviceName)
|
|
27
|
+
});
|
|
28
|
+
if (options?.includeChecks) {
|
|
29
|
+
return baseSchema.extend({
|
|
30
|
+
checks: z.object({
|
|
31
|
+
kv: z.boolean().openapi({ example: true })
|
|
32
|
+
})
|
|
33
|
+
}).openapi(`${serviceName}HealthResponse`);
|
|
34
|
+
}
|
|
35
|
+
return baseSchema.openapi(`${serviceName}HealthResponse`);
|
|
36
|
+
}
|
|
37
|
+
function createWrappedHealthResponseSchema(serviceName) {
|
|
38
|
+
return z.object({
|
|
39
|
+
success: z.literal(true),
|
|
40
|
+
data: z.object({
|
|
41
|
+
status: z.literal("healthy"),
|
|
42
|
+
service: z.literal(serviceName),
|
|
43
|
+
timestamp: z.string().openapi({ example: "2024-01-15T10:30:00.000Z" })
|
|
44
|
+
})
|
|
45
|
+
}).openapi(`${serviceName}HealthResponse`);
|
|
46
|
+
}
|
|
47
|
+
function createSuccessResponseSchema(dataSchema) {
|
|
48
|
+
return z.object({
|
|
49
|
+
success: z.literal(true),
|
|
50
|
+
data: dataSchema
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
function createDeleteResponseSchema(idField = "id", example = "01HXYZ123ABC") {
|
|
54
|
+
return z.object({
|
|
55
|
+
deleted: z.literal(true),
|
|
56
|
+
[idField]: z.string().openapi({ example })
|
|
57
|
+
}).openapi("DeleteResponse");
|
|
58
|
+
}
|
|
59
|
+
var ValidationErrorResponseSchema = z.object({
|
|
60
|
+
error: z.literal("Validation Error"),
|
|
61
|
+
message: z.string().openapi({ example: "id: Required, title: String must contain at least 1 character(s)" })
|
|
62
|
+
}).openapi("ValidationErrorResponse");
|
|
63
|
+
var TaskApiHealthResponseSchema = createHealthResponseSchema("task-api-adapter", {
|
|
64
|
+
includeChecks: true
|
|
65
|
+
});
|
|
66
|
+
var PromptApiHealthResponseSchema = createHealthResponseSchema("prompt-api");
|
|
67
|
+
export {
|
|
68
|
+
DetailedErrorResponseSchema,
|
|
69
|
+
ErrorResponseSchema,
|
|
70
|
+
PromptApiHealthResponseSchema,
|
|
71
|
+
SimpleErrorResponseSchema,
|
|
72
|
+
TaskApiHealthResponseSchema,
|
|
73
|
+
ThrottleErrorResponseSchema,
|
|
74
|
+
ValidationErrorResponseSchema,
|
|
75
|
+
createDeleteResponseSchema,
|
|
76
|
+
createHealthResponseSchema,
|
|
77
|
+
createSuccessResponseSchema,
|
|
78
|
+
createWrappedHealthResponseSchema
|
|
79
|
+
};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sliding window rate limiting using Cloudflare KV
|
|
3
|
+
*
|
|
4
|
+
* Generic implementation that accepts configuration per-consumer.
|
|
5
|
+
* Each worker provides its own limits via RateLimitConfig.
|
|
6
|
+
*/
|
|
7
|
+
interface RateLimitConfig {
|
|
8
|
+
/** Maximum requests allowed per window */
|
|
9
|
+
maxRequests: number;
|
|
10
|
+
/** Window duration in seconds */
|
|
11
|
+
windowSeconds: number;
|
|
12
|
+
/** KV TTL in seconds (should be >= windowSeconds) */
|
|
13
|
+
kvTtlSeconds: number;
|
|
14
|
+
/** KV key prefix (default: 'rate-limit') */
|
|
15
|
+
keyPrefix?: string;
|
|
16
|
+
}
|
|
17
|
+
interface RateLimitResult {
|
|
18
|
+
allowed: boolean;
|
|
19
|
+
remaining: number;
|
|
20
|
+
resetAt: number;
|
|
21
|
+
reason?: string;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Check if an identifier (IP, user key, etc.) is rate limited
|
|
25
|
+
*/
|
|
26
|
+
declare function checkRateLimit(kv: KVNamespace, identifier: string, config: RateLimitConfig): Promise<RateLimitResult>;
|
|
27
|
+
/**
|
|
28
|
+
* Record a request for rate limiting.
|
|
29
|
+
* Call after checkRateLimit returns allowed: true.
|
|
30
|
+
*/
|
|
31
|
+
declare function recordRequest(kv: KVNamespace, identifier: string, config: RateLimitConfig): Promise<void>;
|
|
32
|
+
/**
|
|
33
|
+
* Reset rate limit for an identifier (admin function)
|
|
34
|
+
*/
|
|
35
|
+
declare function resetRateLimit(kv: KVNamespace, identifier: string, config?: {
|
|
36
|
+
keyPrefix?: string;
|
|
37
|
+
}): Promise<void>;
|
|
38
|
+
|
|
39
|
+
export { type RateLimitConfig, type RateLimitResult, checkRateLimit, recordRequest, resetRateLimit };
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
// rate-limit.ts
|
|
2
|
+
async function checkRateLimit(kv, identifier, config) {
|
|
3
|
+
const prefix = config.keyPrefix ?? "rate-limit";
|
|
4
|
+
const key = `${prefix}:${identifier}`;
|
|
5
|
+
const windowMs = config.windowSeconds * 1e3;
|
|
6
|
+
const now = Date.now();
|
|
7
|
+
const existingData = await kv.get(key, "text");
|
|
8
|
+
const existing = existingData ? JSON.parse(existingData) : null;
|
|
9
|
+
if (!existing || now - existing.windowStart >= windowMs) {
|
|
10
|
+
return {
|
|
11
|
+
allowed: true,
|
|
12
|
+
remaining: config.maxRequests - 1,
|
|
13
|
+
resetAt: now + windowMs
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
if (existing.count >= config.maxRequests) {
|
|
17
|
+
const resetAt = existing.windowStart + windowMs;
|
|
18
|
+
const timeRemaining = resetAt - now;
|
|
19
|
+
const display = timeRemaining >= 12e4 ? `${Math.ceil(timeRemaining / 6e4)} minute(s)` : `${Math.ceil(timeRemaining / 1e3)} second(s)`;
|
|
20
|
+
return {
|
|
21
|
+
allowed: false,
|
|
22
|
+
remaining: 0,
|
|
23
|
+
resetAt,
|
|
24
|
+
reason: `Rate limit exceeded. Try again in ${display}.`
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
return {
|
|
28
|
+
allowed: true,
|
|
29
|
+
remaining: config.maxRequests - existing.count - 1,
|
|
30
|
+
resetAt: existing.windowStart + windowMs
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
async function recordRequest(kv, identifier, config) {
|
|
34
|
+
const prefix = config.keyPrefix ?? "rate-limit";
|
|
35
|
+
const key = `${prefix}:${identifier}`;
|
|
36
|
+
const windowMs = config.windowSeconds * 1e3;
|
|
37
|
+
const now = Date.now();
|
|
38
|
+
const existingData = await kv.get(key, "text");
|
|
39
|
+
const existing = existingData ? JSON.parse(existingData) : null;
|
|
40
|
+
let entry;
|
|
41
|
+
if (!existing || now - existing.windowStart >= windowMs) {
|
|
42
|
+
entry = { count: 1, windowStart: now };
|
|
43
|
+
} else {
|
|
44
|
+
entry = { count: existing.count + 1, windowStart: existing.windowStart };
|
|
45
|
+
}
|
|
46
|
+
await kv.put(key, JSON.stringify(entry), {
|
|
47
|
+
expirationTtl: config.kvTtlSeconds
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
async function resetRateLimit(kv, identifier, config) {
|
|
51
|
+
const prefix = config?.keyPrefix ?? "rate-limit";
|
|
52
|
+
const key = `${prefix}:${identifier}`;
|
|
53
|
+
await kv.delete(key);
|
|
54
|
+
}
|
|
55
|
+
export {
|
|
56
|
+
checkRateLimit,
|
|
57
|
+
recordRequest,
|
|
58
|
+
resetRateLimit
|
|
59
|
+
};
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import { Context } from 'hono';
|
|
2
|
+
import { HealthStatus } from './types.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Standard response helpers for Cloudflare Workers
|
|
6
|
+
*
|
|
7
|
+
* Consistent response formatting for:
|
|
8
|
+
* - Success responses
|
|
9
|
+
* - Error responses
|
|
10
|
+
* - Health checks
|
|
11
|
+
* - Not found responses
|
|
12
|
+
* - Validation errors
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* ```typescript
|
|
16
|
+
* import { ok, created, badRequest, notFound, healthCheck } from '../util';
|
|
17
|
+
*
|
|
18
|
+
* // Success response
|
|
19
|
+
* return ok(c, { tasks: [...] });
|
|
20
|
+
*
|
|
21
|
+
* // Created with custom message
|
|
22
|
+
* return created(c, { task }, 'Task created successfully');
|
|
23
|
+
*
|
|
24
|
+
* // Error response
|
|
25
|
+
* return badRequest(c, 'Invalid input');
|
|
26
|
+
*
|
|
27
|
+
* // Health check
|
|
28
|
+
* return healthCheck(c, 'task-api', { kv: true, db: false });
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Send a success response (200)
|
|
34
|
+
*
|
|
35
|
+
* @param c - Hono context
|
|
36
|
+
* @param data - Response data
|
|
37
|
+
* @param message - Optional success message
|
|
38
|
+
* @returns JSON response
|
|
39
|
+
*
|
|
40
|
+
* @example
|
|
41
|
+
* ```typescript
|
|
42
|
+
* return ok(c, { tasks: [...] });
|
|
43
|
+
* return ok(c, { count: 10 }, 'Retrieved successfully');
|
|
44
|
+
* ```
|
|
45
|
+
*/
|
|
46
|
+
declare function ok<T = any>(c: Context, data: T, message?: string): Response;
|
|
47
|
+
/**
|
|
48
|
+
* Send a created response (201)
|
|
49
|
+
*
|
|
50
|
+
* @param c - Hono context
|
|
51
|
+
* @param data - Created resource data
|
|
52
|
+
* @param message - Optional success message
|
|
53
|
+
* @returns JSON response
|
|
54
|
+
*
|
|
55
|
+
* @example
|
|
56
|
+
* ```typescript
|
|
57
|
+
* return created(c, { task: {...} });
|
|
58
|
+
* return created(c, { id: '123' }, 'Task created successfully');
|
|
59
|
+
* ```
|
|
60
|
+
*/
|
|
61
|
+
declare function created<T = any>(c: Context, data: T, message?: string): Response;
|
|
62
|
+
/**
|
|
63
|
+
* Send a bad request error (400)
|
|
64
|
+
*
|
|
65
|
+
* @param c - Hono context
|
|
66
|
+
* @param error - Error message
|
|
67
|
+
* @param details - Additional error details
|
|
68
|
+
* @returns JSON response
|
|
69
|
+
*
|
|
70
|
+
* @example
|
|
71
|
+
* ```typescript
|
|
72
|
+
* return badRequest(c, 'Missing required field: id');
|
|
73
|
+
* return badRequest(c, 'Validation failed', { errors: [...] });
|
|
74
|
+
* ```
|
|
75
|
+
*/
|
|
76
|
+
declare function badRequest(c: Context, error: string, details?: unknown): Response;
|
|
77
|
+
/**
|
|
78
|
+
* Send a not found error (404)
|
|
79
|
+
*
|
|
80
|
+
* @param c - Hono context
|
|
81
|
+
* @param resource - Resource type that wasn't found
|
|
82
|
+
* @returns JSON response
|
|
83
|
+
*
|
|
84
|
+
* @example
|
|
85
|
+
* ```typescript
|
|
86
|
+
* return notFound(c, 'Task');
|
|
87
|
+
* return notFound(c, 'Board');
|
|
88
|
+
* ```
|
|
89
|
+
*/
|
|
90
|
+
declare function notFound(c: Context, resource?: string): Response;
|
|
91
|
+
/**
|
|
92
|
+
* Send an internal server error (500)
|
|
93
|
+
*
|
|
94
|
+
* @param c - Hono context
|
|
95
|
+
* @param message - Error message
|
|
96
|
+
* @param details - Additional error details (omit in production)
|
|
97
|
+
* @returns JSON response
|
|
98
|
+
*
|
|
99
|
+
* @example
|
|
100
|
+
* ```typescript
|
|
101
|
+
* return serverError(c, 'Failed to process request');
|
|
102
|
+
* return serverError(c, 'Database error', { error: err.message });
|
|
103
|
+
* ```
|
|
104
|
+
*/
|
|
105
|
+
declare function serverError(c: Context, message?: string, details?: unknown): Response;
|
|
106
|
+
/**
|
|
107
|
+
* Send validation errors (400)
|
|
108
|
+
*
|
|
109
|
+
* @param c - Hono context
|
|
110
|
+
* @param errors - Array of validation errors
|
|
111
|
+
* @param message - Optional error message
|
|
112
|
+
* @returns JSON response
|
|
113
|
+
*
|
|
114
|
+
* @example
|
|
115
|
+
* ```typescript
|
|
116
|
+
* const validation = validateFields(body, rules);
|
|
117
|
+
* if (!validation.valid) {
|
|
118
|
+
* return validationError(c, validation.errors);
|
|
119
|
+
* }
|
|
120
|
+
* ```
|
|
121
|
+
*/
|
|
122
|
+
declare function validationError(c: Context, errors: {
|
|
123
|
+
field: string;
|
|
124
|
+
message: string;
|
|
125
|
+
}[], message?: string): Response;
|
|
126
|
+
/**
|
|
127
|
+
* Send a health check response
|
|
128
|
+
*
|
|
129
|
+
* @param c - Hono context
|
|
130
|
+
* @param serviceName - Name of the service
|
|
131
|
+
* @param checks - Optional health check results
|
|
132
|
+
* @param options - Additional options
|
|
133
|
+
* @returns JSON response
|
|
134
|
+
*
|
|
135
|
+
* @example
|
|
136
|
+
* ```typescript
|
|
137
|
+
* // Simple health check
|
|
138
|
+
* return healthCheck(c, 'task-api');
|
|
139
|
+
*
|
|
140
|
+
* // With dependency checks
|
|
141
|
+
* return healthCheck(c, 'task-api', {
|
|
142
|
+
* kv: await checkKV(),
|
|
143
|
+
* api: await checkAPI()
|
|
144
|
+
* });
|
|
145
|
+
*
|
|
146
|
+
* // Degraded status
|
|
147
|
+
* return healthCheck(c, 'task-api', { kv: false }, {
|
|
148
|
+
* status: 'degraded'
|
|
149
|
+
* });
|
|
150
|
+
* ```
|
|
151
|
+
*/
|
|
152
|
+
declare function healthCheck(c: Context, serviceName: string, checks?: Record<string, boolean | string>, options?: {
|
|
153
|
+
status?: HealthStatus;
|
|
154
|
+
version?: string;
|
|
155
|
+
remediation?: string;
|
|
156
|
+
gauges?: Record<string, number | null>;
|
|
157
|
+
}): Response;
|
|
158
|
+
|
|
159
|
+
export { badRequest, created, healthCheck, notFound, ok, serverError, validationError };
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
// responses.ts
|
|
2
|
+
function ok(c, data, message) {
|
|
3
|
+
const response = {
|
|
4
|
+
data,
|
|
5
|
+
...message && { message },
|
|
6
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
7
|
+
};
|
|
8
|
+
return c.json(response, 200);
|
|
9
|
+
}
|
|
10
|
+
function created(c, data, message) {
|
|
11
|
+
const response = {
|
|
12
|
+
data,
|
|
13
|
+
message: message || "Resource created successfully",
|
|
14
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
15
|
+
};
|
|
16
|
+
return c.json(response, 201);
|
|
17
|
+
}
|
|
18
|
+
function badRequest(c, error, details) {
|
|
19
|
+
const response = {
|
|
20
|
+
error,
|
|
21
|
+
...details !== void 0 ? { details } : {},
|
|
22
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
23
|
+
};
|
|
24
|
+
return c.json(response, 400);
|
|
25
|
+
}
|
|
26
|
+
function notFound(c, resource = "Resource") {
|
|
27
|
+
const response = {
|
|
28
|
+
error: `${resource} not found`,
|
|
29
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
30
|
+
};
|
|
31
|
+
return c.json(response, 404);
|
|
32
|
+
}
|
|
33
|
+
function serverError(c, message = "Internal server error", details) {
|
|
34
|
+
const response = {
|
|
35
|
+
error: message,
|
|
36
|
+
...details !== void 0 ? { details } : {},
|
|
37
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
38
|
+
};
|
|
39
|
+
return c.json(response, 500);
|
|
40
|
+
}
|
|
41
|
+
function validationError(c, errors, message = "Validation failed") {
|
|
42
|
+
const response = {
|
|
43
|
+
error: message,
|
|
44
|
+
details: { errors },
|
|
45
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
46
|
+
};
|
|
47
|
+
return c.json(response, 400);
|
|
48
|
+
}
|
|
49
|
+
function healthCheck(c, serviceName, checks, options = {}) {
|
|
50
|
+
let status = options.status || "ok";
|
|
51
|
+
if (checks && !options.status) {
|
|
52
|
+
const hasFailures = Object.values(checks).some((v) => v === false);
|
|
53
|
+
const allFailed = Object.values(checks).every((v) => v === false);
|
|
54
|
+
if (allFailed) {
|
|
55
|
+
status = "error";
|
|
56
|
+
} else if (hasFailures) {
|
|
57
|
+
status = "degraded";
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
const response = {
|
|
61
|
+
status,
|
|
62
|
+
service: serviceName,
|
|
63
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
64
|
+
...options.version && { version: options.version },
|
|
65
|
+
...checks && { checks },
|
|
66
|
+
...options.remediation && { remediation: options.remediation },
|
|
67
|
+
...options.gauges && { gauges: options.gauges }
|
|
68
|
+
};
|
|
69
|
+
const statusCode = status === "ok" ? 200 : 503;
|
|
70
|
+
return c.json(response, statusCode);
|
|
71
|
+
}
|
|
72
|
+
export {
|
|
73
|
+
badRequest,
|
|
74
|
+
created,
|
|
75
|
+
healthCheck,
|
|
76
|
+
notFound,
|
|
77
|
+
ok,
|
|
78
|
+
serverError,
|
|
79
|
+
validationError
|
|
80
|
+
};
|