@ahoo-wang/fetcher-cosec 1.5.2 → 1.5.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.
@@ -23,11 +23,13 @@ export declare class CoSecResponseInterceptor implements ResponseInterceptor {
23
23
  readonly name = "CoSecResponseInterceptor";
24
24
  readonly order: number;
25
25
  private options;
26
+ private refreshInProgress?;
26
27
  /**
27
28
  * Creates a new CoSecResponseInterceptor instance.
28
29
  * @param options - The CoSec configuration options including token storage and refresher
29
30
  */
30
31
  constructor(options: CoSecOptions);
32
+ private refresh;
31
33
  /**
32
34
  * Intercepts the response and handles unauthorized responses by refreshing tokens.
33
35
  * @param exchange - The fetch exchange containing request and response information
@@ -1 +1 @@
1
- {"version":3,"file":"cosecResponseInterceptor.d.ts","sourceRoot":"","sources":["../src/cosecResponseInterceptor.ts"],"names":[],"mappings":"AAaA,OAAO,EAAE,KAAK,YAAY,EAAiB,MAAM,SAAS,CAAC;AAC3D,OAAO,EAAE,aAAa,EAAE,KAAK,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAE7E;;GAEG;AACH,eAAO,MAAM,+BAA+B,6BAA6B,CAAC;AAE1E;;;GAGG;AACH,eAAO,MAAM,gCAAgC,QAAiC,CAAC;AAE/E;;;;;;;;;GASG;AACH,qBAAa,wBAAyB,YAAW,mBAAmB;IAClE,QAAQ,CAAC,IAAI,8BAAmC;IAChD,QAAQ,CAAC,KAAK,SAAoC;IAClD,OAAO,CAAC,OAAO,CAAe;IAE9B;;;OAGG;gBACS,OAAO,EAAE,YAAY;IAIjC;;;OAGG;IACG,SAAS,CAAC,QAAQ,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC;CAgCxD"}
1
+ {"version":3,"file":"cosecResponseInterceptor.d.ts","sourceRoot":"","sources":["../src/cosecResponseInterceptor.ts"],"names":[],"mappings":"AAaA,OAAO,EAAE,KAAK,YAAY,EAAiB,MAAM,SAAS,CAAC;AAC3D,OAAO,EAAE,aAAa,EAAE,KAAK,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAG7E;;GAEG;AACH,eAAO,MAAM,+BAA+B,6BAA6B,CAAC;AAE1E;;;GAGG;AACH,eAAO,MAAM,gCAAgC,QAAiC,CAAC;AAE/E;;;;;;;;;GASG;AACH,qBAAa,wBAAyB,YAAW,mBAAmB;IAClE,QAAQ,CAAC,IAAI,8BAAmC;IAChD,QAAQ,CAAC,KAAK,SAAoC;IAClD,OAAO,CAAC,OAAO,CAAe;IAC9B,OAAO,CAAC,iBAAiB,CAAC,CAA0B;IAEpD;;;OAGG;gBACS,OAAO,EAAE,YAAY;YAInB,OAAO;IAqBrB;;;OAGG;IACG,SAAS,CAAC,QAAQ,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC;CA8BxD"}
package/dist/index.es.js CHANGED
@@ -1,13 +1,13 @@
1
- import { REQUEST_BODY_INTERCEPTOR_ORDER as u } from "@ahoo-wang/fetcher";
1
+ import { REQUEST_BODY_INTERCEPTOR_ORDER as E } from "@ahoo-wang/fetcher";
2
2
  import { nanoid as d } from "nanoid";
3
3
  const a = class a {
4
4
  };
5
5
  a.DEVICE_ID = "CoSec-Device-Id", a.APP_ID = "CoSec-App-Id", a.AUTHORIZATION = "Authorization", a.REQUEST_ID = "CoSec-Request-Id";
6
6
  let i = a;
7
- const I = class I {
7
+ const h = class h {
8
8
  };
9
- I.UNAUTHORIZED = 401;
10
- let c = I;
9
+ h.UNAUTHORIZED = 401;
10
+ let c = h;
11
11
  const N = {
12
12
  ALLOW: { authorized: !0, reason: "Allow" },
13
13
  EXPLICIT_DENY: { authorized: !1, reason: "Explicit Deny" },
@@ -15,7 +15,7 @@ const N = {
15
15
  TOKEN_EXPIRED: { authorized: !1, reason: "Token Expired" },
16
16
  TOO_MANY_REQUESTS: { authorized: !1, reason: "Too Many Requests" }
17
17
  };
18
- class p {
18
+ class g {
19
19
  /**
20
20
  * Generate a unique request ID.
21
21
  *
@@ -25,10 +25,10 @@ class p {
25
25
  return d();
26
26
  }
27
27
  }
28
- const h = new p(), g = "CoSecRequestInterceptor", T = u + 1e3;
28
+ const I = new g(), p = "CoSecRequestInterceptor", f = E + 1e3;
29
29
  class m {
30
30
  constructor(e) {
31
- this.name = g, this.order = T, this.options = e;
31
+ this.name = p, this.order = f, this.options = e;
32
32
  }
33
33
  /**
34
34
  * Intercept requests to add CoSec authentication headers.
@@ -51,7 +51,7 @@ class m {
51
51
  * subsequent request processing interceptors.
52
52
  */
53
53
  intercept(e) {
54
- const t = h.generateId(), s = this.options.deviceIdStorage.getOrCreate(), o = this.options.tokenStorage.get(), n = e.ensureRequestHeaders();
54
+ const t = I.generateId(), s = this.options.deviceIdStorage.getOrCreate(), o = this.options.tokenStorage.get(), n = e.ensureRequestHeaders();
55
55
  n[i.APP_ID] = this.options.appId, n[i.DEVICE_ID] = s, n[i.REQUEST_ID] = t, o && !n[i.AUTHORIZATION] && (n[i.AUTHORIZATION] = `Bearer ${o.accessToken}`);
56
56
  }
57
57
  }
@@ -64,6 +64,13 @@ class A {
64
64
  constructor(e) {
65
65
  this.name = R, this.order = S, this.options = e;
66
66
  }
67
+ async refresh(e) {
68
+ return this.refreshInProgress ? this.refreshInProgress : (this.refreshInProgress = this.options.tokenRefresher.refresh(e).then((t) => (this.options.tokenStorage.set(t), t)).catch((t) => {
69
+ throw this.options.tokenStorage.clear(), t;
70
+ }).finally(() => {
71
+ this.refreshInProgress = void 0;
72
+ }), this.refreshInProgress);
73
+ }
67
74
  /**
68
75
  * Intercepts the response and handles unauthorized responses by refreshing tokens.
69
76
  * @param exchange - The fetch exchange containing request and response information
@@ -75,14 +82,13 @@ class A {
75
82
  const s = this.options.tokenStorage.get();
76
83
  if (s)
77
84
  try {
78
- const o = await this.options.tokenRefresher.refresh(s);
79
- this.options.tokenStorage.set(o), await e.fetcher.request(e.request);
85
+ await this.refresh(s), await e.fetcher.request(e.request);
80
86
  } catch (o) {
81
87
  throw this.options.tokenStorage.clear(), o;
82
88
  }
83
89
  }
84
90
  }
85
- class O {
91
+ class T {
86
92
  constructor() {
87
93
  this.store = /* @__PURE__ */ new Map();
88
94
  }
@@ -106,12 +112,12 @@ class O {
106
112
  this.store.set(e, t);
107
113
  }
108
114
  }
109
- function E() {
110
- return typeof window < "u" && window.localStorage ? window.localStorage : new O();
115
+ function l() {
116
+ return typeof window < "u" && window.localStorage ? window.localStorage : new T();
111
117
  }
112
- const f = "cosec-device-id";
118
+ const O = "cosec-device-id";
113
119
  class k {
114
- constructor(e = f, t = E()) {
120
+ constructor(e = O, t = l()) {
115
121
  this.deviceIdKey = e, this.storage = t;
116
122
  }
117
123
  /**
@@ -136,7 +142,7 @@ class k {
136
142
  * @returns A newly generated device ID
137
143
  */
138
144
  generateDeviceId() {
139
- return h.generateId();
145
+ return I.generateId();
140
146
  }
141
147
  /**
142
148
  * Get or create a device ID.
@@ -163,8 +169,8 @@ function _(r) {
163
169
  s.length + (4 - s.length % 4) % 4,
164
170
  "="
165
171
  ), n = decodeURIComponent(
166
- atob(o).split("").map(function(l) {
167
- return "%" + ("00" + l.charCodeAt(0).toString(16)).slice(-2);
172
+ atob(o).split("").map(function(u) {
173
+ return "%" + ("00" + u.charCodeAt(0).toString(16)).slice(-2);
168
174
  }).join("")
169
175
  );
170
176
  return JSON.parse(n);
@@ -172,16 +178,16 @@ function _(r) {
172
178
  return console.error("Failed to parse JWT token", e), null;
173
179
  }
174
180
  }
175
- function w(r, e = 0) {
181
+ function P(r, e = 0) {
176
182
  const t = typeof r == "string" ? _(r) : r;
177
183
  if (!t)
178
184
  return !0;
179
185
  const s = t.exp;
180
186
  return s ? Date.now() / 1e3 > s - e : !1;
181
187
  }
182
- const D = "cosec-token";
183
- class U {
184
- constructor(e = D, t = E()) {
188
+ const y = "cosec-token";
189
+ class v {
190
+ constructor(e = y, t = l()) {
185
191
  this.tokenKey = e, this.storage = t;
186
192
  }
187
193
  /**
@@ -217,23 +223,23 @@ class U {
217
223
  }
218
224
  export {
219
225
  N as AuthorizeResults,
220
- g as COSEC_REQUEST_INTERCEPTOR_NAME,
221
- T as COSEC_REQUEST_INTERCEPTOR_ORDER,
226
+ p as COSEC_REQUEST_INTERCEPTOR_NAME,
227
+ f as COSEC_REQUEST_INTERCEPTOR_ORDER,
222
228
  R as COSEC_RESPONSE_INTERCEPTOR_NAME,
223
229
  S as COSEC_RESPONSE_INTERCEPTOR_ORDER,
224
230
  i as CoSecHeaders,
225
231
  m as CoSecRequestInterceptor,
226
232
  A as CoSecResponseInterceptor,
227
- f as DEFAULT_COSEC_DEVICE_ID_KEY,
228
- D as DEFAULT_COSEC_TOKEN_KEY,
233
+ O as DEFAULT_COSEC_DEVICE_ID_KEY,
234
+ y as DEFAULT_COSEC_TOKEN_KEY,
229
235
  k as DeviceIdStorage,
230
- O as InMemoryStorage,
231
- p as NanoIdGenerator,
236
+ T as InMemoryStorage,
237
+ g as NanoIdGenerator,
232
238
  c as ResponseCodes,
233
- U as TokenStorage,
234
- E as getStorage,
235
- h as idGenerator,
236
- w as isTokenExpired,
239
+ v as TokenStorage,
240
+ l as getStorage,
241
+ I as idGenerator,
242
+ P as isTokenExpired,
237
243
  _ as parseJwtPayload
238
244
  };
239
245
  //# sourceMappingURL=index.es.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.es.js","sources":["../src/types.ts","../src/idGenerator.ts","../src/cosecRequestInterceptor.ts","../src/cosecResponseInterceptor.ts","../src/inMemoryStorage.ts","../src/deviceIdStorage.ts","../src/jwts.ts","../src/tokenStorage.ts"],"sourcesContent":["/*\n * Copyright [2021-present] [ahoo wang <ahoowang@qq.com> (https://github.com/Ahoo-Wang)].\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * http://www.apache.org/licenses/LICENSE-2.0\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { DeviceIdStorage } from './deviceIdStorage';\nimport { TokenStorage } from './tokenStorage';\nimport { TokenRefresher } from './tokenRefresher';\n\n/**\n * CoSec HTTP headers enumeration.\n */\nexport class CoSecHeaders {\n static readonly DEVICE_ID = 'CoSec-Device-Id';\n static readonly APP_ID = 'CoSec-App-Id';\n static readonly AUTHORIZATION = 'Authorization';\n static readonly REQUEST_ID = 'CoSec-Request-Id';\n}\n\nexport class ResponseCodes {\n static readonly UNAUTHORIZED = 401;\n}\n\n/**\n * CoSec options interface.\n */\nexport interface CoSecOptions {\n /**\n * Application ID to be sent in the CoSec-App-Id header.\n */\n appId: string;\n\n /**\n * Device ID storage instance.\n */\n deviceIdStorage: DeviceIdStorage;\n\n /**\n * Token storage instance.\n */\n tokenStorage: TokenStorage;\n\n /**\n * Token refresher function.\n *\n * Takes a CompositeToken and returns a Promise that resolves to a new CompositeToken.\n */\n tokenRefresher: TokenRefresher;\n}\n\n/**\n * Authorization result interface.\n */\nexport interface AuthorizeResult {\n authorized: boolean;\n reason: string;\n}\n\n/**\n * Authorization result constants.\n */\nexport const AuthorizeResults = {\n ALLOW: { authorized: true, reason: 'Allow' },\n EXPLICIT_DENY: { authorized: false, reason: 'Explicit Deny' },\n IMPLICIT_DENY: { authorized: false, reason: 'Implicit Deny' },\n TOKEN_EXPIRED: { authorized: false, reason: 'Token Expired' },\n TOO_MANY_REQUESTS: { authorized: false, reason: 'Too Many Requests' },\n};\n","/*\n * Copyright [2021-present] [ahoo wang <ahoowang@qq.com> (https://github.com/Ahoo-Wang)].\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * http://www.apache.org/licenses/LICENSE-2.0\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { nanoid } from 'nanoid';\n\nexport interface IdGenerator {\n generateId(): string;\n}\n\n/**\n * Nano ID implementation of IdGenerator.\n * Generates unique request IDs using Nano ID.\n */\nexport class NanoIdGenerator implements IdGenerator {\n /**\n * Generate a unique request ID.\n *\n * @returns A unique request ID\n */\n generateId(): string {\n return nanoid();\n }\n}\n\nexport const idGenerator = new NanoIdGenerator();\n","/*\n * Copyright [2021-present] [ahoo wang <ahoowang@qq.com> (https://github.com/Ahoo-Wang)].\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * http://www.apache.org/licenses/LICENSE-2.0\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n FetchExchange,\n REQUEST_BODY_INTERCEPTOR_ORDER,\n type RequestInterceptor,\n} from '@ahoo-wang/fetcher';\nimport { CoSecHeaders, CoSecOptions } from './types';\nimport { idGenerator } from './idGenerator';\n\n/**\n * The name of the CoSecRequestInterceptor.\n */\nexport const COSEC_REQUEST_INTERCEPTOR_NAME = 'CoSecRequestInterceptor';\n\n/**\n * The order of the CoSecRequestInterceptor.\n * Set to REQUEST_BODY_INTERCEPTOR_ORDER + 1000 to ensure it runs after RequestBodyInterceptor.\n */\nexport const COSEC_REQUEST_INTERCEPTOR_ORDER =\n REQUEST_BODY_INTERCEPTOR_ORDER + 1000;\n\n/**\n * Interceptor that automatically adds CoSec authentication headers to requests.\n *\n * This interceptor adds the following headers to each request:\n * - CoSec-Device-Id: Device identifier (stored in localStorage or generated)\n * - CoSec-App-Id: Application identifier\n * - Authorization: Bearer token\n * - CoSec-Request-Id: Unique request identifier for each request\n *\n * @remarks\n * This interceptor runs after RequestBodyInterceptor but before FetchInterceptor.\n * The order is set to COSEC_REQUEST_INTERCEPTOR_ORDER to ensure it runs after\n * request body processing but before the actual HTTP request is made. This positioning\n * allows for proper authentication header addition after all request body transformations\n * are complete, ensuring that the final request is properly authenticated before\n * being sent over the network.\n */\nexport class CoSecRequestInterceptor implements RequestInterceptor {\n readonly name = COSEC_REQUEST_INTERCEPTOR_NAME;\n readonly order = COSEC_REQUEST_INTERCEPTOR_ORDER;\n private options: CoSecOptions;\n\n constructor(options: CoSecOptions) {\n this.options = options;\n }\n\n /**\n * Intercept requests to add CoSec authentication headers.\n *\n * This method adds the following headers to each request:\n * - CoSec-App-Id: The application identifier from the CoSec options\n * - CoSec-Device-Id: A unique device identifier, either retrieved from storage or generated\n * - CoSec-Request-Id: A unique identifier for this specific request\n * - Authorization: Bearer token if available in token storage\n *\n * @param exchange - The fetch exchange containing the request to process\n *\n * @remarks\n * This method runs after RequestBodyInterceptor but before FetchInterceptor.\n * It ensures that authentication headers are added to the request after all\n * body processing is complete. The positioning allows for proper authentication\n * header addition after all request body transformations are finished, ensuring\n * that the final request is properly authenticated before being sent over the network.\n * This execution order prevents authentication headers from being overwritten by\n * subsequent request processing interceptors.\n */\n intercept(exchange: FetchExchange) {\n const requestId = idGenerator.generateId();\n const deviceId = this.options.deviceIdStorage.getOrCreate();\n const token = this.options.tokenStorage.get();\n const requestHeaders = exchange.ensureRequestHeaders();\n requestHeaders[CoSecHeaders.APP_ID] = this.options.appId;\n requestHeaders[CoSecHeaders.DEVICE_ID] = deviceId;\n requestHeaders[CoSecHeaders.REQUEST_ID] = requestId;\n if (token && !requestHeaders[CoSecHeaders.AUTHORIZATION]) {\n requestHeaders[CoSecHeaders.AUTHORIZATION] =\n `Bearer ${token.accessToken}`;\n }\n }\n}\n","/*\n * Copyright [2021-present] [ahoo wang <ahoowang@qq.com> (https://github.com/Ahoo-Wang)].\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * http://www.apache.org/licenses/LICENSE-2.0\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { type CoSecOptions, ResponseCodes } from './types';\nimport { FetchExchange, type ResponseInterceptor } from '@ahoo-wang/fetcher';\n\n/**\n * The name of the CoSecResponseInterceptor.\n */\nexport const COSEC_RESPONSE_INTERCEPTOR_NAME = 'CoSecResponseInterceptor';\n\n/**\n * The order of the CoSecResponseInterceptor.\n * Set to a high negative value to ensure it runs early in the interceptor chain.\n */\nexport const COSEC_RESPONSE_INTERCEPTOR_ORDER = Number.MIN_SAFE_INTEGER + 1000;\n\n/**\n * CoSecResponseInterceptor is responsible for handling unauthorized responses (401)\n * by attempting to refresh the authentication token and retrying the original request.\n *\n * This interceptor:\n * 1. Checks if the response status is 401 (UNAUTHORIZED)\n * 2. If so, and if there's a current token, attempts to refresh it\n * 3. On successful refresh, stores the new token and retries the original request\n * 4. On refresh failure, clears stored tokens and propagates the error\n */\nexport class CoSecResponseInterceptor implements ResponseInterceptor {\n readonly name = COSEC_RESPONSE_INTERCEPTOR_NAME;\n readonly order = COSEC_RESPONSE_INTERCEPTOR_ORDER;\n private options: CoSecOptions;\n\n /**\n * Creates a new CoSecResponseInterceptor instance.\n * @param options - The CoSec configuration options including token storage and refresher\n */\n constructor(options: CoSecOptions) {\n this.options = options;\n }\n\n /**\n * Intercepts the response and handles unauthorized responses by refreshing tokens.\n * @param exchange - The fetch exchange containing request and response information\n */\n async intercept(exchange: FetchExchange): Promise<void> {\n const response = exchange.response;\n // If there's no response, nothing to intercept\n if (!response) {\n return;\n }\n\n // Only handle unauthorized responses (401)\n if (response.status !== ResponseCodes.UNAUTHORIZED) {\n return;\n }\n\n // Get the current token from storage\n const currentToken = this.options.tokenStorage.get();\n // If there's no current token, we can't refresh it\n if (!currentToken) {\n return;\n }\n\n try {\n // Attempt to refresh the token\n const newToken = await this.options.tokenRefresher.refresh(currentToken);\n // Store the refreshed token\n this.options.tokenStorage.set(newToken);\n // Retry the original request with the new token\n await exchange.fetcher.request(exchange.request);\n } catch (error) {\n // If token refresh fails, clear stored tokens and re-throw the error\n this.options.tokenStorage.clear();\n throw error;\n }\n }\n}\n","/*\n * Copyright [2021-present] [ahoo wang <ahoowang@qq.com> (https://github.com/Ahoo-Wang)].\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * http://www.apache.org/licenses/LICENSE-2.0\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * In-memory storage fallback for environments without localStorage.\n */\nexport class InMemoryStorage implements Storage {\n private store: Map<string, string> = new Map();\n\n get length(): number {\n return this.store.size;\n }\n\n clear(): void {\n this.store.clear();\n }\n\n getItem(key: string): string | null {\n const value = this.store.get(key);\n return value !== undefined ? value : null;\n }\n\n key(index: number): string | null {\n const keys = Array.from(this.store.keys());\n return keys[index] || null;\n }\n\n removeItem(key: string): void {\n if (this.store.has(key)) {\n this.store.delete(key);\n }\n }\n\n setItem(key: string, value: string): void {\n this.store.set(key, value);\n }\n}\n\nexport function getStorage(): Storage {\n if (typeof window !== 'undefined' && window.localStorage) {\n return window.localStorage;\n } else {\n // Use in-memory storage as fallback\n return new InMemoryStorage();\n }\n}\n","/*\n * Copyright [2021-present] [ahoo wang <ahoowang@qq.com> (https://github.com/Ahoo-Wang)].\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * http://www.apache.org/licenses/LICENSE-2.0\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { getStorage } from './inMemoryStorage';\nimport { idGenerator } from './idGenerator';\n\nexport const DEFAULT_COSEC_DEVICE_ID_KEY = 'cosec-device-id';\n\n/**\n * Storage class for managing device identifiers.\n */\nexport class DeviceIdStorage {\n private readonly deviceIdKey: string;\n private storage: Storage;\n\n constructor(\n deviceIdKey: string = DEFAULT_COSEC_DEVICE_ID_KEY,\n storage: Storage = getStorage(),\n ) {\n this.deviceIdKey = deviceIdKey;\n this.storage = storage;\n }\n\n /**\n * Get the current device ID.\n *\n * @returns The current device ID or null if not set\n */\n get(): string | null {\n return this.storage.getItem(this.deviceIdKey);\n }\n\n /**\n * Set a device ID.\n *\n * @param deviceId - The device ID to set\n */\n set(deviceId: string): void {\n this.storage.setItem(this.deviceIdKey, deviceId);\n }\n\n /**\n * Generate a new device ID.\n *\n * @returns A newly generated device ID\n */\n generateDeviceId(): string {\n return idGenerator.generateId();\n }\n\n /**\n * Get or create a device ID.\n *\n * @returns The existing device ID if available, otherwise a newly generated one\n */\n getOrCreate(): string {\n // Try to get existing device ID from storage\n let deviceId = this.get();\n if (!deviceId) {\n // Generate a new device ID and store it\n deviceId = this.generateDeviceId();\n this.set(deviceId);\n }\n\n return deviceId;\n }\n\n /**\n * Clear the stored device ID.\n */\n clear(): void {\n this.storage.removeItem(this.deviceIdKey);\n }\n}\n","/*\n * Copyright [2021-present] [ahoo wang <ahoowang@qq.com> (https://github.com/Ahoo-Wang)].\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * http://www.apache.org/licenses/LICENSE-2.0\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Interface representing a JWT payload as defined in RFC 7519.\n * Contains standard JWT claims as well as custom properties.\n */\nexport interface JwtPayload {\n /**\n * JWT ID - provides a unique identifier for the JWT.\n */\n jti?: string;\n /**\n * Subject - identifies the principal that is the subject of the JWT.\n */\n sub?: string;\n /**\n * Issuer - identifies the principal that issued the JWT.\n */\n iss?: string;\n /**\n * Audience - identifies the recipients that the JWT is intended for.\n * Can be a single string or an array of strings.\n */\n aud?: string | string[];\n /**\n * Expiration Time - identifies the expiration time on or after which the JWT MUST NOT be accepted for processing.\n * Represented as NumericDate (seconds since Unix epoch).\n */\n exp?: number;\n /**\n * Not Before - identifies the time before which the JWT MUST NOT be accepted for processing.\n * Represented as NumericDate (seconds since Unix epoch).\n */\n nbf?: number;\n /**\n * Issued At - identifies the time at which the JWT was issued.\n * Represented as NumericDate (seconds since Unix epoch).\n */\n iat?: number;\n\n /**\n * Allows additional custom properties to be included in the payload.\n */\n [key: string]: any;\n}\n\n/**\n * Interface representing a JWT payload with CoSec-specific extensions.\n * Extends the standard JwtPayload interface with additional CoSec-specific properties.\n */\nexport interface CoSecJwtPayload extends JwtPayload {\n /**\n * Tenant identifier - identifies the tenant scope for the JWT.\n */\n tenantId?: string;\n /**\n * Policies - array of policy identifiers associated with the JWT.\n * These are security policies defined internally by Cosec.\n */\n policies?: string[];\n /**\n * Roles - array of role identifiers associated with the JWT.\n * Role IDs indicate what roles the token belongs to.\n */\n roles?: string[];\n /**\n * Attributes - custom key-value pairs providing additional information about the JWT.\n */\n attributes?: Record<string, any>;\n}\n\n/**\n * Parses a JWT token and extracts its payload.\n *\n * This function decodes the payload part of a JWT token, handling Base64URL decoding\n * and JSON parsing. It validates the token structure and returns null for invalid tokens.\n *\n * @param token - The JWT token string to parse\n * @returns The parsed JWT payload or null if parsing fails\n */\nexport function parseJwtPayload<T extends JwtPayload>(token: string): T | null {\n try {\n const parts = token.split('.');\n if (parts.length !== 3) {\n return null;\n }\n\n const base64Url = parts[1];\n const base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');\n\n // Add padding if needed\n const paddedBase64 = base64.padEnd(\n base64.length + ((4 - (base64.length % 4)) % 4),\n '=',\n );\n\n const jsonPayload = decodeURIComponent(\n atob(paddedBase64)\n .split('')\n .map(function(c) {\n return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2);\n })\n .join(''),\n );\n return JSON.parse(jsonPayload) as T;\n } catch (error) {\n // Avoid exposing sensitive information in error logs\n console.error('Failed to parse JWT token', error);\n return null;\n }\n}\n\n/**\n * Checks if a JWT token is expired based on its expiration time (exp claim).\n *\n * This function determines if a JWT token has expired by comparing its exp claim\n * with the current time. If the token is a string, it will be parsed first.\n * Tokens without an exp claim are considered not expired.\n *\n * The early period parameter allows for early token expiration, which is useful\n * for triggering token refresh before the token actually expires. This helps\n * avoid race conditions where a token expires between the time it is checked and\n * the time it is used.\n *\n * @param token - The JWT token to check, either as a string or as a JwtPayload object\n * @param earlyPeriod - The time in seconds before actual expiration when the token should be considered expired (default: 0)\n * @returns true if the token is expired (or will expire within the early period) or cannot be parsed, false otherwise\n */\nexport function isTokenExpired(\n token: string | CoSecJwtPayload,\n earlyPeriod: number = 0,\n): boolean {\n const payload = typeof token === 'string' ? parseJwtPayload(token) : token;\n if (!payload) {\n return true;\n }\n\n const expAt = payload.exp;\n if (!expAt) {\n return false;\n }\n\n const now = Date.now() / 1000;\n return now > expAt - earlyPeriod;\n}\n","/*\n * Copyright [2021-present] [ahoo wang <ahoowang@qq.com> (https://github.com/Ahoo-Wang)].\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * http://www.apache.org/licenses/LICENSE-2.0\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { getStorage } from './inMemoryStorage';\nimport { type CompositeToken } from './tokenRefresher';\n\nexport const DEFAULT_COSEC_TOKEN_KEY = 'cosec-token';\n\n/**\n * Storage class for managing access and refresh tokens.\n */\nexport class TokenStorage {\n private readonly tokenKey: string;\n private storage: Storage;\n\n constructor(\n tokenKey: string = DEFAULT_COSEC_TOKEN_KEY,\n storage: Storage = getStorage(),\n ) {\n this.tokenKey = tokenKey;\n this.storage = storage;\n }\n\n /**\n * Get the current access token.\n *\n * @returns The current composite token or null if not set\n */\n get(): CompositeToken | null {\n const tokenStr = this.storage.getItem(this.tokenKey);\n if (!tokenStr) {\n return null;\n }\n\n try {\n return JSON.parse(tokenStr);\n } catch (error) {\n console.warn('Failed to get token from storage:', error);\n this.clear();\n return null;\n }\n }\n\n /**\n * Store a composite token.\n *\n * @param token - The composite token to store\n */\n set(token: CompositeToken): void {\n const tokenStr = JSON.stringify(token);\n this.storage.setItem(this.tokenKey, tokenStr);\n }\n\n /**\n * Clear all tokens.\n */\n clear(): void {\n this.storage.removeItem(this.tokenKey);\n }\n}\n"],"names":["_CoSecHeaders","CoSecHeaders","_ResponseCodes","ResponseCodes","AuthorizeResults","NanoIdGenerator","nanoid","idGenerator","COSEC_REQUEST_INTERCEPTOR_NAME","COSEC_REQUEST_INTERCEPTOR_ORDER","REQUEST_BODY_INTERCEPTOR_ORDER","CoSecRequestInterceptor","options","exchange","requestId","deviceId","token","requestHeaders","COSEC_RESPONSE_INTERCEPTOR_NAME","COSEC_RESPONSE_INTERCEPTOR_ORDER","CoSecResponseInterceptor","response","currentToken","newToken","error","InMemoryStorage","key","value","index","getStorage","DEFAULT_COSEC_DEVICE_ID_KEY","DeviceIdStorage","deviceIdKey","storage","parseJwtPayload","parts","base64","paddedBase64","jsonPayload","c","isTokenExpired","earlyPeriod","payload","expAt","DEFAULT_COSEC_TOKEN_KEY","TokenStorage","tokenKey","tokenStr"],"mappings":"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"}
1
+ {"version":3,"file":"index.es.js","sources":["../src/types.ts","../src/idGenerator.ts","../src/cosecRequestInterceptor.ts","../src/cosecResponseInterceptor.ts","../src/inMemoryStorage.ts","../src/deviceIdStorage.ts","../src/jwts.ts","../src/tokenStorage.ts"],"sourcesContent":["/*\n * Copyright [2021-present] [ahoo wang <ahoowang@qq.com> (https://github.com/Ahoo-Wang)].\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * http://www.apache.org/licenses/LICENSE-2.0\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { DeviceIdStorage } from './deviceIdStorage';\nimport { TokenStorage } from './tokenStorage';\nimport { TokenRefresher } from './tokenRefresher';\n\n/**\n * CoSec HTTP headers enumeration.\n */\nexport class CoSecHeaders {\n static readonly DEVICE_ID = 'CoSec-Device-Id';\n static readonly APP_ID = 'CoSec-App-Id';\n static readonly AUTHORIZATION = 'Authorization';\n static readonly REQUEST_ID = 'CoSec-Request-Id';\n}\n\nexport class ResponseCodes {\n static readonly UNAUTHORIZED = 401;\n}\n\n/**\n * CoSec options interface.\n */\nexport interface CoSecOptions {\n /**\n * Application ID to be sent in the CoSec-App-Id header.\n */\n appId: string;\n\n /**\n * Device ID storage instance.\n */\n deviceIdStorage: DeviceIdStorage;\n\n /**\n * Token storage instance.\n */\n tokenStorage: TokenStorage;\n\n /**\n * Token refresher function.\n *\n * Takes a CompositeToken and returns a Promise that resolves to a new CompositeToken.\n */\n tokenRefresher: TokenRefresher;\n}\n\n/**\n * Authorization result interface.\n */\nexport interface AuthorizeResult {\n authorized: boolean;\n reason: string;\n}\n\n/**\n * Authorization result constants.\n */\nexport const AuthorizeResults = {\n ALLOW: { authorized: true, reason: 'Allow' },\n EXPLICIT_DENY: { authorized: false, reason: 'Explicit Deny' },\n IMPLICIT_DENY: { authorized: false, reason: 'Implicit Deny' },\n TOKEN_EXPIRED: { authorized: false, reason: 'Token Expired' },\n TOO_MANY_REQUESTS: { authorized: false, reason: 'Too Many Requests' },\n};\n","/*\n * Copyright [2021-present] [ahoo wang <ahoowang@qq.com> (https://github.com/Ahoo-Wang)].\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * http://www.apache.org/licenses/LICENSE-2.0\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { nanoid } from 'nanoid';\n\nexport interface IdGenerator {\n generateId(): string;\n}\n\n/**\n * Nano ID implementation of IdGenerator.\n * Generates unique request IDs using Nano ID.\n */\nexport class NanoIdGenerator implements IdGenerator {\n /**\n * Generate a unique request ID.\n *\n * @returns A unique request ID\n */\n generateId(): string {\n return nanoid();\n }\n}\n\nexport const idGenerator = new NanoIdGenerator();\n","/*\n * Copyright [2021-present] [ahoo wang <ahoowang@qq.com> (https://github.com/Ahoo-Wang)].\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * http://www.apache.org/licenses/LICENSE-2.0\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n FetchExchange,\n REQUEST_BODY_INTERCEPTOR_ORDER,\n type RequestInterceptor,\n} from '@ahoo-wang/fetcher';\nimport { CoSecHeaders, CoSecOptions } from './types';\nimport { idGenerator } from './idGenerator';\n\n/**\n * The name of the CoSecRequestInterceptor.\n */\nexport const COSEC_REQUEST_INTERCEPTOR_NAME = 'CoSecRequestInterceptor';\n\n/**\n * The order of the CoSecRequestInterceptor.\n * Set to REQUEST_BODY_INTERCEPTOR_ORDER + 1000 to ensure it runs after RequestBodyInterceptor.\n */\nexport const COSEC_REQUEST_INTERCEPTOR_ORDER =\n REQUEST_BODY_INTERCEPTOR_ORDER + 1000;\n\n/**\n * Interceptor that automatically adds CoSec authentication headers to requests.\n *\n * This interceptor adds the following headers to each request:\n * - CoSec-Device-Id: Device identifier (stored in localStorage or generated)\n * - CoSec-App-Id: Application identifier\n * - Authorization: Bearer token\n * - CoSec-Request-Id: Unique request identifier for each request\n *\n * @remarks\n * This interceptor runs after RequestBodyInterceptor but before FetchInterceptor.\n * The order is set to COSEC_REQUEST_INTERCEPTOR_ORDER to ensure it runs after\n * request body processing but before the actual HTTP request is made. This positioning\n * allows for proper authentication header addition after all request body transformations\n * are complete, ensuring that the final request is properly authenticated before\n * being sent over the network.\n */\nexport class CoSecRequestInterceptor implements RequestInterceptor {\n readonly name = COSEC_REQUEST_INTERCEPTOR_NAME;\n readonly order = COSEC_REQUEST_INTERCEPTOR_ORDER;\n private options: CoSecOptions;\n\n constructor(options: CoSecOptions) {\n this.options = options;\n }\n\n /**\n * Intercept requests to add CoSec authentication headers.\n *\n * This method adds the following headers to each request:\n * - CoSec-App-Id: The application identifier from the CoSec options\n * - CoSec-Device-Id: A unique device identifier, either retrieved from storage or generated\n * - CoSec-Request-Id: A unique identifier for this specific request\n * - Authorization: Bearer token if available in token storage\n *\n * @param exchange - The fetch exchange containing the request to process\n *\n * @remarks\n * This method runs after RequestBodyInterceptor but before FetchInterceptor.\n * It ensures that authentication headers are added to the request after all\n * body processing is complete. The positioning allows for proper authentication\n * header addition after all request body transformations are finished, ensuring\n * that the final request is properly authenticated before being sent over the network.\n * This execution order prevents authentication headers from being overwritten by\n * subsequent request processing interceptors.\n */\n intercept(exchange: FetchExchange) {\n const requestId = idGenerator.generateId();\n const deviceId = this.options.deviceIdStorage.getOrCreate();\n const token = this.options.tokenStorage.get();\n const requestHeaders = exchange.ensureRequestHeaders();\n requestHeaders[CoSecHeaders.APP_ID] = this.options.appId;\n requestHeaders[CoSecHeaders.DEVICE_ID] = deviceId;\n requestHeaders[CoSecHeaders.REQUEST_ID] = requestId;\n if (token && !requestHeaders[CoSecHeaders.AUTHORIZATION]) {\n requestHeaders[CoSecHeaders.AUTHORIZATION] =\n `Bearer ${token.accessToken}`;\n }\n }\n}\n","/*\n * Copyright [2021-present] [ahoo wang <ahoowang@qq.com> (https://github.com/Ahoo-Wang)].\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * http://www.apache.org/licenses/LICENSE-2.0\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { type CoSecOptions, ResponseCodes } from './types';\nimport { FetchExchange, type ResponseInterceptor } from '@ahoo-wang/fetcher';\nimport { CompositeToken } from './tokenRefresher';\n\n/**\n * The name of the CoSecResponseInterceptor.\n */\nexport const COSEC_RESPONSE_INTERCEPTOR_NAME = 'CoSecResponseInterceptor';\n\n/**\n * The order of the CoSecResponseInterceptor.\n * Set to a high negative value to ensure it runs early in the interceptor chain.\n */\nexport const COSEC_RESPONSE_INTERCEPTOR_ORDER = Number.MIN_SAFE_INTEGER + 1000;\n\n/**\n * CoSecResponseInterceptor is responsible for handling unauthorized responses (401)\n * by attempting to refresh the authentication token and retrying the original request.\n *\n * This interceptor:\n * 1. Checks if the response status is 401 (UNAUTHORIZED)\n * 2. If so, and if there's a current token, attempts to refresh it\n * 3. On successful refresh, stores the new token and retries the original request\n * 4. On refresh failure, clears stored tokens and propagates the error\n */\nexport class CoSecResponseInterceptor implements ResponseInterceptor {\n readonly name = COSEC_RESPONSE_INTERCEPTOR_NAME;\n readonly order = COSEC_RESPONSE_INTERCEPTOR_ORDER;\n private options: CoSecOptions;\n private refreshInProgress?: Promise<CompositeToken>;\n\n /**\n * Creates a new CoSecResponseInterceptor instance.\n * @param options - The CoSec configuration options including token storage and refresher\n */\n constructor(options: CoSecOptions) {\n this.options = options;\n }\n\n private async refresh(currentToken: CompositeToken): Promise<CompositeToken> {\n if (this.refreshInProgress) {\n return this.refreshInProgress;\n }\n\n this.refreshInProgress = this.options.tokenRefresher.refresh(currentToken)\n .then(newToken => {\n this.options.tokenStorage.set(newToken);\n return newToken;\n })\n .catch(error => {\n this.options.tokenStorage.clear();\n throw error;\n })\n .finally(() => {\n this.refreshInProgress = undefined;\n });\n\n return this.refreshInProgress;\n }\n\n /**\n * Intercepts the response and handles unauthorized responses by refreshing tokens.\n * @param exchange - The fetch exchange containing request and response information\n */\n async intercept(exchange: FetchExchange): Promise<void> {\n const response = exchange.response;\n // If there's no response, nothing to intercept\n if (!response) {\n return;\n }\n\n // Only handle unauthorized responses (401)\n if (response.status !== ResponseCodes.UNAUTHORIZED) {\n return;\n }\n\n // Get the current token from storage\n const currentToken = this.options.tokenStorage.get();\n // If there's no current token, we can't refresh it\n if (!currentToken) {\n return;\n }\n\n try {\n // Attempt to refresh the token\n await this.refresh(currentToken);\n // Retry the original request with the new token\n await exchange.fetcher.request(exchange.request);\n } catch (error) {\n // If token refresh fails, clear stored tokens and re-throw the error\n this.options.tokenStorage.clear();\n throw error;\n }\n }\n}\n","/*\n * Copyright [2021-present] [ahoo wang <ahoowang@qq.com> (https://github.com/Ahoo-Wang)].\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * http://www.apache.org/licenses/LICENSE-2.0\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * In-memory storage fallback for environments without localStorage.\n */\nexport class InMemoryStorage implements Storage {\n private store: Map<string, string> = new Map();\n\n get length(): number {\n return this.store.size;\n }\n\n clear(): void {\n this.store.clear();\n }\n\n getItem(key: string): string | null {\n const value = this.store.get(key);\n return value !== undefined ? value : null;\n }\n\n key(index: number): string | null {\n const keys = Array.from(this.store.keys());\n return keys[index] || null;\n }\n\n removeItem(key: string): void {\n if (this.store.has(key)) {\n this.store.delete(key);\n }\n }\n\n setItem(key: string, value: string): void {\n this.store.set(key, value);\n }\n}\n\nexport function getStorage(): Storage {\n if (typeof window !== 'undefined' && window.localStorage) {\n return window.localStorage;\n } else {\n // Use in-memory storage as fallback\n return new InMemoryStorage();\n }\n}\n","/*\n * Copyright [2021-present] [ahoo wang <ahoowang@qq.com> (https://github.com/Ahoo-Wang)].\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * http://www.apache.org/licenses/LICENSE-2.0\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { getStorage } from './inMemoryStorage';\nimport { idGenerator } from './idGenerator';\n\nexport const DEFAULT_COSEC_DEVICE_ID_KEY = 'cosec-device-id';\n\n/**\n * Storage class for managing device identifiers.\n */\nexport class DeviceIdStorage {\n private readonly deviceIdKey: string;\n private storage: Storage;\n\n constructor(\n deviceIdKey: string = DEFAULT_COSEC_DEVICE_ID_KEY,\n storage: Storage = getStorage(),\n ) {\n this.deviceIdKey = deviceIdKey;\n this.storage = storage;\n }\n\n /**\n * Get the current device ID.\n *\n * @returns The current device ID or null if not set\n */\n get(): string | null {\n return this.storage.getItem(this.deviceIdKey);\n }\n\n /**\n * Set a device ID.\n *\n * @param deviceId - The device ID to set\n */\n set(deviceId: string): void {\n this.storage.setItem(this.deviceIdKey, deviceId);\n }\n\n /**\n * Generate a new device ID.\n *\n * @returns A newly generated device ID\n */\n generateDeviceId(): string {\n return idGenerator.generateId();\n }\n\n /**\n * Get or create a device ID.\n *\n * @returns The existing device ID if available, otherwise a newly generated one\n */\n getOrCreate(): string {\n // Try to get existing device ID from storage\n let deviceId = this.get();\n if (!deviceId) {\n // Generate a new device ID and store it\n deviceId = this.generateDeviceId();\n this.set(deviceId);\n }\n\n return deviceId;\n }\n\n /**\n * Clear the stored device ID.\n */\n clear(): void {\n this.storage.removeItem(this.deviceIdKey);\n }\n}\n","/*\n * Copyright [2021-present] [ahoo wang <ahoowang@qq.com> (https://github.com/Ahoo-Wang)].\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * http://www.apache.org/licenses/LICENSE-2.0\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Interface representing a JWT payload as defined in RFC 7519.\n * Contains standard JWT claims as well as custom properties.\n */\nexport interface JwtPayload {\n /**\n * JWT ID - provides a unique identifier for the JWT.\n */\n jti?: string;\n /**\n * Subject - identifies the principal that is the subject of the JWT.\n */\n sub?: string;\n /**\n * Issuer - identifies the principal that issued the JWT.\n */\n iss?: string;\n /**\n * Audience - identifies the recipients that the JWT is intended for.\n * Can be a single string or an array of strings.\n */\n aud?: string | string[];\n /**\n * Expiration Time - identifies the expiration time on or after which the JWT MUST NOT be accepted for processing.\n * Represented as NumericDate (seconds since Unix epoch).\n */\n exp?: number;\n /**\n * Not Before - identifies the time before which the JWT MUST NOT be accepted for processing.\n * Represented as NumericDate (seconds since Unix epoch).\n */\n nbf?: number;\n /**\n * Issued At - identifies the time at which the JWT was issued.\n * Represented as NumericDate (seconds since Unix epoch).\n */\n iat?: number;\n\n /**\n * Allows additional custom properties to be included in the payload.\n */\n [key: string]: any;\n}\n\n/**\n * Interface representing a JWT payload with CoSec-specific extensions.\n * Extends the standard JwtPayload interface with additional CoSec-specific properties.\n */\nexport interface CoSecJwtPayload extends JwtPayload {\n /**\n * Tenant identifier - identifies the tenant scope for the JWT.\n */\n tenantId?: string;\n /**\n * Policies - array of policy identifiers associated with the JWT.\n * These are security policies defined internally by Cosec.\n */\n policies?: string[];\n /**\n * Roles - array of role identifiers associated with the JWT.\n * Role IDs indicate what roles the token belongs to.\n */\n roles?: string[];\n /**\n * Attributes - custom key-value pairs providing additional information about the JWT.\n */\n attributes?: Record<string, any>;\n}\n\n/**\n * Parses a JWT token and extracts its payload.\n *\n * This function decodes the payload part of a JWT token, handling Base64URL decoding\n * and JSON parsing. It validates the token structure and returns null for invalid tokens.\n *\n * @param token - The JWT token string to parse\n * @returns The parsed JWT payload or null if parsing fails\n */\nexport function parseJwtPayload<T extends JwtPayload>(token: string): T | null {\n try {\n const parts = token.split('.');\n if (parts.length !== 3) {\n return null;\n }\n\n const base64Url = parts[1];\n const base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');\n\n // Add padding if needed\n const paddedBase64 = base64.padEnd(\n base64.length + ((4 - (base64.length % 4)) % 4),\n '=',\n );\n\n const jsonPayload = decodeURIComponent(\n atob(paddedBase64)\n .split('')\n .map(function(c) {\n return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2);\n })\n .join(''),\n );\n return JSON.parse(jsonPayload) as T;\n } catch (error) {\n // Avoid exposing sensitive information in error logs\n console.error('Failed to parse JWT token', error);\n return null;\n }\n}\n\n/**\n * Checks if a JWT token is expired based on its expiration time (exp claim).\n *\n * This function determines if a JWT token has expired by comparing its exp claim\n * with the current time. If the token is a string, it will be parsed first.\n * Tokens without an exp claim are considered not expired.\n *\n * The early period parameter allows for early token expiration, which is useful\n * for triggering token refresh before the token actually expires. This helps\n * avoid race conditions where a token expires between the time it is checked and\n * the time it is used.\n *\n * @param token - The JWT token to check, either as a string or as a JwtPayload object\n * @param earlyPeriod - The time in seconds before actual expiration when the token should be considered expired (default: 0)\n * @returns true if the token is expired (or will expire within the early period) or cannot be parsed, false otherwise\n */\nexport function isTokenExpired(\n token: string | CoSecJwtPayload,\n earlyPeriod: number = 0,\n): boolean {\n const payload = typeof token === 'string' ? parseJwtPayload(token) : token;\n if (!payload) {\n return true;\n }\n\n const expAt = payload.exp;\n if (!expAt) {\n return false;\n }\n\n const now = Date.now() / 1000;\n return now > expAt - earlyPeriod;\n}\n","/*\n * Copyright [2021-present] [ahoo wang <ahoowang@qq.com> (https://github.com/Ahoo-Wang)].\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * http://www.apache.org/licenses/LICENSE-2.0\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { getStorage } from './inMemoryStorage';\nimport { type CompositeToken } from './tokenRefresher';\n\nexport const DEFAULT_COSEC_TOKEN_KEY = 'cosec-token';\n\n/**\n * Storage class for managing access and refresh tokens.\n */\nexport class TokenStorage {\n private readonly tokenKey: string;\n private storage: Storage;\n\n constructor(\n tokenKey: string = DEFAULT_COSEC_TOKEN_KEY,\n storage: Storage = getStorage(),\n ) {\n this.tokenKey = tokenKey;\n this.storage = storage;\n }\n\n /**\n * Get the current access token.\n *\n * @returns The current composite token or null if not set\n */\n get(): CompositeToken | null {\n const tokenStr = this.storage.getItem(this.tokenKey);\n if (!tokenStr) {\n return null;\n }\n\n try {\n return JSON.parse(tokenStr);\n } catch (error) {\n console.warn('Failed to get token from storage:', error);\n this.clear();\n return null;\n }\n }\n\n /**\n * Store a composite token.\n *\n * @param token - The composite token to store\n */\n set(token: CompositeToken): void {\n const tokenStr = JSON.stringify(token);\n this.storage.setItem(this.tokenKey, tokenStr);\n }\n\n /**\n * Clear all tokens.\n */\n clear(): void {\n this.storage.removeItem(this.tokenKey);\n }\n}\n"],"names":["_CoSecHeaders","CoSecHeaders","_ResponseCodes","ResponseCodes","AuthorizeResults","NanoIdGenerator","nanoid","idGenerator","COSEC_REQUEST_INTERCEPTOR_NAME","COSEC_REQUEST_INTERCEPTOR_ORDER","REQUEST_BODY_INTERCEPTOR_ORDER","CoSecRequestInterceptor","options","exchange","requestId","deviceId","token","requestHeaders","COSEC_RESPONSE_INTERCEPTOR_NAME","COSEC_RESPONSE_INTERCEPTOR_ORDER","CoSecResponseInterceptor","currentToken","newToken","error","response","InMemoryStorage","key","value","index","getStorage","DEFAULT_COSEC_DEVICE_ID_KEY","DeviceIdStorage","deviceIdKey","storage","parseJwtPayload","parts","base64","paddedBase64","jsonPayload","c","isTokenExpired","earlyPeriod","payload","expAt","DEFAULT_COSEC_TOKEN_KEY","TokenStorage","tokenKey","tokenStr"],"mappings":"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"}
package/dist/index.umd.js CHANGED
@@ -1,2 +1,2 @@
1
- (function(t,E){typeof exports=="object"&&typeof module<"u"?E(exports,require("@ahoo-wang/fetcher"),require("nanoid")):typeof define=="function"&&define.amd?define(["exports","@ahoo-wang/fetcher","nanoid"],E):(t=typeof globalThis<"u"?globalThis:t||self,E(t.FetcherCoSec={},t.Fetcher,t.nanoid))})(this,(function(t,E,p){"use strict";const c=class c{};c.DEVICE_ID="CoSec-Device-Id",c.APP_ID="CoSec-App-Id",c.AUTHORIZATION="Authorization",c.REQUEST_ID="CoSec-Request-Id";let i=c;const h=class h{};h.UNAUTHORIZED=401;let I=h;const N={ALLOW:{authorized:!0,reason:"Allow"},EXPLICIT_DENY:{authorized:!1,reason:"Explicit Deny"},IMPLICIT_DENY:{authorized:!1,reason:"Implicit Deny"},TOKEN_EXPIRED:{authorized:!1,reason:"Token Expired"},TOO_MANY_REQUESTS:{authorized:!1,reason:"Too Many Requests"}};class l{generateId(){return p.nanoid()}}const u=new l,R="CoSecRequestInterceptor",S=E.REQUEST_BODY_INTERCEPTOR_ORDER+1e3;class D{constructor(e){this.name=R,this.order=S,this.options=e}intercept(e){const o=u.generateId(),s=this.options.deviceIdStorage.getOrCreate(),n=this.options.tokenStorage.get(),a=e.ensureRequestHeaders();a[i.APP_ID]=this.options.appId,a[i.DEVICE_ID]=s,a[i.REQUEST_ID]=o,n&&!a[i.AUTHORIZATION]&&(a[i.AUTHORIZATION]=`Bearer ${n.accessToken}`)}}const T="CoSecResponseInterceptor",g=Number.MIN_SAFE_INTEGER+1e3;class y{constructor(e){this.name=T,this.order=g,this.options=e}async intercept(e){const o=e.response;if(!o||o.status!==I.UNAUTHORIZED)return;const s=this.options.tokenStorage.get();if(s)try{const n=await this.options.tokenRefresher.refresh(s);this.options.tokenStorage.set(n),await e.fetcher.request(e.request)}catch(n){throw this.options.tokenStorage.clear(),n}}}class O{constructor(){this.store=new Map}get length(){return this.store.size}clear(){this.store.clear()}getItem(e){const o=this.store.get(e);return o!==void 0?o:null}key(e){return Array.from(this.store.keys())[e]||null}removeItem(e){this.store.has(e)&&this.store.delete(e)}setItem(e,o){this.store.set(e,o)}}function d(){return typeof window<"u"&&window.localStorage?window.localStorage:new O}const _="cosec-device-id";class A{constructor(e=_,o=d()){this.deviceIdKey=e,this.storage=o}get(){return this.storage.getItem(this.deviceIdKey)}set(e){this.storage.setItem(this.deviceIdKey,e)}generateDeviceId(){return u.generateId()}getOrCreate(){let e=this.get();return e||(e=this.generateDeviceId(),this.set(e)),e}clear(){this.storage.removeItem(this.deviceIdKey)}}function C(r){try{const e=r.split(".");if(e.length!==3)return null;const s=e[1].replace(/-/g,"+").replace(/_/g,"/"),n=s.padEnd(s.length+(4-s.length%4)%4,"="),a=decodeURIComponent(atob(n).split("").map(function(P){return"%"+("00"+P.charCodeAt(0).toString(16)).slice(-2)}).join(""));return JSON.parse(a)}catch(e){return console.error("Failed to parse JWT token",e),null}}function m(r,e=0){const o=typeof r=="string"?C(r):r;if(!o)return!0;const s=o.exp;return s?Date.now()/1e3>s-e:!1}const f="cosec-token";class k{constructor(e=f,o=d()){this.tokenKey=e,this.storage=o}get(){const e=this.storage.getItem(this.tokenKey);if(!e)return null;try{return JSON.parse(e)}catch(o){return console.warn("Failed to get token from storage:",o),this.clear(),null}}set(e){const o=JSON.stringify(e);this.storage.setItem(this.tokenKey,o)}clear(){this.storage.removeItem(this.tokenKey)}}t.AuthorizeResults=N,t.COSEC_REQUEST_INTERCEPTOR_NAME=R,t.COSEC_REQUEST_INTERCEPTOR_ORDER=S,t.COSEC_RESPONSE_INTERCEPTOR_NAME=T,t.COSEC_RESPONSE_INTERCEPTOR_ORDER=g,t.CoSecHeaders=i,t.CoSecRequestInterceptor=D,t.CoSecResponseInterceptor=y,t.DEFAULT_COSEC_DEVICE_ID_KEY=_,t.DEFAULT_COSEC_TOKEN_KEY=f,t.DeviceIdStorage=A,t.InMemoryStorage=O,t.NanoIdGenerator=l,t.ResponseCodes=I,t.TokenStorage=k,t.getStorage=d,t.idGenerator=u,t.isTokenExpired=m,t.parseJwtPayload=C,Object.defineProperty(t,Symbol.toStringTag,{value:"Module"})}));
1
+ (function(r,E){typeof exports=="object"&&typeof module<"u"?E(exports,require("@ahoo-wang/fetcher"),require("nanoid")):typeof define=="function"&&define.amd?define(["exports","@ahoo-wang/fetcher","nanoid"],E):(r=typeof globalThis<"u"?globalThis:r||self,E(r.FetcherCoSec={},r.Fetcher,r.nanoid))})(this,(function(r,E,p){"use strict";const c=class c{};c.DEVICE_ID="CoSec-Device-Id",c.APP_ID="CoSec-App-Id",c.AUTHORIZATION="Authorization",c.REQUEST_ID="CoSec-Request-Id";let n=c;const d=class d{};d.UNAUTHORIZED=401;let h=d;const N={ALLOW:{authorized:!0,reason:"Allow"},EXPLICIT_DENY:{authorized:!1,reason:"Explicit Deny"},IMPLICIT_DENY:{authorized:!1,reason:"Implicit Deny"},TOKEN_EXPIRED:{authorized:!1,reason:"Token Expired"},TOO_MANY_REQUESTS:{authorized:!1,reason:"Too Many Requests"}};class l{generateId(){return p.nanoid()}}const I=new l,S="CoSecRequestInterceptor",g=E.REQUEST_BODY_INTERCEPTOR_ORDER+1e3;class D{constructor(e){this.name=S,this.order=g,this.options=e}intercept(e){const t=I.generateId(),o=this.options.deviceIdStorage.getOrCreate(),i=this.options.tokenStorage.get(),a=e.ensureRequestHeaders();a[n.APP_ID]=this.options.appId,a[n.DEVICE_ID]=o,a[n.REQUEST_ID]=t,i&&!a[n.AUTHORIZATION]&&(a[n.AUTHORIZATION]=`Bearer ${i.accessToken}`)}}const R="CoSecResponseInterceptor",T=Number.MIN_SAFE_INTEGER+1e3;class y{constructor(e){this.name=R,this.order=T,this.options=e}async refresh(e){return this.refreshInProgress?this.refreshInProgress:(this.refreshInProgress=this.options.tokenRefresher.refresh(e).then(t=>(this.options.tokenStorage.set(t),t)).catch(t=>{throw this.options.tokenStorage.clear(),t}).finally(()=>{this.refreshInProgress=void 0}),this.refreshInProgress)}async intercept(e){const t=e.response;if(!t||t.status!==h.UNAUTHORIZED)return;const o=this.options.tokenStorage.get();if(o)try{await this.refresh(o),await e.fetcher.request(e.request)}catch(i){throw this.options.tokenStorage.clear(),i}}}class f{constructor(){this.store=new Map}get length(){return this.store.size}clear(){this.store.clear()}getItem(e){const t=this.store.get(e);return t!==void 0?t:null}key(e){return Array.from(this.store.keys())[e]||null}removeItem(e){this.store.has(e)&&this.store.delete(e)}setItem(e,t){this.store.set(e,t)}}function u(){return typeof window<"u"&&window.localStorage?window.localStorage:new f}const O="cosec-device-id";class A{constructor(e=O,t=u()){this.deviceIdKey=e,this.storage=t}get(){return this.storage.getItem(this.deviceIdKey)}set(e){this.storage.setItem(this.deviceIdKey,e)}generateDeviceId(){return I.generateId()}getOrCreate(){let e=this.get();return e||(e=this.generateDeviceId(),this.set(e)),e}clear(){this.storage.removeItem(this.deviceIdKey)}}function _(s){try{const e=s.split(".");if(e.length!==3)return null;const o=e[1].replace(/-/g,"+").replace(/_/g,"/"),i=o.padEnd(o.length+(4-o.length%4)%4,"="),a=decodeURIComponent(atob(i).split("").map(function(k){return"%"+("00"+k.charCodeAt(0).toString(16)).slice(-2)}).join(""));return JSON.parse(a)}catch(e){return console.error("Failed to parse JWT token",e),null}}function P(s,e=0){const t=typeof s=="string"?_(s):s;if(!t)return!0;const o=t.exp;return o?Date.now()/1e3>o-e:!1}const C="cosec-token";class m{constructor(e=C,t=u()){this.tokenKey=e,this.storage=t}get(){const e=this.storage.getItem(this.tokenKey);if(!e)return null;try{return JSON.parse(e)}catch(t){return console.warn("Failed to get token from storage:",t),this.clear(),null}}set(e){const t=JSON.stringify(e);this.storage.setItem(this.tokenKey,t)}clear(){this.storage.removeItem(this.tokenKey)}}r.AuthorizeResults=N,r.COSEC_REQUEST_INTERCEPTOR_NAME=S,r.COSEC_REQUEST_INTERCEPTOR_ORDER=g,r.COSEC_RESPONSE_INTERCEPTOR_NAME=R,r.COSEC_RESPONSE_INTERCEPTOR_ORDER=T,r.CoSecHeaders=n,r.CoSecRequestInterceptor=D,r.CoSecResponseInterceptor=y,r.DEFAULT_COSEC_DEVICE_ID_KEY=O,r.DEFAULT_COSEC_TOKEN_KEY=C,r.DeviceIdStorage=A,r.InMemoryStorage=f,r.NanoIdGenerator=l,r.ResponseCodes=h,r.TokenStorage=m,r.getStorage=u,r.idGenerator=I,r.isTokenExpired=P,r.parseJwtPayload=_,Object.defineProperty(r,Symbol.toStringTag,{value:"Module"})}));
2
2
  //# sourceMappingURL=index.umd.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.umd.js","sources":["../src/types.ts","../src/idGenerator.ts","../src/cosecRequestInterceptor.ts","../src/cosecResponseInterceptor.ts","../src/inMemoryStorage.ts","../src/deviceIdStorage.ts","../src/jwts.ts","../src/tokenStorage.ts"],"sourcesContent":["/*\n * Copyright [2021-present] [ahoo wang <ahoowang@qq.com> (https://github.com/Ahoo-Wang)].\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * http://www.apache.org/licenses/LICENSE-2.0\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { DeviceIdStorage } from './deviceIdStorage';\nimport { TokenStorage } from './tokenStorage';\nimport { TokenRefresher } from './tokenRefresher';\n\n/**\n * CoSec HTTP headers enumeration.\n */\nexport class CoSecHeaders {\n static readonly DEVICE_ID = 'CoSec-Device-Id';\n static readonly APP_ID = 'CoSec-App-Id';\n static readonly AUTHORIZATION = 'Authorization';\n static readonly REQUEST_ID = 'CoSec-Request-Id';\n}\n\nexport class ResponseCodes {\n static readonly UNAUTHORIZED = 401;\n}\n\n/**\n * CoSec options interface.\n */\nexport interface CoSecOptions {\n /**\n * Application ID to be sent in the CoSec-App-Id header.\n */\n appId: string;\n\n /**\n * Device ID storage instance.\n */\n deviceIdStorage: DeviceIdStorage;\n\n /**\n * Token storage instance.\n */\n tokenStorage: TokenStorage;\n\n /**\n * Token refresher function.\n *\n * Takes a CompositeToken and returns a Promise that resolves to a new CompositeToken.\n */\n tokenRefresher: TokenRefresher;\n}\n\n/**\n * Authorization result interface.\n */\nexport interface AuthorizeResult {\n authorized: boolean;\n reason: string;\n}\n\n/**\n * Authorization result constants.\n */\nexport const AuthorizeResults = {\n ALLOW: { authorized: true, reason: 'Allow' },\n EXPLICIT_DENY: { authorized: false, reason: 'Explicit Deny' },\n IMPLICIT_DENY: { authorized: false, reason: 'Implicit Deny' },\n TOKEN_EXPIRED: { authorized: false, reason: 'Token Expired' },\n TOO_MANY_REQUESTS: { authorized: false, reason: 'Too Many Requests' },\n};\n","/*\n * Copyright [2021-present] [ahoo wang <ahoowang@qq.com> (https://github.com/Ahoo-Wang)].\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * http://www.apache.org/licenses/LICENSE-2.0\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { nanoid } from 'nanoid';\n\nexport interface IdGenerator {\n generateId(): string;\n}\n\n/**\n * Nano ID implementation of IdGenerator.\n * Generates unique request IDs using Nano ID.\n */\nexport class NanoIdGenerator implements IdGenerator {\n /**\n * Generate a unique request ID.\n *\n * @returns A unique request ID\n */\n generateId(): string {\n return nanoid();\n }\n}\n\nexport const idGenerator = new NanoIdGenerator();\n","/*\n * Copyright [2021-present] [ahoo wang <ahoowang@qq.com> (https://github.com/Ahoo-Wang)].\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * http://www.apache.org/licenses/LICENSE-2.0\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n FetchExchange,\n REQUEST_BODY_INTERCEPTOR_ORDER,\n type RequestInterceptor,\n} from '@ahoo-wang/fetcher';\nimport { CoSecHeaders, CoSecOptions } from './types';\nimport { idGenerator } from './idGenerator';\n\n/**\n * The name of the CoSecRequestInterceptor.\n */\nexport const COSEC_REQUEST_INTERCEPTOR_NAME = 'CoSecRequestInterceptor';\n\n/**\n * The order of the CoSecRequestInterceptor.\n * Set to REQUEST_BODY_INTERCEPTOR_ORDER + 1000 to ensure it runs after RequestBodyInterceptor.\n */\nexport const COSEC_REQUEST_INTERCEPTOR_ORDER =\n REQUEST_BODY_INTERCEPTOR_ORDER + 1000;\n\n/**\n * Interceptor that automatically adds CoSec authentication headers to requests.\n *\n * This interceptor adds the following headers to each request:\n * - CoSec-Device-Id: Device identifier (stored in localStorage or generated)\n * - CoSec-App-Id: Application identifier\n * - Authorization: Bearer token\n * - CoSec-Request-Id: Unique request identifier for each request\n *\n * @remarks\n * This interceptor runs after RequestBodyInterceptor but before FetchInterceptor.\n * The order is set to COSEC_REQUEST_INTERCEPTOR_ORDER to ensure it runs after\n * request body processing but before the actual HTTP request is made. This positioning\n * allows for proper authentication header addition after all request body transformations\n * are complete, ensuring that the final request is properly authenticated before\n * being sent over the network.\n */\nexport class CoSecRequestInterceptor implements RequestInterceptor {\n readonly name = COSEC_REQUEST_INTERCEPTOR_NAME;\n readonly order = COSEC_REQUEST_INTERCEPTOR_ORDER;\n private options: CoSecOptions;\n\n constructor(options: CoSecOptions) {\n this.options = options;\n }\n\n /**\n * Intercept requests to add CoSec authentication headers.\n *\n * This method adds the following headers to each request:\n * - CoSec-App-Id: The application identifier from the CoSec options\n * - CoSec-Device-Id: A unique device identifier, either retrieved from storage or generated\n * - CoSec-Request-Id: A unique identifier for this specific request\n * - Authorization: Bearer token if available in token storage\n *\n * @param exchange - The fetch exchange containing the request to process\n *\n * @remarks\n * This method runs after RequestBodyInterceptor but before FetchInterceptor.\n * It ensures that authentication headers are added to the request after all\n * body processing is complete. The positioning allows for proper authentication\n * header addition after all request body transformations are finished, ensuring\n * that the final request is properly authenticated before being sent over the network.\n * This execution order prevents authentication headers from being overwritten by\n * subsequent request processing interceptors.\n */\n intercept(exchange: FetchExchange) {\n const requestId = idGenerator.generateId();\n const deviceId = this.options.deviceIdStorage.getOrCreate();\n const token = this.options.tokenStorage.get();\n const requestHeaders = exchange.ensureRequestHeaders();\n requestHeaders[CoSecHeaders.APP_ID] = this.options.appId;\n requestHeaders[CoSecHeaders.DEVICE_ID] = deviceId;\n requestHeaders[CoSecHeaders.REQUEST_ID] = requestId;\n if (token && !requestHeaders[CoSecHeaders.AUTHORIZATION]) {\n requestHeaders[CoSecHeaders.AUTHORIZATION] =\n `Bearer ${token.accessToken}`;\n }\n }\n}\n","/*\n * Copyright [2021-present] [ahoo wang <ahoowang@qq.com> (https://github.com/Ahoo-Wang)].\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * http://www.apache.org/licenses/LICENSE-2.0\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { type CoSecOptions, ResponseCodes } from './types';\nimport { FetchExchange, type ResponseInterceptor } from '@ahoo-wang/fetcher';\n\n/**\n * The name of the CoSecResponseInterceptor.\n */\nexport const COSEC_RESPONSE_INTERCEPTOR_NAME = 'CoSecResponseInterceptor';\n\n/**\n * The order of the CoSecResponseInterceptor.\n * Set to a high negative value to ensure it runs early in the interceptor chain.\n */\nexport const COSEC_RESPONSE_INTERCEPTOR_ORDER = Number.MIN_SAFE_INTEGER + 1000;\n\n/**\n * CoSecResponseInterceptor is responsible for handling unauthorized responses (401)\n * by attempting to refresh the authentication token and retrying the original request.\n *\n * This interceptor:\n * 1. Checks if the response status is 401 (UNAUTHORIZED)\n * 2. If so, and if there's a current token, attempts to refresh it\n * 3. On successful refresh, stores the new token and retries the original request\n * 4. On refresh failure, clears stored tokens and propagates the error\n */\nexport class CoSecResponseInterceptor implements ResponseInterceptor {\n readonly name = COSEC_RESPONSE_INTERCEPTOR_NAME;\n readonly order = COSEC_RESPONSE_INTERCEPTOR_ORDER;\n private options: CoSecOptions;\n\n /**\n * Creates a new CoSecResponseInterceptor instance.\n * @param options - The CoSec configuration options including token storage and refresher\n */\n constructor(options: CoSecOptions) {\n this.options = options;\n }\n\n /**\n * Intercepts the response and handles unauthorized responses by refreshing tokens.\n * @param exchange - The fetch exchange containing request and response information\n */\n async intercept(exchange: FetchExchange): Promise<void> {\n const response = exchange.response;\n // If there's no response, nothing to intercept\n if (!response) {\n return;\n }\n\n // Only handle unauthorized responses (401)\n if (response.status !== ResponseCodes.UNAUTHORIZED) {\n return;\n }\n\n // Get the current token from storage\n const currentToken = this.options.tokenStorage.get();\n // If there's no current token, we can't refresh it\n if (!currentToken) {\n return;\n }\n\n try {\n // Attempt to refresh the token\n const newToken = await this.options.tokenRefresher.refresh(currentToken);\n // Store the refreshed token\n this.options.tokenStorage.set(newToken);\n // Retry the original request with the new token\n await exchange.fetcher.request(exchange.request);\n } catch (error) {\n // If token refresh fails, clear stored tokens and re-throw the error\n this.options.tokenStorage.clear();\n throw error;\n }\n }\n}\n","/*\n * Copyright [2021-present] [ahoo wang <ahoowang@qq.com> (https://github.com/Ahoo-Wang)].\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * http://www.apache.org/licenses/LICENSE-2.0\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * In-memory storage fallback for environments without localStorage.\n */\nexport class InMemoryStorage implements Storage {\n private store: Map<string, string> = new Map();\n\n get length(): number {\n return this.store.size;\n }\n\n clear(): void {\n this.store.clear();\n }\n\n getItem(key: string): string | null {\n const value = this.store.get(key);\n return value !== undefined ? value : null;\n }\n\n key(index: number): string | null {\n const keys = Array.from(this.store.keys());\n return keys[index] || null;\n }\n\n removeItem(key: string): void {\n if (this.store.has(key)) {\n this.store.delete(key);\n }\n }\n\n setItem(key: string, value: string): void {\n this.store.set(key, value);\n }\n}\n\nexport function getStorage(): Storage {\n if (typeof window !== 'undefined' && window.localStorage) {\n return window.localStorage;\n } else {\n // Use in-memory storage as fallback\n return new InMemoryStorage();\n }\n}\n","/*\n * Copyright [2021-present] [ahoo wang <ahoowang@qq.com> (https://github.com/Ahoo-Wang)].\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * http://www.apache.org/licenses/LICENSE-2.0\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { getStorage } from './inMemoryStorage';\nimport { idGenerator } from './idGenerator';\n\nexport const DEFAULT_COSEC_DEVICE_ID_KEY = 'cosec-device-id';\n\n/**\n * Storage class for managing device identifiers.\n */\nexport class DeviceIdStorage {\n private readonly deviceIdKey: string;\n private storage: Storage;\n\n constructor(\n deviceIdKey: string = DEFAULT_COSEC_DEVICE_ID_KEY,\n storage: Storage = getStorage(),\n ) {\n this.deviceIdKey = deviceIdKey;\n this.storage = storage;\n }\n\n /**\n * Get the current device ID.\n *\n * @returns The current device ID or null if not set\n */\n get(): string | null {\n return this.storage.getItem(this.deviceIdKey);\n }\n\n /**\n * Set a device ID.\n *\n * @param deviceId - The device ID to set\n */\n set(deviceId: string): void {\n this.storage.setItem(this.deviceIdKey, deviceId);\n }\n\n /**\n * Generate a new device ID.\n *\n * @returns A newly generated device ID\n */\n generateDeviceId(): string {\n return idGenerator.generateId();\n }\n\n /**\n * Get or create a device ID.\n *\n * @returns The existing device ID if available, otherwise a newly generated one\n */\n getOrCreate(): string {\n // Try to get existing device ID from storage\n let deviceId = this.get();\n if (!deviceId) {\n // Generate a new device ID and store it\n deviceId = this.generateDeviceId();\n this.set(deviceId);\n }\n\n return deviceId;\n }\n\n /**\n * Clear the stored device ID.\n */\n clear(): void {\n this.storage.removeItem(this.deviceIdKey);\n }\n}\n","/*\n * Copyright [2021-present] [ahoo wang <ahoowang@qq.com> (https://github.com/Ahoo-Wang)].\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * http://www.apache.org/licenses/LICENSE-2.0\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Interface representing a JWT payload as defined in RFC 7519.\n * Contains standard JWT claims as well as custom properties.\n */\nexport interface JwtPayload {\n /**\n * JWT ID - provides a unique identifier for the JWT.\n */\n jti?: string;\n /**\n * Subject - identifies the principal that is the subject of the JWT.\n */\n sub?: string;\n /**\n * Issuer - identifies the principal that issued the JWT.\n */\n iss?: string;\n /**\n * Audience - identifies the recipients that the JWT is intended for.\n * Can be a single string or an array of strings.\n */\n aud?: string | string[];\n /**\n * Expiration Time - identifies the expiration time on or after which the JWT MUST NOT be accepted for processing.\n * Represented as NumericDate (seconds since Unix epoch).\n */\n exp?: number;\n /**\n * Not Before - identifies the time before which the JWT MUST NOT be accepted for processing.\n * Represented as NumericDate (seconds since Unix epoch).\n */\n nbf?: number;\n /**\n * Issued At - identifies the time at which the JWT was issued.\n * Represented as NumericDate (seconds since Unix epoch).\n */\n iat?: number;\n\n /**\n * Allows additional custom properties to be included in the payload.\n */\n [key: string]: any;\n}\n\n/**\n * Interface representing a JWT payload with CoSec-specific extensions.\n * Extends the standard JwtPayload interface with additional CoSec-specific properties.\n */\nexport interface CoSecJwtPayload extends JwtPayload {\n /**\n * Tenant identifier - identifies the tenant scope for the JWT.\n */\n tenantId?: string;\n /**\n * Policies - array of policy identifiers associated with the JWT.\n * These are security policies defined internally by Cosec.\n */\n policies?: string[];\n /**\n * Roles - array of role identifiers associated with the JWT.\n * Role IDs indicate what roles the token belongs to.\n */\n roles?: string[];\n /**\n * Attributes - custom key-value pairs providing additional information about the JWT.\n */\n attributes?: Record<string, any>;\n}\n\n/**\n * Parses a JWT token and extracts its payload.\n *\n * This function decodes the payload part of a JWT token, handling Base64URL decoding\n * and JSON parsing. It validates the token structure and returns null for invalid tokens.\n *\n * @param token - The JWT token string to parse\n * @returns The parsed JWT payload or null if parsing fails\n */\nexport function parseJwtPayload<T extends JwtPayload>(token: string): T | null {\n try {\n const parts = token.split('.');\n if (parts.length !== 3) {\n return null;\n }\n\n const base64Url = parts[1];\n const base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');\n\n // Add padding if needed\n const paddedBase64 = base64.padEnd(\n base64.length + ((4 - (base64.length % 4)) % 4),\n '=',\n );\n\n const jsonPayload = decodeURIComponent(\n atob(paddedBase64)\n .split('')\n .map(function(c) {\n return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2);\n })\n .join(''),\n );\n return JSON.parse(jsonPayload) as T;\n } catch (error) {\n // Avoid exposing sensitive information in error logs\n console.error('Failed to parse JWT token', error);\n return null;\n }\n}\n\n/**\n * Checks if a JWT token is expired based on its expiration time (exp claim).\n *\n * This function determines if a JWT token has expired by comparing its exp claim\n * with the current time. If the token is a string, it will be parsed first.\n * Tokens without an exp claim are considered not expired.\n *\n * The early period parameter allows for early token expiration, which is useful\n * for triggering token refresh before the token actually expires. This helps\n * avoid race conditions where a token expires between the time it is checked and\n * the time it is used.\n *\n * @param token - The JWT token to check, either as a string or as a JwtPayload object\n * @param earlyPeriod - The time in seconds before actual expiration when the token should be considered expired (default: 0)\n * @returns true if the token is expired (or will expire within the early period) or cannot be parsed, false otherwise\n */\nexport function isTokenExpired(\n token: string | CoSecJwtPayload,\n earlyPeriod: number = 0,\n): boolean {\n const payload = typeof token === 'string' ? parseJwtPayload(token) : token;\n if (!payload) {\n return true;\n }\n\n const expAt = payload.exp;\n if (!expAt) {\n return false;\n }\n\n const now = Date.now() / 1000;\n return now > expAt - earlyPeriod;\n}\n","/*\n * Copyright [2021-present] [ahoo wang <ahoowang@qq.com> (https://github.com/Ahoo-Wang)].\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * http://www.apache.org/licenses/LICENSE-2.0\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { getStorage } from './inMemoryStorage';\nimport { type CompositeToken } from './tokenRefresher';\n\nexport const DEFAULT_COSEC_TOKEN_KEY = 'cosec-token';\n\n/**\n * Storage class for managing access and refresh tokens.\n */\nexport class TokenStorage {\n private readonly tokenKey: string;\n private storage: Storage;\n\n constructor(\n tokenKey: string = DEFAULT_COSEC_TOKEN_KEY,\n storage: Storage = getStorage(),\n ) {\n this.tokenKey = tokenKey;\n this.storage = storage;\n }\n\n /**\n * Get the current access token.\n *\n * @returns The current composite token or null if not set\n */\n get(): CompositeToken | null {\n const tokenStr = this.storage.getItem(this.tokenKey);\n if (!tokenStr) {\n return null;\n }\n\n try {\n return JSON.parse(tokenStr);\n } catch (error) {\n console.warn('Failed to get token from storage:', error);\n this.clear();\n return null;\n }\n }\n\n /**\n * Store a composite token.\n *\n * @param token - The composite token to store\n */\n set(token: CompositeToken): void {\n const tokenStr = JSON.stringify(token);\n this.storage.setItem(this.tokenKey, tokenStr);\n }\n\n /**\n * Clear all tokens.\n */\n clear(): void {\n this.storage.removeItem(this.tokenKey);\n }\n}\n"],"names":["_CoSecHeaders","CoSecHeaders","_ResponseCodes","ResponseCodes","AuthorizeResults","NanoIdGenerator","nanoid","idGenerator","COSEC_REQUEST_INTERCEPTOR_NAME","COSEC_REQUEST_INTERCEPTOR_ORDER","REQUEST_BODY_INTERCEPTOR_ORDER","CoSecRequestInterceptor","options","exchange","requestId","deviceId","token","requestHeaders","COSEC_RESPONSE_INTERCEPTOR_NAME","COSEC_RESPONSE_INTERCEPTOR_ORDER","CoSecResponseInterceptor","response","currentToken","newToken","error","InMemoryStorage","key","value","index","getStorage","DEFAULT_COSEC_DEVICE_ID_KEY","DeviceIdStorage","deviceIdKey","storage","parseJwtPayload","parts","base64","paddedBase64","jsonPayload","c","isTokenExpired","earlyPeriod","payload","expAt","DEFAULT_COSEC_TOKEN_KEY","TokenStorage","tokenKey","tokenStr"],"mappings":"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"}
1
+ {"version":3,"file":"index.umd.js","sources":["../src/types.ts","../src/idGenerator.ts","../src/cosecRequestInterceptor.ts","../src/cosecResponseInterceptor.ts","../src/inMemoryStorage.ts","../src/deviceIdStorage.ts","../src/jwts.ts","../src/tokenStorage.ts"],"sourcesContent":["/*\n * Copyright [2021-present] [ahoo wang <ahoowang@qq.com> (https://github.com/Ahoo-Wang)].\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * http://www.apache.org/licenses/LICENSE-2.0\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { DeviceIdStorage } from './deviceIdStorage';\nimport { TokenStorage } from './tokenStorage';\nimport { TokenRefresher } from './tokenRefresher';\n\n/**\n * CoSec HTTP headers enumeration.\n */\nexport class CoSecHeaders {\n static readonly DEVICE_ID = 'CoSec-Device-Id';\n static readonly APP_ID = 'CoSec-App-Id';\n static readonly AUTHORIZATION = 'Authorization';\n static readonly REQUEST_ID = 'CoSec-Request-Id';\n}\n\nexport class ResponseCodes {\n static readonly UNAUTHORIZED = 401;\n}\n\n/**\n * CoSec options interface.\n */\nexport interface CoSecOptions {\n /**\n * Application ID to be sent in the CoSec-App-Id header.\n */\n appId: string;\n\n /**\n * Device ID storage instance.\n */\n deviceIdStorage: DeviceIdStorage;\n\n /**\n * Token storage instance.\n */\n tokenStorage: TokenStorage;\n\n /**\n * Token refresher function.\n *\n * Takes a CompositeToken and returns a Promise that resolves to a new CompositeToken.\n */\n tokenRefresher: TokenRefresher;\n}\n\n/**\n * Authorization result interface.\n */\nexport interface AuthorizeResult {\n authorized: boolean;\n reason: string;\n}\n\n/**\n * Authorization result constants.\n */\nexport const AuthorizeResults = {\n ALLOW: { authorized: true, reason: 'Allow' },\n EXPLICIT_DENY: { authorized: false, reason: 'Explicit Deny' },\n IMPLICIT_DENY: { authorized: false, reason: 'Implicit Deny' },\n TOKEN_EXPIRED: { authorized: false, reason: 'Token Expired' },\n TOO_MANY_REQUESTS: { authorized: false, reason: 'Too Many Requests' },\n};\n","/*\n * Copyright [2021-present] [ahoo wang <ahoowang@qq.com> (https://github.com/Ahoo-Wang)].\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * http://www.apache.org/licenses/LICENSE-2.0\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { nanoid } from 'nanoid';\n\nexport interface IdGenerator {\n generateId(): string;\n}\n\n/**\n * Nano ID implementation of IdGenerator.\n * Generates unique request IDs using Nano ID.\n */\nexport class NanoIdGenerator implements IdGenerator {\n /**\n * Generate a unique request ID.\n *\n * @returns A unique request ID\n */\n generateId(): string {\n return nanoid();\n }\n}\n\nexport const idGenerator = new NanoIdGenerator();\n","/*\n * Copyright [2021-present] [ahoo wang <ahoowang@qq.com> (https://github.com/Ahoo-Wang)].\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * http://www.apache.org/licenses/LICENSE-2.0\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n FetchExchange,\n REQUEST_BODY_INTERCEPTOR_ORDER,\n type RequestInterceptor,\n} from '@ahoo-wang/fetcher';\nimport { CoSecHeaders, CoSecOptions } from './types';\nimport { idGenerator } from './idGenerator';\n\n/**\n * The name of the CoSecRequestInterceptor.\n */\nexport const COSEC_REQUEST_INTERCEPTOR_NAME = 'CoSecRequestInterceptor';\n\n/**\n * The order of the CoSecRequestInterceptor.\n * Set to REQUEST_BODY_INTERCEPTOR_ORDER + 1000 to ensure it runs after RequestBodyInterceptor.\n */\nexport const COSEC_REQUEST_INTERCEPTOR_ORDER =\n REQUEST_BODY_INTERCEPTOR_ORDER + 1000;\n\n/**\n * Interceptor that automatically adds CoSec authentication headers to requests.\n *\n * This interceptor adds the following headers to each request:\n * - CoSec-Device-Id: Device identifier (stored in localStorage or generated)\n * - CoSec-App-Id: Application identifier\n * - Authorization: Bearer token\n * - CoSec-Request-Id: Unique request identifier for each request\n *\n * @remarks\n * This interceptor runs after RequestBodyInterceptor but before FetchInterceptor.\n * The order is set to COSEC_REQUEST_INTERCEPTOR_ORDER to ensure it runs after\n * request body processing but before the actual HTTP request is made. This positioning\n * allows for proper authentication header addition after all request body transformations\n * are complete, ensuring that the final request is properly authenticated before\n * being sent over the network.\n */\nexport class CoSecRequestInterceptor implements RequestInterceptor {\n readonly name = COSEC_REQUEST_INTERCEPTOR_NAME;\n readonly order = COSEC_REQUEST_INTERCEPTOR_ORDER;\n private options: CoSecOptions;\n\n constructor(options: CoSecOptions) {\n this.options = options;\n }\n\n /**\n * Intercept requests to add CoSec authentication headers.\n *\n * This method adds the following headers to each request:\n * - CoSec-App-Id: The application identifier from the CoSec options\n * - CoSec-Device-Id: A unique device identifier, either retrieved from storage or generated\n * - CoSec-Request-Id: A unique identifier for this specific request\n * - Authorization: Bearer token if available in token storage\n *\n * @param exchange - The fetch exchange containing the request to process\n *\n * @remarks\n * This method runs after RequestBodyInterceptor but before FetchInterceptor.\n * It ensures that authentication headers are added to the request after all\n * body processing is complete. The positioning allows for proper authentication\n * header addition after all request body transformations are finished, ensuring\n * that the final request is properly authenticated before being sent over the network.\n * This execution order prevents authentication headers from being overwritten by\n * subsequent request processing interceptors.\n */\n intercept(exchange: FetchExchange) {\n const requestId = idGenerator.generateId();\n const deviceId = this.options.deviceIdStorage.getOrCreate();\n const token = this.options.tokenStorage.get();\n const requestHeaders = exchange.ensureRequestHeaders();\n requestHeaders[CoSecHeaders.APP_ID] = this.options.appId;\n requestHeaders[CoSecHeaders.DEVICE_ID] = deviceId;\n requestHeaders[CoSecHeaders.REQUEST_ID] = requestId;\n if (token && !requestHeaders[CoSecHeaders.AUTHORIZATION]) {\n requestHeaders[CoSecHeaders.AUTHORIZATION] =\n `Bearer ${token.accessToken}`;\n }\n }\n}\n","/*\n * Copyright [2021-present] [ahoo wang <ahoowang@qq.com> (https://github.com/Ahoo-Wang)].\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * http://www.apache.org/licenses/LICENSE-2.0\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { type CoSecOptions, ResponseCodes } from './types';\nimport { FetchExchange, type ResponseInterceptor } from '@ahoo-wang/fetcher';\nimport { CompositeToken } from './tokenRefresher';\n\n/**\n * The name of the CoSecResponseInterceptor.\n */\nexport const COSEC_RESPONSE_INTERCEPTOR_NAME = 'CoSecResponseInterceptor';\n\n/**\n * The order of the CoSecResponseInterceptor.\n * Set to a high negative value to ensure it runs early in the interceptor chain.\n */\nexport const COSEC_RESPONSE_INTERCEPTOR_ORDER = Number.MIN_SAFE_INTEGER + 1000;\n\n/**\n * CoSecResponseInterceptor is responsible for handling unauthorized responses (401)\n * by attempting to refresh the authentication token and retrying the original request.\n *\n * This interceptor:\n * 1. Checks if the response status is 401 (UNAUTHORIZED)\n * 2. If so, and if there's a current token, attempts to refresh it\n * 3. On successful refresh, stores the new token and retries the original request\n * 4. On refresh failure, clears stored tokens and propagates the error\n */\nexport class CoSecResponseInterceptor implements ResponseInterceptor {\n readonly name = COSEC_RESPONSE_INTERCEPTOR_NAME;\n readonly order = COSEC_RESPONSE_INTERCEPTOR_ORDER;\n private options: CoSecOptions;\n private refreshInProgress?: Promise<CompositeToken>;\n\n /**\n * Creates a new CoSecResponseInterceptor instance.\n * @param options - The CoSec configuration options including token storage and refresher\n */\n constructor(options: CoSecOptions) {\n this.options = options;\n }\n\n private async refresh(currentToken: CompositeToken): Promise<CompositeToken> {\n if (this.refreshInProgress) {\n return this.refreshInProgress;\n }\n\n this.refreshInProgress = this.options.tokenRefresher.refresh(currentToken)\n .then(newToken => {\n this.options.tokenStorage.set(newToken);\n return newToken;\n })\n .catch(error => {\n this.options.tokenStorage.clear();\n throw error;\n })\n .finally(() => {\n this.refreshInProgress = undefined;\n });\n\n return this.refreshInProgress;\n }\n\n /**\n * Intercepts the response and handles unauthorized responses by refreshing tokens.\n * @param exchange - The fetch exchange containing request and response information\n */\n async intercept(exchange: FetchExchange): Promise<void> {\n const response = exchange.response;\n // If there's no response, nothing to intercept\n if (!response) {\n return;\n }\n\n // Only handle unauthorized responses (401)\n if (response.status !== ResponseCodes.UNAUTHORIZED) {\n return;\n }\n\n // Get the current token from storage\n const currentToken = this.options.tokenStorage.get();\n // If there's no current token, we can't refresh it\n if (!currentToken) {\n return;\n }\n\n try {\n // Attempt to refresh the token\n await this.refresh(currentToken);\n // Retry the original request with the new token\n await exchange.fetcher.request(exchange.request);\n } catch (error) {\n // If token refresh fails, clear stored tokens and re-throw the error\n this.options.tokenStorage.clear();\n throw error;\n }\n }\n}\n","/*\n * Copyright [2021-present] [ahoo wang <ahoowang@qq.com> (https://github.com/Ahoo-Wang)].\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * http://www.apache.org/licenses/LICENSE-2.0\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * In-memory storage fallback for environments without localStorage.\n */\nexport class InMemoryStorage implements Storage {\n private store: Map<string, string> = new Map();\n\n get length(): number {\n return this.store.size;\n }\n\n clear(): void {\n this.store.clear();\n }\n\n getItem(key: string): string | null {\n const value = this.store.get(key);\n return value !== undefined ? value : null;\n }\n\n key(index: number): string | null {\n const keys = Array.from(this.store.keys());\n return keys[index] || null;\n }\n\n removeItem(key: string): void {\n if (this.store.has(key)) {\n this.store.delete(key);\n }\n }\n\n setItem(key: string, value: string): void {\n this.store.set(key, value);\n }\n}\n\nexport function getStorage(): Storage {\n if (typeof window !== 'undefined' && window.localStorage) {\n return window.localStorage;\n } else {\n // Use in-memory storage as fallback\n return new InMemoryStorage();\n }\n}\n","/*\n * Copyright [2021-present] [ahoo wang <ahoowang@qq.com> (https://github.com/Ahoo-Wang)].\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * http://www.apache.org/licenses/LICENSE-2.0\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { getStorage } from './inMemoryStorage';\nimport { idGenerator } from './idGenerator';\n\nexport const DEFAULT_COSEC_DEVICE_ID_KEY = 'cosec-device-id';\n\n/**\n * Storage class for managing device identifiers.\n */\nexport class DeviceIdStorage {\n private readonly deviceIdKey: string;\n private storage: Storage;\n\n constructor(\n deviceIdKey: string = DEFAULT_COSEC_DEVICE_ID_KEY,\n storage: Storage = getStorage(),\n ) {\n this.deviceIdKey = deviceIdKey;\n this.storage = storage;\n }\n\n /**\n * Get the current device ID.\n *\n * @returns The current device ID or null if not set\n */\n get(): string | null {\n return this.storage.getItem(this.deviceIdKey);\n }\n\n /**\n * Set a device ID.\n *\n * @param deviceId - The device ID to set\n */\n set(deviceId: string): void {\n this.storage.setItem(this.deviceIdKey, deviceId);\n }\n\n /**\n * Generate a new device ID.\n *\n * @returns A newly generated device ID\n */\n generateDeviceId(): string {\n return idGenerator.generateId();\n }\n\n /**\n * Get or create a device ID.\n *\n * @returns The existing device ID if available, otherwise a newly generated one\n */\n getOrCreate(): string {\n // Try to get existing device ID from storage\n let deviceId = this.get();\n if (!deviceId) {\n // Generate a new device ID and store it\n deviceId = this.generateDeviceId();\n this.set(deviceId);\n }\n\n return deviceId;\n }\n\n /**\n * Clear the stored device ID.\n */\n clear(): void {\n this.storage.removeItem(this.deviceIdKey);\n }\n}\n","/*\n * Copyright [2021-present] [ahoo wang <ahoowang@qq.com> (https://github.com/Ahoo-Wang)].\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * http://www.apache.org/licenses/LICENSE-2.0\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Interface representing a JWT payload as defined in RFC 7519.\n * Contains standard JWT claims as well as custom properties.\n */\nexport interface JwtPayload {\n /**\n * JWT ID - provides a unique identifier for the JWT.\n */\n jti?: string;\n /**\n * Subject - identifies the principal that is the subject of the JWT.\n */\n sub?: string;\n /**\n * Issuer - identifies the principal that issued the JWT.\n */\n iss?: string;\n /**\n * Audience - identifies the recipients that the JWT is intended for.\n * Can be a single string or an array of strings.\n */\n aud?: string | string[];\n /**\n * Expiration Time - identifies the expiration time on or after which the JWT MUST NOT be accepted for processing.\n * Represented as NumericDate (seconds since Unix epoch).\n */\n exp?: number;\n /**\n * Not Before - identifies the time before which the JWT MUST NOT be accepted for processing.\n * Represented as NumericDate (seconds since Unix epoch).\n */\n nbf?: number;\n /**\n * Issued At - identifies the time at which the JWT was issued.\n * Represented as NumericDate (seconds since Unix epoch).\n */\n iat?: number;\n\n /**\n * Allows additional custom properties to be included in the payload.\n */\n [key: string]: any;\n}\n\n/**\n * Interface representing a JWT payload with CoSec-specific extensions.\n * Extends the standard JwtPayload interface with additional CoSec-specific properties.\n */\nexport interface CoSecJwtPayload extends JwtPayload {\n /**\n * Tenant identifier - identifies the tenant scope for the JWT.\n */\n tenantId?: string;\n /**\n * Policies - array of policy identifiers associated with the JWT.\n * These are security policies defined internally by Cosec.\n */\n policies?: string[];\n /**\n * Roles - array of role identifiers associated with the JWT.\n * Role IDs indicate what roles the token belongs to.\n */\n roles?: string[];\n /**\n * Attributes - custom key-value pairs providing additional information about the JWT.\n */\n attributes?: Record<string, any>;\n}\n\n/**\n * Parses a JWT token and extracts its payload.\n *\n * This function decodes the payload part of a JWT token, handling Base64URL decoding\n * and JSON parsing. It validates the token structure and returns null for invalid tokens.\n *\n * @param token - The JWT token string to parse\n * @returns The parsed JWT payload or null if parsing fails\n */\nexport function parseJwtPayload<T extends JwtPayload>(token: string): T | null {\n try {\n const parts = token.split('.');\n if (parts.length !== 3) {\n return null;\n }\n\n const base64Url = parts[1];\n const base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');\n\n // Add padding if needed\n const paddedBase64 = base64.padEnd(\n base64.length + ((4 - (base64.length % 4)) % 4),\n '=',\n );\n\n const jsonPayload = decodeURIComponent(\n atob(paddedBase64)\n .split('')\n .map(function(c) {\n return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2);\n })\n .join(''),\n );\n return JSON.parse(jsonPayload) as T;\n } catch (error) {\n // Avoid exposing sensitive information in error logs\n console.error('Failed to parse JWT token', error);\n return null;\n }\n}\n\n/**\n * Checks if a JWT token is expired based on its expiration time (exp claim).\n *\n * This function determines if a JWT token has expired by comparing its exp claim\n * with the current time. If the token is a string, it will be parsed first.\n * Tokens without an exp claim are considered not expired.\n *\n * The early period parameter allows for early token expiration, which is useful\n * for triggering token refresh before the token actually expires. This helps\n * avoid race conditions where a token expires between the time it is checked and\n * the time it is used.\n *\n * @param token - The JWT token to check, either as a string or as a JwtPayload object\n * @param earlyPeriod - The time in seconds before actual expiration when the token should be considered expired (default: 0)\n * @returns true if the token is expired (or will expire within the early period) or cannot be parsed, false otherwise\n */\nexport function isTokenExpired(\n token: string | CoSecJwtPayload,\n earlyPeriod: number = 0,\n): boolean {\n const payload = typeof token === 'string' ? parseJwtPayload(token) : token;\n if (!payload) {\n return true;\n }\n\n const expAt = payload.exp;\n if (!expAt) {\n return false;\n }\n\n const now = Date.now() / 1000;\n return now > expAt - earlyPeriod;\n}\n","/*\n * Copyright [2021-present] [ahoo wang <ahoowang@qq.com> (https://github.com/Ahoo-Wang)].\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * http://www.apache.org/licenses/LICENSE-2.0\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { getStorage } from './inMemoryStorage';\nimport { type CompositeToken } from './tokenRefresher';\n\nexport const DEFAULT_COSEC_TOKEN_KEY = 'cosec-token';\n\n/**\n * Storage class for managing access and refresh tokens.\n */\nexport class TokenStorage {\n private readonly tokenKey: string;\n private storage: Storage;\n\n constructor(\n tokenKey: string = DEFAULT_COSEC_TOKEN_KEY,\n storage: Storage = getStorage(),\n ) {\n this.tokenKey = tokenKey;\n this.storage = storage;\n }\n\n /**\n * Get the current access token.\n *\n * @returns The current composite token or null if not set\n */\n get(): CompositeToken | null {\n const tokenStr = this.storage.getItem(this.tokenKey);\n if (!tokenStr) {\n return null;\n }\n\n try {\n return JSON.parse(tokenStr);\n } catch (error) {\n console.warn('Failed to get token from storage:', error);\n this.clear();\n return null;\n }\n }\n\n /**\n * Store a composite token.\n *\n * @param token - The composite token to store\n */\n set(token: CompositeToken): void {\n const tokenStr = JSON.stringify(token);\n this.storage.setItem(this.tokenKey, tokenStr);\n }\n\n /**\n * Clear all tokens.\n */\n clear(): void {\n this.storage.removeItem(this.tokenKey);\n }\n}\n"],"names":["_CoSecHeaders","CoSecHeaders","_ResponseCodes","ResponseCodes","AuthorizeResults","NanoIdGenerator","nanoid","idGenerator","COSEC_REQUEST_INTERCEPTOR_NAME","COSEC_REQUEST_INTERCEPTOR_ORDER","REQUEST_BODY_INTERCEPTOR_ORDER","CoSecRequestInterceptor","options","exchange","requestId","deviceId","token","requestHeaders","COSEC_RESPONSE_INTERCEPTOR_NAME","COSEC_RESPONSE_INTERCEPTOR_ORDER","CoSecResponseInterceptor","currentToken","newToken","error","response","InMemoryStorage","key","value","index","getStorage","DEFAULT_COSEC_DEVICE_ID_KEY","DeviceIdStorage","deviceIdKey","storage","parseJwtPayload","parts","base64","paddedBase64","jsonPayload","c","isTokenExpired","earlyPeriod","payload","expAt","DEFAULT_COSEC_TOKEN_KEY","TokenStorage","tokenKey","tokenStr"],"mappings":"0UAoBO,MAAMA,EAAN,MAAMA,CAAa,CAK1B,EAJEA,EAAgB,UAAY,kBAC5BA,EAAgB,OAAS,eACzBA,EAAgB,cAAgB,gBAChCA,EAAgB,WAAa,mBAJxB,IAAMC,EAAND,EAOA,MAAME,EAAN,MAAMA,CAAc,CAE3B,EADEA,EAAgB,aAAe,IAD1B,IAAMC,EAAND,EA0CA,MAAME,EAAmB,CAC9B,MAAO,CAAE,WAAY,GAAM,OAAQ,OAAA,EACnC,cAAe,CAAE,WAAY,GAAO,OAAQ,eAAA,EAC5C,cAAe,CAAE,WAAY,GAAO,OAAQ,eAAA,EAC5C,cAAe,CAAE,WAAY,GAAO,OAAQ,eAAA,EAC5C,kBAAmB,CAAE,WAAY,GAAO,OAAQ,mBAAA,CAClD,ECpDO,MAAMC,CAAuC,CAMlD,YAAqB,CACnB,OAAOC,SAAA,CACT,CACF,CAEO,MAAMC,EAAc,IAAIF,ECVlBG,EAAiC,0BAMjCC,EACXC,EAAAA,+BAAiC,IAmB5B,MAAMC,CAAsD,CAKjE,YAAYC,EAAuB,CAJnC,KAAS,KAAOJ,EAChB,KAAS,MAAQC,EAIf,KAAK,QAAUG,CACjB,CAsBA,UAAUC,EAAyB,CACjC,MAAMC,EAAYP,EAAY,WAAA,EACxBQ,EAAW,KAAK,QAAQ,gBAAgB,YAAA,EACxCC,EAAQ,KAAK,QAAQ,aAAa,IAAA,EAClCC,EAAiBJ,EAAS,qBAAA,EAChCI,EAAehB,EAAa,MAAM,EAAI,KAAK,QAAQ,MACnDgB,EAAehB,EAAa,SAAS,EAAIc,EACzCE,EAAehB,EAAa,UAAU,EAAIa,EACtCE,GAAS,CAACC,EAAehB,EAAa,aAAa,IACrDgB,EAAehB,EAAa,aAAa,EACvC,UAAUe,EAAM,WAAW,GAEjC,CACF,CCxEO,MAAME,EAAkC,2BAMlCC,EAAmC,OAAO,iBAAmB,IAYnE,MAAMC,CAAwD,CAUnE,YAAYR,EAAuB,CATnC,KAAS,KAAOM,EAChB,KAAS,MAAQC,EASf,KAAK,QAAUP,CACjB,CAEA,MAAc,QAAQS,EAAuD,CAC3E,OAAI,KAAK,kBACA,KAAK,mBAGd,KAAK,kBAAoB,KAAK,QAAQ,eAAe,QAAQA,CAAY,EACtE,KAAKC,IACJ,KAAK,QAAQ,aAAa,IAAIA,CAAQ,EAC/BA,EACR,EACA,MAAMC,GAAS,CACd,WAAK,QAAQ,aAAa,MAAA,EACpBA,CACR,CAAC,EACA,QAAQ,IAAM,CACb,KAAK,kBAAoB,MAC3B,CAAC,EAEI,KAAK,kBACd,CAMA,MAAM,UAAUV,EAAwC,CACtD,MAAMW,EAAWX,EAAS,SAO1B,GALI,CAACW,GAKDA,EAAS,SAAWrB,EAAc,aACpC,OAIF,MAAMkB,EAAe,KAAK,QAAQ,aAAa,IAAA,EAE/C,GAAKA,EAIL,GAAI,CAEF,MAAM,KAAK,QAAQA,CAAY,EAE/B,MAAMR,EAAS,QAAQ,QAAQA,EAAS,OAAO,CACjD,OAASU,EAAO,CAEd,WAAK,QAAQ,aAAa,MAAA,EACpBA,CACR,CACF,CACF,CC3FO,MAAME,CAAmC,CAAzC,aAAA,CACL,KAAQ,UAAiC,GAAI,CAE7C,IAAI,QAAiB,CACnB,OAAO,KAAK,MAAM,IACpB,CAEA,OAAc,CACZ,KAAK,MAAM,MAAA,CACb,CAEA,QAAQC,EAA4B,CAClC,MAAMC,EAAQ,KAAK,MAAM,IAAID,CAAG,EAChC,OAAOC,IAAU,OAAYA,EAAQ,IACvC,CAEA,IAAIC,EAA8B,CAEhC,OADa,MAAM,KAAK,KAAK,MAAM,MAAM,EAC7BA,CAAK,GAAK,IACxB,CAEA,WAAWF,EAAmB,CACxB,KAAK,MAAM,IAAIA,CAAG,GACpB,KAAK,MAAM,OAAOA,CAAG,CAEzB,CAEA,QAAQA,EAAaC,EAAqB,CACxC,KAAK,MAAM,IAAID,EAAKC,CAAK,CAC3B,CACF,CAEO,SAASE,GAAsB,CACpC,OAAI,OAAO,OAAW,KAAe,OAAO,aACnC,OAAO,aAGP,IAAIJ,CAEf,CCvCO,MAAMK,EAA8B,kBAKpC,MAAMC,CAAgB,CAI3B,YACEC,EAAsBF,EACtBG,EAAmBJ,IACnB,CACA,KAAK,YAAcG,EACnB,KAAK,QAAUC,CACjB,CAOA,KAAqB,CACnB,OAAO,KAAK,QAAQ,QAAQ,KAAK,WAAW,CAC9C,CAOA,IAAIlB,EAAwB,CAC1B,KAAK,QAAQ,QAAQ,KAAK,YAAaA,CAAQ,CACjD,CAOA,kBAA2B,CACzB,OAAOR,EAAY,WAAA,CACrB,CAOA,aAAsB,CAEpB,IAAIQ,EAAW,KAAK,IAAA,EACpB,OAAKA,IAEHA,EAAW,KAAK,iBAAA,EAChB,KAAK,IAAIA,CAAQ,GAGZA,CACT,CAKA,OAAc,CACZ,KAAK,QAAQ,WAAW,KAAK,WAAW,CAC1C,CACF,CCOO,SAASmB,EAAsClB,EAAyB,CAC7E,GAAI,CACF,MAAMmB,EAAQnB,EAAM,MAAM,GAAG,EAC7B,GAAImB,EAAM,SAAW,EACnB,OAAO,KAIT,MAAMC,EADYD,EAAM,CAAC,EACA,QAAQ,KAAM,GAAG,EAAE,QAAQ,KAAM,GAAG,EAGvDE,EAAeD,EAAO,OAC1BA,EAAO,QAAW,EAAKA,EAAO,OAAS,GAAM,EAC7C,GAAA,EAGIE,EAAc,mBAClB,KAAKD,CAAY,EACd,MAAM,EAAE,EACR,IAAI,SAASE,EAAG,CACf,MAAO,KAAO,KAAOA,EAAE,WAAW,CAAC,EAAE,SAAS,EAAE,GAAG,MAAM,EAAE,CAC7D,CAAC,EACA,KAAK,EAAE,CAAA,EAEZ,OAAO,KAAK,MAAMD,CAAW,CAC/B,OAASf,EAAO,CAEd,eAAQ,MAAM,4BAA6BA,CAAK,EACzC,IACT,CACF,CAkBO,SAASiB,EACdxB,EACAyB,EAAsB,EACb,CACT,MAAMC,EAAU,OAAO1B,GAAU,SAAWkB,EAAgBlB,CAAK,EAAIA,EACrE,GAAI,CAAC0B,EACH,MAAO,GAGT,MAAMC,EAAQD,EAAQ,IACtB,OAAKC,EAIO,KAAK,IAAA,EAAQ,IACZA,EAAQF,EAJZ,EAKX,CC1IO,MAAMG,EAA0B,cAKhC,MAAMC,CAAa,CAIxB,YACEC,EAAmBF,EACnBX,EAAmBJ,IACnB,CACA,KAAK,SAAWiB,EAChB,KAAK,QAAUb,CACjB,CAOA,KAA6B,CAC3B,MAAMc,EAAW,KAAK,QAAQ,QAAQ,KAAK,QAAQ,EACnD,GAAI,CAACA,EACH,OAAO,KAGT,GAAI,CACF,OAAO,KAAK,MAAMA,CAAQ,CAC5B,OAASxB,EAAO,CACd,eAAQ,KAAK,oCAAqCA,CAAK,EACvD,KAAK,MAAA,EACE,IACT,CACF,CAOA,IAAIP,EAA6B,CAC/B,MAAM+B,EAAW,KAAK,UAAU/B,CAAK,EACrC,KAAK,QAAQ,QAAQ,KAAK,SAAU+B,CAAQ,CAC9C,CAKA,OAAc,CACZ,KAAK,QAAQ,WAAW,KAAK,QAAQ,CACvC,CACF"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ahoo-wang/fetcher-cosec",
3
- "version": "1.5.2",
3
+ "version": "1.5.3",
4
4
  "description": "CoSec authentication integration for Fetcher HTTP client with enterprise-grade security features. Provides automatic token management, device ID persistence, and request tracking.",
5
5
  "keywords": [
6
6
  "fetch",
@@ -40,7 +40,7 @@
40
40
  ],
41
41
  "dependencies": {
42
42
  "nanoid": "^5.1.5",
43
- "@ahoo-wang/fetcher": "1.5.2"
43
+ "@ahoo-wang/fetcher": "1.5.3"
44
44
  },
45
45
  "devDependencies": {
46
46
  "@vitest/coverage-v8": "^3.2.4",