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/errors.js
ADDED
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Standardized Error Handling for Takos Platform
|
|
3
|
+
*
|
|
4
|
+
* This module provides a consistent error handling pattern across all takos packages.
|
|
5
|
+
* All errors extend from AppError and include:
|
|
6
|
+
* - code: A unique error code for client-side handling
|
|
7
|
+
* - message: A user-safe message (no internal details)
|
|
8
|
+
* - statusCode: The HTTP status code to return
|
|
9
|
+
* - details: Optional field-level or additional details
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* Standard error codes for consistent client handling
|
|
13
|
+
*/
|
|
14
|
+
export const ErrorCodes = {
|
|
15
|
+
// 4xx Client Errors
|
|
16
|
+
BAD_REQUEST: 'BAD_REQUEST',
|
|
17
|
+
UNAUTHORIZED: 'UNAUTHORIZED',
|
|
18
|
+
PAYMENT_REQUIRED: 'PAYMENT_REQUIRED',
|
|
19
|
+
FORBIDDEN: 'FORBIDDEN',
|
|
20
|
+
NOT_FOUND: 'NOT_FOUND',
|
|
21
|
+
CONFLICT: 'CONFLICT',
|
|
22
|
+
GONE: 'GONE',
|
|
23
|
+
VALIDATION_ERROR: 'VALIDATION_ERROR',
|
|
24
|
+
RATE_LIMITED: 'RATE_LIMITED',
|
|
25
|
+
PAYLOAD_TOO_LARGE: 'PAYLOAD_TOO_LARGE',
|
|
26
|
+
// 5xx Server Errors
|
|
27
|
+
INTERNAL_ERROR: 'INTERNAL_ERROR',
|
|
28
|
+
NOT_IMPLEMENTED: 'NOT_IMPLEMENTED',
|
|
29
|
+
SERVICE_UNAVAILABLE: 'SERVICE_UNAVAILABLE',
|
|
30
|
+
BAD_GATEWAY: 'BAD_GATEWAY',
|
|
31
|
+
GATEWAY_TIMEOUT: 'GATEWAY_TIMEOUT',
|
|
32
|
+
};
|
|
33
|
+
/**
|
|
34
|
+
* Base application error class
|
|
35
|
+
* All custom errors should extend from this class
|
|
36
|
+
*/
|
|
37
|
+
export class AppError extends Error {
|
|
38
|
+
code;
|
|
39
|
+
statusCode;
|
|
40
|
+
details;
|
|
41
|
+
constructor(message, code = ErrorCodes.INTERNAL_ERROR, statusCode = 500, details) {
|
|
42
|
+
super(message);
|
|
43
|
+
this.name = this.constructor.name;
|
|
44
|
+
this.code = code;
|
|
45
|
+
this.statusCode = statusCode;
|
|
46
|
+
this.details = details;
|
|
47
|
+
// Maintains proper stack trace for where error was thrown
|
|
48
|
+
Error.captureStackTrace?.(this, this.constructor);
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Convert error to API response format
|
|
52
|
+
* This ensures no internal details leak to clients
|
|
53
|
+
*/
|
|
54
|
+
toResponse() {
|
|
55
|
+
return {
|
|
56
|
+
error: {
|
|
57
|
+
code: this.code,
|
|
58
|
+
message: this.message,
|
|
59
|
+
...(this.details !== undefined && { details: this.details }),
|
|
60
|
+
},
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* 400 Bad Request - Invalid request syntax or parameters
|
|
66
|
+
*/
|
|
67
|
+
export class BadRequestError extends AppError {
|
|
68
|
+
constructor(message = 'Bad request', details) {
|
|
69
|
+
super(message, ErrorCodes.BAD_REQUEST, 400, details);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* 401 Unauthorized - Authentication required or invalid
|
|
74
|
+
*/
|
|
75
|
+
export class AuthenticationError extends AppError {
|
|
76
|
+
constructor(message = 'Authentication required', details) {
|
|
77
|
+
super(message, ErrorCodes.UNAUTHORIZED, 401, details);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* 402 Payment Required - Payment is required to access the resource
|
|
82
|
+
*/
|
|
83
|
+
export class PaymentRequiredError extends AppError {
|
|
84
|
+
constructor(message = 'Payment required', details) {
|
|
85
|
+
super(message, ErrorCodes.PAYMENT_REQUIRED, 402, details);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* 403 Forbidden - Authenticated but not authorized
|
|
90
|
+
*/
|
|
91
|
+
export class AuthorizationError extends AppError {
|
|
92
|
+
constructor(message = 'Access denied', details) {
|
|
93
|
+
super(message, ErrorCodes.FORBIDDEN, 403, details);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* 404 Not Found - Resource does not exist
|
|
98
|
+
*/
|
|
99
|
+
export class NotFoundError extends AppError {
|
|
100
|
+
constructor(resource = 'Resource', details) {
|
|
101
|
+
super(`${resource} not found`, ErrorCodes.NOT_FOUND, 404, details);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* 409 Conflict - Resource conflict (e.g., duplicate)
|
|
106
|
+
*/
|
|
107
|
+
export class ConflictError extends AppError {
|
|
108
|
+
constructor(message = 'Resource conflict', details) {
|
|
109
|
+
super(message, ErrorCodes.CONFLICT, 409, details);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* 410 Gone - Resource no longer available
|
|
114
|
+
*/
|
|
115
|
+
export class GoneError extends AppError {
|
|
116
|
+
constructor(message = 'Resource is no longer available', details) {
|
|
117
|
+
super(message, ErrorCodes.GONE, 410, details);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* 413 Payload Too Large - Request payload exceeds limit
|
|
122
|
+
*/
|
|
123
|
+
export class PayloadTooLargeError extends AppError {
|
|
124
|
+
constructor(message = 'Payload too large', details) {
|
|
125
|
+
super(message, ErrorCodes.PAYLOAD_TOO_LARGE, 413, details);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* 422 Unprocessable Entity - Validation failed
|
|
130
|
+
*/
|
|
131
|
+
export class ValidationError extends AppError {
|
|
132
|
+
fieldErrors;
|
|
133
|
+
constructor(message = 'Validation failed', fieldErrors = []) {
|
|
134
|
+
super(message, ErrorCodes.VALIDATION_ERROR, 422, fieldErrors.length > 0 ? { fields: fieldErrors } : undefined);
|
|
135
|
+
this.fieldErrors = fieldErrors;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* 429 Too Many Requests - Rate limit exceeded
|
|
140
|
+
*/
|
|
141
|
+
export class RateLimitError extends AppError {
|
|
142
|
+
retryAfter;
|
|
143
|
+
constructor(message = 'Rate limit exceeded', retryAfter) {
|
|
144
|
+
super(message, ErrorCodes.RATE_LIMITED, 429);
|
|
145
|
+
this.retryAfter = retryAfter;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* 500 Internal Server Error - Unexpected server error
|
|
150
|
+
*/
|
|
151
|
+
export class InternalError extends AppError {
|
|
152
|
+
constructor(message = 'Internal server error', details) {
|
|
153
|
+
super(message, ErrorCodes.INTERNAL_ERROR, 500, details);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* 501 Not Implemented - Functionality not implemented
|
|
158
|
+
*/
|
|
159
|
+
export class NotImplementedError extends AppError {
|
|
160
|
+
constructor(message = 'Not implemented', details) {
|
|
161
|
+
super(message, ErrorCodes.NOT_IMPLEMENTED, 501, details);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* 502 Bad Gateway - Invalid response from upstream service
|
|
166
|
+
*/
|
|
167
|
+
export class BadGatewayError extends AppError {
|
|
168
|
+
constructor(message = 'Bad gateway', details) {
|
|
169
|
+
super(message, ErrorCodes.BAD_GATEWAY, 502, details);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* 503 Service Unavailable - Service temporarily unavailable
|
|
174
|
+
*/
|
|
175
|
+
export class ServiceUnavailableError extends AppError {
|
|
176
|
+
constructor(message = 'Service temporarily unavailable', details) {
|
|
177
|
+
super(message, ErrorCodes.SERVICE_UNAVAILABLE, 503, details);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* 504 Gateway Timeout - Upstream service timeout
|
|
182
|
+
*/
|
|
183
|
+
export class GatewayTimeoutError extends AppError {
|
|
184
|
+
constructor(message = 'Gateway timeout', details) {
|
|
185
|
+
super(message, ErrorCodes.GATEWAY_TIMEOUT, 504, details);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Type guard to check if an error is an AppError
|
|
190
|
+
*/
|
|
191
|
+
export function isAppError(error) {
|
|
192
|
+
return error instanceof AppError;
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Convert unknown error to AppError
|
|
196
|
+
* Use this to normalize errors before sending responses
|
|
197
|
+
*/
|
|
198
|
+
export function normalizeError(error, logger) {
|
|
199
|
+
if (isAppError(error)) {
|
|
200
|
+
return error;
|
|
201
|
+
}
|
|
202
|
+
if (error instanceof Error) {
|
|
203
|
+
if (logger) {
|
|
204
|
+
logger.error('Converting Error to AppError', { error });
|
|
205
|
+
}
|
|
206
|
+
else {
|
|
207
|
+
console.error('[normalizeError] Converting Error to AppError:', error);
|
|
208
|
+
}
|
|
209
|
+
return new InternalError('An unexpected error occurred');
|
|
210
|
+
}
|
|
211
|
+
if (logger) {
|
|
212
|
+
logger.error('Converting unknown to AppError', { value: String(error) });
|
|
213
|
+
}
|
|
214
|
+
else {
|
|
215
|
+
console.error('[normalizeError] Converting unknown to AppError:', error);
|
|
216
|
+
}
|
|
217
|
+
return new InternalError('An unexpected error occurred');
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* Extract a human-readable message from an unknown thrown value.
|
|
221
|
+
*
|
|
222
|
+
* When called with a single argument the behaviour matches the former
|
|
223
|
+
* `runtime-service/utils/error-message` helper (`String(err)` for
|
|
224
|
+
* non-Error values). When a `fallback` string is supplied the
|
|
225
|
+
* behaviour matches the former `control/web/lib/errors` helper
|
|
226
|
+
* (returns the fallback when no meaningful message can be extracted).
|
|
227
|
+
*/
|
|
228
|
+
export function getErrorMessage(error, fallback) {
|
|
229
|
+
if (typeof error === 'string' && error.trim()) {
|
|
230
|
+
return error;
|
|
231
|
+
}
|
|
232
|
+
if (error instanceof Error && error.message.trim()) {
|
|
233
|
+
return error.message;
|
|
234
|
+
}
|
|
235
|
+
if (typeof error === 'object' && error !== null) {
|
|
236
|
+
const candidate = error.message;
|
|
237
|
+
if (typeof candidate === 'string' && candidate.trim()) {
|
|
238
|
+
return candidate;
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
return fallback !== undefined ? fallback : String(error);
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* Log error with full details for server-side debugging
|
|
245
|
+
*/
|
|
246
|
+
export function logError(error, context, logger) {
|
|
247
|
+
const errorInfo = isAppError(error)
|
|
248
|
+
? {
|
|
249
|
+
name: error.name,
|
|
250
|
+
code: error.code,
|
|
251
|
+
message: error.message,
|
|
252
|
+
statusCode: error.statusCode,
|
|
253
|
+
details: error.details,
|
|
254
|
+
stack: error.stack,
|
|
255
|
+
}
|
|
256
|
+
: {
|
|
257
|
+
message: error instanceof Error ? error.message : String(error),
|
|
258
|
+
stack: error instanceof Error ? error.stack : undefined,
|
|
259
|
+
};
|
|
260
|
+
if (logger) {
|
|
261
|
+
logger.error('Error', { ...errorInfo, ...context });
|
|
262
|
+
}
|
|
263
|
+
else {
|
|
264
|
+
console.error('[Error]', {
|
|
265
|
+
...errorInfo,
|
|
266
|
+
context,
|
|
267
|
+
timestamp: new Date().toISOString(),
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"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"}
|
package/dist/id.d.ts
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ID Generation Utilities
|
|
3
|
+
*
|
|
4
|
+
* Provides cryptographically secure random ID generation
|
|
5
|
+
* for use across all takos packages.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Generate a cryptographically secure random ID using alphanumeric characters.
|
|
9
|
+
*
|
|
10
|
+
* Uses crypto.getRandomValues() for secure randomness.
|
|
11
|
+
* Character set: lowercase letters and digits (36 chars).
|
|
12
|
+
*
|
|
13
|
+
* @param length - Length of the ID (default: 12)
|
|
14
|
+
* @returns Random alphanumeric string
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```typescript
|
|
18
|
+
* const id = generateId(); // e.g., "a1b2c3d4e5f6"
|
|
19
|
+
* const longId = generateId(24); // e.g., "a1b2c3d4e5f6g7h8i9j0k1l2"
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
export declare function generateId(length?: number): string;
|
|
23
|
+
//# sourceMappingURL=id.d.ts.map
|
package/dist/id.d.ts.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"id.d.ts","sourceRoot":"","sources":["../src/id.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,UAAU,CAAC,MAAM,GAAE,MAAW,GAAG,MAAM,CAStD"}
|
package/dist/id.js
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ID Generation Utilities
|
|
3
|
+
*
|
|
4
|
+
* Provides cryptographically secure random ID generation
|
|
5
|
+
* for use across all takos packages.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Generate a cryptographically secure random ID using alphanumeric characters.
|
|
9
|
+
*
|
|
10
|
+
* Uses crypto.getRandomValues() for secure randomness.
|
|
11
|
+
* Character set: lowercase letters and digits (36 chars).
|
|
12
|
+
*
|
|
13
|
+
* @param length - Length of the ID (default: 12)
|
|
14
|
+
* @returns Random alphanumeric string
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```typescript
|
|
18
|
+
* const id = generateId(); // e.g., "a1b2c3d4e5f6"
|
|
19
|
+
* const longId = generateId(24); // e.g., "a1b2c3d4e5f6g7h8i9j0k1l2"
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
export function generateId(length = 12) {
|
|
23
|
+
const chars = 'abcdefghijklmnopqrstuvwxyz0123456789';
|
|
24
|
+
const array = new Uint8Array(length);
|
|
25
|
+
crypto.getRandomValues(array);
|
|
26
|
+
let result = '';
|
|
27
|
+
for (let i = 0; i < length; i++) {
|
|
28
|
+
result += chars[array[i] % chars.length];
|
|
29
|
+
}
|
|
30
|
+
return result;
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=id.js.map
|
package/dist/id.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"id.js","sourceRoot":"","sources":["../src/id.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,UAAU,CAAC,SAAiB,EAAE;IAC5C,MAAM,KAAK,GAAG,sCAAsC,CAAC;IACrD,MAAM,KAAK,GAAG,IAAI,UAAU,CAAC,MAAM,CAAC,CAAC;IACrC,MAAM,CAAC,eAAe,CAAC,KAAK,CAAC,CAAC;IAC9B,IAAI,MAAM,GAAG,EAAE,CAAC;IAChB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QAChC,MAAM,IAAI,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC;IAC3C,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* takos-common - Shared utilities for Takos services
|
|
3
|
+
*
|
|
4
|
+
* This package provides common utilities used across all takos packages:
|
|
5
|
+
* - ID generation (generateId)
|
|
6
|
+
* - Validation helpers (isLocalhost, isPrivateIP)
|
|
7
|
+
* - Error handling (AppError, ValidationError, etc.)
|
|
8
|
+
* - Structured logging (createLogger)
|
|
9
|
+
* - Hono middleware
|
|
10
|
+
*/
|
|
11
|
+
export { generateId, } from './id.js';
|
|
12
|
+
export { isLocalhost, isPrivateIP, } from './validation.js';
|
|
13
|
+
export { createLogger, type Logger, type LogLevel } from './logger.js';
|
|
14
|
+
export { throwIfAborted } from './abort.js';
|
|
15
|
+
export { parseIntEnv, parseIntEnvRequired, parseIntValue, parseFloatEnv, parseFloatValue, } from './env-parse.js';
|
|
16
|
+
export { ErrorCodes, type ErrorCode, AppError, BadRequestError, AuthenticationError, PaymentRequiredError, AuthorizationError, NotFoundError, ConflictError, GoneError, PayloadTooLargeError, ValidationError, RateLimitError, InternalError, NotImplementedError, BadGatewayError, ServiceUnavailableError, GatewayTimeoutError, isAppError, normalizeError, logError, getErrorMessage, type ErrorResponse, type ValidationErrorDetail, } from './errors.js';
|
|
17
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAKH,OAAO,EACL,UAAU,GACX,MAAM,SAAS,CAAC;AAKjB,OAAO,EACL,WAAW,EACX,WAAW,GACZ,MAAM,iBAAiB,CAAC;AAKzB,OAAO,EAAE,YAAY,EAAE,KAAK,MAAM,EAAE,KAAK,QAAQ,EAAE,MAAM,aAAa,CAAC;AAKvE,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAK5C,OAAO,EACL,WAAW,EACX,mBAAmB,EACnB,aAAa,EACb,aAAa,EACb,eAAe,GAChB,MAAM,gBAAgB,CAAC;AAKxB,OAAO,EAEL,UAAU,EACV,KAAK,SAAS,EAEd,QAAQ,EAER,eAAe,EACf,mBAAmB,EACnB,oBAAoB,EACpB,kBAAkB,EAClB,aAAa,EACb,aAAa,EACb,SAAS,EACT,oBAAoB,EACpB,eAAe,EACf,cAAc,EACd,aAAa,EACb,mBAAmB,EACnB,eAAe,EACf,uBAAuB,EACvB,mBAAmB,EAEnB,UAAU,EACV,cAAc,EACd,QAAQ,EACR,eAAe,EAEf,KAAK,aAAa,EAClB,KAAK,qBAAqB,GAC3B,MAAM,aAAa,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* takos-common - Shared utilities for Takos services
|
|
3
|
+
*
|
|
4
|
+
* This package provides common utilities used across all takos packages:
|
|
5
|
+
* - ID generation (generateId)
|
|
6
|
+
* - Validation helpers (isLocalhost, isPrivateIP)
|
|
7
|
+
* - Error handling (AppError, ValidationError, etc.)
|
|
8
|
+
* - Structured logging (createLogger)
|
|
9
|
+
* - Hono middleware
|
|
10
|
+
*/
|
|
11
|
+
// =============================================================================
|
|
12
|
+
// ID Generation Utilities
|
|
13
|
+
// =============================================================================
|
|
14
|
+
export { generateId, } from './id.js';
|
|
15
|
+
// =============================================================================
|
|
16
|
+
// Validation Utilities
|
|
17
|
+
// =============================================================================
|
|
18
|
+
export { isLocalhost, isPrivateIP, } from './validation.js';
|
|
19
|
+
// =============================================================================
|
|
20
|
+
// Structured Logging
|
|
21
|
+
// =============================================================================
|
|
22
|
+
export { createLogger } from './logger.js';
|
|
23
|
+
// =============================================================================
|
|
24
|
+
// Abort Signal Utilities
|
|
25
|
+
// =============================================================================
|
|
26
|
+
export { throwIfAborted } from './abort.js';
|
|
27
|
+
// =============================================================================
|
|
28
|
+
// Environment Variable Parsing
|
|
29
|
+
// =============================================================================
|
|
30
|
+
export { parseIntEnv, parseIntEnvRequired, parseIntValue, parseFloatEnv, parseFloatValue, } from './env-parse.js';
|
|
31
|
+
// =============================================================================
|
|
32
|
+
// Error Handling
|
|
33
|
+
// =============================================================================
|
|
34
|
+
export {
|
|
35
|
+
// Error codes
|
|
36
|
+
ErrorCodes,
|
|
37
|
+
// Base errors
|
|
38
|
+
AppError,
|
|
39
|
+
// HTTP errors
|
|
40
|
+
BadRequestError, AuthenticationError, PaymentRequiredError, AuthorizationError, NotFoundError, ConflictError, GoneError, PayloadTooLargeError, ValidationError, RateLimitError, InternalError, NotImplementedError, BadGatewayError, ServiceUnavailableError, GatewayTimeoutError,
|
|
41
|
+
// Utility functions
|
|
42
|
+
isAppError, normalizeError, logError, getErrorMessage, } from './errors.js';
|
|
43
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,gFAAgF;AAChF,0BAA0B;AAC1B,gFAAgF;AAChF,OAAO,EACL,UAAU,GACX,MAAM,SAAS,CAAC;AAEjB,gFAAgF;AAChF,uBAAuB;AACvB,gFAAgF;AAChF,OAAO,EACL,WAAW,EACX,WAAW,GACZ,MAAM,iBAAiB,CAAC;AAEzB,gFAAgF;AAChF,qBAAqB;AACrB,gFAAgF;AAChF,OAAO,EAAE,YAAY,EAA8B,MAAM,aAAa,CAAC;AAEvE,gFAAgF;AAChF,yBAAyB;AACzB,gFAAgF;AAChF,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAE5C,gFAAgF;AAChF,+BAA+B;AAC/B,gFAAgF;AAChF,OAAO,EACL,WAAW,EACX,mBAAmB,EACnB,aAAa,EACb,aAAa,EACb,eAAe,GAChB,MAAM,gBAAgB,CAAC;AAExB,gFAAgF;AAChF,iBAAiB;AACjB,gFAAgF;AAChF,OAAO;AACL,cAAc;AACd,UAAU;AAEV,cAAc;AACd,QAAQ;AACR,cAAc;AACd,eAAe,EACf,mBAAmB,EACnB,oBAAoB,EACpB,kBAAkB,EAClB,aAAa,EACb,aAAa,EACb,SAAS,EACT,oBAAoB,EACpB,eAAe,EACf,cAAc,EACd,aAAa,EACb,mBAAmB,EACnB,eAAe,EACf,uBAAuB,EACvB,mBAAmB;AACnB,oBAAoB;AACpB,UAAU,EACV,cAAc,EACd,QAAQ,EACR,eAAe,GAIhB,MAAM,aAAa,CAAC"}
|
package/dist/jwt.d.ts
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* JWT utilities for service-to-service authentication
|
|
3
|
+
* Uses RS256 (RSA with SHA-256) for asymmetric signing
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Service token JWT payload structure
|
|
7
|
+
*/
|
|
8
|
+
export interface ServiceTokenPayload {
|
|
9
|
+
iss: string;
|
|
10
|
+
sub: string;
|
|
11
|
+
aud: string;
|
|
12
|
+
exp: number;
|
|
13
|
+
iat: number;
|
|
14
|
+
jti: string;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Extended payload with optional custom claims
|
|
18
|
+
*/
|
|
19
|
+
export interface ServiceTokenPayloadWithClaims extends ServiceTokenPayload {
|
|
20
|
+
[key: string]: unknown;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Options for verifying a service token
|
|
24
|
+
*/
|
|
25
|
+
export interface VerifyServiceTokenOptions {
|
|
26
|
+
/** JWT token string */
|
|
27
|
+
token: string;
|
|
28
|
+
/** Public key in PEM format (used as default/fallback) */
|
|
29
|
+
publicKey: string;
|
|
30
|
+
/** Additional public keys for rotation, keyed by kid (optional) */
|
|
31
|
+
publicKeys?: Record<string, string>;
|
|
32
|
+
/** Expected audience (required) */
|
|
33
|
+
expectedAudience: string;
|
|
34
|
+
/** Expected issuer (required) */
|
|
35
|
+
expectedIssuer: string;
|
|
36
|
+
/** Clock tolerance in seconds for exp/iat checks (default: 30) */
|
|
37
|
+
clockToleranceSeconds?: number;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Result of token verification
|
|
41
|
+
*/
|
|
42
|
+
export interface VerifyServiceTokenResult {
|
|
43
|
+
valid: boolean;
|
|
44
|
+
payload?: ServiceTokenPayloadWithClaims;
|
|
45
|
+
error?: string;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Verify a service token using RS256 algorithm
|
|
49
|
+
*
|
|
50
|
+
* @param options - Verification options including token and public key
|
|
51
|
+
* @returns Verification result with payload if valid
|
|
52
|
+
*/
|
|
53
|
+
export declare function verifyServiceToken(options: VerifyServiceTokenOptions): VerifyServiceTokenResult;
|
|
54
|
+
//# sourceMappingURL=jwt.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jwt.d.ts","sourceRoot":"","sources":["../src/jwt.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;CACb;AAED;;GAEG;AACH,MAAM,WAAW,6BAA8B,SAAQ,mBAAmB;IACxE,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED;;GAEG;AACH,MAAM,WAAW,yBAAyB;IACxC,uBAAuB;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,0DAA0D;IAC1D,SAAS,EAAE,MAAM,CAAC;IAClB,mEAAmE;IACnE,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACpC,mCAAmC;IACnC,gBAAgB,EAAE,MAAM,CAAC;IACzB,iCAAiC;IACjC,cAAc,EAAE,MAAM,CAAC;IACvB,kEAAkE;IAClE,qBAAqB,CAAC,EAAE,MAAM,CAAC;CAChC;AAED;;GAEG;AACH,MAAM,WAAW,wBAAwB;IACvC,KAAK,EAAE,OAAO,CAAC;IACf,OAAO,CAAC,EAAE,6BAA6B,CAAC;IACxC,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AA+BD;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,yBAAyB,GAAG,wBAAwB,CAiI/F"}
|
package/dist/jwt.js
ADDED
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* JWT utilities for service-to-service authentication
|
|
3
|
+
* Uses RS256 (RSA with SHA-256) for asymmetric signing
|
|
4
|
+
*/
|
|
5
|
+
import * as crypto from 'crypto';
|
|
6
|
+
// Base64URL encoding/decoding utilities
|
|
7
|
+
function base64UrlEncode(data) {
|
|
8
|
+
const buffer = typeof data === 'string' ? Buffer.from(data) : data;
|
|
9
|
+
return buffer.toString('base64')
|
|
10
|
+
.replace(/\+/g, '-')
|
|
11
|
+
.replace(/\//g, '_')
|
|
12
|
+
.replace(/=+$/, '');
|
|
13
|
+
}
|
|
14
|
+
function base64UrlDecode(str) {
|
|
15
|
+
// Add padding back
|
|
16
|
+
let padded = str.replace(/-/g, '+').replace(/_/g, '/');
|
|
17
|
+
const padding = (4 - (padded.length % 4)) % 4;
|
|
18
|
+
padded += '='.repeat(padding);
|
|
19
|
+
const buf = Buffer.from(padded, 'base64');
|
|
20
|
+
if (buf.length === 0 && str.length > 0) {
|
|
21
|
+
throw new Error('Invalid base64url encoding');
|
|
22
|
+
}
|
|
23
|
+
return buf;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Generate a unique JWT ID (jti)
|
|
27
|
+
*/
|
|
28
|
+
function generateJTI() {
|
|
29
|
+
const bytes = crypto.randomBytes(16);
|
|
30
|
+
return base64UrlEncode(bytes);
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Verify a service token using RS256 algorithm
|
|
34
|
+
*
|
|
35
|
+
* @param options - Verification options including token and public key
|
|
36
|
+
* @returns Verification result with payload if valid
|
|
37
|
+
*/
|
|
38
|
+
export function verifyServiceToken(options) {
|
|
39
|
+
const { token, publicKey, publicKeys = {}, expectedAudience, expectedIssuer, clockToleranceSeconds = 30, } = options;
|
|
40
|
+
try {
|
|
41
|
+
// Split token
|
|
42
|
+
const parts = token.split('.');
|
|
43
|
+
if (parts.length !== 3) {
|
|
44
|
+
return { valid: false, error: 'Invalid token format' };
|
|
45
|
+
}
|
|
46
|
+
const [encodedHeader, encodedPayload, encodedSignature] = parts;
|
|
47
|
+
// Decode header
|
|
48
|
+
let header;
|
|
49
|
+
try {
|
|
50
|
+
header = JSON.parse(base64UrlDecode(encodedHeader).toString('utf-8'));
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
return { valid: false, error: 'Invalid header encoding' };
|
|
54
|
+
}
|
|
55
|
+
// Verify algorithm
|
|
56
|
+
if (header.alg !== 'RS256') {
|
|
57
|
+
return { valid: false, error: `Unsupported algorithm: ${header.alg}` };
|
|
58
|
+
}
|
|
59
|
+
// Resolve which public key(s) to try based on kid.
|
|
60
|
+
// When kid is present, use only the matching key (strict rotation).
|
|
61
|
+
// When kid is absent, use only the default public key (latest/current key)
|
|
62
|
+
// to prevent accepting tokens signed with any older rotation key.
|
|
63
|
+
const keysToTry = [];
|
|
64
|
+
if (header.kid) {
|
|
65
|
+
if (publicKeys[header.kid]) {
|
|
66
|
+
// Token has a kid and we have a matching key - use it exclusively
|
|
67
|
+
keysToTry.push(publicKeys[header.kid]);
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
// kid is present but unknown - reject immediately
|
|
71
|
+
return { valid: false, error: `Unknown key ID: ${header.kid}` };
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
// No kid: only try the default (latest) public key
|
|
76
|
+
keysToTry.push(publicKey);
|
|
77
|
+
}
|
|
78
|
+
// Try each key for signature verification
|
|
79
|
+
const signingInput = `${encodedHeader}.${encodedPayload}`;
|
|
80
|
+
const signature = base64UrlDecode(encodedSignature);
|
|
81
|
+
let isValidSignature = false;
|
|
82
|
+
for (const key of keysToTry) {
|
|
83
|
+
const verify = crypto.createVerify('RSA-SHA256');
|
|
84
|
+
verify.update(signingInput);
|
|
85
|
+
verify.end();
|
|
86
|
+
if (verify.verify(key, signature)) {
|
|
87
|
+
isValidSignature = true;
|
|
88
|
+
break;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
if (!isValidSignature) {
|
|
92
|
+
return { valid: false, error: 'Invalid signature' };
|
|
93
|
+
}
|
|
94
|
+
// Decode payload
|
|
95
|
+
let payload;
|
|
96
|
+
try {
|
|
97
|
+
payload = JSON.parse(base64UrlDecode(encodedPayload).toString('utf-8'));
|
|
98
|
+
}
|
|
99
|
+
catch {
|
|
100
|
+
return { valid: false, error: 'Invalid payload encoding' };
|
|
101
|
+
}
|
|
102
|
+
// Required verification config
|
|
103
|
+
if (!expectedAudience || !expectedIssuer) {
|
|
104
|
+
return { valid: false, error: 'expectedAudience and expectedIssuer are required' };
|
|
105
|
+
}
|
|
106
|
+
// Required claims
|
|
107
|
+
if (typeof payload.iss !== 'string' || payload.iss.length === 0) {
|
|
108
|
+
return { valid: false, error: 'Missing or invalid iss claim' };
|
|
109
|
+
}
|
|
110
|
+
if (typeof payload.aud !== 'string' || payload.aud.length === 0) {
|
|
111
|
+
return { valid: false, error: 'Missing or invalid aud claim' };
|
|
112
|
+
}
|
|
113
|
+
if (typeof payload.sub !== 'string' || payload.sub.length === 0) {
|
|
114
|
+
return { valid: false, error: 'Missing or invalid sub claim' };
|
|
115
|
+
}
|
|
116
|
+
if (typeof payload.jti !== 'string' || payload.jti.length === 0) {
|
|
117
|
+
return { valid: false, error: 'Missing or invalid jti claim' };
|
|
118
|
+
}
|
|
119
|
+
if (typeof payload.exp !== 'number' || !Number.isFinite(payload.exp)) {
|
|
120
|
+
return { valid: false, error: 'Missing or invalid exp claim' };
|
|
121
|
+
}
|
|
122
|
+
if (typeof payload.iat !== 'number' || !Number.isFinite(payload.iat)) {
|
|
123
|
+
return { valid: false, error: 'Missing or invalid iat claim' };
|
|
124
|
+
}
|
|
125
|
+
// Verify expiration
|
|
126
|
+
const now = Math.floor(Date.now() / 1000);
|
|
127
|
+
if (payload.exp + clockToleranceSeconds < now) {
|
|
128
|
+
return { valid: false, error: 'Token has expired' };
|
|
129
|
+
}
|
|
130
|
+
// Verify not-before (iat - tolerance)
|
|
131
|
+
if (payload.iat - clockToleranceSeconds > now) {
|
|
132
|
+
return { valid: false, error: 'Token issued in the future' };
|
|
133
|
+
}
|
|
134
|
+
// Verify audience
|
|
135
|
+
if (payload.aud !== expectedAudience) {
|
|
136
|
+
return { valid: false, error: `Invalid audience: expected ${expectedAudience}, got ${payload.aud}` };
|
|
137
|
+
}
|
|
138
|
+
// Verify issuer
|
|
139
|
+
if (payload.iss !== expectedIssuer) {
|
|
140
|
+
return { valid: false, error: `Invalid issuer: expected ${expectedIssuer}, got ${payload.iss}` };
|
|
141
|
+
}
|
|
142
|
+
return { valid: true, payload };
|
|
143
|
+
}
|
|
144
|
+
catch (err) {
|
|
145
|
+
const message = err instanceof Error ? err.message : 'Unknown error';
|
|
146
|
+
return { valid: false, error: `Verification failed: ${message}` };
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
//# sourceMappingURL=jwt.js.map
|
package/dist/jwt.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jwt.js","sourceRoot":"","sources":["../src/jwt.ts"],"names":[],"mappings":"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"}
|
package/dist/logger.d.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
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
|
+
export type LogLevel = 'debug' | 'info' | 'warn' | 'error';
|
|
8
|
+
interface LoggerOptions {
|
|
9
|
+
service?: string;
|
|
10
|
+
level?: LogLevel;
|
|
11
|
+
defaultFields?: Record<string, unknown>;
|
|
12
|
+
}
|
|
13
|
+
export interface Logger {
|
|
14
|
+
debug(msg: string, data?: Record<string, unknown>): void;
|
|
15
|
+
info(msg: string, data?: Record<string, unknown>): void;
|
|
16
|
+
warn(msg: string, data?: Record<string, unknown>): void;
|
|
17
|
+
error(msg: string, data?: Record<string, unknown>): void;
|
|
18
|
+
child(fields: Record<string, unknown>): Logger;
|
|
19
|
+
}
|
|
20
|
+
export declare function createLogger(opts?: LoggerOptions): Logger;
|
|
21
|
+
export {};
|
|
22
|
+
//# sourceMappingURL=logger.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,MAAM,QAAQ,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC;AAiB3D,UAAU,aAAa;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,QAAQ,CAAC;IACjB,aAAa,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACzC;AAED,MAAM,WAAW,MAAM;IACrB,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IACzD,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IACxD,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IACxD,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IACzD,KAAK,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC;CAChD;AAiGD,wBAAgB,YAAY,CAAC,IAAI,CAAC,EAAE,aAAa,GAAG,MAAM,CAEzD"}
|