@hadoku/worker-utils 0.0.0-stage → 1.6.19
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,108 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Data masking utilities for sensitive information logging
|
|
3
|
+
*
|
|
4
|
+
* Provides functions to safely mask keys, session IDs, and other
|
|
5
|
+
* sensitive data before logging to prevent exposure.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```typescript
|
|
9
|
+
* import { maskKey, maskSessionId } from '../util';
|
|
10
|
+
*
|
|
11
|
+
* const key = 'admin-secret-key-12345';
|
|
12
|
+
* console.log(maskKey(key)); // "admin-secre" (first 50%)
|
|
13
|
+
*
|
|
14
|
+
* const sessionId = '1a2b3c4d5e6f7g8h9i0j1k2l';
|
|
15
|
+
* console.log(maskSessionId(sessionId)); // "1a2b3c4d5e6f7g8h" (first 16 chars)
|
|
16
|
+
* ```
|
|
17
|
+
*/
|
|
18
|
+
/**
|
|
19
|
+
* Masking configuration constants
|
|
20
|
+
*/
|
|
21
|
+
declare const MASKING: {
|
|
22
|
+
/** Percentage of key to show (default: 50%) */
|
|
23
|
+
readonly KEY_PERCENTAGE: 0.5;
|
|
24
|
+
/** Minimum characters to show for keys */
|
|
25
|
+
readonly KEY_MIN_LENGTH: 4;
|
|
26
|
+
/** Number of characters to show for session IDs (default: 16) */
|
|
27
|
+
readonly SESSION_ID_PREFIX_LENGTH: 16;
|
|
28
|
+
};
|
|
29
|
+
/**
|
|
30
|
+
* Mask a key for logging (show first 50% of characters)
|
|
31
|
+
*
|
|
32
|
+
* Shows the first half of the key to allow identification while
|
|
33
|
+
* protecting the full key from exposure in logs/dashboards.
|
|
34
|
+
* No suffix is added - just the raw truncated key.
|
|
35
|
+
*
|
|
36
|
+
* @param key - Key to mask
|
|
37
|
+
* @param percentage - Percentage of key to show (default: 50%)
|
|
38
|
+
* @returns Masked key (e.g., "N7RZK2YW" for a 16-char key)
|
|
39
|
+
*
|
|
40
|
+
* @example
|
|
41
|
+
* ```typescript
|
|
42
|
+
* maskKey('N7RZK2YWABCD1234'); // "N7RZK2YW"
|
|
43
|
+
* maskKey('short'); // "sh"
|
|
44
|
+
* maskKey('ab'); // "ab"
|
|
45
|
+
* ```
|
|
46
|
+
*/
|
|
47
|
+
declare function maskKey(key: string, percentage?: number): string;
|
|
48
|
+
/**
|
|
49
|
+
* Mask a session ID for logging (show first 16 characters)
|
|
50
|
+
*
|
|
51
|
+
* @param id - Session ID to mask
|
|
52
|
+
* @returns Masked session ID (e.g., "1a2b3c4d5e6f7g8h")
|
|
53
|
+
*
|
|
54
|
+
* @example
|
|
55
|
+
* ```typescript
|
|
56
|
+
* maskSessionId('1a2b3c4d5e6f7g8h9i0j'); // "1a2b3c4d5e6f7g8h"
|
|
57
|
+
* maskSessionId('short-id'); // "short-id"
|
|
58
|
+
* ```
|
|
59
|
+
*/
|
|
60
|
+
declare function maskSessionId(id: string): string;
|
|
61
|
+
/**
|
|
62
|
+
* Mask an IP address for logging (show first 3 octets, replace last with *)
|
|
63
|
+
*
|
|
64
|
+
* @param ip - IP address to mask
|
|
65
|
+
* @returns Masked IP (e.g., "203.0.113.*")
|
|
66
|
+
*
|
|
67
|
+
* @example
|
|
68
|
+
* ```typescript
|
|
69
|
+
* maskIpAddress('203.0.113.45'); // "203.0.113.*"
|
|
70
|
+
* maskIpAddress('2001:db8::1'); // "2001:db8::*"
|
|
71
|
+
* maskIpAddress(null); // undefined
|
|
72
|
+
* ```
|
|
73
|
+
*/
|
|
74
|
+
declare function maskIpAddress(ip: string | null | undefined): string | undefined;
|
|
75
|
+
/**
|
|
76
|
+
* Mask an email address for logging (show first character and domain)
|
|
77
|
+
*
|
|
78
|
+
* @param email - Email to mask
|
|
79
|
+
* @returns Masked email (e.g., "j***@example.com")
|
|
80
|
+
*
|
|
81
|
+
* @example
|
|
82
|
+
* ```typescript
|
|
83
|
+
* maskEmail('john@example.com'); // "j***@example.com"
|
|
84
|
+
* maskEmail('invalid-email'); // "invalid-email"
|
|
85
|
+
* ```
|
|
86
|
+
*/
|
|
87
|
+
declare function maskEmail(email: string): string;
|
|
88
|
+
/**
|
|
89
|
+
* Redact multiple sensitive fields from an object
|
|
90
|
+
*
|
|
91
|
+
* @param obj - Object to redact
|
|
92
|
+
* @param fields - Field names to redact
|
|
93
|
+
* @returns New object with redacted fields
|
|
94
|
+
*
|
|
95
|
+
* @example
|
|
96
|
+
* ```typescript
|
|
97
|
+
* const data = { username: 'john', password: 'secret123', email: 'john@example.com' };
|
|
98
|
+
* redactFields(data, ['password']);
|
|
99
|
+
* // { username: 'john', password: '[REDACTED]', email: 'john@example.com' }
|
|
100
|
+
* ```
|
|
101
|
+
*/
|
|
102
|
+
declare function redactFields<T extends Record<string, any>>(obj: T, fields: string[]): T;
|
|
103
|
+
/**
|
|
104
|
+
* Common sensitive field names to redact
|
|
105
|
+
*/
|
|
106
|
+
declare const SENSITIVE_FIELDS: readonly ["password", "token", "secret", "apiKey", "api_key", "accessToken", "access_token", "refreshToken", "refresh_token", "privateKey", "private_key", "authKey", "auth_key"];
|
|
107
|
+
|
|
108
|
+
export { MASKING, SENSITIVE_FIELDS, maskEmail, maskIpAddress, maskKey, maskSessionId, redactFields };
|
package/dist/masking.js
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
// masking.ts
|
|
2
|
+
var MASKING = {
|
|
3
|
+
/** Percentage of key to show (default: 50%) */
|
|
4
|
+
KEY_PERCENTAGE: 0.5,
|
|
5
|
+
/** Minimum characters to show for keys */
|
|
6
|
+
KEY_MIN_LENGTH: 4,
|
|
7
|
+
/** Number of characters to show for session IDs (default: 16) */
|
|
8
|
+
SESSION_ID_PREFIX_LENGTH: 16
|
|
9
|
+
};
|
|
10
|
+
function maskKey(key, percentage = MASKING.KEY_PERCENTAGE) {
|
|
11
|
+
if (!key) {
|
|
12
|
+
return key;
|
|
13
|
+
}
|
|
14
|
+
const showLength = Math.max(MASKING.KEY_MIN_LENGTH, Math.floor(key.length * percentage));
|
|
15
|
+
if (key.length <= showLength) {
|
|
16
|
+
return key;
|
|
17
|
+
}
|
|
18
|
+
return key.substring(0, showLength);
|
|
19
|
+
}
|
|
20
|
+
function maskSessionId(id) {
|
|
21
|
+
if (!id) {
|
|
22
|
+
return id;
|
|
23
|
+
}
|
|
24
|
+
const length = MASKING.SESSION_ID_PREFIX_LENGTH;
|
|
25
|
+
if (id.length <= length) {
|
|
26
|
+
return id;
|
|
27
|
+
}
|
|
28
|
+
return id.substring(0, length);
|
|
29
|
+
}
|
|
30
|
+
function maskIpAddress(ip) {
|
|
31
|
+
if (!ip) {
|
|
32
|
+
return void 0;
|
|
33
|
+
}
|
|
34
|
+
if (ip.includes(".")) {
|
|
35
|
+
const parts = ip.split(".");
|
|
36
|
+
if (parts.length === 4) {
|
|
37
|
+
return `${parts[0]}.${parts[1]}.${parts[2]}.*`;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
if (ip.includes(":")) {
|
|
41
|
+
const lastColon = ip.lastIndexOf(":");
|
|
42
|
+
if (lastColon > 0) {
|
|
43
|
+
return ip.substring(0, lastColon + 1) + "*";
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return ip;
|
|
47
|
+
}
|
|
48
|
+
function maskEmail(email) {
|
|
49
|
+
if (!email?.includes("@")) {
|
|
50
|
+
return email;
|
|
51
|
+
}
|
|
52
|
+
const [username, domain] = email.split("@");
|
|
53
|
+
if (!username || !domain) {
|
|
54
|
+
return email;
|
|
55
|
+
}
|
|
56
|
+
return `${username[0]}***@${domain}`;
|
|
57
|
+
}
|
|
58
|
+
function redactFields(obj, fields) {
|
|
59
|
+
const redacted = { ...obj };
|
|
60
|
+
for (const field of fields) {
|
|
61
|
+
if (field in redacted) {
|
|
62
|
+
redacted[field] = "[REDACTED]";
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return redacted;
|
|
66
|
+
}
|
|
67
|
+
var SENSITIVE_FIELDS = [
|
|
68
|
+
"password",
|
|
69
|
+
"token",
|
|
70
|
+
"secret",
|
|
71
|
+
"apiKey",
|
|
72
|
+
"api_key",
|
|
73
|
+
"accessToken",
|
|
74
|
+
"access_token",
|
|
75
|
+
"refreshToken",
|
|
76
|
+
"refresh_token",
|
|
77
|
+
"privateKey",
|
|
78
|
+
"private_key",
|
|
79
|
+
"authKey",
|
|
80
|
+
"auth_key"
|
|
81
|
+
];
|
|
82
|
+
export {
|
|
83
|
+
MASKING,
|
|
84
|
+
SENSITIVE_FIELDS,
|
|
85
|
+
maskEmail,
|
|
86
|
+
maskIpAddress,
|
|
87
|
+
maskKey,
|
|
88
|
+
maskSessionId,
|
|
89
|
+
redactFields
|
|
90
|
+
};
|
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
import * as hono_utils_types from 'hono/utils/types';
|
|
2
|
+
import * as hono from 'hono';
|
|
3
|
+
import { Context } from 'hono';
|
|
4
|
+
|
|
5
|
+
interface ValidationHookResult {
|
|
6
|
+
success: false;
|
|
7
|
+
error: {
|
|
8
|
+
issues: {
|
|
9
|
+
path: PropertyKey[];
|
|
10
|
+
message: string;
|
|
11
|
+
}[];
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Error response format type
|
|
16
|
+
* - 'simple': { error, message } - Used by task-api
|
|
17
|
+
* - 'wrapped': { success: false, error, message } - Used by prompt-api, oss-recon-api
|
|
18
|
+
*/
|
|
19
|
+
type ErrorResponseFormat = 'simple' | 'wrapped';
|
|
20
|
+
/**
|
|
21
|
+
* Creates a validation hook for OpenAPIHono that formats Zod validation errors.
|
|
22
|
+
*
|
|
23
|
+
* @param format - The error response format to use
|
|
24
|
+
* @returns A defaultHook function for OpenAPIHono
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* ```typescript
|
|
28
|
+
* const app = new OpenAPIHono({
|
|
29
|
+
* defaultHook: createValidationHook('simple'),
|
|
30
|
+
* });
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
declare function createValidationHook(format?: ErrorResponseFormat): (result: ValidationHookResult | {
|
|
34
|
+
success: true;
|
|
35
|
+
}, c: Context) => (Response & hono.TypedResponse<{
|
|
36
|
+
error: string;
|
|
37
|
+
message: string;
|
|
38
|
+
}, 400, "json">) | undefined;
|
|
39
|
+
/**
|
|
40
|
+
* Pre-built validation hook for simple error format (task-api style)
|
|
41
|
+
*/
|
|
42
|
+
declare const simpleValidationHook: (result: ValidationHookResult | {
|
|
43
|
+
success: true;
|
|
44
|
+
}, c: Context) => (Response & hono.TypedResponse<{
|
|
45
|
+
error: string;
|
|
46
|
+
message: string;
|
|
47
|
+
}, 400, "json">) | undefined;
|
|
48
|
+
/**
|
|
49
|
+
* Pre-built validation hook for wrapped error format (prompt-api style)
|
|
50
|
+
*/
|
|
51
|
+
declare const wrappedValidationHook: (result: ValidationHookResult | {
|
|
52
|
+
success: true;
|
|
53
|
+
}, c: Context) => (Response & hono.TypedResponse<{
|
|
54
|
+
error: string;
|
|
55
|
+
message: string;
|
|
56
|
+
}, 400, "json">) | undefined;
|
|
57
|
+
/**
|
|
58
|
+
* Creates 404 and error handlers with consistent response format.
|
|
59
|
+
*
|
|
60
|
+
* @param format - The error response format to use
|
|
61
|
+
* @returns Object with notFoundHandler and errorHandler functions
|
|
62
|
+
*
|
|
63
|
+
* @example
|
|
64
|
+
* ```typescript
|
|
65
|
+
* const { notFoundHandler, errorHandler } = createErrorHandlers('wrapped');
|
|
66
|
+
* app.notFound(notFoundHandler);
|
|
67
|
+
* app.onError(errorHandler);
|
|
68
|
+
* ```
|
|
69
|
+
*/
|
|
70
|
+
declare function createErrorHandlers(format?: ErrorResponseFormat): {
|
|
71
|
+
notFoundHandler: (c: Context) => Response & hono.TypedResponse<{
|
|
72
|
+
error: string;
|
|
73
|
+
message: string;
|
|
74
|
+
}, 404, "json">;
|
|
75
|
+
errorHandler: (err: Error, c: Context) => Response & hono.TypedResponse<{
|
|
76
|
+
error: string;
|
|
77
|
+
message: string;
|
|
78
|
+
}, 500, "json">;
|
|
79
|
+
};
|
|
80
|
+
interface OpenAPIDocOptions {
|
|
81
|
+
/** API title */
|
|
82
|
+
title: string;
|
|
83
|
+
/** API version */
|
|
84
|
+
version: string;
|
|
85
|
+
/** API description (supports markdown) */
|
|
86
|
+
description: string;
|
|
87
|
+
/** Base path for production URL (e.g., '/api' or '/task/api') */
|
|
88
|
+
basePath?: string;
|
|
89
|
+
/** Production server URL */
|
|
90
|
+
production: string;
|
|
91
|
+
/** Local development port (default: 8787) */
|
|
92
|
+
localPort?: number;
|
|
93
|
+
/** OpenAPI tags */
|
|
94
|
+
tags?: {
|
|
95
|
+
name: string;
|
|
96
|
+
description: string;
|
|
97
|
+
}[];
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Creates a standardized OpenAPI doc configuration.
|
|
101
|
+
*
|
|
102
|
+
* @param options - Configuration options
|
|
103
|
+
* @returns OpenAPI doc configuration object for app.doc()
|
|
104
|
+
*
|
|
105
|
+
* @example
|
|
106
|
+
* ```typescript
|
|
107
|
+
* app.doc('/api/openapi.json', createOpenAPIDocConfig({
|
|
108
|
+
* title: 'Task API',
|
|
109
|
+
* version: '1.0.0',
|
|
110
|
+
* description: 'API for managing tasks',
|
|
111
|
+
* production: 'https://task-api.hadoku.workers.dev',
|
|
112
|
+
* tags: [
|
|
113
|
+
* { name: 'Tasks', description: 'Task operations' },
|
|
114
|
+
* ],
|
|
115
|
+
* }));
|
|
116
|
+
* ```
|
|
117
|
+
*/
|
|
118
|
+
declare function createOpenAPIDocConfig(options: OpenAPIDocOptions): {
|
|
119
|
+
openapi: "3.0.0";
|
|
120
|
+
info: {
|
|
121
|
+
title: string;
|
|
122
|
+
version: string;
|
|
123
|
+
description: string;
|
|
124
|
+
};
|
|
125
|
+
servers: {
|
|
126
|
+
url: string;
|
|
127
|
+
description: string;
|
|
128
|
+
}[];
|
|
129
|
+
tags: {
|
|
130
|
+
name: string;
|
|
131
|
+
description: string;
|
|
132
|
+
}[];
|
|
133
|
+
};
|
|
134
|
+
/**
|
|
135
|
+
* Common OpenAPI tags used across multiple APIs
|
|
136
|
+
*/
|
|
137
|
+
declare const CommonTags: {
|
|
138
|
+
readonly Health: {
|
|
139
|
+
readonly name: "Health";
|
|
140
|
+
readonly description: "Health check endpoints";
|
|
141
|
+
};
|
|
142
|
+
readonly Admin: {
|
|
143
|
+
readonly name: "Admin";
|
|
144
|
+
readonly description: "Admin operations (requires authentication)";
|
|
145
|
+
};
|
|
146
|
+
};
|
|
147
|
+
/**
|
|
148
|
+
* Success response helper with wrapped format { success: true, data }
|
|
149
|
+
* Used by: prompt-api, oss-recon-api
|
|
150
|
+
*
|
|
151
|
+
* @example
|
|
152
|
+
* ```typescript
|
|
153
|
+
* return okWrapped(c, { categories: [...] });
|
|
154
|
+
* ```
|
|
155
|
+
*/
|
|
156
|
+
declare function okWrapped<T>(c: Context, data: T): Response & hono.TypedResponse<true | T extends bigint | readonly bigint[] ? never : { [K in keyof {
|
|
157
|
+
success: true;
|
|
158
|
+
data: T;
|
|
159
|
+
} as ({
|
|
160
|
+
success: true;
|
|
161
|
+
data: T;
|
|
162
|
+
}[K] extends infer T_1 ? T_1 extends {
|
|
163
|
+
success: true;
|
|
164
|
+
data: T;
|
|
165
|
+
}[K] ? T_1 extends hono_utils_types.InvalidJSONValue ? true : false : never : never) extends true ? never : K]: boolean extends ({
|
|
166
|
+
success: true;
|
|
167
|
+
data: T;
|
|
168
|
+
}[K] extends infer T_2 ? T_2 extends {
|
|
169
|
+
success: true;
|
|
170
|
+
data: T;
|
|
171
|
+
}[K] ? T_2 extends hono_utils_types.InvalidJSONValue ? true : false : never : never) ? hono_utils_types.JSONParsed<{
|
|
172
|
+
success: true;
|
|
173
|
+
data: T;
|
|
174
|
+
}[K], bigint | readonly bigint[]> | undefined : hono_utils_types.JSONParsed<{
|
|
175
|
+
success: true;
|
|
176
|
+
data: T;
|
|
177
|
+
}[K], bigint | readonly bigint[]>; }, 200, "json">;
|
|
178
|
+
/**
|
|
179
|
+
* Created response helper with wrapped format { success: true, data }
|
|
180
|
+
* Used by: prompt-api admin routes
|
|
181
|
+
*
|
|
182
|
+
* @example
|
|
183
|
+
* ```typescript
|
|
184
|
+
* return createdWrapped(c, { category });
|
|
185
|
+
* ```
|
|
186
|
+
*/
|
|
187
|
+
declare function createdWrapped<T>(c: Context, data: T): Response & hono.TypedResponse<true | T extends bigint | readonly bigint[] ? never : { [K in keyof {
|
|
188
|
+
success: true;
|
|
189
|
+
data: T;
|
|
190
|
+
} as ({
|
|
191
|
+
success: true;
|
|
192
|
+
data: T;
|
|
193
|
+
}[K] extends infer T_1 ? T_1 extends {
|
|
194
|
+
success: true;
|
|
195
|
+
data: T;
|
|
196
|
+
}[K] ? T_1 extends hono_utils_types.InvalidJSONValue ? true : false : never : never) extends true ? never : K]: boolean extends ({
|
|
197
|
+
success: true;
|
|
198
|
+
data: T;
|
|
199
|
+
}[K] extends infer T_2 ? T_2 extends {
|
|
200
|
+
success: true;
|
|
201
|
+
data: T;
|
|
202
|
+
}[K] ? T_2 extends hono_utils_types.InvalidJSONValue ? true : false : never : never) ? hono_utils_types.JSONParsed<{
|
|
203
|
+
success: true;
|
|
204
|
+
data: T;
|
|
205
|
+
}[K], bigint | readonly bigint[]> | undefined : hono_utils_types.JSONParsed<{
|
|
206
|
+
success: true;
|
|
207
|
+
data: T;
|
|
208
|
+
}[K], bigint | readonly bigint[]>; }, 201, "json">;
|
|
209
|
+
/**
|
|
210
|
+
* Bad request response helper with wrapped format { success: false, error, message }
|
|
211
|
+
* Use in openapi() handlers where the route declares DetailedErrorResponseSchema for 400.
|
|
212
|
+
*
|
|
213
|
+
* @example
|
|
214
|
+
* ```typescript
|
|
215
|
+
* return badRequestWrapped(c, 'Missing required field: id');
|
|
216
|
+
* ```
|
|
217
|
+
*/
|
|
218
|
+
declare function badRequestWrapped(c: Context, message: string): Response & hono.TypedResponse<{
|
|
219
|
+
success: false;
|
|
220
|
+
error: string;
|
|
221
|
+
message: string;
|
|
222
|
+
}, 400, "json">;
|
|
223
|
+
/**
|
|
224
|
+
* Not found response helper with wrapped format { success: false, error, message }
|
|
225
|
+
* Use in openapi() handlers where the route declares DetailedErrorResponseSchema for 404.
|
|
226
|
+
*
|
|
227
|
+
* @example
|
|
228
|
+
* ```typescript
|
|
229
|
+
* return notFoundWrapped(c, 'Category');
|
|
230
|
+
* ```
|
|
231
|
+
*/
|
|
232
|
+
declare function notFoundWrapped(c: Context, resource?: string): Response & hono.TypedResponse<{
|
|
233
|
+
success: false;
|
|
234
|
+
error: string;
|
|
235
|
+
message: string;
|
|
236
|
+
}, 404, "json">;
|
|
237
|
+
/**
|
|
238
|
+
* Server error response helper with wrapped format { success: false, error, message }
|
|
239
|
+
* Use in openapi() handlers where the route declares DetailedErrorResponseSchema for 500.
|
|
240
|
+
*
|
|
241
|
+
* @example
|
|
242
|
+
* ```typescript
|
|
243
|
+
* return serverErrorWrapped(c, 'Failed to fetch categories');
|
|
244
|
+
* ```
|
|
245
|
+
*/
|
|
246
|
+
declare function serverErrorWrapped(c: Context, message?: string): Response & hono.TypedResponse<{
|
|
247
|
+
success: false;
|
|
248
|
+
error: string;
|
|
249
|
+
message: string;
|
|
250
|
+
}, 500, "json">;
|
|
251
|
+
|
|
252
|
+
export { CommonTags, type ErrorResponseFormat, badRequestWrapped, createErrorHandlers, createOpenAPIDocConfig, createValidationHook, createdWrapped, notFoundWrapped, okWrapped, serverErrorWrapped, simpleValidationHook, wrappedValidationHook };
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
// openapi-config.ts
|
|
2
|
+
function createValidationHook(format = "simple") {
|
|
3
|
+
return (result, c) => {
|
|
4
|
+
if (!result.success) {
|
|
5
|
+
const issues = result.error.issues;
|
|
6
|
+
const message = issues.map((i) => `${i.path.join(".")}: ${i.message}`).join(", ");
|
|
7
|
+
if (format === "wrapped") {
|
|
8
|
+
return c.json(
|
|
9
|
+
{
|
|
10
|
+
success: false,
|
|
11
|
+
error: "Validation Error",
|
|
12
|
+
message
|
|
13
|
+
},
|
|
14
|
+
400
|
|
15
|
+
);
|
|
16
|
+
}
|
|
17
|
+
return c.json(
|
|
18
|
+
{
|
|
19
|
+
error: "Validation Error",
|
|
20
|
+
message
|
|
21
|
+
},
|
|
22
|
+
400
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
var simpleValidationHook = createValidationHook("simple");
|
|
28
|
+
var wrappedValidationHook = createValidationHook("wrapped");
|
|
29
|
+
function createErrorHandlers(format = "simple") {
|
|
30
|
+
const notFoundHandler = (c) => {
|
|
31
|
+
if (format === "wrapped") {
|
|
32
|
+
return c.json(
|
|
33
|
+
{
|
|
34
|
+
success: false,
|
|
35
|
+
error: "Not found",
|
|
36
|
+
message: "The requested endpoint does not exist"
|
|
37
|
+
},
|
|
38
|
+
404
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
return c.json(
|
|
42
|
+
{
|
|
43
|
+
error: "Not found",
|
|
44
|
+
message: "The requested endpoint does not exist"
|
|
45
|
+
},
|
|
46
|
+
404
|
|
47
|
+
);
|
|
48
|
+
};
|
|
49
|
+
const errorHandler = (err, c) => {
|
|
50
|
+
console.error("Unhandled error:", err);
|
|
51
|
+
if (format === "wrapped") {
|
|
52
|
+
return c.json(
|
|
53
|
+
{
|
|
54
|
+
success: false,
|
|
55
|
+
error: "Internal server error",
|
|
56
|
+
message: "An unexpected error occurred"
|
|
57
|
+
},
|
|
58
|
+
500
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
return c.json(
|
|
62
|
+
{
|
|
63
|
+
error: "Internal server error",
|
|
64
|
+
message: "An unexpected error occurred"
|
|
65
|
+
},
|
|
66
|
+
500
|
|
67
|
+
);
|
|
68
|
+
};
|
|
69
|
+
return { notFoundHandler, errorHandler };
|
|
70
|
+
}
|
|
71
|
+
function createOpenAPIDocConfig(options) {
|
|
72
|
+
const { title, version, description, production, localPort = 8787, tags = [] } = options;
|
|
73
|
+
return {
|
|
74
|
+
openapi: "3.0.0",
|
|
75
|
+
info: {
|
|
76
|
+
title,
|
|
77
|
+
version,
|
|
78
|
+
description: description.trim()
|
|
79
|
+
},
|
|
80
|
+
servers: [
|
|
81
|
+
{
|
|
82
|
+
url: production,
|
|
83
|
+
description: "Production"
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
url: `http://localhost:${localPort}`,
|
|
87
|
+
description: "Local development"
|
|
88
|
+
}
|
|
89
|
+
],
|
|
90
|
+
tags
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
var CommonTags = {
|
|
94
|
+
Health: { name: "Health", description: "Health check endpoints" },
|
|
95
|
+
Admin: { name: "Admin", description: "Admin operations (requires authentication)" }
|
|
96
|
+
};
|
|
97
|
+
function okWrapped(c, data) {
|
|
98
|
+
return c.json({ success: true, data }, 200);
|
|
99
|
+
}
|
|
100
|
+
function createdWrapped(c, data) {
|
|
101
|
+
return c.json({ success: true, data }, 201);
|
|
102
|
+
}
|
|
103
|
+
function badRequestWrapped(c, message) {
|
|
104
|
+
return c.json({ success: false, error: "Bad Request", message }, 400);
|
|
105
|
+
}
|
|
106
|
+
function notFoundWrapped(c, resource = "Resource") {
|
|
107
|
+
return c.json(
|
|
108
|
+
{
|
|
109
|
+
success: false,
|
|
110
|
+
error: "Not Found",
|
|
111
|
+
message: `${resource} not found`
|
|
112
|
+
},
|
|
113
|
+
404
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
function serverErrorWrapped(c, message = "Internal server error") {
|
|
117
|
+
return c.json({ success: false, error: "Internal Server Error", message }, 500);
|
|
118
|
+
}
|
|
119
|
+
export {
|
|
120
|
+
CommonTags,
|
|
121
|
+
badRequestWrapped,
|
|
122
|
+
createErrorHandlers,
|
|
123
|
+
createOpenAPIDocConfig,
|
|
124
|
+
createValidationHook,
|
|
125
|
+
createdWrapped,
|
|
126
|
+
notFoundWrapped,
|
|
127
|
+
okWrapped,
|
|
128
|
+
serverErrorWrapped,
|
|
129
|
+
simpleValidationHook,
|
|
130
|
+
wrappedValidationHook
|
|
131
|
+
};
|