astro-tokenkit 1.0.16 → 1.0.18

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
@@ -134,6 +134,10 @@ interface AuthConfig {
134
134
  policy?: RefreshPolicy;
135
135
  /** Cookie configuration */
136
136
  cookies?: CookieConfig;
137
+ /** Custom fetch implementation */
138
+ fetch?: typeof fetch;
139
+ /** Dangerously ignore certificate errors (bypass SSL validation) */
140
+ dangerouslyIgnoreCertificateErrors?: boolean;
137
141
  }
138
142
  /**
139
143
  * Refresh policy
@@ -172,6 +176,37 @@ interface RetryConfig {
172
176
  /** Initial delay in ms */
173
177
  delay?: number;
174
178
  }
179
+ /**
180
+ * Idle timeout configuration
181
+ */
182
+ interface IdleConfig {
183
+ /** Idle timeout in seconds */
184
+ timeout: number;
185
+ /**
186
+ * Callback when idle timeout is reached.
187
+ * NOTE: This function is only used if you manually initialize IdleManager.
188
+ * If using the Astro integration, use window.addEventListener('tk:idle', ...) instead.
189
+ */
190
+ onIdle?: () => void;
191
+ /** Whether to automatically logout on idle (default: true) */
192
+ autoLogout?: boolean;
193
+ /** Whether to monitor activity only on the active tab (default: true) */
194
+ activeTabOnly?: boolean;
195
+ /**
196
+ * Custom data to pass to the 'tk:idle' event.
197
+ * Ideal for configuring client-side alerts (e.g. SweetAlert).
198
+ */
199
+ alert?: AlertOptions | any;
200
+ }
201
+ /**
202
+ * Alert options for client-side notifications (e.g. SweetAlert)
203
+ */
204
+ interface AlertOptions {
205
+ title?: string;
206
+ text?: string;
207
+ icon?: 'success' | 'error' | 'warning' | 'info' | 'question' | string;
208
+ [key: string]: any;
209
+ }
175
210
  /**
176
211
  * Request interceptor
177
212
  */
@@ -208,7 +243,9 @@ interface ClientConfig {
208
243
  retry?: RetryConfig;
209
244
  /** Interceptors */
210
245
  interceptors?: InterceptorsConfig;
211
- /** AsyncLocalStorage instance */
246
+ /** Idle timeout configuration */
247
+ idle?: IdleConfig;
248
+ /** AsyncLocalStorage instance (Node only) */
212
249
  context?: AsyncLocalStorage<any>;
213
250
  /** Custom context store getter */
214
251
  getContextStore?: () => TokenKitContext | undefined | null;
@@ -216,6 +253,12 @@ interface ClientConfig {
216
253
  setContextStore?: (ctx: TokenKitContext) => void;
217
254
  /** Custom context runner */
218
255
  runWithContext?: <T>(ctx: TokenKitContext, fn: () => T) => T;
256
+ /** Custom fetch implementation */
257
+ fetch?: typeof fetch;
258
+ /** Enable debug logging */
259
+ debug?: boolean;
260
+ /** Dangerously ignore certificate errors (bypass SSL validation) */
261
+ dangerouslyIgnoreCertificateErrors?: boolean;
219
262
  }
220
263
  /**
221
264
  * TokenKit Global Configuration
@@ -234,25 +277,26 @@ declare class APIError extends Error {
234
277
  status?: number | undefined;
235
278
  response?: any | undefined;
236
279
  request?: RequestConfig | undefined;
237
- constructor(message: string, status?: number | undefined, response?: any | undefined, request?: RequestConfig | undefined);
280
+ cause?: any | undefined;
281
+ constructor(message: string, status?: number | undefined, response?: any | undefined, request?: RequestConfig | undefined, cause?: any | undefined);
238
282
  }
239
283
  /**
240
284
  * Authentication Error
241
285
  */
242
286
  declare class AuthError extends APIError {
243
- constructor(message: string, status?: number, response?: any, request?: RequestConfig);
287
+ constructor(message: string, status?: number, response?: any, request?: RequestConfig, cause?: any);
244
288
  }
245
289
  /**
246
290
  * Network Error
247
291
  */
248
292
  declare class NetworkError extends APIError {
249
- constructor(message: string, request?: RequestConfig);
293
+ constructor(message: string, request?: RequestConfig, cause?: any);
250
294
  }
251
295
  /**
252
296
  * Timeout Error
253
297
  */
254
298
  declare class TimeoutError extends APIError {
255
- constructor(message: string, request?: RequestConfig);
299
+ constructor(message: string, request?: RequestConfig, cause?: any);
256
300
  }
257
301
 
258
302
  /**
@@ -402,6 +446,13 @@ declare function createClient(config?: Partial<TokenKitConfig>): APIClient;
402
446
  * Astro integration for TokenKit
403
447
  *
404
448
  * This integration facilitates the setup of TokenKit in an Astro project.
449
+ * It performs the following:
450
+ * - Sets the global configuration for the API client.
451
+ * - Injects the configuration into the client-side via Vite's `define`.
452
+ * - Automatically registers the TokenKit middleware (unless `autoMiddleware` is set to `false`).
453
+ * - Injects a client-side script (`astro-tokenkit/client-init`) to handle idle session monitoring and automatic logout.
454
+ *
455
+ * @param config - TokenKit configuration options.
405
456
  *
406
457
  * @example
407
458
  * ```ts
@@ -415,6 +466,10 @@ declare function createClient(config?: Partial<TokenKitConfig>): APIClient;
415
466
  * auth: {
416
467
  * login: '/auth/login',
417
468
  * refresh: '/auth/refresh',
469
+ * },
470
+ * idle: {
471
+ * timeout: 3600, // 1 hour
472
+ * alert: { title: 'Session Expired' }
418
473
  * }
419
474
  * })
420
475
  * ]
@@ -423,7 +478,18 @@ declare function createClient(config?: Partial<TokenKitConfig>): APIClient;
423
478
  */
424
479
  declare function tokenKit(config: TokenKitConfig): AstroIntegration;
425
480
  /**
426
- * Helper to define middleware in a separate file if needed
481
+ * Helper to create the TokenKit middleware.
482
+ *
483
+ * Use this if you have `autoMiddleware: false` in your integration configuration
484
+ * and want to manually register the middleware in your `src/middleware.ts` file.
485
+ *
486
+ * @example
487
+ * ```ts
488
+ * // src/middleware.ts
489
+ * import { defineMiddleware } from 'astro-tokenkit';
490
+ *
491
+ * export const onRequest = defineMiddleware();
492
+ * ```
427
493
  */
428
494
  declare const defineMiddleware: () => astro.MiddlewareHandler;
429
495
 
package/dist/index.js CHANGED
@@ -37,20 +37,23 @@ typeof SuppressedError === "function" ? SuppressedError : function (error, suppr
37
37
  * API Error
38
38
  */
39
39
  class APIError extends Error {
40
- constructor(message, status, response, request) {
40
+ constructor(message, status, response, request, cause) {
41
41
  super(message);
42
42
  this.status = status;
43
43
  this.response = response;
44
44
  this.request = request;
45
+ this.cause = cause;
45
46
  this.name = 'APIError';
47
+ if (cause && !this.cause)
48
+ this.cause = cause;
46
49
  }
47
50
  }
48
51
  /**
49
52
  * Authentication Error
50
53
  */
51
54
  class AuthError extends APIError {
52
- constructor(message, status, response, request) {
53
- super(message, status, response, request);
55
+ constructor(message, status, response, request, cause) {
56
+ super(message, status, response, request, cause);
54
57
  this.name = 'AuthError';
55
58
  }
56
59
  }
@@ -58,8 +61,8 @@ class AuthError extends APIError {
58
61
  * Network Error
59
62
  */
60
63
  class NetworkError extends APIError {
61
- constructor(message, request) {
62
- super(message, undefined, undefined, request);
64
+ constructor(message, request, cause) {
65
+ super(message, undefined, undefined, request, cause);
63
66
  this.name = 'NetworkError';
64
67
  }
65
68
  }
@@ -67,8 +70,8 @@ class NetworkError extends APIError {
67
70
  * Timeout Error
68
71
  */
69
72
  class TimeoutError extends APIError {
70
- constructor(message, request) {
71
- super(message, undefined, undefined, request);
73
+ constructor(message, request, cause) {
74
+ super(message, undefined, undefined, request, cause);
72
75
  this.name = 'TimeoutError';
73
76
  }
74
77
  }
@@ -394,6 +397,122 @@ function isExpired(expiresAt, now, policy = {}) {
394
397
  return now + clockSkew > expiresAt;
395
398
  }
396
399
 
400
+ // packages/astro-tokenkit/src/utils/fetch.ts
401
+ /**
402
+ * Perform a fetch request with optional certificate validation bypass
403
+ */
404
+ function safeFetch(url, init, config) {
405
+ return __awaiter(this, void 0, void 0, function* () {
406
+ const fetchFn = config.fetch || fetch;
407
+ const fetchOptions = Object.assign({}, init);
408
+ if (config.dangerouslyIgnoreCertificateErrors && typeof process !== 'undefined') {
409
+ // In Node.js environment
410
+ try {
411
+ // Try to use undici Agent if available (it is built-in in Node 18+)
412
+ // However, we might need to import it if we want to create an Agent.
413
+ // Since we don't want to depend on undici in package.json, we use dynamic import.
414
+ // But wait, undici's Agent is what we need.
415
+ // As a fallback and most reliable way for self-signed certs in Node without extra deps:
416
+ process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0';
417
+ // NOTE: This affects the whole process. We should ideally only do this if it's not already 0.
418
+ // But for a dev tool / specialized library, it's often what's needed.
419
+ }
420
+ catch (e) {
421
+ // Ignore
422
+ }
423
+ }
424
+ return fetchFn(url, fetchOptions);
425
+ });
426
+ }
427
+
428
+ // packages/astro-tokenkit/src/config.ts
429
+ const CONFIG_KEY = Symbol.for('astro-tokenkit.config');
430
+ const MANAGER_KEY = Symbol.for('astro-tokenkit.manager');
431
+ const globalStorage = globalThis;
432
+ // Initialize global storage if not present
433
+ if (!globalStorage[CONFIG_KEY]) {
434
+ globalStorage[CONFIG_KEY] = {
435
+ runWithContext: undefined,
436
+ getContextStore: undefined,
437
+ setContextStore: undefined,
438
+ baseURL: "",
439
+ debug: false,
440
+ };
441
+ }
442
+ /**
443
+ * Set configuration
444
+ */
445
+ function setConfig(userConfig) {
446
+ var _a, _b;
447
+ const currentConfig = globalStorage[CONFIG_KEY];
448
+ const finalConfig = Object.assign(Object.assign({}, currentConfig), userConfig);
449
+ // Validate that getter and setter are defined together
450
+ if ((finalConfig.getContextStore && !finalConfig.setContextStore) ||
451
+ (!finalConfig.getContextStore && finalConfig.setContextStore)) {
452
+ throw new Error("[TokenKit] getContextStore and setContextStore must be defined together.");
453
+ }
454
+ globalStorage[CONFIG_KEY] = finalConfig;
455
+ // Re-initialize global token manager if auth changed
456
+ if (finalConfig.auth) {
457
+ const authConfig = Object.assign(Object.assign({}, finalConfig.auth), { fetch: (_a = finalConfig.auth.fetch) !== null && _a !== void 0 ? _a : finalConfig.fetch, dangerouslyIgnoreCertificateErrors: (_b = finalConfig.auth.dangerouslyIgnoreCertificateErrors) !== null && _b !== void 0 ? _b : finalConfig.dangerouslyIgnoreCertificateErrors });
458
+ globalStorage[MANAGER_KEY] = new TokenManager(authConfig, finalConfig.baseURL);
459
+ }
460
+ else {
461
+ globalStorage[MANAGER_KEY] = undefined;
462
+ }
463
+ }
464
+ /**
465
+ * Get current configuration
466
+ */
467
+ function getConfig() {
468
+ return globalStorage[CONFIG_KEY];
469
+ }
470
+ /**
471
+ * Get global token manager
472
+ */
473
+ function getTokenManager() {
474
+ return globalStorage[MANAGER_KEY];
475
+ }
476
+ /**
477
+ * Set global token manager (mainly for testing)
478
+ */
479
+ function setTokenManager(manager) {
480
+ globalStorage[MANAGER_KEY] = manager;
481
+ }
482
+ // Handle injected configuration from Astro integration
483
+ try {
484
+ // @ts-ignore
485
+ const injectedConfig = typeof __TOKENKIT_CONFIG__ !== 'undefined' ? __TOKENKIT_CONFIG__ : undefined;
486
+ if (injectedConfig) {
487
+ setConfig(injectedConfig);
488
+ }
489
+ }
490
+ catch (e) {
491
+ // Ignore errors in environments where __TOKENKIT_CONFIG__ might be restricted
492
+ }
493
+
494
+ /**
495
+ * Logger utility that respects the debug flag in the configuration
496
+ */
497
+ const logger = {
498
+ debug: (message, ...args) => {
499
+ if (getConfig().debug) {
500
+ console.debug(message, ...args);
501
+ }
502
+ },
503
+ info: (message, ...args) => {
504
+ if (getConfig().debug) {
505
+ console.log(message, ...args);
506
+ }
507
+ },
508
+ warn: (message, ...args) => {
509
+ console.warn(message, ...args);
510
+ },
511
+ error: (message, ...args) => {
512
+ console.error(message, ...args);
513
+ }
514
+ };
515
+
397
516
  // packages/astro-tokenkit/src/auth/manager.ts
398
517
  /**
399
518
  * Single-flight refresh manager
@@ -450,14 +569,14 @@ class TokenManager {
450
569
  }
451
570
  let response;
452
571
  try {
453
- response = yield fetch(url, {
572
+ response = yield safeFetch(url, {
454
573
  method: 'POST',
455
574
  headers,
456
575
  body: requestBody,
457
- });
576
+ }, this.config);
458
577
  }
459
578
  catch (error) {
460
- const authError = new AuthError(`Login request failed: ${error.message}`);
579
+ const authError = new AuthError(`Login request failed: ${error.message}`, undefined, undefined, undefined, error);
461
580
  if (options === null || options === void 0 ? void 0 : options.onError)
462
581
  yield options.onError(authError, ctx);
463
582
  throw authError;
@@ -531,14 +650,14 @@ class TokenManager {
531
650
  }
532
651
  let response;
533
652
  try {
534
- response = yield fetch(url, {
653
+ response = yield safeFetch(url, {
535
654
  method: 'POST',
536
655
  headers,
537
656
  body: requestBody,
538
- });
657
+ }, this.config);
539
658
  }
540
659
  catch (error) {
541
- throw new AuthError(`Refresh request failed: ${error.message}`);
660
+ throw new AuthError(`Refresh request failed: ${error.message}`, undefined, undefined, undefined, error);
542
661
  }
543
662
  if (!response.ok) {
544
663
  // 401/403 = invalid refresh token
@@ -636,11 +755,11 @@ class TokenManager {
636
755
  const injectFn = (_a = this.config.injectToken) !== null && _a !== void 0 ? _a : ((token, type) => `${type !== null && type !== void 0 ? type : 'Bearer'} ${token}`);
637
756
  headers['Authorization'] = injectFn(session.accessToken, session.tokenType);
638
757
  }
639
- yield fetch(url, { method: 'POST', headers });
758
+ yield safeFetch(url, { method: 'POST', headers }, this.config);
640
759
  }
641
760
  catch (error) {
642
761
  // Ignore logout endpoint errors
643
- console.warn('[TokenKit] Logout endpoint failed:', error);
762
+ logger.debug('[TokenKit] Logout endpoint failed:', error);
644
763
  }
645
764
  }
646
765
  clearTokens(ctx, this.config.cookies);
@@ -686,69 +805,6 @@ class TokenManager {
686
805
  }
687
806
  }
688
807
 
689
- // packages/astro-tokenkit/src/config.ts
690
- const CONFIG_KEY = Symbol.for('astro-tokenkit.config');
691
- const MANAGER_KEY = Symbol.for('astro-tokenkit.manager');
692
- const globalStorage = globalThis;
693
- // Initialize global storage if not present
694
- if (!globalStorage[CONFIG_KEY]) {
695
- globalStorage[CONFIG_KEY] = {
696
- runWithContext: undefined,
697
- getContextStore: undefined,
698
- setContextStore: undefined,
699
- baseURL: "",
700
- };
701
- }
702
- /**
703
- * Set configuration
704
- */
705
- function setConfig(userConfig) {
706
- const currentConfig = globalStorage[CONFIG_KEY];
707
- const finalConfig = Object.assign(Object.assign({}, currentConfig), userConfig);
708
- // Validate that getter and setter are defined together
709
- if ((finalConfig.getContextStore && !finalConfig.setContextStore) ||
710
- (!finalConfig.getContextStore && finalConfig.setContextStore)) {
711
- throw new Error("[TokenKit] getContextStore and setContextStore must be defined together.");
712
- }
713
- globalStorage[CONFIG_KEY] = finalConfig;
714
- // Re-initialize global token manager if auth changed
715
- if (finalConfig.auth) {
716
- globalStorage[MANAGER_KEY] = new TokenManager(finalConfig.auth, finalConfig.baseURL);
717
- }
718
- else {
719
- globalStorage[MANAGER_KEY] = undefined;
720
- }
721
- }
722
- /**
723
- * Get current configuration
724
- */
725
- function getConfig() {
726
- return globalStorage[CONFIG_KEY];
727
- }
728
- /**
729
- * Get global token manager
730
- */
731
- function getTokenManager() {
732
- return globalStorage[MANAGER_KEY];
733
- }
734
- /**
735
- * Set global token manager (mainly for testing)
736
- */
737
- function setTokenManager(manager) {
738
- globalStorage[MANAGER_KEY] = manager;
739
- }
740
- // Handle injected configuration from Astro integration
741
- try {
742
- // @ts-ignore
743
- const injectedConfig = typeof __TOKENKIT_CONFIG__ !== 'undefined' ? __TOKENKIT_CONFIG__ : undefined;
744
- if (injectedConfig) {
745
- setConfig(injectedConfig);
746
- }
747
- }
748
- catch (e) {
749
- // Ignore errors in environments where __TOKENKIT_CONFIG__ might be restricted
750
- }
751
-
752
808
  // packages/astro-tokenkit/src/client/context.ts
753
809
  /**
754
810
  * Async local storage for Astro context
@@ -845,7 +901,7 @@ function createMiddleware() {
845
901
  else if (config.context) {
846
902
  contextStrategy = 'custom (external AsyncLocalStorage)';
847
903
  }
848
- console.log(`[TokenKit] Middleware initialized (auth: ${authStatus}, context: ${contextStrategy})`);
904
+ logger.debug(`[TokenKit] Middleware initialized (auth: ${authStatus}, context: ${contextStrategy})`);
849
905
  globalStorage[LOGGED_KEY] = true;
850
906
  }
851
907
  const runLogic = () => __awaiter(this, void 0, void 0, function* () {
@@ -857,7 +913,7 @@ function createMiddleware() {
857
913
  }
858
914
  catch (error) {
859
915
  // Log only the message to avoid leaking sensitive data in the error object
860
- console.error('[TokenKit] Automatic token rotation failed:', error.message || error);
916
+ logger.debug('[TokenKit] Automatic token rotation failed:', error.message || error);
861
917
  }
862
918
  }
863
919
  return next();
@@ -902,6 +958,7 @@ class APIClient {
902
958
  * Get token manager
903
959
  */
904
960
  get tokenManager() {
961
+ var _a, _b;
905
962
  const config = this.config;
906
963
  if (!config.auth)
907
964
  return undefined;
@@ -917,7 +974,9 @@ class APIClient {
917
974
  if (!this._localTokenManager ||
918
975
  this._lastUsedAuth !== config.auth ||
919
976
  this._lastUsedBaseURL !== config.baseURL) {
920
- this._localTokenManager = new TokenManager(config.auth, config.baseURL);
977
+ // Merge client-level fetch and SSL settings into auth config
978
+ const authConfig = Object.assign(Object.assign({}, config.auth), { fetch: (_a = config.auth.fetch) !== null && _a !== void 0 ? _a : config.fetch, dangerouslyIgnoreCertificateErrors: (_b = config.auth.dangerouslyIgnoreCertificateErrors) !== null && _b !== void 0 ? _b : config.dangerouslyIgnoreCertificateErrors });
979
+ this._localTokenManager = new TokenManager(authConfig, config.baseURL);
921
980
  this._lastUsedAuth = config.auth;
922
981
  this._lastUsedBaseURL = config.baseURL;
923
982
  }
@@ -1034,7 +1093,7 @@ class APIClient {
1034
1093
  const controller = new AbortController();
1035
1094
  const timeoutId = setTimeout(() => controller.abort(), timeout);
1036
1095
  try {
1037
- const response = yield fetch(fullURL, Object.assign(Object.assign({}, init), { signal: controller.signal }));
1096
+ const response = yield safeFetch(fullURL, Object.assign(Object.assign({}, init), { signal: controller.signal }), this.config);
1038
1097
  clearTimeout(timeoutId);
1039
1098
  // Handle 401 (try refresh and retry once)
1040
1099
  if (response.status === 401 && this.tokenManager && !config.skipAuth && attempt === 1) {
@@ -1067,12 +1126,12 @@ class APIClient {
1067
1126
  }
1068
1127
  // Transform errors
1069
1128
  if (error instanceof Error && error.name === 'AbortError') {
1070
- throw new TimeoutError(`Request timeout after ${timeout}ms`, requestConfig);
1129
+ throw new TimeoutError(`Request timeout after ${timeout}ms`, requestConfig, error);
1071
1130
  }
1072
1131
  if (error instanceof APIError) {
1073
1132
  throw error;
1074
1133
  }
1075
- throw new NetworkError(error.message, requestConfig);
1134
+ throw new NetworkError(error.message, requestConfig, error);
1076
1135
  }
1077
1136
  });
1078
1137
  }
@@ -1166,7 +1225,7 @@ class APIClient {
1166
1225
  throw new Error('Auth is not configured for this client');
1167
1226
  }
1168
1227
  const context = getContextStore();
1169
- return this.tokenManager.login(context, credentials, options);
1228
+ return yield this.tokenManager.login(context, credentials, options);
1170
1229
  });
1171
1230
  }
1172
1231
  /**
@@ -1223,6 +1282,13 @@ function createClient(config) {
1223
1282
  * Astro integration for TokenKit
1224
1283
  *
1225
1284
  * This integration facilitates the setup of TokenKit in an Astro project.
1285
+ * It performs the following:
1286
+ * - Sets the global configuration for the API client.
1287
+ * - Injects the configuration into the client-side via Vite's `define`.
1288
+ * - Automatically registers the TokenKit middleware (unless `autoMiddleware` is set to `false`).
1289
+ * - Injects a client-side script (`astro-tokenkit/client-init`) to handle idle session monitoring and automatic logout.
1290
+ *
1291
+ * @param config - TokenKit configuration options.
1226
1292
  *
1227
1293
  * @example
1228
1294
  * ```ts
@@ -1236,6 +1302,10 @@ function createClient(config) {
1236
1302
  * auth: {
1237
1303
  * login: '/auth/login',
1238
1304
  * refresh: '/auth/refresh',
1305
+ * },
1306
+ * idle: {
1307
+ * timeout: 3600, // 1 hour
1308
+ * alert: { title: 'Session Expired' }
1239
1309
  * }
1240
1310
  * })
1241
1311
  * ]
@@ -1253,7 +1323,7 @@ function tokenKit(config) {
1253
1323
  return {
1254
1324
  name: 'astro-tokenkit',
1255
1325
  hooks: {
1256
- 'astro:config:setup': ({ updateConfig, addMiddleware }) => {
1326
+ 'astro:config:setup': ({ updateConfig, addMiddleware, injectScript }) => {
1257
1327
  updateConfig({
1258
1328
  vite: {
1259
1329
  define: {
@@ -1268,13 +1338,26 @@ function tokenKit(config) {
1268
1338
  order: 'pre'
1269
1339
  });
1270
1340
  }
1271
- console.log('[TokenKit] Integration initialized');
1341
+ // Always inject the client-side script for idle monitoring
1342
+ injectScript('page', `import 'astro-tokenkit/client-init';`);
1343
+ logger.debug('[TokenKit] Integration initialized');
1272
1344
  },
1273
1345
  },
1274
1346
  };
1275
1347
  }
1276
1348
  /**
1277
- * Helper to define middleware in a separate file if needed
1349
+ * Helper to create the TokenKit middleware.
1350
+ *
1351
+ * Use this if you have `autoMiddleware: false` in your integration configuration
1352
+ * and want to manually register the middleware in your `src/middleware.ts` file.
1353
+ *
1354
+ * @example
1355
+ * ```ts
1356
+ * // src/middleware.ts
1357
+ * import { defineMiddleware } from 'astro-tokenkit';
1358
+ *
1359
+ * export const onRequest = defineMiddleware();
1360
+ * ```
1278
1361
  */
1279
1362
  const defineMiddleware = () => createMiddleware();
1280
1363