@thunderid/browser 1.3.0 → 1.3.1

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.
@@ -1,4 +1,4 @@
1
- import { ThunderIDJavaScriptClient, User, IsomorphicCrypto, IdToken, OIDCEndpoints, Storage, TokenResponse, HttpRequestConfig, HttpResponse } from '@thunderid/javascript';
1
+ import { ThunderIDJavaScriptClient, User, IsomorphicCrypto, IdToken, OIDCEndpoints, Storage, TokenResponse, HttpRequestConfig, HttpResponse, UserProfile } from '@thunderid/javascript';
2
2
  import FetchHttpClient from './FetchHttpClient';
3
3
  import { BrowserAuthConfig } from './models/BrowserConfig';
4
4
  import SignInConfig from './models/SignInConfig';
@@ -11,14 +11,7 @@ declare class ThunderIDBrowserClient<T = BrowserAuthConfig> extends ThunderIDJav
11
11
  private _storage;
12
12
  private _getSignOutURLFromSessionStorage;
13
13
  private _isHttpHandlerEnabled;
14
- private _httpErrorCallback;
15
- private _httpFinishCallback;
16
- private _onSignInCallback;
17
- private _onSignOutCallback;
18
- private _onSignOutFailedCallback;
19
- private _onEndUserSession;
20
- private _onInitialize;
21
- private _onCustomGrant;
14
+ private _hookCallbacks;
22
15
  private _initialized;
23
16
  private _startedInitialize;
24
17
  constructor(instanceId?: number);
@@ -39,6 +32,17 @@ declare class ThunderIDBrowserClient<T = BrowserAuthConfig> extends ThunderIDJav
39
32
  httpRequest(requestConfig: HttpRequestConfig): Promise<HttpResponse | undefined>;
40
33
  httpRequestAll(configs: HttpRequestConfig[]): Promise<HttpResponse[] | undefined>;
41
34
  getUser(userId?: string): Promise<User>;
35
+ /**
36
+ * Fetches the signed-in user's profile from the `/users/me` endpoint (or `endpoints.usersMe` when
37
+ * configured), sending the request through the client's HTTP client so the access token is attached.
38
+ *
39
+ * @param options - Optional `baseUrl` or full `url` overrides for the profile endpoint.
40
+ * @returns The user's profile.
41
+ */
42
+ getUserProfile(options?: {
43
+ baseUrl?: string;
44
+ url?: string;
45
+ }): Promise<UserProfile>;
42
46
  getAccessToken(sessionId?: string): Promise<string>;
43
47
  getDecodedIdToken(userId?: string, idToken?: string): Promise<IdToken>;
44
48
  getIdToken(userId?: string): Promise<string>;
@@ -56,7 +60,20 @@ declare class ThunderIDBrowserClient<T = BrowserAuthConfig> extends ThunderIDJav
56
60
  enableHttpHandler(): Promise<boolean | undefined>;
57
61
  disableHttpHandler(): Promise<boolean | undefined>;
58
62
  decodeJwtToken<R = Record<string, unknown>>(token: string): Promise<R>;
63
+ /**
64
+ * Subscribes a callback to a lifecycle hook. Every callback registered for a hook is called when it
65
+ * fires; registering the same callback again for the same hook has no further effect. Hooks can be
66
+ * registered before or after `initialize()`.
67
+ *
68
+ * @param hook - The hook to subscribe to (see `Hooks`).
69
+ * @param callback - Called when the hook fires.
70
+ * @param id - The custom grant id. Required for `custom-grant`.
71
+ */
59
72
  on(hook: string, callback: (response?: any) => void | Promise<void>, id?: string): Promise<void>;
73
+ private _addHookCallback;
74
+ private _emitHook;
75
+ private _emitHttpError;
76
+ private _emitHttpFinish;
60
77
  private _enableRetrievingSignOutURLFromSession;
61
78
  private _constructSilentSignInUrl;
62
79
  getInstanceId(): number;
@@ -1 +1 @@
1
- {"version":3,"file":"ThunderIDBrowserClient.d.ts","sourceRoot":"","sources":["../src/ThunderIDBrowserClient.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ThunderIDBrowserClient.d.ts","sourceRoot":"","sources":["../src/ThunderIDBrowserClient.ts"],"names":[],"mappings":"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"}
@@ -35,6 +35,26 @@ fast_sha256 = __toESM(fast_sha256);
35
35
  let jose = require("jose");
36
36
  jose = __toESM(jose);
37
37
 
38
+ //#region src/constants/Hooks.ts
39
+ /**
40
+ * Event hook names for attaching callbacks via `ThunderIDBrowserClient.on()`.
41
+ */
42
+ let Hooks = /* @__PURE__ */ function(Hooks$1) {
43
+ Hooks$1["SignIn"] = "sign-in";
44
+ Hooks$1["SignOut"] = "sign-out";
45
+ Hooks$1["Initialize"] = "initialize";
46
+ Hooks$1["HttpRequestStart"] = "http-request-start";
47
+ Hooks$1["HttpRequestFinish"] = "http-request-finish";
48
+ Hooks$1["HttpRequestError"] = "http-request-error";
49
+ Hooks$1["HttpRequestSuccess"] = "http-request-success";
50
+ Hooks$1["RevokeAccessToken"] = "revoke-access-token";
51
+ Hooks$1["CustomGrant"] = "custom-grant";
52
+ Hooks$1["SignOutFailed"] = "sign-out-failed";
53
+ return Hooks$1;
54
+ }({});
55
+ var Hooks_default = Hooks;
56
+
57
+ //#endregion
38
58
  //#region src/constants/SPAConstants.ts
39
59
  const ERROR = "error";
40
60
  const ERROR_DESCRIPTION = "error_description";
@@ -782,6 +802,57 @@ var AuthenticationHelper = class {
782
802
  };
783
803
  var AuthenticationHelper_default = AuthenticationHelper;
784
804
 
805
+ //#endregion
806
+ //#region src/utils/createHttpClientFetcher.ts
807
+ /**
808
+ * Builds a `fetch`-shaped function backed by the shared `FetchHttpClient` singleton, for
809
+ * framework wrappers (`@thunderid/react`, `@thunderid/vue`) that need to hand a core
810
+ * `@thunderid/javascript` API function a `fetcher` carrying the access token automatically.
811
+ *
812
+ * Unlike a plain `fetch` call, `FetchHttpClient.request` throws on a non-2xx response instead
813
+ * of resolving it. This adapter catches that and reconstructs a `Response`-shaped object from
814
+ * the thrown error's `response`, so the core API function sees the real status and body instead
815
+ * of treating every non-2xx result as a network failure. A genuine network error (no response
816
+ * attached) still propagates.
817
+ *
818
+ * @param instanceId - Which `FetchHttpClient` instance to use, for multi-instance apps. Defaults to `0`.
819
+ * @returns A function matching the `(url, config) => Promise<Response>` shape core API functions expect.
820
+ * @example
821
+ * ```typescript
822
+ * await updateMeCredentials({
823
+ * ...config,
824
+ * fetcher: fetcher ?? createHttpClientFetcher(instanceId),
825
+ * });
826
+ * ```
827
+ */
828
+ const createHttpClientFetcher = (instanceId = 0) => {
829
+ return async (url, config) => {
830
+ const httpClient = FetchHttpClient_default.getInstance(instanceId);
831
+ const toResponse = (data, status, statusText) => ({
832
+ json: () => Promise.resolve(data),
833
+ ok: status >= 200 && status < 300,
834
+ status,
835
+ statusText,
836
+ text: () => Promise.resolve(typeof data === "string" ? data : JSON.stringify(data))
837
+ });
838
+ try {
839
+ const response = await httpClient.request({
840
+ data: config.body ? JSON.parse(config.body) : void 0,
841
+ headers: config.headers,
842
+ method: config.method || "POST",
843
+ signal: config.signal,
844
+ url
845
+ });
846
+ return toResponse(response.data, response.status, response.statusText || "");
847
+ } catch (error) {
848
+ const httpError = error;
849
+ if (httpError?.response) return toResponse(httpError.response.data, httpError.response.status, httpError.response.statusText ?? "");
850
+ throw error;
851
+ }
852
+ };
853
+ };
854
+ var createHttpClientFetcher_default = createHttpClientFetcher;
855
+
785
856
  //#endregion
786
857
  //#region src/utils/navigate.ts
787
858
  /**
@@ -2441,14 +2512,7 @@ var ThunderIDBrowserClient = class ThunderIDBrowserClient extends __thunderid_ja
2441
2512
  _storage = BrowserStorage_default.SessionStorage;
2442
2513
  _getSignOutURLFromSessionStorage = false;
2443
2514
  _isHttpHandlerEnabled = true;
2444
- _httpErrorCallback;
2445
- _httpFinishCallback;
2446
- _onSignInCallback = () => null;
2447
- _onSignOutCallback = () => null;
2448
- _onSignOutFailedCallback = () => null;
2449
- _onEndUserSession = () => null;
2450
- _onInitialize = () => null;
2451
- _onCustomGrant = /* @__PURE__ */ new Map();
2515
+ _hookCallbacks = /* @__PURE__ */ new Map();
2452
2516
  _initialized = false;
2453
2517
  _startedInitialize = false;
2454
2518
  constructor(instanceId = 0) {
@@ -2475,6 +2539,12 @@ var ThunderIDBrowserClient = class ThunderIDBrowserClient extends __thunderid_ja
2475
2539
  await this._authHelper?.attachTokenToRequestConfig(request);
2476
2540
  };
2477
2541
  this._httpClient = FetchHttpClient_default.getInstance(this._browserInstanceId, true, attachToken);
2542
+ this._httpClient.setHttpRequestStartCallback((request) => {
2543
+ this._emitHook(Hooks_default.HttpRequestStart, request);
2544
+ });
2545
+ this._httpClient.setHttpRequestSuccessCallback((response) => {
2546
+ this._emitHook(Hooks_default.HttpRequestSuccess, response);
2547
+ });
2478
2548
  this._authHelper = new AuthenticationHelper_default(sm, this._spaHelper, this._browserInstanceId, {
2479
2549
  exchangeToken: (cfg) => super.exchangeToken(cfg),
2480
2550
  getAccessToken: (sessionId) => this.getAccessToken(sessionId),
@@ -2489,7 +2559,7 @@ var ThunderIDBrowserClient = class ThunderIDBrowserClient extends __thunderid_ja
2489
2559
  setPKCECode: (pkceKey, state) => this.setPKCECode(pkceKey, state)
2490
2560
  });
2491
2561
  this._initialized = true;
2492
- if (this._onInitialize) this._onInitialize(true);
2562
+ this._emitHook(Hooks_default.Initialize, true);
2493
2563
  if (!merged.autoLogoutOnTokenRefreshError) return true;
2494
2564
  window.addEventListener("message", (event) => {
2495
2565
  if (event?.data?.type === REFRESH_ACCESS_TOKEN_ERR0R) this.signOut();
@@ -2520,7 +2590,7 @@ var ThunderIDBrowserClient = class ThunderIDBrowserClient extends __thunderid_ja
2520
2590
  if (!SPAUtils_default.canContinueSignIn(Boolean(config?.callOnlyOnRedirect), authorizationCode)) return;
2521
2591
  delete config?.callOnlyOnRedirect;
2522
2592
  const user = await this._signInInternal(config, authorizationCode, state, tokenRequestConfig);
2523
- if (user && this._onSignInCallback) this._onSignInCallback(user);
2593
+ if (user) this._emitHook(Hooks_default.SignIn, user);
2524
2594
  return user;
2525
2595
  }
2526
2596
  async _signInInternal(signInConfig, authorizationCode, state, tokenRequestConfig) {
@@ -2589,7 +2659,7 @@ var ThunderIDBrowserClient = class ThunderIDBrowserClient extends __thunderid_ja
2589
2659
  await this.isInitialized();
2590
2660
  if (SPAUtils_default.wasSignInCalled()) return;
2591
2661
  const response = await this._signInSilentlyInternal(additionalParams, tokenRequestConfig);
2592
- if (this._onSignInCallback && response) this._onSignInCallback(response);
2662
+ if (response) this._emitHook(Hooks_default.SignIn, response);
2593
2663
  return response;
2594
2664
  }
2595
2665
  async _signInSilentlyInternal(additionalParams, tokenRequestConfig) {
@@ -2635,16 +2705,40 @@ var ThunderIDBrowserClient = class ThunderIDBrowserClient extends __thunderid_ja
2635
2705
  }
2636
2706
  async httpRequest(requestConfig) {
2637
2707
  await this._validateMethod(false);
2638
- return this._authHelper.httpRequest(this._httpClient, requestConfig, this._isHttpHandlerEnabled, this._httpErrorCallback, this._httpFinishCallback);
2708
+ const response = await this._authHelper.httpRequest(this._httpClient, requestConfig, this._isHttpHandlerEnabled, this._emitHttpError, this._emitHttpFinish);
2709
+ if (this._isHttpHandlerEnabled) this._emitHttpFinish();
2710
+ return response;
2639
2711
  }
2640
2712
  async httpRequestAll(configs) {
2641
2713
  await this._validateMethod(false);
2642
- return this._authHelper.httpRequestAll(configs, this._httpClient, this._isHttpHandlerEnabled, this._httpErrorCallback, this._httpFinishCallback);
2714
+ const responses = await this._authHelper.httpRequestAll(configs, this._httpClient, this._isHttpHandlerEnabled, this._emitHttpError, this._emitHttpFinish);
2715
+ if (this._isHttpHandlerEnabled) this._emitHttpFinish();
2716
+ return responses;
2643
2717
  }
2644
2718
  async getUser(userId) {
2645
2719
  await this._validateMethod();
2646
2720
  return super.getUser(userId);
2647
2721
  }
2722
+ /**
2723
+ * Fetches the signed-in user's profile from the `/users/me` endpoint (or `endpoints.usersMe` when
2724
+ * configured), sending the request through the client's HTTP client so the access token is attached.
2725
+ *
2726
+ * @param options - Optional `baseUrl` or full `url` overrides for the profile endpoint.
2727
+ * @returns The user's profile.
2728
+ */
2729
+ async getUserProfile(options) {
2730
+ await this._validateMethod();
2731
+ const configData = await this.getStorageManager().getConfigData();
2732
+ const profile = await (0, __thunderid_javascript.getUsersMe)({
2733
+ baseUrl: options?.baseUrl ?? configData?.baseUrl,
2734
+ fetcher: createHttpClientFetcher_default(this._browserInstanceId),
2735
+ url: (0, __thunderid_javascript.resolveResourceEndpoint)("usersMe", configData, options?.url)
2736
+ });
2737
+ return {
2738
+ flattenedProfile: (0, __thunderid_javascript.generateFlattenedUserProfile)(profile),
2739
+ profile
2740
+ };
2741
+ }
2648
2742
  async getAccessToken(sessionId) {
2649
2743
  return super.getAccessToken(sessionId);
2650
2744
  }
@@ -2673,7 +2767,7 @@ var ThunderIDBrowserClient = class ThunderIDBrowserClient extends __thunderid_ja
2673
2767
  return super.isSignedIn(userId);
2674
2768
  }
2675
2769
  notifySignIn(user) {
2676
- this._onSignInCallback(user);
2770
+ this._emitHook(Hooks_default.SignIn, user);
2677
2771
  }
2678
2772
  async isSessionActive() {
2679
2773
  await this.isInitialized();
@@ -2692,14 +2786,14 @@ var ThunderIDBrowserClient = class ThunderIDBrowserClient extends __thunderid_ja
2692
2786
  const timer = await this._spaHelper.getRefreshTimeoutTimer();
2693
2787
  await super.revokeAccessToken(userId);
2694
2788
  await this._spaHelper.clearRefreshTokenTimeout(timer);
2695
- this._onEndUserSession && await this._onEndUserSession(true);
2789
+ await this._emitHook(Hooks_default.RevokeAccessToken, true);
2696
2790
  return true;
2697
2791
  }
2698
2792
  async exchangeToken(config) {
2699
2793
  if (config.signInRequired) await this._validateMethod();
2700
2794
  if (!config.id) return Promise.reject(new __thunderid_javascript.ThunderIDAuthException("SPA-AUTH_CLIENT-RCG-NF01", "The custom grant request id not found.", "Set the `id` attribute on the token exchange config."));
2701
2795
  const response = await this._authHelper.exchangeToken(config, (cfg) => this._enableRetrievingSignOutURLFromSession(cfg));
2702
- this._onCustomGrant.get(config.id)?.(response);
2796
+ this._emitHook(`${Hooks_default.CustomGrant}:${config.id}`, response);
2703
2797
  return response;
2704
2798
  }
2705
2799
  async reInitialize(config) {
@@ -2726,47 +2820,56 @@ var ThunderIDBrowserClient = class ThunderIDBrowserClient extends __thunderid_ja
2726
2820
  async decodeJwtToken(token) {
2727
2821
  return this.getCryptoHelper().decodeJwtToken(token);
2728
2822
  }
2823
+ /**
2824
+ * Subscribes a callback to a lifecycle hook. Every callback registered for a hook is called when it
2825
+ * fires; registering the same callback again for the same hook has no further effect. Hooks can be
2826
+ * registered before or after `initialize()`.
2827
+ *
2828
+ * @param hook - The hook to subscribe to (see `Hooks`).
2829
+ * @param callback - Called when the hook fires.
2830
+ * @param id - The custom grant id. Required for `custom-grant`.
2831
+ */
2729
2832
  async on(hook, callback, id) {
2730
2833
  await this.isInitialized();
2731
2834
  if (!callback || typeof callback !== "function") throw new __thunderid_javascript.ThunderIDAuthException("SPA-AUTH_CLIENT-ON-IV02", "Invalid callback function.", "The provided callback must be a function.");
2732
2835
  switch (hook) {
2733
- case "sign-in":
2734
- this._onSignInCallback = callback;
2735
- break;
2736
- case "sign-out":
2737
- this._onSignOutCallback = callback;
2738
- if (await SPAUtils_default.isSignOutSuccessful(ThunderIDBrowserClient.isSignOutSuccessful.bind(ThunderIDBrowserClient), () => ThunderIDBrowserClient.clearSession())) this._onSignOutCallback();
2836
+ case Hooks_default.SignIn:
2837
+ case Hooks_default.RevokeAccessToken:
2838
+ case Hooks_default.Initialize:
2839
+ case Hooks_default.HttpRequestError:
2840
+ case Hooks_default.HttpRequestFinish:
2841
+ case Hooks_default.HttpRequestStart:
2842
+ case Hooks_default.HttpRequestSuccess:
2843
+ this._addHookCallback(hook, callback);
2739
2844
  break;
2740
- case "revoke-access-token":
2741
- this._onEndUserSession = callback;
2845
+ case Hooks_default.SignOut:
2846
+ if (await SPAUtils_default.isSignOutSuccessful(ThunderIDBrowserClient.isSignOutSuccessful.bind(ThunderIDBrowserClient), () => ThunderIDBrowserClient.clearSession())) callback();
2742
2847
  break;
2743
- case "initialize":
2744
- this._onInitialize = callback;
2848
+ case Hooks_default.CustomGrant:
2849
+ id && this._addHookCallback(`${Hooks_default.CustomGrant}:${id}`, callback);
2745
2850
  break;
2746
- case "http-request-error":
2747
- this._httpErrorCallback = callback;
2748
- break;
2749
- case "http-request-finish":
2750
- this._httpFinishCallback = callback;
2751
- break;
2752
- case "http-request-start":
2753
- this._httpClient?.setHttpRequestStartCallback?.(callback);
2754
- break;
2755
- case "http-request-success":
2756
- this._httpClient?.setHttpRequestSuccessCallback?.(callback);
2757
- break;
2758
- case "custom-grant":
2759
- id && this._onCustomGrant.set(id, callback);
2760
- break;
2761
- case "sign-out-failed": {
2762
- this._onSignOutFailedCallback = callback;
2851
+ case Hooks_default.SignOutFailed: {
2763
2852
  const signOutFail = SPAUtils_default.didSignOutFail(ThunderIDBrowserClient.didSignOutFail.bind(ThunderIDBrowserClient));
2764
- if (signOutFail) this._onSignOutFailedCallback(signOutFail);
2853
+ if (signOutFail) callback(signOutFail);
2765
2854
  break;
2766
2855
  }
2767
2856
  default: throw new __thunderid_javascript.ThunderIDAuthException("SPA-AUTH_CLIENT-ON-IV01", "Invalid hook.", `Unknown hook: "${hook}"`);
2768
2857
  }
2769
2858
  }
2859
+ _addHookCallback(hook, callback) {
2860
+ const callbacks = this._hookCallbacks.get(hook) ?? /* @__PURE__ */ new Set();
2861
+ callbacks.add(callback);
2862
+ this._hookCallbacks.set(hook, callbacks);
2863
+ }
2864
+ _emitHook(hook, response) {
2865
+ return Promise.all([...this._hookCallbacks.get(hook) ?? []].map((callback) => Promise.resolve(callback(response))));
2866
+ }
2867
+ _emitHttpError = async (error) => {
2868
+ await this._emitHook(Hooks_default.HttpRequestError, error);
2869
+ };
2870
+ _emitHttpFinish = () => {
2871
+ this._emitHook(Hooks_default.HttpRequestFinish);
2872
+ };
2770
2873
  _enableRetrievingSignOutURLFromSession(config) {
2771
2874
  if (config.preventSignOutURLUpdate) this._getSignOutURLFromSessionStorage = true;
2772
2875
  }
@@ -2792,26 +2895,6 @@ var ThunderIDBrowserClient = class ThunderIDBrowserClient extends __thunderid_ja
2792
2895
  };
2793
2896
  var ThunderIDBrowserClient_default = ThunderIDBrowserClient;
2794
2897
 
2795
- //#endregion
2796
- //#region src/constants/Hooks.ts
2797
- /**
2798
- * Event hook names for attaching callbacks via `ThunderIDBrowserClient.on()`.
2799
- */
2800
- let Hooks = /* @__PURE__ */ function(Hooks$1) {
2801
- Hooks$1["SignIn"] = "sign-in";
2802
- Hooks$1["SignOut"] = "sign-out";
2803
- Hooks$1["Initialize"] = "initialize";
2804
- Hooks$1["HttpRequestStart"] = "http-request-start";
2805
- Hooks$1["HttpRequestFinish"] = "http-request-finish";
2806
- Hooks$1["HttpRequestError"] = "http-request-error";
2807
- Hooks$1["HttpRequestSuccess"] = "http-request-success";
2808
- Hooks$1["RevokeAccessToken"] = "revoke-access-token";
2809
- Hooks$1["CustomGrant"] = "custom-grant";
2810
- Hooks$1["SignOutFailed"] = "sign-out-failed";
2811
- return Hooks$1;
2812
- }({});
2813
- var Hooks_default = Hooks;
2814
-
2815
2898
  //#endregion
2816
2899
  //#region src/utils/hasAuthParamsInUrl.ts
2817
2900
  /**
@@ -2863,57 +2946,6 @@ const http = (client) => {
2863
2946
  };
2864
2947
  var http_default = http;
2865
2948
 
2866
- //#endregion
2867
- //#region src/utils/createHttpClientFetcher.ts
2868
- /**
2869
- * Builds a `fetch`-shaped function backed by the shared `FetchHttpClient` singleton, for
2870
- * framework wrappers (`@thunderid/react`, `@thunderid/vue`) that need to hand a core
2871
- * `@thunderid/javascript` API function a `fetcher` carrying the access token automatically.
2872
- *
2873
- * Unlike a plain `fetch` call, `FetchHttpClient.request` throws on a non-2xx response instead
2874
- * of resolving it. This adapter catches that and reconstructs a `Response`-shaped object from
2875
- * the thrown error's `response`, so the core API function sees the real status and body instead
2876
- * of treating every non-2xx result as a network failure. A genuine network error (no response
2877
- * attached) still propagates.
2878
- *
2879
- * @param instanceId - Which `FetchHttpClient` instance to use, for multi-instance apps. Defaults to `0`.
2880
- * @returns A function matching the `(url, config) => Promise<Response>` shape core API functions expect.
2881
- * @example
2882
- * ```typescript
2883
- * await updateMeCredentials({
2884
- * ...config,
2885
- * fetcher: fetcher ?? createHttpClientFetcher(instanceId),
2886
- * });
2887
- * ```
2888
- */
2889
- const createHttpClientFetcher = (instanceId = 0) => {
2890
- return async (url, config) => {
2891
- const httpClient = FetchHttpClient_default.getInstance(instanceId);
2892
- const toResponse = (data, status, statusText) => ({
2893
- json: () => Promise.resolve(data),
2894
- ok: status >= 200 && status < 300,
2895
- status,
2896
- statusText,
2897
- text: () => Promise.resolve(typeof data === "string" ? data : JSON.stringify(data))
2898
- });
2899
- try {
2900
- const response = await httpClient.request({
2901
- data: config.body ? JSON.parse(config.body) : void 0,
2902
- headers: config.headers,
2903
- method: config.method || "POST",
2904
- signal: config.signal,
2905
- url
2906
- });
2907
- return toResponse(response.data, response.status, response.statusText || "");
2908
- } catch (error) {
2909
- const httpError = error;
2910
- if (httpError?.response) return toResponse(httpError.response.data, httpError.response.status, httpError.response.statusText ?? "");
2911
- throw error;
2912
- }
2913
- };
2914
- };
2915
- var createHttpClientFetcher_default = createHttpClientFetcher;
2916
-
2917
2949
  //#endregion
2918
2950
  //#region src/utils/copyTextToClipboard.ts
2919
2951
  /**
@@ -3507,6 +3539,23 @@ const AVATAR_PALETTES = [
3507
3539
  * without needing the actual palette values.
3508
3540
  */
3509
3541
  const AVATAR_GRADIENT_COUNT = AVATAR_PALETTES.length;
3542
+ const XML_ESCAPES = {
3543
+ "\"": "&quot;",
3544
+ "&": "&amp;",
3545
+ "'": "&apos;",
3546
+ "<": "&lt;",
3547
+ ">": "&gt;"
3548
+ };
3549
+ /**
3550
+ * Escapes a caller-supplied value for interpolation into SVG text.
3551
+ */
3552
+ const escapeXml = (value) => value.replace(/["&'<>]/g, (ch) => XML_ESCAPES[ch]);
3553
+ /**
3554
+ * Encodes an SVG document as a data URI. Lone UTF-16 surrogates are replaced with U+FFFD
3555
+ * first, since `encodeURIComponent` throws a `URIError` on them. `Array.from` iterates by
3556
+ * code point, so a surrogate it yields on its own is unpaired.
3557
+ */
3558
+ const toSvgDataUri = (svg) => `data:image/svg+xml,${encodeURIComponent(Array.from(svg, (ch) => ch.length === 1 && ch >= "\ud800" && ch <= "\udfff" ? "�" : ch).join(""))}`;
3510
3559
  /**
3511
3560
  * Fills in `params.content` when the spec itself didn't carry one, deriving it from a raw
3512
3561
  * seed name (e.g. an app or user display name) — extracting initials for letter variants,
@@ -3557,11 +3606,10 @@ const generateGradientAvatar = ({ shape, variant, content, colors, bg }) => {
3557
3606
  let text = "";
3558
3607
  if (variant !== "blank") {
3559
3608
  const letterCount = variant === "one_letter" ? 1 : 2;
3560
- const initials = (content || "A").toUpperCase().slice(0, letterCount);
3609
+ const initials = escapeXml(Array.from((content || "A").toUpperCase()).slice(0, letterCount).join(""));
3561
3610
  text = `<text x="50" y="64" font-family="Helvetica,Arial,sans-serif" font-weight="700" font-size="${letterCount === 1 ? 46 : 34}" text-anchor="middle" fill="#fff">${initials}</text>`;
3562
3611
  }
3563
- const svg = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">${defs}${shapeMarkup(shape, background)}${text}</svg>`;
3564
- return `data:image/svg+xml,${encodeURIComponent(svg)}`;
3612
+ return toSvgDataUri(`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">${defs}${shapeMarkup(shape, background)}${text}</svg>`);
3565
3613
  };
3566
3614
  /**
3567
3615
  * Renders one of the curated anonymous-animal icons, applying the requested background
@@ -3573,8 +3621,7 @@ const generateAnonymousAnimalAvatar = ({ shape, content, bg }) => {
3573
3621
  const icon = ANONYMOUS_ANIMAL_ICONS[Object.prototype.hasOwnProperty.call(ANONYMOUS_ANIMAL_ICONS, content) ? content : pickAnonymousAvatarName_default()];
3574
3622
  const color = bg ?? icon.color;
3575
3623
  const markup = icon.markup.replaceAll("%COLOR%", color);
3576
- const svg = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">${shapeMarkup(shape, color)}${markup}</svg>`;
3577
- return `data:image/svg+xml,${encodeURIComponent(svg)}`;
3624
+ return toSvgDataUri(`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">${shapeMarkup(shape, color)}${markup}</svg>`);
3578
3625
  };
3579
3626
  /**
3580
3627
  * Renders one of the curated anonymous-entity icons (application/organization/resource-server
@@ -3586,8 +3633,7 @@ const generateAnonymousEntityAvatar = ({ shape, content, bg }) => {
3586
3633
  const icon = ANONYMOUS_ENTITY_ICONS[Object.prototype.hasOwnProperty.call(ANONYMOUS_ENTITY_ICONS, content) ? content : pickAnonymousEntityName_default()];
3587
3634
  const color = bg ?? icon.color;
3588
3635
  const markup = icon.markup.replaceAll("%COLOR%", color);
3589
- const svg = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">${shapeMarkup(shape, color)}${markup}</svg>`;
3590
- return `data:image/svg+xml,${encodeURIComponent(svg)}`;
3636
+ return toSvgDataUri(`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">${shapeMarkup(shape, color)}${markup}</svg>`);
3591
3637
  };
3592
3638
  /**
3593
3639
  * Generates an avatar image as an `svg+xml` data URI from a fully-resolved {@link AvatarParams}.
@@ -3704,7 +3750,8 @@ const createClassObserver = (targetElement, callback, config = {}) => {
3704
3750
  return observer;
3705
3751
  };
3706
3752
  /**
3707
- * Creates a media query listener for system theme changes
3753
+ * Creates a media query listener for system theme changes. Call `disconnect()` on the returned
3754
+ * object to remove the listener.
3708
3755
  */
3709
3756
  const createMediaQueryListener = (callback) => {
3710
3757
  if (typeof window === "undefined" || !window.matchMedia) return null;
@@ -3714,7 +3761,10 @@ const createMediaQueryListener = (callback) => {
3714
3761
  };
3715
3762
  if (mediaQuery.addEventListener) mediaQuery.addEventListener("change", handleChange);
3716
3763
  else mediaQuery.addListener(handleChange);
3717
- return mediaQuery;
3764
+ return Object.assign(mediaQuery, { disconnect: () => {
3765
+ if (mediaQuery.removeEventListener) mediaQuery.removeEventListener("change", handleChange);
3766
+ else mediaQuery.removeListener(handleChange);
3767
+ } });
3718
3768
  };
3719
3769
 
3720
3770
  //#endregion