@zskj-idaas/sdk 1.0.0 → 1.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.mts CHANGED
@@ -18,6 +18,11 @@ interface TokenSet {
18
18
  refresh_token?: string;
19
19
  scope: string;
20
20
  }
21
+ interface ServiceTokenState {
22
+ accessToken: string;
23
+ expiresAt: number;
24
+ scope: string;
25
+ }
21
26
  interface UserInfo {
22
27
  sub: string;
23
28
  preferred_username?: string;
@@ -35,6 +40,10 @@ interface AuthState {
35
40
  idToken: string | null;
36
41
  expiresAt: number | null;
37
42
  }
43
+ interface SessionLogoutOptions {
44
+ /** Redirect after the server-side session and local tokens are cleared. */
45
+ redirectTo?: string;
46
+ }
38
47
  interface PKCEPair {
39
48
  codeVerifier: string;
40
49
  codeChallenge: string;
@@ -45,6 +54,7 @@ declare class IdaasClient {
45
54
  private endpoints;
46
55
  private storage;
47
56
  private userInfo;
57
+ private serviceTokens;
48
58
  constructor(config: IdaasConfig);
49
59
  init(): Promise<AuthState>;
50
60
  private discover;
@@ -53,13 +63,16 @@ declare class IdaasClient {
53
63
  refreshToken(): Promise<TokenSet | null>;
54
64
  getUserInfo(forceRefresh?: boolean): Promise<UserInfo | null>;
55
65
  getAccessToken(): Promise<string | null>;
66
+ getServiceToken(serviceKey: string): Promise<string | null>;
56
67
  getIdToken(): string | null;
57
68
  getAuthState(): AuthState;
58
69
  isAuthenticated(): boolean;
59
70
  logout(): Promise<void>;
71
+ logoutSession(options?: SessionLogoutOptions): Promise<void>;
60
72
  revokeToken(): Promise<void>;
61
73
  /** Create an authenticated fetch wrapper */
62
74
  fetch(url: string, init?: RequestInit): Promise<Response>;
75
+ fetchService(serviceKey: string, url: string, init?: RequestInit): Promise<Response>;
63
76
  }
64
77
 
65
78
  declare function createPKCE(): Promise<PKCEPair>;
@@ -86,4 +99,4 @@ declare class TokenStorage {
86
99
  clearAuthState(): void;
87
100
  }
88
101
 
89
- export { type AuthState, IdaasClient, type IdaasConfig, type TokenSet, TokenStorage, type UserInfo, createPKCE, generateNonce, generateState };
102
+ export { type AuthState, IdaasClient, type IdaasConfig, type ServiceTokenState, type SessionLogoutOptions, type TokenSet, TokenStorage, type UserInfo, createPKCE, generateNonce, generateState };
package/dist/index.d.ts CHANGED
@@ -18,6 +18,11 @@ interface TokenSet {
18
18
  refresh_token?: string;
19
19
  scope: string;
20
20
  }
21
+ interface ServiceTokenState {
22
+ accessToken: string;
23
+ expiresAt: number;
24
+ scope: string;
25
+ }
21
26
  interface UserInfo {
22
27
  sub: string;
23
28
  preferred_username?: string;
@@ -35,6 +40,10 @@ interface AuthState {
35
40
  idToken: string | null;
36
41
  expiresAt: number | null;
37
42
  }
43
+ interface SessionLogoutOptions {
44
+ /** Redirect after the server-side session and local tokens are cleared. */
45
+ redirectTo?: string;
46
+ }
38
47
  interface PKCEPair {
39
48
  codeVerifier: string;
40
49
  codeChallenge: string;
@@ -45,6 +54,7 @@ declare class IdaasClient {
45
54
  private endpoints;
46
55
  private storage;
47
56
  private userInfo;
57
+ private serviceTokens;
48
58
  constructor(config: IdaasConfig);
49
59
  init(): Promise<AuthState>;
50
60
  private discover;
@@ -53,13 +63,16 @@ declare class IdaasClient {
53
63
  refreshToken(): Promise<TokenSet | null>;
54
64
  getUserInfo(forceRefresh?: boolean): Promise<UserInfo | null>;
55
65
  getAccessToken(): Promise<string | null>;
66
+ getServiceToken(serviceKey: string): Promise<string | null>;
56
67
  getIdToken(): string | null;
57
68
  getAuthState(): AuthState;
58
69
  isAuthenticated(): boolean;
59
70
  logout(): Promise<void>;
71
+ logoutSession(options?: SessionLogoutOptions): Promise<void>;
60
72
  revokeToken(): Promise<void>;
61
73
  /** Create an authenticated fetch wrapper */
62
74
  fetch(url: string, init?: RequestInit): Promise<Response>;
75
+ fetchService(serviceKey: string, url: string, init?: RequestInit): Promise<Response>;
63
76
  }
64
77
 
65
78
  declare function createPKCE(): Promise<PKCEPair>;
@@ -86,4 +99,4 @@ declare class TokenStorage {
86
99
  clearAuthState(): void;
87
100
  }
88
101
 
89
- export { type AuthState, IdaasClient, type IdaasConfig, type TokenSet, TokenStorage, type UserInfo, createPKCE, generateNonce, generateState };
102
+ export { type AuthState, IdaasClient, type IdaasConfig, type ServiceTokenState, type SessionLogoutOptions, type TokenSet, TokenStorage, type UserInfo, createPKCE, generateNonce, generateState };
package/dist/index.js CHANGED
@@ -42,11 +42,129 @@ function base64UrlEncode(buffer) {
42
42
  });
43
43
  return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
44
44
  }
45
+ async function sha256(data) {
46
+ if (typeof crypto !== "undefined" && crypto.subtle) {
47
+ return crypto.subtle.digest("SHA-256", data);
48
+ }
49
+ const K = [
50
+ 1116352408,
51
+ 1899447441,
52
+ 3049323471,
53
+ 3921009573,
54
+ 961987163,
55
+ 1508970993,
56
+ 2453635748,
57
+ 2870763221,
58
+ 3624381080,
59
+ 310598401,
60
+ 607225278,
61
+ 1426881987,
62
+ 1925078388,
63
+ 2162078206,
64
+ 2614888103,
65
+ 3248222580,
66
+ 3835390401,
67
+ 4022224774,
68
+ 264347078,
69
+ 604807628,
70
+ 770255983,
71
+ 1249150122,
72
+ 1555081692,
73
+ 1996064986,
74
+ 2554220882,
75
+ 2821834349,
76
+ 2952996808,
77
+ 3210313671,
78
+ 3336571891,
79
+ 3584528711,
80
+ 113926993,
81
+ 338241895,
82
+ 666307205,
83
+ 773529912,
84
+ 1294757372,
85
+ 1396182291,
86
+ 1695183700,
87
+ 1986661051,
88
+ 2177026350,
89
+ 2456956037,
90
+ 2730485921,
91
+ 2820302411,
92
+ 3259730800,
93
+ 3345764771,
94
+ 3516065817,
95
+ 3600352804,
96
+ 4094571909,
97
+ 275423344,
98
+ 430227734,
99
+ 506948616,
100
+ 659060556,
101
+ 883997877,
102
+ 958139571,
103
+ 1322822218,
104
+ 1537002063,
105
+ 1747873779,
106
+ 1955562222,
107
+ 2024104815,
108
+ 2227730452,
109
+ 2361852424,
110
+ 2428436474,
111
+ 2756734187,
112
+ 3204031479,
113
+ 3329325298
114
+ ];
115
+ const rotr = (n, x) => x >>> n | x << 32 - n;
116
+ const ch = (x, y, z) => x & y ^ ~x & z;
117
+ const maj = (x, y, z) => x & y ^ x & z ^ y & z;
118
+ const sigma0 = (x) => rotr(2, x) ^ rotr(13, x) ^ rotr(22, x);
119
+ const sigma1 = (x) => rotr(6, x) ^ rotr(11, x) ^ rotr(25, x);
120
+ const gamma0 = (x) => rotr(7, x) ^ rotr(18, x) ^ x >>> 3;
121
+ const gamma1 = (x) => rotr(17, x) ^ rotr(19, x) ^ x >>> 10;
122
+ const len = data.length;
123
+ const bitLen = len * 8;
124
+ const padLen = (len + 8 >> 6 << 6) + 64;
125
+ const padded = new Uint8Array(padLen);
126
+ padded.set(data);
127
+ padded[len] = 128;
128
+ const view = new DataView(padded.buffer);
129
+ view.setUint32(padLen - 4, bitLen, false);
130
+ let h0 = 1779033703, h1 = 3144134277, h2 = 1013904242, h3 = 2773480762;
131
+ let h4 = 1359893119, h5 = 2600822924, h6 = 528734635, h7 = 1541459225;
132
+ for (let offset = 0; offset < padLen; offset += 64) {
133
+ const w = new Array(64);
134
+ for (let i = 0; i < 16; i++) w[i] = view.getUint32(offset + i * 4, false);
135
+ for (let i = 16; i < 64; i++) w[i] = gamma1(w[i - 2]) + w[i - 7] + gamma0(w[i - 15]) + w[i - 16] | 0;
136
+ let a = h0, b = h1, c = h2, d = h3, e = h4, f = h5, g = h6, h = h7;
137
+ for (let i = 0; i < 64; i++) {
138
+ const t1 = h + sigma1(e) + ch(e, f, g) + K[i] + w[i] | 0;
139
+ const t2 = sigma0(a) + maj(a, b, c) | 0;
140
+ h = g;
141
+ g = f;
142
+ f = e;
143
+ e = d + t1 | 0;
144
+ d = c;
145
+ c = b;
146
+ b = a;
147
+ a = t1 + t2 | 0;
148
+ }
149
+ h0 = h0 + a | 0;
150
+ h1 = h1 + b | 0;
151
+ h2 = h2 + c | 0;
152
+ h3 = h3 + d | 0;
153
+ h4 = h4 + e | 0;
154
+ h5 = h5 + f | 0;
155
+ h6 = h6 + g | 0;
156
+ h7 = h7 + h | 0;
157
+ }
158
+ const result = new ArrayBuffer(32);
159
+ const dv = new DataView(result);
160
+ [h0, h1, h2, h3, h4, h5, h6, h7].forEach((v, i) => dv.setUint32(i * 4, v, false));
161
+ return result;
162
+ }
45
163
  async function createPKCE() {
46
164
  const codeVerifier = generateRandomString(64);
47
165
  const encoder = new TextEncoder();
48
166
  const data = encoder.encode(codeVerifier);
49
- const digest = await crypto.subtle.digest("SHA-256", data);
167
+ const digest = await sha256(data);
50
168
  const codeChallenge = base64UrlEncode(digest);
51
169
  return { codeVerifier, codeChallenge };
52
170
  }
@@ -136,6 +254,7 @@ var IdaasClient = class {
136
254
  constructor(config) {
137
255
  this.endpoints = null;
138
256
  this.userInfo = null;
257
+ this.serviceTokens = /* @__PURE__ */ new Map();
139
258
  this.config = {
140
259
  scope: "openid profile email",
141
260
  autoDiscovery: true,
@@ -158,6 +277,7 @@ var IdaasClient = class {
158
277
  introspection_endpoint: `${this.config.issuer}/oauth2/introspect`
159
278
  };
160
279
  }
280
+ await this.getAccessToken();
161
281
  return this.getAuthState();
162
282
  }
163
283
  async discover() {
@@ -274,14 +394,40 @@ var IdaasClient = class {
274
394
  return this.userInfo;
275
395
  }
276
396
  async getAccessToken() {
277
- let token = this.storage.getAccessToken();
278
- if (token) return token;
279
- if (this.storage.isTokenExpired()) {
280
- const tokens = await this.refreshToken();
281
- if (tokens) return tokens.access_token;
282
- }
397
+ const token = this.storage.getAccessToken();
398
+ if (token && !this.storage.isTokenExpired()) return token;
399
+ const tokens = await this.refreshToken();
400
+ if (tokens) return tokens.access_token;
283
401
  return null;
284
402
  }
403
+ async getServiceToken(serviceKey) {
404
+ if (!this.endpoints) await this.init();
405
+ const cached = this.serviceTokens.get(serviceKey);
406
+ if (cached && Date.now() < cached.expiresAt - 6e4) {
407
+ return cached.accessToken;
408
+ }
409
+ const baseToken = await this.getAccessToken();
410
+ if (!baseToken) return null;
411
+ const res = await fetch(`${this.config.issuer}/oauth2/business-token`, {
412
+ method: "POST",
413
+ headers: {
414
+ "Content-Type": "application/json",
415
+ Authorization: `Bearer ${baseToken}`
416
+ },
417
+ body: JSON.stringify({ service_key: serviceKey })
418
+ });
419
+ if (!res.ok) {
420
+ this.serviceTokens.delete(serviceKey);
421
+ return null;
422
+ }
423
+ const tokenSet = await res.json();
424
+ this.serviceTokens.set(serviceKey, {
425
+ accessToken: tokenSet.access_token,
426
+ expiresAt: Date.now() + tokenSet.expires_in * 1e3,
427
+ scope: tokenSet.scope
428
+ });
429
+ return tokenSet.access_token;
430
+ }
285
431
  getIdToken() {
286
432
  return this.storage.getIdToken();
287
433
  }
@@ -310,6 +456,17 @@ var IdaasClient = class {
310
456
  }
311
457
  window.location.href = `${this.endpoints.end_session_endpoint}?${params.toString()}`;
312
458
  }
459
+ async logoutSession(options = {}) {
460
+ this.storage.clear();
461
+ this.userInfo = null;
462
+ await fetch(`${this.config.issuer}/auth/logout`, {
463
+ method: "POST",
464
+ credentials: "include"
465
+ }).catch(() => void 0);
466
+ if (options.redirectTo) {
467
+ window.location.href = options.redirectTo;
468
+ }
469
+ }
313
470
  async revokeToken() {
314
471
  if (!this.endpoints) await this.init();
315
472
  const refreshToken = this.storage.getRefreshToken();
@@ -333,6 +490,13 @@ var IdaasClient = class {
333
490
  }
334
491
  return fetch(url, { ...init, headers });
335
492
  }
493
+ async fetchService(serviceKey, url, init) {
494
+ const token = await this.getServiceToken(serviceKey);
495
+ if (!token) throw new Error(`No service token available for ${serviceKey}`);
496
+ const headers = new Headers(init?.headers);
497
+ headers.set("Authorization", `Bearer ${token}`);
498
+ return fetch(url, { ...init, headers });
499
+ }
336
500
  };
337
501
  // Annotate the CommonJS export names for ESM import in node:
338
502
  0 && (module.exports = {
package/dist/index.mjs CHANGED
@@ -12,11 +12,129 @@ function base64UrlEncode(buffer) {
12
12
  });
13
13
  return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
14
14
  }
15
+ async function sha256(data) {
16
+ if (typeof crypto !== "undefined" && crypto.subtle) {
17
+ return crypto.subtle.digest("SHA-256", data);
18
+ }
19
+ const K = [
20
+ 1116352408,
21
+ 1899447441,
22
+ 3049323471,
23
+ 3921009573,
24
+ 961987163,
25
+ 1508970993,
26
+ 2453635748,
27
+ 2870763221,
28
+ 3624381080,
29
+ 310598401,
30
+ 607225278,
31
+ 1426881987,
32
+ 1925078388,
33
+ 2162078206,
34
+ 2614888103,
35
+ 3248222580,
36
+ 3835390401,
37
+ 4022224774,
38
+ 264347078,
39
+ 604807628,
40
+ 770255983,
41
+ 1249150122,
42
+ 1555081692,
43
+ 1996064986,
44
+ 2554220882,
45
+ 2821834349,
46
+ 2952996808,
47
+ 3210313671,
48
+ 3336571891,
49
+ 3584528711,
50
+ 113926993,
51
+ 338241895,
52
+ 666307205,
53
+ 773529912,
54
+ 1294757372,
55
+ 1396182291,
56
+ 1695183700,
57
+ 1986661051,
58
+ 2177026350,
59
+ 2456956037,
60
+ 2730485921,
61
+ 2820302411,
62
+ 3259730800,
63
+ 3345764771,
64
+ 3516065817,
65
+ 3600352804,
66
+ 4094571909,
67
+ 275423344,
68
+ 430227734,
69
+ 506948616,
70
+ 659060556,
71
+ 883997877,
72
+ 958139571,
73
+ 1322822218,
74
+ 1537002063,
75
+ 1747873779,
76
+ 1955562222,
77
+ 2024104815,
78
+ 2227730452,
79
+ 2361852424,
80
+ 2428436474,
81
+ 2756734187,
82
+ 3204031479,
83
+ 3329325298
84
+ ];
85
+ const rotr = (n, x) => x >>> n | x << 32 - n;
86
+ const ch = (x, y, z) => x & y ^ ~x & z;
87
+ const maj = (x, y, z) => x & y ^ x & z ^ y & z;
88
+ const sigma0 = (x) => rotr(2, x) ^ rotr(13, x) ^ rotr(22, x);
89
+ const sigma1 = (x) => rotr(6, x) ^ rotr(11, x) ^ rotr(25, x);
90
+ const gamma0 = (x) => rotr(7, x) ^ rotr(18, x) ^ x >>> 3;
91
+ const gamma1 = (x) => rotr(17, x) ^ rotr(19, x) ^ x >>> 10;
92
+ const len = data.length;
93
+ const bitLen = len * 8;
94
+ const padLen = (len + 8 >> 6 << 6) + 64;
95
+ const padded = new Uint8Array(padLen);
96
+ padded.set(data);
97
+ padded[len] = 128;
98
+ const view = new DataView(padded.buffer);
99
+ view.setUint32(padLen - 4, bitLen, false);
100
+ let h0 = 1779033703, h1 = 3144134277, h2 = 1013904242, h3 = 2773480762;
101
+ let h4 = 1359893119, h5 = 2600822924, h6 = 528734635, h7 = 1541459225;
102
+ for (let offset = 0; offset < padLen; offset += 64) {
103
+ const w = new Array(64);
104
+ for (let i = 0; i < 16; i++) w[i] = view.getUint32(offset + i * 4, false);
105
+ for (let i = 16; i < 64; i++) w[i] = gamma1(w[i - 2]) + w[i - 7] + gamma0(w[i - 15]) + w[i - 16] | 0;
106
+ let a = h0, b = h1, c = h2, d = h3, e = h4, f = h5, g = h6, h = h7;
107
+ for (let i = 0; i < 64; i++) {
108
+ const t1 = h + sigma1(e) + ch(e, f, g) + K[i] + w[i] | 0;
109
+ const t2 = sigma0(a) + maj(a, b, c) | 0;
110
+ h = g;
111
+ g = f;
112
+ f = e;
113
+ e = d + t1 | 0;
114
+ d = c;
115
+ c = b;
116
+ b = a;
117
+ a = t1 + t2 | 0;
118
+ }
119
+ h0 = h0 + a | 0;
120
+ h1 = h1 + b | 0;
121
+ h2 = h2 + c | 0;
122
+ h3 = h3 + d | 0;
123
+ h4 = h4 + e | 0;
124
+ h5 = h5 + f | 0;
125
+ h6 = h6 + g | 0;
126
+ h7 = h7 + h | 0;
127
+ }
128
+ const result = new ArrayBuffer(32);
129
+ const dv = new DataView(result);
130
+ [h0, h1, h2, h3, h4, h5, h6, h7].forEach((v, i) => dv.setUint32(i * 4, v, false));
131
+ return result;
132
+ }
15
133
  async function createPKCE() {
16
134
  const codeVerifier = generateRandomString(64);
17
135
  const encoder = new TextEncoder();
18
136
  const data = encoder.encode(codeVerifier);
19
- const digest = await crypto.subtle.digest("SHA-256", data);
137
+ const digest = await sha256(data);
20
138
  const codeChallenge = base64UrlEncode(digest);
21
139
  return { codeVerifier, codeChallenge };
22
140
  }
@@ -106,6 +224,7 @@ var IdaasClient = class {
106
224
  constructor(config) {
107
225
  this.endpoints = null;
108
226
  this.userInfo = null;
227
+ this.serviceTokens = /* @__PURE__ */ new Map();
109
228
  this.config = {
110
229
  scope: "openid profile email",
111
230
  autoDiscovery: true,
@@ -128,6 +247,7 @@ var IdaasClient = class {
128
247
  introspection_endpoint: `${this.config.issuer}/oauth2/introspect`
129
248
  };
130
249
  }
250
+ await this.getAccessToken();
131
251
  return this.getAuthState();
132
252
  }
133
253
  async discover() {
@@ -244,14 +364,40 @@ var IdaasClient = class {
244
364
  return this.userInfo;
245
365
  }
246
366
  async getAccessToken() {
247
- let token = this.storage.getAccessToken();
248
- if (token) return token;
249
- if (this.storage.isTokenExpired()) {
250
- const tokens = await this.refreshToken();
251
- if (tokens) return tokens.access_token;
252
- }
367
+ const token = this.storage.getAccessToken();
368
+ if (token && !this.storage.isTokenExpired()) return token;
369
+ const tokens = await this.refreshToken();
370
+ if (tokens) return tokens.access_token;
253
371
  return null;
254
372
  }
373
+ async getServiceToken(serviceKey) {
374
+ if (!this.endpoints) await this.init();
375
+ const cached = this.serviceTokens.get(serviceKey);
376
+ if (cached && Date.now() < cached.expiresAt - 6e4) {
377
+ return cached.accessToken;
378
+ }
379
+ const baseToken = await this.getAccessToken();
380
+ if (!baseToken) return null;
381
+ const res = await fetch(`${this.config.issuer}/oauth2/business-token`, {
382
+ method: "POST",
383
+ headers: {
384
+ "Content-Type": "application/json",
385
+ Authorization: `Bearer ${baseToken}`
386
+ },
387
+ body: JSON.stringify({ service_key: serviceKey })
388
+ });
389
+ if (!res.ok) {
390
+ this.serviceTokens.delete(serviceKey);
391
+ return null;
392
+ }
393
+ const tokenSet = await res.json();
394
+ this.serviceTokens.set(serviceKey, {
395
+ accessToken: tokenSet.access_token,
396
+ expiresAt: Date.now() + tokenSet.expires_in * 1e3,
397
+ scope: tokenSet.scope
398
+ });
399
+ return tokenSet.access_token;
400
+ }
255
401
  getIdToken() {
256
402
  return this.storage.getIdToken();
257
403
  }
@@ -280,6 +426,17 @@ var IdaasClient = class {
280
426
  }
281
427
  window.location.href = `${this.endpoints.end_session_endpoint}?${params.toString()}`;
282
428
  }
429
+ async logoutSession(options = {}) {
430
+ this.storage.clear();
431
+ this.userInfo = null;
432
+ await fetch(`${this.config.issuer}/auth/logout`, {
433
+ method: "POST",
434
+ credentials: "include"
435
+ }).catch(() => void 0);
436
+ if (options.redirectTo) {
437
+ window.location.href = options.redirectTo;
438
+ }
439
+ }
283
440
  async revokeToken() {
284
441
  if (!this.endpoints) await this.init();
285
442
  const refreshToken = this.storage.getRefreshToken();
@@ -303,6 +460,13 @@ var IdaasClient = class {
303
460
  }
304
461
  return fetch(url, { ...init, headers });
305
462
  }
463
+ async fetchService(serviceKey, url, init) {
464
+ const token = await this.getServiceToken(serviceKey);
465
+ if (!token) throw new Error(`No service token available for ${serviceKey}`);
466
+ const headers = new Headers(init?.headers);
467
+ headers.set("Authorization", `Bearer ${token}`);
468
+ return fetch(url, { ...init, headers });
469
+ }
306
470
  };
307
471
  export {
308
472
  IdaasClient,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zskj-idaas/sdk",
3
- "version": "1.0.0",
3
+ "version": "1.0.3",
4
4
  "description": "IDaaS OIDC Client SDK for browser applications — supports Authorization Code Flow with PKCE, token management, and authenticated fetch.",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
@@ -26,6 +26,7 @@
26
26
  "scripts": {
27
27
  "build": "tsup src/index.ts --format cjs,esm --dts --clean",
28
28
  "dev": "tsup src/index.ts --format cjs,esm --dts --watch",
29
+ "test": "vitest run",
29
30
  "prepublishOnly": "npm run build"
30
31
  },
31
32
  "keywords": [
@@ -57,9 +58,9 @@
57
58
  "not dead",
58
59
  "not ie 11"
59
60
  ],
60
- "dependencies": {},
61
61
  "devDependencies": {
62
62
  "tsup": "^8.2.0",
63
- "typescript": "^5.6.0"
63
+ "typescript": "^5.6.0",
64
+ "vitest": "^4.1.7"
64
65
  }
65
66
  }