@nuria-tech/auth-sdk 1.0.2 → 1.0.4

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.cts CHANGED
@@ -1,73 +1,6 @@
1
- interface TokenSet {
2
- accessToken: string;
3
- tokenType?: string;
4
- expiresIn?: number;
5
- refreshToken?: string;
6
- idToken?: string;
7
- scope?: string;
8
- expiresAt?: number;
9
- }
10
- interface Session {
11
- tokens: TokenSet;
12
- createdAt: number;
13
- }
14
- interface StartLoginOptions {
15
- loginHint?: string;
16
- scopes?: string[];
17
- extraParams?: Record<string, string>;
18
- }
19
- interface StorageAdapter {
20
- get(key: string): Promise<string | null> | string | null;
21
- set(key: string, value: string): Promise<void> | void;
22
- remove(key: string): Promise<void> | void;
23
- }
24
- interface AuthTransportRequest {
25
- method?: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
26
- headers?: Record<string, string>;
27
- credentials?: RequestCredentials;
28
- query?: Record<string, string | undefined>;
29
- body?: unknown;
30
- timeoutMs?: number;
31
- retries?: number;
32
- }
33
- interface AuthTransportResponse<T = unknown> {
34
- status: number;
35
- data: T;
36
- headers: Headers;
37
- }
38
- interface AuthTransport {
39
- request<T = unknown>(url: string, req?: AuthTransportRequest): Promise<AuthTransportResponse<T>>;
40
- }
41
- interface TransportInterceptor {
42
- onRequest?: (url: string, req: AuthTransportRequest) => Promise<AuthTransportRequest> | AuthTransportRequest;
43
- onResponse?: <T>(res: AuthTransportResponse<T>) => Promise<AuthTransportResponse<T>> | AuthTransportResponse<T>;
44
- }
45
- interface AuthConfig {
46
- clientId: string;
47
- authorizationEndpoint: string;
48
- tokenEndpoint: string;
49
- redirectUri: string;
50
- scope?: string;
51
- logoutEndpoint?: string;
52
- userinfoEndpoint?: string;
53
- storage?: StorageAdapter;
54
- transport?: AuthTransport;
55
- onRedirect?: (url: string) => void | Promise<void>;
56
- enableRefreshToken?: boolean;
57
- now?: () => number;
58
- }
59
- interface AuthClient {
60
- startLogin(options?: StartLoginOptions): Promise<void>;
61
- handleRedirectCallback(callbackUrl?: string): Promise<Session>;
62
- getSession(): Session | null;
63
- getAccessToken(): Promise<string | null>;
64
- logout(options?: {
65
- returnTo?: string;
66
- }): Promise<void>;
67
- isAuthenticated(): boolean;
68
- onAuthStateChanged(handler: (session: Session | null) => void): () => void;
69
- getUserinfo(): Promise<Record<string, unknown>>;
70
- }
1
+ import { A as AuthConfig, a as AuthClient, S as StorageAdapter, b as AuthTransport, T as TransportInterceptor, c as AuthTransportRequest, d as AuthTransportResponse } from './types-2k4ZYpF4.cjs';
2
+ export { G as GoogleLoginOptions, L as LoginCodeChallengeOptions, P as PasswordLoginOptions, e as Session, f as StartLoginOptions, g as TokenSet, h as TwoFactorChallenge, V as VerifyLoginCodeOptions } from './types-2k4ZYpF4.cjs';
3
+ export { C as CookieStorageAdapter } from './cookie-storage-adapter-FKYkZ--Y.cjs';
71
4
 
72
5
  declare function createAuthClient(config: AuthConfig): AuthClient;
73
6
 
@@ -86,19 +19,6 @@ declare class WebStorageAdapter implements StorageAdapter {
86
19
  remove(key: string): void;
87
20
  }
88
21
 
89
- interface CookieStorageCallbacks {
90
- getCookie(name: string): string | null | Promise<string | null>;
91
- setCookie(name: string, value: string): void | Promise<void>;
92
- removeCookie(name: string): void | Promise<void>;
93
- }
94
- declare class CookieStorageAdapter implements StorageAdapter {
95
- private readonly callbacks;
96
- constructor(callbacks: CookieStorageCallbacks);
97
- get(key: string): Promise<string | null>;
98
- set(key: string, value: string): Promise<void>;
99
- remove(key: string): Promise<void>;
100
- }
101
-
102
22
  interface BrowserCookieStorageOptions {
103
23
  domain?: string;
104
24
  path?: string;
@@ -142,4 +62,4 @@ declare class AuthError extends Error {
142
62
  constructor(code: AuthErrorCode, message: string, cause?: unknown | undefined);
143
63
  }
144
64
 
145
- export { type AuthClient, type AuthConfig, AuthError, AuthErrorCode, type AuthTransport, type AuthTransportRequest, type AuthTransportResponse, type BrowserCookieStorageOptions, CookieStorageAdapter, FetchAuthTransport, MemoryStorageAdapter, type Session, type StartLoginOptions, type StorageAdapter, type TokenSet, type TransportInterceptor, WebStorageAdapter, createAuthClient, createBrowserCookieStorage };
65
+ export { AuthClient, AuthConfig, AuthError, AuthErrorCode, AuthTransport, AuthTransportRequest, AuthTransportResponse, type BrowserCookieStorageOptions, FetchAuthTransport, MemoryStorageAdapter, StorageAdapter, TransportInterceptor, WebStorageAdapter, createAuthClient, createBrowserCookieStorage };
package/dist/index.d.ts CHANGED
@@ -1,73 +1,6 @@
1
- interface TokenSet {
2
- accessToken: string;
3
- tokenType?: string;
4
- expiresIn?: number;
5
- refreshToken?: string;
6
- idToken?: string;
7
- scope?: string;
8
- expiresAt?: number;
9
- }
10
- interface Session {
11
- tokens: TokenSet;
12
- createdAt: number;
13
- }
14
- interface StartLoginOptions {
15
- loginHint?: string;
16
- scopes?: string[];
17
- extraParams?: Record<string, string>;
18
- }
19
- interface StorageAdapter {
20
- get(key: string): Promise<string | null> | string | null;
21
- set(key: string, value: string): Promise<void> | void;
22
- remove(key: string): Promise<void> | void;
23
- }
24
- interface AuthTransportRequest {
25
- method?: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
26
- headers?: Record<string, string>;
27
- credentials?: RequestCredentials;
28
- query?: Record<string, string | undefined>;
29
- body?: unknown;
30
- timeoutMs?: number;
31
- retries?: number;
32
- }
33
- interface AuthTransportResponse<T = unknown> {
34
- status: number;
35
- data: T;
36
- headers: Headers;
37
- }
38
- interface AuthTransport {
39
- request<T = unknown>(url: string, req?: AuthTransportRequest): Promise<AuthTransportResponse<T>>;
40
- }
41
- interface TransportInterceptor {
42
- onRequest?: (url: string, req: AuthTransportRequest) => Promise<AuthTransportRequest> | AuthTransportRequest;
43
- onResponse?: <T>(res: AuthTransportResponse<T>) => Promise<AuthTransportResponse<T>> | AuthTransportResponse<T>;
44
- }
45
- interface AuthConfig {
46
- clientId: string;
47
- authorizationEndpoint: string;
48
- tokenEndpoint: string;
49
- redirectUri: string;
50
- scope?: string;
51
- logoutEndpoint?: string;
52
- userinfoEndpoint?: string;
53
- storage?: StorageAdapter;
54
- transport?: AuthTransport;
55
- onRedirect?: (url: string) => void | Promise<void>;
56
- enableRefreshToken?: boolean;
57
- now?: () => number;
58
- }
59
- interface AuthClient {
60
- startLogin(options?: StartLoginOptions): Promise<void>;
61
- handleRedirectCallback(callbackUrl?: string): Promise<Session>;
62
- getSession(): Session | null;
63
- getAccessToken(): Promise<string | null>;
64
- logout(options?: {
65
- returnTo?: string;
66
- }): Promise<void>;
67
- isAuthenticated(): boolean;
68
- onAuthStateChanged(handler: (session: Session | null) => void): () => void;
69
- getUserinfo(): Promise<Record<string, unknown>>;
70
- }
1
+ import { A as AuthConfig, a as AuthClient, S as StorageAdapter, b as AuthTransport, T as TransportInterceptor, c as AuthTransportRequest, d as AuthTransportResponse } from './types-2k4ZYpF4.js';
2
+ export { G as GoogleLoginOptions, L as LoginCodeChallengeOptions, P as PasswordLoginOptions, e as Session, f as StartLoginOptions, g as TokenSet, h as TwoFactorChallenge, V as VerifyLoginCodeOptions } from './types-2k4ZYpF4.js';
3
+ export { C as CookieStorageAdapter } from './cookie-storage-adapter-EJeGX8Tl.js';
71
4
 
72
5
  declare function createAuthClient(config: AuthConfig): AuthClient;
73
6
 
@@ -86,19 +19,6 @@ declare class WebStorageAdapter implements StorageAdapter {
86
19
  remove(key: string): void;
87
20
  }
88
21
 
89
- interface CookieStorageCallbacks {
90
- getCookie(name: string): string | null | Promise<string | null>;
91
- setCookie(name: string, value: string): void | Promise<void>;
92
- removeCookie(name: string): void | Promise<void>;
93
- }
94
- declare class CookieStorageAdapter implements StorageAdapter {
95
- private readonly callbacks;
96
- constructor(callbacks: CookieStorageCallbacks);
97
- get(key: string): Promise<string | null>;
98
- set(key: string, value: string): Promise<void>;
99
- remove(key: string): Promise<void>;
100
- }
101
-
102
22
  interface BrowserCookieStorageOptions {
103
23
  domain?: string;
104
24
  path?: string;
@@ -142,4 +62,4 @@ declare class AuthError extends Error {
142
62
  constructor(code: AuthErrorCode, message: string, cause?: unknown | undefined);
143
63
  }
144
64
 
145
- export { type AuthClient, type AuthConfig, AuthError, AuthErrorCode, type AuthTransport, type AuthTransportRequest, type AuthTransportResponse, type BrowserCookieStorageOptions, CookieStorageAdapter, FetchAuthTransport, MemoryStorageAdapter, type Session, type StartLoginOptions, type StorageAdapter, type TokenSet, type TransportInterceptor, WebStorageAdapter, createAuthClient, createBrowserCookieStorage };
65
+ export { AuthClient, AuthConfig, AuthError, AuthErrorCode, AuthTransport, AuthTransportRequest, AuthTransportResponse, type BrowserCookieStorageOptions, FetchAuthTransport, MemoryStorageAdapter, StorageAdapter, TransportInterceptor, WebStorageAdapter, createAuthClient, createBrowserCookieStorage };
package/dist/index.js CHANGED
@@ -68,23 +68,25 @@ var STORAGE_KEYS = {
68
68
  codeVerifier: "nuria:oauth:code_verifier"
69
69
  };
70
70
  function normalizeTokenSet(raw, now) {
71
- var _a, _b, _c, _d, _e, _f;
72
- const accessToken = (_a = raw.access_token) != null ? _a : raw.accessToken;
71
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j;
72
+ const accessToken = (_b = (_a = raw.access_token) != null ? _a : raw.accessToken) != null ? _b : raw.Token;
73
73
  if (!accessToken || typeof accessToken !== "string") {
74
74
  throw new AuthError(
75
75
  "TOKEN_EXCHANGE_FAILED" /* TOKEN_EXCHANGE_FAILED */,
76
76
  "Missing access token in token response"
77
77
  );
78
78
  }
79
- const expiresIn = Number((_c = (_b = raw.expires_in) != null ? _b : raw.expiresIn) != null ? _c : 0) || void 0;
79
+ const expiresIn = Number((_d = (_c = raw.expires_in) != null ? _c : raw.expiresIn) != null ? _d : 0) || void 0;
80
+ const expiresAtFromResponse = Number((_e = raw.ExpiresAt) != null ? _e : 0) || void 0;
81
+ const computedExpiresAt = expiresIn != null ? now() + expiresIn * 1e3 : expiresAtFromResponse ? expiresAtFromResponse : void 0;
80
82
  return {
81
83
  accessToken,
82
- tokenType: (_d = raw.token_type) != null ? _d : raw.tokenType,
84
+ tokenType: (_g = (_f = raw.token_type) != null ? _f : raw.tokenType) != null ? _g : raw.TokenType,
83
85
  expiresIn,
84
- refreshToken: (_e = raw.refresh_token) != null ? _e : raw.refreshToken,
85
- idToken: (_f = raw.id_token) != null ? _f : raw.idToken,
86
+ refreshToken: (_i = (_h = raw.refresh_token) != null ? _h : raw.refreshToken) != null ? _i : raw.RefreshToken,
87
+ idToken: (_j = raw.id_token) != null ? _j : raw.idToken,
86
88
  scope: raw.scope,
87
- expiresAt: expiresIn ? now() + expiresIn * 1e3 : void 0
89
+ expiresAt: computedExpiresAt
88
90
  };
89
91
  }
90
92
  async function safeGet(storage, key) {
@@ -335,7 +337,6 @@ var DefaultAuthClient = class {
335
337
  "State validation failed"
336
338
  );
337
339
  }
338
- await safeRemove(this.storage, STORAGE_KEYS.state);
339
340
  return this.exchangeCode(code);
340
341
  }
341
342
  getSession() {
@@ -360,11 +361,27 @@ var DefaultAuthClient = class {
360
361
  }
361
362
  async logout(options) {
362
363
  if (options == null ? void 0 : options.returnTo) {
363
- const returnTo = options.returnTo;
364
- if (returnTo.startsWith("//") || !/^https?:\/\//.test(returnTo)) {
364
+ let returnToUrl;
365
+ try {
366
+ returnToUrl = new URL(options.returnTo);
367
+ } catch (e) {
368
+ throw new AuthError(
369
+ "INVALID_CONFIG" /* INVALID_CONFIG */,
370
+ "returnTo must be a valid absolute URL"
371
+ );
372
+ }
373
+ const isHttps = returnToUrl.protocol === "https:";
374
+ const isLocalHttp = returnToUrl.protocol === "http:" && (returnToUrl.hostname === "localhost" || returnToUrl.hostname === "127.0.0.1" || returnToUrl.hostname === "[::1]");
375
+ if (!isHttps && !isLocalHttp) {
365
376
  throw new AuthError(
366
377
  "INVALID_CONFIG" /* INVALID_CONFIG */,
367
- "returnTo must be an absolute https:// or http:// URL"
378
+ "returnTo must use https:// (or http:// only for localhost)"
379
+ );
380
+ }
381
+ if (returnToUrl.username || returnToUrl.password) {
382
+ throw new AuthError(
383
+ "INVALID_CONFIG" /* INVALID_CONFIG */,
384
+ "returnTo must not include URL credentials"
368
385
  );
369
386
  }
370
387
  }
@@ -387,8 +404,12 @@ var DefaultAuthClient = class {
387
404
  }
388
405
  }
389
406
  isAuthenticated() {
390
- var _a;
391
- return Boolean((_a = this.session) == null ? void 0 : _a.tokens.accessToken);
407
+ var _a, _b;
408
+ const accessToken = (_a = this.session) == null ? void 0 : _a.tokens.accessToken;
409
+ if (!accessToken) return false;
410
+ const exp = (_b = this.session) == null ? void 0 : _b.tokens.expiresAt;
411
+ if (exp && exp <= this.now()) return false;
412
+ return true;
392
413
  }
393
414
  onAuthStateChanged(handler) {
394
415
  this.listeners.add(handler);
@@ -414,6 +435,103 @@ var DefaultAuthClient = class {
414
435
  );
415
436
  return response.data;
416
437
  }
438
+ async startLoginCodeChallenge(options) {
439
+ var _a, _b, _c, _d, _e, _f, _g;
440
+ if (!(options == null ? void 0 : options.email)) {
441
+ throw new AuthError(
442
+ "INVALID_CONFIG" /* INVALID_CONFIG */,
443
+ "email is required for startLoginCodeChallenge"
444
+ );
445
+ }
446
+ const response = await this.transport.request(
447
+ `${this.config.baseUrl}/v2/login-code/challenge`,
448
+ {
449
+ method: "POST",
450
+ credentials: "include",
451
+ body: {
452
+ email: options.email,
453
+ channel: (_a = options.channel) != null ? _a : "email",
454
+ destination: options.destination,
455
+ purpose: (_b = options.purpose) != null ? _b : "login"
456
+ }
457
+ }
458
+ );
459
+ return {
460
+ challengeId: String((_c = response.data.challengeId) != null ? _c : ""),
461
+ channel: String((_d = response.data.channel) != null ? _d : ""),
462
+ destinationMasked: String((_e = response.data.destinationMasked) != null ? _e : ""),
463
+ expiresAt: Number((_f = response.data.expiresAt) != null ? _f : 0),
464
+ purpose: String((_g = response.data.purpose) != null ? _g : "login")
465
+ };
466
+ }
467
+ async verifyLoginCode(options) {
468
+ if (!(options == null ? void 0 : options.challengeId) || !(options == null ? void 0 : options.code)) {
469
+ throw new AuthError(
470
+ "INVALID_CONFIG" /* INVALID_CONFIG */,
471
+ "challengeId and code are required for verifyLoginCode"
472
+ );
473
+ }
474
+ const response = await this.transport.request(
475
+ `${this.config.baseUrl}/v2/2fa/verify-login`,
476
+ {
477
+ method: "POST",
478
+ credentials: "include",
479
+ body: {
480
+ challengeId: options.challengeId,
481
+ code: options.code
482
+ }
483
+ }
484
+ );
485
+ const tokens = normalizeTokenSet(response.data, this.now);
486
+ return this.createSession(tokens);
487
+ }
488
+ async loginWithCodeSent(options) {
489
+ return this.startLoginCodeChallenge(options);
490
+ }
491
+ async completeLoginWithCode(options) {
492
+ return this.verifyLoginCode(options);
493
+ }
494
+ async loginWithGoogle(options) {
495
+ if (!(options == null ? void 0 : options.idToken)) {
496
+ throw new AuthError(
497
+ "INVALID_CONFIG" /* INVALID_CONFIG */,
498
+ "idToken is required for loginWithGoogle"
499
+ );
500
+ }
501
+ const response = await this.transport.request(
502
+ `${this.config.baseUrl}/v2/google`,
503
+ {
504
+ method: "POST",
505
+ credentials: "include",
506
+ body: {
507
+ idToken: options.idToken
508
+ }
509
+ }
510
+ );
511
+ const tokens = normalizeTokenSet(response.data, this.now);
512
+ return this.createSession(tokens);
513
+ }
514
+ async loginWithPassword(options) {
515
+ if (!(options == null ? void 0 : options.email) || !(options == null ? void 0 : options.password)) {
516
+ throw new AuthError(
517
+ "INVALID_CONFIG" /* INVALID_CONFIG */,
518
+ "email and password are required for loginWithPassword"
519
+ );
520
+ }
521
+ const response = await this.transport.request(
522
+ `${this.config.baseUrl}/v2/login`,
523
+ {
524
+ method: "POST",
525
+ credentials: "include",
526
+ body: {
527
+ email: options.email,
528
+ password: options.password
529
+ }
530
+ }
531
+ );
532
+ const tokens = normalizeTokenSet(response.data, this.now);
533
+ return this.createSession(tokens);
534
+ }
417
535
  async exchangeCode(code) {
418
536
  const verifier = await safeGet(this.storage, STORAGE_KEYS.codeVerifier);
419
537
  if (!verifier) {
@@ -438,6 +556,7 @@ var DefaultAuthClient = class {
438
556
  }
439
557
  );
440
558
  const tokens = normalizeTokenSet(response.data, this.now);
559
+ await safeRemove(this.storage, STORAGE_KEYS.state);
441
560
  await safeRemove(this.storage, STORAGE_KEYS.codeVerifier);
442
561
  return this.createSession(tokens);
443
562
  }
@@ -501,23 +620,48 @@ var DefaultAuthClient = class {
501
620
  };
502
621
 
503
622
  // src/client/create-client.ts
504
- function createAuthClient(config) {
505
- if (!(config == null ? void 0 : config.clientId)) {
623
+ var DEFAULT_AUTH_BASE_URL = "https://ms-auth-v2.nuria.com.br";
624
+ var DEFAULT_AUTHORIZATION_PATH = "/v2/oauth/authorize";
625
+ var DEFAULT_TOKEN_PATH = "/v2/oauth/token";
626
+ var DEFAULT_SCOPE = "openid profile email";
627
+ function normalizeBaseUrl(value) {
628
+ const raw = String(value != null ? value : DEFAULT_AUTH_BASE_URL).trim();
629
+ if (!raw) {
506
630
  throw new AuthError(
507
631
  "INVALID_CONFIG" /* INVALID_CONFIG */,
508
- "config.clientId is required"
632
+ "config.baseUrl must be a valid absolute URL"
509
633
  );
510
634
  }
511
- if (!config.authorizationEndpoint) {
635
+ let parsed;
636
+ try {
637
+ parsed = new URL(raw);
638
+ } catch (e) {
512
639
  throw new AuthError(
513
640
  "INVALID_CONFIG" /* INVALID_CONFIG */,
514
- "config.authorizationEndpoint is required"
641
+ "config.baseUrl must be a valid absolute URL"
515
642
  );
516
643
  }
517
- if (!config.tokenEndpoint) {
644
+ return parsed.toString().replace(/\/+$/, "");
645
+ }
646
+ function resolveEndpoint(baseUrl, explicit, fallbackPath) {
647
+ if (explicit) {
648
+ try {
649
+ return new URL(explicit).toString();
650
+ } catch (e) {
651
+ throw new AuthError(
652
+ "INVALID_CONFIG" /* INVALID_CONFIG */,
653
+ "OAuth endpoints must be valid absolute URLs"
654
+ );
655
+ }
656
+ }
657
+ return new URL(fallbackPath, `${baseUrl}/`).toString();
658
+ }
659
+ function createAuthClient(config) {
660
+ var _a, _b;
661
+ if (!(config == null ? void 0 : config.clientId)) {
518
662
  throw new AuthError(
519
663
  "INVALID_CONFIG" /* INVALID_CONFIG */,
520
- "config.tokenEndpoint is required"
664
+ "config.clientId is required"
521
665
  );
522
666
  }
523
667
  if (!config.redirectUri) {
@@ -526,7 +670,24 @@ function createAuthClient(config) {
526
670
  "config.redirectUri is required"
527
671
  );
528
672
  }
529
- return new DefaultAuthClient(config);
673
+ const baseUrl = normalizeBaseUrl(config.baseUrl);
674
+ const resolvedConfig = {
675
+ ...config,
676
+ baseUrl,
677
+ scope: String((_a = config.scope) != null ? _a : "").trim() || DEFAULT_SCOPE,
678
+ enableRefreshToken: (_b = config.enableRefreshToken) != null ? _b : true,
679
+ authorizationEndpoint: resolveEndpoint(
680
+ baseUrl,
681
+ config.authorizationEndpoint,
682
+ DEFAULT_AUTHORIZATION_PATH
683
+ ),
684
+ tokenEndpoint: resolveEndpoint(
685
+ baseUrl,
686
+ config.tokenEndpoint,
687
+ DEFAULT_TOKEN_PATH
688
+ )
689
+ };
690
+ return new DefaultAuthClient(resolvedConfig);
530
691
  }
531
692
 
532
693
  // src/storage/web-storage-adapter.ts
@@ -565,8 +726,18 @@ var CookieStorageAdapter = class {
565
726
  var getCookieValue = (name) => {
566
727
  var _a;
567
728
  if (typeof document === "undefined") return null;
568
- const result = document.cookie.match(`(^|;)\\s*${name}\\s*=\\s*([^;]+)`);
569
- return result ? (_a = result.pop()) != null ? _a : null : null;
729
+ const escapedName = name.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
730
+ const result = document.cookie.match(
731
+ `(^|;)\\s*${escapedName}\\s*=\\s*([^;]+)`
732
+ );
733
+ if (!result) return null;
734
+ const raw = (_a = result.pop()) != null ? _a : null;
735
+ if (raw == null) return null;
736
+ try {
737
+ return decodeURIComponent(raw);
738
+ } catch (e) {
739
+ return raw;
740
+ }
570
741
  };
571
742
  function createBrowserCookieStorage(options = {}) {
572
743
  const { domain, path = "/", sameSite = "strict", secure = true } = options;
@@ -575,7 +746,7 @@ function createBrowserCookieStorage(options = {}) {
575
746
  };
576
747
  const set = (key, value) => {
577
748
  if (typeof document === "undefined") return;
578
- let cookie = `${key}=${value}`;
749
+ let cookie = `${key}=${encodeURIComponent(value)}`;
579
750
  if (path) cookie += `; path=${path}`;
580
751
  if (domain) cookie += `; domain=${domain}`;
581
752
  if (sameSite) cookie += `; samesite=${sameSite}`;