@rdlabo/workers-hono-kit 0.4.3 → 0.5.1
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/README.md +21 -1
- package/dist/http/mysql-driver-error.d.ts +25 -0
- package/dist/http/mysql-driver-error.js +35 -0
- package/dist/http/nest-error.js +7 -1
- package/dist/http/query-failed-error.d.ts +29 -0
- package/dist/http/query-failed-error.js +43 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +2 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -65,7 +65,9 @@ npm install ai ai-gateway-provider # createAiGatewayProvider
|
|
|
65
65
|
| `getAppInfo(c)` / `AppInfo` | Read `x-amz-meta-version` / `x-amz-meta-uuid`. |
|
|
66
66
|
| `resolveAppEnv(env)` / `isProductionEnv(env)` / `AppEnv` | Resolve `'development'` / `'production'` from `env.APP_ENV` (defaults to `'production'` for safety). |
|
|
67
67
|
| `HttpStatus` | HTTP status enum identical to NestJS `@nestjs/common`. |
|
|
68
|
-
| `createNestErrorHandler(options?)` / `NestErrorHandlerOptions` | `app.onError()` handler that maps a thrown `HTTPException` to the NestJS exception-filter body (`{ statusCode, message, error? }`; `401` omits `error`). Configurable field order, reason phrases, error predicate, and unhandled-error report hook. |
|
|
68
|
+
| `createNestErrorHandler(options?)` / `NestErrorHandlerOptions` | `app.onError()` handler that maps a thrown `HTTPException` to the NestJS exception-filter body (`{ statusCode, message, error? }`; `401` omits `error`). Configurable field order, reason phrases, error predicate, and unhandled-error report hook. Unhandled errors log via `console.error` (mysql2 errors include `sqlMessage` / `errno` when detectable). |
|
|
69
|
+
| `createQueryFailedNestErrorHandler(options)` / `QueryFailedClassifier` / `ClassifiedDbError` | Compose a NestJS `QueryFailedExceptionFilter` analog **before** `createNestErrorHandler`: consumer supplies `classify(err)` (parity-critical messages stay in the app); classified DB errors log (`warn` for 400, `error` for 500) and trigger `onUnhandledError` on 500 only. |
|
|
70
|
+
| `findMysqlDriverError(err)` / `logMysqlDriverError(err, statusCode)` / `reportClassifiedDbError(...)` | Low-level mysql2 driver-error detection (follows `err.cause`), structured logging, and classify-path reporting helpers. |
|
|
69
71
|
| `nestNotFoundHandler(c)` | `app.notFound()` handler with the Express/Nest default `{ message: 'Cannot METHOD path', error, statusCode }` 404 body. |
|
|
70
72
|
| `normalizeTrailingSlash(request)` | Strip trailing slash(es) from the request URL before routing (Express/Nest parity). Does **not** 301-redirect — preserves POST/PUT/DELETE bodies. |
|
|
71
73
|
| `NEST_REASON_PHRASES` | `{ 400, 401, 403, 404 }` → NestJS reason phrases. |
|
|
@@ -298,6 +300,24 @@ app.onError(
|
|
|
298
300
|
);
|
|
299
301
|
```
|
|
300
302
|
|
|
303
|
+
**Important:** `Sentry.withSentry` does **not** capture errors handled by `app.onError`. Wire
|
|
304
|
+
`onUnhandledError` → `Sentry.captureException` explicitly (mirrors Nest `SentryGlobalFilter`).
|
|
305
|
+
|
|
306
|
+
Repos with a Nest `QueryFailedExceptionFilter` (e.g. odss-mobile) should use
|
|
307
|
+
`createQueryFailedNestErrorHandler` so classified DB errors still log and report to Sentry:
|
|
308
|
+
|
|
309
|
+
```ts
|
|
310
|
+
import { createQueryFailedNestErrorHandler } from '@rdlabo/workers-hono-kit';
|
|
311
|
+
|
|
312
|
+
app.onError(
|
|
313
|
+
createQueryFailedNestErrorHandler({
|
|
314
|
+
fieldOrder: 'message-first',
|
|
315
|
+
classify: classifyQueryFailed, // app-local parity (Japanese messages, errno rules)
|
|
316
|
+
onUnhandledError: (err, c) => container.reportError?.(err, { requestId: c.get('requestId') }),
|
|
317
|
+
}),
|
|
318
|
+
);
|
|
319
|
+
```
|
|
320
|
+
|
|
301
321
|
### Auth middleware
|
|
302
322
|
|
|
303
323
|
Encodes the shared skeleton (read token header → verify → `getAppInfo` → resolve user id →
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* mysql2 driver error shape (= TypeORM QueryFailedError.driverError 相当).
|
|
3
|
+
* Drizzle / Error.cause で wrap される場合があるため cause も再帰探索する。
|
|
4
|
+
*/
|
|
5
|
+
export interface MysqlDriverErrorLike {
|
|
6
|
+
errno?: number;
|
|
7
|
+
sqlMessage?: string;
|
|
8
|
+
sqlState?: string;
|
|
9
|
+
message?: string;
|
|
10
|
+
sql?: string;
|
|
11
|
+
code?: string;
|
|
12
|
+
cause?: unknown;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* err 本体 → `err.cause` を再帰的に辿り、mysql2 ドライバエラーを取り出す。
|
|
16
|
+
*
|
|
17
|
+
* @param err - handler / onError に渡された thrown value。
|
|
18
|
+
* @param seen - 循環参照防止(内部用)。
|
|
19
|
+
*/
|
|
20
|
+
export declare function findMysqlDriverError(err: unknown, seen?: Set<unknown>): MysqlDriverErrorLike | null;
|
|
21
|
+
/**
|
|
22
|
+
* Nest QueryFailedExceptionFilter の logger.error / logger.warn 相当。
|
|
23
|
+
* レスポンス body には載せず、Workers Logs 用に errno / sqlMessage / sql を残す。
|
|
24
|
+
*/
|
|
25
|
+
export declare function logMysqlDriverError(err: unknown, statusCode: number): void;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* err 本体 → `err.cause` を再帰的に辿り、mysql2 ドライバエラーを取り出す。
|
|
3
|
+
*
|
|
4
|
+
* @param err - handler / onError に渡された thrown value。
|
|
5
|
+
* @param seen - 循環参照防止(内部用)。
|
|
6
|
+
*/
|
|
7
|
+
export function findMysqlDriverError(err, seen = new Set()) {
|
|
8
|
+
if (err === null || err === undefined || seen.has(err)) {
|
|
9
|
+
return null;
|
|
10
|
+
}
|
|
11
|
+
seen.add(err);
|
|
12
|
+
const e = err;
|
|
13
|
+
if (typeof e.errno === 'number' && (typeof e.sqlMessage === 'string' || typeof e.sqlState === 'string')) {
|
|
14
|
+
return e;
|
|
15
|
+
}
|
|
16
|
+
if (e.cause !== undefined) {
|
|
17
|
+
return findMysqlDriverError(e.cause, seen);
|
|
18
|
+
}
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Nest QueryFailedExceptionFilter の logger.error / logger.warn 相当。
|
|
23
|
+
* レスポンス body には載せず、Workers Logs 用に errno / sqlMessage / sql を残す。
|
|
24
|
+
*/
|
|
25
|
+
export function logMysqlDriverError(err, statusCode) {
|
|
26
|
+
const driver = findMysqlDriverError(err);
|
|
27
|
+
const rawMessage = driver?.sqlMessage ?? driver?.message ?? (err instanceof Error ? err.message : String(err));
|
|
28
|
+
const detail = driver === null ? undefined : { errno: driver.errno, sql: driver.sql, code: driver.code };
|
|
29
|
+
const line = `QueryFailedError (${statusCode}): ${rawMessage}`;
|
|
30
|
+
if (statusCode >= 500) {
|
|
31
|
+
console.error(line, detail);
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
console.warn(line, detail);
|
|
35
|
+
}
|
package/dist/http/nest-error.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { findMysqlDriverError, logMysqlDriverError } from './mysql-driver-error.js';
|
|
1
2
|
/**
|
|
2
3
|
* Reason phrases attached by the NestJS default exception filter, keyed by HTTP status code.
|
|
3
4
|
*
|
|
@@ -82,7 +83,12 @@ export function createNestErrorHandler(options = {}) {
|
|
|
82
83
|
catch {
|
|
83
84
|
// Reporting must never change the behavior of the error response.
|
|
84
85
|
}
|
|
85
|
-
|
|
86
|
+
if (findMysqlDriverError(err)) {
|
|
87
|
+
logMysqlDriverError(err, 500);
|
|
88
|
+
}
|
|
89
|
+
else {
|
|
90
|
+
console.error(err);
|
|
91
|
+
}
|
|
86
92
|
return c.json(internalServerErrorBody, 500);
|
|
87
93
|
};
|
|
88
94
|
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { Context, Env } from 'hono';
|
|
2
|
+
import type { ErrorReporter, NestErrorHandlerOptions } from './nest-error.js';
|
|
3
|
+
/** Nest QueryFailedExceptionFilter が返す `{ statusCode, message }` 形(error フィールド無し)。 */
|
|
4
|
+
export interface ClassifiedDbError {
|
|
5
|
+
statusCode: 400 | 500;
|
|
6
|
+
message: string;
|
|
7
|
+
}
|
|
8
|
+
/** mysql2 / Drizzle 由来の DB エラーを HTTP 応答用に分類する。非 DB エラーは null。 */
|
|
9
|
+
export type QueryFailedClassifier = (err: unknown) => ClassifiedDbError | null;
|
|
10
|
+
/**
|
|
11
|
+
* 分類済み DB エラーをログし、500 のみ {@link ErrorReporter} へ通報する。
|
|
12
|
+
* 400 はビジネスエラー扱い(warn ログのみ、Sentry 不要)。
|
|
13
|
+
*/
|
|
14
|
+
export declare function reportClassifiedDbError(err: unknown, classified: ClassifiedDbError, reportError?: ErrorReporter, requestId?: string): void;
|
|
15
|
+
export interface QueryFailedNestErrorHandlerOptions<E extends Env = Env> extends NestErrorHandlerOptions<E> {
|
|
16
|
+
/** アプリ固有の分類(parity-critical な日本語メッセージ等は consumer 側で定義)。 */
|
|
17
|
+
classify: QueryFailedClassifier;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* QueryFailedExceptionFilter → Nest 既定 exception filter の合成 onError。
|
|
21
|
+
*
|
|
22
|
+
* @remarks
|
|
23
|
+
* classify が non-null のときは parity 用 body を返しつつログ(+ 500 は onUnhandledError)を残す。
|
|
24
|
+
* 非 DB エラーは {@link createNestErrorHandler} に委譲する。
|
|
25
|
+
*
|
|
26
|
+
* `Sentry.withSentry` だけでは onError 握りエラーは capture されないため、
|
|
27
|
+
* `onUnhandledError: (err, c) => container.reportError?.(err, { requestId: c.get('requestId') })` を必ず配線する。
|
|
28
|
+
*/
|
|
29
|
+
export declare function createQueryFailedNestErrorHandler<E extends Env = Env>(options: QueryFailedNestErrorHandlerOptions<E>): (err: Error, c: Context<E>) => Response;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { logMysqlDriverError } from './mysql-driver-error.js';
|
|
2
|
+
import { createNestErrorHandler } from './nest-error.js';
|
|
3
|
+
/**
|
|
4
|
+
* 分類済み DB エラーをログし、500 のみ {@link ErrorReporter} へ通報する。
|
|
5
|
+
* 400 はビジネスエラー扱い(warn ログのみ、Sentry 不要)。
|
|
6
|
+
*/
|
|
7
|
+
export function reportClassifiedDbError(err, classified, reportError, requestId) {
|
|
8
|
+
logMysqlDriverError(err, classified.statusCode);
|
|
9
|
+
if (classified.statusCode === 500) {
|
|
10
|
+
reportError?.(err, { requestId });
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* QueryFailedExceptionFilter → Nest 既定 exception filter の合成 onError。
|
|
15
|
+
*
|
|
16
|
+
* @remarks
|
|
17
|
+
* classify が non-null のときは parity 用 body を返しつつログ(+ 500 は onUnhandledError)を残す。
|
|
18
|
+
* 非 DB エラーは {@link createNestErrorHandler} に委譲する。
|
|
19
|
+
*
|
|
20
|
+
* `Sentry.withSentry` だけでは onError 握りエラーは capture されないため、
|
|
21
|
+
* `onUnhandledError: (err, c) => container.reportError?.(err, { requestId: c.get('requestId') })` を必ず配線する。
|
|
22
|
+
*/
|
|
23
|
+
export function createQueryFailedNestErrorHandler(options) {
|
|
24
|
+
const { classify, ...nestOptions } = options;
|
|
25
|
+
const nestErrorHandler = createNestErrorHandler(nestOptions);
|
|
26
|
+
const { onUnhandledError } = nestOptions;
|
|
27
|
+
return (err, c) => {
|
|
28
|
+
const classified = classify(err);
|
|
29
|
+
if (classified) {
|
|
30
|
+
logMysqlDriverError(err, classified.statusCode);
|
|
31
|
+
if (classified.statusCode === 500) {
|
|
32
|
+
try {
|
|
33
|
+
onUnhandledError?.(err, c);
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
// Reporting must never change the error response.
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return c.json({ statusCode: classified.statusCode, message: classified.message }, classified.statusCode);
|
|
40
|
+
}
|
|
41
|
+
return nestErrorHandler(err, c);
|
|
42
|
+
};
|
|
43
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -26,6 +26,10 @@ export type { AppEnv } from './http/app-env.js';
|
|
|
26
26
|
export { HttpStatus } from './http/http-status.js';
|
|
27
27
|
export { createNestErrorHandler, nestNotFoundHandler, NEST_REASON_PHRASES } from './http/nest-error.js';
|
|
28
28
|
export type { NestErrorHandlerOptions, ErrorReportContext, ErrorReporter } from './http/nest-error.js';
|
|
29
|
+
export { findMysqlDriverError, logMysqlDriverError } from './http/mysql-driver-error.js';
|
|
30
|
+
export type { MysqlDriverErrorLike } from './http/mysql-driver-error.js';
|
|
31
|
+
export { createQueryFailedNestErrorHandler, reportClassifiedDbError } from './http/query-failed-error.js';
|
|
32
|
+
export type { ClassifiedDbError, QueryFailedClassifier, QueryFailedNestErrorHandlerOptions, } from './http/query-failed-error.js';
|
|
29
33
|
export { normalizeTrailingSlash } from './http/trailing-slash.js';
|
|
30
34
|
export type { ExecutionContextLike } from './http/execution-context.js';
|
|
31
35
|
export { KVCache } from './cache/kv-cache.js';
|
package/dist/index.js
CHANGED
|
@@ -21,6 +21,8 @@ export { getAppInfo } from './http/app-info.js';
|
|
|
21
21
|
export { resolveAppEnv, isProductionEnv } from './http/app-env.js';
|
|
22
22
|
export { HttpStatus } from './http/http-status.js';
|
|
23
23
|
export { createNestErrorHandler, nestNotFoundHandler, NEST_REASON_PHRASES } from './http/nest-error.js';
|
|
24
|
+
export { findMysqlDriverError, logMysqlDriverError } from './http/mysql-driver-error.js';
|
|
25
|
+
export { createQueryFailedNestErrorHandler, reportClassifiedDbError } from './http/query-failed-error.js';
|
|
24
26
|
export { normalizeTrailingSlash } from './http/trailing-slash.js';
|
|
25
27
|
// cache
|
|
26
28
|
export { KVCache } from './cache/kv-cache.js';
|