@a_jackie_z/fastify 1.1.2 → 1.1.3

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/index.d.ts CHANGED
@@ -6,6 +6,7 @@ export * from 'fastify-type-provider-zod';
6
6
  import { RateLimitPluginOptions } from '@fastify/rate-limit';
7
7
  import { FastifyJWTOptions } from '@fastify/jwt';
8
8
  import { IncomingMessage, ServerResponse } from 'node:http';
9
+ import { z } from 'zod';
9
10
 
10
11
  type JWTAuthorizationHandler = <TUser = unknown>(request: FastifyRequest, reply: FastifyReply, user: TUser) => Promise<boolean> | boolean;
11
12
  interface JWTRouteConfig {
@@ -44,8 +45,114 @@ type FastifyServer = FastifyInstance<RawServerDefault, IncomingMessage, ServerRe
44
45
  declare function createFastify(options?: CreateFastifyOptions): Promise<FastifyServer>;
45
46
  declare function runFastify(fastify: FastifyServer, host: string, port: number): Promise<void>;
46
47
 
48
+ /**
49
+ * Base access token payload schema
50
+ */
51
+ declare const baseAccessTokenPayloadSchema: z.ZodObject<{
52
+ identityId: z.ZodString;
53
+ type: z.ZodLiteral<"access">;
54
+ exp: z.ZodNumber;
55
+ }, z.core.$strip>;
56
+ /**
57
+ * Base refresh token payload schema
58
+ */
59
+ declare const baseRefreshTokenPayloadSchema: z.ZodObject<{
60
+ identityId: z.ZodString;
61
+ type: z.ZodLiteral<"refresh">;
62
+ exp: z.ZodNumber;
63
+ }, z.core.$strip>;
64
+ /**
65
+ * Inferred types from base schemas
66
+ */
67
+ type BaseAccessTokenPayload = z.infer<typeof baseAccessTokenPayloadSchema>;
68
+ type BaseRefreshTokenPayload = z.infer<typeof baseRefreshTokenPayloadSchema>;
69
+ /**
70
+ * Token pair interface
71
+ */
72
+ interface TokenPair {
73
+ accessToken: string;
74
+ refreshToken: string;
75
+ }
76
+ /**
77
+ * FastifyJwtService configuration options
78
+ */
79
+ interface FastifyJwtServiceOptions {
80
+ accessTokenExpiry: string;
81
+ refreshTokenExpiry: string;
82
+ renewalThreshold: string;
83
+ accessTokenSchema?: z.ZodSchema;
84
+ refreshTokenSchema?: z.ZodSchema;
85
+ }
86
+ /**
87
+ * Generic JWT service for Fastify applications
88
+ *
89
+ * @template TAccess - Access token payload type extending BaseAccessTokenPayload
90
+ * @template TRefresh - Refresh token payload type extending BaseRefreshTokenPayload
91
+ *
92
+ * @example
93
+ * // Using base schemas
94
+ * const jwtService = new FastifyJwtService(app, {
95
+ * accessTokenExpiry: '15m',
96
+ * refreshTokenExpiry: '7d',
97
+ * renewalThreshold: '1d',
98
+ * })
99
+ *
100
+ * @example
101
+ * // Using extended schemas
102
+ * const customAccessSchema = baseAccessTokenPayloadSchema.extend({
103
+ * roles: z.array(z.string())
104
+ * })
105
+ * const jwtService = new FastifyJwtService(app, {
106
+ * accessTokenExpiry: '15m',
107
+ * refreshTokenExpiry: '7d',
108
+ * renewalThreshold: '1d',
109
+ * accessTokenSchema: customAccessSchema,
110
+ * })
111
+ */
112
+ declare class FastifyJwtService<TAccess extends BaseAccessTokenPayload = BaseAccessTokenPayload, TRefresh extends BaseRefreshTokenPayload = BaseRefreshTokenPayload> {
113
+ private readonly app;
114
+ private readonly accessTokenExpiry;
115
+ private readonly refreshTokenExpiry;
116
+ private readonly renewalThreshold;
117
+ private readonly accessTokenSchema;
118
+ private readonly refreshTokenSchema;
119
+ constructor(app: FastifyServer, options: FastifyJwtServiceOptions);
120
+ /**
121
+ * Generate an access token for the given identity
122
+ */
123
+ generateAccessToken(identityId: string, extraPayload?: Partial<Omit<TAccess, 'identityId' | 'type' | 'exp'>>): string;
124
+ /**
125
+ * Generate a refresh token for the given identity
126
+ */
127
+ generateRefreshToken(identityId: string, extraPayload?: Partial<Omit<TRefresh, 'identityId' | 'type' | 'exp'>>): string;
128
+ /**
129
+ * Generate both access and refresh tokens
130
+ */
131
+ generateTokenPair(identityId: string): TokenPair;
132
+ /**
133
+ * Verify and decode an access token
134
+ */
135
+ verifyAccessToken(token: string): TAccess;
136
+ /**
137
+ * Verify and decode a refresh token
138
+ */
139
+ verifyRefreshToken(token: string): TRefresh;
140
+ /**
141
+ * Renew a refresh token for the given identity
142
+ */
143
+ renewRefreshToken(identityId: string): string;
144
+ /**
145
+ * Check if a refresh token should be renewed based on its expiry
146
+ */
147
+ shouldRenewRefreshToken(exp: number): boolean;
148
+ /**
149
+ * Parse timespan string to seconds
150
+ */
151
+ private parseTimespan;
152
+ }
153
+
47
154
  declare function createFastifyPlugin(cb: FastifyPluginCallback): (fastify: FastifyServer, options: Parameters<FastifyPluginCallback>[1], done: Parameters<FastifyPluginCallback>[2]) => void;
48
155
 
49
156
  declare const healthPlugin: (fastify: FastifyServer, options: Parameters<fastify.FastifyPluginCallback>[1], done: Parameters<fastify.FastifyPluginCallback>[2]) => void;
50
157
 
51
- export { type CreateFastifyOptions, type FastifyServer, type JWTAuthorizationHandler, type JWTRouteConfig, createFastify, createFastifyPlugin, healthPlugin, runFastify };
158
+ export { type BaseAccessTokenPayload, type BaseRefreshTokenPayload, type CreateFastifyOptions, FastifyJwtService, type FastifyJwtServiceOptions, type FastifyServer, type JWTAuthorizationHandler, type JWTRouteConfig, type TokenPair, baseAccessTokenPayloadSchema, baseRefreshTokenPayloadSchema, createFastify, createFastifyPlugin, healthPlugin, runFastify };
package/dist/index.js CHANGED
@@ -99,6 +99,142 @@ async function runFastify(fastify, host, port) {
99
99
  }
100
100
  }
101
101
 
102
+ // lib/jwt.service.ts
103
+ import { z } from "zod";
104
+ var baseAccessTokenPayloadSchema = z.object({
105
+ identityId: z.string(),
106
+ type: z.literal("access"),
107
+ exp: z.number()
108
+ });
109
+ var baseRefreshTokenPayloadSchema = z.object({
110
+ identityId: z.string(),
111
+ type: z.literal("refresh"),
112
+ exp: z.number()
113
+ });
114
+ var FastifyJwtService = class {
115
+ constructor(app, options) {
116
+ this.app = app;
117
+ this.accessTokenExpiry = options.accessTokenExpiry;
118
+ this.refreshTokenExpiry = options.refreshTokenExpiry;
119
+ this.renewalThreshold = options.renewalThreshold;
120
+ this.accessTokenSchema = options.accessTokenSchema ?? baseAccessTokenPayloadSchema;
121
+ this.refreshTokenSchema = options.refreshTokenSchema ?? baseRefreshTokenPayloadSchema;
122
+ }
123
+ accessTokenExpiry;
124
+ refreshTokenExpiry;
125
+ renewalThreshold;
126
+ accessTokenSchema;
127
+ refreshTokenSchema;
128
+ /**
129
+ * Generate an access token for the given identity
130
+ */
131
+ generateAccessToken(identityId, extraPayload) {
132
+ const payload = {
133
+ identityId,
134
+ type: "access",
135
+ ...extraPayload
136
+ };
137
+ return this.app.jwt.sign(payload, {
138
+ expiresIn: this.accessTokenExpiry
139
+ });
140
+ }
141
+ /**
142
+ * Generate a refresh token for the given identity
143
+ */
144
+ generateRefreshToken(identityId, extraPayload) {
145
+ const payload = {
146
+ identityId,
147
+ type: "refresh",
148
+ ...extraPayload
149
+ };
150
+ return this.app.jwt.sign(payload, {
151
+ expiresIn: this.refreshTokenExpiry
152
+ });
153
+ }
154
+ /**
155
+ * Generate both access and refresh tokens
156
+ */
157
+ generateTokenPair(identityId) {
158
+ return {
159
+ accessToken: this.generateAccessToken(identityId),
160
+ refreshToken: this.generateRefreshToken(identityId)
161
+ };
162
+ }
163
+ /**
164
+ * Verify and decode an access token
165
+ */
166
+ verifyAccessToken(token) {
167
+ try {
168
+ const payload = this.app.jwt.verify(token);
169
+ const validated = this.accessTokenSchema.parse(payload);
170
+ if (validated.type !== "access") {
171
+ throw new Error("Invalid token type");
172
+ }
173
+ return validated;
174
+ } catch (error) {
175
+ if (error instanceof z.ZodError) {
176
+ throw new Error("Invalid token payload");
177
+ }
178
+ throw error;
179
+ }
180
+ }
181
+ /**
182
+ * Verify and decode a refresh token
183
+ */
184
+ verifyRefreshToken(token) {
185
+ try {
186
+ const payload = this.app.jwt.verify(token);
187
+ const validated = this.refreshTokenSchema.parse(payload);
188
+ if (validated.type !== "refresh") {
189
+ throw new Error("Invalid token type");
190
+ }
191
+ return validated;
192
+ } catch (error) {
193
+ if (error instanceof z.ZodError) {
194
+ throw new Error("Invalid token payload");
195
+ }
196
+ throw error;
197
+ }
198
+ }
199
+ /**
200
+ * Renew a refresh token for the given identity
201
+ */
202
+ renewRefreshToken(identityId) {
203
+ return this.generateRefreshToken(identityId);
204
+ }
205
+ /**
206
+ * Check if a refresh token should be renewed based on its expiry
207
+ */
208
+ shouldRenewRefreshToken(exp) {
209
+ const now = Math.floor(Date.now() / 1e3);
210
+ const threshold = this.parseTimespan(this.renewalThreshold);
211
+ return exp - now < threshold;
212
+ }
213
+ /**
214
+ * Parse timespan string to seconds
215
+ */
216
+ parseTimespan(timespan) {
217
+ const match = timespan.match(/^(\d+)([smhd])$/);
218
+ if (!match || !match[1] || !match[2]) {
219
+ throw new Error(`Invalid timespan format: ${timespan}`);
220
+ }
221
+ const value = parseInt(match[1], 10);
222
+ const unit = match[2];
223
+ switch (unit) {
224
+ case "s":
225
+ return value;
226
+ case "m":
227
+ return value * 60;
228
+ case "h":
229
+ return value * 3600;
230
+ case "d":
231
+ return value * 86400;
232
+ default:
233
+ throw new Error(`Unknown timespan unit: ${unit}`);
234
+ }
235
+ }
236
+ };
237
+
102
238
  // lib/plugin.ts
103
239
  function createFastifyPlugin(cb) {
104
240
  return function createFastifyPluginWrapper(fastify, options, done) {
@@ -136,6 +272,9 @@ var healthPlugin = createFastifyPlugin((app) => {
136
272
  // index.ts
137
273
  export * from "fastify-type-provider-zod";
138
274
  export {
275
+ FastifyJwtService,
276
+ baseAccessTokenPayloadSchema,
277
+ baseRefreshTokenPayloadSchema,
139
278
  createFastify,
140
279
  createFastifyPlugin,
141
280
  healthPlugin,
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../lib/fastify.ts","../lib/plugin.ts","../lib/plugins/healthPlugin.ts","../index.ts"],"sourcesContent":["import Fastify, {\n FastifyBaseLogger,\n FastifyContextConfig,\n FastifyInstance,\n FastifyReply,\n FastifyServerOptions,\n RawServerDefault,\n} from 'fastify'\nimport type { FastifyRequest } from 'fastify'\nimport {\n serializerCompiler,\n validatorCompiler,\n ZodTypeProvider,\n} from 'fastify-type-provider-zod'\nimport fastifyRateLimit, { RateLimitPluginOptions } from '@fastify/rate-limit'\nimport fastifyJwt, { FastifyJWTOptions } from '@fastify/jwt'\nimport fastifySwagger, { SwaggerOptions } from '@fastify/swagger'\nimport fastifySwaggerUI, { FastifySwaggerUiOptions } from '@fastify/swagger-ui'\nimport { IncomingMessage, ServerResponse } from 'node:http'\n\nexport type JWTAuthorizationHandler = <TUser = unknown>(\n request: FastifyRequest,\n reply: FastifyReply,\n user: TUser\n) => Promise<boolean> | boolean\n\nexport interface JWTRouteConfig {\n jwt?:\n | boolean // true = require JWT, false = bypass JWT\n | {\n required?: boolean // Default: true if global enabled\n authorize?: JWTAuthorizationHandler // Check JWT info before route processing\n }\n}\n\n// Extend Fastify types to include custom config\ndeclare module 'fastify' {\n interface FastifyContextConfig {\n jwt?:\n | boolean\n | {\n required?: boolean\n authorize?: JWTAuthorizationHandler\n }\n }\n}\n\nexport interface CreateFastifyOptions {\n logger?: FastifyServerOptions['loggerInstance'],\n rateLimit?: {\n global?: RateLimitPluginOptions\n }\n jwt?: {\n secret: string\n sign?: FastifyJWTOptions['sign']\n verify?: FastifyJWTOptions['verify']\n global?: boolean // Enable JWT check globally for all routes\n }\n swagger?: {\n title: string\n version: string\n description: string\n routePrefix?: string\n }\n}\n\nexport type FastifyServer = FastifyInstance<\n RawServerDefault,\n IncomingMessage,\n ServerResponse,\n FastifyBaseLogger,\n ZodTypeProvider\n> & FastifyContextConfig\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 // Store Swagger route prefix for JWT bypass logic\n let swaggerRoutePrefix: string | undefined\n\n // Register Swagger first to capture all routes with Zod schemas\n if (options?.swagger) {\n await fastify.register(fastifySwagger, {\n openapi: {\n info: {\n title: options.swagger.title,\n version: options.swagger.version,\n description: options.swagger.description,\n },\n },\n } 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 swaggerRoutePrefix = routePrefix\n await fastify.register(fastifySwaggerUI, { routePrefix } as FastifySwaggerUiOptions)\n }\n\n // Register JWT authentication\n if (options?.jwt) {\n const jwtOptions: FastifyJWTOptions = {\n secret: options.jwt.secret,\n }\n if (options.jwt.sign !== undefined) {\n jwtOptions.sign = options.jwt.sign\n }\n if (options.jwt.verify !== undefined) {\n jwtOptions.verify = options.jwt.verify\n }\n await fastify.register(fastifyJwt, jwtOptions)\n\n // Global JWT checking hook\n if (options.jwt.global) {\n fastify.addHook('onRequest', async (request, reply) => {\n // Skip JWT verification for Swagger documentation routes\n if (swaggerRoutePrefix && request.url.startsWith(swaggerRoutePrefix)) {\n return\n }\n\n const routeConfig = (request.routeOptions.config as JWTRouteConfig) || {}\n const jwtConfig = routeConfig.jwt\n\n // Check if route explicitly bypasses JWT\n if (jwtConfig === false) {\n return\n }\n\n // Check if route explicitly requires JWT or uses global setting\n const shouldVerify =\n jwtConfig === true ||\n (typeof jwtConfig === 'object' && jwtConfig.required !== false) ||\n (jwtConfig === undefined && options?.jwt?.global === true)\n\n if (shouldVerify) {\n try {\n await request.jwtVerify()\n\n // Custom authorization - check JWT info before route processing\n if (typeof jwtConfig === 'object' && jwtConfig.authorize) {\n const authorized = await jwtConfig.authorize(request, reply, request.user)\n if (!authorized) {\n reply.status(403).send({\n error: 'Forbidden',\n message: 'Authorization failed',\n })\n return\n }\n }\n } catch (err) {\n reply.status(401).send({\n error: 'Unauthorized',\n message: 'Invalid or missing JWT token',\n })\n }\n }\n })\n }\n }\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 { 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/plugin.ts'\nexport * from './lib/plugins/healthPlugin.ts'\nexport * from 'fastify-type-provider-zod'\nexport type {\n FastifyRequest,\n FastifyReply,\n} from 'fastify'\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../lib/fastify.ts","../lib/jwt.service.ts","../lib/plugin.ts","../lib/plugins/healthPlugin.ts","../index.ts"],"sourcesContent":["import Fastify, {\n FastifyBaseLogger,\n FastifyContextConfig,\n FastifyInstance,\n FastifyReply,\n FastifyServerOptions,\n RawServerDefault,\n} from 'fastify'\nimport type { FastifyRequest } from 'fastify'\nimport {\n serializerCompiler,\n validatorCompiler,\n ZodTypeProvider,\n} from 'fastify-type-provider-zod'\nimport fastifyRateLimit, { RateLimitPluginOptions } from '@fastify/rate-limit'\nimport fastifyJwt, { FastifyJWTOptions } from '@fastify/jwt'\nimport fastifySwagger, { SwaggerOptions } from '@fastify/swagger'\nimport fastifySwaggerUI, { FastifySwaggerUiOptions } from '@fastify/swagger-ui'\nimport { IncomingMessage, ServerResponse } from 'node:http'\n\nexport type JWTAuthorizationHandler = <TUser = unknown>(\n request: FastifyRequest,\n reply: FastifyReply,\n user: TUser\n) => Promise<boolean> | boolean\n\nexport interface JWTRouteConfig {\n jwt?:\n | boolean // true = require JWT, false = bypass JWT\n | {\n required?: boolean // Default: true if global enabled\n authorize?: JWTAuthorizationHandler // Check JWT info before route processing\n }\n}\n\n// Extend Fastify types to include custom config\ndeclare module 'fastify' {\n interface FastifyContextConfig {\n jwt?:\n | boolean\n | {\n required?: boolean\n authorize?: JWTAuthorizationHandler\n }\n }\n}\n\nexport interface CreateFastifyOptions {\n logger?: FastifyServerOptions['loggerInstance'],\n rateLimit?: {\n global?: RateLimitPluginOptions\n }\n jwt?: {\n secret: string\n sign?: FastifyJWTOptions['sign']\n verify?: FastifyJWTOptions['verify']\n global?: boolean // Enable JWT check globally for all routes\n }\n swagger?: {\n title: string\n version: string\n description: string\n routePrefix?: string\n }\n}\n\nexport type FastifyServer = FastifyInstance<\n RawServerDefault,\n IncomingMessage,\n ServerResponse,\n FastifyBaseLogger,\n ZodTypeProvider\n> & FastifyContextConfig\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 // Store Swagger route prefix for JWT bypass logic\n let swaggerRoutePrefix: string | undefined\n\n // Register Swagger first to capture all routes with Zod schemas\n if (options?.swagger) {\n await fastify.register(fastifySwagger, {\n openapi: {\n info: {\n title: options.swagger.title,\n version: options.swagger.version,\n description: options.swagger.description,\n },\n },\n } 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 swaggerRoutePrefix = routePrefix\n await fastify.register(fastifySwaggerUI, { routePrefix } as FastifySwaggerUiOptions)\n }\n\n // Register JWT authentication\n if (options?.jwt) {\n const jwtOptions: FastifyJWTOptions = {\n secret: options.jwt.secret,\n }\n if (options.jwt.sign !== undefined) {\n jwtOptions.sign = options.jwt.sign\n }\n if (options.jwt.verify !== undefined) {\n jwtOptions.verify = options.jwt.verify\n }\n await fastify.register(fastifyJwt, jwtOptions)\n\n // Global JWT checking hook\n if (options.jwt.global) {\n fastify.addHook('onRequest', async (request, reply) => {\n // Skip JWT verification for Swagger documentation routes\n if (swaggerRoutePrefix && request.url.startsWith(swaggerRoutePrefix)) {\n return\n }\n\n const routeConfig = (request.routeOptions.config as JWTRouteConfig) || {}\n const jwtConfig = routeConfig.jwt\n\n // Check if route explicitly bypasses JWT\n if (jwtConfig === false) {\n return\n }\n\n // Check if route explicitly requires JWT or uses global setting\n const shouldVerify =\n jwtConfig === true ||\n (typeof jwtConfig === 'object' && jwtConfig.required !== false) ||\n (jwtConfig === undefined && options?.jwt?.global === true)\n\n if (shouldVerify) {\n try {\n await request.jwtVerify()\n\n // Custom authorization - check JWT info before route processing\n if (typeof jwtConfig === 'object' && jwtConfig.authorize) {\n const authorized = await jwtConfig.authorize(request, reply, request.user)\n if (!authorized) {\n reply.status(403).send({\n error: 'Forbidden',\n message: 'Authorization failed',\n })\n return\n }\n }\n } catch (err) {\n reply.status(401).send({\n error: 'Unauthorized',\n message: 'Invalid or missing JWT token',\n })\n }\n }\n })\n }\n }\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'\nimport { FastifyServer } from './fastify.ts'\n\n/**\n * Base access token payload schema\n */\nexport const baseAccessTokenPayloadSchema = z.object({\n identityId: z.string(),\n type: z.literal('access'),\n exp: z.number(),\n})\n\n/**\n * Base refresh token payload schema\n */\nexport const baseRefreshTokenPayloadSchema = z.object({\n identityId: z.string(),\n type: z.literal('refresh'),\n exp: z.number(),\n})\n\n/**\n * Inferred types from base schemas\n */\nexport type BaseAccessTokenPayload = z.infer<typeof baseAccessTokenPayloadSchema>\nexport type BaseRefreshTokenPayload = z.infer<typeof baseRefreshTokenPayloadSchema>\n\n/**\n * Token pair interface\n */\nexport interface TokenPair {\n accessToken: string\n refreshToken: string\n}\n\n/**\n * FastifyJwtService configuration options\n */\nexport interface FastifyJwtServiceOptions {\n accessTokenExpiry: string\n refreshTokenExpiry: string\n renewalThreshold: string\n accessTokenSchema?: z.ZodSchema\n refreshTokenSchema?: z.ZodSchema\n}\n\n/**\n * Generic JWT service for Fastify applications\n *\n * @template TAccess - Access token payload type extending BaseAccessTokenPayload\n * @template TRefresh - Refresh token payload type extending BaseRefreshTokenPayload\n *\n * @example\n * // Using base schemas\n * const jwtService = new FastifyJwtService(app, {\n * accessTokenExpiry: '15m',\n * refreshTokenExpiry: '7d',\n * renewalThreshold: '1d',\n * })\n *\n * @example\n * // Using extended schemas\n * const customAccessSchema = baseAccessTokenPayloadSchema.extend({\n * roles: z.array(z.string())\n * })\n * const jwtService = new FastifyJwtService(app, {\n * accessTokenExpiry: '15m',\n * refreshTokenExpiry: '7d',\n * renewalThreshold: '1d',\n * accessTokenSchema: customAccessSchema,\n * })\n */\nexport class FastifyJwtService<\n TAccess extends BaseAccessTokenPayload = BaseAccessTokenPayload,\n TRefresh extends BaseRefreshTokenPayload = BaseRefreshTokenPayload\n> {\n private readonly accessTokenExpiry: string\n private readonly refreshTokenExpiry: string\n private readonly renewalThreshold: string\n private readonly accessTokenSchema: z.ZodSchema<TAccess>\n private readonly refreshTokenSchema: z.ZodSchema<TRefresh>\n\n constructor(\n private readonly app: FastifyServer,\n options: FastifyJwtServiceOptions\n ) {\n this.accessTokenExpiry = options.accessTokenExpiry\n this.refreshTokenExpiry = options.refreshTokenExpiry\n this.renewalThreshold = options.renewalThreshold\n this.accessTokenSchema = (options.accessTokenSchema ?? baseAccessTokenPayloadSchema) as z.ZodSchema<TAccess>\n this.refreshTokenSchema = (options.refreshTokenSchema ?? baseRefreshTokenPayloadSchema) as z.ZodSchema<TRefresh>\n }\n\n /**\n * Generate an access token for the given identity\n */\n generateAccessToken(identityId: string, extraPayload?: Partial<Omit<TAccess, 'identityId' | 'type' | 'exp'>>): string {\n const payload = {\n identityId,\n type: 'access' as const,\n ...extraPayload,\n }\n\n return this.app.jwt.sign(payload, {\n expiresIn: this.accessTokenExpiry,\n })\n }\n\n /**\n * Generate a refresh token for the given identity\n */\n generateRefreshToken(identityId: string, extraPayload?: Partial<Omit<TRefresh, 'identityId' | 'type' | 'exp'>>): string {\n const payload = {\n identityId,\n type: 'refresh' as const,\n ...extraPayload,\n }\n\n return this.app.jwt.sign(payload, {\n expiresIn: this.refreshTokenExpiry,\n })\n }\n\n /**\n * Generate both access and refresh tokens\n */\n generateTokenPair(identityId: string): TokenPair {\n return {\n accessToken: this.generateAccessToken(identityId),\n refreshToken: this.generateRefreshToken(identityId),\n }\n }\n\n /**\n * Verify and decode an access token\n */\n verifyAccessToken(token: string): TAccess {\n try {\n const payload = this.app.jwt.verify(token) as unknown\n const validated = this.accessTokenSchema.parse(payload)\n\n if (validated.type !== 'access') {\n throw new Error('Invalid token type')\n }\n\n return validated\n } catch (error) {\n if (error instanceof z.ZodError) {\n throw new Error('Invalid token payload')\n }\n throw error\n }\n }\n\n /**\n * Verify and decode a refresh token\n */\n verifyRefreshToken(token: string): TRefresh {\n try {\n const payload = this.app.jwt.verify(token) as unknown\n const validated = this.refreshTokenSchema.parse(payload)\n\n if (validated.type !== 'refresh') {\n throw new Error('Invalid token type')\n }\n\n return validated\n } catch (error) {\n if (error instanceof z.ZodError) {\n throw new Error('Invalid token payload')\n }\n throw error\n }\n }\n\n /**\n * Renew a refresh token for the given identity\n */\n renewRefreshToken(identityId: string): string {\n return this.generateRefreshToken(identityId)\n }\n\n /**\n * Check if a refresh token should be renewed based on its expiry\n */\n shouldRenewRefreshToken(exp: number): boolean {\n const now = Math.floor(Date.now() / 1000)\n const threshold = this.parseTimespan(this.renewalThreshold)\n return (exp - now) < threshold\n }\n\n /**\n * Parse timespan string to seconds\n */\n private parseTimespan(timespan: string): number {\n const match = timespan.match(/^(\\d+)([smhd])$/)\n if (!match || !match[1] || !match[2]) {\n throw new Error(`Invalid timespan format: ${timespan}`)\n }\n\n const value = parseInt(match[1]!, 10)\n const unit = match[2]!\n\n switch (unit) {\n case 's':\n return value\n case 'm':\n return value * 60\n case 'h':\n return value * 3600\n case 'd':\n return value * 86400\n default:\n throw new Error(`Unknown timespan unit: ${unit}`)\n }\n }\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/jwt.service.ts'\nexport * from './lib/plugin.ts'\nexport * from './lib/plugins/healthPlugin.ts'\nexport * from 'fastify-type-provider-zod'\nexport type {\n FastifyRequest,\n FastifyReply,\n} from 'fastify'\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@a_jackie_z/fastify",
3
- "version": "1.1.2",
3
+ "version": "1.1.3",
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>",