@gz-fronted/gz-pc 0.1.1-beta.1 → 0.1.1-beta.2

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/README.md CHANGED
@@ -185,6 +185,7 @@ const result = await gzFetch<SaveResult, SaveParams>({
185
185
  | `showErrorMessage` | `boolean` | `true` | 实例级错误提示开关 |
186
186
  | `auth.headerName` | `string` | `Authorization` | Token 请求头名称 |
187
187
  | `auth.formatToken` | `(token: string) => string` | `Bearer ${token}` | Token 格式化函数 |
188
+ | `unauthorized` | `GzFetchUnauthorizedOptions` | 未开启 | 可选的 HTTP 401 统一处理 |
188
189
  | `middlewares` | `readonly GzFetchMiddleware[]` | `[]` | 请求、响应和错误中间件 |
189
190
 
190
191
  需要覆盖默认成功状态时,只在应用初始化层配置:
@@ -284,6 +285,72 @@ controller.abort();
284
285
  await promise;
285
286
  ```
286
287
 
288
+ ## HTTP 401 统一处理
289
+
290
+ 401 统一处理默认关闭,未显式开启时继续执行原有 HTTP 错误提示和抛错逻辑,不弹窗、
291
+ 不跳转。需要启用时,在应用初始化配置中传入 `unauthorized.enabled: true`:
292
+
293
+ ```ts
294
+ configureGzFetch({
295
+ baseURL: '/api',
296
+ getToken: () => runtimeToken,
297
+ unauthorized: {
298
+ enabled: true,
299
+ loginUrl: '/login',
300
+ modalTitle: '登录失效',
301
+ modalMessage: '当前登录状态已失效,请重新登录。',
302
+ },
303
+ });
304
+ ```
305
+
306
+ 配置默认值如下:
307
+
308
+ | 字段 | 类型 | 默认值 |
309
+ | ---------------- | ------------ | --------------------------------------- |
310
+ | `enabled` | `boolean` | `false` |
311
+ | `loginUrl` | `string` | `/login` |
312
+ | `modalTitle` | `string` | `登录失效` |
313
+ | `modalMessage` | `string` | `当前登录状态已失效,请重新登录。` |
314
+ | `onUnauthorized` | `() => void` | 无;确认后使用 `window.location.assign` |
315
+
316
+ 为了让弹窗继承宿主项目当前的 gg-ui 主题,需要在应用根部的 `ConfigProvider` 内挂载一次
317
+ `GzFetchUnauthorizedModal`:
318
+
319
+ ```tsx
320
+ import { GzFetchUnauthorizedModal } from '@gz-fronted/gz-pc/fetch';
321
+ import { ConfigProvider } from '@chenhui996/gg-ui';
322
+
323
+ <ConfigProvider themeMode={themeMode}>
324
+ <GzFetchUnauthorizedModal />
325
+ <App />
326
+ </ConfigProvider>;
327
+ ```
328
+
329
+ 该组件使用受控 gg-ui `Modal`,不会调用脱离 React 上下文的静态
330
+ `Modal.confirm`,因此主题切换时会继续读取宿主 `ConfigProvider` 的主题 Token,
331
+ 无需复制亮色或暗色样式。业务页面不需要维护 401 状态或重复渲染弹窗。
332
+
333
+ 所有由同一份 gz-pc 包创建的默认客户端和独立客户端共享一个 401 管理器。首个 401
334
+ 打开弹窗,后续并发 401 不会创建新弹窗;取消或弹窗关闭动画结束后会释放状态,后续
335
+ 独立发生的 401 可以再次打开。确认操作也有同一状态锁,不会重复执行登录跳转。
336
+
337
+ 业务项目需要自定义 SSO、登出或状态清理时传入 `onUnauthorized`。用户确认后优先执行
338
+ 该回调;只有未提供回调时才通过 `window.location.assign(loginUrl)` 跳转:
339
+
340
+ ```ts
341
+ configureGzFetch({
342
+ unauthorized: {
343
+ enabled: true,
344
+ onUnauthorized: () => {
345
+ authService.logout();
346
+ },
347
+ },
348
+ });
349
+ ```
350
+
351
+ 开启该能力后,401 仍会作为 `GzFetchError` 抛给调用方,但请求核心不会再为同一个
352
+ 401 额外调用普通 `message.error`。其他 HTTP 错误、网络错误、超时和取消请求行为不变。
353
+
287
354
  ## 中间件
288
355
 
289
356
  中间件的 `onRequest`、`onResponse` 和 `onError` 都按注册顺序执行,不采用洋葱
@@ -2,6 +2,8 @@
2
2
 
3
3
  var ggUi = require('@chenhui996/gg-ui');
4
4
  var axios = require('axios');
5
+ var react = require('react');
6
+ var jsxRuntime = require('react/jsx-runtime');
5
7
 
6
8
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
7
9
 
@@ -96,6 +98,103 @@ async function runErrorMiddlewares(error, context, middlewares) {
96
98
  }
97
99
  }
98
100
 
101
+ // src/fetch/unauthorized.ts
102
+ var DEFAULT_UNAUTHORIZED_LOGIN_URL = "/login";
103
+ var DEFAULT_UNAUTHORIZED_MODAL_TITLE = "登录失效";
104
+ var DEFAULT_UNAUTHORIZED_MODAL_MESSAGE = "当前登录状态已失效,请重新登录。";
105
+ var IDLE_SNAPSHOT = { phase: "idle" };
106
+ var snapshot = IDLE_SNAPSHOT;
107
+ var listeners = /* @__PURE__ */ new Set();
108
+ var modalHosts = /* @__PURE__ */ new Set();
109
+ var emitChange = () => {
110
+ for (const listener of listeners) {
111
+ listener();
112
+ }
113
+ };
114
+ var setSnapshot = (nextSnapshot) => {
115
+ snapshot = nextSnapshot;
116
+ emitChange();
117
+ };
118
+ var resolveUnauthorizedOptions = (options) => {
119
+ var _a, _b, _c, _d;
120
+ return {
121
+ enabled: (_a = options == null ? void 0 : options.enabled) != null ? _a : false,
122
+ loginUrl: (_b = options == null ? void 0 : options.loginUrl) != null ? _b : DEFAULT_UNAUTHORIZED_LOGIN_URL,
123
+ modalTitle: (_c = options == null ? void 0 : options.modalTitle) != null ? _c : DEFAULT_UNAUTHORIZED_MODAL_TITLE,
124
+ modalMessage: (_d = options == null ? void 0 : options.modalMessage) != null ? _d : DEFAULT_UNAUTHORIZED_MODAL_MESSAGE,
125
+ ...(options == null ? void 0 : options.onUnauthorized) === void 0 ? {} : { onUnauthorized: options.onUnauthorized }
126
+ };
127
+ };
128
+ var getUnauthorizedSnapshot = () => snapshot;
129
+ var getUnauthorizedServerSnapshot = () => IDLE_SNAPSHOT;
130
+ var subscribeUnauthorized = (listener) => {
131
+ listeners.add(listener);
132
+ return () => listeners.delete(listener);
133
+ };
134
+ var registerUnauthorizedModalHost = (hostId) => {
135
+ modalHosts.add(hostId);
136
+ if (snapshot.activeHostId === void 0) {
137
+ setSnapshot({ ...snapshot, activeHostId: hostId });
138
+ }
139
+ return () => {
140
+ modalHosts.delete(hostId);
141
+ if (snapshot.activeHostId !== hostId) {
142
+ return;
143
+ }
144
+ const nextHost = modalHosts.values().next();
145
+ const nextHostId = nextHost.done ? void 0 : nextHost.value;
146
+ if (snapshot.phase === "closing") {
147
+ setSnapshot(
148
+ nextHostId === void 0 ? IDLE_SNAPSHOT : { activeHostId: nextHostId, phase: "idle" }
149
+ );
150
+ return;
151
+ }
152
+ if (nextHostId === void 0) {
153
+ setSnapshot(
154
+ snapshot.options === void 0 ? { phase: snapshot.phase } : { options: snapshot.options, phase: snapshot.phase }
155
+ );
156
+ return;
157
+ }
158
+ setSnapshot({ ...snapshot, activeHostId: nextHostId });
159
+ };
160
+ };
161
+ var openUnauthorizedModal = (options) => {
162
+ if (!options.enabled || snapshot.phase !== "idle") {
163
+ return false;
164
+ }
165
+ setSnapshot({
166
+ ...snapshot.activeHostId === void 0 ? {} : { activeHostId: snapshot.activeHostId },
167
+ options,
168
+ phase: "open"
169
+ });
170
+ return true;
171
+ };
172
+ var beginCloseUnauthorizedModal = () => {
173
+ if (snapshot.phase !== "open" || snapshot.options === void 0) {
174
+ return void 0;
175
+ }
176
+ const { options } = snapshot;
177
+ setSnapshot({ ...snapshot, phase: "closing" });
178
+ return options;
179
+ };
180
+ var cancelUnauthorizedModal = () => beginCloseUnauthorizedModal() !== void 0;
181
+ var confirmUnauthorizedModal = () => beginCloseUnauthorizedModal();
182
+ var completeUnauthorizedModal = () => {
183
+ if (snapshot.phase !== "closing") {
184
+ return;
185
+ }
186
+ setSnapshot(
187
+ snapshot.activeHostId === void 0 ? IDLE_SNAPSHOT : { activeHostId: snapshot.activeHostId, phase: "idle" }
188
+ );
189
+ };
190
+ var executeUnauthorizedAction = (options, navigate = (loginUrl) => window.location.assign(loginUrl)) => {
191
+ if (options.onUnauthorized) {
192
+ options.onUnauthorized();
193
+ return;
194
+ }
195
+ navigate(options.loginUrl);
196
+ };
197
+
99
198
  // src/fetch/client.ts
100
199
  var DEFAULT_TIMEOUT = 15e3;
101
200
  var DEFAULT_AUTH_HEADER = "Authorization";
@@ -115,6 +214,7 @@ function createGzFetch(options = {}) {
115
214
  const showInstanceErrorMessage = (_b = options.showErrorMessage) != null ? _b : true;
116
215
  const headerName = (_d = (_c = options.auth) == null ? void 0 : _c.headerName) != null ? _d : DEFAULT_AUTH_HEADER;
117
216
  const formatToken = (_f = (_e = options.auth) == null ? void 0 : _e.formatToken) != null ? _f : ((token) => `Bearer ${token}`);
217
+ const unauthorizedOptions = resolveUnauthorizedOptions(options.unauthorized);
118
218
  const instance = axios__default.default.create({
119
219
  ...options.baseURL === void 0 ? {} : { baseURL: options.baseURL },
120
220
  timeout: (_g = options.timeout) != null ? _g : DEFAULT_TIMEOUT,
@@ -156,9 +256,13 @@ function createGzFetch(options = {}) {
156
256
  return await runResponseMiddlewares(response.data, context, middlewares);
157
257
  } catch (originalError) {
158
258
  const error = toGzFetchError(originalError);
259
+ const shouldHandleUnauthorized = unauthorizedOptions.enabled && error.type === "HTTP_ERROR" && error.status === 401;
260
+ if (shouldHandleUnauthorized) {
261
+ openUnauthorizedModal(unauthorizedOptions);
262
+ }
159
263
  await runErrorMiddlewares(error, { config: currentConfig }, middlewares);
160
264
  const shouldShowError = (_a2 = currentConfig.showErrorMessage) != null ? _a2 : showInstanceErrorMessage;
161
- if (shouldShowError && error.type !== "CANCELED_ERROR") {
265
+ if (shouldShowError && error.type !== "CANCELED_ERROR" && !shouldHandleUnauthorized) {
162
266
  ggUi.message.error(error.message);
163
267
  }
164
268
  throw error;
@@ -194,8 +298,46 @@ async function gzFetch(config) {
194
298
  }
195
299
  return defaultClient(config);
196
300
  }
301
+ var GzFetchUnauthorizedModal = () => {
302
+ var _a, _b, _c;
303
+ const hostIdRef = react.useRef(void 0);
304
+ (_a = hostIdRef.current) != null ? _a : hostIdRef.current = /* @__PURE__ */ Symbol("gz-fetch-unauthorized-modal-host");
305
+ const hostId = hostIdRef.current;
306
+ const snapshot2 = react.useSyncExternalStore(
307
+ subscribeUnauthorized,
308
+ getUnauthorizedSnapshot,
309
+ getUnauthorizedServerSnapshot
310
+ );
311
+ react.useEffect(() => registerUnauthorizedModalHost(hostId), [hostId]);
312
+ if (snapshot2.activeHostId !== hostId) {
313
+ return null;
314
+ }
315
+ const handleConfirm = () => {
316
+ const options = confirmUnauthorizedModal();
317
+ if (options) {
318
+ executeUnauthorizedAction(options);
319
+ }
320
+ };
321
+ return /* @__PURE__ */ jsxRuntime.jsx(
322
+ ggUi.Modal,
323
+ {
324
+ afterClose: completeUnauthorizedModal,
325
+ cancelText: "取消",
326
+ closable: true,
327
+ okText: "重新登录",
328
+ open: snapshot2.phase === "open",
329
+ title: (_b = snapshot2.options) == null ? void 0 : _b.modalTitle,
330
+ onCancel: cancelUnauthorizedModal,
331
+ onOk: handleConfirm,
332
+ children: (_c = snapshot2.options) == null ? void 0 : _c.modalMessage
333
+ }
334
+ );
335
+ };
336
+ GzFetchUnauthorizedModal.displayName = "GzFetchUnauthorizedModal";
337
+ var GzFetchUnauthorizedModal_default = GzFetchUnauthorizedModal;
197
338
 
198
339
  exports.GzFetchError = GzFetchError;
340
+ exports.GzFetchUnauthorizedModal = GzFetchUnauthorizedModal_default;
199
341
  exports.configureGzFetch = configureGzFetch;
200
342
  exports.createGzFetch = createGzFetch;
201
343
  exports.getResponseMessage = getResponseMessage;
@@ -1,4 +1,5 @@
1
1
  import { AxiosHeaders } from 'axios';
2
+ import { FC } from 'react';
2
3
 
3
4
  type GzFetchErrorType = 'HTTP_ERROR' | 'NETWORK_ERROR' | 'TIMEOUT_ERROR' | 'CANCELED_ERROR' | 'UNKNOWN_ERROR';
4
5
  interface GzFetchErrorOptions {
@@ -54,6 +55,13 @@ interface GzFetchAuthOptions {
54
55
  headerName?: string;
55
56
  formatToken?: (token: string) => string;
56
57
  }
58
+ interface GzFetchUnauthorizedOptions {
59
+ enabled?: boolean;
60
+ loginUrl?: string;
61
+ modalTitle?: string;
62
+ modalMessage?: string;
63
+ onUnauthorized?: () => void;
64
+ }
57
65
  interface CreateGzFetchOptions {
58
66
  baseURL?: string;
59
67
  timeout?: number;
@@ -61,6 +69,7 @@ interface CreateGzFetchOptions {
61
69
  getToken?: () => string | undefined | Promise<string | undefined>;
62
70
  showErrorMessage?: boolean;
63
71
  auth?: GzFetchAuthOptions;
72
+ unauthorized?: GzFetchUnauthorizedOptions;
64
73
  middlewares?: readonly GzFetchMiddleware[];
65
74
  }
66
75
  interface GzFetchClient {
@@ -77,4 +86,6 @@ declare function createGzFetch(options?: CreateGzFetchOptions): GzFetchClient;
77
86
  declare function configureGzFetch(options: CreateGzFetchOptions): void;
78
87
  declare function gzFetch<TResponse, TRequestParams = unknown>(config: GzRequestConfig<TRequestParams>): Promise<TResponse>;
79
88
 
80
- export { type CreateGzFetchOptions, type GzErrorContext, type GzFetch, type GzFetchAuthOptions, type GzFetchClient, GzFetchError, type GzFetchErrorOptions, type GzFetchErrorType, type GzFetchMiddleware, type GzInternalRequestConfig, type GzRequestConfig, type GzRequestHeaders, type GzRequestMethod, type GzRequestOptions, type GzRequestParams, type GzResponseContext, type GzResponseType, configureGzFetch, createGzFetch, getResponseMessage, gzFetch, toGzFetchError };
89
+ declare const GzFetchUnauthorizedModal: FC;
90
+
91
+ export { type CreateGzFetchOptions, type GzErrorContext, type GzFetch, type GzFetchAuthOptions, type GzFetchClient, GzFetchError, type GzFetchErrorOptions, type GzFetchErrorType, type GzFetchMiddleware, GzFetchUnauthorizedModal, type GzFetchUnauthorizedOptions, type GzInternalRequestConfig, type GzRequestConfig, type GzRequestHeaders, type GzRequestMethod, type GzRequestOptions, type GzRequestParams, type GzResponseContext, type GzResponseType, configureGzFetch, createGzFetch, getResponseMessage, gzFetch, toGzFetchError };
@@ -1,4 +1,5 @@
1
1
  import { AxiosHeaders } from 'axios';
2
+ import { FC } from 'react';
2
3
 
3
4
  type GzFetchErrorType = 'HTTP_ERROR' | 'NETWORK_ERROR' | 'TIMEOUT_ERROR' | 'CANCELED_ERROR' | 'UNKNOWN_ERROR';
4
5
  interface GzFetchErrorOptions {
@@ -54,6 +55,13 @@ interface GzFetchAuthOptions {
54
55
  headerName?: string;
55
56
  formatToken?: (token: string) => string;
56
57
  }
58
+ interface GzFetchUnauthorizedOptions {
59
+ enabled?: boolean;
60
+ loginUrl?: string;
61
+ modalTitle?: string;
62
+ modalMessage?: string;
63
+ onUnauthorized?: () => void;
64
+ }
57
65
  interface CreateGzFetchOptions {
58
66
  baseURL?: string;
59
67
  timeout?: number;
@@ -61,6 +69,7 @@ interface CreateGzFetchOptions {
61
69
  getToken?: () => string | undefined | Promise<string | undefined>;
62
70
  showErrorMessage?: boolean;
63
71
  auth?: GzFetchAuthOptions;
72
+ unauthorized?: GzFetchUnauthorizedOptions;
64
73
  middlewares?: readonly GzFetchMiddleware[];
65
74
  }
66
75
  interface GzFetchClient {
@@ -77,4 +86,6 @@ declare function createGzFetch(options?: CreateGzFetchOptions): GzFetchClient;
77
86
  declare function configureGzFetch(options: CreateGzFetchOptions): void;
78
87
  declare function gzFetch<TResponse, TRequestParams = unknown>(config: GzRequestConfig<TRequestParams>): Promise<TResponse>;
79
88
 
80
- export { type CreateGzFetchOptions, type GzErrorContext, type GzFetch, type GzFetchAuthOptions, type GzFetchClient, GzFetchError, type GzFetchErrorOptions, type GzFetchErrorType, type GzFetchMiddleware, type GzInternalRequestConfig, type GzRequestConfig, type GzRequestHeaders, type GzRequestMethod, type GzRequestOptions, type GzRequestParams, type GzResponseContext, type GzResponseType, configureGzFetch, createGzFetch, getResponseMessage, gzFetch, toGzFetchError };
89
+ declare const GzFetchUnauthorizedModal: FC;
90
+
91
+ export { type CreateGzFetchOptions, type GzErrorContext, type GzFetch, type GzFetchAuthOptions, type GzFetchClient, GzFetchError, type GzFetchErrorOptions, type GzFetchErrorType, type GzFetchMiddleware, GzFetchUnauthorizedModal, type GzFetchUnauthorizedOptions, type GzInternalRequestConfig, type GzRequestConfig, type GzRequestHeaders, type GzRequestMethod, type GzRequestOptions, type GzRequestParams, type GzResponseContext, type GzResponseType, configureGzFetch, createGzFetch, getResponseMessage, gzFetch, toGzFetchError };
@@ -1,5 +1,7 @@
1
- import { message } from '@chenhui996/gg-ui';
1
+ import { Modal, message } from '@chenhui996/gg-ui';
2
2
  import axios, { AxiosError, AxiosHeaders } from 'axios';
3
+ import { useRef, useSyncExternalStore, useEffect } from 'react';
4
+ import { jsx } from 'react/jsx-runtime';
3
5
 
4
6
  // src/fetch/client.ts
5
7
  var GzFetchError = class extends Error {
@@ -90,6 +92,103 @@ async function runErrorMiddlewares(error, context, middlewares) {
90
92
  }
91
93
  }
92
94
 
95
+ // src/fetch/unauthorized.ts
96
+ var DEFAULT_UNAUTHORIZED_LOGIN_URL = "/login";
97
+ var DEFAULT_UNAUTHORIZED_MODAL_TITLE = "登录失效";
98
+ var DEFAULT_UNAUTHORIZED_MODAL_MESSAGE = "当前登录状态已失效,请重新登录。";
99
+ var IDLE_SNAPSHOT = { phase: "idle" };
100
+ var snapshot = IDLE_SNAPSHOT;
101
+ var listeners = /* @__PURE__ */ new Set();
102
+ var modalHosts = /* @__PURE__ */ new Set();
103
+ var emitChange = () => {
104
+ for (const listener of listeners) {
105
+ listener();
106
+ }
107
+ };
108
+ var setSnapshot = (nextSnapshot) => {
109
+ snapshot = nextSnapshot;
110
+ emitChange();
111
+ };
112
+ var resolveUnauthorizedOptions = (options) => {
113
+ var _a, _b, _c, _d;
114
+ return {
115
+ enabled: (_a = options == null ? void 0 : options.enabled) != null ? _a : false,
116
+ loginUrl: (_b = options == null ? void 0 : options.loginUrl) != null ? _b : DEFAULT_UNAUTHORIZED_LOGIN_URL,
117
+ modalTitle: (_c = options == null ? void 0 : options.modalTitle) != null ? _c : DEFAULT_UNAUTHORIZED_MODAL_TITLE,
118
+ modalMessage: (_d = options == null ? void 0 : options.modalMessage) != null ? _d : DEFAULT_UNAUTHORIZED_MODAL_MESSAGE,
119
+ ...(options == null ? void 0 : options.onUnauthorized) === void 0 ? {} : { onUnauthorized: options.onUnauthorized }
120
+ };
121
+ };
122
+ var getUnauthorizedSnapshot = () => snapshot;
123
+ var getUnauthorizedServerSnapshot = () => IDLE_SNAPSHOT;
124
+ var subscribeUnauthorized = (listener) => {
125
+ listeners.add(listener);
126
+ return () => listeners.delete(listener);
127
+ };
128
+ var registerUnauthorizedModalHost = (hostId) => {
129
+ modalHosts.add(hostId);
130
+ if (snapshot.activeHostId === void 0) {
131
+ setSnapshot({ ...snapshot, activeHostId: hostId });
132
+ }
133
+ return () => {
134
+ modalHosts.delete(hostId);
135
+ if (snapshot.activeHostId !== hostId) {
136
+ return;
137
+ }
138
+ const nextHost = modalHosts.values().next();
139
+ const nextHostId = nextHost.done ? void 0 : nextHost.value;
140
+ if (snapshot.phase === "closing") {
141
+ setSnapshot(
142
+ nextHostId === void 0 ? IDLE_SNAPSHOT : { activeHostId: nextHostId, phase: "idle" }
143
+ );
144
+ return;
145
+ }
146
+ if (nextHostId === void 0) {
147
+ setSnapshot(
148
+ snapshot.options === void 0 ? { phase: snapshot.phase } : { options: snapshot.options, phase: snapshot.phase }
149
+ );
150
+ return;
151
+ }
152
+ setSnapshot({ ...snapshot, activeHostId: nextHostId });
153
+ };
154
+ };
155
+ var openUnauthorizedModal = (options) => {
156
+ if (!options.enabled || snapshot.phase !== "idle") {
157
+ return false;
158
+ }
159
+ setSnapshot({
160
+ ...snapshot.activeHostId === void 0 ? {} : { activeHostId: snapshot.activeHostId },
161
+ options,
162
+ phase: "open"
163
+ });
164
+ return true;
165
+ };
166
+ var beginCloseUnauthorizedModal = () => {
167
+ if (snapshot.phase !== "open" || snapshot.options === void 0) {
168
+ return void 0;
169
+ }
170
+ const { options } = snapshot;
171
+ setSnapshot({ ...snapshot, phase: "closing" });
172
+ return options;
173
+ };
174
+ var cancelUnauthorizedModal = () => beginCloseUnauthorizedModal() !== void 0;
175
+ var confirmUnauthorizedModal = () => beginCloseUnauthorizedModal();
176
+ var completeUnauthorizedModal = () => {
177
+ if (snapshot.phase !== "closing") {
178
+ return;
179
+ }
180
+ setSnapshot(
181
+ snapshot.activeHostId === void 0 ? IDLE_SNAPSHOT : { activeHostId: snapshot.activeHostId, phase: "idle" }
182
+ );
183
+ };
184
+ var executeUnauthorizedAction = (options, navigate = (loginUrl) => window.location.assign(loginUrl)) => {
185
+ if (options.onUnauthorized) {
186
+ options.onUnauthorized();
187
+ return;
188
+ }
189
+ navigate(options.loginUrl);
190
+ };
191
+
93
192
  // src/fetch/client.ts
94
193
  var DEFAULT_TIMEOUT = 15e3;
95
194
  var DEFAULT_AUTH_HEADER = "Authorization";
@@ -109,6 +208,7 @@ function createGzFetch(options = {}) {
109
208
  const showInstanceErrorMessage = (_b = options.showErrorMessage) != null ? _b : true;
110
209
  const headerName = (_d = (_c = options.auth) == null ? void 0 : _c.headerName) != null ? _d : DEFAULT_AUTH_HEADER;
111
210
  const formatToken = (_f = (_e = options.auth) == null ? void 0 : _e.formatToken) != null ? _f : ((token) => `Bearer ${token}`);
211
+ const unauthorizedOptions = resolveUnauthorizedOptions(options.unauthorized);
112
212
  const instance = axios.create({
113
213
  ...options.baseURL === void 0 ? {} : { baseURL: options.baseURL },
114
214
  timeout: (_g = options.timeout) != null ? _g : DEFAULT_TIMEOUT,
@@ -150,9 +250,13 @@ function createGzFetch(options = {}) {
150
250
  return await runResponseMiddlewares(response.data, context, middlewares);
151
251
  } catch (originalError) {
152
252
  const error = toGzFetchError(originalError);
253
+ const shouldHandleUnauthorized = unauthorizedOptions.enabled && error.type === "HTTP_ERROR" && error.status === 401;
254
+ if (shouldHandleUnauthorized) {
255
+ openUnauthorizedModal(unauthorizedOptions);
256
+ }
153
257
  await runErrorMiddlewares(error, { config: currentConfig }, middlewares);
154
258
  const shouldShowError = (_a2 = currentConfig.showErrorMessage) != null ? _a2 : showInstanceErrorMessage;
155
- if (shouldShowError && error.type !== "CANCELED_ERROR") {
259
+ if (shouldShowError && error.type !== "CANCELED_ERROR" && !shouldHandleUnauthorized) {
156
260
  message.error(error.message);
157
261
  }
158
262
  throw error;
@@ -188,5 +292,42 @@ async function gzFetch(config) {
188
292
  }
189
293
  return defaultClient(config);
190
294
  }
295
+ var GzFetchUnauthorizedModal = () => {
296
+ var _a, _b, _c;
297
+ const hostIdRef = useRef(void 0);
298
+ (_a = hostIdRef.current) != null ? _a : hostIdRef.current = /* @__PURE__ */ Symbol("gz-fetch-unauthorized-modal-host");
299
+ const hostId = hostIdRef.current;
300
+ const snapshot2 = useSyncExternalStore(
301
+ subscribeUnauthorized,
302
+ getUnauthorizedSnapshot,
303
+ getUnauthorizedServerSnapshot
304
+ );
305
+ useEffect(() => registerUnauthorizedModalHost(hostId), [hostId]);
306
+ if (snapshot2.activeHostId !== hostId) {
307
+ return null;
308
+ }
309
+ const handleConfirm = () => {
310
+ const options = confirmUnauthorizedModal();
311
+ if (options) {
312
+ executeUnauthorizedAction(options);
313
+ }
314
+ };
315
+ return /* @__PURE__ */ jsx(
316
+ Modal,
317
+ {
318
+ afterClose: completeUnauthorizedModal,
319
+ cancelText: "取消",
320
+ closable: true,
321
+ okText: "重新登录",
322
+ open: snapshot2.phase === "open",
323
+ title: (_b = snapshot2.options) == null ? void 0 : _b.modalTitle,
324
+ onCancel: cancelUnauthorizedModal,
325
+ onOk: handleConfirm,
326
+ children: (_c = snapshot2.options) == null ? void 0 : _c.modalMessage
327
+ }
328
+ );
329
+ };
330
+ GzFetchUnauthorizedModal.displayName = "GzFetchUnauthorizedModal";
331
+ var GzFetchUnauthorizedModal_default = GzFetchUnauthorizedModal;
191
332
 
192
- export { GzFetchError, configureGzFetch, createGzFetch, getResponseMessage, gzFetch, toGzFetchError };
333
+ export { GzFetchError, GzFetchUnauthorizedModal_default as GzFetchUnauthorizedModal, configureGzFetch, createGzFetch, getResponseMessage, gzFetch, toGzFetchError };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gz-fronted/gz-pc",
3
- "version": "0.1.1-beta.1",
3
+ "version": "0.1.1-beta.2",
4
4
  "description": "Shared fetch, hooks and utility primitives for GZ frontend applications.",
5
5
  "license": "MIT",
6
6
  "type": "module",