@ahoo-wang/fetcher-cosec 1.5.8 → 1.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -9,6 +9,7 @@ export declare const COSEC_REQUEST_INTERCEPTOR_NAME = "CoSecRequestInterceptor";
9
9
  * Set to REQUEST_BODY_INTERCEPTOR_ORDER + 1000 to ensure it runs after RequestBodyInterceptor.
10
10
  */
11
11
  export declare const COSEC_REQUEST_INTERCEPTOR_ORDER: number;
12
+ export declare const IGNORE_REFRESH_TOKEN_ATTRIBUTE_KEY = "Ignore-Refresh-Token";
12
13
  /**
13
14
  * Interceptor that automatically adds CoSec authentication headers to requests.
14
15
  *
@@ -30,6 +31,10 @@ export declare class CoSecRequestInterceptor implements RequestInterceptor {
30
31
  readonly name = "CoSecRequestInterceptor";
31
32
  readonly order: number;
32
33
  private options;
34
+ /**
35
+ * Creates a new CoSecRequestInterceptor instance.
36
+ * @param options - The CoSec configuration options including appId, deviceIdStorage, and tokenManager
37
+ */
33
38
  constructor(options: CoSecOptions);
34
39
  /**
35
40
  * Intercept requests to add CoSec authentication headers.
@@ -50,7 +55,10 @@ export declare class CoSecRequestInterceptor implements RequestInterceptor {
50
55
  * that the final request is properly authenticated before being sent over the network.
51
56
  * This execution order prevents authentication headers from being overwritten by
52
57
  * subsequent request processing interceptors.
58
+ *
59
+ * The method also handles token refreshing when the current token is expired but still refreshable.
60
+ * It will attempt to refresh the token before adding the Authorization header to the request.
53
61
  */
54
- intercept(exchange: FetchExchange): void;
62
+ intercept(exchange: FetchExchange): Promise<void>;
55
63
  }
56
64
  //# sourceMappingURL=cosecRequestInterceptor.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"cosecRequestInterceptor.d.ts","sourceRoot":"","sources":["../src/cosecRequestInterceptor.ts"],"names":[],"mappings":"AAaA,OAAO,EACL,aAAa,EAEb,KAAK,kBAAkB,EACxB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAgB,YAAY,EAAE,MAAM,SAAS,CAAC;AAGrD;;GAEG;AACH,eAAO,MAAM,8BAA8B,4BAA4B,CAAC;AAExE;;;GAGG;AACH,eAAO,MAAM,+BAA+B,QACL,CAAC;AAExC;;;;;;;;;;;;;;;;GAgBG;AACH,qBAAa,uBAAwB,YAAW,kBAAkB;IAChE,QAAQ,CAAC,IAAI,6BAAkC;IAC/C,QAAQ,CAAC,KAAK,SAAmC;IACjD,OAAO,CAAC,OAAO,CAAe;gBAElB,OAAO,EAAE,YAAY;IAIjC;;;;;;;;;;;;;;;;;;;OAmBG;IACH,SAAS,CAAC,QAAQ,EAAE,aAAa;CAalC"}
1
+ {"version":3,"file":"cosecRequestInterceptor.d.ts","sourceRoot":"","sources":["../src/cosecRequestInterceptor.ts"],"names":[],"mappings":"AAaA,OAAO,EACL,aAAa,EAEb,KAAK,kBAAkB,EACxB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAgB,YAAY,EAAE,MAAM,SAAS,CAAC;AAGrD;;GAEG;AACH,eAAO,MAAM,8BAA8B,4BAA4B,CAAC;AAExE;;;GAGG;AACH,eAAO,MAAM,+BAA+B,QACL,CAAC;AAExC,eAAO,MAAM,kCAAkC,yBAAyB,CAAC;AAEzE;;;;;;;;;;;;;;;;GAgBG;AACH,qBAAa,uBAAwB,YAAW,kBAAkB;IAChE,QAAQ,CAAC,IAAI,6BAAkC;IAC/C,QAAQ,CAAC,KAAK,SAAmC;IACjD,OAAO,CAAC,OAAO,CAAe;IAE9B;;;OAGG;gBACS,OAAO,EAAE,YAAY;IAIjC;;;;;;;;;;;;;;;;;;;;;;OAsBG;IACG,SAAS,CAAC,QAAQ,EAAE,aAAa;CAoCxC"}
@@ -23,13 +23,11 @@ export declare class CoSecResponseInterceptor implements ResponseInterceptor {
23
23
  readonly name = "CoSecResponseInterceptor";
24
24
  readonly order: number;
25
25
  private options;
26
- private refreshInProgress?;
27
26
  /**
28
27
  * Creates a new CoSecResponseInterceptor instance.
29
28
  * @param options - The CoSec configuration options including token storage and refresher
30
29
  */
31
30
  constructor(options: CoSecOptions);
32
- private refresh;
33
31
  /**
34
32
  * Intercepts the response and handles unauthorized responses by refreshing tokens.
35
33
  * @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;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"}
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;CAyBxD"}
package/dist/index.es.js CHANGED
@@ -1,9 +1,9 @@
1
- import { REQUEST_BODY_INTERCEPTOR_ORDER as R } from "@ahoo-wang/fetcher";
2
- import { nanoid as k } from "nanoid";
1
+ import { REQUEST_BODY_INTERCEPTOR_ORDER as k, ResultExtractors as y } from "@ahoo-wang/fetcher";
2
+ import { nanoid as m } from "nanoid";
3
3
  function g() {
4
4
  return typeof window < "u";
5
5
  }
6
- class m {
6
+ class O {
7
7
  constructor() {
8
8
  this.store = /* @__PURE__ */ new Map(), this.listeners = /* @__PURE__ */ new Set();
9
9
  }
@@ -79,7 +79,7 @@ class m {
79
79
  });
80
80
  }
81
81
  }
82
- class y {
82
+ class _ {
83
83
  /**
84
84
  * Creates a new BrowserListenableStorage instance.
85
85
  * @param storage - The native Storage object to wrap (e.g., localStorage or sessionStorage)
@@ -142,8 +142,8 @@ class y {
142
142
  this.storage.setItem(e, t);
143
143
  }
144
144
  }
145
- const u = "storage", c = () => g() ? new y(window.localStorage) : new m();
146
- class O {
145
+ const u = "storage", h = () => g() ? new _(window.localStorage) : new O();
146
+ class N {
147
147
  /**
148
148
  * Serializes a value to a JSON string
149
149
  * @param value The value to serialize
@@ -172,7 +172,7 @@ class O {
172
172
  return JSON.parse(e);
173
173
  }
174
174
  }
175
- class f {
175
+ class E {
176
176
  /**
177
177
  * Returns the value as-is without serialization
178
178
  * @param value The value to pass through
@@ -201,11 +201,11 @@ class f {
201
201
  return e;
202
202
  }
203
203
  }
204
- const b = new O(), C = new f();
205
- function N() {
204
+ const x = new N(), C = new E();
205
+ function z() {
206
206
  return C;
207
207
  }
208
- class I {
208
+ class f {
209
209
  /**
210
210
  * Creates a new KeyStorage instance
211
211
  * @param options Configuration options for the storage
@@ -213,7 +213,7 @@ class I {
213
213
  constructor(e) {
214
214
  this.cacheValue = null, this.listener = (t) => {
215
215
  t.key === this.key && (this.cacheValue = null, t.newValue && this.refreshCache(t.newValue));
216
- }, this.key = e.key, this.serializer = e.serializer ?? N(), this.storage = e.storage ?? c(), this.storage.addListener(this.listener);
216
+ }, this.key = e.key, this.serializer = e.serializer ?? z(), this.storage = e.storage ?? h(), this.storage.addListener(this.listener);
217
217
  }
218
218
  /**
219
219
  * Refreshes the cached value by deserializing the provided string
@@ -250,35 +250,39 @@ class I {
250
250
  this.storage.removeItem(this.key), this.cacheValue = null;
251
251
  }
252
252
  }
253
- const n = class n {
253
+ const a = class a {
254
254
  };
255
- n.DEVICE_ID = "CoSec-Device-Id", n.APP_ID = "CoSec-App-Id", n.AUTHORIZATION = "Authorization", n.REQUEST_ID = "CoSec-Request-Id";
256
- let o = n;
255
+ a.DEVICE_ID = "CoSec-Device-Id", a.APP_ID = "CoSec-App-Id", a.AUTHORIZATION = "Authorization", a.REQUEST_ID = "CoSec-Request-Id";
256
+ let n = a;
257
257
  const l = class l {
258
258
  };
259
259
  l.UNAUTHORIZED = 401;
260
- let h = l;
261
- const x = {
260
+ let c = l;
261
+ const Y = {
262
262
  ALLOW: { authorized: !0, reason: "Allow" },
263
263
  EXPLICIT_DENY: { authorized: !1, reason: "Explicit Deny" },
264
264
  IMPLICIT_DENY: { authorized: !1, reason: "Implicit Deny" },
265
265
  TOKEN_EXPIRED: { authorized: !1, reason: "Token Expired" },
266
266
  TOO_MANY_REQUESTS: { authorized: !1, reason: "Too Many Requests" }
267
267
  };
268
- class _ {
268
+ class A {
269
269
  /**
270
270
  * Generate a unique request ID.
271
271
  *
272
272
  * @returns A unique request ID
273
273
  */
274
274
  generateId() {
275
- return k();
275
+ return m();
276
276
  }
277
277
  }
278
- const E = new _(), z = "CoSecRequestInterceptor", P = R + 1e3;
279
- class M {
278
+ const I = new A(), D = "CoSecRequestInterceptor", P = k + 1e3, p = "Ignore-Refresh-Token";
279
+ class q {
280
+ /**
281
+ * Creates a new CoSecRequestInterceptor instance.
282
+ * @param options - The CoSec configuration options including appId, deviceIdStorage, and tokenManager
283
+ */
280
284
  constructor(e) {
281
- this.name = z, this.order = P, this.options = e;
285
+ this.name = D, this.order = P, this.options = e;
282
286
  }
283
287
  /**
284
288
  * Intercept requests to add CoSec authentication headers.
@@ -299,29 +303,25 @@ class M {
299
303
  * that the final request is properly authenticated before being sent over the network.
300
304
  * This execution order prevents authentication headers from being overwritten by
301
305
  * subsequent request processing interceptors.
306
+ *
307
+ * The method also handles token refreshing when the current token is expired but still refreshable.
308
+ * It will attempt to refresh the token before adding the Authorization header to the request.
302
309
  */
303
- intercept(e) {
304
- const t = E.generateId(), s = this.options.deviceIdStorage.getOrCreate(), i = e.ensureRequestHeaders();
305
- i[o.APP_ID] = this.options.appId, i[o.DEVICE_ID] = s, i[o.REQUEST_ID] = t;
306
- const a = this.options.tokenStorage.get();
307
- a && !i[o.AUTHORIZATION] && (i[o.AUTHORIZATION] = `Bearer ${a.access.token}`);
310
+ async intercept(e) {
311
+ const t = I.generateId(), s = this.options.deviceIdStorage.getOrCreate(), i = e.ensureRequestHeaders();
312
+ i[n.APP_ID] = this.options.appId, i[n.DEVICE_ID] = s, i[n.REQUEST_ID] = t;
313
+ let o = this.options.tokenManager.currentToken;
314
+ !o || i[n.AUTHORIZATION] || (!e.attributes[p] && o.isRefreshNeeded && o.isRefreshable && await this.options.tokenManager.refresh(), o = this.options.tokenManager.currentToken, o && (i[n.AUTHORIZATION] = `Bearer ${o.access.token}`));
308
315
  }
309
316
  }
310
- const D = "CoSecResponseInterceptor", A = Number.MIN_SAFE_INTEGER + 1e3;
311
- class q {
317
+ const U = "CoSecResponseInterceptor", V = Number.MIN_SAFE_INTEGER + 1e3;
318
+ class K {
312
319
  /**
313
320
  * Creates a new CoSecResponseInterceptor instance.
314
321
  * @param options - The CoSec configuration options including token storage and refresher
315
322
  */
316
323
  constructor(e) {
317
- this.name = D, this.order = A, this.options = e;
318
- }
319
- async refresh(e) {
320
- return this.refreshInProgress ? this.refreshInProgress : (this.refreshInProgress = this.options.tokenRefresher.refresh(e).then((t) => (this.options.tokenStorage.setCompositeToken(t), t)).catch((t) => {
321
- throw this.options.tokenStorage.remove(), t;
322
- }).finally(() => {
323
- this.refreshInProgress = void 0;
324
- }), this.refreshInProgress);
324
+ this.name = U, this.order = V, this.options = e;
325
325
  }
326
326
  /**
327
327
  * Intercepts the response and handles unauthorized responses by refreshing tokens.
@@ -329,24 +329,21 @@ class q {
329
329
  */
330
330
  async intercept(e) {
331
331
  const t = e.response;
332
- if (!t || t.status !== h.UNAUTHORIZED)
333
- return;
334
- const s = this.options.tokenStorage.get();
335
- if (s)
332
+ if (t && t.status === c.UNAUTHORIZED && this.options.tokenManager.isRefreshable)
336
333
  try {
337
- await this.refresh(s.token), await e.fetcher.interceptors.exchange(e);
338
- } catch (i) {
339
- throw this.options.tokenStorage.remove(), i;
334
+ await this.options.tokenManager.refresh(), await e.fetcher.interceptors.exchange(e);
335
+ } catch (s) {
336
+ throw this.options.tokenManager.tokenStorage.remove(), s;
340
337
  }
341
338
  }
342
339
  }
343
- const U = "cosec-device-id";
344
- class Y extends I {
345
- constructor(e = U) {
340
+ const L = "cosec-device-id";
341
+ class B extends f {
342
+ constructor(e = L) {
346
343
  super({
347
344
  key: e,
348
- serializer: new f(),
349
- storage: c()
345
+ serializer: new E(),
346
+ storage: h()
350
347
  });
351
348
  }
352
349
  /**
@@ -355,7 +352,7 @@ class Y extends I {
355
352
  * @returns A newly generated device ID
356
353
  */
357
354
  generateDeviceId() {
358
- return E.generateId();
355
+ return I.generateId();
359
356
  }
360
357
  /**
361
358
  * Get or create a device ID.
@@ -367,7 +364,7 @@ class Y extends I {
367
364
  return e || (e = this.generateDeviceId(), this.set(e)), e;
368
365
  }
369
366
  }
370
- function p(r) {
367
+ function T(r) {
371
368
  try {
372
369
  const e = r.split(".");
373
370
  if (e.length !== 3)
@@ -375,18 +372,18 @@ function p(r) {
375
372
  const s = e[1].replace(/-/g, "+").replace(/_/g, "/"), i = s.padEnd(
376
373
  s.length + (4 - s.length % 4) % 4,
377
374
  "="
378
- ), a = decodeURIComponent(
379
- atob(i).split("").map(function(w) {
380
- return "%" + ("00" + w.charCodeAt(0).toString(16)).slice(-2);
375
+ ), o = decodeURIComponent(
376
+ atob(i).split("").map(function(S) {
377
+ return "%" + ("00" + S.charCodeAt(0).toString(16)).slice(-2);
381
378
  }).join("")
382
379
  );
383
- return JSON.parse(a);
380
+ return JSON.parse(o);
384
381
  } catch (e) {
385
382
  return console.error("Failed to parse JWT token", e), null;
386
383
  }
387
384
  }
388
- function V(r, e = 0) {
389
- const t = typeof r == "string" ? p(r) : r;
385
+ function b(r, e = 0) {
386
+ const t = typeof r == "string" ? T(r) : r;
390
387
  if (!t)
391
388
  return !0;
392
389
  const s = t.exp;
@@ -397,17 +394,17 @@ class d {
397
394
  * Creates a new JwtToken instance
398
395
  */
399
396
  constructor(e, t = 0) {
400
- this.token = e, this.earlyPeriod = t, this.payload = p(e);
397
+ this.token = e, this.earlyPeriod = t, this.payload = T(e);
401
398
  }
402
399
  /**
403
400
  * Checks if the token is expired
404
401
  * @returns true if the token is expired, false otherwise
405
402
  */
406
403
  get isExpired() {
407
- return V(this.payload, this.earlyPeriod);
404
+ return b(this.payload, this.earlyPeriod);
408
405
  }
409
406
  }
410
- class T {
407
+ class R {
411
408
  /**
412
409
  * Creates a new JwtCompositeToken instance
413
410
  */
@@ -429,7 +426,7 @@ class T {
429
426
  return !this.refresh.isExpired;
430
427
  }
431
428
  }
432
- class S {
429
+ class w {
433
430
  constructor(e = 0) {
434
431
  this.earlyPeriod = e;
435
432
  }
@@ -440,7 +437,7 @@ class S {
440
437
  */
441
438
  deserialize(e) {
442
439
  const t = JSON.parse(e);
443
- return new T(t, this.earlyPeriod);
440
+ return new R(t, this.earlyPeriod);
444
441
  }
445
442
  /**
446
443
  * Serializes a JwtCompositeToken to a JSON string
@@ -451,8 +448,8 @@ class S {
451
448
  return JSON.stringify(e.token);
452
449
  }
453
450
  }
454
- const j = new S();
455
- class Q {
451
+ const Q = new w();
452
+ class j {
456
453
  /**
457
454
  * Creates a new JwtTokenManager instance
458
455
  * @param tokenStorage The storage used to persist tokens
@@ -504,54 +501,84 @@ class Q {
504
501
  return this.currentToken ? this.currentToken.isRefreshable : !1;
505
502
  }
506
503
  }
507
- const L = "cosec-token";
508
- class B extends I {
509
- constructor(e = L, t = 0) {
504
+ class F {
505
+ /**
506
+ * Creates a new instance of CoSecTokenRefresher.
507
+ *
508
+ * @param options The configuration options for the token refresher including fetcher and endpoint
509
+ */
510
+ constructor(e) {
511
+ this.options = e;
512
+ }
513
+ /**
514
+ * Refresh the given token and return a new CompositeToken.
515
+ *
516
+ * @param token The token to refresh
517
+ * @returns A Promise that resolves to a new CompositeToken
518
+ */
519
+ refresh(e) {
520
+ return this.options.fetcher.post(
521
+ this.options.endpoint,
522
+ {
523
+ body: e
524
+ },
525
+ y.Json,
526
+ {
527
+ [p]: !0
528
+ }
529
+ );
530
+ }
531
+ }
532
+ const M = "cosec-token";
533
+ class G extends f {
534
+ constructor(e = M, t = 0) {
510
535
  super({
511
536
  key: e,
512
- storage: c(),
513
- serializer: new S(t)
537
+ storage: h(),
538
+ serializer: new w(t)
514
539
  }), this.earlyPeriod = t;
515
540
  }
516
541
  setCompositeToken(e) {
517
542
  this.set(
518
- new T(e)
543
+ new R(e)
519
544
  );
520
545
  }
521
546
  }
522
547
  export {
523
- x as AuthorizeResults,
524
- y as BrowserListenableStorage,
525
- z as COSEC_REQUEST_INTERCEPTOR_NAME,
548
+ Y as AuthorizeResults,
549
+ _ as BrowserListenableStorage,
550
+ D as COSEC_REQUEST_INTERCEPTOR_NAME,
526
551
  P as COSEC_REQUEST_INTERCEPTOR_ORDER,
527
- D as COSEC_RESPONSE_INTERCEPTOR_NAME,
528
- A as COSEC_RESPONSE_INTERCEPTOR_ORDER,
529
- o as CoSecHeaders,
530
- M as CoSecRequestInterceptor,
531
- q as CoSecResponseInterceptor,
532
- U as DEFAULT_COSEC_DEVICE_ID_KEY,
533
- L as DEFAULT_COSEC_TOKEN_KEY,
534
- Y as DeviceIdStorage,
535
- f as IdentitySerializer,
536
- m as InMemoryListenableStorage,
537
- O as JsonSerializer,
538
- T as JwtCompositeToken,
539
- S as JwtCompositeTokenSerializer,
552
+ U as COSEC_RESPONSE_INTERCEPTOR_NAME,
553
+ V as COSEC_RESPONSE_INTERCEPTOR_ORDER,
554
+ n as CoSecHeaders,
555
+ q as CoSecRequestInterceptor,
556
+ K as CoSecResponseInterceptor,
557
+ F as CoSecTokenRefresher,
558
+ L as DEFAULT_COSEC_DEVICE_ID_KEY,
559
+ M as DEFAULT_COSEC_TOKEN_KEY,
560
+ B as DeviceIdStorage,
561
+ p as IGNORE_REFRESH_TOKEN_ATTRIBUTE_KEY,
562
+ E as IdentitySerializer,
563
+ O as InMemoryListenableStorage,
564
+ N as JsonSerializer,
565
+ R as JwtCompositeToken,
566
+ w as JwtCompositeTokenSerializer,
540
567
  d as JwtToken,
541
- Q as JwtTokenManager,
542
- I as KeyStorage,
543
- _ as NanoIdGenerator,
544
- h as ResponseCodes,
568
+ j as JwtTokenManager,
569
+ f as KeyStorage,
570
+ A as NanoIdGenerator,
571
+ c as ResponseCodes,
545
572
  u as STORAGE_EVENT_TYPE,
546
- B as TokenStorage,
547
- c as createListenableStorage,
548
- E as idGenerator,
573
+ G as TokenStorage,
574
+ h as createListenableStorage,
575
+ I as idGenerator,
549
576
  C as identitySerializer,
550
577
  g as isBrowser,
551
- V as isTokenExpired,
552
- b as jsonSerializer,
553
- j as jwtCompositeTokenSerializer,
554
- p as parseJwtPayload,
555
- N as typedIdentitySerializer
578
+ b as isTokenExpired,
579
+ x as jsonSerializer,
580
+ Q as jwtCompositeTokenSerializer,
581
+ T as parseJwtPayload,
582
+ z as typedIdentitySerializer
556
583
  };
557
584
  //# sourceMappingURL=index.es.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.es.js","sources":["../src/env.ts","../src/storage/inMemoryListenableStorage.ts","../src/storage/browserListenableStorage.ts","../src/storage/listenableStorage.ts","../src/serializer.ts","../src/storage/keyStorage.ts","../src/types.ts","../src/idGenerator.ts","../src/cosecRequestInterceptor.ts","../src/cosecResponseInterceptor.ts","../src/deviceIdStorage.ts","../src/jwts.ts","../src/jwtToken.ts","../src/jwtTokenManager.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\nexport function isBrowser(): boolean {\n return typeof window !== 'undefined';\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 { ListenableStorage, RemoveStorageListener, StorageListener } from './listenableStorage';\nimport { isBrowser } from '../env';\n\n/**\n * An in-memory implementation of ListenableStorage that works in any environment.\n * This implementation stores data in a Map and manually fires storage events when data changes.\n */\nexport class InMemoryListenableStorage implements ListenableStorage {\n private readonly store: Map<string, string> = new Map();\n private readonly listeners: Set<StorageListener> = new Set();\n\n /**\n * Gets the number of items stored in the storage.\n */\n get length(): number {\n return this.store.size;\n }\n\n /**\n * Clears all items from the storage.\n */\n clear(): void {\n this.store.clear();\n }\n\n /**\n * Gets an item from the storage.\n * @param key - The key of the item to retrieve\n * @returns The value of the item, or null if the item does not exist\n */\n getItem(key: string): string | null {\n const value = this.store.get(key);\n return value !== undefined ? value : null;\n }\n\n /**\n * Gets the key at the specified index.\n * @param index - The index of the key to retrieve\n * @returns The key at the specified index, or null if the index is out of bounds\n */\n key(index: number): string | null {\n const keys = Array.from(this.store.keys());\n return keys[index] || null;\n }\n\n /**\n * Removes an item from the storage.\n * @param key - The key of the item to remove\n */\n removeItem(key: string): void {\n const oldValue = this.getItem(key);\n if (this.store.has(key)) {\n this.store.delete(key);\n this.notifyListeners({\n key,\n oldValue,\n newValue: null,\n });\n }\n }\n\n /**\n * Sets an item in the storage.\n * @param key - The key of the item to set\n * @param value - The value to set\n */\n setItem(key: string, value: string): void {\n const oldValue = this.getItem(key);\n this.store.set(key, value);\n this.notifyListeners({ key, oldValue, newValue: value });\n }\n\n /**\n * Adds a listener for storage changes.\n * @param listener - The listener function to be called when storage changes\n * @returns A function that can be called to remove the listener\n */\n addListener(listener: StorageListener): RemoveStorageListener {\n this.listeners.add(listener);\n return () => this.listeners.delete(listener);\n }\n\n /**\n * Notifies all listeners of a storage change by creating and dispatching a StorageEvent.\n * @param eventInit - The initialization object for the StorageEvent\n */\n private notifyListeners(\n eventInit: StorageEventInit,\n ): void {\n if (isBrowser() && window.location) {\n eventInit.url = eventInit.url || window.location.href;\n }\n eventInit.storageArea = this;\n this.listeners.forEach(listener => {\n try {\n listener(eventInit);\n } catch (error) {\n console.error('Error in storage change listener:', 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\nimport { ListenableStorage, RemoveStorageListener, STORAGE_EVENT_TYPE, StorageListener } from './listenableStorage';\n\n/**\n * A wrapper around the browser's native Storage (localStorage or sessionStorage)\n * that implements the ListenableStorage interface by using the browser's native storage events.\n */\nexport class BrowserListenableStorage implements ListenableStorage {\n\n /**\n * Creates a new BrowserListenableStorage instance.\n * @param storage - The native Storage object to wrap (e.g., localStorage or sessionStorage)\n */\n constructor(private readonly storage: Storage) {\n }\n\n /**\n * Gets the number of items stored in the storage.\n */\n get length(): number {\n return this.storage.length;\n }\n\n /**\n * Adds a listener for storage changes.\n * @param listener - The listener function to be called when storage changes\n * @returns A function that can be called to remove the listener\n */\n addListener(listener: StorageListener): RemoveStorageListener {\n const wrapper: StorageListener = (event: StorageEventInit) => {\n if (event.storageArea === this.storage) {\n listener(event);\n }\n };\n window.addEventListener(STORAGE_EVENT_TYPE, wrapper);\n return () => window.removeEventListener(STORAGE_EVENT_TYPE, wrapper);\n }\n\n /**\n * Clears all items from the storage.\n */\n clear(): void {\n this.storage.clear();\n }\n\n /**\n * Gets an item from the storage.\n * @param key - The key of the item to retrieve\n * @returns The value of the item, or null if the item does not exist\n */\n getItem(key: string): string | null {\n return this.storage.getItem(key);\n }\n\n /**\n * Gets the key at the specified index.\n * @param index - The index of the key to retrieve\n * @returns The key at the specified index, or null if the index is out of bounds\n */\n key(index: number): string | null {\n return this.storage.key(index);\n }\n\n /**\n * Removes an item from the storage.\n * @param key - The key of the item to remove\n */\n removeItem(key: string): void {\n this.storage.removeItem(key);\n }\n\n /**\n * Sets an item in the storage.\n * @param key - The key of the item to set\n * @param value - The value to set\n */\n setItem(key: string, value: string): void {\n this.storage.setItem(key, value);\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 { InMemoryListenableStorage } from './inMemoryListenableStorage';\nimport { BrowserListenableStorage } from './browserListenableStorage';\nimport { isBrowser } from '../env';\n\n/**\n * The type of storage event used for listening to storage changes.\n */\nexport const STORAGE_EVENT_TYPE = 'storage';\n\n/**\n * A function that handles storage change events.\n */\nexport type StorageListener = (event: StorageEventInit) => void;\n\n/**\n * A function that removes a storage listener when called.\n */\nexport type RemoveStorageListener = () => void;\n\nexport interface StorageListenable {\n /**\n * Adds a listener for storage changes.\n * @param listener - The listener function to be called when storage changes\n * @returns A function that can be called to remove the listener\n */\n addListener(listener: StorageListener): RemoveStorageListener;\n}\n\n/**\n * An interface that extends the native Storage interface with the ability to listen for storage changes.\n */\nexport interface ListenableStorage extends Storage, StorageListenable {\n\n}\n\n/**\n * Factory function to get an appropriate ListenableStorage implementation based on the environment.\n * In a browser environment, it returns a BrowserListenableStorage wrapping localStorage.\n * In other environments, it returns an InMemoryListenableStorage.\n * @returns A ListenableStorage instance suitable for the current environment\n */\nexport const createListenableStorage = (): ListenableStorage => {\n if (isBrowser()) {\n return new BrowserListenableStorage(window.localStorage);\n }\n return new InMemoryListenableStorage();\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 * Interface for serializing and deserializing values\n * @template Serialized The type of the serialized value\n */\n/**\n * Interface for serializing and deserializing values\n * @template Serialized The type of the serialized value\n * @template Deserialized The type of the deserialized value\n */\nexport interface Serializer<Serialized, Deserialized> {\n /**\n * Serializes a value to the specified format\n * @param value The value to serialize\n * @returns The serialized value\n */\n serialize(value: any): Serialized;\n\n /**\n * Deserializes a value from the specified format\n * @template Deserialized The type of the deserialized value\n * @param value The value to deserialize\n * @returns The deserialized value\n */\n /**\n * Deserializes a value from the specified format\n * @param value The value to deserialize\n * @returns The deserialized value\n */\n deserialize(value: Serialized): Deserialized;\n}\n\n/**\n * Implementation of Serializer that uses JSON for serialization\n */\nexport class JsonSerializer implements Serializer<string, any> {\n /**\n * Serializes a value to a JSON string\n * @param value The value to serialize\n * @returns The JSON string representation of the value\n */\n /**\n * Serializes a value to a JSON string\n * @param value The value to serialize\n * @returns The JSON string representation of the value\n */\n serialize(value: any): string {\n return JSON.stringify(value);\n }\n\n /**\n * Deserializes a JSON string to a value\n * @template V The type of the deserialized value\n * @param value The JSON string to deserialize\n * @returns The deserialized value\n */\n /**\n * Deserializes a JSON string to a value\n * @param value The JSON string to deserialize\n * @returns The deserialized value\n */\n deserialize<V>(value: string): V {\n return JSON.parse(value);\n }\n}\n\n/**\n * Implementation of Serializer that performs no actual serialization\n * @template T The type of the value to pass through\n */\nexport class IdentitySerializer<T> implements Serializer<T, T> {\n /**\n * Returns the value as-is without serialization\n * @param value The value to pass through\n * @returns The same value that was passed in\n */\n /**\n * Returns the value as-is without serialization\n * @param value The value to pass through\n * @returns The same value that was passed in\n */\n serialize(value: T): T {\n return value;\n }\n\n /**\n * Returns the value as-is without deserialization\n * @template Deserialized The type of the deserialized value\n * @param value The value to pass through\n * @returns The same value that was passed in, cast to the target type\n */\n /**\n * Returns the value as-is without deserialization\n * @param value The value to pass through\n * @returns The same value that was passed in\n */\n deserialize(value: T): T {\n return value;\n }\n}\n\n/**\n * Global instance of JsonSerializer\n */\nexport const jsonSerializer = new JsonSerializer();\n/**\n * Global instance of IdentitySerializer\n */\nexport const identitySerializer = new IdentitySerializer<any>();\n\nexport function typedIdentitySerializer<T>(): IdentitySerializer<T> {\n return identitySerializer as IdentitySerializer<T>;\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 { createListenableStorage, ListenableStorage, StorageListener } from './listenableStorage';\nimport { Serializer, typedIdentitySerializer } from '../serializer';\n\n/**\n * Options for configuring KeyStorage\n */\nexport interface KeyStorageOptions<Deserialized> {\n /**\n * The key used to store and retrieve values from storage\n */\n key: string;\n\n /**\n * Optional serializer for converting values to and from storage format\n * Defaults to IdentitySerializer if not provided\n */\n serializer?: Serializer<string, Deserialized>;\n\n /**\n * Optional storage implementation\n * Defaults to the result of createListenableStorage() if not provided\n */\n storage?: ListenableStorage;\n}\n\n/**\n * A storage wrapper that manages a single value associated with a specific key\n * Provides caching and automatic cache invalidation when the storage value changes\n * @template Deserialized The type of the value being stored\n */\nexport class KeyStorage<Deserialized> {\n private readonly key: string;\n private readonly serializer: Serializer<string, Deserialized>;\n private readonly storage: ListenableStorage;\n private cacheValue: Deserialized | null = null;\n\n /**\n * Listener for storage change events\n * Invalidates the cache when the relevant key is modified\n */\n private readonly listener: StorageListener = (event) => {\n if (event.key !== this.key) {\n return;\n }\n this.cacheValue = null;\n if (event.newValue) {\n this.refreshCache(event.newValue);\n }\n };\n\n /**\n * Refreshes the cached value by deserializing the provided string\n * @param value The serialized value to deserialize and cache\n */\n private refreshCache(value: string) {\n this.cacheValue = this.serializer.deserialize(value);\n }\n\n /**\n * Creates a new KeyStorage instance\n * @param options Configuration options for the storage\n */\n constructor(options: KeyStorageOptions<Deserialized>) {\n this.key = options.key;\n this.serializer = options.serializer ?? typedIdentitySerializer();\n this.storage = options.storage ?? createListenableStorage();\n this.storage.addListener(this.listener);\n }\n\n /**\n * Gets the value associated with the key from storage\n * Uses cached value if available, otherwise retrieves from storage and caches it\n * @returns The deserialized value or null if no value is found\n */\n get(): Deserialized | null {\n if (this.cacheValue) {\n return this.cacheValue;\n }\n const value = this.storage.getItem(this.key);\n if (!value) {\n return null;\n }\n this.refreshCache(value);\n return this.cacheValue;\n }\n\n /**\n * Sets the value associated with the key in storage\n * Also updates the cached value\n * @param value The value to serialize and store\n */\n set(value: Deserialized): void {\n const serialized = this.serializer.serialize(value);\n this.storage.setItem(this.key, serialized);\n this.cacheValue = value;\n }\n\n /**\n * Removes the value associated with the key from storage\n * Also clears the cached value\n */\n remove(): void {\n this.storage.removeItem(this.key);\n this.cacheValue = null;\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 { 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 requestHeaders = exchange.ensureRequestHeaders();\n requestHeaders[CoSecHeaders.APP_ID] = this.options.appId;\n requestHeaders[CoSecHeaders.DEVICE_ID] = deviceId;\n requestHeaders[CoSecHeaders.REQUEST_ID] = requestId;\n const token = this.options.tokenStorage.get();\n if (token && !requestHeaders[CoSecHeaders.AUTHORIZATION]) {\n requestHeaders[CoSecHeaders.AUTHORIZATION] =\n `Bearer ${token.access.token}`;\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.setCompositeToken(newToken);\n return newToken;\n })\n .catch(error => {\n this.options.tokenStorage.remove();\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 jwtToken = this.options.tokenStorage.get();\n // If there's no current token, we can't refresh it\n if (!jwtToken) {\n return;\n }\n\n try {\n // Attempt to refresh the token\n await this.refresh(jwtToken.token);\n // Retry the original request with the new token\n await exchange.fetcher.interceptors.exchange(exchange);\n } catch (error) {\n // If token refresh fails, clear stored tokens and re-throw the error\n this.options.tokenStorage.remove();\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\nimport { idGenerator } from './idGenerator';\nimport { createListenableStorage, KeyStorage } from './storage';\nimport { IdentitySerializer } from './serializer';\n\nexport const DEFAULT_COSEC_DEVICE_ID_KEY = 'cosec-device-id';\n\n/**\n * Storage class for managing device identifiers.\n */\nexport class DeviceIdStorage extends KeyStorage<string> {\n constructor(key: string = DEFAULT_COSEC_DEVICE_ID_KEY) {\n super({\n key,\n serializer: new IdentitySerializer<string>(),\n storage: createListenableStorage(),\n });\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","/*\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\nexport interface EarlyPeriodCapable {\n /**\n * The time in seconds before actual expiration when the token should be considered expired (default: 0)\n */\n readonly earlyPeriod: number;\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 { CoSecJwtPayload, EarlyPeriodCapable, isTokenExpired, JwtPayload, parseJwtPayload } from './jwts';\nimport { Serializer } from './serializer';\nimport { CompositeToken } from './tokenRefresher';\n\n\n/**\n * Interface for JWT token with typed payload\n * @template Payload The type of the JWT payload\n */\nexport interface IJwtToken<Payload extends JwtPayload> extends EarlyPeriodCapable {\n readonly token: string;\n readonly payload: Payload;\n\n isExpired: boolean;\n}\n\n/**\n * Class representing a JWT token with typed payload\n * @template Payload The type of the JWT payload\n */\nexport class JwtToken<Payload extends JwtPayload> implements IJwtToken<Payload> {\n public readonly payload: Payload;\n\n /**\n * Creates a new JwtToken instance\n */\n constructor(\n public readonly token: string,\n public readonly earlyPeriod: number = 0,\n ) {\n this.payload = parseJwtPayload(token) as Payload;\n }\n\n /**\n * Checks if the token is expired\n * @returns true if the token is expired, false otherwise\n */\n get isExpired(): boolean {\n return isTokenExpired(this.payload, this.earlyPeriod);\n }\n}\n\nexport interface RefreshTokenStatusCapable {\n /**\n * Checks if the access token needs to be refreshed\n * @returns true if the access token is expired, false otherwise\n */\n readonly isRefreshNeeded: boolean;\n /**\n * Checks if the refresh token is still valid and can be used to refresh the access token\n * @returns true if the refresh token is not expired, false otherwise\n */\n readonly isRefreshable: boolean;\n}\n\n/**\n * Class representing a composite token containing both access and refresh tokens\n */\nexport class JwtCompositeToken implements EarlyPeriodCapable, RefreshTokenStatusCapable {\n public readonly access: JwtToken<CoSecJwtPayload>;\n public readonly refresh: JwtToken<JwtPayload>;\n\n /**\n * Creates a new JwtCompositeToken instance\n */\n constructor(public readonly token: CompositeToken, public readonly earlyPeriod: number = 0) {\n this.access = new JwtToken(token.accessToken, earlyPeriod);\n this.refresh = new JwtToken(token.refreshToken, earlyPeriod);\n }\n\n /**\n * Checks if the access token needs to be refreshed\n * @returns true if the access token is expired, false otherwise\n */\n get isRefreshNeeded(): boolean {\n return this.access.isExpired;\n }\n\n /**\n * Checks if the refresh token is still valid and can be used to refresh the access token\n * @returns true if the refresh token is not expired, false otherwise\n */\n get isRefreshable(): boolean {\n return !this.refresh.isExpired;\n }\n\n}\n\n/**\n * Serializer for JwtCompositeToken that handles conversion to and from JSON strings\n */\nexport class JwtCompositeTokenSerializer implements Serializer<string, JwtCompositeToken>, EarlyPeriodCapable {\n constructor(public readonly earlyPeriod: number = 0) {\n }\n\n /**\n * Deserializes a JSON string to a JwtCompositeToken\n * @param value The JSON string representation of a composite token\n * @returns A JwtCompositeToken instance\n */\n deserialize(value: string): JwtCompositeToken {\n const compositeToken = JSON.parse(value) as CompositeToken;\n return new JwtCompositeToken(compositeToken, this.earlyPeriod);\n }\n\n /**\n * Serializes a JwtCompositeToken to a JSON string\n * @param value The JwtCompositeToken to serialize\n * @returns A JSON string representation of the composite token\n */\n serialize(value: JwtCompositeToken): string {\n return JSON.stringify(value.token);\n }\n}\n\nexport const jwtCompositeTokenSerializer = new JwtCompositeTokenSerializer();","/*\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 { TokenStorage } from './tokenStorage';\nimport { CompositeToken, TokenRefresher } from './tokenRefresher';\nimport { JwtCompositeToken, RefreshTokenStatusCapable } from './jwtToken';\n\n/**\n * Manages JWT token refreshing operations and provides status information\n */\nexport class JwtTokenManager implements RefreshTokenStatusCapable {\n private refreshInProgress?: Promise<void>;\n\n /**\n * Creates a new JwtTokenManager instance\n * @param tokenStorage The storage used to persist tokens\n * @param tokenRefresher The refresher used to refresh expired tokens\n */\n constructor(\n private readonly tokenStorage: TokenStorage,\n private readonly tokenRefresher: TokenRefresher,\n ) {\n\n }\n\n /**\n * Gets the current JWT composite token from storage\n * @returns The current token or null if none exists\n */\n get currentToken(): JwtCompositeToken | null {\n return this.tokenStorage.get();\n }\n\n /**\n * Refreshes the JWT token\n * @param currentToken Optional current token to refresh. If not provided, uses the stored token.\n * @returns Promise that resolves when refresh is complete\n * @throws Error if no token is found or refresh fails\n */\n async refresh(currentToken?: CompositeToken): Promise<void> {\n if (!currentToken) {\n const jwtToken = this.currentToken;\n if (!jwtToken) {\n throw new Error('No token found');\n }\n currentToken = jwtToken.token;\n }\n if (this.refreshInProgress) {\n return this.refreshInProgress;\n }\n\n this.refreshInProgress = this.tokenRefresher.refresh(currentToken)\n .then(newToken => {\n this.tokenStorage.setCompositeToken(newToken);\n })\n .catch(error => {\n this.tokenStorage.remove();\n throw error;\n })\n .finally(() => {\n this.refreshInProgress = undefined;\n });\n\n return this.refreshInProgress;\n }\n\n /**\n * Indicates if the current token needs to be refreshed\n * @returns true if the access token is expired and needs refresh, false otherwise\n */\n get isRefreshNeeded(): boolean {\n if (!this.currentToken) {\n return false;\n }\n return this.currentToken.isRefreshNeeded;\n }\n\n /**\n * Indicates if the current token can be refreshed\n * @returns true if the refresh token is still valid, false otherwise\n */\n get isRefreshable(): boolean {\n if (!this.currentToken) {\n return false;\n }\n return this.currentToken.isRefreshable;\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 { createListenableStorage, KeyStorage } from './storage';\nimport { JwtCompositeToken, JwtCompositeTokenSerializer } from './jwtToken';\nimport { CompositeToken } from './tokenRefresher';\nimport { EarlyPeriodCapable } from './jwts';\n\nexport const DEFAULT_COSEC_TOKEN_KEY = 'cosec-token';\n\n/**\n * Storage class for managing access and refresh tokens.\n */\nexport class TokenStorage extends KeyStorage<JwtCompositeToken> implements EarlyPeriodCapable {\n\n constructor(\n key: string = DEFAULT_COSEC_TOKEN_KEY,\n public readonly earlyPeriod: number = 0,\n ) {\n super({\n key,\n storage: createListenableStorage(),\n serializer: new JwtCompositeTokenSerializer(earlyPeriod),\n });\n }\n\n setCompositeToken(compositeToken: CompositeToken) {\n this.set(\n new JwtCompositeToken(compositeToken),\n );\n }\n}\n"],"names":["isBrowser","InMemoryListenableStorage","key","value","index","oldValue","listener","eventInit","error","BrowserListenableStorage","storage","wrapper","event","STORAGE_EVENT_TYPE","createListenableStorage","JsonSerializer","IdentitySerializer","jsonSerializer","identitySerializer","typedIdentitySerializer","KeyStorage","options","serialized","_CoSecHeaders","CoSecHeaders","_ResponseCodes","ResponseCodes","AuthorizeResults","NanoIdGenerator","nanoid","idGenerator","COSEC_REQUEST_INTERCEPTOR_NAME","COSEC_REQUEST_INTERCEPTOR_ORDER","REQUEST_BODY_INTERCEPTOR_ORDER","CoSecRequestInterceptor","exchange","requestId","deviceId","requestHeaders","token","COSEC_RESPONSE_INTERCEPTOR_NAME","COSEC_RESPONSE_INTERCEPTOR_ORDER","CoSecResponseInterceptor","currentToken","newToken","response","jwtToken","DEFAULT_COSEC_DEVICE_ID_KEY","DeviceIdStorage","parseJwtPayload","parts","base64","paddedBase64","jsonPayload","c","isTokenExpired","earlyPeriod","payload","expAt","JwtToken","JwtCompositeToken","JwtCompositeTokenSerializer","compositeToken","jwtCompositeTokenSerializer","JwtTokenManager","tokenStorage","tokenRefresher","DEFAULT_COSEC_TOKEN_KEY","TokenStorage"],"mappings":"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"}
1
+ {"version":3,"file":"index.es.js","sources":["../src/env.ts","../src/storage/inMemoryListenableStorage.ts","../src/storage/browserListenableStorage.ts","../src/storage/listenableStorage.ts","../src/serializer.ts","../src/storage/keyStorage.ts","../src/types.ts","../src/idGenerator.ts","../src/cosecRequestInterceptor.ts","../src/cosecResponseInterceptor.ts","../src/deviceIdStorage.ts","../src/jwts.ts","../src/jwtToken.ts","../src/jwtTokenManager.ts","../src/tokenRefresher.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\nexport function isBrowser(): boolean {\n return typeof window !== 'undefined';\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 { ListenableStorage, RemoveStorageListener, StorageListener } from './listenableStorage';\nimport { isBrowser } from '../env';\n\n/**\n * An in-memory implementation of ListenableStorage that works in any environment.\n * This implementation stores data in a Map and manually fires storage events when data changes.\n */\nexport class InMemoryListenableStorage implements ListenableStorage {\n private readonly store: Map<string, string> = new Map();\n private readonly listeners: Set<StorageListener> = new Set();\n\n /**\n * Gets the number of items stored in the storage.\n */\n get length(): number {\n return this.store.size;\n }\n\n /**\n * Clears all items from the storage.\n */\n clear(): void {\n this.store.clear();\n }\n\n /**\n * Gets an item from the storage.\n * @param key - The key of the item to retrieve\n * @returns The value of the item, or null if the item does not exist\n */\n getItem(key: string): string | null {\n const value = this.store.get(key);\n return value !== undefined ? value : null;\n }\n\n /**\n * Gets the key at the specified index.\n * @param index - The index of the key to retrieve\n * @returns The key at the specified index, or null if the index is out of bounds\n */\n key(index: number): string | null {\n const keys = Array.from(this.store.keys());\n return keys[index] || null;\n }\n\n /**\n * Removes an item from the storage.\n * @param key - The key of the item to remove\n */\n removeItem(key: string): void {\n const oldValue = this.getItem(key);\n if (this.store.has(key)) {\n this.store.delete(key);\n this.notifyListeners({\n key,\n oldValue,\n newValue: null,\n });\n }\n }\n\n /**\n * Sets an item in the storage.\n * @param key - The key of the item to set\n * @param value - The value to set\n */\n setItem(key: string, value: string): void {\n const oldValue = this.getItem(key);\n this.store.set(key, value);\n this.notifyListeners({ key, oldValue, newValue: value });\n }\n\n /**\n * Adds a listener for storage changes.\n * @param listener - The listener function to be called when storage changes\n * @returns A function that can be called to remove the listener\n */\n addListener(listener: StorageListener): RemoveStorageListener {\n this.listeners.add(listener);\n return () => this.listeners.delete(listener);\n }\n\n /**\n * Notifies all listeners of a storage change by creating and dispatching a StorageEvent.\n * @param eventInit - The initialization object for the StorageEvent\n */\n private notifyListeners(\n eventInit: StorageEventInit,\n ): void {\n if (isBrowser() && window.location) {\n eventInit.url = eventInit.url || window.location.href;\n }\n eventInit.storageArea = this;\n this.listeners.forEach(listener => {\n try {\n listener(eventInit);\n } catch (error) {\n console.error('Error in storage change listener:', 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\nimport { ListenableStorage, RemoveStorageListener, STORAGE_EVENT_TYPE, StorageListener } from './listenableStorage';\n\n/**\n * A wrapper around the browser's native Storage (localStorage or sessionStorage)\n * that implements the ListenableStorage interface by using the browser's native storage events.\n */\nexport class BrowserListenableStorage implements ListenableStorage {\n\n /**\n * Creates a new BrowserListenableStorage instance.\n * @param storage - The native Storage object to wrap (e.g., localStorage or sessionStorage)\n */\n constructor(private readonly storage: Storage) {\n }\n\n /**\n * Gets the number of items stored in the storage.\n */\n get length(): number {\n return this.storage.length;\n }\n\n /**\n * Adds a listener for storage changes.\n * @param listener - The listener function to be called when storage changes\n * @returns A function that can be called to remove the listener\n */\n addListener(listener: StorageListener): RemoveStorageListener {\n const wrapper: StorageListener = (event: StorageEventInit) => {\n if (event.storageArea === this.storage) {\n listener(event);\n }\n };\n window.addEventListener(STORAGE_EVENT_TYPE, wrapper);\n return () => window.removeEventListener(STORAGE_EVENT_TYPE, wrapper);\n }\n\n /**\n * Clears all items from the storage.\n */\n clear(): void {\n this.storage.clear();\n }\n\n /**\n * Gets an item from the storage.\n * @param key - The key of the item to retrieve\n * @returns The value of the item, or null if the item does not exist\n */\n getItem(key: string): string | null {\n return this.storage.getItem(key);\n }\n\n /**\n * Gets the key at the specified index.\n * @param index - The index of the key to retrieve\n * @returns The key at the specified index, or null if the index is out of bounds\n */\n key(index: number): string | null {\n return this.storage.key(index);\n }\n\n /**\n * Removes an item from the storage.\n * @param key - The key of the item to remove\n */\n removeItem(key: string): void {\n this.storage.removeItem(key);\n }\n\n /**\n * Sets an item in the storage.\n * @param key - The key of the item to set\n * @param value - The value to set\n */\n setItem(key: string, value: string): void {\n this.storage.setItem(key, value);\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 { InMemoryListenableStorage } from './inMemoryListenableStorage';\nimport { BrowserListenableStorage } from './browserListenableStorage';\nimport { isBrowser } from '../env';\n\n/**\n * The type of storage event used for listening to storage changes.\n */\nexport const STORAGE_EVENT_TYPE = 'storage';\n\n/**\n * A function that handles storage change events.\n */\nexport type StorageListener = (event: StorageEventInit) => void;\n\n/**\n * A function that removes a storage listener when called.\n */\nexport type RemoveStorageListener = () => void;\n\nexport interface StorageListenable {\n /**\n * Adds a listener for storage changes.\n * @param listener - The listener function to be called when storage changes\n * @returns A function that can be called to remove the listener\n */\n addListener(listener: StorageListener): RemoveStorageListener;\n}\n\n/**\n * An interface that extends the native Storage interface with the ability to listen for storage changes.\n */\nexport interface ListenableStorage extends Storage, StorageListenable {\n\n}\n\n/**\n * Factory function to get an appropriate ListenableStorage implementation based on the environment.\n * In a browser environment, it returns a BrowserListenableStorage wrapping localStorage.\n * In other environments, it returns an InMemoryListenableStorage.\n * @returns A ListenableStorage instance suitable for the current environment\n */\nexport const createListenableStorage = (): ListenableStorage => {\n if (isBrowser()) {\n return new BrowserListenableStorage(window.localStorage);\n }\n return new InMemoryListenableStorage();\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 * Interface for serializing and deserializing values\n * @template Serialized The type of the serialized value\n */\n/**\n * Interface for serializing and deserializing values\n * @template Serialized The type of the serialized value\n * @template Deserialized The type of the deserialized value\n */\nexport interface Serializer<Serialized, Deserialized> {\n /**\n * Serializes a value to the specified format\n * @param value The value to serialize\n * @returns The serialized value\n */\n serialize(value: any): Serialized;\n\n /**\n * Deserializes a value from the specified format\n * @template Deserialized The type of the deserialized value\n * @param value The value to deserialize\n * @returns The deserialized value\n */\n /**\n * Deserializes a value from the specified format\n * @param value The value to deserialize\n * @returns The deserialized value\n */\n deserialize(value: Serialized): Deserialized;\n}\n\n/**\n * Implementation of Serializer that uses JSON for serialization\n */\nexport class JsonSerializer implements Serializer<string, any> {\n /**\n * Serializes a value to a JSON string\n * @param value The value to serialize\n * @returns The JSON string representation of the value\n */\n /**\n * Serializes a value to a JSON string\n * @param value The value to serialize\n * @returns The JSON string representation of the value\n */\n serialize(value: any): string {\n return JSON.stringify(value);\n }\n\n /**\n * Deserializes a JSON string to a value\n * @template V The type of the deserialized value\n * @param value The JSON string to deserialize\n * @returns The deserialized value\n */\n /**\n * Deserializes a JSON string to a value\n * @param value The JSON string to deserialize\n * @returns The deserialized value\n */\n deserialize<V>(value: string): V {\n return JSON.parse(value);\n }\n}\n\n/**\n * Implementation of Serializer that performs no actual serialization\n * @template T The type of the value to pass through\n */\nexport class IdentitySerializer<T> implements Serializer<T, T> {\n /**\n * Returns the value as-is without serialization\n * @param value The value to pass through\n * @returns The same value that was passed in\n */\n /**\n * Returns the value as-is without serialization\n * @param value The value to pass through\n * @returns The same value that was passed in\n */\n serialize(value: T): T {\n return value;\n }\n\n /**\n * Returns the value as-is without deserialization\n * @template Deserialized The type of the deserialized value\n * @param value The value to pass through\n * @returns The same value that was passed in, cast to the target type\n */\n /**\n * Returns the value as-is without deserialization\n * @param value The value to pass through\n * @returns The same value that was passed in\n */\n deserialize(value: T): T {\n return value;\n }\n}\n\n/**\n * Global instance of JsonSerializer\n */\nexport const jsonSerializer = new JsonSerializer();\n/**\n * Global instance of IdentitySerializer\n */\nexport const identitySerializer = new IdentitySerializer<any>();\n\nexport function typedIdentitySerializer<T>(): IdentitySerializer<T> {\n return identitySerializer as IdentitySerializer<T>;\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 { createListenableStorage, ListenableStorage, StorageListener } from './listenableStorage';\nimport { Serializer, typedIdentitySerializer } from '../serializer';\n\n/**\n * Options for configuring KeyStorage\n */\nexport interface KeyStorageOptions<Deserialized> {\n /**\n * The key used to store and retrieve values from storage\n */\n key: string;\n\n /**\n * Optional serializer for converting values to and from storage format\n * Defaults to IdentitySerializer if not provided\n */\n serializer?: Serializer<string, Deserialized>;\n\n /**\n * Optional storage implementation\n * Defaults to the result of createListenableStorage() if not provided\n */\n storage?: ListenableStorage;\n}\n\n/**\n * A storage wrapper that manages a single value associated with a specific key\n * Provides caching and automatic cache invalidation when the storage value changes\n * @template Deserialized The type of the value being stored\n */\nexport class KeyStorage<Deserialized> {\n private readonly key: string;\n private readonly serializer: Serializer<string, Deserialized>;\n private readonly storage: ListenableStorage;\n private cacheValue: Deserialized | null = null;\n\n /**\n * Listener for storage change events\n * Invalidates the cache when the relevant key is modified\n */\n private readonly listener: StorageListener = (event) => {\n if (event.key !== this.key) {\n return;\n }\n this.cacheValue = null;\n if (event.newValue) {\n this.refreshCache(event.newValue);\n }\n };\n\n /**\n * Refreshes the cached value by deserializing the provided string\n * @param value The serialized value to deserialize and cache\n */\n private refreshCache(value: string) {\n this.cacheValue = this.serializer.deserialize(value);\n }\n\n /**\n * Creates a new KeyStorage instance\n * @param options Configuration options for the storage\n */\n constructor(options: KeyStorageOptions<Deserialized>) {\n this.key = options.key;\n this.serializer = options.serializer ?? typedIdentitySerializer();\n this.storage = options.storage ?? createListenableStorage();\n this.storage.addListener(this.listener);\n }\n\n /**\n * Gets the value associated with the key from storage\n * Uses cached value if available, otherwise retrieves from storage and caches it\n * @returns The deserialized value or null if no value is found\n */\n get(): Deserialized | null {\n if (this.cacheValue) {\n return this.cacheValue;\n }\n const value = this.storage.getItem(this.key);\n if (!value) {\n return null;\n }\n this.refreshCache(value);\n return this.cacheValue;\n }\n\n /**\n * Sets the value associated with the key in storage\n * Also updates the cached value\n * @param value The value to serialize and store\n */\n set(value: Deserialized): void {\n const serialized = this.serializer.serialize(value);\n this.storage.setItem(this.key, serialized);\n this.cacheValue = value;\n }\n\n /**\n * Removes the value associated with the key from storage\n * Also clears the cached value\n */\n remove(): void {\n this.storage.removeItem(this.key);\n this.cacheValue = null;\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 { DeviceIdStorage } from './deviceIdStorage';\nimport { JwtTokenManager } from './jwtTokenManager';\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 tokenManager: JwtTokenManager;\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\nexport const IGNORE_REFRESH_TOKEN_ATTRIBUTE_KEY = 'Ignore-Refresh-Token';\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 /**\n * Creates a new CoSecRequestInterceptor instance.\n * @param options - The CoSec configuration options including appId, deviceIdStorage, and tokenManager\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 * The method also handles token refreshing when the current token is expired but still refreshable.\n * It will attempt to refresh the token before adding the Authorization header to the request.\n */\n async intercept(exchange: FetchExchange) {\n // Generate a unique request ID for this request\n const requestId = idGenerator.generateId();\n\n // Get or create a device ID\n const deviceId = this.options.deviceIdStorage.getOrCreate();\n\n // Ensure request headers object exists\n const requestHeaders = exchange.ensureRequestHeaders();\n\n // Add CoSec headers to the request\n requestHeaders[CoSecHeaders.APP_ID] = this.options.appId;\n requestHeaders[CoSecHeaders.DEVICE_ID] = deviceId;\n requestHeaders[CoSecHeaders.REQUEST_ID] = requestId;\n\n // Get the current token from token manager\n let currentToken = this.options.tokenManager.currentToken;\n\n // Skip if no token exists or Authorization header is already set\n if (!currentToken || requestHeaders[CoSecHeaders.AUTHORIZATION]) {\n return;\n }\n\n // Refresh token if needed and refreshable\n if (!exchange.attributes[IGNORE_REFRESH_TOKEN_ATTRIBUTE_KEY] && currentToken.isRefreshNeeded && currentToken.isRefreshable) {\n await this.options.tokenManager.refresh();\n }\n\n // Get the current token again (might have been refreshed)\n currentToken = this.options.tokenManager.currentToken;\n\n // Add Authorization header if we have a token\n if (currentToken) {\n requestHeaders[CoSecHeaders.AUTHORIZATION] = `Bearer ${currentToken.access.token}`;\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 if (!this.options.tokenManager.isRefreshable) {\n return;\n }\n try {\n await this.options.tokenManager.refresh();\n // Retry the original request with the new token\n await exchange.fetcher.interceptors.exchange(exchange);\n } catch (error) {\n // If token refresh fails, clear stored tokens and re-throw the error\n this.options.tokenManager.tokenStorage.remove();\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\nimport { idGenerator } from './idGenerator';\nimport { createListenableStorage, KeyStorage } from './storage';\nimport { IdentitySerializer } from './serializer';\n\nexport const DEFAULT_COSEC_DEVICE_ID_KEY = 'cosec-device-id';\n\n/**\n * Storage class for managing device identifiers.\n */\nexport class DeviceIdStorage extends KeyStorage<string> {\n constructor(key: string = DEFAULT_COSEC_DEVICE_ID_KEY) {\n super({\n key,\n serializer: new IdentitySerializer<string>(),\n storage: createListenableStorage(),\n });\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","/*\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\nexport interface EarlyPeriodCapable {\n /**\n * The time in seconds before actual expiration when the token should be considered expired (default: 0)\n */\n readonly earlyPeriod: number;\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 { CoSecJwtPayload, EarlyPeriodCapable, isTokenExpired, JwtPayload, parseJwtPayload } from './jwts';\nimport { Serializer } from './serializer';\nimport { CompositeToken } from './tokenRefresher';\n\n\n/**\n * Interface for JWT token with typed payload\n * @template Payload The type of the JWT payload\n */\nexport interface IJwtToken<Payload extends JwtPayload> extends EarlyPeriodCapable {\n readonly token: string;\n readonly payload: Payload;\n\n isExpired: boolean;\n}\n\n/**\n * Class representing a JWT token with typed payload\n * @template Payload The type of the JWT payload\n */\nexport class JwtToken<Payload extends JwtPayload> implements IJwtToken<Payload> {\n public readonly payload: Payload;\n\n /**\n * Creates a new JwtToken instance\n */\n constructor(\n public readonly token: string,\n public readonly earlyPeriod: number = 0,\n ) {\n this.payload = parseJwtPayload(token) as Payload;\n }\n\n /**\n * Checks if the token is expired\n * @returns true if the token is expired, false otherwise\n */\n get isExpired(): boolean {\n return isTokenExpired(this.payload, this.earlyPeriod);\n }\n}\n\nexport interface RefreshTokenStatusCapable {\n /**\n * Checks if the access token needs to be refreshed\n * @returns true if the access token is expired, false otherwise\n */\n readonly isRefreshNeeded: boolean;\n /**\n * Checks if the refresh token is still valid and can be used to refresh the access token\n * @returns true if the refresh token is not expired, false otherwise\n */\n readonly isRefreshable: boolean;\n}\n\n/**\n * Class representing a composite token containing both access and refresh tokens\n */\nexport class JwtCompositeToken implements EarlyPeriodCapable, RefreshTokenStatusCapable {\n public readonly access: JwtToken<CoSecJwtPayload>;\n public readonly refresh: JwtToken<JwtPayload>;\n\n /**\n * Creates a new JwtCompositeToken instance\n */\n constructor(public readonly token: CompositeToken, public readonly earlyPeriod: number = 0) {\n this.access = new JwtToken(token.accessToken, earlyPeriod);\n this.refresh = new JwtToken(token.refreshToken, earlyPeriod);\n }\n\n /**\n * Checks if the access token needs to be refreshed\n * @returns true if the access token is expired, false otherwise\n */\n get isRefreshNeeded(): boolean {\n return this.access.isExpired;\n }\n\n /**\n * Checks if the refresh token is still valid and can be used to refresh the access token\n * @returns true if the refresh token is not expired, false otherwise\n */\n get isRefreshable(): boolean {\n return !this.refresh.isExpired;\n }\n\n}\n\n/**\n * Serializer for JwtCompositeToken that handles conversion to and from JSON strings\n */\nexport class JwtCompositeTokenSerializer implements Serializer<string, JwtCompositeToken>, EarlyPeriodCapable {\n constructor(public readonly earlyPeriod: number = 0) {\n }\n\n /**\n * Deserializes a JSON string to a JwtCompositeToken\n * @param value The JSON string representation of a composite token\n * @returns A JwtCompositeToken instance\n */\n deserialize(value: string): JwtCompositeToken {\n const compositeToken = JSON.parse(value) as CompositeToken;\n return new JwtCompositeToken(compositeToken, this.earlyPeriod);\n }\n\n /**\n * Serializes a JwtCompositeToken to a JSON string\n * @param value The JwtCompositeToken to serialize\n * @returns A JSON string representation of the composite token\n */\n serialize(value: JwtCompositeToken): string {\n return JSON.stringify(value.token);\n }\n}\n\nexport const jwtCompositeTokenSerializer = new JwtCompositeTokenSerializer();","/*\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 { TokenStorage } from './tokenStorage';\nimport { CompositeToken, TokenRefresher } from './tokenRefresher';\nimport { JwtCompositeToken, RefreshTokenStatusCapable } from './jwtToken';\n\n/**\n * Manages JWT token refreshing operations and provides status information\n */\nexport class JwtTokenManager implements RefreshTokenStatusCapable {\n private refreshInProgress?: Promise<void>;\n\n /**\n * Creates a new JwtTokenManager instance\n * @param tokenStorage The storage used to persist tokens\n * @param tokenRefresher The refresher used to refresh expired tokens\n */\n constructor(\n public readonly tokenStorage: TokenStorage,\n public readonly tokenRefresher: TokenRefresher,\n ) {\n\n }\n\n /**\n * Gets the current JWT composite token from storage\n * @returns The current token or null if none exists\n */\n get currentToken(): JwtCompositeToken | null {\n return this.tokenStorage.get();\n }\n\n /**\n * Refreshes the JWT token\n * @param currentToken Optional current token to refresh. If not provided, uses the stored token.\n * @returns Promise that resolves when refresh is complete\n * @throws Error if no token is found or refresh fails\n */\n async refresh(currentToken?: CompositeToken): Promise<void> {\n if (!currentToken) {\n const jwtToken = this.currentToken;\n if (!jwtToken) {\n throw new Error('No token found');\n }\n currentToken = jwtToken.token;\n }\n if (this.refreshInProgress) {\n return this.refreshInProgress;\n }\n\n this.refreshInProgress = this.tokenRefresher.refresh(currentToken)\n .then(newToken => {\n this.tokenStorage.setCompositeToken(newToken);\n })\n .catch(error => {\n this.tokenStorage.remove();\n throw error;\n })\n .finally(() => {\n this.refreshInProgress = undefined;\n });\n\n return this.refreshInProgress;\n }\n\n /**\n * Indicates if the current token needs to be refreshed\n * @returns true if the access token is expired and needs refresh, false otherwise\n */\n get isRefreshNeeded(): boolean {\n if (!this.currentToken) {\n return false;\n }\n return this.currentToken.isRefreshNeeded;\n }\n\n /**\n * Indicates if the current token can be refreshed\n * @returns true if the refresh token is still valid, false otherwise\n */\n get isRefreshable(): boolean {\n if (!this.currentToken) {\n return false;\n }\n return this.currentToken.isRefreshable;\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 { Fetcher, ResultExtractors } from '@ahoo-wang/fetcher';\nimport { IGNORE_REFRESH_TOKEN_ATTRIBUTE_KEY } from './cosecRequestInterceptor';\n\n/**\n * Interface for access tokens.\n */\nexport interface AccessToken {\n accessToken: string;\n}\n\n/**\n * Interface for refresh tokens.\n */\nexport interface RefreshToken {\n refreshToken: string;\n}\n\n/**\n * Composite token interface that contains both access and refresh tokens.\n *\n * accessToken and refreshToken always appear in pairs, no need to split them.\n */\nexport interface CompositeToken extends AccessToken, RefreshToken {\n}\n\n/**\n * Interface for token refreshers.\n *\n * Provides a method to refresh tokens.\n */\nexport interface TokenRefresher {\n /**\n * Refresh the given token and return a new CompositeToken.\n *\n * @param token The token to refresh\n * @returns A Promise that resolves to a new CompositeToken\n */\n refresh(token: CompositeToken): Promise<CompositeToken>;\n}\n\nexport interface CoSecTokenRefresherOptions {\n fetcher: Fetcher;\n endpoint: string;\n}\n\n/**\n * CoSecTokenRefresher is a class that implements the TokenRefresher interface\n * for refreshing composite tokens through a configured endpoint.\n */\nexport class CoSecTokenRefresher implements TokenRefresher {\n /**\n * Creates a new instance of CoSecTokenRefresher.\n *\n * @param options The configuration options for the token refresher including fetcher and endpoint\n */\n constructor(public readonly options: CoSecTokenRefresherOptions) {\n }\n\n /**\n * Refresh the given token and return a new CompositeToken.\n *\n * @param token The token to refresh\n * @returns A Promise that resolves to a new CompositeToken\n */\n refresh(token: CompositeToken): Promise<CompositeToken> {\n // Send a POST request to the configured endpoint with the token as body\n // and extract the response as JSON to return a new CompositeToken\n return this.options.fetcher.post<CompositeToken>(this.options.endpoint,\n {\n body: token,\n }, ResultExtractors.Json,\n {\n [IGNORE_REFRESH_TOKEN_ATTRIBUTE_KEY]: true,\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 { createListenableStorage, KeyStorage } from './storage';\nimport { JwtCompositeToken, JwtCompositeTokenSerializer } from './jwtToken';\nimport { CompositeToken } from './tokenRefresher';\nimport { EarlyPeriodCapable } from './jwts';\n\nexport const DEFAULT_COSEC_TOKEN_KEY = 'cosec-token';\n\n/**\n * Storage class for managing access and refresh tokens.\n */\nexport class TokenStorage extends KeyStorage<JwtCompositeToken> implements EarlyPeriodCapable {\n\n constructor(\n key: string = DEFAULT_COSEC_TOKEN_KEY,\n public readonly earlyPeriod: number = 0,\n ) {\n super({\n key,\n storage: createListenableStorage(),\n serializer: new JwtCompositeTokenSerializer(earlyPeriod),\n });\n }\n\n setCompositeToken(compositeToken: CompositeToken) {\n this.set(\n new JwtCompositeToken(compositeToken),\n );\n }\n}\n"],"names":["isBrowser","InMemoryListenableStorage","key","value","index","oldValue","listener","eventInit","error","BrowserListenableStorage","storage","wrapper","event","STORAGE_EVENT_TYPE","createListenableStorage","JsonSerializer","IdentitySerializer","jsonSerializer","identitySerializer","typedIdentitySerializer","KeyStorage","options","serialized","_CoSecHeaders","CoSecHeaders","_ResponseCodes","ResponseCodes","AuthorizeResults","NanoIdGenerator","nanoid","idGenerator","COSEC_REQUEST_INTERCEPTOR_NAME","COSEC_REQUEST_INTERCEPTOR_ORDER","REQUEST_BODY_INTERCEPTOR_ORDER","IGNORE_REFRESH_TOKEN_ATTRIBUTE_KEY","CoSecRequestInterceptor","exchange","requestId","deviceId","requestHeaders","currentToken","COSEC_RESPONSE_INTERCEPTOR_NAME","COSEC_RESPONSE_INTERCEPTOR_ORDER","CoSecResponseInterceptor","response","DEFAULT_COSEC_DEVICE_ID_KEY","DeviceIdStorage","parseJwtPayload","token","parts","base64","paddedBase64","jsonPayload","c","isTokenExpired","earlyPeriod","payload","expAt","JwtToken","JwtCompositeToken","JwtCompositeTokenSerializer","compositeToken","jwtCompositeTokenSerializer","JwtTokenManager","tokenStorage","tokenRefresher","jwtToken","newToken","CoSecTokenRefresher","ResultExtractors","DEFAULT_COSEC_TOKEN_KEY","TokenStorage"],"mappings":"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"}
package/dist/index.umd.js CHANGED
@@ -1,2 +1,2 @@
1
- (function(r,h){typeof exports=="object"&&typeof module<"u"?h(exports,require("@ahoo-wang/fetcher"),require("nanoid")):typeof define=="function"&&define.amd?define(["exports","@ahoo-wang/fetcher","nanoid"],h):(r=typeof globalThis<"u"?globalThis:r||self,h(r.FetcherCoSec={},r.Fetcher,r.nanoid))})(this,(function(r,h,V){"use strict";function d(){return typeof window<"u"}class O{constructor(){this.store=new Map,this.listeners=new Set}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){const t=this.getItem(e);this.store.has(e)&&(this.store.delete(e),this.notifyListeners({key:e,oldValue:t,newValue:null}))}setItem(e,t){const i=this.getItem(e);this.store.set(e,t),this.notifyListeners({key:e,oldValue:i,newValue:t})}addListener(e){return this.listeners.add(e),()=>this.listeners.delete(e)}notifyListeners(e){d()&&window.location&&(e.url=e.url||window.location.href),e.storageArea=this,this.listeners.forEach(t=>{try{t(e)}catch(i){console.error("Error in storage change listener:",i)}})}}class _{constructor(e){this.storage=e}get length(){return this.storage.length}addListener(e){const t=i=>{i.storageArea===this.storage&&e(i)};return window.addEventListener(E,t),()=>window.removeEventListener(E,t)}clear(){this.storage.clear()}getItem(e){return this.storage.getItem(e)}key(e){return this.storage.key(e)}removeItem(e){this.storage.removeItem(e)}setItem(e,t){this.storage.setItem(e,t)}}const E="storage",c=()=>d()?new _(window.localStorage):new O;class y{serialize(e){return JSON.stringify(e)}deserialize(e){return JSON.parse(e)}}class g{serialize(e){return e}deserialize(e){return e}}const v=new y,k=new g;function p(){return k}class f{constructor(e){this.cacheValue=null,this.listener=t=>{t.key===this.key&&(this.cacheValue=null,t.newValue&&this.refreshCache(t.newValue))},this.key=e.key,this.serializer=e.serializer??p(),this.storage=e.storage??c(),this.storage.addListener(this.listener)}refreshCache(e){this.cacheValue=this.serializer.deserialize(e)}get(){if(this.cacheValue)return this.cacheValue;const e=this.storage.getItem(this.key);return e?(this.refreshCache(e),this.cacheValue):null}set(e){const t=this.serializer.serialize(e);this.storage.setItem(this.key,t),this.cacheValue=e}remove(){this.storage.removeItem(this.key),this.cacheValue=null}}const a=class a{};a.DEVICE_ID="CoSec-Device-Id",a.APP_ID="CoSec-App-Id",a.AUTHORIZATION="Authorization",a.REQUEST_ID="CoSec-Request-Id";let o=a;const C=class C{};C.UNAUTHORIZED=401;let l=C;const J={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 m{generateId(){return V.nanoid()}}const I=new m,N="CoSecRequestInterceptor",P=h.REQUEST_BODY_INTERCEPTOR_ORDER+1e3;class b{constructor(e){this.name=N,this.order=P,this.options=e}intercept(e){const t=I.generateId(),i=this.options.deviceIdStorage.getOrCreate(),n=e.ensureRequestHeaders();n[o.APP_ID]=this.options.appId,n[o.DEVICE_ID]=i,n[o.REQUEST_ID]=t;const u=this.options.tokenStorage.get();u&&!n[o.AUTHORIZATION]&&(n[o.AUTHORIZATION]=`Bearer ${u.access.token}`)}}const z="CoSecResponseInterceptor",D=Number.MIN_SAFE_INTEGER+1e3;class M{constructor(e){this.name=z,this.order=D,this.options=e}async refresh(e){return this.refreshInProgress?this.refreshInProgress:(this.refreshInProgress=this.options.tokenRefresher.refresh(e).then(t=>(this.options.tokenStorage.setCompositeToken(t),t)).catch(t=>{throw this.options.tokenStorage.remove(),t}).finally(()=>{this.refreshInProgress=void 0}),this.refreshInProgress)}async intercept(e){const t=e.response;if(!t||t.status!==l.UNAUTHORIZED)return;const i=this.options.tokenStorage.get();if(i)try{await this.refresh(i.token),await e.fetcher.interceptors.exchange(e)}catch(n){throw this.options.tokenStorage.remove(),n}}}const A="cosec-device-id";class j extends f{constructor(e=A){super({key:e,serializer:new g,storage:c()})}generateDeviceId(){return I.generateId()}getOrCreate(){let e=this.get();return e||(e=this.generateDeviceId(),this.set(e)),e}}function S(s){try{const e=s.split(".");if(e.length!==3)return null;const i=e[1].replace(/-/g,"+").replace(/_/g,"/"),n=i.padEnd(i.length+(4-i.length%4)%4,"="),u=decodeURIComponent(atob(n).split("").map(function(F){return"%"+("00"+F.charCodeAt(0).toString(16)).slice(-2)}).join(""));return JSON.parse(u)}catch(e){return console.error("Failed to parse JWT token",e),null}}function L(s,e=0){const t=typeof s=="string"?S(s):s;if(!t)return!0;const i=t.exp;return i?Date.now()/1e3>i-e:!1}class T{constructor(e,t=0){this.token=e,this.earlyPeriod=t,this.payload=S(e)}get isExpired(){return L(this.payload,this.earlyPeriod)}}class R{constructor(e,t=0){this.token=e,this.earlyPeriod=t,this.access=new T(e.accessToken,t),this.refresh=new T(e.refreshToken,t)}get isRefreshNeeded(){return this.access.isExpired}get isRefreshable(){return!this.refresh.isExpired}}class w{constructor(e=0){this.earlyPeriod=e}deserialize(e){const t=JSON.parse(e);return new R(t,this.earlyPeriod)}serialize(e){return JSON.stringify(e.token)}}const q=new w;class Y{constructor(e,t){this.tokenStorage=e,this.tokenRefresher=t}get currentToken(){return this.tokenStorage.get()}async refresh(e){if(!e){const t=this.currentToken;if(!t)throw new Error("No token found");e=t.token}return this.refreshInProgress?this.refreshInProgress:(this.refreshInProgress=this.tokenRefresher.refresh(e).then(t=>{this.tokenStorage.setCompositeToken(t)}).catch(t=>{throw this.tokenStorage.remove(),t}).finally(()=>{this.refreshInProgress=void 0}),this.refreshInProgress)}get isRefreshNeeded(){return this.currentToken?this.currentToken.isRefreshNeeded:!1}get isRefreshable(){return this.currentToken?this.currentToken.isRefreshable:!1}}const U="cosec-token";class K extends f{constructor(e=U,t=0){super({key:e,storage:c(),serializer:new w(t)}),this.earlyPeriod=t}setCompositeToken(e){this.set(new R(e))}}r.AuthorizeResults=J,r.BrowserListenableStorage=_,r.COSEC_REQUEST_INTERCEPTOR_NAME=N,r.COSEC_REQUEST_INTERCEPTOR_ORDER=P,r.COSEC_RESPONSE_INTERCEPTOR_NAME=z,r.COSEC_RESPONSE_INTERCEPTOR_ORDER=D,r.CoSecHeaders=o,r.CoSecRequestInterceptor=b,r.CoSecResponseInterceptor=M,r.DEFAULT_COSEC_DEVICE_ID_KEY=A,r.DEFAULT_COSEC_TOKEN_KEY=U,r.DeviceIdStorage=j,r.IdentitySerializer=g,r.InMemoryListenableStorage=O,r.JsonSerializer=y,r.JwtCompositeToken=R,r.JwtCompositeTokenSerializer=w,r.JwtToken=T,r.JwtTokenManager=Y,r.KeyStorage=f,r.NanoIdGenerator=m,r.ResponseCodes=l,r.STORAGE_EVENT_TYPE=E,r.TokenStorage=K,r.createListenableStorage=c,r.idGenerator=I,r.identitySerializer=k,r.isBrowser=d,r.isTokenExpired=L,r.jsonSerializer=v,r.jwtCompositeTokenSerializer=q,r.parseJwtPayload=S,r.typedIdentitySerializer=p,Object.defineProperty(r,Symbol.toStringTag,{value:"Module"})}));
1
+ (function(r,c){typeof exports=="object"&&typeof module<"u"?c(exports,require("@ahoo-wang/fetcher"),require("nanoid")):typeof define=="function"&&define.amd?define(["exports","@ahoo-wang/fetcher","nanoid"],c):(r=typeof globalThis<"u"?globalThis:r||self,c(r.FetcherCoSec={},r.Fetcher,r.nanoid))})(this,(function(r,c,V){"use strict";function E(){return typeof window<"u"}class O{constructor(){this.store=new Map,this.listeners=new Set}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){const t=this.getItem(e);this.store.has(e)&&(this.store.delete(e),this.notifyListeners({key:e,oldValue:t,newValue:null}))}setItem(e,t){const i=this.getItem(e);this.store.set(e,t),this.notifyListeners({key:e,oldValue:i,newValue:t})}addListener(e){return this.listeners.add(e),()=>this.listeners.delete(e)}notifyListeners(e){E()&&window.location&&(e.url=e.url||window.location.href),e.storageArea=this,this.listeners.forEach(t=>{try{t(e)}catch(i){console.error("Error in storage change listener:",i)}})}}class k{constructor(e){this.storage=e}get length(){return this.storage.length}addListener(e){const t=i=>{i.storageArea===this.storage&&e(i)};return window.addEventListener(d,t),()=>window.removeEventListener(d,t)}clear(){this.storage.clear()}getItem(e){return this.storage.getItem(e)}key(e){return this.storage.key(e)}removeItem(e){this.storage.removeItem(e)}setItem(e,t){this.storage.setItem(e,t)}}const d="storage",l=()=>E()?new k(window.localStorage):new O;class y{serialize(e){return JSON.stringify(e)}deserialize(e){return JSON.parse(e)}}class T{serialize(e){return e}deserialize(e){return e}}const b=new y,p=new T;function N(){return p}class f{constructor(e){this.cacheValue=null,this.listener=t=>{t.key===this.key&&(this.cacheValue=null,t.newValue&&this.refreshCache(t.newValue))},this.key=e.key,this.serializer=e.serializer??N(),this.storage=e.storage??l(),this.storage.addListener(this.listener)}refreshCache(e){this.cacheValue=this.serializer.deserialize(e)}get(){if(this.cacheValue)return this.cacheValue;const e=this.storage.getItem(this.key);return e?(this.refreshCache(e),this.cacheValue):null}set(e){const t=this.serializer.serialize(e);this.storage.setItem(this.key,t),this.cacheValue=e}remove(){this.storage.removeItem(this.key),this.cacheValue=null}}const h=class h{};h.DEVICE_ID="CoSec-Device-Id",h.APP_ID="CoSec-App-Id",h.AUTHORIZATION="Authorization",h.REQUEST_ID="CoSec-Request-Id";let n=h;const C=class C{};C.UNAUTHORIZED=401;let u=C;const J={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 m{generateId(){return V.nanoid()}}const g=new m,z="CoSecRequestInterceptor",A=c.REQUEST_BODY_INTERCEPTOR_ORDER+1e3,I="Ignore-Refresh-Token";class v{constructor(e){this.name=z,this.order=A,this.options=e}async intercept(e){const t=g.generateId(),i=this.options.deviceIdStorage.getOrCreate(),o=e.ensureRequestHeaders();o[n.APP_ID]=this.options.appId,o[n.DEVICE_ID]=i,o[n.REQUEST_ID]=t;let a=this.options.tokenManager.currentToken;!a||o[n.AUTHORIZATION]||(!e.attributes[I]&&a.isRefreshNeeded&&a.isRefreshable&&await this.options.tokenManager.refresh(),a=this.options.tokenManager.currentToken,a&&(o[n.AUTHORIZATION]=`Bearer ${a.access.token}`))}}const D="CoSecResponseInterceptor",P=Number.MIN_SAFE_INTEGER+1e3;class K{constructor(e){this.name=D,this.order=P,this.options=e}async intercept(e){const t=e.response;if(t&&t.status===u.UNAUTHORIZED&&this.options.tokenManager.isRefreshable)try{await this.options.tokenManager.refresh(),await e.fetcher.interceptors.exchange(e)}catch(i){throw this.options.tokenManager.tokenStorage.remove(),i}}}const U="cosec-device-id";class Y extends f{constructor(e=U){super({key:e,serializer:new T,storage:l()})}generateDeviceId(){return g.generateId()}getOrCreate(){let e=this.get();return e||(e=this.generateDeviceId(),this.set(e)),e}}function S(s){try{const e=s.split(".");if(e.length!==3)return null;const i=e[1].replace(/-/g,"+").replace(/_/g,"/"),o=i.padEnd(i.length+(4-i.length%4)%4,"="),a=decodeURIComponent(atob(o).split("").map(function(G){return"%"+("00"+G.charCodeAt(0).toString(16)).slice(-2)}).join(""));return JSON.parse(a)}catch(e){return console.error("Failed to parse JWT token",e),null}}function L(s,e=0){const t=typeof s=="string"?S(s):s;if(!t)return!0;const i=t.exp;return i?Date.now()/1e3>i-e:!1}class R{constructor(e,t=0){this.token=e,this.earlyPeriod=t,this.payload=S(e)}get isExpired(){return L(this.payload,this.earlyPeriod)}}class _{constructor(e,t=0){this.token=e,this.earlyPeriod=t,this.access=new R(e.accessToken,t),this.refresh=new R(e.refreshToken,t)}get isRefreshNeeded(){return this.access.isExpired}get isRefreshable(){return!this.refresh.isExpired}}class w{constructor(e=0){this.earlyPeriod=e}deserialize(e){const t=JSON.parse(e);return new _(t,this.earlyPeriod)}serialize(e){return JSON.stringify(e.token)}}const q=new w;class F{constructor(e,t){this.tokenStorage=e,this.tokenRefresher=t}get currentToken(){return this.tokenStorage.get()}async refresh(e){if(!e){const t=this.currentToken;if(!t)throw new Error("No token found");e=t.token}return this.refreshInProgress?this.refreshInProgress:(this.refreshInProgress=this.tokenRefresher.refresh(e).then(t=>{this.tokenStorage.setCompositeToken(t)}).catch(t=>{throw this.tokenStorage.remove(),t}).finally(()=>{this.refreshInProgress=void 0}),this.refreshInProgress)}get isRefreshNeeded(){return this.currentToken?this.currentToken.isRefreshNeeded:!1}get isRefreshable(){return this.currentToken?this.currentToken.isRefreshable:!1}}class j{constructor(e){this.options=e}refresh(e){return this.options.fetcher.post(this.options.endpoint,{body:e},c.ResultExtractors.Json,{[I]:!0})}}const M="cosec-token";class B extends f{constructor(e=M,t=0){super({key:e,storage:l(),serializer:new w(t)}),this.earlyPeriod=t}setCompositeToken(e){this.set(new _(e))}}r.AuthorizeResults=J,r.BrowserListenableStorage=k,r.COSEC_REQUEST_INTERCEPTOR_NAME=z,r.COSEC_REQUEST_INTERCEPTOR_ORDER=A,r.COSEC_RESPONSE_INTERCEPTOR_NAME=D,r.COSEC_RESPONSE_INTERCEPTOR_ORDER=P,r.CoSecHeaders=n,r.CoSecRequestInterceptor=v,r.CoSecResponseInterceptor=K,r.CoSecTokenRefresher=j,r.DEFAULT_COSEC_DEVICE_ID_KEY=U,r.DEFAULT_COSEC_TOKEN_KEY=M,r.DeviceIdStorage=Y,r.IGNORE_REFRESH_TOKEN_ATTRIBUTE_KEY=I,r.IdentitySerializer=T,r.InMemoryListenableStorage=O,r.JsonSerializer=y,r.JwtCompositeToken=_,r.JwtCompositeTokenSerializer=w,r.JwtToken=R,r.JwtTokenManager=F,r.KeyStorage=f,r.NanoIdGenerator=m,r.ResponseCodes=u,r.STORAGE_EVENT_TYPE=d,r.TokenStorage=B,r.createListenableStorage=l,r.idGenerator=g,r.identitySerializer=p,r.isBrowser=E,r.isTokenExpired=L,r.jsonSerializer=b,r.jwtCompositeTokenSerializer=q,r.parseJwtPayload=S,r.typedIdentitySerializer=N,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/env.ts","../src/storage/inMemoryListenableStorage.ts","../src/storage/browserListenableStorage.ts","../src/storage/listenableStorage.ts","../src/serializer.ts","../src/storage/keyStorage.ts","../src/types.ts","../src/idGenerator.ts","../src/cosecRequestInterceptor.ts","../src/cosecResponseInterceptor.ts","../src/deviceIdStorage.ts","../src/jwts.ts","../src/jwtToken.ts","../src/jwtTokenManager.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\nexport function isBrowser(): boolean {\n return typeof window !== 'undefined';\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 { ListenableStorage, RemoveStorageListener, StorageListener } from './listenableStorage';\nimport { isBrowser } from '../env';\n\n/**\n * An in-memory implementation of ListenableStorage that works in any environment.\n * This implementation stores data in a Map and manually fires storage events when data changes.\n */\nexport class InMemoryListenableStorage implements ListenableStorage {\n private readonly store: Map<string, string> = new Map();\n private readonly listeners: Set<StorageListener> = new Set();\n\n /**\n * Gets the number of items stored in the storage.\n */\n get length(): number {\n return this.store.size;\n }\n\n /**\n * Clears all items from the storage.\n */\n clear(): void {\n this.store.clear();\n }\n\n /**\n * Gets an item from the storage.\n * @param key - The key of the item to retrieve\n * @returns The value of the item, or null if the item does not exist\n */\n getItem(key: string): string | null {\n const value = this.store.get(key);\n return value !== undefined ? value : null;\n }\n\n /**\n * Gets the key at the specified index.\n * @param index - The index of the key to retrieve\n * @returns The key at the specified index, or null if the index is out of bounds\n */\n key(index: number): string | null {\n const keys = Array.from(this.store.keys());\n return keys[index] || null;\n }\n\n /**\n * Removes an item from the storage.\n * @param key - The key of the item to remove\n */\n removeItem(key: string): void {\n const oldValue = this.getItem(key);\n if (this.store.has(key)) {\n this.store.delete(key);\n this.notifyListeners({\n key,\n oldValue,\n newValue: null,\n });\n }\n }\n\n /**\n * Sets an item in the storage.\n * @param key - The key of the item to set\n * @param value - The value to set\n */\n setItem(key: string, value: string): void {\n const oldValue = this.getItem(key);\n this.store.set(key, value);\n this.notifyListeners({ key, oldValue, newValue: value });\n }\n\n /**\n * Adds a listener for storage changes.\n * @param listener - The listener function to be called when storage changes\n * @returns A function that can be called to remove the listener\n */\n addListener(listener: StorageListener): RemoveStorageListener {\n this.listeners.add(listener);\n return () => this.listeners.delete(listener);\n }\n\n /**\n * Notifies all listeners of a storage change by creating and dispatching a StorageEvent.\n * @param eventInit - The initialization object for the StorageEvent\n */\n private notifyListeners(\n eventInit: StorageEventInit,\n ): void {\n if (isBrowser() && window.location) {\n eventInit.url = eventInit.url || window.location.href;\n }\n eventInit.storageArea = this;\n this.listeners.forEach(listener => {\n try {\n listener(eventInit);\n } catch (error) {\n console.error('Error in storage change listener:', 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\nimport { ListenableStorage, RemoveStorageListener, STORAGE_EVENT_TYPE, StorageListener } from './listenableStorage';\n\n/**\n * A wrapper around the browser's native Storage (localStorage or sessionStorage)\n * that implements the ListenableStorage interface by using the browser's native storage events.\n */\nexport class BrowserListenableStorage implements ListenableStorage {\n\n /**\n * Creates a new BrowserListenableStorage instance.\n * @param storage - The native Storage object to wrap (e.g., localStorage or sessionStorage)\n */\n constructor(private readonly storage: Storage) {\n }\n\n /**\n * Gets the number of items stored in the storage.\n */\n get length(): number {\n return this.storage.length;\n }\n\n /**\n * Adds a listener for storage changes.\n * @param listener - The listener function to be called when storage changes\n * @returns A function that can be called to remove the listener\n */\n addListener(listener: StorageListener): RemoveStorageListener {\n const wrapper: StorageListener = (event: StorageEventInit) => {\n if (event.storageArea === this.storage) {\n listener(event);\n }\n };\n window.addEventListener(STORAGE_EVENT_TYPE, wrapper);\n return () => window.removeEventListener(STORAGE_EVENT_TYPE, wrapper);\n }\n\n /**\n * Clears all items from the storage.\n */\n clear(): void {\n this.storage.clear();\n }\n\n /**\n * Gets an item from the storage.\n * @param key - The key of the item to retrieve\n * @returns The value of the item, or null if the item does not exist\n */\n getItem(key: string): string | null {\n return this.storage.getItem(key);\n }\n\n /**\n * Gets the key at the specified index.\n * @param index - The index of the key to retrieve\n * @returns The key at the specified index, or null if the index is out of bounds\n */\n key(index: number): string | null {\n return this.storage.key(index);\n }\n\n /**\n * Removes an item from the storage.\n * @param key - The key of the item to remove\n */\n removeItem(key: string): void {\n this.storage.removeItem(key);\n }\n\n /**\n * Sets an item in the storage.\n * @param key - The key of the item to set\n * @param value - The value to set\n */\n setItem(key: string, value: string): void {\n this.storage.setItem(key, value);\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 { InMemoryListenableStorage } from './inMemoryListenableStorage';\nimport { BrowserListenableStorage } from './browserListenableStorage';\nimport { isBrowser } from '../env';\n\n/**\n * The type of storage event used for listening to storage changes.\n */\nexport const STORAGE_EVENT_TYPE = 'storage';\n\n/**\n * A function that handles storage change events.\n */\nexport type StorageListener = (event: StorageEventInit) => void;\n\n/**\n * A function that removes a storage listener when called.\n */\nexport type RemoveStorageListener = () => void;\n\nexport interface StorageListenable {\n /**\n * Adds a listener for storage changes.\n * @param listener - The listener function to be called when storage changes\n * @returns A function that can be called to remove the listener\n */\n addListener(listener: StorageListener): RemoveStorageListener;\n}\n\n/**\n * An interface that extends the native Storage interface with the ability to listen for storage changes.\n */\nexport interface ListenableStorage extends Storage, StorageListenable {\n\n}\n\n/**\n * Factory function to get an appropriate ListenableStorage implementation based on the environment.\n * In a browser environment, it returns a BrowserListenableStorage wrapping localStorage.\n * In other environments, it returns an InMemoryListenableStorage.\n * @returns A ListenableStorage instance suitable for the current environment\n */\nexport const createListenableStorage = (): ListenableStorage => {\n if (isBrowser()) {\n return new BrowserListenableStorage(window.localStorage);\n }\n return new InMemoryListenableStorage();\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 * Interface for serializing and deserializing values\n * @template Serialized The type of the serialized value\n */\n/**\n * Interface for serializing and deserializing values\n * @template Serialized The type of the serialized value\n * @template Deserialized The type of the deserialized value\n */\nexport interface Serializer<Serialized, Deserialized> {\n /**\n * Serializes a value to the specified format\n * @param value The value to serialize\n * @returns The serialized value\n */\n serialize(value: any): Serialized;\n\n /**\n * Deserializes a value from the specified format\n * @template Deserialized The type of the deserialized value\n * @param value The value to deserialize\n * @returns The deserialized value\n */\n /**\n * Deserializes a value from the specified format\n * @param value The value to deserialize\n * @returns The deserialized value\n */\n deserialize(value: Serialized): Deserialized;\n}\n\n/**\n * Implementation of Serializer that uses JSON for serialization\n */\nexport class JsonSerializer implements Serializer<string, any> {\n /**\n * Serializes a value to a JSON string\n * @param value The value to serialize\n * @returns The JSON string representation of the value\n */\n /**\n * Serializes a value to a JSON string\n * @param value The value to serialize\n * @returns The JSON string representation of the value\n */\n serialize(value: any): string {\n return JSON.stringify(value);\n }\n\n /**\n * Deserializes a JSON string to a value\n * @template V The type of the deserialized value\n * @param value The JSON string to deserialize\n * @returns The deserialized value\n */\n /**\n * Deserializes a JSON string to a value\n * @param value The JSON string to deserialize\n * @returns The deserialized value\n */\n deserialize<V>(value: string): V {\n return JSON.parse(value);\n }\n}\n\n/**\n * Implementation of Serializer that performs no actual serialization\n * @template T The type of the value to pass through\n */\nexport class IdentitySerializer<T> implements Serializer<T, T> {\n /**\n * Returns the value as-is without serialization\n * @param value The value to pass through\n * @returns The same value that was passed in\n */\n /**\n * Returns the value as-is without serialization\n * @param value The value to pass through\n * @returns The same value that was passed in\n */\n serialize(value: T): T {\n return value;\n }\n\n /**\n * Returns the value as-is without deserialization\n * @template Deserialized The type of the deserialized value\n * @param value The value to pass through\n * @returns The same value that was passed in, cast to the target type\n */\n /**\n * Returns the value as-is without deserialization\n * @param value The value to pass through\n * @returns The same value that was passed in\n */\n deserialize(value: T): T {\n return value;\n }\n}\n\n/**\n * Global instance of JsonSerializer\n */\nexport const jsonSerializer = new JsonSerializer();\n/**\n * Global instance of IdentitySerializer\n */\nexport const identitySerializer = new IdentitySerializer<any>();\n\nexport function typedIdentitySerializer<T>(): IdentitySerializer<T> {\n return identitySerializer as IdentitySerializer<T>;\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 { createListenableStorage, ListenableStorage, StorageListener } from './listenableStorage';\nimport { Serializer, typedIdentitySerializer } from '../serializer';\n\n/**\n * Options for configuring KeyStorage\n */\nexport interface KeyStorageOptions<Deserialized> {\n /**\n * The key used to store and retrieve values from storage\n */\n key: string;\n\n /**\n * Optional serializer for converting values to and from storage format\n * Defaults to IdentitySerializer if not provided\n */\n serializer?: Serializer<string, Deserialized>;\n\n /**\n * Optional storage implementation\n * Defaults to the result of createListenableStorage() if not provided\n */\n storage?: ListenableStorage;\n}\n\n/**\n * A storage wrapper that manages a single value associated with a specific key\n * Provides caching and automatic cache invalidation when the storage value changes\n * @template Deserialized The type of the value being stored\n */\nexport class KeyStorage<Deserialized> {\n private readonly key: string;\n private readonly serializer: Serializer<string, Deserialized>;\n private readonly storage: ListenableStorage;\n private cacheValue: Deserialized | null = null;\n\n /**\n * Listener for storage change events\n * Invalidates the cache when the relevant key is modified\n */\n private readonly listener: StorageListener = (event) => {\n if (event.key !== this.key) {\n return;\n }\n this.cacheValue = null;\n if (event.newValue) {\n this.refreshCache(event.newValue);\n }\n };\n\n /**\n * Refreshes the cached value by deserializing the provided string\n * @param value The serialized value to deserialize and cache\n */\n private refreshCache(value: string) {\n this.cacheValue = this.serializer.deserialize(value);\n }\n\n /**\n * Creates a new KeyStorage instance\n * @param options Configuration options for the storage\n */\n constructor(options: KeyStorageOptions<Deserialized>) {\n this.key = options.key;\n this.serializer = options.serializer ?? typedIdentitySerializer();\n this.storage = options.storage ?? createListenableStorage();\n this.storage.addListener(this.listener);\n }\n\n /**\n * Gets the value associated with the key from storage\n * Uses cached value if available, otherwise retrieves from storage and caches it\n * @returns The deserialized value or null if no value is found\n */\n get(): Deserialized | null {\n if (this.cacheValue) {\n return this.cacheValue;\n }\n const value = this.storage.getItem(this.key);\n if (!value) {\n return null;\n }\n this.refreshCache(value);\n return this.cacheValue;\n }\n\n /**\n * Sets the value associated with the key in storage\n * Also updates the cached value\n * @param value The value to serialize and store\n */\n set(value: Deserialized): void {\n const serialized = this.serializer.serialize(value);\n this.storage.setItem(this.key, serialized);\n this.cacheValue = value;\n }\n\n /**\n * Removes the value associated with the key from storage\n * Also clears the cached value\n */\n remove(): void {\n this.storage.removeItem(this.key);\n this.cacheValue = null;\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 { 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 requestHeaders = exchange.ensureRequestHeaders();\n requestHeaders[CoSecHeaders.APP_ID] = this.options.appId;\n requestHeaders[CoSecHeaders.DEVICE_ID] = deviceId;\n requestHeaders[CoSecHeaders.REQUEST_ID] = requestId;\n const token = this.options.tokenStorage.get();\n if (token && !requestHeaders[CoSecHeaders.AUTHORIZATION]) {\n requestHeaders[CoSecHeaders.AUTHORIZATION] =\n `Bearer ${token.access.token}`;\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.setCompositeToken(newToken);\n return newToken;\n })\n .catch(error => {\n this.options.tokenStorage.remove();\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 jwtToken = this.options.tokenStorage.get();\n // If there's no current token, we can't refresh it\n if (!jwtToken) {\n return;\n }\n\n try {\n // Attempt to refresh the token\n await this.refresh(jwtToken.token);\n // Retry the original request with the new token\n await exchange.fetcher.interceptors.exchange(exchange);\n } catch (error) {\n // If token refresh fails, clear stored tokens and re-throw the error\n this.options.tokenStorage.remove();\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\nimport { idGenerator } from './idGenerator';\nimport { createListenableStorage, KeyStorage } from './storage';\nimport { IdentitySerializer } from './serializer';\n\nexport const DEFAULT_COSEC_DEVICE_ID_KEY = 'cosec-device-id';\n\n/**\n * Storage class for managing device identifiers.\n */\nexport class DeviceIdStorage extends KeyStorage<string> {\n constructor(key: string = DEFAULT_COSEC_DEVICE_ID_KEY) {\n super({\n key,\n serializer: new IdentitySerializer<string>(),\n storage: createListenableStorage(),\n });\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","/*\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\nexport interface EarlyPeriodCapable {\n /**\n * The time in seconds before actual expiration when the token should be considered expired (default: 0)\n */\n readonly earlyPeriod: number;\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 { CoSecJwtPayload, EarlyPeriodCapable, isTokenExpired, JwtPayload, parseJwtPayload } from './jwts';\nimport { Serializer } from './serializer';\nimport { CompositeToken } from './tokenRefresher';\n\n\n/**\n * Interface for JWT token with typed payload\n * @template Payload The type of the JWT payload\n */\nexport interface IJwtToken<Payload extends JwtPayload> extends EarlyPeriodCapable {\n readonly token: string;\n readonly payload: Payload;\n\n isExpired: boolean;\n}\n\n/**\n * Class representing a JWT token with typed payload\n * @template Payload The type of the JWT payload\n */\nexport class JwtToken<Payload extends JwtPayload> implements IJwtToken<Payload> {\n public readonly payload: Payload;\n\n /**\n * Creates a new JwtToken instance\n */\n constructor(\n public readonly token: string,\n public readonly earlyPeriod: number = 0,\n ) {\n this.payload = parseJwtPayload(token) as Payload;\n }\n\n /**\n * Checks if the token is expired\n * @returns true if the token is expired, false otherwise\n */\n get isExpired(): boolean {\n return isTokenExpired(this.payload, this.earlyPeriod);\n }\n}\n\nexport interface RefreshTokenStatusCapable {\n /**\n * Checks if the access token needs to be refreshed\n * @returns true if the access token is expired, false otherwise\n */\n readonly isRefreshNeeded: boolean;\n /**\n * Checks if the refresh token is still valid and can be used to refresh the access token\n * @returns true if the refresh token is not expired, false otherwise\n */\n readonly isRefreshable: boolean;\n}\n\n/**\n * Class representing a composite token containing both access and refresh tokens\n */\nexport class JwtCompositeToken implements EarlyPeriodCapable, RefreshTokenStatusCapable {\n public readonly access: JwtToken<CoSecJwtPayload>;\n public readonly refresh: JwtToken<JwtPayload>;\n\n /**\n * Creates a new JwtCompositeToken instance\n */\n constructor(public readonly token: CompositeToken, public readonly earlyPeriod: number = 0) {\n this.access = new JwtToken(token.accessToken, earlyPeriod);\n this.refresh = new JwtToken(token.refreshToken, earlyPeriod);\n }\n\n /**\n * Checks if the access token needs to be refreshed\n * @returns true if the access token is expired, false otherwise\n */\n get isRefreshNeeded(): boolean {\n return this.access.isExpired;\n }\n\n /**\n * Checks if the refresh token is still valid and can be used to refresh the access token\n * @returns true if the refresh token is not expired, false otherwise\n */\n get isRefreshable(): boolean {\n return !this.refresh.isExpired;\n }\n\n}\n\n/**\n * Serializer for JwtCompositeToken that handles conversion to and from JSON strings\n */\nexport class JwtCompositeTokenSerializer implements Serializer<string, JwtCompositeToken>, EarlyPeriodCapable {\n constructor(public readonly earlyPeriod: number = 0) {\n }\n\n /**\n * Deserializes a JSON string to a JwtCompositeToken\n * @param value The JSON string representation of a composite token\n * @returns A JwtCompositeToken instance\n */\n deserialize(value: string): JwtCompositeToken {\n const compositeToken = JSON.parse(value) as CompositeToken;\n return new JwtCompositeToken(compositeToken, this.earlyPeriod);\n }\n\n /**\n * Serializes a JwtCompositeToken to a JSON string\n * @param value The JwtCompositeToken to serialize\n * @returns A JSON string representation of the composite token\n */\n serialize(value: JwtCompositeToken): string {\n return JSON.stringify(value.token);\n }\n}\n\nexport const jwtCompositeTokenSerializer = new JwtCompositeTokenSerializer();","/*\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 { TokenStorage } from './tokenStorage';\nimport { CompositeToken, TokenRefresher } from './tokenRefresher';\nimport { JwtCompositeToken, RefreshTokenStatusCapable } from './jwtToken';\n\n/**\n * Manages JWT token refreshing operations and provides status information\n */\nexport class JwtTokenManager implements RefreshTokenStatusCapable {\n private refreshInProgress?: Promise<void>;\n\n /**\n * Creates a new JwtTokenManager instance\n * @param tokenStorage The storage used to persist tokens\n * @param tokenRefresher The refresher used to refresh expired tokens\n */\n constructor(\n private readonly tokenStorage: TokenStorage,\n private readonly tokenRefresher: TokenRefresher,\n ) {\n\n }\n\n /**\n * Gets the current JWT composite token from storage\n * @returns The current token or null if none exists\n */\n get currentToken(): JwtCompositeToken | null {\n return this.tokenStorage.get();\n }\n\n /**\n * Refreshes the JWT token\n * @param currentToken Optional current token to refresh. If not provided, uses the stored token.\n * @returns Promise that resolves when refresh is complete\n * @throws Error if no token is found or refresh fails\n */\n async refresh(currentToken?: CompositeToken): Promise<void> {\n if (!currentToken) {\n const jwtToken = this.currentToken;\n if (!jwtToken) {\n throw new Error('No token found');\n }\n currentToken = jwtToken.token;\n }\n if (this.refreshInProgress) {\n return this.refreshInProgress;\n }\n\n this.refreshInProgress = this.tokenRefresher.refresh(currentToken)\n .then(newToken => {\n this.tokenStorage.setCompositeToken(newToken);\n })\n .catch(error => {\n this.tokenStorage.remove();\n throw error;\n })\n .finally(() => {\n this.refreshInProgress = undefined;\n });\n\n return this.refreshInProgress;\n }\n\n /**\n * Indicates if the current token needs to be refreshed\n * @returns true if the access token is expired and needs refresh, false otherwise\n */\n get isRefreshNeeded(): boolean {\n if (!this.currentToken) {\n return false;\n }\n return this.currentToken.isRefreshNeeded;\n }\n\n /**\n * Indicates if the current token can be refreshed\n * @returns true if the refresh token is still valid, false otherwise\n */\n get isRefreshable(): boolean {\n if (!this.currentToken) {\n return false;\n }\n return this.currentToken.isRefreshable;\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 { createListenableStorage, KeyStorage } from './storage';\nimport { JwtCompositeToken, JwtCompositeTokenSerializer } from './jwtToken';\nimport { CompositeToken } from './tokenRefresher';\nimport { EarlyPeriodCapable } from './jwts';\n\nexport const DEFAULT_COSEC_TOKEN_KEY = 'cosec-token';\n\n/**\n * Storage class for managing access and refresh tokens.\n */\nexport class TokenStorage extends KeyStorage<JwtCompositeToken> implements EarlyPeriodCapable {\n\n constructor(\n key: string = DEFAULT_COSEC_TOKEN_KEY,\n public readonly earlyPeriod: number = 0,\n ) {\n super({\n key,\n storage: createListenableStorage(),\n serializer: new JwtCompositeTokenSerializer(earlyPeriod),\n });\n }\n\n setCompositeToken(compositeToken: CompositeToken) {\n this.set(\n new JwtCompositeToken(compositeToken),\n );\n }\n}\n"],"names":["isBrowser","InMemoryListenableStorage","key","value","index","oldValue","listener","eventInit","error","BrowserListenableStorage","storage","wrapper","event","STORAGE_EVENT_TYPE","createListenableStorage","JsonSerializer","IdentitySerializer","jsonSerializer","identitySerializer","typedIdentitySerializer","KeyStorage","options","serialized","_CoSecHeaders","CoSecHeaders","_ResponseCodes","ResponseCodes","AuthorizeResults","NanoIdGenerator","nanoid","idGenerator","COSEC_REQUEST_INTERCEPTOR_NAME","COSEC_REQUEST_INTERCEPTOR_ORDER","REQUEST_BODY_INTERCEPTOR_ORDER","CoSecRequestInterceptor","exchange","requestId","deviceId","requestHeaders","token","COSEC_RESPONSE_INTERCEPTOR_NAME","COSEC_RESPONSE_INTERCEPTOR_ORDER","CoSecResponseInterceptor","currentToken","newToken","response","jwtToken","DEFAULT_COSEC_DEVICE_ID_KEY","DeviceIdStorage","parseJwtPayload","parts","base64","paddedBase64","jsonPayload","c","isTokenExpired","earlyPeriod","payload","expAt","JwtToken","JwtCompositeToken","JwtCompositeTokenSerializer","compositeToken","jwtCompositeTokenSerializer","JwtTokenManager","tokenStorage","tokenRefresher","DEFAULT_COSEC_TOKEN_KEY","TokenStorage"],"mappings":"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"}
1
+ {"version":3,"file":"index.umd.js","sources":["../src/env.ts","../src/storage/inMemoryListenableStorage.ts","../src/storage/browserListenableStorage.ts","../src/storage/listenableStorage.ts","../src/serializer.ts","../src/storage/keyStorage.ts","../src/types.ts","../src/idGenerator.ts","../src/cosecRequestInterceptor.ts","../src/cosecResponseInterceptor.ts","../src/deviceIdStorage.ts","../src/jwts.ts","../src/jwtToken.ts","../src/jwtTokenManager.ts","../src/tokenRefresher.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\nexport function isBrowser(): boolean {\n return typeof window !== 'undefined';\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 { ListenableStorage, RemoveStorageListener, StorageListener } from './listenableStorage';\nimport { isBrowser } from '../env';\n\n/**\n * An in-memory implementation of ListenableStorage that works in any environment.\n * This implementation stores data in a Map and manually fires storage events when data changes.\n */\nexport class InMemoryListenableStorage implements ListenableStorage {\n private readonly store: Map<string, string> = new Map();\n private readonly listeners: Set<StorageListener> = new Set();\n\n /**\n * Gets the number of items stored in the storage.\n */\n get length(): number {\n return this.store.size;\n }\n\n /**\n * Clears all items from the storage.\n */\n clear(): void {\n this.store.clear();\n }\n\n /**\n * Gets an item from the storage.\n * @param key - The key of the item to retrieve\n * @returns The value of the item, or null if the item does not exist\n */\n getItem(key: string): string | null {\n const value = this.store.get(key);\n return value !== undefined ? value : null;\n }\n\n /**\n * Gets the key at the specified index.\n * @param index - The index of the key to retrieve\n * @returns The key at the specified index, or null if the index is out of bounds\n */\n key(index: number): string | null {\n const keys = Array.from(this.store.keys());\n return keys[index] || null;\n }\n\n /**\n * Removes an item from the storage.\n * @param key - The key of the item to remove\n */\n removeItem(key: string): void {\n const oldValue = this.getItem(key);\n if (this.store.has(key)) {\n this.store.delete(key);\n this.notifyListeners({\n key,\n oldValue,\n newValue: null,\n });\n }\n }\n\n /**\n * Sets an item in the storage.\n * @param key - The key of the item to set\n * @param value - The value to set\n */\n setItem(key: string, value: string): void {\n const oldValue = this.getItem(key);\n this.store.set(key, value);\n this.notifyListeners({ key, oldValue, newValue: value });\n }\n\n /**\n * Adds a listener for storage changes.\n * @param listener - The listener function to be called when storage changes\n * @returns A function that can be called to remove the listener\n */\n addListener(listener: StorageListener): RemoveStorageListener {\n this.listeners.add(listener);\n return () => this.listeners.delete(listener);\n }\n\n /**\n * Notifies all listeners of a storage change by creating and dispatching a StorageEvent.\n * @param eventInit - The initialization object for the StorageEvent\n */\n private notifyListeners(\n eventInit: StorageEventInit,\n ): void {\n if (isBrowser() && window.location) {\n eventInit.url = eventInit.url || window.location.href;\n }\n eventInit.storageArea = this;\n this.listeners.forEach(listener => {\n try {\n listener(eventInit);\n } catch (error) {\n console.error('Error in storage change listener:', 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\nimport { ListenableStorage, RemoveStorageListener, STORAGE_EVENT_TYPE, StorageListener } from './listenableStorage';\n\n/**\n * A wrapper around the browser's native Storage (localStorage or sessionStorage)\n * that implements the ListenableStorage interface by using the browser's native storage events.\n */\nexport class BrowserListenableStorage implements ListenableStorage {\n\n /**\n * Creates a new BrowserListenableStorage instance.\n * @param storage - The native Storage object to wrap (e.g., localStorage or sessionStorage)\n */\n constructor(private readonly storage: Storage) {\n }\n\n /**\n * Gets the number of items stored in the storage.\n */\n get length(): number {\n return this.storage.length;\n }\n\n /**\n * Adds a listener for storage changes.\n * @param listener - The listener function to be called when storage changes\n * @returns A function that can be called to remove the listener\n */\n addListener(listener: StorageListener): RemoveStorageListener {\n const wrapper: StorageListener = (event: StorageEventInit) => {\n if (event.storageArea === this.storage) {\n listener(event);\n }\n };\n window.addEventListener(STORAGE_EVENT_TYPE, wrapper);\n return () => window.removeEventListener(STORAGE_EVENT_TYPE, wrapper);\n }\n\n /**\n * Clears all items from the storage.\n */\n clear(): void {\n this.storage.clear();\n }\n\n /**\n * Gets an item from the storage.\n * @param key - The key of the item to retrieve\n * @returns The value of the item, or null if the item does not exist\n */\n getItem(key: string): string | null {\n return this.storage.getItem(key);\n }\n\n /**\n * Gets the key at the specified index.\n * @param index - The index of the key to retrieve\n * @returns The key at the specified index, or null if the index is out of bounds\n */\n key(index: number): string | null {\n return this.storage.key(index);\n }\n\n /**\n * Removes an item from the storage.\n * @param key - The key of the item to remove\n */\n removeItem(key: string): void {\n this.storage.removeItem(key);\n }\n\n /**\n * Sets an item in the storage.\n * @param key - The key of the item to set\n * @param value - The value to set\n */\n setItem(key: string, value: string): void {\n this.storage.setItem(key, value);\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 { InMemoryListenableStorage } from './inMemoryListenableStorage';\nimport { BrowserListenableStorage } from './browserListenableStorage';\nimport { isBrowser } from '../env';\n\n/**\n * The type of storage event used for listening to storage changes.\n */\nexport const STORAGE_EVENT_TYPE = 'storage';\n\n/**\n * A function that handles storage change events.\n */\nexport type StorageListener = (event: StorageEventInit) => void;\n\n/**\n * A function that removes a storage listener when called.\n */\nexport type RemoveStorageListener = () => void;\n\nexport interface StorageListenable {\n /**\n * Adds a listener for storage changes.\n * @param listener - The listener function to be called when storage changes\n * @returns A function that can be called to remove the listener\n */\n addListener(listener: StorageListener): RemoveStorageListener;\n}\n\n/**\n * An interface that extends the native Storage interface with the ability to listen for storage changes.\n */\nexport interface ListenableStorage extends Storage, StorageListenable {\n\n}\n\n/**\n * Factory function to get an appropriate ListenableStorage implementation based on the environment.\n * In a browser environment, it returns a BrowserListenableStorage wrapping localStorage.\n * In other environments, it returns an InMemoryListenableStorage.\n * @returns A ListenableStorage instance suitable for the current environment\n */\nexport const createListenableStorage = (): ListenableStorage => {\n if (isBrowser()) {\n return new BrowserListenableStorage(window.localStorage);\n }\n return new InMemoryListenableStorage();\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 * Interface for serializing and deserializing values\n * @template Serialized The type of the serialized value\n */\n/**\n * Interface for serializing and deserializing values\n * @template Serialized The type of the serialized value\n * @template Deserialized The type of the deserialized value\n */\nexport interface Serializer<Serialized, Deserialized> {\n /**\n * Serializes a value to the specified format\n * @param value The value to serialize\n * @returns The serialized value\n */\n serialize(value: any): Serialized;\n\n /**\n * Deserializes a value from the specified format\n * @template Deserialized The type of the deserialized value\n * @param value The value to deserialize\n * @returns The deserialized value\n */\n /**\n * Deserializes a value from the specified format\n * @param value The value to deserialize\n * @returns The deserialized value\n */\n deserialize(value: Serialized): Deserialized;\n}\n\n/**\n * Implementation of Serializer that uses JSON for serialization\n */\nexport class JsonSerializer implements Serializer<string, any> {\n /**\n * Serializes a value to a JSON string\n * @param value The value to serialize\n * @returns The JSON string representation of the value\n */\n /**\n * Serializes a value to a JSON string\n * @param value The value to serialize\n * @returns The JSON string representation of the value\n */\n serialize(value: any): string {\n return JSON.stringify(value);\n }\n\n /**\n * Deserializes a JSON string to a value\n * @template V The type of the deserialized value\n * @param value The JSON string to deserialize\n * @returns The deserialized value\n */\n /**\n * Deserializes a JSON string to a value\n * @param value The JSON string to deserialize\n * @returns The deserialized value\n */\n deserialize<V>(value: string): V {\n return JSON.parse(value);\n }\n}\n\n/**\n * Implementation of Serializer that performs no actual serialization\n * @template T The type of the value to pass through\n */\nexport class IdentitySerializer<T> implements Serializer<T, T> {\n /**\n * Returns the value as-is without serialization\n * @param value The value to pass through\n * @returns The same value that was passed in\n */\n /**\n * Returns the value as-is without serialization\n * @param value The value to pass through\n * @returns The same value that was passed in\n */\n serialize(value: T): T {\n return value;\n }\n\n /**\n * Returns the value as-is without deserialization\n * @template Deserialized The type of the deserialized value\n * @param value The value to pass through\n * @returns The same value that was passed in, cast to the target type\n */\n /**\n * Returns the value as-is without deserialization\n * @param value The value to pass through\n * @returns The same value that was passed in\n */\n deserialize(value: T): T {\n return value;\n }\n}\n\n/**\n * Global instance of JsonSerializer\n */\nexport const jsonSerializer = new JsonSerializer();\n/**\n * Global instance of IdentitySerializer\n */\nexport const identitySerializer = new IdentitySerializer<any>();\n\nexport function typedIdentitySerializer<T>(): IdentitySerializer<T> {\n return identitySerializer as IdentitySerializer<T>;\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 { createListenableStorage, ListenableStorage, StorageListener } from './listenableStorage';\nimport { Serializer, typedIdentitySerializer } from '../serializer';\n\n/**\n * Options for configuring KeyStorage\n */\nexport interface KeyStorageOptions<Deserialized> {\n /**\n * The key used to store and retrieve values from storage\n */\n key: string;\n\n /**\n * Optional serializer for converting values to and from storage format\n * Defaults to IdentitySerializer if not provided\n */\n serializer?: Serializer<string, Deserialized>;\n\n /**\n * Optional storage implementation\n * Defaults to the result of createListenableStorage() if not provided\n */\n storage?: ListenableStorage;\n}\n\n/**\n * A storage wrapper that manages a single value associated with a specific key\n * Provides caching and automatic cache invalidation when the storage value changes\n * @template Deserialized The type of the value being stored\n */\nexport class KeyStorage<Deserialized> {\n private readonly key: string;\n private readonly serializer: Serializer<string, Deserialized>;\n private readonly storage: ListenableStorage;\n private cacheValue: Deserialized | null = null;\n\n /**\n * Listener for storage change events\n * Invalidates the cache when the relevant key is modified\n */\n private readonly listener: StorageListener = (event) => {\n if (event.key !== this.key) {\n return;\n }\n this.cacheValue = null;\n if (event.newValue) {\n this.refreshCache(event.newValue);\n }\n };\n\n /**\n * Refreshes the cached value by deserializing the provided string\n * @param value The serialized value to deserialize and cache\n */\n private refreshCache(value: string) {\n this.cacheValue = this.serializer.deserialize(value);\n }\n\n /**\n * Creates a new KeyStorage instance\n * @param options Configuration options for the storage\n */\n constructor(options: KeyStorageOptions<Deserialized>) {\n this.key = options.key;\n this.serializer = options.serializer ?? typedIdentitySerializer();\n this.storage = options.storage ?? createListenableStorage();\n this.storage.addListener(this.listener);\n }\n\n /**\n * Gets the value associated with the key from storage\n * Uses cached value if available, otherwise retrieves from storage and caches it\n * @returns The deserialized value or null if no value is found\n */\n get(): Deserialized | null {\n if (this.cacheValue) {\n return this.cacheValue;\n }\n const value = this.storage.getItem(this.key);\n if (!value) {\n return null;\n }\n this.refreshCache(value);\n return this.cacheValue;\n }\n\n /**\n * Sets the value associated with the key in storage\n * Also updates the cached value\n * @param value The value to serialize and store\n */\n set(value: Deserialized): void {\n const serialized = this.serializer.serialize(value);\n this.storage.setItem(this.key, serialized);\n this.cacheValue = value;\n }\n\n /**\n * Removes the value associated with the key from storage\n * Also clears the cached value\n */\n remove(): void {\n this.storage.removeItem(this.key);\n this.cacheValue = null;\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 { DeviceIdStorage } from './deviceIdStorage';\nimport { JwtTokenManager } from './jwtTokenManager';\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 tokenManager: JwtTokenManager;\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\nexport const IGNORE_REFRESH_TOKEN_ATTRIBUTE_KEY = 'Ignore-Refresh-Token';\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 /**\n * Creates a new CoSecRequestInterceptor instance.\n * @param options - The CoSec configuration options including appId, deviceIdStorage, and tokenManager\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 * The method also handles token refreshing when the current token is expired but still refreshable.\n * It will attempt to refresh the token before adding the Authorization header to the request.\n */\n async intercept(exchange: FetchExchange) {\n // Generate a unique request ID for this request\n const requestId = idGenerator.generateId();\n\n // Get or create a device ID\n const deviceId = this.options.deviceIdStorage.getOrCreate();\n\n // Ensure request headers object exists\n const requestHeaders = exchange.ensureRequestHeaders();\n\n // Add CoSec headers to the request\n requestHeaders[CoSecHeaders.APP_ID] = this.options.appId;\n requestHeaders[CoSecHeaders.DEVICE_ID] = deviceId;\n requestHeaders[CoSecHeaders.REQUEST_ID] = requestId;\n\n // Get the current token from token manager\n let currentToken = this.options.tokenManager.currentToken;\n\n // Skip if no token exists or Authorization header is already set\n if (!currentToken || requestHeaders[CoSecHeaders.AUTHORIZATION]) {\n return;\n }\n\n // Refresh token if needed and refreshable\n if (!exchange.attributes[IGNORE_REFRESH_TOKEN_ATTRIBUTE_KEY] && currentToken.isRefreshNeeded && currentToken.isRefreshable) {\n await this.options.tokenManager.refresh();\n }\n\n // Get the current token again (might have been refreshed)\n currentToken = this.options.tokenManager.currentToken;\n\n // Add Authorization header if we have a token\n if (currentToken) {\n requestHeaders[CoSecHeaders.AUTHORIZATION] = `Bearer ${currentToken.access.token}`;\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 if (!this.options.tokenManager.isRefreshable) {\n return;\n }\n try {\n await this.options.tokenManager.refresh();\n // Retry the original request with the new token\n await exchange.fetcher.interceptors.exchange(exchange);\n } catch (error) {\n // If token refresh fails, clear stored tokens and re-throw the error\n this.options.tokenManager.tokenStorage.remove();\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\nimport { idGenerator } from './idGenerator';\nimport { createListenableStorage, KeyStorage } from './storage';\nimport { IdentitySerializer } from './serializer';\n\nexport const DEFAULT_COSEC_DEVICE_ID_KEY = 'cosec-device-id';\n\n/**\n * Storage class for managing device identifiers.\n */\nexport class DeviceIdStorage extends KeyStorage<string> {\n constructor(key: string = DEFAULT_COSEC_DEVICE_ID_KEY) {\n super({\n key,\n serializer: new IdentitySerializer<string>(),\n storage: createListenableStorage(),\n });\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","/*\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\nexport interface EarlyPeriodCapable {\n /**\n * The time in seconds before actual expiration when the token should be considered expired (default: 0)\n */\n readonly earlyPeriod: number;\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 { CoSecJwtPayload, EarlyPeriodCapable, isTokenExpired, JwtPayload, parseJwtPayload } from './jwts';\nimport { Serializer } from './serializer';\nimport { CompositeToken } from './tokenRefresher';\n\n\n/**\n * Interface for JWT token with typed payload\n * @template Payload The type of the JWT payload\n */\nexport interface IJwtToken<Payload extends JwtPayload> extends EarlyPeriodCapable {\n readonly token: string;\n readonly payload: Payload;\n\n isExpired: boolean;\n}\n\n/**\n * Class representing a JWT token with typed payload\n * @template Payload The type of the JWT payload\n */\nexport class JwtToken<Payload extends JwtPayload> implements IJwtToken<Payload> {\n public readonly payload: Payload;\n\n /**\n * Creates a new JwtToken instance\n */\n constructor(\n public readonly token: string,\n public readonly earlyPeriod: number = 0,\n ) {\n this.payload = parseJwtPayload(token) as Payload;\n }\n\n /**\n * Checks if the token is expired\n * @returns true if the token is expired, false otherwise\n */\n get isExpired(): boolean {\n return isTokenExpired(this.payload, this.earlyPeriod);\n }\n}\n\nexport interface RefreshTokenStatusCapable {\n /**\n * Checks if the access token needs to be refreshed\n * @returns true if the access token is expired, false otherwise\n */\n readonly isRefreshNeeded: boolean;\n /**\n * Checks if the refresh token is still valid and can be used to refresh the access token\n * @returns true if the refresh token is not expired, false otherwise\n */\n readonly isRefreshable: boolean;\n}\n\n/**\n * Class representing a composite token containing both access and refresh tokens\n */\nexport class JwtCompositeToken implements EarlyPeriodCapable, RefreshTokenStatusCapable {\n public readonly access: JwtToken<CoSecJwtPayload>;\n public readonly refresh: JwtToken<JwtPayload>;\n\n /**\n * Creates a new JwtCompositeToken instance\n */\n constructor(public readonly token: CompositeToken, public readonly earlyPeriod: number = 0) {\n this.access = new JwtToken(token.accessToken, earlyPeriod);\n this.refresh = new JwtToken(token.refreshToken, earlyPeriod);\n }\n\n /**\n * Checks if the access token needs to be refreshed\n * @returns true if the access token is expired, false otherwise\n */\n get isRefreshNeeded(): boolean {\n return this.access.isExpired;\n }\n\n /**\n * Checks if the refresh token is still valid and can be used to refresh the access token\n * @returns true if the refresh token is not expired, false otherwise\n */\n get isRefreshable(): boolean {\n return !this.refresh.isExpired;\n }\n\n}\n\n/**\n * Serializer for JwtCompositeToken that handles conversion to and from JSON strings\n */\nexport class JwtCompositeTokenSerializer implements Serializer<string, JwtCompositeToken>, EarlyPeriodCapable {\n constructor(public readonly earlyPeriod: number = 0) {\n }\n\n /**\n * Deserializes a JSON string to a JwtCompositeToken\n * @param value The JSON string representation of a composite token\n * @returns A JwtCompositeToken instance\n */\n deserialize(value: string): JwtCompositeToken {\n const compositeToken = JSON.parse(value) as CompositeToken;\n return new JwtCompositeToken(compositeToken, this.earlyPeriod);\n }\n\n /**\n * Serializes a JwtCompositeToken to a JSON string\n * @param value The JwtCompositeToken to serialize\n * @returns A JSON string representation of the composite token\n */\n serialize(value: JwtCompositeToken): string {\n return JSON.stringify(value.token);\n }\n}\n\nexport const jwtCompositeTokenSerializer = new JwtCompositeTokenSerializer();","/*\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 { TokenStorage } from './tokenStorage';\nimport { CompositeToken, TokenRefresher } from './tokenRefresher';\nimport { JwtCompositeToken, RefreshTokenStatusCapable } from './jwtToken';\n\n/**\n * Manages JWT token refreshing operations and provides status information\n */\nexport class JwtTokenManager implements RefreshTokenStatusCapable {\n private refreshInProgress?: Promise<void>;\n\n /**\n * Creates a new JwtTokenManager instance\n * @param tokenStorage The storage used to persist tokens\n * @param tokenRefresher The refresher used to refresh expired tokens\n */\n constructor(\n public readonly tokenStorage: TokenStorage,\n public readonly tokenRefresher: TokenRefresher,\n ) {\n\n }\n\n /**\n * Gets the current JWT composite token from storage\n * @returns The current token or null if none exists\n */\n get currentToken(): JwtCompositeToken | null {\n return this.tokenStorage.get();\n }\n\n /**\n * Refreshes the JWT token\n * @param currentToken Optional current token to refresh. If not provided, uses the stored token.\n * @returns Promise that resolves when refresh is complete\n * @throws Error if no token is found or refresh fails\n */\n async refresh(currentToken?: CompositeToken): Promise<void> {\n if (!currentToken) {\n const jwtToken = this.currentToken;\n if (!jwtToken) {\n throw new Error('No token found');\n }\n currentToken = jwtToken.token;\n }\n if (this.refreshInProgress) {\n return this.refreshInProgress;\n }\n\n this.refreshInProgress = this.tokenRefresher.refresh(currentToken)\n .then(newToken => {\n this.tokenStorage.setCompositeToken(newToken);\n })\n .catch(error => {\n this.tokenStorage.remove();\n throw error;\n })\n .finally(() => {\n this.refreshInProgress = undefined;\n });\n\n return this.refreshInProgress;\n }\n\n /**\n * Indicates if the current token needs to be refreshed\n * @returns true if the access token is expired and needs refresh, false otherwise\n */\n get isRefreshNeeded(): boolean {\n if (!this.currentToken) {\n return false;\n }\n return this.currentToken.isRefreshNeeded;\n }\n\n /**\n * Indicates if the current token can be refreshed\n * @returns true if the refresh token is still valid, false otherwise\n */\n get isRefreshable(): boolean {\n if (!this.currentToken) {\n return false;\n }\n return this.currentToken.isRefreshable;\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 { Fetcher, ResultExtractors } from '@ahoo-wang/fetcher';\nimport { IGNORE_REFRESH_TOKEN_ATTRIBUTE_KEY } from './cosecRequestInterceptor';\n\n/**\n * Interface for access tokens.\n */\nexport interface AccessToken {\n accessToken: string;\n}\n\n/**\n * Interface for refresh tokens.\n */\nexport interface RefreshToken {\n refreshToken: string;\n}\n\n/**\n * Composite token interface that contains both access and refresh tokens.\n *\n * accessToken and refreshToken always appear in pairs, no need to split them.\n */\nexport interface CompositeToken extends AccessToken, RefreshToken {\n}\n\n/**\n * Interface for token refreshers.\n *\n * Provides a method to refresh tokens.\n */\nexport interface TokenRefresher {\n /**\n * Refresh the given token and return a new CompositeToken.\n *\n * @param token The token to refresh\n * @returns A Promise that resolves to a new CompositeToken\n */\n refresh(token: CompositeToken): Promise<CompositeToken>;\n}\n\nexport interface CoSecTokenRefresherOptions {\n fetcher: Fetcher;\n endpoint: string;\n}\n\n/**\n * CoSecTokenRefresher is a class that implements the TokenRefresher interface\n * for refreshing composite tokens through a configured endpoint.\n */\nexport class CoSecTokenRefresher implements TokenRefresher {\n /**\n * Creates a new instance of CoSecTokenRefresher.\n *\n * @param options The configuration options for the token refresher including fetcher and endpoint\n */\n constructor(public readonly options: CoSecTokenRefresherOptions) {\n }\n\n /**\n * Refresh the given token and return a new CompositeToken.\n *\n * @param token The token to refresh\n * @returns A Promise that resolves to a new CompositeToken\n */\n refresh(token: CompositeToken): Promise<CompositeToken> {\n // Send a POST request to the configured endpoint with the token as body\n // and extract the response as JSON to return a new CompositeToken\n return this.options.fetcher.post<CompositeToken>(this.options.endpoint,\n {\n body: token,\n }, ResultExtractors.Json,\n {\n [IGNORE_REFRESH_TOKEN_ATTRIBUTE_KEY]: true,\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 { createListenableStorage, KeyStorage } from './storage';\nimport { JwtCompositeToken, JwtCompositeTokenSerializer } from './jwtToken';\nimport { CompositeToken } from './tokenRefresher';\nimport { EarlyPeriodCapable } from './jwts';\n\nexport const DEFAULT_COSEC_TOKEN_KEY = 'cosec-token';\n\n/**\n * Storage class for managing access and refresh tokens.\n */\nexport class TokenStorage extends KeyStorage<JwtCompositeToken> implements EarlyPeriodCapable {\n\n constructor(\n key: string = DEFAULT_COSEC_TOKEN_KEY,\n public readonly earlyPeriod: number = 0,\n ) {\n super({\n key,\n storage: createListenableStorage(),\n serializer: new JwtCompositeTokenSerializer(earlyPeriod),\n });\n }\n\n setCompositeToken(compositeToken: CompositeToken) {\n this.set(\n new JwtCompositeToken(compositeToken),\n );\n }\n}\n"],"names":["isBrowser","InMemoryListenableStorage","key","value","index","oldValue","listener","eventInit","error","BrowserListenableStorage","storage","wrapper","event","STORAGE_EVENT_TYPE","createListenableStorage","JsonSerializer","IdentitySerializer","jsonSerializer","identitySerializer","typedIdentitySerializer","KeyStorage","options","serialized","_CoSecHeaders","CoSecHeaders","_ResponseCodes","ResponseCodes","AuthorizeResults","NanoIdGenerator","nanoid","idGenerator","COSEC_REQUEST_INTERCEPTOR_NAME","COSEC_REQUEST_INTERCEPTOR_ORDER","REQUEST_BODY_INTERCEPTOR_ORDER","IGNORE_REFRESH_TOKEN_ATTRIBUTE_KEY","CoSecRequestInterceptor","exchange","requestId","deviceId","requestHeaders","currentToken","COSEC_RESPONSE_INTERCEPTOR_NAME","COSEC_RESPONSE_INTERCEPTOR_ORDER","CoSecResponseInterceptor","response","DEFAULT_COSEC_DEVICE_ID_KEY","DeviceIdStorage","parseJwtPayload","token","parts","base64","paddedBase64","jsonPayload","c","isTokenExpired","earlyPeriod","payload","expAt","JwtToken","JwtCompositeToken","JwtCompositeTokenSerializer","compositeToken","jwtCompositeTokenSerializer","JwtTokenManager","tokenStorage","tokenRefresher","jwtToken","newToken","CoSecTokenRefresher","ResultExtractors","DEFAULT_COSEC_TOKEN_KEY","TokenStorage"],"mappings":"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"}
@@ -5,8 +5,8 @@ import { JwtCompositeToken, RefreshTokenStatusCapable } from './jwtToken';
5
5
  * Manages JWT token refreshing operations and provides status information
6
6
  */
7
7
  export declare class JwtTokenManager implements RefreshTokenStatusCapable {
8
- private readonly tokenStorage;
9
- private readonly tokenRefresher;
8
+ readonly tokenStorage: TokenStorage;
9
+ readonly tokenRefresher: TokenRefresher;
10
10
  private refreshInProgress?;
11
11
  /**
12
12
  * Creates a new JwtTokenManager instance
@@ -1 +1 @@
1
- {"version":3,"file":"jwtTokenManager.d.ts","sourceRoot":"","sources":["../src/jwtTokenManager.ts"],"names":[],"mappings":"AAaA,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAClE,OAAO,EAAE,iBAAiB,EAAE,yBAAyB,EAAE,MAAM,YAAY,CAAC;AAE1E;;GAEG;AACH,qBAAa,eAAgB,YAAW,yBAAyB;IAS7D,OAAO,CAAC,QAAQ,CAAC,YAAY;IAC7B,OAAO,CAAC,QAAQ,CAAC,cAAc;IATjC,OAAO,CAAC,iBAAiB,CAAC,CAAgB;IAE1C;;;;OAIG;gBAEgB,YAAY,EAAE,YAAY,EAC1B,cAAc,EAAE,cAAc;IAKjD;;;OAGG;IACH,IAAI,YAAY,IAAI,iBAAiB,GAAG,IAAI,CAE3C;IAED;;;;;OAKG;IACG,OAAO,CAAC,YAAY,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC;IA2B3D;;;OAGG;IACH,IAAI,eAAe,IAAI,OAAO,CAK7B;IAED;;;OAGG;IACH,IAAI,aAAa,IAAI,OAAO,CAK3B;CAEF"}
1
+ {"version":3,"file":"jwtTokenManager.d.ts","sourceRoot":"","sources":["../src/jwtTokenManager.ts"],"names":[],"mappings":"AAaA,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAClE,OAAO,EAAE,iBAAiB,EAAE,yBAAyB,EAAE,MAAM,YAAY,CAAC;AAE1E;;GAEG;AACH,qBAAa,eAAgB,YAAW,yBAAyB;aAS7C,YAAY,EAAE,YAAY;aAC1B,cAAc,EAAE,cAAc;IAThD,OAAO,CAAC,iBAAiB,CAAC,CAAgB;IAE1C;;;;OAIG;gBAEe,YAAY,EAAE,YAAY,EAC1B,cAAc,EAAE,cAAc;IAKhD;;;OAGG;IACH,IAAI,YAAY,IAAI,iBAAiB,GAAG,IAAI,CAE3C;IAED;;;;;OAKG;IACG,OAAO,CAAC,YAAY,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC;IA2B3D;;;OAGG;IACH,IAAI,eAAe,IAAI,OAAO,CAK7B;IAED;;;OAGG;IACH,IAAI,aAAa,IAAI,OAAO,CAK3B;CAEF"}
@@ -1,3 +1,4 @@
1
+ import { Fetcher } from '@ahoo-wang/fetcher';
1
2
  /**
2
3
  * Interface for access tokens.
3
4
  */
@@ -31,4 +32,28 @@ export interface TokenRefresher {
31
32
  */
32
33
  refresh(token: CompositeToken): Promise<CompositeToken>;
33
34
  }
35
+ export interface CoSecTokenRefresherOptions {
36
+ fetcher: Fetcher;
37
+ endpoint: string;
38
+ }
39
+ /**
40
+ * CoSecTokenRefresher is a class that implements the TokenRefresher interface
41
+ * for refreshing composite tokens through a configured endpoint.
42
+ */
43
+ export declare class CoSecTokenRefresher implements TokenRefresher {
44
+ readonly options: CoSecTokenRefresherOptions;
45
+ /**
46
+ * Creates a new instance of CoSecTokenRefresher.
47
+ *
48
+ * @param options The configuration options for the token refresher including fetcher and endpoint
49
+ */
50
+ constructor(options: CoSecTokenRefresherOptions);
51
+ /**
52
+ * Refresh the given token and return a new CompositeToken.
53
+ *
54
+ * @param token The token to refresh
55
+ * @returns A Promise that resolves to a new CompositeToken
56
+ */
57
+ refresh(token: CompositeToken): Promise<CompositeToken>;
58
+ }
34
59
  //# sourceMappingURL=tokenRefresher.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"tokenRefresher.d.ts","sourceRoot":"","sources":["../src/tokenRefresher.ts"],"names":[],"mappings":"AAaA;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,WAAW,EAAE,MAAM,CAAC;CACrB;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,YAAY,EAAE,MAAM,CAAC;CACtB;AAED;;;;GAIG;AACH,MAAM,WAAW,cAAe,SAAQ,WAAW,EAAE,YAAY;CAAG;AAEpE;;;;GAIG;AACH,MAAM,WAAW,cAAc;IAC7B;;;;;OAKG;IACH,OAAO,CAAC,KAAK,EAAE,cAAc,GAAG,OAAO,CAAC,cAAc,CAAC,CAAC;CACzD"}
1
+ {"version":3,"file":"tokenRefresher.d.ts","sourceRoot":"","sources":["../src/tokenRefresher.ts"],"names":[],"mappings":"AAaA,OAAO,EAAE,OAAO,EAAoB,MAAM,oBAAoB,CAAC;AAG/D;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,WAAW,EAAE,MAAM,CAAC;CACrB;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,YAAY,EAAE,MAAM,CAAC;CACtB;AAED;;;;GAIG;AACH,MAAM,WAAW,cAAe,SAAQ,WAAW,EAAE,YAAY;CAChE;AAED;;;;GAIG;AACH,MAAM,WAAW,cAAc;IAC7B;;;;;OAKG;IACH,OAAO,CAAC,KAAK,EAAE,cAAc,GAAG,OAAO,CAAC,cAAc,CAAC,CAAC;CACzD;AAED,MAAM,WAAW,0BAA0B;IACzC,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;;GAGG;AACH,qBAAa,mBAAoB,YAAW,cAAc;aAM5B,OAAO,EAAE,0BAA0B;IAL/D;;;;OAIG;gBACyB,OAAO,EAAE,0BAA0B;IAG/D;;;;;OAKG;IACH,OAAO,CAAC,KAAK,EAAE,cAAc,GAAG,OAAO,CAAC,cAAc,CAAC;CAWxD"}
package/dist/types.d.ts CHANGED
@@ -1,6 +1,5 @@
1
1
  import { DeviceIdStorage } from './deviceIdStorage';
2
- import { TokenStorage } from './tokenStorage';
3
- import { TokenRefresher } from './tokenRefresher';
2
+ import { JwtTokenManager } from './jwtTokenManager';
4
3
  /**
5
4
  * CoSec HTTP headers enumeration.
6
5
  */
@@ -25,16 +24,7 @@ export interface CoSecOptions {
25
24
  * Device ID storage instance.
26
25
  */
27
26
  deviceIdStorage: DeviceIdStorage;
28
- /**
29
- * Token storage instance.
30
- */
31
- tokenStorage: TokenStorage;
32
- /**
33
- * Token refresher function.
34
- *
35
- * Takes a CompositeToken and returns a Promise that resolves to a new CompositeToken.
36
- */
37
- tokenRefresher: TokenRefresher;
27
+ tokenManager: JwtTokenManager;
38
28
  }
39
29
  /**
40
30
  * Authorization result interface.
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAaA,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAElD;;GAEG;AACH,qBAAa,YAAY;IACvB,MAAM,CAAC,QAAQ,CAAC,SAAS,qBAAqB;IAC9C,MAAM,CAAC,QAAQ,CAAC,MAAM,kBAAkB;IACxC,MAAM,CAAC,QAAQ,CAAC,aAAa,mBAAmB;IAChD,MAAM,CAAC,QAAQ,CAAC,UAAU,sBAAsB;CACjD;AAED,qBAAa,aAAa;IACxB,MAAM,CAAC,QAAQ,CAAC,YAAY,OAAO;CACpC;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B;;OAEG;IACH,KAAK,EAAE,MAAM,CAAC;IAEd;;OAEG;IACH,eAAe,EAAE,eAAe,CAAC;IAEjC;;OAEG;IACH,YAAY,EAAE,YAAY,CAAC;IAE3B;;;;OAIG;IACH,cAAc,EAAE,cAAc,CAAC;CAChC;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,UAAU,EAAE,OAAO,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;GAEG;AACH,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;CAM5B,CAAC"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAaA,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAEpD;;GAEG;AACH,qBAAa,YAAY;IACvB,MAAM,CAAC,QAAQ,CAAC,SAAS,qBAAqB;IAC9C,MAAM,CAAC,QAAQ,CAAC,MAAM,kBAAkB;IACxC,MAAM,CAAC,QAAQ,CAAC,aAAa,mBAAmB;IAChD,MAAM,CAAC,QAAQ,CAAC,UAAU,sBAAsB;CACjD;AAED,qBAAa,aAAa;IACxB,MAAM,CAAC,QAAQ,CAAC,YAAY,OAAO;CACpC;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B;;OAEG;IACH,KAAK,EAAE,MAAM,CAAC;IAEd;;OAEG;IACH,eAAe,EAAE,eAAe,CAAC;IAEjC,YAAY,EAAE,eAAe,CAAC;CAC/B;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,UAAU,EAAE,OAAO,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;GAEG;AACH,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;CAM5B,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ahoo-wang/fetcher-cosec",
3
- "version": "1.5.8",
3
+ "version": "1.6.0",
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.8"
43
+ "@ahoo-wang/fetcher": "1.6.0"
44
44
  },
45
45
  "devDependencies": {
46
46
  "@vitest/coverage-v8": "^3.2.4",