takos-common 1.0.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.
- package/dist/abort.d.ts +14 -0
- package/dist/abort.d.ts.map +1 -0
- package/dist/abort.js +26 -0
- package/dist/abort.js.map +1 -0
- package/dist/env-parse.d.ts +74 -0
- package/dist/env-parse.d.ts.map +1 -0
- package/dist/env-parse.js +124 -0
- package/dist/env-parse.js.map +1 -0
- package/dist/errors.d.ts +181 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +271 -0
- package/dist/errors.js.map +1 -0
- package/dist/id.d.ts +23 -0
- package/dist/id.d.ts.map +1 -0
- package/dist/id.js +32 -0
- package/dist/id.js.map +1 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +43 -0
- package/dist/index.js.map +1 -0
- package/dist/jwt.d.ts +54 -0
- package/dist/jwt.d.ts.map +1 -0
- package/dist/jwt.js +149 -0
- package/dist/jwt.js.map +1 -0
- package/dist/logger.d.ts +22 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +99 -0
- package/dist/logger.js.map +1 -0
- package/dist/middleware/hono.d.ts +151 -0
- package/dist/middleware/hono.d.ts.map +1 -0
- package/dist/middleware/hono.js +189 -0
- package/dist/middleware/hono.js.map +1 -0
- package/dist/validation.d.ts +21 -0
- package/dist/validation.d.ts.map +1 -0
- package/dist/validation.js +102 -0
- package/dist/validation.js.map +1 -0
- package/package.json +55 -0
- package/src/__tests__/jwt.test.ts +208 -0
- package/src/__tests__/validation.test.ts +42 -0
- package/src/abort.ts +30 -0
- package/src/env-parse.ts +153 -0
- package/src/errors.ts +341 -0
- package/src/id.ts +33 -0
- package/src/index.ts +81 -0
- package/src/jwt.ts +218 -0
- package/src/logger.ts +136 -0
- package/src/middleware/hono.ts +310 -0
- package/src/validation.ts +105 -0
- package/tsconfig.json +18 -0
- package/tsconfig.tsbuildinfo +1 -0
package/dist/logger.js
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Structured Logger for Takos Platform
|
|
3
|
+
*
|
|
4
|
+
* Zero-dependency structured logging compatible with Cloudflare Workers.
|
|
5
|
+
* Outputs JSON to console methods so CF observability picks up the correct level.
|
|
6
|
+
*/
|
|
7
|
+
const LEVEL_ORDER = {
|
|
8
|
+
debug: 0,
|
|
9
|
+
info: 1,
|
|
10
|
+
warn: 2,
|
|
11
|
+
error: 3,
|
|
12
|
+
};
|
|
13
|
+
function serializeError(value) {
|
|
14
|
+
if (value instanceof Error) {
|
|
15
|
+
return {
|
|
16
|
+
name: value.name,
|
|
17
|
+
message: value.message,
|
|
18
|
+
stack: value.stack,
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
return { message: String(value) };
|
|
22
|
+
}
|
|
23
|
+
function formatData(data) {
|
|
24
|
+
if (!data)
|
|
25
|
+
return undefined;
|
|
26
|
+
const result = {};
|
|
27
|
+
for (const [key, value] of Object.entries(data)) {
|
|
28
|
+
if (value instanceof Error) {
|
|
29
|
+
result[key] = serializeError(value);
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
32
|
+
result[key] = value;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return result;
|
|
36
|
+
}
|
|
37
|
+
const LEVEL_NAMES = Object.fromEntries(Object.entries(LEVEL_ORDER).map(([k, v]) => [v, k]));
|
|
38
|
+
class LoggerImpl {
|
|
39
|
+
minLevel;
|
|
40
|
+
service;
|
|
41
|
+
fields;
|
|
42
|
+
constructor(opts) {
|
|
43
|
+
this.minLevel = LEVEL_ORDER[opts?.level ?? 'debug'];
|
|
44
|
+
this.service = opts?.service;
|
|
45
|
+
this.fields = { ...opts?.defaultFields, ...opts?._fields };
|
|
46
|
+
}
|
|
47
|
+
emit(level, msg, data) {
|
|
48
|
+
if (LEVEL_ORDER[level] < this.minLevel)
|
|
49
|
+
return;
|
|
50
|
+
const entry = {
|
|
51
|
+
level,
|
|
52
|
+
msg,
|
|
53
|
+
ts: new Date().toISOString(),
|
|
54
|
+
...(this.service ? { service: this.service } : {}),
|
|
55
|
+
...this.fields,
|
|
56
|
+
...formatData(data),
|
|
57
|
+
};
|
|
58
|
+
const line = JSON.stringify(entry);
|
|
59
|
+
switch (level) {
|
|
60
|
+
case 'debug':
|
|
61
|
+
// eslint-disable-next-line no-console -- logger implementation
|
|
62
|
+
console.log(line);
|
|
63
|
+
break;
|
|
64
|
+
case 'info':
|
|
65
|
+
// eslint-disable-next-line no-console -- logger implementation
|
|
66
|
+
console.log(line);
|
|
67
|
+
break;
|
|
68
|
+
case 'warn':
|
|
69
|
+
console.warn(line);
|
|
70
|
+
break;
|
|
71
|
+
case 'error':
|
|
72
|
+
console.error(line);
|
|
73
|
+
break;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
debug(msg, data) {
|
|
77
|
+
this.emit('debug', msg, data);
|
|
78
|
+
}
|
|
79
|
+
info(msg, data) {
|
|
80
|
+
this.emit('info', msg, data);
|
|
81
|
+
}
|
|
82
|
+
warn(msg, data) {
|
|
83
|
+
this.emit('warn', msg, data);
|
|
84
|
+
}
|
|
85
|
+
error(msg, data) {
|
|
86
|
+
this.emit('error', msg, data);
|
|
87
|
+
}
|
|
88
|
+
child(fields) {
|
|
89
|
+
return new LoggerImpl({
|
|
90
|
+
level: LEVEL_NAMES[this.minLevel],
|
|
91
|
+
service: this.service,
|
|
92
|
+
_fields: { ...this.fields, ...fields },
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
export function createLogger(opts) {
|
|
97
|
+
return new LoggerImpl(opts);
|
|
98
|
+
}
|
|
99
|
+
//# sourceMappingURL=logger.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logger.js","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hono Middleware for takos-runtime
|
|
3
|
+
*
|
|
4
|
+
* Provides:
|
|
5
|
+
* - Service-to-service JWT authentication (RS256)
|
|
6
|
+
* - Consistent error handling
|
|
7
|
+
*/
|
|
8
|
+
import type { Context, Next, Env, ErrorHandler } from 'hono';
|
|
9
|
+
import { type ServiceTokenPayloadWithClaims } from '../jwt.js';
|
|
10
|
+
export type { ServiceTokenPayloadWithClaims };
|
|
11
|
+
import { AppError } from '../errors.js';
|
|
12
|
+
/**
|
|
13
|
+
* Environment bindings for service token middleware.
|
|
14
|
+
* Use this as a type parameter for Hono to get typed context variables.
|
|
15
|
+
*
|
|
16
|
+
* Usage:
|
|
17
|
+
* ```typescript
|
|
18
|
+
* import type { ServiceTokenEnv } from 'takos-common/middleware/hono';
|
|
19
|
+
* const app = new Hono<ServiceTokenEnv>();
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
export interface ServiceTokenEnv extends Env {
|
|
23
|
+
Variables: {
|
|
24
|
+
serviceToken: ServiceTokenPayloadWithClaims;
|
|
25
|
+
serviceAuthMethod: 'jwt';
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Configuration for service token middleware
|
|
30
|
+
*/
|
|
31
|
+
export interface ServiceTokenConfig {
|
|
32
|
+
/** Public key for JWT verification (PEM format) */
|
|
33
|
+
jwtPublicKey?: string;
|
|
34
|
+
/** Expected issuer for JWT tokens (required when jwtPublicKey is set) */
|
|
35
|
+
expectedIssuer?: string;
|
|
36
|
+
/** Expected audience for JWT tokens (required when jwtPublicKey is set) */
|
|
37
|
+
expectedAudience?: string;
|
|
38
|
+
/** Paths to skip authentication (e.g., health checks) */
|
|
39
|
+
skipPaths?: string[];
|
|
40
|
+
/** Clock tolerance in seconds (default: 30) */
|
|
41
|
+
clockToleranceSeconds?: number;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Extract service token from Authorization header.
|
|
45
|
+
*
|
|
46
|
+
* Note: `takos/packages/control/src/shared/utils/url-utils.ts` contains an
|
|
47
|
+
* equivalent lower-level helper `extractBearerToken(header: string | null)`
|
|
48
|
+
* that operates on a raw header string. The two cannot be unified here
|
|
49
|
+
* because `takos-common` must not import from the `control` package.
|
|
50
|
+
* If the extraction logic ever needs to change, update both places.
|
|
51
|
+
*/
|
|
52
|
+
export declare function getServiceTokenFromHeader(c: Context): string | null;
|
|
53
|
+
/**
|
|
54
|
+
* Create Hono middleware for service token authentication.
|
|
55
|
+
* Only RS256 JWTs are accepted.
|
|
56
|
+
*
|
|
57
|
+
* @param config - Configuration for the middleware
|
|
58
|
+
* @returns Hono middleware function
|
|
59
|
+
*
|
|
60
|
+
* Usage:
|
|
61
|
+
* ```typescript
|
|
62
|
+
* import { createServiceTokenMiddleware } from 'takos-common/middleware/hono';
|
|
63
|
+
*
|
|
64
|
+
* const app = new Hono<ServiceTokenEnv>();
|
|
65
|
+
* app.use('*', createServiceTokenMiddleware({ jwtPublicKey: '...' }));
|
|
66
|
+
* ```
|
|
67
|
+
*/
|
|
68
|
+
export declare function createServiceTokenMiddleware(config: ServiceTokenConfig): (c: Context, next: Next) => Promise<Response | void>;
|
|
69
|
+
/**
|
|
70
|
+
* Error handler options
|
|
71
|
+
*/
|
|
72
|
+
export interface ErrorHandlerOptions {
|
|
73
|
+
/** Include stack traces in responses (only for development) */
|
|
74
|
+
includeStack?: boolean;
|
|
75
|
+
/** Custom error logger */
|
|
76
|
+
logger?: (error: unknown, context?: Record<string, unknown>) => void;
|
|
77
|
+
/** Custom error transformer */
|
|
78
|
+
transformError?: (error: unknown) => AppError;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Create Hono error handler (for use with `app.onError`)
|
|
82
|
+
*
|
|
83
|
+
* Usage:
|
|
84
|
+
* ```typescript
|
|
85
|
+
* import { createErrorHandler } from 'takos-common/middleware/hono';
|
|
86
|
+
*
|
|
87
|
+
* const app = new Hono();
|
|
88
|
+
* app.onError(createErrorHandler({ includeStack: true }));
|
|
89
|
+
* ```
|
|
90
|
+
*/
|
|
91
|
+
export declare function createErrorHandler(options?: ErrorHandlerOptions): ErrorHandler;
|
|
92
|
+
/**
|
|
93
|
+
* Not found handler for Hono
|
|
94
|
+
* Use with `app.notFound()`
|
|
95
|
+
*
|
|
96
|
+
* Usage:
|
|
97
|
+
* ```typescript
|
|
98
|
+
* import { notFoundHandler } from 'takos-common/middleware/hono';
|
|
99
|
+
*
|
|
100
|
+
* const app = new Hono();
|
|
101
|
+
* app.notFound(notFoundHandler);
|
|
102
|
+
* ```
|
|
103
|
+
*/
|
|
104
|
+
export declare function notFoundHandler(c: Context): Response & import("hono").TypedResponse<{
|
|
105
|
+
error: {
|
|
106
|
+
code: string;
|
|
107
|
+
message: string;
|
|
108
|
+
details?: import("hono/utils/types").JSONValue | undefined;
|
|
109
|
+
};
|
|
110
|
+
}, 404, "json">;
|
|
111
|
+
/**
|
|
112
|
+
* 400 Bad Request
|
|
113
|
+
*/
|
|
114
|
+
export declare function badRequest(c: Context, message?: string, details?: unknown): Response & import("hono").TypedResponse<{
|
|
115
|
+
error: {
|
|
116
|
+
code: string;
|
|
117
|
+
message: string;
|
|
118
|
+
details?: import("hono/utils/types").JSONValue | undefined;
|
|
119
|
+
};
|
|
120
|
+
}, 400, "json">;
|
|
121
|
+
/**
|
|
122
|
+
* 404 Not Found
|
|
123
|
+
*/
|
|
124
|
+
export declare function notFound(c: Context, message?: string, details?: unknown): Response & import("hono").TypedResponse<{
|
|
125
|
+
error: {
|
|
126
|
+
code: string;
|
|
127
|
+
message: string;
|
|
128
|
+
details?: import("hono/utils/types").JSONValue | undefined;
|
|
129
|
+
};
|
|
130
|
+
}, 404, "json">;
|
|
131
|
+
/**
|
|
132
|
+
* 403 Forbidden
|
|
133
|
+
*/
|
|
134
|
+
export declare function forbidden(c: Context, message?: string, details?: unknown): Response & import("hono").TypedResponse<{
|
|
135
|
+
error: {
|
|
136
|
+
code: string;
|
|
137
|
+
message: string;
|
|
138
|
+
details?: import("hono/utils/types").JSONValue | undefined;
|
|
139
|
+
};
|
|
140
|
+
}, 403, "json">;
|
|
141
|
+
/**
|
|
142
|
+
* 500 Internal Server Error
|
|
143
|
+
*/
|
|
144
|
+
export declare function internalError(c: Context, message?: string, details?: unknown): Response & import("hono").TypedResponse<{
|
|
145
|
+
error: {
|
|
146
|
+
code: string;
|
|
147
|
+
message: string;
|
|
148
|
+
details?: import("hono/utils/types").JSONValue | undefined;
|
|
149
|
+
};
|
|
150
|
+
}, 500, "json">;
|
|
151
|
+
//# sourceMappingURL=hono.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hono.d.ts","sourceRoot":"","sources":["../../src/middleware/hono.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,GAAG,EAAE,YAAY,EAAE,MAAM,MAAM,CAAC;AAC7D,OAAO,EAEL,KAAK,6BAA6B,EACnC,MAAM,WAAW,CAAC;AAEnB,YAAY,EAAE,6BAA6B,EAAE,CAAC;AAC9C,OAAO,EACL,QAAQ,EAWT,MAAM,cAAc,CAAC;AAMtB;;;;;;;;;GASG;AACH,MAAM,WAAW,eAAgB,SAAQ,GAAG;IAC1C,SAAS,EAAE;QACT,YAAY,EAAE,6BAA6B,CAAC;QAC5C,iBAAiB,EAAE,KAAK,CAAC;KAC1B,CAAC;CACH;AAMD;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,mDAAmD;IACnD,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,yEAAyE;IACzE,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,2EAA2E;IAC3E,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,yDAAyD;IACzD,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IACrB,+CAA+C;IAC/C,qBAAqB,CAAC,EAAE,MAAM,CAAC;CAChC;AAED;;;;;;;;GAQG;AACH,wBAAgB,yBAAyB,CAAC,CAAC,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAMnE;AAUD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,4BAA4B,CAAC,MAAM,EAAE,kBAAkB,IASvD,GAAG,OAAO,EAAE,MAAM,IAAI,KAAG,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,CAgDhE;AAMD;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,+DAA+D;IAC/D,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,0BAA0B;IAC1B,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC;IACrE,+BAA+B;IAC/B,cAAc,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,QAAQ,CAAC;CAC/C;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,kBAAkB,CAChC,OAAO,GAAE,mBAAwB,GAChC,YAAY,CAoCd;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,eAAe,CAAC,CAAC,EAAE,OAAO;;;;;;gBAGzC;AAoBD;;GAEG;AACH,wBAAgB,UAAU,CACxB,CAAC,EAAE,OAAO,EACV,OAAO,SAAgB,EACvB,OAAO,CAAC,EAAE,OAAO;;;;;;gBAGlB;AAED;;GAEG;AACH,wBAAgB,QAAQ,CACtB,CAAC,EAAE,OAAO,EACV,OAAO,SAAc,EACrB,OAAO,CAAC,EAAE,OAAO;;;;;;gBAGlB;AAED;;GAEG;AACH,wBAAgB,SAAS,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,SAAkB,EAAE,OAAO,CAAC,EAAE,OAAO;;;;;;gBAEjF;AAED;;GAEG;AACH,wBAAgB,aAAa,CAC3B,CAAC,EAAE,OAAO,EACV,OAAO,SAA0B,EACjC,OAAO,CAAC,EAAE,OAAO;;;;;;gBAGlB"}
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hono Middleware for takos-runtime
|
|
3
|
+
*
|
|
4
|
+
* Provides:
|
|
5
|
+
* - Service-to-service JWT authentication (RS256)
|
|
6
|
+
* - Consistent error handling
|
|
7
|
+
*/
|
|
8
|
+
import { verifyServiceToken, } from '../jwt.js';
|
|
9
|
+
import { AuthenticationError, ErrorCodes, InternalError, isAppError, logError, NotFoundError, RateLimitError, ServiceUnavailableError, } from '../errors.js';
|
|
10
|
+
/**
|
|
11
|
+
* Extract service token from Authorization header.
|
|
12
|
+
*
|
|
13
|
+
* Note: `takos/packages/control/src/shared/utils/url-utils.ts` contains an
|
|
14
|
+
* equivalent lower-level helper `extractBearerToken(header: string | null)`
|
|
15
|
+
* that operates on a raw header string. The two cannot be unified here
|
|
16
|
+
* because `takos-common` must not import from the `control` package.
|
|
17
|
+
* If the extraction logic ever needs to change, update both places.
|
|
18
|
+
*/
|
|
19
|
+
export function getServiceTokenFromHeader(c) {
|
|
20
|
+
const authHeader = c.req.header('Authorization');
|
|
21
|
+
if (authHeader?.startsWith('Bearer ')) {
|
|
22
|
+
return authHeader.slice('Bearer '.length).trim();
|
|
23
|
+
}
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Check if the token looks like a JWT (has 3 parts separated by dots)
|
|
28
|
+
*/
|
|
29
|
+
function isJwtFormat(token) {
|
|
30
|
+
const parts = token.split('.');
|
|
31
|
+
return parts.length === 3;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Create Hono middleware for service token authentication.
|
|
35
|
+
* Only RS256 JWTs are accepted.
|
|
36
|
+
*
|
|
37
|
+
* @param config - Configuration for the middleware
|
|
38
|
+
* @returns Hono middleware function
|
|
39
|
+
*
|
|
40
|
+
* Usage:
|
|
41
|
+
* ```typescript
|
|
42
|
+
* import { createServiceTokenMiddleware } from 'takos-common/middleware/hono';
|
|
43
|
+
*
|
|
44
|
+
* const app = new Hono<ServiceTokenEnv>();
|
|
45
|
+
* app.use('*', createServiceTokenMiddleware({ jwtPublicKey: '...' }));
|
|
46
|
+
* ```
|
|
47
|
+
*/
|
|
48
|
+
export function createServiceTokenMiddleware(config) {
|
|
49
|
+
const { jwtPublicKey, expectedIssuer, expectedAudience, skipPaths = ['/health'], clockToleranceSeconds = 30, } = config;
|
|
50
|
+
return async (c, next) => {
|
|
51
|
+
// Skip authentication for certain paths (e.g., health checks)
|
|
52
|
+
const path = new URL(c.req.url).pathname;
|
|
53
|
+
if (skipPaths.includes(path)) {
|
|
54
|
+
await next();
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
if (!jwtPublicKey) {
|
|
58
|
+
const error = new ServiceUnavailableError('Service token not configured');
|
|
59
|
+
return c.json(error.toResponse(), error.statusCode);
|
|
60
|
+
}
|
|
61
|
+
if (!expectedIssuer || !expectedAudience) {
|
|
62
|
+
const error = new ServiceUnavailableError('JWT verification requires expectedIssuer and expectedAudience');
|
|
63
|
+
return c.json(error.toResponse(), error.statusCode);
|
|
64
|
+
}
|
|
65
|
+
// Extract token from request
|
|
66
|
+
const token = getServiceTokenFromHeader(c);
|
|
67
|
+
if (!token) {
|
|
68
|
+
const error = new AuthenticationError('Authorization token is required');
|
|
69
|
+
return c.json(error.toResponse(), error.statusCode);
|
|
70
|
+
}
|
|
71
|
+
if (!isJwtFormat(token)) {
|
|
72
|
+
const error = new AuthenticationError('Service token must be a JWT');
|
|
73
|
+
return c.json(error.toResponse(), error.statusCode);
|
|
74
|
+
}
|
|
75
|
+
const result = verifyServiceToken({
|
|
76
|
+
token,
|
|
77
|
+
publicKey: jwtPublicKey,
|
|
78
|
+
expectedAudience,
|
|
79
|
+
expectedIssuer,
|
|
80
|
+
clockToleranceSeconds,
|
|
81
|
+
});
|
|
82
|
+
if (result.valid && result.payload) {
|
|
83
|
+
c.set('serviceToken', result.payload);
|
|
84
|
+
c.set('serviceAuthMethod', 'jwt');
|
|
85
|
+
await next();
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
const error = new AuthenticationError(result.error || 'Invalid JWT token');
|
|
89
|
+
return c.json(error.toResponse(), error.statusCode);
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Create Hono error handler (for use with `app.onError`)
|
|
94
|
+
*
|
|
95
|
+
* Usage:
|
|
96
|
+
* ```typescript
|
|
97
|
+
* import { createErrorHandler } from 'takos-common/middleware/hono';
|
|
98
|
+
*
|
|
99
|
+
* const app = new Hono();
|
|
100
|
+
* app.onError(createErrorHandler({ includeStack: true }));
|
|
101
|
+
* ```
|
|
102
|
+
*/
|
|
103
|
+
export function createErrorHandler(options = {}) {
|
|
104
|
+
const { includeStack = false, logger = logError, transformError } = options;
|
|
105
|
+
return (err, c) => {
|
|
106
|
+
// Transform error if transformer provided
|
|
107
|
+
let appError;
|
|
108
|
+
if (transformError) {
|
|
109
|
+
appError = transformError(err);
|
|
110
|
+
}
|
|
111
|
+
else if (isAppError(err)) {
|
|
112
|
+
appError = err;
|
|
113
|
+
}
|
|
114
|
+
else {
|
|
115
|
+
// Log non-operational errors with full details
|
|
116
|
+
const path = new URL(c.req.url).pathname;
|
|
117
|
+
logger(err, {
|
|
118
|
+
path,
|
|
119
|
+
method: c.req.method,
|
|
120
|
+
requestId: c.req.header('x-request-id'),
|
|
121
|
+
});
|
|
122
|
+
appError = new InternalError('An unexpected error occurred');
|
|
123
|
+
}
|
|
124
|
+
// Build response
|
|
125
|
+
const response = appError.toResponse();
|
|
126
|
+
// Add stack trace in development mode
|
|
127
|
+
if (includeStack && appError.stack) {
|
|
128
|
+
response.error.stack = appError.stack;
|
|
129
|
+
}
|
|
130
|
+
// Set special headers for rate limiting
|
|
131
|
+
if (appError instanceof RateLimitError && appError.retryAfter) {
|
|
132
|
+
c.header('Retry-After', String(appError.retryAfter));
|
|
133
|
+
}
|
|
134
|
+
return c.json(response, appError.statusCode);
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Not found handler for Hono
|
|
139
|
+
* Use with `app.notFound()`
|
|
140
|
+
*
|
|
141
|
+
* Usage:
|
|
142
|
+
* ```typescript
|
|
143
|
+
* import { notFoundHandler } from 'takos-common/middleware/hono';
|
|
144
|
+
*
|
|
145
|
+
* const app = new Hono();
|
|
146
|
+
* app.notFound(notFoundHandler);
|
|
147
|
+
* ```
|
|
148
|
+
*/
|
|
149
|
+
export function notFoundHandler(c) {
|
|
150
|
+
const error = new NotFoundError('Route');
|
|
151
|
+
return c.json(error.toResponse(), 404);
|
|
152
|
+
}
|
|
153
|
+
// ============================================================================
|
|
154
|
+
// Helper functions for route handlers
|
|
155
|
+
// ============================================================================
|
|
156
|
+
function buildErrorBody(message, code, details) {
|
|
157
|
+
return {
|
|
158
|
+
error: {
|
|
159
|
+
code,
|
|
160
|
+
message,
|
|
161
|
+
...(details !== undefined && { details }),
|
|
162
|
+
},
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* 400 Bad Request
|
|
167
|
+
*/
|
|
168
|
+
export function badRequest(c, message = 'Bad request', details) {
|
|
169
|
+
return c.json(buildErrorBody(message, ErrorCodes.BAD_REQUEST, details), 400);
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* 404 Not Found
|
|
173
|
+
*/
|
|
174
|
+
export function notFound(c, message = 'Not found', details) {
|
|
175
|
+
return c.json(buildErrorBody(message, ErrorCodes.NOT_FOUND, details), 404);
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* 403 Forbidden
|
|
179
|
+
*/
|
|
180
|
+
export function forbidden(c, message = 'Access denied', details) {
|
|
181
|
+
return c.json(buildErrorBody(message, ErrorCodes.FORBIDDEN, details), 403);
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* 500 Internal Server Error
|
|
185
|
+
*/
|
|
186
|
+
export function internalError(c, message = 'Internal server error', details) {
|
|
187
|
+
return c.json(buildErrorBody(message, ErrorCodes.INTERNAL_ERROR, details), 500);
|
|
188
|
+
}
|
|
189
|
+
//# sourceMappingURL=hono.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hono.js","sourceRoot":"","sources":["../../src/middleware/hono.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Validation Utilities
|
|
3
|
+
*
|
|
4
|
+
* Provides common validation functions for input sanitization
|
|
5
|
+
* and security across all takos packages.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Check if a hostname is localhost or a local address.
|
|
9
|
+
*
|
|
10
|
+
* @param hostname - Hostname to check
|
|
11
|
+
* @returns true if the hostname is local
|
|
12
|
+
*/
|
|
13
|
+
export declare function isLocalhost(hostname: string): boolean;
|
|
14
|
+
/**
|
|
15
|
+
* Check if an IP address is a private/internal address.
|
|
16
|
+
*
|
|
17
|
+
* @param ip - IP address to check
|
|
18
|
+
* @returns true if the IP is private
|
|
19
|
+
*/
|
|
20
|
+
export declare function isPrivateIP(ip: string): boolean;
|
|
21
|
+
//# sourceMappingURL=validation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../src/validation.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAWrD;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAwE/C"}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Validation Utilities
|
|
3
|
+
*
|
|
4
|
+
* Provides common validation functions for input sanitization
|
|
5
|
+
* and security across all takos packages.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Check if a hostname is localhost or a local address.
|
|
9
|
+
*
|
|
10
|
+
* @param hostname - Hostname to check
|
|
11
|
+
* @returns true if the hostname is local
|
|
12
|
+
*/
|
|
13
|
+
export function isLocalhost(hostname) {
|
|
14
|
+
const lower = hostname.toLowerCase();
|
|
15
|
+
return (lower === 'localhost' ||
|
|
16
|
+
lower === '127.0.0.1' ||
|
|
17
|
+
lower === '::1' ||
|
|
18
|
+
lower.endsWith('.localhost') ||
|
|
19
|
+
lower.endsWith('.local') ||
|
|
20
|
+
lower.endsWith('.localdomain') ||
|
|
21
|
+
lower.endsWith('.internal'));
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Check if an IP address is a private/internal address.
|
|
25
|
+
*
|
|
26
|
+
* @param ip - IP address to check
|
|
27
|
+
* @returns true if the IP is private
|
|
28
|
+
*/
|
|
29
|
+
export function isPrivateIP(ip) {
|
|
30
|
+
// Check for IPv4 private ranges
|
|
31
|
+
const ipv4Match = ip.match(/^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/);
|
|
32
|
+
if (ipv4Match) {
|
|
33
|
+
const [, a, b, c] = ipv4Match.map(Number);
|
|
34
|
+
// 0.0.0.0/8 - Current network
|
|
35
|
+
if (a === 0)
|
|
36
|
+
return true;
|
|
37
|
+
// 10.0.0.0/8 - Private network
|
|
38
|
+
if (a === 10)
|
|
39
|
+
return true;
|
|
40
|
+
// 127.0.0.0/8 - Loopback
|
|
41
|
+
if (a === 127)
|
|
42
|
+
return true;
|
|
43
|
+
// 169.254.0.0/16 - Link-local
|
|
44
|
+
if (a === 169 && b === 254)
|
|
45
|
+
return true;
|
|
46
|
+
// 172.16.0.0/12 - Private network
|
|
47
|
+
if (a === 172 && b >= 16 && b <= 31)
|
|
48
|
+
return true;
|
|
49
|
+
// 192.168.0.0/16 - Private network
|
|
50
|
+
if (a === 192 && b === 168)
|
|
51
|
+
return true;
|
|
52
|
+
// 100.64.0.0/10 - Carrier-grade NAT
|
|
53
|
+
if (a === 100 && b >= 64 && b <= 127)
|
|
54
|
+
return true;
|
|
55
|
+
// 192.0.0.0/24 - IETF Protocol Assignments
|
|
56
|
+
if (a === 192 && b === 0 && c === 0)
|
|
57
|
+
return true;
|
|
58
|
+
// 192.0.2.0/24 - Documentation (TEST-NET-1)
|
|
59
|
+
if (a === 192 && b === 0 && c === 2)
|
|
60
|
+
return true;
|
|
61
|
+
// 198.18.0.0/15 - Benchmarking
|
|
62
|
+
if (a === 198 && (b === 18 || b === 19))
|
|
63
|
+
return true;
|
|
64
|
+
// 198.51.100.0/24 - Documentation (TEST-NET-2)
|
|
65
|
+
if (a === 198 && b === 51 && c === 100)
|
|
66
|
+
return true;
|
|
67
|
+
// 203.0.113.0/24 - Documentation (TEST-NET-3)
|
|
68
|
+
if (a === 203 && b === 0 && c === 113)
|
|
69
|
+
return true;
|
|
70
|
+
// 224.0.0.0+ - Multicast and reserved
|
|
71
|
+
if (a >= 224)
|
|
72
|
+
return true;
|
|
73
|
+
}
|
|
74
|
+
// IPv6 private ranges
|
|
75
|
+
if (ip.startsWith('::1'))
|
|
76
|
+
return true; // Loopback
|
|
77
|
+
if (ip.startsWith('fe80:'))
|
|
78
|
+
return true; // Link-local
|
|
79
|
+
if (ip.startsWith('fc') || ip.startsWith('fd'))
|
|
80
|
+
return true; // Unique local
|
|
81
|
+
// IPv4-mapped IPv6 addresses (e.g. ::ffff:192.168.1.1 or ::ffff:0a00:0001)
|
|
82
|
+
const ipLower = ip.toLowerCase();
|
|
83
|
+
if (ipLower.startsWith('::ffff:')) {
|
|
84
|
+
const rest = ipLower.slice('::ffff:'.length);
|
|
85
|
+
// Handle dotted-decimal form: ::ffff:192.168.1.1
|
|
86
|
+
if (/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.test(rest)) {
|
|
87
|
+
return isPrivateIP(rest);
|
|
88
|
+
}
|
|
89
|
+
// Handle hex form: ::ffff:c0a8:0101 → convert to dotted-decimal
|
|
90
|
+
const hexParts = rest.split(':');
|
|
91
|
+
if (hexParts.length === 2) {
|
|
92
|
+
const hi = parseInt(hexParts[0], 16);
|
|
93
|
+
const lo = parseInt(hexParts[1], 16);
|
|
94
|
+
if (!isNaN(hi) && !isNaN(lo)) {
|
|
95
|
+
const dotted = `${(hi >> 8) & 0xff}.${hi & 0xff}.${(lo >> 8) & 0xff}.${lo & 0xff}`;
|
|
96
|
+
return isPrivateIP(dotted);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return false;
|
|
101
|
+
}
|
|
102
|
+
//# sourceMappingURL=validation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validation.js","sourceRoot":"","sources":["../src/validation.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH;;;;;GAKG;AACH,MAAM,UAAU,WAAW,CAAC,QAAgB;IAC1C,MAAM,KAAK,GAAG,QAAQ,CAAC,WAAW,EAAE,CAAC;IACrC,OAAO,CACL,KAAK,KAAK,WAAW;QACrB,KAAK,KAAK,WAAW;QACrB,KAAK,KAAK,KAAK;QACf,KAAK,CAAC,QAAQ,CAAC,YAAY,CAAC;QAC5B,KAAK,CAAC,QAAQ,CAAC,QAAQ,CAAC;QACxB,KAAK,CAAC,QAAQ,CAAC,cAAc,CAAC;QAC9B,KAAK,CAAC,QAAQ,CAAC,WAAW,CAAC,CAC5B,CAAC;AACJ,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,WAAW,CAAC,EAAU;IACpC,gCAAgC;IAChC,MAAM,SAAS,GAAG,EAAE,CAAC,KAAK,CAAC,8CAA8C,CAAC,CAAC;IAC3E,IAAI,SAAS,EAAE,CAAC;QACd,MAAM,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,GAAG,SAAS,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QAE1C,8BAA8B;QAC9B,IAAI,CAAC,KAAK,CAAC;YAAE,OAAO,IAAI,CAAC;QAEzB,+BAA+B;QAC/B,IAAI,CAAC,KAAK,EAAE;YAAE,OAAO,IAAI,CAAC;QAE1B,yBAAyB;QACzB,IAAI,CAAC,KAAK,GAAG;YAAE,OAAO,IAAI,CAAC;QAE3B,8BAA8B;QAC9B,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,GAAG;YAAE,OAAO,IAAI,CAAC;QAExC,kCAAkC;QAClC,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE;YAAE,OAAO,IAAI,CAAC;QAEjD,mCAAmC;QACnC,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,GAAG;YAAE,OAAO,IAAI,CAAC;QAExC,oCAAoC;QACpC,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,GAAG;YAAE,OAAO,IAAI,CAAC;QAElD,2CAA2C;QAC3C,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC;YAAE,OAAO,IAAI,CAAC;QAEjD,4CAA4C;QAC5C,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC;YAAE,OAAO,IAAI,CAAC;QAEjD,+BAA+B;QAC/B,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,CAAC,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,CAAC;YAAE,OAAO,IAAI,CAAC;QAErD,+CAA+C;QAC/C,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,KAAK,GAAG;YAAE,OAAO,IAAI,CAAC;QAEpD,8CAA8C;QAC9C,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,GAAG;YAAE,OAAO,IAAI,CAAC;QAEnD,sCAAsC;QACtC,IAAI,CAAC,IAAI,GAAG;YAAE,OAAO,IAAI,CAAC;IAC5B,CAAC;IAED,sBAAsB;IACtB,IAAI,EAAE,CAAC,UAAU,CAAC,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC,CAAC,WAAW;IAClD,IAAI,EAAE,CAAC,UAAU,CAAC,OAAO,CAAC;QAAE,OAAO,IAAI,CAAC,CAAC,aAAa;IACtD,IAAI,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC,CAAC,eAAe;IAE5E,2EAA2E;IAC3E,MAAM,OAAO,GAAG,EAAE,CAAC,WAAW,EAAE,CAAC;IACjC,IAAI,OAAO,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;QAClC,MAAM,IAAI,GAAG,OAAO,CAAC,KAAK,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;QAC7C,iDAAiD;QACjD,IAAI,sCAAsC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;YACtD,OAAO,WAAW,CAAC,IAAI,CAAC,CAAC;QAC3B,CAAC;QACD,gEAAgE;QAChE,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QACjC,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC1B,MAAM,EAAE,GAAG,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YACrC,MAAM,EAAE,GAAG,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YACrC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,EAAE,CAAC;gBAC7B,MAAM,MAAM,GAAG,GAAG,CAAC,EAAE,IAAI,CAAC,CAAC,GAAG,IAAI,IAAI,EAAE,GAAG,IAAI,IAAI,CAAC,EAAE,IAAI,CAAC,CAAC,GAAG,IAAI,IAAI,EAAE,GAAG,IAAI,EAAE,CAAC;gBACnF,OAAO,WAAW,CAAC,MAAM,CAAC,CAAC;YAC7B,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "takos-common",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"main": "./dist/index.js",
|
|
6
|
+
"types": "./dist/index.d.ts",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"import": "./dist/index.js"
|
|
11
|
+
},
|
|
12
|
+
"./id": {
|
|
13
|
+
"types": "./dist/id.d.ts",
|
|
14
|
+
"import": "./dist/id.js"
|
|
15
|
+
},
|
|
16
|
+
"./validation": {
|
|
17
|
+
"types": "./dist/validation.d.ts",
|
|
18
|
+
"import": "./dist/validation.js"
|
|
19
|
+
},
|
|
20
|
+
"./abort": {
|
|
21
|
+
"types": "./dist/abort.d.ts",
|
|
22
|
+
"import": "./dist/abort.js"
|
|
23
|
+
},
|
|
24
|
+
"./errors": {
|
|
25
|
+
"types": "./dist/errors.d.ts",
|
|
26
|
+
"import": "./dist/errors.js"
|
|
27
|
+
},
|
|
28
|
+
"./middleware/hono": {
|
|
29
|
+
"types": "./dist/middleware/hono.d.ts",
|
|
30
|
+
"import": "./dist/middleware/hono.js"
|
|
31
|
+
},
|
|
32
|
+
"./logger": {
|
|
33
|
+
"types": "./dist/logger.d.ts",
|
|
34
|
+
"import": "./dist/logger.js"
|
|
35
|
+
},
|
|
36
|
+
"./env-parse": {
|
|
37
|
+
"types": "./dist/env-parse.d.ts",
|
|
38
|
+
"import": "./dist/env-parse.js"
|
|
39
|
+
}
|
|
40
|
+
},
|
|
41
|
+
"scripts": {
|
|
42
|
+
"build": "tsc",
|
|
43
|
+
"typecheck": "tsc --noEmit",
|
|
44
|
+
"clean": "rm -rf dist",
|
|
45
|
+
"test": "vitest run"
|
|
46
|
+
},
|
|
47
|
+
"dependencies": {
|
|
48
|
+
"hono": "^4.12.4"
|
|
49
|
+
},
|
|
50
|
+
"devDependencies": {
|
|
51
|
+
"@types/node": "^22.0.0",
|
|
52
|
+
"typescript": "^5.7.0",
|
|
53
|
+
"vitest": "^2.1.0"
|
|
54
|
+
}
|
|
55
|
+
}
|