@a_jackie_z/fastify 1.3.3 → 1.4.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 CHANGED
@@ -5,15 +5,22 @@ A collection of Fastify plugins and utilities for building robust web applicatio
5
5
  ## Installation
6
6
 
7
7
  ```bash
8
+ # For server-side Fastify applications
8
9
  npm install @a_jackie_z/fastify
10
+
11
+ # For frontend applications (types and schemas only)
12
+ npm install @a_jackie_z/fastify-types
9
13
  ```
10
14
 
15
+ > **Note:** If you're building a frontend application (React, Vue, etc.) and only need API types and response schemas, install `@a_jackie_z/fastify-types` instead. It's frontend-compatible and has no Node.js dependencies.
16
+
11
17
  ## Quick Start
12
18
 
13
19
  ### Basic Setup
14
20
 
15
21
  ```typescript
16
- import { createFastify, runFastify, formatSuccess } from '@a_jackie_z/fastify'
22
+ import { createFastify, runFastify } from '@a_jackie_z/fastify'
23
+ import { formatSuccess } from '@a_jackie_z/fastify-types'
17
24
  import { z } from 'zod'
18
25
 
19
26
  const app = await createFastify()
@@ -40,6 +47,38 @@ app.route({
40
47
  await runFastify(app, '0.0.0.0', 3000)
41
48
  ```
42
49
 
50
+ ## Package Architecture
51
+
52
+ This package is split into two complementary packages for better frontend compatibility:
53
+
54
+ ### `@a_jackie_z/fastify` (Server Package)
55
+ **For:** Node.js server applications
56
+ **Includes:**
57
+ - Fastify server creation and configuration (`createFastify`, `runFastify`)
58
+ - JWT service with token generation/verification (`FastifyJwtService`)
59
+ - Server-side crypto utilities (`generateId`, `generateSessionToken`)
60
+ - Fastify plugins and middleware
61
+ - Server-specific types (`CreateFastifyOptions`, `FastifyServer`)
62
+
63
+ ### `@a_jackie_z/fastify-types` (Types Package)
64
+ **For:** Frontend applications (React, Vue, etc.) and API contracts
65
+ **Includes:**
66
+ - Response types (`SuccessResponse`, `ErrorResponse`, `ValidationDetail`)
67
+ - Response formatters (`formatSuccess`, `formatError`)
68
+ - Zod schemas (`successResponseSchema`, `errorResponseSchema`)
69
+ - JWT configuration types (`TokenTypeConfig`, `SignTokenOptions`)
70
+ - Error utilities (`createError`, `HTTP_STATUS_CODES`)
71
+ - No Node.js dependencies - browser compatible
72
+
73
+ ```typescript
74
+ // Server code
75
+ import { createFastify, generateId } from '@a_jackie_z/fastify'
76
+ import { formatSuccess } from '@a_jackie_z/fastify-types'
77
+
78
+ // Frontend code
79
+ import { successResponseSchema, type SuccessResponse } from '@a_jackie_z/fastify-types'
80
+ ```
81
+
43
82
  ## Features
44
83
 
45
84
  - **Dynamic JWT Authentication** - Flexible multi-token-type JWT system with per-type configuration
@@ -147,6 +186,9 @@ The JWT system supports **dynamic token types** where each type can have its own
147
186
  Each token type is configured independently:
148
187
 
149
188
  ```typescript
189
+ // TokenTypeConfig is available in @a_jackie_z/fastify-types
190
+ import type { TokenTypeConfig } from '@a_jackie_z/fastify-types'
191
+
150
192
  interface TokenTypeConfig {
151
193
  headerName: string // Header to extract token from
152
194
  expiresIn: string // Token expiration (e.g., '15m', '7d', '1h')
@@ -229,7 +271,7 @@ app.get('/public', { config: { jwtTypes: false } }, handler)
229
271
  ### 2. Authentication Flow - Login Route
230
272
 
231
273
  ```typescript
232
- import { formatSuccess, formatError } from '@a_jackie_z/fastify'
274
+ import { formatSuccess, formatError } from '@a_jackie_z/fastify-types'
233
275
 
234
276
  app.route({
235
277
  method: 'POST',
@@ -620,7 +662,7 @@ All responses should follow a consistent format using the provided utility funct
620
662
  #### Success Response Format
621
663
 
622
664
  ```typescript
623
- import { formatSuccess } from '@a_jackie_z/fastify'
665
+ import { formatSuccess } from '@a_jackie_z/fastify-types'
624
666
 
625
667
  app.route({
626
668
  method: 'GET',
@@ -656,7 +698,7 @@ app.route({
656
698
  #### Error Response Format
657
699
 
658
700
  ```typescript
659
- import { formatError } from '@a_jackie_z/fastify'
701
+ import { formatError } from '@a_jackie_z/fastify-types'
660
702
 
661
703
  app.route({
662
704
  method: 'DELETE',
@@ -801,11 +843,13 @@ config: { jwtTypes: ['access', 'service'] }
801
843
 
802
844
  ## API Reference
803
845
 
804
- ### `createFastify(options?: CreateFastifyOptions): Promise<FastifyServer>`
846
+ ### Server Functions (`@a_jackie_z/fastify`)
847
+
848
+ #### `createFastify(options?: CreateFastifyOptions): Promise<FastifyServer>`
805
849
 
806
850
  Creates and configures a Fastify server instance with Zod support and optional plugins.
807
851
 
808
- ### `runFastify(fastify: FastifyServer, host: string, port: number): Promise<void>`
852
+ #### `runFastify(fastify: FastifyServer, host: string, port: number): Promise<void>`
809
853
 
810
854
  Starts the Fastify server. Handles errors and exits the process if the server fails to start.
811
855
 
@@ -821,7 +865,22 @@ const app = await createFastify()
821
865
  await runFastify(app, '0.0.0.0', 3000)
822
866
  ```
823
867
 
824
- ### Response Formatting Functions
868
+ #### Server-Side Crypto Utilities
869
+
870
+ ```typescript
871
+ import { generateId, generateSessionToken, generateSecureString } from '@a_jackie_z/fastify'
872
+
873
+ // Generate 16-character alphanumeric ID
874
+ const id = generateId() // "A1b2C3d4E5f6G7h8"
875
+
876
+ // Generate 64-character hex session token
877
+ const token = generateSessionToken() // "abc123...def789"
878
+
879
+ // Generate custom secure string
880
+ const customId = generateSecureString(8, '0123456789') // "42875391"
881
+ ```
882
+
883
+ ### Response Formatting Functions (`@a_jackie_z/fastify-types`)
825
884
 
826
885
  #### `formatSuccess<T>(status: number, data: T): SuccessResponse<T>`
827
886
 
@@ -875,7 +934,7 @@ return reply.status(404).send(
875
934
  // Returns: { status: 404, success: false, error: 'Not Found', message: 'User not found' }
876
935
  ```
877
936
 
878
- ### Response Type Interfaces
937
+ ### Response Type Interfaces (`@a_jackie_z/fastify-types`)
879
938
 
880
939
  #### `SuccessResponse<T>`
881
940
  ```typescript
@@ -905,7 +964,7 @@ interface ValidationDetail {
905
964
  }
906
965
  ```
907
966
 
908
- ### Zod Schema Helpers
967
+ ### Zod Schema Helpers (`@a_jackie_z/fastify-types`)
909
968
 
910
969
  For defining response schemas with Zod validation:
911
970
 
@@ -921,7 +980,7 @@ Creates a Zod schema for standardized success responses.
921
980
  **Example:**
922
981
  ```typescript
923
982
  import { z } from 'zod'
924
- import { successResponseSchema } from '@a_jackie_z/fastify'
983
+ import { successResponseSchema } from '@a_jackie_z/fastify-types'
925
984
 
926
985
  const userSchema = z.object({
927
986
  id: z.string(),
@@ -948,7 +1007,7 @@ Zod schema for standardized error responses. Matches `ErrorResponse` interface.
948
1007
 
949
1008
  **Example:**
950
1009
  ```typescript
951
- import { errorResponseSchema } from '@a_jackie_z/fastify'
1010
+ import { errorResponseSchema } from '@a_jackie_z/fastify-types'
952
1011
 
953
1012
  const myRouteSchema = {
954
1013
  response: {
package/dist/index.d.ts CHANGED
@@ -1,12 +1,11 @@
1
1
  import * as fastify from 'fastify';
2
- import { FastifyServerOptions, FastifyInstance, RawServerDefault, FastifyBaseLogger, FastifyContextConfig, FastifyPluginCallback, FastifyError } from 'fastify';
2
+ import { FastifyServerOptions, FastifyInstance, RawServerDefault, FastifyBaseLogger, FastifyContextConfig, FastifyPluginCallback } from 'fastify';
3
3
  export { FastifyErrorCodes, FastifyReply, FastifyRequest } from 'fastify';
4
4
  import { RateLimitPluginOptions } from '@fastify/rate-limit';
5
5
  import { ZodTypeProvider } from 'fastify-type-provider-zod';
6
6
  export * from 'fastify-type-provider-zod';
7
7
  import { IncomingMessage, ServerResponse } from 'node:http';
8
- import { Algorithm, JwtPayload } from 'jsonwebtoken';
9
- import { z } from 'zod';
8
+ import { TokenTypeConfig } from '@a_jackie_z/fastify-types';
10
9
 
11
10
  interface FastifyJwtServiceOptions {
12
11
  secrets: Record<string, string>;
@@ -42,53 +41,6 @@ declare class FastifyJwtService {
42
41
  getTokenTypeNames(): string[];
43
42
  }
44
43
 
45
- interface SignTokenOptions {
46
- algorithm?: Algorithm;
47
- iss?: string;
48
- aud?: string;
49
- header?: {
50
- typ?: string;
51
- cty?: string;
52
- [key: string]: any;
53
- };
54
- }
55
- interface VerifyTokenOptions {
56
- allowedIss?: string[];
57
- debug?: boolean;
58
- expectedHeader?: {
59
- typ?: string;
60
- cty?: string;
61
- [key: string]: any;
62
- };
63
- }
64
- /**
65
- * Sign a JWT token with kid, expiration, and optional JWT header parameters
66
- */
67
- declare function signToken(payload: Record<string, any>, secret: string, kid: string, expiresIn: string, options?: SignTokenOptions): string;
68
- /**
69
- * Verify a JWT token with kid-based secret selection and optional issuer validation
70
- */
71
- declare function verifyToken(token: string, secrets: Record<string, string>, options?: VerifyTokenOptions): JwtPayload;
72
- /**
73
- * Extract token from header value with Bearer prefix stripping
74
- * All JWT tokens must use Bearer prefix format: "Bearer <token>"
75
- */
76
- declare function extractTokenFromHeader(headerValue: string | string[] | undefined, headerName: string, debug?: boolean): string | null;
77
-
78
- interface TokenTypeConfig {
79
- headerName: string;
80
- expiresIn: string;
81
- payloadSchema?: z.ZodSchema<any>;
82
- algorithm?: Algorithm;
83
- iss?: string;
84
- aud?: string;
85
- allowedIss?: string[];
86
- header?: {
87
- typ?: string;
88
- cty?: string;
89
- [key: string]: any;
90
- };
91
- }
92
44
  declare module 'fastify' {
93
45
  interface FastifyContextConfig {
94
46
  jwtTypes?: string[] | false;
@@ -102,6 +54,8 @@ declare module 'fastify' {
102
54
  }
103
55
  interface CreateFastifyOptions {
104
56
  logger?: FastifyServerOptions['loggerInstance'];
57
+ requestTimeout?: number;
58
+ bodyLimit?: number;
105
59
  rateLimit?: {
106
60
  global?: RateLimitPluginOptions;
107
61
  };
@@ -121,39 +75,20 @@ interface CreateFastifyOptions {
121
75
  }
122
76
  type FastifyServer = FastifyInstance<RawServerDefault, IncomingMessage, ServerResponse, FastifyBaseLogger, ZodTypeProvider> & FastifyContextConfig;
123
77
 
124
- interface ValidationDetail {
125
- field: string;
126
- message: string;
127
- }
128
- interface SuccessResponse<T> {
129
- status: number;
130
- success: true;
131
- data: T;
132
- }
133
- interface ErrorResponse {
134
- status: number;
135
- success: false;
136
- error: string;
137
- message: string;
138
- details?: ValidationDetail[];
139
- }
140
- declare function formatSuccess<T>(status: number, data: T): SuccessResponse<T>;
141
- declare function formatError(status: number, error: string, message: string, details?: ValidationDetail[]): ErrorResponse;
142
- declare const successResponseSchema: <T extends z.ZodTypeAny>(dataSchema: T) => z.ZodObject<{
143
- status: z.ZodNumber;
144
- success: z.ZodLiteral<true>;
145
- data: T;
146
- }, z.core.$strip>;
147
- declare const errorResponseSchema: z.ZodObject<{
148
- status: z.ZodNumber;
149
- success: z.ZodLiteral<false>;
150
- error: z.ZodString;
151
- message: z.ZodString;
152
- details: z.ZodOptional<z.ZodArray<z.ZodObject<{
153
- field: z.ZodString;
154
- message: z.ZodString;
155
- }, z.core.$strip>>>;
156
- }, z.core.$strip>;
78
+ /**
79
+ * Generate a random 16-character alphanumeric ID
80
+ */
81
+ declare function generateId(): string;
82
+ /**
83
+ * Generate a secure session token (64-character hex string)
84
+ */
85
+ declare function generateSessionToken(): string;
86
+ /**
87
+ * Generate a secure random string with specified length
88
+ * @param length - Length of the string to generate
89
+ * @param alphabet - Alphabet to use (defaults to alphanumeric)
90
+ */
91
+ declare function generateSecureString(length: number, alphabet?: string): string;
157
92
 
158
93
  declare function createFastify(options?: CreateFastifyOptions): Promise<FastifyServer>;
159
94
  declare function runFastify(fastify: FastifyServer, host: string, port: number): Promise<void>;
@@ -170,12 +105,6 @@ declare function createFastifyPlugin(cb: FastifyPluginCallback): (fastify: Fasti
170
105
 
171
106
  declare const healthPlugin: (fastify: FastifyServer, options: Parameters<fastify.FastifyPluginCallback>[1], done: Parameters<fastify.FastifyPluginCallback>[2]) => void;
172
107
 
173
- interface CreateErrorOptions {
174
- statusCode?: number;
175
- message: string;
176
- name?: string;
177
- }
178
- declare function createError(options: CreateErrorOptions): FastifyError;
179
108
  declare function setupErrorHandler(fastify: FastifyInstance): void;
180
109
 
181
- export { type CreateErrorOptions, type CreateFastifyOptions, type ErrorResponse, FastifyJwtService, type FastifyJwtServiceOptions, type FastifyServer, type SignTokenOptions, type SuccessResponse, type TokenTypeConfig, type ValidationDetail, type VerifyTokenOptions, createError, createFastify, createFastifyPlugin, errorResponseSchema, extractTokenFromHeader, formatError, formatSuccess, healthPlugin, parseJwtSecrets, runFastify, setupErrorHandler, signToken, successResponseSchema, verifyToken };
110
+ export { type CreateFastifyOptions, FastifyJwtService, type FastifyJwtServiceOptions, type FastifyServer, createFastify, createFastifyPlugin, generateId, generateSecureString, generateSessionToken, healthPlugin, parseJwtSecrets, runFastify, setupErrorHandler };
package/dist/index.js CHANGED
@@ -3,55 +3,8 @@ import Fastify from "fastify";
3
3
  import { serializerCompiler, validatorCompiler } from "fastify-type-provider-zod";
4
4
  import fastifyRateLimit from "@fastify/rate-limit";
5
5
 
6
- // lib/response.ts
7
- import { z } from "zod";
8
- function formatSuccess(status, data) {
9
- return {
10
- status,
11
- success: true,
12
- data
13
- };
14
- }
15
- function formatError(status, error, message, details) {
16
- const response = {
17
- status,
18
- success: false,
19
- error,
20
- message
21
- };
22
- if (details && details.length > 0) {
23
- response.details = details;
24
- }
25
- return response;
26
- }
27
- var successResponseSchema = (dataSchema) => z.object({
28
- status: z.number(),
29
- success: z.literal(true),
30
- data: dataSchema
31
- });
32
- var errorResponseSchema = z.object({
33
- status: z.number(),
34
- success: z.literal(false),
35
- error: z.string(),
36
- message: z.string(),
37
- details: z.array(z.object({
38
- field: z.string(),
39
- message: z.string()
40
- })).optional()
41
- });
42
-
43
6
  // lib/error-handler.ts
44
- function createError(options) {
45
- const {
46
- statusCode = 500,
47
- message,
48
- name = "Error"
49
- } = options;
50
- const error = new Error(message);
51
- error.statusCode = statusCode;
52
- error.name = name;
53
- return error;
54
- }
7
+ import { formatError } from "@a_jackie_z/fastify-types";
55
8
  function setupErrorHandler(fastify) {
56
9
  fastify.setErrorHandler((error, request, reply) => {
57
10
  fastify.log.error({
@@ -446,6 +399,7 @@ async function registerJWT(fastify, options) {
446
399
  }
447
400
 
448
401
  // lib/jwt/hooks.ts
402
+ import { formatError as formatError2 } from "@a_jackie_z/fastify-types";
449
403
  function createVerificationHook(options) {
450
404
  return async (request, reply) => {
451
405
  const { jwtService, swaggerRoutePrefix, requiredTypes, debug } = options;
@@ -472,7 +426,7 @@ function createVerificationHook(options) {
472
426
  const tokenTypeConfig = jwtService.getTokenTypeConfig(typeName);
473
427
  if (!tokenTypeConfig) {
474
428
  const errorMsg = debug ? `Unknown token type "${typeName}" in route configuration. Available types: ${jwtService.getTokenTypeNames().join(", ")}` : `Unknown token type "${typeName}"`;
475
- return reply.status(500).send(formatError(500, "Internal Server Error", errorMsg));
429
+ return reply.status(500).send(formatError2(500, "Internal Server Error", errorMsg));
476
430
  }
477
431
  const headerName = tokenTypeConfig.headerName.toLowerCase();
478
432
  const headerValue = request.headers[headerName];
@@ -481,18 +435,18 @@ function createVerificationHook(options) {
481
435
  token = jwtService.extractTokenFromHeader(headerValue, typeName);
482
436
  } catch (err) {
483
437
  const errorMsg = debug && err instanceof Error ? err.message : `Invalid ${headerName} header format`;
484
- return reply.status(401).send(formatError(401, "Unauthorized", errorMsg));
438
+ return reply.status(401).send(formatError2(401, "Unauthorized", errorMsg));
485
439
  }
486
440
  if (!token) {
487
441
  const errorMsg = debug ? `Missing or invalid ${headerName} header for token type "${typeName}"` : `Missing or invalid ${headerName} header`;
488
- return reply.status(401).send(formatError(401, "Unauthorized", errorMsg));
442
+ return reply.status(401).send(formatError2(401, "Unauthorized", errorMsg));
489
443
  }
490
444
  try {
491
445
  const payload = jwtService.verifyToken(typeName, token);
492
446
  request.jwtPayloads.set(typeName, payload);
493
447
  } catch (err) {
494
448
  const errorMsg = debug && err instanceof Error ? `Token verification failed for type "${typeName}": ${err.message}` : `Invalid or expired ${typeName} token`;
495
- return reply.status(401).send(formatError(401, "Unauthorized", errorMsg));
449
+ return reply.status(401).send(formatError2(401, "Unauthorized", errorMsg));
496
450
  }
497
451
  }
498
452
  };
@@ -522,9 +476,36 @@ async function setupJWT(fastify, options, swaggerRoutePrefix) {
522
476
  fastify.addHook("onRequest", createVerificationHook(hookOptions));
523
477
  }
524
478
 
479
+ // lib/crypto-utils.ts
480
+ import { randomBytes } from "crypto";
481
+ var ID_ALPHABET = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
482
+ var ID_LENGTH = 16;
483
+ function generateId() {
484
+ const bytes = randomBytes(ID_LENGTH);
485
+ let result = "";
486
+ for (let i = 0; i < ID_LENGTH; i++) {
487
+ result += ID_ALPHABET[bytes[i] % ID_ALPHABET.length];
488
+ }
489
+ return result;
490
+ }
491
+ function generateSessionToken() {
492
+ return randomBytes(32).toString("hex");
493
+ }
494
+ function generateSecureString(length, alphabet = ID_ALPHABET) {
495
+ const bytes = randomBytes(length);
496
+ let result = "";
497
+ for (let i = 0; i < length; i++) {
498
+ result += alphabet[bytes[i] % alphabet.length];
499
+ }
500
+ return result;
501
+ }
502
+
525
503
  // lib/fastify.ts
526
504
  async function createFastify(options) {
527
- const fastifyOptions = {};
505
+ const fastifyOptions = {
506
+ ...options?.requestTimeout ? { requestTimeout: options.requestTimeout } : {},
507
+ ...options?.bodyLimit ? { bodyLimit: options.bodyLimit } : {}
508
+ };
528
509
  if (options?.logger) {
529
510
  fastifyOptions.loggerInstance = options.logger;
530
511
  }
@@ -628,19 +609,14 @@ var healthPlugin = createFastifyPlugin((app) => {
628
609
  export * from "fastify-type-provider-zod";
629
610
  export {
630
611
  FastifyJwtService,
631
- createError,
632
612
  createFastify,
633
613
  createFastifyPlugin,
634
- errorResponseSchema,
635
- extractTokenFromHeader,
636
- formatError,
637
- formatSuccess,
614
+ generateId,
615
+ generateSecureString,
616
+ generateSessionToken,
638
617
  healthPlugin,
639
618
  parseJwtSecrets,
640
619
  runFastify,
641
- setupErrorHandler,
642
- signToken,
643
- successResponseSchema,
644
- verifyToken
620
+ setupErrorHandler
645
621
  };
646
622
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../lib/fastify.ts","../lib/response.ts","../lib/error-handler.ts","../lib/swagger-setup.ts","../lib/jwt/core.ts","../lib/jwt/service.ts","../lib/jwt/register.ts","../lib/jwt/hooks.ts","../lib/jwt/setup.ts","../lib/jwt/utils.ts","../lib/plugin.ts","../lib/plugins/healthPlugin.ts","../index.ts"],"sourcesContent":["import Fastify, { FastifyServerOptions } from 'fastify'\nimport { serializerCompiler, validatorCompiler, ZodTypeProvider } from 'fastify-type-provider-zod'\nimport fastifyRateLimit from '@fastify/rate-limit'\nimport { setupErrorHandler } from './error-handler.ts'\nimport { setupSwagger, setupSwaggerSecurityHook } from './swagger-setup.ts'\nimport { setupJWT } from './jwt/setup.ts'\nimport type { CreateFastifyOptions, FastifyServer } from './types.ts'\n\n// Re-export types and utilities from other modules\nexport * from './response.ts'\nexport * from './types.ts'\n\nexport async function createFastify(options?: CreateFastifyOptions): Promise<FastifyServer> {\n const fastifyOptions: FastifyServerOptions = {}\n\n if (options?.logger) {\n fastifyOptions.loggerInstance = options.logger\n }\n\n const fastify = Fastify(fastifyOptions).withTypeProvider<ZodTypeProvider>()\n\n // Set up Zod validation and serialization\n fastify.setValidatorCompiler(validatorCompiler)\n fastify.setSerializerCompiler(serializerCompiler)\n\n // Set up error handler for standardized error responses\n setupErrorHandler(fastify)\n\n // Register Swagger first to capture all routes with Zod schemas\n const swaggerRoutePrefix = await setupSwagger(fastify, options || {})\n\n // Auto-inject security requirements for JWT-protected routes\n setupSwaggerSecurityHook(fastify, options || {}, swaggerRoutePrefix)\n\n // Register JWT authentication\n await setupJWT(fastify, options || {}, swaggerRoutePrefix)\n\n // Register Rate Limiting\n if (options?.rateLimit?.global) {\n await fastify.register(fastifyRateLimit, {\n global: true,\n ...options.rateLimit.global,\n })\n }\n\n return fastify\n}\n\nexport async function runFastify(fastify: FastifyServer, host: string, port: number) {\n try {\n await fastify.listen({ host, port })\n } catch (err) {\n fastify.log.error(err)\n process.exit(1)\n }\n}\n","import { z } from 'zod'\n\n// Response Types\nexport interface ValidationDetail {\n field: string\n message: string\n}\n\nexport interface SuccessResponse<T> {\n status: number\n success: true\n data: T\n}\n\nexport interface ErrorResponse {\n status: number\n success: false\n error: string\n message: string\n details?: ValidationDetail[]\n}\n\n// Response Formatters\nexport function formatSuccess<T>(status: number, data: T): SuccessResponse<T> {\n return {\n status,\n success: true,\n data,\n }\n}\n\nexport function formatError(\n status: number,\n error: string,\n message: string,\n details?: ValidationDetail[]\n): ErrorResponse {\n const response: ErrorResponse = {\n status,\n success: false,\n error,\n message,\n }\n if (details && details.length > 0) {\n response.details = details\n }\n return response\n}\n\n// Zod Schema Helpers for Standardized Responses\nexport const successResponseSchema = <T extends z.ZodTypeAny>(dataSchema: T) => z.object({\n status: z.number(),\n success: z.literal(true),\n data: dataSchema,\n})\n\nexport const errorResponseSchema = z.object({\n status: z.number(),\n success: z.literal(false),\n error: z.string(),\n message: z.string(),\n details: z.array(z.object({\n field: z.string(),\n message: z.string(),\n })).optional(),\n})\n","import type { FastifyError, FastifyInstance } from 'fastify'\nimport { formatError, type ValidationDetail } from './response.ts'\n\nexport interface CreateErrorOptions {\n statusCode?: number\n message: string\n name?: string\n}\n\nexport function createError(options: CreateErrorOptions): FastifyError {\n const {\n statusCode = 500,\n message,\n name = 'Error',\n } = options\n\n const error = new Error(message) as FastifyError\n error.statusCode = statusCode\n error.name = name\n return error\n}\n\nexport function setupErrorHandler(fastify: FastifyInstance): void {\n fastify.setErrorHandler((error: FastifyError, request, reply) => {\n // Log all errors\n fastify.log.error({\n err: error,\n url: request.url,\n method: request.method,\n }, 'Request error')\n\n // Handle Zod validation errors\n if (error.validation) {\n const details: ValidationDetail[] = error.validation.map((issue: any) => {\n // Build field path from dataPath or instancePath\n const field = issue.instancePath || issue.dataPath || issue.params?.missingProperty || 'unknown'\n const cleanField = field.startsWith('/') ? field.slice(1).replace(/\\//g, '.') : field\n\n return {\n field: cleanField || 'unknown',\n message: issue.message || 'Validation failed',\n }\n })\n\n return reply.status(400).send(\n formatError(400, 'Validation Error', 'Request validation failed', details)\n )\n }\n\n // Handle rate limit errors\n if (error.statusCode === 429) {\n return reply.status(429).send(\n formatError(429, 'Too Many Requests', 'Rate limit exceeded')\n )\n }\n\n // Handle authentication errors\n if (error.statusCode === 401) {\n return reply.status(401).send(\n formatError(401, 'Unauthorized', error.message || 'Authentication required')\n )\n }\n\n // Handle authorization errors\n if (error.statusCode === 403) {\n return reply.status(403).send(\n formatError(403, 'Forbidden', error.message || 'Access denied')\n )\n }\n\n // Handle not found errors\n if (error.statusCode === 404) {\n return reply.status(404).send(\n formatError(404, 'Not Found', error.message || 'Resource not found')\n )\n }\n\n // Handle all other errors as internal server errors\n const statusCode = error.statusCode || 500\n return reply.status(statusCode).send(\n formatError(\n statusCode,\n statusCode === 500 ? 'Internal Server Error' : error.name || 'Error',\n error.message || 'An unexpected error occurred'\n )\n )\n })\n}\n","import type { FastifyInstance } from 'fastify'\nimport fastifySwagger, { SwaggerOptions } from '@fastify/swagger'\nimport fastifySwaggerUI, { FastifySwaggerUiOptions } from '@fastify/swagger-ui'\nimport { jsonSchemaTransform } from 'fastify-type-provider-zod'\nimport type { CreateFastifyOptions } from './types.ts'\n\nexport async function setupSwagger(\n fastify: FastifyInstance,\n options: CreateFastifyOptions\n): Promise<string | undefined> {\n if (!options.swagger) {\n return undefined\n }\n\n const openApiConfig: any = {\n openapi: {\n info: {\n title: options.swagger.title,\n version: options.swagger.version,\n description: options.swagger.description,\n },\n },\n transform: jsonSchemaTransform,\n }\n\n // Add security schemes for each token type if JWT is enabled\n if (options.jwt && options.jwt.tokenTypes) {\n const securitySchemes: any = {}\n\n for (const [typeName, typeConfig] of Object.entries(options.jwt.tokenTypes)) {\n const headerName = typeConfig.headerName.toLowerCase()\n\n // Use bearer auth for 'authorization' header, apiKey for custom headers\n if (headerName === 'authorization') {\n securitySchemes[typeName] = {\n type: 'http',\n scheme: 'bearer',\n bearerFormat: 'JWT',\n description: `JWT token for ${typeName} authentication`,\n }\n } else {\n securitySchemes[typeName] = {\n type: 'apiKey',\n in: 'header',\n name: typeConfig.headerName,\n description: `JWT token for ${typeName} authentication`,\n }\n }\n }\n\n openApiConfig.openapi.components = {\n securitySchemes,\n }\n }\n\n await fastify.register(fastifySwagger, openApiConfig as SwaggerOptions)\n\n let routePrefix = options.swagger.routePrefix || '/docs/'\n\n if (!routePrefix.startsWith('/')) {\n routePrefix = '/' + routePrefix\n }\n\n if (!routePrefix.endsWith('/')) {\n routePrefix = routePrefix + '/'\n }\n\n await fastify.register(fastifySwaggerUI, { routePrefix } as FastifySwaggerUiOptions)\n\n return routePrefix\n}\n\nexport function setupSwaggerSecurityHook(\n fastify: FastifyInstance,\n options: CreateFastifyOptions,\n swaggerRoutePrefix: string | undefined\n): void {\n if (!options.jwt || !options.swagger) {\n return\n }\n\n // Auto-inject security requirements for JWT-protected routes\n fastify.addHook('onRoute', (routeOptions) => {\n // Skip Swagger routes\n if (swaggerRoutePrefix && routeOptions.url.startsWith(swaggerRoutePrefix)) {\n return\n }\n\n const routeConfig = (routeOptions.config as any) || {}\n const jwtTypes = routeConfig.jwtTypes\n\n // Skip if JWT is explicitly bypassed\n if (jwtTypes === false) {\n return\n }\n\n // Determine which token types are required\n // Start with global requiredTypes (always checked)\n let requiredTypes: string[] = []\n\n if (options.jwt?.requiredTypes && options.jwt.requiredTypes.length > 0) {\n requiredTypes = [...options.jwt.requiredTypes]\n }\n\n // Add route-specific types if specified\n if (Array.isArray(jwtTypes) && jwtTypes.length > 0) {\n // Combine both, removing duplicates\n requiredTypes = [...new Set([...requiredTypes, ...jwtTypes])]\n }\n\n // Inject security requirement if JWT types are required\n if (requiredTypes.length > 0) {\n if (!routeOptions.schema) {\n routeOptions.schema = {}\n }\n if (!routeOptions.schema.security) {\n routeOptions.schema.security = requiredTypes.map(typeName => ({ [typeName]: [] }))\n }\n }\n })\n}\n","import jwt, { Algorithm, JwtPayload, SignOptions, VerifyOptions } from 'jsonwebtoken'\n\nexport interface SignTokenOptions {\n algorithm?: Algorithm\n iss?: string\n aud?: string\n header?: {\n typ?: string\n cty?: string\n [key: string]: any\n }\n}\n\nexport interface VerifyTokenOptions {\n allowedIss?: string[]\n debug?: boolean\n expectedHeader?: {\n typ?: string\n cty?: string\n [key: string]: any\n }\n}\n\n/**\n * Sign a JWT token with kid, expiration, and optional JWT header parameters\n */\nexport function signToken(\n payload: Record<string, any>,\n secret: string,\n kid: string,\n expiresIn: string,\n options?: SignTokenOptions\n): string {\n const signOptions: SignOptions = {\n expiresIn: expiresIn as any,\n keyid: kid,\n }\n\n if (options?.algorithm) {\n signOptions.algorithm = options.algorithm\n }\n if (options?.iss) {\n signOptions.issuer = options.iss\n }\n if (options?.aud) {\n signOptions.audience = options.aud\n }\n\n // Apply custom header configuration\n if (options?.header) {\n const headerConfig: Record<string, any> = {}\n\n // Add standard JWT headers\n if (options.header.typ !== undefined) {\n headerConfig.typ = options.header.typ\n }\n if (options.header.cty !== undefined) {\n headerConfig.cty = options.header.cty\n }\n\n // Add custom header claims (exclude typ and cty as they're already handled)\n for (const [key, value] of Object.entries(options.header)) {\n if (key !== 'typ' && key !== 'cty') {\n headerConfig[key] = value\n }\n }\n\n // Only set header if we have custom fields\n if (Object.keys(headerConfig).length > 0) {\n signOptions.header = headerConfig as any\n }\n }\n\n return jwt.sign(payload, secret, signOptions)\n}\n\n/**\n * Verify a JWT token with kid-based secret selection and optional issuer validation\n */\nexport function verifyToken(\n token: string,\n secrets: Record<string, string>,\n options?: VerifyTokenOptions\n): JwtPayload {\n const debug = options?.debug ?? false\n\n try {\n // Decode header to extract kid\n const parts = token.split('.')\n if (parts.length !== 3 || !parts[0]) {\n throw new Error(debug ? 'Malformed JWT token: Token must have 3 parts separated by dots' : 'Malformed token')\n }\n\n const headerPart = parts[0]\n let header: any\n try {\n header = JSON.parse(Buffer.from(headerPart, 'base64').toString())\n } catch {\n throw new Error(debug ? 'Malformed JWT token: Invalid base64 encoding in header' : 'Malformed token')\n }\n\n const kid = header.kid\n if (!kid) {\n throw new Error(\n debug\n ? 'Missing kid in JWT token header. Ensure the token was signed with a kid parameter.'\n : 'Missing kid in token header'\n )\n }\n\n // Validate expected header claims if configured\n if (options?.expectedHeader) {\n // Validate typ (token type)\n if (options.expectedHeader.typ !== undefined && header.typ !== options.expectedHeader.typ) {\n throw new Error(\n debug\n ? `Invalid JWT header 'typ': expected \"${options.expectedHeader.typ}\", got \"${header.typ || 'undefined'}\"`\n : 'Invalid JWT header typ'\n )\n }\n\n // Validate cty (content type)\n if (options.expectedHeader.cty !== undefined && header.cty !== options.expectedHeader.cty) {\n throw new Error(\n debug\n ? `Invalid JWT header 'cty': expected \"${options.expectedHeader.cty}\", got \"${header.cty || 'undefined'}\"`\n : 'Invalid JWT header cty'\n )\n }\n\n // Validate custom header claims\n for (const [key, expectedValue] of Object.entries(options.expectedHeader)) {\n if (key !== 'typ' && key !== 'cty' && expectedValue !== undefined) {\n if (header[key] !== expectedValue) {\n throw new Error(\n debug\n ? `Invalid JWT header '${key}': expected \"${expectedValue}\", got \"${header[key] || 'undefined'}\"`\n : `Invalid JWT header ${key}`\n )\n }\n }\n }\n }\n\n const secret = secrets[kid]\n if (!secret) {\n const availableKeys = Object.keys(secrets).join(', ')\n throw new Error(\n debug\n ? `Unknown key ID \"${kid}\" in JWT token. Available keys: ${availableKeys}`\n : `Unknown key ID \"${kid}\"`\n )\n }\n\n // Verify token with the secret\n const verifyOptions: VerifyOptions = {\n complete: false,\n }\n\n const decoded = jwt.verify(token, secret, verifyOptions) as JwtPayload\n\n // Validate issuer if allowedIss is configured\n if (options?.allowedIss && options.allowedIss.length > 0) {\n const tokenIss = decoded.iss\n if (!tokenIss) {\n throw new Error(\n debug\n ? `Token missing issuer (iss) claim. Expected one of: ${options.allowedIss.join(', ')}`\n : 'Token missing issuer claim'\n )\n }\n if (!options.allowedIss.includes(tokenIss)) {\n throw new Error(\n debug\n ? `Invalid token issuer \"${tokenIss}\". Expected one of: ${options.allowedIss.join(', ')}`\n : `Invalid token issuer \"${tokenIss}\"`\n )\n }\n }\n\n return decoded\n } catch (error) {\n if (error instanceof jwt.TokenExpiredError) {\n if (debug) {\n const expDate = new Date(error.expiredAt).toISOString()\n const now = Math.floor(Date.now() / 1000)\n const exp = Math.floor(error.expiredAt.getTime() / 1000)\n const diff = now - exp\n throw new Error(`Token expired at ${expDate} (${diff} seconds ago)`)\n }\n throw new Error('Token has expired')\n }\n\n if (error instanceof jwt.JsonWebTokenError) {\n if (debug) {\n if (error.message.includes('signature')) {\n throw new Error('Invalid token signature: Token was signed with a different secret or has been tampered with')\n }\n if (error.message.includes('malformed')) {\n throw new Error('Malformed token: Token structure is invalid or corrupted')\n }\n throw new Error(`Token verification failed: ${error.message}`)\n }\n throw new Error('Invalid token')\n }\n\n throw error\n }\n}\n\n/**\n * Extract token from header value with Bearer prefix stripping\n * All JWT tokens must use Bearer prefix format: \"Bearer <token>\"\n */\nexport function extractTokenFromHeader(\n headerValue: string | string[] | undefined,\n headerName: string,\n debug?: boolean\n): string | null {\n if (!headerValue) {\n return null\n }\n\n const value = typeof headerValue === 'string' ? headerValue : headerValue[0]\n if (!value) {\n return null\n }\n\n // All JWT tokens must use \"Bearer <token>\" format\n const match = value.match(/^Bearer\\s+(\\S+)$/i)\n if (!match || !match[1]) {\n if (debug && value) {\n throw new Error(`Invalid ${headerName} header format. Expected: \"Bearer <token>\"`)\n }\n return null\n }\n\n return match[1]\n}\n\n","import { signToken, verifyToken, extractTokenFromHeader, type SignTokenOptions, type VerifyTokenOptions } from './core.ts'\nimport type { TokenTypeConfig } from '../types.ts'\n\nexport interface FastifyJwtServiceOptions {\n secrets: Record<string, string>\n defaultKeyId: string\n tokenTypes: Record<string, TokenTypeConfig>\n debug?: boolean\n}\n\nexport class FastifyJwtService {\n private readonly secrets: Record<string, string>\n private readonly defaultKeyId: string\n private readonly tokenTypes: Record<string, TokenTypeConfig>\n private readonly debug: boolean\n\n constructor(options: FastifyJwtServiceOptions) {\n this.secrets = options.secrets\n this.defaultKeyId = options.defaultKeyId\n this.tokenTypes = options.tokenTypes\n this.debug = options.debug ?? false\n }\n\n /**\n * Generate a token for a specific type\n */\n generateToken(typeName: string, payload: Record<string, any>): string {\n const tokenType = this.tokenTypes[typeName]\n if (!tokenType) {\n throw new Error(\n this.debug\n ? `Unknown token type \"${typeName}\". Available types: ${Object.keys(this.tokenTypes).join(', ')}`\n : `Unknown token type \"${typeName}\"`\n )\n }\n\n const signOptions: SignTokenOptions = {}\n if (tokenType.algorithm) signOptions.algorithm = tokenType.algorithm\n if (tokenType.iss) signOptions.iss = tokenType.iss\n if (tokenType.aud) signOptions.aud = tokenType.aud\n if (tokenType.header) signOptions.header = tokenType.header\n\n const secret = this.secrets[this.defaultKeyId]\n if (!secret) {\n throw new Error(`Default key ID \"${this.defaultKeyId}\" not found in secrets`)\n }\n\n return signToken(payload, secret, this.defaultKeyId, tokenType.expiresIn, signOptions)\n }\n\n /**\n * Verify a token for a specific type\n */\n verifyToken(typeName: string, token: string): any {\n const tokenType = this.tokenTypes[typeName]\n if (!tokenType) {\n throw new Error(\n this.debug\n ? `Unknown token type \"${typeName}\". Available types: ${Object.keys(this.tokenTypes).join(', ')}`\n : `Unknown token type \"${typeName}\"`\n )\n }\n\n const verifyOptions: VerifyTokenOptions = {\n debug: this.debug,\n }\n\n if (tokenType.allowedIss && tokenType.allowedIss.length > 0) {\n verifyOptions.allowedIss = tokenType.allowedIss\n }\n\n if (tokenType.header) {\n verifyOptions.expectedHeader = tokenType.header\n }\n\n const decoded = verifyToken(token, this.secrets, verifyOptions)\n\n // Validate payload with Zod schema if provided\n if (tokenType.payloadSchema) {\n try {\n return tokenType.payloadSchema.parse(decoded)\n } catch (error) {\n if (this.debug && error instanceof Error) {\n throw new Error(`Token payload validation failed: ${error.message}`)\n }\n throw new Error('Invalid token payload')\n }\n }\n\n return decoded\n }\n\n /**\n * Extract token from header value\n */\n extractTokenFromHeader(headerValue: string | string[] | undefined, typeName: string): string | null {\n const tokenType = this.tokenTypes[typeName]\n if (!tokenType) {\n throw new Error(\n this.debug\n ? `Unknown token type \"${typeName}\". Available types: ${Object.keys(this.tokenTypes).join(', ')}`\n : `Unknown token type \"${typeName}\"`\n )\n }\n\n return extractTokenFromHeader(headerValue, tokenType.headerName, this.debug)\n }\n\n /**\n * Get token type configuration\n */\n getTokenTypeConfig(typeName: string): TokenTypeConfig | undefined {\n return this.tokenTypes[typeName]\n }\n\n /**\n * Get all configured token type names\n */\n getTokenTypeNames(): string[] {\n return Object.keys(this.tokenTypes)\n }\n}\n\n","import type { FastifyInstance } from 'fastify'\nimport { FastifyJwtService, type FastifyJwtServiceOptions } from './service.ts'\n\nexport async function registerJWT(\n fastify: FastifyInstance,\n options: FastifyJwtServiceOptions\n): Promise<FastifyJwtService> {\n const { secrets, defaultKeyId, tokenTypes } = options\n\n // Validate that defaultKeyId exists in secrets\n if (!secrets[defaultKeyId]) {\n throw new Error(`Default key ID \"${defaultKeyId}\" not found in JWT secrets`)\n }\n\n // Validate that at least one token type is configured\n if (!tokenTypes || Object.keys(tokenTypes).length === 0) {\n throw new Error('At least one token type must be configured')\n }\n\n // Validate token types configuration\n for (const [typeName, config] of Object.entries(tokenTypes)) {\n if (!config.headerName) {\n throw new Error(`Token type \"${typeName}\" is missing required field: headerName`)\n }\n if (!config.expiresIn) {\n throw new Error(`Token type \"${typeName}\" is missing required field: expiresIn`)\n }\n }\n\n // Create JWT service\n const jwtService = new FastifyJwtService(options)\n\n // Decorate Fastify instance\n fastify.decorate('jwtService', jwtService)\n\n return jwtService\n}\n","import type { FastifyReply, FastifyRequest } from 'fastify'\nimport { formatError } from '../response.ts'\nimport type { FastifyJwtService } from './service.ts'\n\ninterface HookOptions {\n jwtService: FastifyJwtService\n swaggerRoutePrefix?: string\n requiredTypes?: string[]\n debug?: boolean\n}\n\nexport function createVerificationHook(options: HookOptions) {\n return async (request: FastifyRequest, reply: FastifyReply) => {\n const { jwtService, swaggerRoutePrefix, requiredTypes, debug } = options\n\n // Skip Swagger routes\n if (swaggerRoutePrefix && request.url.startsWith(swaggerRoutePrefix)) {\n return\n }\n\n const routeConfig = (request.routeOptions.config as any) || {}\n const jwtTypes = routeConfig.jwtTypes\n\n // If jwtTypes is explicitly false, skip verification\n if (jwtTypes === false) {\n return\n }\n\n // Determine which token types to verify\n // Always include requiredTypes first (checked for all routes)\n let typesToVerify: string[] = []\n\n if (requiredTypes && requiredTypes.length > 0) {\n typesToVerify = [...requiredTypes]\n }\n\n // Add route-specific types if specified\n if (Array.isArray(jwtTypes) && jwtTypes.length > 0) {\n // Combine both, removing duplicates\n typesToVerify = [...new Set([...typesToVerify, ...jwtTypes])]\n }\n\n // If no types to verify, route is public\n if (typesToVerify.length === 0) {\n return\n }\n\n // Initialize jwtPayloads map on request\n request.jwtPayloads = new Map()\n\n // Verify each required token type\n for (const typeName of typesToVerify) {\n const tokenTypeConfig = jwtService.getTokenTypeConfig(typeName)\n if (!tokenTypeConfig) {\n const errorMsg = debug\n ? `Unknown token type \"${typeName}\" in route configuration. Available types: ${jwtService.getTokenTypeNames().join(', ')}`\n : `Unknown token type \"${typeName}\"`\n return reply.status(500).send(formatError(500, 'Internal Server Error', errorMsg))\n }\n\n // Extract token from configured header\n const headerName = tokenTypeConfig.headerName.toLowerCase()\n const headerValue = request.headers[headerName]\n\n let token: string | null = null\n try {\n token = jwtService.extractTokenFromHeader(headerValue, typeName)\n } catch (err) {\n const errorMsg = debug && err instanceof Error\n ? err.message\n : `Invalid ${headerName} header format`\n return reply.status(401).send(formatError(401, 'Unauthorized', errorMsg))\n }\n\n if (!token) {\n const errorMsg = debug\n ? `Missing or invalid ${headerName} header for token type \"${typeName}\"`\n : `Missing or invalid ${headerName} header`\n return reply.status(401).send(formatError(401, 'Unauthorized', errorMsg))\n }\n\n // Verify token\n try {\n const payload = jwtService.verifyToken(typeName, token)\n request.jwtPayloads.set(typeName, payload)\n } catch (err) {\n const errorMsg = debug && err instanceof Error\n ? `Token verification failed for type \"${typeName}\": ${err.message}`\n : `Invalid or expired ${typeName} token`\n return reply.status(401).send(formatError(401, 'Unauthorized', errorMsg))\n }\n }\n }\n}\n","import type { FastifyInstance } from 'fastify'\nimport type { CreateFastifyOptions } from '../types.ts'\nimport { registerJWT } from './register.ts'\nimport { createVerificationHook } from './hooks.ts'\n\nexport async function setupJWT(\n fastify: FastifyInstance,\n options: CreateFastifyOptions,\n swaggerRoutePrefix: string | undefined\n): Promise<void> {\n if (!options.jwt) {\n return\n }\n\n const jwtService = await registerJWT(fastify, {\n secrets: options.jwt.secrets,\n defaultKeyId: options.jwt.defaultKeyId,\n tokenTypes: options.jwt.tokenTypes,\n debug: options.jwt.debug ?? false,\n })\n\n // Add verification hook for JWT verification (always add when JWT configured)\n const hookOptions: Parameters<typeof createVerificationHook>[0] = {\n jwtService,\n debug: options.jwt.debug ?? false,\n }\n\n if (options.jwt.requiredTypes !== undefined) {\n hookOptions.requiredTypes = options.jwt.requiredTypes\n }\n\n if (swaggerRoutePrefix !== undefined) {\n hookOptions.swaggerRoutePrefix = swaggerRoutePrefix\n }\n\n fastify.addHook('onRequest', createVerificationHook(hookOptions))\n}\n","/**\n * Parse JWT secrets from environment variable format\n * @param secretsString - Secrets in format: \"key1=secret1|key2=secret2\"\n * @returns Record mapping key IDs to their secrets\n * @throws Error if format is invalid, empty, contains duplicates, or has empty values\n */\nexport function parseJwtSecrets(secretsString: string): Record<string, string> {\n if (!secretsString || secretsString.trim().length === 0) {\n throw new Error('JWT secrets string cannot be empty')\n }\n\n const secrets: Record<string, string> = {}\n const pairs = secretsString.split('|')\n\n if (pairs.length === 0) {\n throw new Error('JWT secrets string must contain at least one key=secret pair')\n }\n\n for (const pair of pairs) {\n const trimmedPair = pair.trim()\n if (!trimmedPair) {\n continue // Skip empty pairs from trailing/leading separators\n }\n\n const separatorIndex = trimmedPair.indexOf('=')\n if (separatorIndex === -1) {\n throw new Error(`Invalid JWT secret format: \"${pair}\". Expected format: key=secret`)\n }\n\n const keyId = trimmedPair.substring(0, separatorIndex).trim()\n const secret = trimmedPair.substring(separatorIndex + 1).trim()\n\n if (!keyId) {\n throw new Error(`Empty key ID in JWT secret pair: \"${pair}\"`)\n }\n\n if (!secret) {\n throw new Error(`Empty secret for key ID \"${keyId}\"`)\n }\n\n if (secrets[keyId]) {\n throw new Error(`Duplicate key ID \"${keyId}\" in JWT secrets`)\n }\n\n secrets[keyId] = secret\n }\n\n if (Object.keys(secrets).length === 0) {\n throw new Error('JWT secrets string must contain at least one valid key=secret pair')\n }\n\n return secrets\n}\n","import { FastifyPluginCallback } from 'fastify'\nimport { FastifyServer } from './fastify.ts'\nimport { ZodTypeProvider } from 'fastify-type-provider-zod'\n\nexport function createFastifyPlugin(cb: FastifyPluginCallback) {\n return function createFastifyPluginWrapper(\n fastify: FastifyServer,\n options: Parameters<FastifyPluginCallback>[1],\n done: Parameters<FastifyPluginCallback>[2],\n ) {\n const server = fastify.withTypeProvider<ZodTypeProvider>()\n let doneCalled = false\n\n const doneWrapper = (err?: Error) => {\n done(err)\n doneCalled = true\n }\n\n cb(server, options, doneWrapper)\n\n if (!doneCalled) {\n done()\n }\n }\n}\n","import { createFastifyPlugin } from '../plugin.ts'\nimport { FastifyServer } from '../fastify.ts'\n\nexport const healthPlugin = createFastifyPlugin((app: FastifyServer) => {\n app.get('/v1/health', {\n schema: {\n tags: ['health'],\n },\n config: {\n jwt: false,\n },\n handler: async () => {\n return {\n status: 200,\n message: 'ok',\n }\n },\n })\n})\n","export * from './lib/fastify.ts'\nexport * from './lib/response.ts'\nexport * from './lib/types.ts'\nexport * from './lib/jwt/service.ts'\nexport * from './lib/jwt/core.ts'\nexport { parseJwtSecrets } from './lib/jwt/utils.ts'\nexport * from './lib/plugin.ts'\nexport * from './lib/plugins/healthPlugin.ts'\nexport * from './lib/error-handler.ts'\nexport * from 'fastify-type-provider-zod'\nexport type { FastifyErrorCodes, FastifyRequest, FastifyReply } from 'fastify'\n"],"mappings":";AAAA,OAAO,aAAuC;AAC9C,SAAS,oBAAoB,yBAA0C;AACvE,OAAO,sBAAsB;;;ACF7B,SAAS,SAAS;AAuBX,SAAS,cAAiB,QAAgB,MAA6B;AAC5E,SAAO;AAAA,IACL;AAAA,IACA,SAAS;AAAA,IACT;AAAA,EACF;AACF;AAEO,SAAS,YACd,QACA,OACA,SACA,SACe;AACf,QAAM,WAA0B;AAAA,IAC9B;AAAA,IACA,SAAS;AAAA,IACT;AAAA,IACA;AAAA,EACF;AACA,MAAI,WAAW,QAAQ,SAAS,GAAG;AACjC,aAAS,UAAU;AAAA,EACrB;AACA,SAAO;AACT;AAGO,IAAM,wBAAwB,CAAyB,eAAkB,EAAE,OAAO;AAAA,EACvF,QAAQ,EAAE,OAAO;AAAA,EACjB,SAAS,EAAE,QAAQ,IAAI;AAAA,EACvB,MAAM;AACR,CAAC;AAEM,IAAM,sBAAsB,EAAE,OAAO;AAAA,EAC1C,QAAQ,EAAE,OAAO;AAAA,EACjB,SAAS,EAAE,QAAQ,KAAK;AAAA,EACxB,OAAO,EAAE,OAAO;AAAA,EAChB,SAAS,EAAE,OAAO;AAAA,EAClB,SAAS,EAAE,MAAM,EAAE,OAAO;AAAA,IACxB,OAAO,EAAE,OAAO;AAAA,IAChB,SAAS,EAAE,OAAO;AAAA,EACpB,CAAC,CAAC,EAAE,SAAS;AACf,CAAC;;;ACxDM,SAAS,YAAY,SAA2C;AACrE,QAAM;AAAA,IACJ,aAAa;AAAA,IACb;AAAA,IACA,OAAO;AAAA,EACT,IAAI;AAEJ,QAAM,QAAQ,IAAI,MAAM,OAAO;AAC/B,QAAM,aAAa;AACnB,QAAM,OAAO;AACb,SAAO;AACT;AAEO,SAAS,kBAAkB,SAAgC;AAChE,UAAQ,gBAAgB,CAAC,OAAqB,SAAS,UAAU;AAE/D,YAAQ,IAAI,MAAM;AAAA,MAChB,KAAK;AAAA,MACL,KAAK,QAAQ;AAAA,MACb,QAAQ,QAAQ;AAAA,IAClB,GAAG,eAAe;AAGlB,QAAI,MAAM,YAAY;AACpB,YAAM,UAA8B,MAAM,WAAW,IAAI,CAAC,UAAe;AAEvE,cAAM,QAAQ,MAAM,gBAAgB,MAAM,YAAY,MAAM,QAAQ,mBAAmB;AACvF,cAAM,aAAa,MAAM,WAAW,GAAG,IAAI,MAAM,MAAM,CAAC,EAAE,QAAQ,OAAO,GAAG,IAAI;AAEhF,eAAO;AAAA,UACL,OAAO,cAAc;AAAA,UACrB,SAAS,MAAM,WAAW;AAAA,QAC5B;AAAA,MACF,CAAC;AAED,aAAO,MAAM,OAAO,GAAG,EAAE;AAAA,QACvB,YAAY,KAAK,oBAAoB,6BAA6B,OAAO;AAAA,MAC3E;AAAA,IACF;AAGA,QAAI,MAAM,eAAe,KAAK;AAC5B,aAAO,MAAM,OAAO,GAAG,EAAE;AAAA,QACvB,YAAY,KAAK,qBAAqB,qBAAqB;AAAA,MAC7D;AAAA,IACF;AAGA,QAAI,MAAM,eAAe,KAAK;AAC5B,aAAO,MAAM,OAAO,GAAG,EAAE;AAAA,QACvB,YAAY,KAAK,gBAAgB,MAAM,WAAW,yBAAyB;AAAA,MAC7E;AAAA,IACF;AAGA,QAAI,MAAM,eAAe,KAAK;AAC5B,aAAO,MAAM,OAAO,GAAG,EAAE;AAAA,QACvB,YAAY,KAAK,aAAa,MAAM,WAAW,eAAe;AAAA,MAChE;AAAA,IACF;AAGA,QAAI,MAAM,eAAe,KAAK;AAC5B,aAAO,MAAM,OAAO,GAAG,EAAE;AAAA,QACvB,YAAY,KAAK,aAAa,MAAM,WAAW,oBAAoB;AAAA,MACrE;AAAA,IACF;AAGA,UAAM,aAAa,MAAM,cAAc;AACvC,WAAO,MAAM,OAAO,UAAU,EAAE;AAAA,MAC9B;AAAA,QACE;AAAA,QACA,eAAe,MAAM,0BAA0B,MAAM,QAAQ;AAAA,QAC7D,MAAM,WAAW;AAAA,MACnB;AAAA,IACF;AAAA,EACF,CAAC;AACH;;;ACtFA,OAAO,oBAAwC;AAC/C,OAAO,sBAAmD;AAC1D,SAAS,2BAA2B;AAGpC,eAAsB,aACpB,SACA,SAC6B;AAC7B,MAAI,CAAC,QAAQ,SAAS;AACpB,WAAO;AAAA,EACT;AAEA,QAAM,gBAAqB;AAAA,IACzB,SAAS;AAAA,MACP,MAAM;AAAA,QACJ,OAAO,QAAQ,QAAQ;AAAA,QACvB,SAAS,QAAQ,QAAQ;AAAA,QACzB,aAAa,QAAQ,QAAQ;AAAA,MAC/B;AAAA,IACF;AAAA,IACA,WAAW;AAAA,EACb;AAGA,MAAI,QAAQ,OAAO,QAAQ,IAAI,YAAY;AACzC,UAAM,kBAAuB,CAAC;AAE9B,eAAW,CAAC,UAAU,UAAU,KAAK,OAAO,QAAQ,QAAQ,IAAI,UAAU,GAAG;AAC3E,YAAM,aAAa,WAAW,WAAW,YAAY;AAGrD,UAAI,eAAe,iBAAiB;AAClC,wBAAgB,QAAQ,IAAI;AAAA,UAC1B,MAAM;AAAA,UACN,QAAQ;AAAA,UACR,cAAc;AAAA,UACd,aAAa,iBAAiB,QAAQ;AAAA,QACxC;AAAA,MACF,OAAO;AACL,wBAAgB,QAAQ,IAAI;AAAA,UAC1B,MAAM;AAAA,UACN,IAAI;AAAA,UACJ,MAAM,WAAW;AAAA,UACjB,aAAa,iBAAiB,QAAQ;AAAA,QACxC;AAAA,MACF;AAAA,IACF;AAEA,kBAAc,QAAQ,aAAa;AAAA,MACjC;AAAA,IACF;AAAA,EACF;AAEA,QAAM,QAAQ,SAAS,gBAAgB,aAA+B;AAEtE,MAAI,cAAc,QAAQ,QAAQ,eAAe;AAEjD,MAAI,CAAC,YAAY,WAAW,GAAG,GAAG;AAChC,kBAAc,MAAM;AAAA,EACtB;AAEA,MAAI,CAAC,YAAY,SAAS,GAAG,GAAG;AAC9B,kBAAc,cAAc;AAAA,EAC9B;AAEA,QAAM,QAAQ,SAAS,kBAAkB,EAAE,YAAY,CAA4B;AAEnF,SAAO;AACT;AAEO,SAAS,yBACd,SACA,SACA,oBACM;AACN,MAAI,CAAC,QAAQ,OAAO,CAAC,QAAQ,SAAS;AACpC;AAAA,EACF;AAGA,UAAQ,QAAQ,WAAW,CAAC,iBAAiB;AAE3C,QAAI,sBAAsB,aAAa,IAAI,WAAW,kBAAkB,GAAG;AACzE;AAAA,IACF;AAEA,UAAM,cAAe,aAAa,UAAkB,CAAC;AACrD,UAAM,WAAW,YAAY;AAG7B,QAAI,aAAa,OAAO;AACtB;AAAA,IACF;AAIA,QAAI,gBAA0B,CAAC;AAE/B,QAAI,QAAQ,KAAK,iBAAiB,QAAQ,IAAI,cAAc,SAAS,GAAG;AACtE,sBAAgB,CAAC,GAAG,QAAQ,IAAI,aAAa;AAAA,IAC/C;AAGA,QAAI,MAAM,QAAQ,QAAQ,KAAK,SAAS,SAAS,GAAG;AAElD,sBAAgB,CAAC,GAAG,oBAAI,IAAI,CAAC,GAAG,eAAe,GAAG,QAAQ,CAAC,CAAC;AAAA,IAC9D;AAGA,QAAI,cAAc,SAAS,GAAG;AAC5B,UAAI,CAAC,aAAa,QAAQ;AACxB,qBAAa,SAAS,CAAC;AAAA,MACzB;AACA,UAAI,CAAC,aAAa,OAAO,UAAU;AACjC,qBAAa,OAAO,WAAW,cAAc,IAAI,eAAa,EAAE,CAAC,QAAQ,GAAG,CAAC,EAAE,EAAE;AAAA,MACnF;AAAA,IACF;AAAA,EACF,CAAC;AACH;;;ACxHA,OAAO,SAAgE;AA0BhE,SAAS,UACd,SACA,QACA,KACA,WACA,SACQ;AACR,QAAM,cAA2B;AAAA,IAC/B;AAAA,IACA,OAAO;AAAA,EACT;AAEA,MAAI,SAAS,WAAW;AACtB,gBAAY,YAAY,QAAQ;AAAA,EAClC;AACA,MAAI,SAAS,KAAK;AAChB,gBAAY,SAAS,QAAQ;AAAA,EAC/B;AACA,MAAI,SAAS,KAAK;AAChB,gBAAY,WAAW,QAAQ;AAAA,EACjC;AAGA,MAAI,SAAS,QAAQ;AACnB,UAAM,eAAoC,CAAC;AAG3C,QAAI,QAAQ,OAAO,QAAQ,QAAW;AACpC,mBAAa,MAAM,QAAQ,OAAO;AAAA,IACpC;AACA,QAAI,QAAQ,OAAO,QAAQ,QAAW;AACpC,mBAAa,MAAM,QAAQ,OAAO;AAAA,IACpC;AAGA,eAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,QAAQ,MAAM,GAAG;AACzD,UAAI,QAAQ,SAAS,QAAQ,OAAO;AAClC,qBAAa,GAAG,IAAI;AAAA,MACtB;AAAA,IACF;AAGA,QAAI,OAAO,KAAK,YAAY,EAAE,SAAS,GAAG;AACxC,kBAAY,SAAS;AAAA,IACvB;AAAA,EACF;AAEA,SAAO,IAAI,KAAK,SAAS,QAAQ,WAAW;AAC9C;AAKO,SAAS,YACd,OACA,SACA,SACY;AACZ,QAAM,QAAQ,SAAS,SAAS;AAEhC,MAAI;AAEF,UAAM,QAAQ,MAAM,MAAM,GAAG;AAC7B,QAAI,MAAM,WAAW,KAAK,CAAC,MAAM,CAAC,GAAG;AACnC,YAAM,IAAI,MAAM,QAAQ,mEAAmE,iBAAiB;AAAA,IAC9G;AAEA,UAAM,aAAa,MAAM,CAAC;AAC1B,QAAI;AACJ,QAAI;AACF,eAAS,KAAK,MAAM,OAAO,KAAK,YAAY,QAAQ,EAAE,SAAS,CAAC;AAAA,IAClE,QAAQ;AACN,YAAM,IAAI,MAAM,QAAQ,2DAA2D,iBAAiB;AAAA,IACtG;AAEA,UAAM,MAAM,OAAO;AACnB,QAAI,CAAC,KAAK;AACR,YAAM,IAAI;AAAA,QACR,QACI,uFACA;AAAA,MACN;AAAA,IACF;AAGA,QAAI,SAAS,gBAAgB;AAE3B,UAAI,QAAQ,eAAe,QAAQ,UAAa,OAAO,QAAQ,QAAQ,eAAe,KAAK;AACzF,cAAM,IAAI;AAAA,UACR,QACI,uCAAuC,QAAQ,eAAe,GAAG,WAAW,OAAO,OAAO,WAAW,MACrG;AAAA,QACN;AAAA,MACF;AAGA,UAAI,QAAQ,eAAe,QAAQ,UAAa,OAAO,QAAQ,QAAQ,eAAe,KAAK;AACzF,cAAM,IAAI;AAAA,UACR,QACI,uCAAuC,QAAQ,eAAe,GAAG,WAAW,OAAO,OAAO,WAAW,MACrG;AAAA,QACN;AAAA,MACF;AAGA,iBAAW,CAAC,KAAK,aAAa,KAAK,OAAO,QAAQ,QAAQ,cAAc,GAAG;AACzE,YAAI,QAAQ,SAAS,QAAQ,SAAS,kBAAkB,QAAW;AACjE,cAAI,OAAO,GAAG,MAAM,eAAe;AACjC,kBAAM,IAAI;AAAA,cACR,QACI,uBAAuB,GAAG,gBAAgB,aAAa,WAAW,OAAO,GAAG,KAAK,WAAW,MAC5F,sBAAsB,GAAG;AAAA,YAC/B;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAEA,UAAM,SAAS,QAAQ,GAAG;AAC1B,QAAI,CAAC,QAAQ;AACX,YAAM,gBAAgB,OAAO,KAAK,OAAO,EAAE,KAAK,IAAI;AACpD,YAAM,IAAI;AAAA,QACR,QACI,mBAAmB,GAAG,mCAAmC,aAAa,KACtE,mBAAmB,GAAG;AAAA,MAC5B;AAAA,IACF;AAGA,UAAM,gBAA+B;AAAA,MACnC,UAAU;AAAA,IACZ;AAEA,UAAM,UAAU,IAAI,OAAO,OAAO,QAAQ,aAAa;AAGvD,QAAI,SAAS,cAAc,QAAQ,WAAW,SAAS,GAAG;AACxD,YAAM,WAAW,QAAQ;AACzB,UAAI,CAAC,UAAU;AACb,cAAM,IAAI;AAAA,UACR,QACI,sDAAsD,QAAQ,WAAW,KAAK,IAAI,CAAC,KACnF;AAAA,QACN;AAAA,MACF;AACA,UAAI,CAAC,QAAQ,WAAW,SAAS,QAAQ,GAAG;AAC1C,cAAM,IAAI;AAAA,UACR,QACI,yBAAyB,QAAQ,uBAAuB,QAAQ,WAAW,KAAK,IAAI,CAAC,KACrF,yBAAyB,QAAQ;AAAA,QACvC;AAAA,MACF;AAAA,IACF;AAEA,WAAO;AAAA,EACT,SAAS,OAAO;AACd,QAAI,iBAAiB,IAAI,mBAAmB;AAC1C,UAAI,OAAO;AACT,cAAM,UAAU,IAAI,KAAK,MAAM,SAAS,EAAE,YAAY;AACtD,cAAM,MAAM,KAAK,MAAM,KAAK,IAAI,IAAI,GAAI;AACxC,cAAM,MAAM,KAAK,MAAM,MAAM,UAAU,QAAQ,IAAI,GAAI;AACvD,cAAM,OAAO,MAAM;AACnB,cAAM,IAAI,MAAM,oBAAoB,OAAO,KAAK,IAAI,eAAe;AAAA,MACrE;AACA,YAAM,IAAI,MAAM,mBAAmB;AAAA,IACrC;AAEA,QAAI,iBAAiB,IAAI,mBAAmB;AAC1C,UAAI,OAAO;AACT,YAAI,MAAM,QAAQ,SAAS,WAAW,GAAG;AACvC,gBAAM,IAAI,MAAM,6FAA6F;AAAA,QAC/G;AACA,YAAI,MAAM,QAAQ,SAAS,WAAW,GAAG;AACvC,gBAAM,IAAI,MAAM,0DAA0D;AAAA,QAC5E;AACA,cAAM,IAAI,MAAM,8BAA8B,MAAM,OAAO,EAAE;AAAA,MAC/D;AACA,YAAM,IAAI,MAAM,eAAe;AAAA,IACjC;AAEA,UAAM;AAAA,EACR;AACF;AAMO,SAAS,uBACd,aACA,YACA,OACe;AACf,MAAI,CAAC,aAAa;AAChB,WAAO;AAAA,EACT;AAEA,QAAM,QAAQ,OAAO,gBAAgB,WAAW,cAAc,YAAY,CAAC;AAC3E,MAAI,CAAC,OAAO;AACV,WAAO;AAAA,EACT;AAGA,QAAM,QAAQ,MAAM,MAAM,mBAAmB;AAC7C,MAAI,CAAC,SAAS,CAAC,MAAM,CAAC,GAAG;AACvB,QAAI,SAAS,OAAO;AAClB,YAAM,IAAI,MAAM,WAAW,UAAU,4CAA4C;AAAA,IACnF;AACA,WAAO;AAAA,EACT;AAEA,SAAO,MAAM,CAAC;AAChB;;;ACpOO,IAAM,oBAAN,MAAwB;AAAA,EACZ;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAEjB,YAAY,SAAmC;AAC7C,SAAK,UAAU,QAAQ;AACvB,SAAK,eAAe,QAAQ;AAC5B,SAAK,aAAa,QAAQ;AAC1B,SAAK,QAAQ,QAAQ,SAAS;AAAA,EAChC;AAAA;AAAA;AAAA;AAAA,EAKA,cAAc,UAAkB,SAAsC;AACpE,UAAM,YAAY,KAAK,WAAW,QAAQ;AAC1C,QAAI,CAAC,WAAW;AACd,YAAM,IAAI;AAAA,QACR,KAAK,QACD,uBAAuB,QAAQ,uBAAuB,OAAO,KAAK,KAAK,UAAU,EAAE,KAAK,IAAI,CAAC,KAC7F,uBAAuB,QAAQ;AAAA,MACrC;AAAA,IACF;AAEA,UAAM,cAAgC,CAAC;AACvC,QAAI,UAAU,UAAW,aAAY,YAAY,UAAU;AAC3D,QAAI,UAAU,IAAK,aAAY,MAAM,UAAU;AAC/C,QAAI,UAAU,IAAK,aAAY,MAAM,UAAU;AAC/C,QAAI,UAAU,OAAQ,aAAY,SAAS,UAAU;AAErD,UAAM,SAAS,KAAK,QAAQ,KAAK,YAAY;AAC7C,QAAI,CAAC,QAAQ;AACX,YAAM,IAAI,MAAM,mBAAmB,KAAK,YAAY,wBAAwB;AAAA,IAC9E;AAEA,WAAO,UAAU,SAAS,QAAQ,KAAK,cAAc,UAAU,WAAW,WAAW;AAAA,EACvF;AAAA;AAAA;AAAA;AAAA,EAKA,YAAY,UAAkB,OAAoB;AAChD,UAAM,YAAY,KAAK,WAAW,QAAQ;AAC1C,QAAI,CAAC,WAAW;AACd,YAAM,IAAI;AAAA,QACR,KAAK,QACD,uBAAuB,QAAQ,uBAAuB,OAAO,KAAK,KAAK,UAAU,EAAE,KAAK,IAAI,CAAC,KAC7F,uBAAuB,QAAQ;AAAA,MACrC;AAAA,IACF;AAEA,UAAM,gBAAoC;AAAA,MACxC,OAAO,KAAK;AAAA,IACd;AAEA,QAAI,UAAU,cAAc,UAAU,WAAW,SAAS,GAAG;AAC3D,oBAAc,aAAa,UAAU;AAAA,IACvC;AAEA,QAAI,UAAU,QAAQ;AACpB,oBAAc,iBAAiB,UAAU;AAAA,IAC3C;AAEA,UAAM,UAAU,YAAY,OAAO,KAAK,SAAS,aAAa;AAG9D,QAAI,UAAU,eAAe;AAC3B,UAAI;AACF,eAAO,UAAU,cAAc,MAAM,OAAO;AAAA,MAC9C,SAAS,OAAO;AACd,YAAI,KAAK,SAAS,iBAAiB,OAAO;AACxC,gBAAM,IAAI,MAAM,oCAAoC,MAAM,OAAO,EAAE;AAAA,QACrE;AACA,cAAM,IAAI,MAAM,uBAAuB;AAAA,MACzC;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,uBAAuB,aAA4C,UAAiC;AAClG,UAAM,YAAY,KAAK,WAAW,QAAQ;AAC1C,QAAI,CAAC,WAAW;AACd,YAAM,IAAI;AAAA,QACR,KAAK,QACD,uBAAuB,QAAQ,uBAAuB,OAAO,KAAK,KAAK,UAAU,EAAE,KAAK,IAAI,CAAC,KAC7F,uBAAuB,QAAQ;AAAA,MACrC;AAAA,IACF;AAEA,WAAO,uBAAuB,aAAa,UAAU,YAAY,KAAK,KAAK;AAAA,EAC7E;AAAA;AAAA;AAAA;AAAA,EAKA,mBAAmB,UAA+C;AAChE,WAAO,KAAK,WAAW,QAAQ;AAAA,EACjC;AAAA;AAAA;AAAA;AAAA,EAKA,oBAA8B;AAC5B,WAAO,OAAO,KAAK,KAAK,UAAU;AAAA,EACpC;AACF;;;ACtHA,eAAsB,YACpB,SACA,SAC4B;AAC5B,QAAM,EAAE,SAAS,cAAc,WAAW,IAAI;AAG9C,MAAI,CAAC,QAAQ,YAAY,GAAG;AAC1B,UAAM,IAAI,MAAM,mBAAmB,YAAY,4BAA4B;AAAA,EAC7E;AAGA,MAAI,CAAC,cAAc,OAAO,KAAK,UAAU,EAAE,WAAW,GAAG;AACvD,UAAM,IAAI,MAAM,4CAA4C;AAAA,EAC9D;AAGA,aAAW,CAAC,UAAU,MAAM,KAAK,OAAO,QAAQ,UAAU,GAAG;AAC3D,QAAI,CAAC,OAAO,YAAY;AACtB,YAAM,IAAI,MAAM,eAAe,QAAQ,yCAAyC;AAAA,IAClF;AACA,QAAI,CAAC,OAAO,WAAW;AACrB,YAAM,IAAI,MAAM,eAAe,QAAQ,wCAAwC;AAAA,IACjF;AAAA,EACF;AAGA,QAAM,aAAa,IAAI,kBAAkB,OAAO;AAGhD,UAAQ,SAAS,cAAc,UAAU;AAEzC,SAAO;AACT;;;ACzBO,SAAS,uBAAuB,SAAsB;AAC3D,SAAO,OAAO,SAAyB,UAAwB;AAC7D,UAAM,EAAE,YAAY,oBAAoB,eAAe,MAAM,IAAI;AAGjE,QAAI,sBAAsB,QAAQ,IAAI,WAAW,kBAAkB,GAAG;AACpE;AAAA,IACF;AAEA,UAAM,cAAe,QAAQ,aAAa,UAAkB,CAAC;AAC7D,UAAM,WAAW,YAAY;AAG7B,QAAI,aAAa,OAAO;AACtB;AAAA,IACF;AAIA,QAAI,gBAA0B,CAAC;AAE/B,QAAI,iBAAiB,cAAc,SAAS,GAAG;AAC7C,sBAAgB,CAAC,GAAG,aAAa;AAAA,IACnC;AAGA,QAAI,MAAM,QAAQ,QAAQ,KAAK,SAAS,SAAS,GAAG;AAElD,sBAAgB,CAAC,GAAG,oBAAI,IAAI,CAAC,GAAG,eAAe,GAAG,QAAQ,CAAC,CAAC;AAAA,IAC9D;AAGA,QAAI,cAAc,WAAW,GAAG;AAC9B;AAAA,IACF;AAGA,YAAQ,cAAc,oBAAI,IAAI;AAG9B,eAAW,YAAY,eAAe;AACpC,YAAM,kBAAkB,WAAW,mBAAmB,QAAQ;AAC9D,UAAI,CAAC,iBAAiB;AACpB,cAAM,WAAW,QACb,uBAAuB,QAAQ,8CAA8C,WAAW,kBAAkB,EAAE,KAAK,IAAI,CAAC,KACtH,uBAAuB,QAAQ;AACnC,eAAO,MAAM,OAAO,GAAG,EAAE,KAAK,YAAY,KAAK,yBAAyB,QAAQ,CAAC;AAAA,MACnF;AAGA,YAAM,aAAa,gBAAgB,WAAW,YAAY;AAC1D,YAAM,cAAc,QAAQ,QAAQ,UAAU;AAE9C,UAAI,QAAuB;AAC3B,UAAI;AACF,gBAAQ,WAAW,uBAAuB,aAAa,QAAQ;AAAA,MACjE,SAAS,KAAK;AACZ,cAAM,WAAW,SAAS,eAAe,QACrC,IAAI,UACJ,WAAW,UAAU;AACzB,eAAO,MAAM,OAAO,GAAG,EAAE,KAAK,YAAY,KAAK,gBAAgB,QAAQ,CAAC;AAAA,MAC1E;AAEA,UAAI,CAAC,OAAO;AACV,cAAM,WAAW,QACb,sBAAsB,UAAU,2BAA2B,QAAQ,MACnE,sBAAsB,UAAU;AACpC,eAAO,MAAM,OAAO,GAAG,EAAE,KAAK,YAAY,KAAK,gBAAgB,QAAQ,CAAC;AAAA,MAC1E;AAGA,UAAI;AACF,cAAM,UAAU,WAAW,YAAY,UAAU,KAAK;AACtD,gBAAQ,YAAY,IAAI,UAAU,OAAO;AAAA,MAC3C,SAAS,KAAK;AACZ,cAAM,WAAW,SAAS,eAAe,QACrC,uCAAuC,QAAQ,MAAM,IAAI,OAAO,KAChE,sBAAsB,QAAQ;AAClC,eAAO,MAAM,OAAO,GAAG,EAAE,KAAK,YAAY,KAAK,gBAAgB,QAAQ,CAAC;AAAA,MAC1E;AAAA,IACF;AAAA,EACF;AACF;;;ACxFA,eAAsB,SACpB,SACA,SACA,oBACe;AACf,MAAI,CAAC,QAAQ,KAAK;AAChB;AAAA,EACF;AAEA,QAAM,aAAa,MAAM,YAAY,SAAS;AAAA,IAC5C,SAAS,QAAQ,IAAI;AAAA,IACrB,cAAc,QAAQ,IAAI;AAAA,IAC1B,YAAY,QAAQ,IAAI;AAAA,IACxB,OAAO,QAAQ,IAAI,SAAS;AAAA,EAC9B,CAAC;AAGD,QAAM,cAA4D;AAAA,IAChE;AAAA,IACA,OAAO,QAAQ,IAAI,SAAS;AAAA,EAC9B;AAEA,MAAI,QAAQ,IAAI,kBAAkB,QAAW;AAC3C,gBAAY,gBAAgB,QAAQ,IAAI;AAAA,EAC1C;AAEA,MAAI,uBAAuB,QAAW;AACpC,gBAAY,qBAAqB;AAAA,EACnC;AAEA,UAAQ,QAAQ,aAAa,uBAAuB,WAAW,CAAC;AAClE;;;ARxBA,eAAsB,cAAc,SAAwD;AAC1F,QAAM,iBAAuC,CAAC;AAE9C,MAAI,SAAS,QAAQ;AACnB,mBAAe,iBAAiB,QAAQ;AAAA,EAC1C;AAEA,QAAM,UAAU,QAAQ,cAAc,EAAE,iBAAkC;AAG1E,UAAQ,qBAAqB,iBAAiB;AAC9C,UAAQ,sBAAsB,kBAAkB;AAGhD,oBAAkB,OAAO;AAGzB,QAAM,qBAAqB,MAAM,aAAa,SAAS,WAAW,CAAC,CAAC;AAGpE,2BAAyB,SAAS,WAAW,CAAC,GAAG,kBAAkB;AAGnE,QAAM,SAAS,SAAS,WAAW,CAAC,GAAG,kBAAkB;AAGzD,MAAI,SAAS,WAAW,QAAQ;AAC9B,UAAM,QAAQ,SAAS,kBAAkB;AAAA,MACvC,QAAQ;AAAA,MACR,GAAG,QAAQ,UAAU;AAAA,IACvB,CAAC;AAAA,EACH;AAEA,SAAO;AACT;AAEA,eAAsB,WAAW,SAAwB,MAAc,MAAc;AACnF,MAAI;AACF,UAAM,QAAQ,OAAO,EAAE,MAAM,KAAK,CAAC;AAAA,EACrC,SAAS,KAAK;AACZ,YAAQ,IAAI,MAAM,GAAG;AACrB,YAAQ,KAAK,CAAC;AAAA,EAChB;AACF;;;ASjDO,SAAS,gBAAgB,eAA+C;AAC7E,MAAI,CAAC,iBAAiB,cAAc,KAAK,EAAE,WAAW,GAAG;AACvD,UAAM,IAAI,MAAM,oCAAoC;AAAA,EACtD;AAEA,QAAM,UAAkC,CAAC;AACzC,QAAM,QAAQ,cAAc,MAAM,GAAG;AAErC,MAAI,MAAM,WAAW,GAAG;AACtB,UAAM,IAAI,MAAM,8DAA8D;AAAA,EAChF;AAEA,aAAW,QAAQ,OAAO;AACxB,UAAM,cAAc,KAAK,KAAK;AAC9B,QAAI,CAAC,aAAa;AAChB;AAAA,IACF;AAEA,UAAM,iBAAiB,YAAY,QAAQ,GAAG;AAC9C,QAAI,mBAAmB,IAAI;AACzB,YAAM,IAAI,MAAM,+BAA+B,IAAI,gCAAgC;AAAA,IACrF;AAEA,UAAM,QAAQ,YAAY,UAAU,GAAG,cAAc,EAAE,KAAK;AAC5D,UAAM,SAAS,YAAY,UAAU,iBAAiB,CAAC,EAAE,KAAK;AAE9D,QAAI,CAAC,OAAO;AACV,YAAM,IAAI,MAAM,qCAAqC,IAAI,GAAG;AAAA,IAC9D;AAEA,QAAI,CAAC,QAAQ;AACX,YAAM,IAAI,MAAM,4BAA4B,KAAK,GAAG;AAAA,IACtD;AAEA,QAAI,QAAQ,KAAK,GAAG;AAClB,YAAM,IAAI,MAAM,qBAAqB,KAAK,kBAAkB;AAAA,IAC9D;AAEA,YAAQ,KAAK,IAAI;AAAA,EACnB;AAEA,MAAI,OAAO,KAAK,OAAO,EAAE,WAAW,GAAG;AACrC,UAAM,IAAI,MAAM,oEAAoE;AAAA,EACtF;AAEA,SAAO;AACT;;;AChDO,SAAS,oBAAoB,IAA2B;AAC7D,SAAO,SAAS,2BACd,SACA,SACA,MACA;AACA,UAAM,SAAS,QAAQ,iBAAkC;AACzD,QAAI,aAAa;AAEjB,UAAM,cAAc,CAAC,QAAgB;AACnC,WAAK,GAAG;AACR,mBAAa;AAAA,IACf;AAEA,OAAG,QAAQ,SAAS,WAAW;AAE/B,QAAI,CAAC,YAAY;AACf,WAAK;AAAA,IACP;AAAA,EACF;AACF;;;ACrBO,IAAM,eAAe,oBAAoB,CAAC,QAAuB;AACtE,MAAI,IAAI,cAAc;AAAA,IACpB,QAAQ;AAAA,MACN,MAAM,CAAC,QAAQ;AAAA,IACjB;AAAA,IACA,QAAQ;AAAA,MACN,KAAK;AAAA,IACP;AAAA,IACA,SAAS,YAAY;AACnB,aAAO;AAAA,QACL,QAAQ;AAAA,QACR,SAAS;AAAA,MACX;AAAA,IACF;AAAA,EACF,CAAC;AACH,CAAC;;;ACTD,cAAc;","names":[]}
1
+ {"version":3,"sources":["../lib/fastify.ts","../lib/error-handler.ts","../lib/swagger-setup.ts","../lib/jwt/core.ts","../lib/jwt/service.ts","../lib/jwt/register.ts","../lib/jwt/hooks.ts","../lib/jwt/setup.ts","../lib/crypto-utils.ts","../lib/jwt/utils.ts","../lib/plugin.ts","../lib/plugins/healthPlugin.ts","../index.ts"],"sourcesContent":["import Fastify, { FastifyServerOptions } from 'fastify'\nimport { serializerCompiler, validatorCompiler, ZodTypeProvider } from 'fastify-type-provider-zod'\nimport fastifyRateLimit from '@fastify/rate-limit'\nimport { setupErrorHandler } from './error-handler.ts'\nimport { setupSwagger, setupSwaggerSecurityHook } from './swagger-setup.ts'\nimport { setupJWT } from './jwt/setup.ts'\nimport type { CreateFastifyOptions, FastifyServer } from './types.ts'\n\n// Re-export server-specific utilities\nexport * from './crypto-utils.ts'\n\n\nexport async function createFastify(options?: CreateFastifyOptions): Promise<FastifyServer> {\n const fastifyOptions: FastifyServerOptions = {\n ...options?.requestTimeout ? {requestTimeout: options.requestTimeout} : {},\n ...options?.bodyLimit ? {bodyLimit: options.bodyLimit} : {},\n }\n\n if (options?.logger) {\n fastifyOptions.loggerInstance = options.logger\n }\n\n const fastify = Fastify(fastifyOptions).withTypeProvider<ZodTypeProvider>()\n\n // Set up Zod validation and serialization\n fastify.setValidatorCompiler(validatorCompiler)\n fastify.setSerializerCompiler(serializerCompiler)\n\n // Set up error handler for standardized error responses\n setupErrorHandler(fastify)\n\n // Register Swagger first to capture all routes with Zod schemas\n const swaggerRoutePrefix = await setupSwagger(fastify, options || {})\n\n // Auto-inject security requirements for JWT-protected routes\n setupSwaggerSecurityHook(fastify, options || {}, swaggerRoutePrefix)\n\n // Register JWT authentication\n await setupJWT(fastify, options || {}, swaggerRoutePrefix)\n\n // Register Rate Limiting\n if (options?.rateLimit?.global) {\n await fastify.register(fastifyRateLimit, {\n global: true,\n ...options.rateLimit.global,\n })\n }\n\n return fastify\n}\n\nexport async function runFastify(fastify: FastifyServer, host: string, port: number) {\n try {\n await fastify.listen({ host, port })\n } catch (err) {\n fastify.log.error(err)\n process.exit(1)\n }\n}\n","import type { FastifyError, FastifyInstance } from 'fastify'\nimport { formatError, type ValidationDetail } from '@a_jackie_z/fastify-types'\n\nexport function setupErrorHandler(fastify: FastifyInstance): void {\n fastify.setErrorHandler((error: FastifyError, request, reply) => {\n // Log all errors\n fastify.log.error({\n err: error,\n url: request.url,\n method: request.method,\n }, 'Request error')\n\n // Handle Zod validation errors\n if (error.validation) {\n const details: ValidationDetail[] = error.validation.map((issue: any) => {\n // Build field path from dataPath or instancePath\n const field = issue.instancePath || issue.dataPath || issue.params?.missingProperty || 'unknown'\n const cleanField = field.startsWith('/') ? field.slice(1).replace(/\\//g, '.') : field\n\n return {\n field: cleanField || 'unknown',\n message: issue.message || 'Validation failed',\n }\n })\n\n return reply.status(400).send(\n formatError(400, 'Validation Error', 'Request validation failed', details)\n )\n }\n\n // Handle rate limit errors\n if (error.statusCode === 429) {\n return reply.status(429).send(\n formatError(429, 'Too Many Requests', 'Rate limit exceeded')\n )\n }\n\n // Handle authentication errors\n if (error.statusCode === 401) {\n return reply.status(401).send(\n formatError(401, 'Unauthorized', error.message || 'Authentication required')\n )\n }\n\n // Handle authorization errors\n if (error.statusCode === 403) {\n return reply.status(403).send(\n formatError(403, 'Forbidden', error.message || 'Access denied')\n )\n }\n\n // Handle not found errors\n if (error.statusCode === 404) {\n return reply.status(404).send(\n formatError(404, 'Not Found', error.message || 'Resource not found')\n )\n }\n\n // Handle all other errors as internal server errors\n const statusCode = error.statusCode || 500\n return reply.status(statusCode).send(\n formatError(\n statusCode,\n statusCode === 500 ? 'Internal Server Error' : error.name || 'Error',\n error.message || 'An unexpected error occurred'\n )\n )\n })\n}\n","import type { FastifyInstance } from 'fastify'\nimport fastifySwagger, { SwaggerOptions } from '@fastify/swagger'\nimport fastifySwaggerUI, { FastifySwaggerUiOptions } from '@fastify/swagger-ui'\nimport { jsonSchemaTransform } from 'fastify-type-provider-zod'\nimport type { CreateFastifyOptions } from './types.ts'\n\nexport async function setupSwagger(\n fastify: FastifyInstance,\n options: CreateFastifyOptions\n): Promise<string | undefined> {\n if (!options.swagger) {\n return undefined\n }\n\n const openApiConfig: any = {\n openapi: {\n info: {\n title: options.swagger.title,\n version: options.swagger.version,\n description: options.swagger.description,\n },\n },\n transform: jsonSchemaTransform,\n }\n\n // Add security schemes for each token type if JWT is enabled\n if (options.jwt && options.jwt.tokenTypes) {\n const securitySchemes: any = {}\n\n for (const [typeName, typeConfig] of Object.entries(options.jwt.tokenTypes)) {\n const headerName = typeConfig.headerName.toLowerCase()\n\n // Use bearer auth for 'authorization' header, apiKey for custom headers\n if (headerName === 'authorization') {\n securitySchemes[typeName] = {\n type: 'http',\n scheme: 'bearer',\n bearerFormat: 'JWT',\n description: `JWT token for ${typeName} authentication`,\n }\n } else {\n securitySchemes[typeName] = {\n type: 'apiKey',\n in: 'header',\n name: typeConfig.headerName,\n description: `JWT token for ${typeName} authentication`,\n }\n }\n }\n\n openApiConfig.openapi.components = {\n securitySchemes,\n }\n }\n\n await fastify.register(fastifySwagger, openApiConfig as SwaggerOptions)\n\n let routePrefix = options.swagger.routePrefix || '/docs/'\n\n if (!routePrefix.startsWith('/')) {\n routePrefix = '/' + routePrefix\n }\n\n if (!routePrefix.endsWith('/')) {\n routePrefix = routePrefix + '/'\n }\n\n await fastify.register(fastifySwaggerUI, { routePrefix } as FastifySwaggerUiOptions)\n\n return routePrefix\n}\n\nexport function setupSwaggerSecurityHook(\n fastify: FastifyInstance,\n options: CreateFastifyOptions,\n swaggerRoutePrefix: string | undefined\n): void {\n if (!options.jwt || !options.swagger) {\n return\n }\n\n // Auto-inject security requirements for JWT-protected routes\n fastify.addHook('onRoute', (routeOptions) => {\n // Skip Swagger routes\n if (swaggerRoutePrefix && routeOptions.url.startsWith(swaggerRoutePrefix)) {\n return\n }\n\n const routeConfig = (routeOptions.config as any) || {}\n const jwtTypes = routeConfig.jwtTypes\n\n // Skip if JWT is explicitly bypassed\n if (jwtTypes === false) {\n return\n }\n\n // Determine which token types are required\n // Start with global requiredTypes (always checked)\n let requiredTypes: string[] = []\n\n if (options.jwt?.requiredTypes && options.jwt.requiredTypes.length > 0) {\n requiredTypes = [...options.jwt.requiredTypes]\n }\n\n // Add route-specific types if specified\n if (Array.isArray(jwtTypes) && jwtTypes.length > 0) {\n // Combine both, removing duplicates\n requiredTypes = [...new Set([...requiredTypes, ...jwtTypes])]\n }\n\n // Inject security requirement if JWT types are required\n if (requiredTypes.length > 0) {\n if (!routeOptions.schema) {\n routeOptions.schema = {}\n }\n if (!routeOptions.schema.security) {\n routeOptions.schema.security = requiredTypes.map(typeName => ({ [typeName]: [] }))\n }\n }\n })\n}\n","import jwt, { JwtPayload, SignOptions, VerifyOptions } from 'jsonwebtoken'\nimport type { SignTokenOptions, VerifyTokenOptions } from '@a_jackie_z/fastify-types'\n\n/**\n * Sign a JWT token with kid, expiration, and optional JWT header parameters\n */\nexport function signToken(\n payload: Record<string, any>,\n secret: string,\n kid: string,\n expiresIn: string,\n options?: SignTokenOptions\n): string {\n const signOptions: SignOptions = {\n expiresIn: expiresIn as any,\n keyid: kid,\n }\n\n if (options?.algorithm) {\n signOptions.algorithm = options.algorithm\n }\n if (options?.iss) {\n signOptions.issuer = options.iss\n }\n if (options?.aud) {\n signOptions.audience = options.aud\n }\n\n // Apply custom header configuration\n if (options?.header) {\n const headerConfig: Record<string, any> = {}\n\n // Add standard JWT headers\n if (options.header.typ !== undefined) {\n headerConfig.typ = options.header.typ\n }\n if (options.header.cty !== undefined) {\n headerConfig.cty = options.header.cty\n }\n\n // Add custom header claims (exclude typ and cty as they're already handled)\n for (const [key, value] of Object.entries(options.header)) {\n if (key !== 'typ' && key !== 'cty') {\n headerConfig[key] = value\n }\n }\n\n // Only set header if we have custom fields\n if (Object.keys(headerConfig).length > 0) {\n signOptions.header = headerConfig as any\n }\n }\n\n return jwt.sign(payload, secret, signOptions)\n}\n\n/**\n * Verify a JWT token with kid-based secret selection and optional issuer validation\n */\nexport function verifyToken(\n token: string,\n secrets: Record<string, string>,\n options?: VerifyTokenOptions\n): JwtPayload {\n const debug = options?.debug ?? false\n\n try {\n // Decode header to extract kid\n const parts = token.split('.')\n if (parts.length !== 3 || !parts[0]) {\n throw new Error(debug ? 'Malformed JWT token: Token must have 3 parts separated by dots' : 'Malformed token')\n }\n\n const headerPart = parts[0]\n let header: any\n try {\n header = JSON.parse(Buffer.from(headerPart, 'base64').toString())\n } catch {\n throw new Error(debug ? 'Malformed JWT token: Invalid base64 encoding in header' : 'Malformed token')\n }\n\n const kid = header.kid\n if (!kid) {\n throw new Error(\n debug\n ? 'Missing kid in JWT token header. Ensure the token was signed with a kid parameter.'\n : 'Missing kid in token header'\n )\n }\n\n // Validate expected header claims if configured\n if (options?.expectedHeader) {\n // Validate typ (token type)\n if (options.expectedHeader.typ !== undefined && header.typ !== options.expectedHeader.typ) {\n throw new Error(\n debug\n ? `Invalid JWT header 'typ': expected \"${options.expectedHeader.typ}\", got \"${header.typ || 'undefined'}\"`\n : 'Invalid JWT header typ'\n )\n }\n\n // Validate cty (content type)\n if (options.expectedHeader.cty !== undefined && header.cty !== options.expectedHeader.cty) {\n throw new Error(\n debug\n ? `Invalid JWT header 'cty': expected \"${options.expectedHeader.cty}\", got \"${header.cty || 'undefined'}\"`\n : 'Invalid JWT header cty'\n )\n }\n\n // Validate custom header claims\n for (const [key, expectedValue] of Object.entries(options.expectedHeader)) {\n if (key !== 'typ' && key !== 'cty' && expectedValue !== undefined) {\n if (header[key] !== expectedValue) {\n throw new Error(\n debug\n ? `Invalid JWT header '${key}': expected \"${expectedValue}\", got \"${header[key] || 'undefined'}\"`\n : `Invalid JWT header ${key}`\n )\n }\n }\n }\n }\n\n const secret = secrets[kid]\n if (!secret) {\n const availableKeys = Object.keys(secrets).join(', ')\n throw new Error(\n debug\n ? `Unknown key ID \"${kid}\" in JWT token. Available keys: ${availableKeys}`\n : `Unknown key ID \"${kid}\"`\n )\n }\n\n // Verify token with the secret\n const verifyOptions: VerifyOptions = {\n complete: false,\n }\n\n const decoded = jwt.verify(token, secret, verifyOptions) as JwtPayload\n\n // Validate issuer if allowedIss is configured\n if (options?.allowedIss && options.allowedIss.length > 0) {\n const tokenIss = decoded.iss\n if (!tokenIss) {\n throw new Error(\n debug\n ? `Token missing issuer (iss) claim. Expected one of: ${options.allowedIss.join(', ')}`\n : 'Token missing issuer claim'\n )\n }\n if (!options.allowedIss.includes(tokenIss)) {\n throw new Error(\n debug\n ? `Invalid token issuer \"${tokenIss}\". Expected one of: ${options.allowedIss.join(', ')}`\n : `Invalid token issuer \"${tokenIss}\"`\n )\n }\n }\n\n return decoded\n } catch (error) {\n if (error instanceof jwt.TokenExpiredError) {\n if (debug) {\n const expDate = new Date(error.expiredAt).toISOString()\n const now = Math.floor(Date.now() / 1000)\n const exp = Math.floor(error.expiredAt.getTime() / 1000)\n const diff = now - exp\n throw new Error(`Token expired at ${expDate} (${diff} seconds ago)`)\n }\n throw new Error('Token has expired')\n }\n\n if (error instanceof jwt.JsonWebTokenError) {\n if (debug) {\n if (error.message.includes('signature')) {\n throw new Error('Invalid token signature: Token was signed with a different secret or has been tampered with')\n }\n if (error.message.includes('malformed')) {\n throw new Error('Malformed token: Token structure is invalid or corrupted')\n }\n throw new Error(`Token verification failed: ${error.message}`)\n }\n throw new Error('Invalid token')\n }\n\n throw error\n }\n}\n\n/**\n * Extract token from header value with Bearer prefix stripping\n * All JWT tokens must use Bearer prefix format: \"Bearer <token>\"\n */\nexport function extractTokenFromHeader(\n headerValue: string | string[] | undefined,\n headerName: string,\n debug?: boolean\n): string | null {\n if (!headerValue) {\n return null\n }\n\n const value = typeof headerValue === 'string' ? headerValue : headerValue[0]\n if (!value) {\n return null\n }\n\n // All JWT tokens must use \"Bearer <token>\" format\n const match = value.match(/^Bearer\\s+(\\S+)$/i)\n if (!match || !match[1]) {\n if (debug && value) {\n throw new Error(`Invalid ${headerName} header format. Expected: \"Bearer <token>\"`)\n }\n return null\n }\n\n return match[1]\n}\n\n","import { signToken, verifyToken, extractTokenFromHeader } from './core.ts'\nimport type { TokenTypeConfig, SignTokenOptions, VerifyTokenOptions } from '@a_jackie_z/fastify-types'\n\nexport interface FastifyJwtServiceOptions {\n secrets: Record<string, string>\n defaultKeyId: string\n tokenTypes: Record<string, TokenTypeConfig>\n debug?: boolean\n}\n\nexport class FastifyJwtService {\n private readonly secrets: Record<string, string>\n private readonly defaultKeyId: string\n private readonly tokenTypes: Record<string, TokenTypeConfig>\n private readonly debug: boolean\n\n constructor(options: FastifyJwtServiceOptions) {\n this.secrets = options.secrets\n this.defaultKeyId = options.defaultKeyId\n this.tokenTypes = options.tokenTypes\n this.debug = options.debug ?? false\n }\n\n /**\n * Generate a token for a specific type\n */\n generateToken(typeName: string, payload: Record<string, any>): string {\n const tokenType = this.tokenTypes[typeName]\n if (!tokenType) {\n throw new Error(\n this.debug\n ? `Unknown token type \"${typeName}\". Available types: ${Object.keys(this.tokenTypes).join(', ')}`\n : `Unknown token type \"${typeName}\"`\n )\n }\n\n const signOptions: SignTokenOptions = {}\n if (tokenType.algorithm) signOptions.algorithm = tokenType.algorithm\n if (tokenType.iss) signOptions.iss = tokenType.iss\n if (tokenType.aud) signOptions.aud = tokenType.aud\n if (tokenType.header) signOptions.header = tokenType.header\n\n const secret = this.secrets[this.defaultKeyId]\n if (!secret) {\n throw new Error(`Default key ID \"${this.defaultKeyId}\" not found in secrets`)\n }\n\n return signToken(payload, secret, this.defaultKeyId, tokenType.expiresIn, signOptions)\n }\n\n /**\n * Verify a token for a specific type\n */\n verifyToken(typeName: string, token: string): any {\n const tokenType = this.tokenTypes[typeName]\n if (!tokenType) {\n throw new Error(\n this.debug\n ? `Unknown token type \"${typeName}\". Available types: ${Object.keys(this.tokenTypes).join(', ')}`\n : `Unknown token type \"${typeName}\"`\n )\n }\n\n const verifyOptions: VerifyTokenOptions = {\n debug: this.debug,\n }\n\n if (tokenType.allowedIss && tokenType.allowedIss.length > 0) {\n verifyOptions.allowedIss = tokenType.allowedIss\n }\n\n if (tokenType.header) {\n verifyOptions.expectedHeader = tokenType.header\n }\n\n const decoded = verifyToken(token, this.secrets, verifyOptions)\n\n // Validate payload with Zod schema if provided\n if (tokenType.payloadSchema) {\n try {\n return tokenType.payloadSchema.parse(decoded)\n } catch (error) {\n if (this.debug && error instanceof Error) {\n throw new Error(`Token payload validation failed: ${error.message}`)\n }\n throw new Error('Invalid token payload')\n }\n }\n\n return decoded\n }\n\n /**\n * Extract token from header value\n */\n extractTokenFromHeader(headerValue: string | string[] | undefined, typeName: string): string | null {\n const tokenType = this.tokenTypes[typeName]\n if (!tokenType) {\n throw new Error(\n this.debug\n ? `Unknown token type \"${typeName}\". Available types: ${Object.keys(this.tokenTypes).join(', ')}`\n : `Unknown token type \"${typeName}\"`\n )\n }\n\n return extractTokenFromHeader(headerValue, tokenType.headerName, this.debug)\n }\n\n /**\n * Get token type configuration\n */\n getTokenTypeConfig(typeName: string): TokenTypeConfig | undefined {\n return this.tokenTypes[typeName]\n }\n\n /**\n * Get all configured token type names\n */\n getTokenTypeNames(): string[] {\n return Object.keys(this.tokenTypes)\n }\n}\n\n","import type { FastifyInstance } from 'fastify'\nimport { FastifyJwtService, type FastifyJwtServiceOptions } from './service.ts'\n\nexport async function registerJWT(\n fastify: FastifyInstance,\n options: FastifyJwtServiceOptions\n): Promise<FastifyJwtService> {\n const { secrets, defaultKeyId, tokenTypes } = options\n\n // Validate that defaultKeyId exists in secrets\n if (!secrets[defaultKeyId]) {\n throw new Error(`Default key ID \"${defaultKeyId}\" not found in JWT secrets`)\n }\n\n // Validate that at least one token type is configured\n if (!tokenTypes || Object.keys(tokenTypes).length === 0) {\n throw new Error('At least one token type must be configured')\n }\n\n // Validate token types configuration\n for (const [typeName, config] of Object.entries(tokenTypes)) {\n if (!config.headerName) {\n throw new Error(`Token type \"${typeName}\" is missing required field: headerName`)\n }\n if (!config.expiresIn) {\n throw new Error(`Token type \"${typeName}\" is missing required field: expiresIn`)\n }\n }\n\n // Create JWT service\n const jwtService = new FastifyJwtService(options)\n\n // Decorate Fastify instance\n fastify.decorate('jwtService', jwtService)\n\n return jwtService\n}\n","import type { FastifyReply, FastifyRequest } from 'fastify'\nimport type { FastifyJwtService } from './service.ts'\nimport { formatError } from '@a_jackie_z/fastify-types'\n\ninterface HookOptions {\n jwtService: FastifyJwtService\n swaggerRoutePrefix?: string\n requiredTypes?: string[]\n debug?: boolean\n}\n\nexport function createVerificationHook(options: HookOptions) {\n return async (request: FastifyRequest, reply: FastifyReply) => {\n const { jwtService, swaggerRoutePrefix, requiredTypes, debug } = options\n\n // Skip Swagger routes\n if (swaggerRoutePrefix && request.url.startsWith(swaggerRoutePrefix)) {\n return\n }\n\n const routeConfig = (request.routeOptions.config as any) || {}\n const jwtTypes = routeConfig.jwtTypes\n\n // If jwtTypes is explicitly false, skip verification\n if (jwtTypes === false) {\n return\n }\n\n // Determine which token types to verify\n // Always include requiredTypes first (checked for all routes)\n let typesToVerify: string[] = []\n\n if (requiredTypes && requiredTypes.length > 0) {\n typesToVerify = [...requiredTypes]\n }\n\n // Add route-specific types if specified\n if (Array.isArray(jwtTypes) && jwtTypes.length > 0) {\n // Combine both, removing duplicates\n typesToVerify = [...new Set([...typesToVerify, ...jwtTypes])]\n }\n\n // If no types to verify, route is public\n if (typesToVerify.length === 0) {\n return\n }\n\n // Initialize jwtPayloads map on request\n request.jwtPayloads = new Map()\n\n // Verify each required token type\n for (const typeName of typesToVerify) {\n const tokenTypeConfig = jwtService.getTokenTypeConfig(typeName)\n if (!tokenTypeConfig) {\n const errorMsg = debug\n ? `Unknown token type \"${typeName}\" in route configuration. Available types: ${jwtService.getTokenTypeNames().join(', ')}`\n : `Unknown token type \"${typeName}\"`\n return reply.status(500).send(formatError(500, 'Internal Server Error', errorMsg))\n }\n\n // Extract token from configured header\n const headerName = tokenTypeConfig.headerName.toLowerCase()\n const headerValue = request.headers[headerName]\n\n let token: string | null = null\n try {\n token = jwtService.extractTokenFromHeader(headerValue, typeName)\n } catch (err) {\n const errorMsg = debug && err instanceof Error\n ? err.message\n : `Invalid ${headerName} header format`\n return reply.status(401).send(formatError(401, 'Unauthorized', errorMsg))\n }\n\n if (!token) {\n const errorMsg = debug\n ? `Missing or invalid ${headerName} header for token type \"${typeName}\"`\n : `Missing or invalid ${headerName} header`\n return reply.status(401).send(formatError(401, 'Unauthorized', errorMsg))\n }\n\n // Verify token\n try {\n const payload = jwtService.verifyToken(typeName, token)\n request.jwtPayloads.set(typeName, payload)\n } catch (err) {\n const errorMsg = debug && err instanceof Error\n ? `Token verification failed for type \"${typeName}\": ${err.message}`\n : `Invalid or expired ${typeName} token`\n return reply.status(401).send(formatError(401, 'Unauthorized', errorMsg))\n }\n }\n }\n}\n","import type { FastifyInstance } from 'fastify'\nimport type { CreateFastifyOptions } from '../types.ts'\nimport { registerJWT } from './register.ts'\nimport { createVerificationHook } from './hooks.ts'\n\nexport async function setupJWT(\n fastify: FastifyInstance,\n options: CreateFastifyOptions,\n swaggerRoutePrefix: string | undefined\n): Promise<void> {\n if (!options.jwt) {\n return\n }\n\n const jwtService = await registerJWT(fastify, {\n secrets: options.jwt.secrets,\n defaultKeyId: options.jwt.defaultKeyId,\n tokenTypes: options.jwt.tokenTypes,\n debug: options.jwt.debug ?? false,\n })\n\n // Add verification hook for JWT verification (always add when JWT configured)\n const hookOptions: Parameters<typeof createVerificationHook>[0] = {\n jwtService,\n debug: options.jwt.debug ?? false,\n }\n\n if (options.jwt.requiredTypes !== undefined) {\n hookOptions.requiredTypes = options.jwt.requiredTypes\n }\n\n if (swaggerRoutePrefix !== undefined) {\n hookOptions.swaggerRoutePrefix = swaggerRoutePrefix\n }\n\n fastify.addHook('onRequest', createVerificationHook(hookOptions))\n}\n","import { randomBytes } from 'node:crypto'\n\n/**\n * Server-side crypto utilities for generating secure IDs and tokens\n * These functions require Node.js crypto module and are not frontend-compatible\n */\n\nconst ID_ALPHABET = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'\nconst ID_LENGTH = 16\n\n/**\n * Generate a random 16-character alphanumeric ID\n */\nexport function generateId(): string {\n const bytes = randomBytes(ID_LENGTH)\n let result = ''\n for (let i = 0; i < ID_LENGTH; i++) {\n result += ID_ALPHABET[bytes[i]! % ID_ALPHABET.length]\n }\n return result\n}\n\n/**\n * Generate a secure session token (64-character hex string)\n */\nexport function generateSessionToken(): string {\n return randomBytes(32).toString('hex')\n}\n\n/**\n * Generate a secure random string with specified length\n * @param length - Length of the string to generate\n * @param alphabet - Alphabet to use (defaults to alphanumeric)\n */\nexport function generateSecureString(length: number, alphabet = ID_ALPHABET): string {\n const bytes = randomBytes(length)\n let result = ''\n for (let i = 0; i < length; i++) {\n result += alphabet[bytes[i]! % alphabet.length]\n }\n return result\n}\n","/**\n * Parse JWT secrets from environment variable format\n * @param secretsString - Secrets in format: \"key1=secret1|key2=secret2\"\n * @returns Record mapping key IDs to their secrets\n * @throws Error if format is invalid, empty, contains duplicates, or has empty values\n */\nexport function parseJwtSecrets(secretsString: string): Record<string, string> {\n if (!secretsString || secretsString.trim().length === 0) {\n throw new Error('JWT secrets string cannot be empty')\n }\n\n const secrets: Record<string, string> = {}\n const pairs = secretsString.split('|')\n\n if (pairs.length === 0) {\n throw new Error('JWT secrets string must contain at least one key=secret pair')\n }\n\n for (const pair of pairs) {\n const trimmedPair = pair.trim()\n if (!trimmedPair) {\n continue // Skip empty pairs from trailing/leading separators\n }\n\n const separatorIndex = trimmedPair.indexOf('=')\n if (separatorIndex === -1) {\n throw new Error(`Invalid JWT secret format: \"${pair}\". Expected format: key=secret`)\n }\n\n const keyId = trimmedPair.substring(0, separatorIndex).trim()\n const secret = trimmedPair.substring(separatorIndex + 1).trim()\n\n if (!keyId) {\n throw new Error(`Empty key ID in JWT secret pair: \"${pair}\"`)\n }\n\n if (!secret) {\n throw new Error(`Empty secret for key ID \"${keyId}\"`)\n }\n\n if (secrets[keyId]) {\n throw new Error(`Duplicate key ID \"${keyId}\" in JWT secrets`)\n }\n\n secrets[keyId] = secret\n }\n\n if (Object.keys(secrets).length === 0) {\n throw new Error('JWT secrets string must contain at least one valid key=secret pair')\n }\n\n return secrets\n}\n","import { FastifyPluginCallback } from 'fastify'\nimport type { FastifyServer } from './types.ts'\nimport { ZodTypeProvider } from 'fastify-type-provider-zod'\n\nexport function createFastifyPlugin(cb: FastifyPluginCallback) {\n return function createFastifyPluginWrapper(\n fastify: FastifyServer,\n options: Parameters<FastifyPluginCallback>[1],\n done: Parameters<FastifyPluginCallback>[2],\n ) {\n const server = fastify.withTypeProvider<ZodTypeProvider>()\n let doneCalled = false\n\n const doneWrapper = (err?: Error) => {\n done(err)\n doneCalled = true\n }\n\n cb(server, options, doneWrapper)\n\n if (!doneCalled) {\n done()\n }\n }\n}\n","import { createFastifyPlugin } from '../plugin.ts'\nimport type { FastifyServer } from '../types.ts'\n\nexport const healthPlugin = createFastifyPlugin((app: FastifyServer) => {\n app.get('/v1/health', {\n schema: {\n tags: ['health'],\n },\n config: {\n jwt: false,\n },\n handler: async () => {\n return {\n status: 200,\n message: 'ok',\n }\n },\n })\n})\n","export * from './lib/fastify.ts'\nexport * from './lib/types.ts'\nexport * from './lib/crypto-utils.ts'\nexport * from './lib/jwt/service.ts'\nexport { parseJwtSecrets } from './lib/jwt/utils.ts'\nexport * from './lib/plugin.ts'\nexport * from './lib/plugins/healthPlugin.ts'\nexport * from './lib/error-handler.ts'\nexport * from 'fastify-type-provider-zod'\nexport type { FastifyErrorCodes, FastifyRequest, FastifyReply } from 'fastify'\n"],"mappings":"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","names":["formatError"]}
package/package.json CHANGED
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "name": "@a_jackie_z/fastify",
3
- "version": "1.3.3",
3
+ "version": "1.4.1",
4
4
  "description": "A collection of Fastify plugins and utilities for building robust web applications.",
5
5
  "license": "MIT",
6
6
  "author": "Sang Lu <connect.with.sang@gmail.com>",
7
7
  "repository": {
8
- "url": "https://github.com/a-jackie-z/fastify"
8
+ "url": "git+https://github.com/a-jackie-z/fastify.git"
9
9
  },
10
10
  "type": "module",
11
11
  "main": "./dist/index.js",
@@ -23,6 +23,7 @@
23
23
  "build": "tsup"
24
24
  },
25
25
  "dependencies": {
26
+ "@a_jackie_z/fastify-types": "^1.0.1",
26
27
  "@fastify/rate-limit": "^10.3.0",
27
28
  "@fastify/swagger": "^9.6.1",
28
29
  "@fastify/swagger-ui": "^5.2.5",