openxiangda 1.0.89 → 1.0.90

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.
@@ -827,6 +827,7 @@ __export(runtime_exports, {
827
827
  AuthClientError: () => AuthClientError,
828
828
  BuiltinRouteRenderer: () => BuiltinRouteRenderer,
829
829
  LoginPage: () => LoginPage,
830
+ OpenXiangdaPageProvider: () => OpenXiangdaPageProvider,
830
831
  OpenXiangdaProvider: () => OpenXiangdaProvider,
831
832
  PageProvider: () => PageProvider,
832
833
  PermissionBoundary: () => PermissionBoundary,
@@ -3622,1487 +3623,1610 @@ var usePageRoute = () => {
3622
3623
  init_cjs_shims();
3623
3624
  var import_react8 = require("react");
3624
3625
 
3625
- // packages/sdk/src/runtime/react/auth.tsx
3626
+ // packages/sdk/src/runtime/host/browserHost.ts
3626
3627
  init_cjs_shims();
3627
- var import_react7 = require("react");
3628
- var import_antd2 = require("antd");
3629
- var import_icons = require("@ant-design/icons");
3630
- var import_jsx_runtime3 = require("react/jsx-runtime");
3631
- var useAuth = (options = {}) => {
3632
- const runtime = useOpenXiangda();
3633
- const client = (0, import_react7.useMemo)(
3634
- () => createAuthClient({
3635
- appType: options.appType || runtime.appType,
3636
- servicePrefix: options.servicePrefix || runtime.servicePrefix,
3637
- fetchImpl: options.fetchImpl || runtime.fetchImpl
3638
- }),
3639
- [
3640
- options.appType,
3641
- options.fetchImpl,
3642
- options.servicePrefix,
3643
- runtime.appType,
3644
- runtime.fetchImpl,
3645
- runtime.servicePrefix
3646
- ]
3647
- );
3648
- return (0, import_react7.useMemo)(
3649
- () => ({
3650
- client,
3651
- getMethods: client.getMethods,
3652
- passwordLogin: client.passwordLogin,
3653
- dingtalkLogin: client.dingtalkLogin,
3654
- guestLogin: client.guestLogin,
3655
- sendPhoneCode: client.sendPhoneCode,
3656
- phoneCodeLogin: client.phoneCodeLogin,
3657
- registerWithPhoneCode: client.registerWithPhoneCode,
3658
- getSsoLoginUrl: client.getSsoLoginUrl,
3659
- refresh: client.refresh,
3660
- logout: client.logout,
3661
- resolveLoginUrl: client.resolveLoginUrl
3662
- }),
3663
- [client]
3664
- );
3628
+ var NAMESPACE_ROOT_CLASS2 = "sy-app-workspace";
3629
+ var defaultModuleLoader = (url2) => import(
3630
+ /* @vite-ignore */
3631
+ url2
3632
+ );
3633
+ var trimTrailingSlash = (value) => value.replace(/\/+$/, "");
3634
+ var getDefaultServicePrefix = () => typeof window !== "undefined" ? trimTrailingSlash(window.__OPENXIANGDA_SERVICE_PREFIX__ || "/service") : "/service";
3635
+ var joinServicePath = (servicePrefix, path) => {
3636
+ if (/^https?:\/\//i.test(path)) return path;
3637
+ const normalizedPrefix = trimTrailingSlash(servicePrefix || "/service");
3638
+ if (path.startsWith(normalizedPrefix)) return path;
3639
+ return `${normalizedPrefix}${path.startsWith("/") ? path : `/${path}`}`;
3665
3640
  };
3666
- var useLoginMethods = (options = {}) => {
3667
- const auth = useAuth(options);
3668
- const [state, setState] = (0, import_react7.useState)({
3669
- data: null,
3670
- loading: true,
3671
- error: null
3672
- });
3673
- const reload = (0, import_react7.useCallback)(async () => {
3674
- setState((prev) => ({ ...prev, loading: true, error: null }));
3675
- try {
3676
- const data = await auth.getMethods();
3677
- setState({ data, loading: false, error: null });
3678
- } catch (error) {
3679
- setState({
3680
- data: null,
3681
- loading: false,
3682
- error: normalizeError(error)
3683
- });
3641
+ var appendQuery = (url2, query) => {
3642
+ if (!query) return url2;
3643
+ return `${url2}${url2.includes("?") ? "&" : "?"}${query}`;
3644
+ };
3645
+ var normalizeMethod2 = (method4) => {
3646
+ const value = String(method4 || "get").toUpperCase();
3647
+ return ["GET", "POST", "PUT", "DELETE", "PATCH"].includes(value) ? value : "GET";
3648
+ };
3649
+ var normalizeCssIsolation2 = (value) => {
3650
+ if (value === "namespace" || value === "shadow" || value === "none") {
3651
+ return value;
3652
+ }
3653
+ return "none";
3654
+ };
3655
+ var normalizeEnvelopeCode2 = (value, fallback) => {
3656
+ if (value === void 0 || value === null || value === "") return fallback;
3657
+ const normalized = Number(value);
3658
+ return Number.isFinite(normalized) ? normalized : String(value);
3659
+ };
3660
+ var isSuccessCode4 = (value) => {
3661
+ if (value === void 0 || value === null || value === "") return true;
3662
+ const normalized = Number(value);
3663
+ return Number.isFinite(normalized) ? normalized === 0 || normalized >= 200 && normalized < 300 : false;
3664
+ };
3665
+ var parseJsonResponse = async (response) => {
3666
+ const payload = await response.json().catch(() => null);
3667
+ if (!response.ok) {
3668
+ const message7 = payload && typeof payload === "object" ? payload.message || payload.error || response.statusText : response.statusText;
3669
+ throw new Error(message7 || "\u8BF7\u6C42\u5931\u8D25");
3670
+ }
3671
+ if (payload && typeof payload === "object" && "code" in payload) {
3672
+ const code = normalizeEnvelopeCode2(payload.code, response.status);
3673
+ return {
3674
+ code,
3675
+ success: payload.success !== false && isSuccessCode4(code),
3676
+ message: payload.message,
3677
+ result: payload.result ?? payload.data ?? null,
3678
+ data: payload.data,
3679
+ raw: payload
3680
+ };
3681
+ }
3682
+ return {
3683
+ code: response.status,
3684
+ success: response.ok,
3685
+ message: response.statusText,
3686
+ result: payload,
3687
+ data: payload,
3688
+ raw: payload
3689
+ };
3690
+ };
3691
+ var createBrowserPageBridge = (options = {}) => {
3692
+ const servicePrefix = options.servicePrefix || getDefaultServicePrefix();
3693
+ const fetchImpl = createBoundFetch(options.fetchImpl);
3694
+ const request = async (payload) => {
3695
+ if (!payload?.path) {
3696
+ throw new Error("transport.request \u9700\u8981 path");
3684
3697
  }
3685
- }, [auth]);
3686
- (0, import_react7.useEffect)(() => {
3687
- let disposed = false;
3688
- const run = async () => {
3689
- setState((prev) => ({ ...prev, loading: true, error: null }));
3690
- try {
3691
- const data = await auth.getMethods();
3692
- if (!disposed) setState({ data, loading: false, error: null });
3693
- } catch (error) {
3694
- if (!disposed) {
3695
- setState({
3696
- data: null,
3697
- loading: false,
3698
- error: normalizeError(error)
3699
- });
3700
- }
3698
+ const url2 = appendQuery(joinServicePath(servicePrefix, payload.path), payload.query);
3699
+ const headers = new Headers(payload.headers);
3700
+ let body;
3701
+ if (payload.body !== void 0) {
3702
+ if (payload.body instanceof FormData) {
3703
+ body = payload.body;
3704
+ } else {
3705
+ headers.set("Content-Type", headers.get("Content-Type") || "application/json");
3706
+ body = JSON.stringify(payload.body);
3701
3707
  }
3708
+ }
3709
+ const response = await fetchImpl(url2, {
3710
+ method: normalizeMethod2(payload.method),
3711
+ headers,
3712
+ body,
3713
+ credentials: "include"
3714
+ });
3715
+ return parseJsonResponse(response);
3716
+ };
3717
+ const download = async (payload) => {
3718
+ if (!payload?.path) {
3719
+ throw new Error("transport.download \u9700\u8981 path");
3720
+ }
3721
+ const url2 = appendQuery(joinServicePath(servicePrefix, payload.path), payload.query);
3722
+ const headers = new Headers(payload.headers);
3723
+ let body;
3724
+ if (payload.body !== void 0) {
3725
+ if (payload.body instanceof FormData) {
3726
+ body = payload.body;
3727
+ } else {
3728
+ headers.set("Content-Type", headers.get("Content-Type") || "application/json");
3729
+ body = JSON.stringify(payload.body);
3730
+ }
3731
+ }
3732
+ const response = await fetchImpl(url2, {
3733
+ method: normalizeMethod2(payload.method),
3734
+ headers,
3735
+ body,
3736
+ credentials: "include"
3737
+ });
3738
+ if (!response.ok) {
3739
+ throw new Error(response.statusText || "\u4E0B\u8F7D\u5931\u8D25");
3740
+ }
3741
+ return {
3742
+ blob: await response.blob(),
3743
+ contentType: response.headers.get("content-type") || void 0,
3744
+ fileName: response.headers.get("content-disposition") || void 0,
3745
+ headers: Object.fromEntries(response.headers.entries())
3702
3746
  };
3703
- void run();
3704
- return () => {
3705
- disposed = true;
3706
- };
3707
- }, [auth]);
3747
+ };
3708
3748
  return {
3709
- ...state,
3710
- methods: state.data?.methods || [],
3711
- reload
3749
+ invoke: async (method4, payload) => {
3750
+ if (method4 === "transport.request") return await request(payload);
3751
+ if (method4 === "transport.download") return await download(payload);
3752
+ throw new Error(`\u4E0D\u652F\u6301\u7684 bridge \u65B9\u6CD5: ${method4}`);
3753
+ }
3712
3754
  };
3713
3755
  };
3714
- var LoginPage = ({
3715
- title,
3716
- subtitle,
3717
- className,
3718
- style: style2,
3719
- defaultMethod,
3720
- redirectUrl,
3721
- redirectOnSuccess = true,
3722
- onSuccess,
3723
- ...authOptions
3724
- }) => {
3725
- const runtime = useOpenXiangda();
3726
- const auth = useAuth(authOptions);
3727
- const methodsState = useLoginMethods(authOptions);
3728
- const [passwordForm] = import_antd2.Form.useForm();
3729
- const [phoneForm] = import_antd2.Form.useForm();
3730
- const [activeMethod, setActiveMethod] = (0, import_react7.useState)(
3731
- defaultMethod || "password"
3732
- );
3733
- const [phonePurpose, setPhonePurpose] = (0, import_react7.useState)(
3734
- "login"
3735
- );
3736
- const [phoneChallengeId, setPhoneChallengeId] = (0, import_react7.useState)("");
3737
- const [submitting, setSubmitting] = (0, import_react7.useState)(false);
3738
- const [sendingCode, setSendingCode] = (0, import_react7.useState)(false);
3739
- const [error, setError] = (0, import_react7.useState)(null);
3740
- const methods = methodsState.methods.filter((method4) => method4.enabled !== false);
3741
- const passwordMethod = findMethod(methods, "password");
3742
- const phoneMethod = findMethod(methods, "phone_code");
3743
- const dingtalkMethod = findMethod(methods, "dingtalk");
3744
- const ssoMethod = findMethod(methods, "sso");
3745
- const guestMethod = findMethod(methods, "guest");
3746
- const allowRegister = methodsState.data?.registration?.mode !== "reject";
3747
- const tabItems = (0, import_react7.useMemo)(() => {
3748
- const items = [];
3749
- if (passwordMethod) {
3750
- items.push({
3751
- key: "password",
3752
- label: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_antd2.Space, { size: 6, children: [
3753
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_icons.UserOutlined, {}),
3754
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { children: passwordMethod.label || "\u8D26\u53F7\u5BC6\u7801" })
3755
- ] }),
3756
- children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
3757
- PasswordLoginForm,
3758
- {
3759
- form: passwordForm,
3760
- loading: submitting,
3761
- onFinish: async (values) => {
3762
- setSubmitting(true);
3763
- setError(null);
3764
- try {
3765
- await handleSuccess(
3766
- await auth.passwordLogin({
3767
- username: values.username,
3768
- password: values.password
3769
- })
3770
- );
3771
- } catch (loginError) {
3772
- setError(normalizeError(loginError).message);
3773
- } finally {
3774
- setSubmitting(false);
3775
- }
3776
- }
3777
- }
3778
- )
3779
- });
3780
- }
3781
- if (phoneMethod) {
3782
- items.push({
3783
- key: "phone_code",
3784
- label: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_antd2.Space, { size: 6, children: [
3785
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_icons.MobileOutlined, {}),
3786
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { children: phoneMethod.label || "\u624B\u673A\u53F7" })
3787
- ] }),
3788
- children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
3789
- PhoneCodeLoginForm,
3790
- {
3791
- allowRegister,
3792
- form: phoneForm,
3793
- loading: submitting,
3794
- phonePurpose,
3795
- sendingCode,
3796
- onPurposeChange: setPhonePurpose,
3797
- onSendCode: async () => {
3798
- const values = await phoneForm.validateFields(["phone"]);
3799
- setSendingCode(true);
3800
- setError(null);
3801
- try {
3802
- const result = await auth.sendPhoneCode({
3803
- phone: values.phone,
3804
- purpose: phonePurpose
3805
- });
3806
- setPhoneChallengeId(result.challengeId);
3807
- } catch (sendError) {
3808
- setError(normalizeError(sendError).message);
3809
- } finally {
3810
- setSendingCode(false);
3811
- }
3812
- },
3813
- onFinish: async (values) => {
3814
- setSubmitting(true);
3815
- setError(null);
3816
- try {
3817
- const data = phonePurpose === "register" ? await auth.registerWithPhoneCode({
3818
- phone: values.phone,
3819
- code: values.code,
3820
- challengeId: phoneChallengeId
3821
- }) : await auth.phoneCodeLogin({
3822
- phone: values.phone,
3823
- code: values.code,
3824
- challengeId: phoneChallengeId
3825
- });
3826
- await handleSuccess(data);
3827
- } catch (loginError) {
3828
- setError(normalizeError(loginError).message);
3829
- } finally {
3830
- setSubmitting(false);
3831
- }
3832
- }
3833
- }
3834
- )
3835
- });
3836
- }
3837
- return items;
3838
- }, [
3839
- allowRegister,
3840
- auth,
3841
- handleSuccess,
3842
- passwordForm,
3843
- passwordMethod,
3844
- phoneChallengeId,
3845
- phoneForm,
3846
- phoneMethod,
3847
- phonePurpose,
3848
- sendingCode,
3849
- submitting
3850
- ]);
3851
- (0, import_react7.useEffect)(() => {
3852
- const firstKey = tabItems[0]?.key;
3853
- if (firstKey && !tabItems.some((item) => item.key === activeMethod)) {
3854
- setActiveMethod(firstKey);
3855
- }
3856
- }, [activeMethod, tabItems]);
3857
- const handleDingTalkLogin = async () => {
3858
- setSubmitting(true);
3859
- setError(null);
3860
- try {
3861
- const code = await requestDingTalkCode(dingtalkMethod);
3862
- await handleSuccess(
3863
- await auth.dingtalkLogin({
3864
- code,
3865
- corpId: getString(dingtalkMethod, "corpId")
3866
- })
3867
- );
3868
- } catch (loginError) {
3869
- setError(normalizeError(loginError).message);
3870
- } finally {
3871
- setSubmitting(false);
3872
- }
3756
+ var createBrowserPageContext = (bootstrap, options = {}) => {
3757
+ const route = {
3758
+ pathname: options.route?.pathname || (typeof window !== "undefined" ? window.location.pathname : ""),
3759
+ fullPath: options.route?.fullPath || (typeof window !== "undefined" ? `${window.location.pathname}${window.location.search}${window.location.hash}` : ""),
3760
+ params: options.route?.params || {},
3761
+ query: options.route?.query || {},
3762
+ hash: options.route?.hash || (typeof window !== "undefined" ? window.location.hash : "")
3873
3763
  };
3874
- const handleSsoLogin = async () => {
3875
- setSubmitting(true);
3876
- setError(null);
3877
- try {
3878
- const redirectUri = getCurrentHref3();
3879
- const result = await auth.getSsoLoginUrl({
3880
- protocol: getString(ssoMethod, "protocol") || "cas",
3881
- redirectUri
3882
- });
3883
- window.location.assign(result.loginUrl);
3884
- } catch (loginError) {
3885
- setError(normalizeError(loginError).message);
3886
- setSubmitting(false);
3764
+ return {
3765
+ protocolVersion: bootstrap.asset?.protocolVersion || "1.0",
3766
+ app: bootstrap.app,
3767
+ page: {
3768
+ ...bootstrap.page,
3769
+ version: bootstrap.asset?.version || bootstrap.page.version,
3770
+ buildId: bootstrap.asset?.buildId || bootstrap.page.buildId
3771
+ },
3772
+ user: bootstrap.user,
3773
+ route,
3774
+ env: bootstrap.env || {},
3775
+ permissions: {
3776
+ canView: bootstrap.permissions?.canView !== false,
3777
+ hasFullAccess: bootstrap.permissions?.hasFullAccess === true,
3778
+ ...bootstrap.permissions || {}
3779
+ },
3780
+ capabilities: Array.from(
3781
+ /* @__PURE__ */ new Set([
3782
+ "navigation",
3783
+ "ui.message",
3784
+ "ui.modal",
3785
+ "transport.request",
3786
+ "transport.download",
3787
+ ...bootstrap.sdk?.supportedBridgeMethods || []
3788
+ ])
3789
+ ),
3790
+ ui: {
3791
+ message: {
3792
+ success: (text) => options.message?.success?.(text),
3793
+ error: (text) => options.message?.error?.(text),
3794
+ warning: (text) => options.message?.warning?.(text),
3795
+ info: (text) => options.message?.info?.(text),
3796
+ loading: (text) => options.message?.loading?.(text) || (() => void 0)
3797
+ },
3798
+ modal: {
3799
+ confirm: (input) => options.modal?.confirm?.(input) || Promise.resolve(false)
3800
+ }
3801
+ },
3802
+ navigation: {
3803
+ pushPage: (pageKey, query) => options.navigation?.pushPage?.(pageKey, query),
3804
+ replacePage: (pageKey, query) => options.navigation?.replacePage?.(pageKey, query),
3805
+ pushRoute: (routeValue, query) => options.navigation?.pushRoute?.(routeValue, query),
3806
+ replaceRoute: (routeValue, query) => options.navigation?.replaceRoute?.(routeValue, query),
3807
+ updateQuery: (query) => options.navigation?.updateQuery?.(query),
3808
+ setHash: (hash) => options.navigation?.setHash?.(hash),
3809
+ back: () => options.navigation?.back?.()
3810
+ },
3811
+ bridge: createBrowserPageBridge(options),
3812
+ sdk: {
3813
+ ...bootstrap.sdk,
3814
+ supportedBridgeMethods: ["transport.request", "transport.download"]
3887
3815
  }
3888
3816
  };
3889
- const handleGuestLogin = async () => {
3890
- setSubmitting(true);
3891
- setError(null);
3892
- try {
3893
- await handleSuccess(
3894
- await auth.guestLogin({
3895
- guestIdentifier: getOrCreateGuestIdentifier(auth.client),
3896
- domain: getCurrentHostname()
3897
- })
3898
- );
3899
- } catch (loginError) {
3900
- setError(normalizeError(loginError).message);
3901
- } finally {
3902
- setSubmitting(false);
3903
- }
3817
+ };
3818
+ var resolveRuntimeAssets = async (bootstrap, fetchImpl = fetch) => {
3819
+ const boundFetch = createBoundFetch(fetchImpl);
3820
+ const fallback = {
3821
+ entryUrl: bootstrap.runtimeAssets?.entryUrl || bootstrap.asset?.entryUrl || "",
3822
+ cssUrls: bootstrap.runtimeAssets?.cssUrls || bootstrap.asset?.cssAssets || [],
3823
+ jsUrls: bootstrap.runtimeAssets?.jsUrls || bootstrap.asset?.jsAssets || [],
3824
+ cssIsolation: normalizeCssIsolation2(
3825
+ bootstrap.runtimeAssets?.cssIsolation || bootstrap.page.capabilities?.cssIsolation
3826
+ )
3904
3827
  };
3905
- async function handleSuccess(data) {
3906
- await onSuccess?.(data);
3907
- await runtime.reload();
3908
- if (redirectOnSuccess && typeof window !== "undefined") {
3909
- window.location.replace(
3910
- redirectUrl || getCallbackUrl() || `/view/${auth.client.appType}`
3911
- );
3912
- }
3828
+ if (bootstrap.runtimeAssets?.entryUrl || !bootstrap.asset?.manifestUrl) {
3829
+ return fallback;
3913
3830
  }
3914
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
3915
- "div",
3831
+ try {
3832
+ const response = await boundFetch(bootstrap.asset.manifestUrl, {
3833
+ credentials: "omit"
3834
+ });
3835
+ if (!response.ok) return fallback;
3836
+ const manifest = await response.json();
3837
+ const base = bootstrap.asset.manifestUrl;
3838
+ const resolveUrl = (value) => new URL(value, base).toString();
3839
+ return {
3840
+ entryUrl: manifest?.entry?.url ? resolveUrl(manifest.entry.url) : fallback.entryUrl,
3841
+ cssUrls: Array.isArray(manifest?.assets?.css) ? manifest.assets.css.map(resolveUrl) : fallback.cssUrls,
3842
+ jsUrls: Array.isArray(manifest?.assets?.js) ? manifest.assets.js.map(resolveUrl) : fallback.jsUrls,
3843
+ cssIsolation: normalizeCssIsolation2(
3844
+ manifest?.style?.isolation || fallback.cssIsolation
3845
+ )
3846
+ };
3847
+ } catch {
3848
+ return fallback;
3849
+ }
3850
+ };
3851
+ var fetchBrowserRuntimeBootstrap = async ({
3852
+ appType,
3853
+ bootstrapPath,
3854
+ fetchImpl = fetch,
3855
+ pageKey,
3856
+ servicePrefix
3857
+ }) => {
3858
+ if (!appType) {
3859
+ throw new Error("appType \u7F3A\u5931");
3860
+ }
3861
+ if (!pageKey) {
3862
+ throw new Error("pageKey \u7F3A\u5931");
3863
+ }
3864
+ const path = bootstrapPath || `/openxiangda-api/v1/apps/${encodeURIComponent(
3865
+ appType
3866
+ )}/pages/${encodeURIComponent(pageKey)}/bootstrap`;
3867
+ const boundFetch = createBoundFetch(fetchImpl);
3868
+ const response = await boundFetch(
3869
+ joinServicePath(servicePrefix || getDefaultServicePrefix(), path),
3916
3870
  {
3917
- className,
3918
- style: {
3919
- minHeight: "100vh",
3920
- display: "grid",
3921
- placeItems: "center",
3922
- padding: 24,
3923
- background: "#f6f8fb",
3924
- ...style2
3925
- },
3926
- children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
3927
- import_antd2.Card,
3928
- {
3929
- style: {
3930
- width: "min(100%, 420px)",
3931
- borderRadius: 8,
3932
- boxShadow: "0 16px 48px rgba(15, 23, 42, 0.10)"
3933
- },
3934
- styles: { body: { padding: 28 } },
3935
- children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_antd2.Space, { direction: "vertical", size: 20, style: { width: "100%" }, children: [
3936
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { children: [
3937
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_antd2.Typography.Title, { level: 3, style: { margin: 0 }, children: title || "\u5E94\u7528\u767B\u5F55" }),
3938
- subtitle ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_antd2.Typography.Text, { type: "secondary", children: subtitle }) : null
3939
- ] }),
3940
- methodsState.error ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
3941
- import_antd2.Alert,
3942
- {
3943
- showIcon: true,
3944
- type: "error",
3945
- message: methodsState.error.message
3946
- }
3947
- ) : null,
3948
- error ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_antd2.Alert, { showIcon: true, type: "error", message: error }) : null,
3949
- methodsState.loading ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_antd2.Button, { block: true, loading: true, children: "\u52A0\u8F7D\u4E2D" }) : tabItems.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
3950
- import_antd2.Tabs,
3951
- {
3952
- activeKey: activeMethod,
3953
- items: tabItems,
3954
- onChange: setActiveMethod
3955
- }
3956
- ) : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_antd2.Empty, { description: "\u672A\u542F\u7528\u767B\u5F55\u65B9\u5F0F" }),
3957
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_antd2.Space, { direction: "vertical", size: 10, style: { width: "100%" }, children: [
3958
- dingtalkMethod ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
3959
- import_antd2.Button,
3960
- {
3961
- block: true,
3962
- icon: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_icons.QrcodeOutlined, {}),
3963
- loading: submitting,
3964
- onClick: handleDingTalkLogin,
3965
- children: dingtalkMethod.label || "\u9489\u9489\u514D\u767B"
3966
- }
3967
- ) : null,
3968
- ssoMethod ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
3969
- import_antd2.Button,
3970
- {
3971
- block: true,
3972
- icon: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_icons.SafetyCertificateOutlined, {}),
3973
- loading: submitting,
3974
- onClick: handleSsoLogin,
3975
- children: ssoMethod.label || "SSO \u767B\u5F55"
3976
- }
3977
- ) : null,
3978
- guestMethod ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
3979
- import_antd2.Button,
3980
- {
3981
- block: true,
3982
- icon: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_icons.LoginOutlined, {}),
3983
- loading: submitting,
3984
- onClick: handleGuestLogin,
3985
- children: guestMethod.label || "\u8BBF\u5BA2\u8BBF\u95EE"
3986
- }
3987
- ) : null
3988
- ] })
3989
- ] })
3990
- }
3991
- )
3871
+ method: "GET",
3872
+ credentials: "include"
3992
3873
  }
3993
3874
  );
3994
- };
3995
- var PasswordLoginForm = ({ form, loading, onFinish }) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_antd2.Form, { form, layout: "vertical", requiredMark: false, onFinish, children: [
3996
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
3997
- import_antd2.Form.Item,
3998
- {
3999
- label: "\u8D26\u53F7",
4000
- name: "username",
4001
- rules: [{ required: true, message: "\u8BF7\u8F93\u5165\u8D26\u53F7" }],
4002
- children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_antd2.Input, { autoComplete: "username" })
4003
- }
4004
- ),
4005
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
4006
- import_antd2.Form.Item,
4007
- {
4008
- label: "\u5BC6\u7801",
4009
- name: "password",
4010
- rules: [{ required: true, message: "\u8BF7\u8F93\u5165\u5BC6\u7801" }],
4011
- children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_antd2.Input.Password, { autoComplete: "current-password" })
4012
- }
4013
- ),
4014
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_antd2.Button, { block: true, htmlType: "submit", icon: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_icons.LoginOutlined, {}), loading, type: "primary", children: "\u767B\u5F55" })
4015
- ] });
4016
- var PhoneCodeLoginForm = ({
4017
- allowRegister,
4018
- form,
4019
- loading,
4020
- phonePurpose,
4021
- sendingCode,
4022
- onPurposeChange,
4023
- onSendCode,
4024
- onFinish
4025
- }) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_antd2.Form, { form, layout: "vertical", requiredMark: false, onFinish, children: [
4026
- allowRegister ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_antd2.Form.Item, { style: { marginBottom: 12 }, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
4027
- import_antd2.Tabs,
4028
- {
4029
- activeKey: phonePurpose,
4030
- items: [
4031
- { key: "login", label: "\u767B\u5F55" },
4032
- { key: "register", label: "\u6CE8\u518C" }
4033
- ],
4034
- onChange: (key) => onPurposeChange(key === "register" ? "register" : "login"),
4035
- size: "small"
4036
- }
4037
- ) }) : null,
4038
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
4039
- import_antd2.Form.Item,
4040
- {
4041
- label: "\u624B\u673A\u53F7",
4042
- name: "phone",
4043
- rules: [{ required: true, message: "\u8BF7\u8F93\u5165\u624B\u673A\u53F7" }],
4044
- children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_antd2.Input, { autoComplete: "tel" })
4045
- }
4046
- ),
4047
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
4048
- import_antd2.Form.Item,
4049
- {
4050
- label: "\u9A8C\u8BC1\u7801",
4051
- name: "code",
4052
- rules: [{ required: true, message: "\u8BF7\u8F93\u5165\u9A8C\u8BC1\u7801" }],
4053
- children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
4054
- import_antd2.Input,
4055
- {
4056
- addonAfter: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
4057
- import_antd2.Button,
4058
- {
4059
- loading: sendingCode,
4060
- onClick: onSendCode,
4061
- size: "small",
4062
- type: "link",
4063
- children: "\u53D1\u9001"
4064
- }
4065
- ),
4066
- autoComplete: "one-time-code"
4067
- }
4068
- )
4069
- }
4070
- ),
4071
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_antd2.Button, { block: true, htmlType: "submit", icon: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_icons.MobileOutlined, {}), loading, type: "primary", children: phonePurpose === "register" ? "\u6CE8\u518C" : "\u767B\u5F55" })
4072
- ] });
4073
- var findMethod = (methods, type4) => methods.find((method4) => method4.type === type4);
4074
- var normalizeError = (error) => error instanceof Error ? error : new Error(String(error || "\u8BF7\u6C42\u5931\u8D25"));
4075
- var getString = (value, key) => {
4076
- if (!value || typeof value !== "object") return void 0;
4077
- const result = value[key];
4078
- return typeof result === "string" ? result : void 0;
4079
- };
4080
- var requestDingTalkCode = (method4) => {
4081
- const dd = typeof window === "undefined" ? void 0 : window.dd;
4082
- const corpId = getString(method4, "corpId");
4083
- return new Promise((resolve, reject) => {
4084
- const requestAuthCode = dd?.runtime?.permission?.requestAuthCode || dd?.requestAuthCode;
4085
- if (!requestAuthCode) {
4086
- reject(new Error("\u5F53\u524D\u73AF\u5883\u4E0D\u652F\u6301\u9489\u9489\u514D\u767B"));
4087
- return;
3875
+ const payload = await response.json().catch(() => null);
3876
+ if (!response.ok) {
3877
+ throw new Error(payload?.message || response.statusText || "\u83B7\u53D6\u9875\u9762\u8FD0\u884C\u65F6\u5931\u8D25");
3878
+ }
3879
+ if (payload && typeof payload === "object" && ("code" in payload || "success" in payload)) {
3880
+ if (payload.success === false || payload.code && Number(payload.code) !== 200) {
3881
+ throw new Error(payload.message || "\u83B7\u53D6\u9875\u9762\u8FD0\u884C\u65F6\u5931\u8D25");
4088
3882
  }
4089
- requestAuthCode({
4090
- corpId,
4091
- onSuccess: (result) => {
4092
- if (result?.code) resolve(result.code);
4093
- else reject(new Error("\u9489\u9489\u672A\u8FD4\u56DE\u514D\u767B\u7801"));
4094
- },
4095
- onFail: (error) => reject(normalizeError(error))
4096
- });
4097
- });
4098
- };
4099
- var getOrCreateGuestIdentifier = (client) => {
4100
- const key = `openxiangda:${client.appType}:guest_id`;
4101
- if (typeof window === "undefined") return createGuestIdentifier();
4102
- const current = window.localStorage.getItem(key);
4103
- if (current) return current;
4104
- const next = createGuestIdentifier();
4105
- window.localStorage.setItem(key, next);
4106
- return next;
4107
- };
4108
- var createGuestIdentifier = () => `guest_${Date.now()}_${Math.random().toString(36).slice(2, 10)}`;
4109
- var getCurrentHref3 = () => typeof window === "undefined" ? "" : window.location.href;
4110
- var getCurrentHostname = () => typeof window === "undefined" ? "" : window.location.hostname;
4111
- var getCallbackUrl = () => {
4112
- if (typeof window === "undefined") return "";
4113
- const query = new URLSearchParams(window.location.search);
4114
- return query.get("callback") || query.get("redirectUri") || "";
4115
- };
4116
-
4117
- // packages/sdk/src/runtime/react/openxiangdaProvider.tsx
4118
- var import_jsx_runtime4 = require("react/jsx-runtime");
4119
- var RuntimeHttpError = class extends Error {
4120
- constructor(snapshot) {
4121
- super(snapshot.message);
4122
- this.name = "RuntimeHttpError";
4123
- this.type = snapshot.type;
4124
- this.status = snapshot.status;
4125
- this.code = snapshot.code;
4126
- this.payload = snapshot.payload;
3883
+ return payload.data || {};
4127
3884
  }
3885
+ return payload || {};
4128
3886
  };
4129
- var OpenXiangdaRuntimeContext = (0, import_react8.createContext)(null);
4130
- var OpenXiangdaProvider = ({
3887
+ var resolveBrowserRuntimeRoute = async ({
4131
3888
  appType,
4132
- servicePrefix = "/service",
4133
- fetchImpl,
4134
- children
3889
+ fetchImpl = fetch,
3890
+ path,
3891
+ resolvePath,
3892
+ search,
3893
+ servicePrefix
4135
3894
  }) => {
4136
- const resolvedFetch = (0, import_react8.useMemo)(() => createBoundFetch(fetchImpl), [fetchImpl]);
4137
- const resolvedAppType = (0, import_react8.useMemo)(
4138
- () => appType || resolveAppTypeFromLocation(),
4139
- [appType]
3895
+ if (!appType) {
3896
+ throw new Error("appType \u7F3A\u5931");
3897
+ }
3898
+ const currentPath = path || (typeof window !== "undefined" ? window.location.pathname : "/");
3899
+ const currentSearch = search !== void 0 ? search : typeof window !== "undefined" ? window.location.search : "";
3900
+ const endpoint = resolvePath || `/openxiangda-api/v1/apps/${encodeURIComponent(
3901
+ appType
3902
+ )}/runtime/routes/resolve`;
3903
+ const boundFetch = createBoundFetch(fetchImpl);
3904
+ const response = await boundFetch(
3905
+ joinServicePath(servicePrefix || getDefaultServicePrefix(), endpoint),
3906
+ {
3907
+ method: "POST",
3908
+ credentials: "include",
3909
+ headers: {
3910
+ "Content-Type": "application/json"
3911
+ },
3912
+ body: JSON.stringify({
3913
+ path: currentPath,
3914
+ search: currentSearch
3915
+ })
3916
+ }
4140
3917
  );
4141
- const [accessToken, setAccessTokenState] = (0, import_react8.useState)(null);
4142
- const setAccessToken = (0, import_react8.useCallback)(
4143
- (nextAccessToken) => {
4144
- setAccessTokenState(nextAccessToken || null);
4145
- },
4146
- []
3918
+ const parsed = await parseJsonResponse(response);
3919
+ if (!parsed.success || !parsed.result) {
3920
+ throw new Error(parsed.message || "\u89E3\u6790\u8FD0\u884C\u65F6\u8DEF\u7531\u5931\u8D25");
3921
+ }
3922
+ return parsed.result;
3923
+ };
3924
+ var loadRuntimeScriptModules = async (jsUrls = [], moduleLoader = defaultModuleLoader) => {
3925
+ const urls = Array.from(
3926
+ new Set(jsUrls.map((url2) => String(url2 || "").trim()).filter(Boolean))
4147
3927
  );
4148
- const authorizedFetch = (0, import_react8.useMemo)(
4149
- () => createAuthorizedFetch(resolvedFetch, accessToken),
4150
- [accessToken, resolvedFetch]
3928
+ for (const url2 of urls) {
3929
+ await moduleLoader(url2);
3930
+ }
3931
+ };
3932
+ var loadCustomPageModule = async (entryUrl, moduleLoader = defaultModuleLoader, jsUrls = []) => {
3933
+ if (!entryUrl) {
3934
+ throw new Error("\u4EE3\u7801\u9875\u9762\u7F3A\u5C11 entryUrl");
3935
+ }
3936
+ await loadRuntimeScriptModules(
3937
+ jsUrls.filter((url2) => url2 && url2 !== entryUrl),
3938
+ moduleLoader
4151
3939
  );
4152
- const [state, setState] = (0, import_react8.useState)({
3940
+ const loaded = await moduleLoader(entryUrl);
3941
+ return loaded?.default && (loaded.default.mount || loaded.default.unmount) ? { ...loaded.default, ...loaded } : loaded || {};
3942
+ };
3943
+ var mountCustomPageRuntime = async ({
3944
+ assets,
3945
+ container,
3946
+ context,
3947
+ module: module2
3948
+ }) => {
3949
+ const cssIsolation = normalizeCssIsolation2(assets?.cssIsolation);
3950
+ const mountedLinks = [];
3951
+ container.innerHTML = "";
3952
+ container.classList.toggle(NAMESPACE_ROOT_CLASS2, cssIsolation === "namespace");
3953
+ (assets?.cssUrls || []).forEach((href) => {
3954
+ const link = document.createElement("link");
3955
+ link.rel = "stylesheet";
3956
+ link.href = href;
3957
+ link.setAttribute("data-openxiangda-runtime-style", href);
3958
+ document.head.appendChild(link);
3959
+ mountedLinks.push(link);
3960
+ });
3961
+ await module2.mount?.(container, context);
3962
+ return async () => {
3963
+ await module2.unmount?.(container, context);
3964
+ mountedLinks.forEach((link) => link.remove());
3965
+ container.classList.remove(NAMESPACE_ROOT_CLASS2);
3966
+ container.innerHTML = "";
3967
+ };
3968
+ };
3969
+ var mountBrowserPageRuntime = async (options) => {
3970
+ const bootstrap = await fetchBrowserRuntimeBootstrap({
3971
+ appType: options.appType,
3972
+ pageKey: options.pageKey,
3973
+ bootstrapPath: options.bootstrapPath,
3974
+ fetchImpl: options.fetchImpl,
3975
+ servicePrefix: options.servicePrefix
3976
+ });
3977
+ if (bootstrap.permissions?.canView === false) {
3978
+ throw new Error(String(bootstrap.permissions.message || "\u60A8\u6CA1\u6709\u6743\u9650\u8BBF\u95EE\u8BE5\u9875\u9762"));
3979
+ }
3980
+ const assets = await resolveRuntimeAssets(bootstrap, options.fetchImpl || fetch);
3981
+ if (!assets.entryUrl) {
3982
+ throw new Error("\u4EE3\u7801\u9875\u9762\u7F3A\u5C11 entryUrl");
3983
+ }
3984
+ const context = createBrowserPageContext(bootstrap, options);
3985
+ const module2 = await loadCustomPageModule(
3986
+ assets.entryUrl,
3987
+ options.moduleLoader,
3988
+ assets.jsUrls
3989
+ );
3990
+ const cleanup2 = await mountCustomPageRuntime({
3991
+ assets,
3992
+ container: options.container,
3993
+ context,
3994
+ module: module2
3995
+ });
3996
+ return {
3997
+ bootstrap,
3998
+ assets,
3999
+ context,
4000
+ module: module2,
4001
+ cleanup: cleanup2
4002
+ };
4003
+ };
4004
+
4005
+ // packages/sdk/src/runtime/react/auth.tsx
4006
+ init_cjs_shims();
4007
+ var import_react7 = require("react");
4008
+ var import_antd2 = require("antd");
4009
+ var import_icons = require("@ant-design/icons");
4010
+ var import_jsx_runtime3 = require("react/jsx-runtime");
4011
+ var useAuth = (options = {}) => {
4012
+ const runtime = useOpenXiangda();
4013
+ const client = (0, import_react7.useMemo)(
4014
+ () => createAuthClient({
4015
+ appType: options.appType || runtime.appType,
4016
+ servicePrefix: options.servicePrefix || runtime.servicePrefix,
4017
+ fetchImpl: options.fetchImpl || runtime.fetchImpl
4018
+ }),
4019
+ [
4020
+ options.appType,
4021
+ options.fetchImpl,
4022
+ options.servicePrefix,
4023
+ runtime.appType,
4024
+ runtime.fetchImpl,
4025
+ runtime.servicePrefix
4026
+ ]
4027
+ );
4028
+ return (0, import_react7.useMemo)(
4029
+ () => ({
4030
+ client,
4031
+ getMethods: client.getMethods,
4032
+ passwordLogin: client.passwordLogin,
4033
+ dingtalkLogin: client.dingtalkLogin,
4034
+ guestLogin: client.guestLogin,
4035
+ sendPhoneCode: client.sendPhoneCode,
4036
+ phoneCodeLogin: client.phoneCodeLogin,
4037
+ registerWithPhoneCode: client.registerWithPhoneCode,
4038
+ getSsoLoginUrl: client.getSsoLoginUrl,
4039
+ refresh: client.refresh,
4040
+ logout: client.logout,
4041
+ resolveLoginUrl: client.resolveLoginUrl
4042
+ }),
4043
+ [client]
4044
+ );
4045
+ };
4046
+ var useLoginMethods = (options = {}) => {
4047
+ const auth = useAuth(options);
4048
+ const [state, setState] = (0, import_react7.useState)({
4153
4049
  data: null,
4154
4050
  loading: true,
4155
4051
  error: null
4156
4052
  });
4157
- const reload = (0, import_react8.useCallback)(async (options = {}) => {
4158
- if (!resolvedAppType) {
4159
- setState({
4160
- data: null,
4161
- loading: false,
4162
- error: createRuntimeError({
4163
- message: "appType \u4E0D\u80FD\u4E3A\u7A7A",
4164
- type: "unknown"
4165
- })
4166
- });
4167
- return;
4168
- }
4053
+ const reload = (0, import_react7.useCallback)(async () => {
4169
4054
  setState((prev) => ({ ...prev, loading: true, error: null }));
4170
- const requestFetch = options.accessToken !== void 0 ? createAuthorizedFetch(resolvedFetch, options.accessToken || null) : authorizedFetch;
4171
4055
  try {
4172
- const response = await requestFetch(
4173
- buildServiceUrl3(
4174
- servicePrefix,
4175
- `/openxiangda-api/v1/apps/${encodeURIComponent(
4176
- resolvedAppType
4177
- )}/runtime/bootstrap`
4178
- ),
4179
- {
4180
- credentials: "include",
4181
- headers: { accept: "application/json" }
4182
- }
4183
- );
4184
- const payload = await readJsonPayload(response);
4185
- if (isRuntimeEnvelopeFailure(response, payload)) {
4186
- throw createRuntimeHttpError(
4187
- response,
4188
- payload,
4189
- payload?.message || `Runtime bootstrap failed: ${response.status}`
4190
- );
4191
- }
4192
- setState({
4193
- data: payload?.data || null,
4194
- loading: false,
4195
- error: null
4196
- });
4056
+ const data = await auth.getMethods();
4057
+ setState({ data, loading: false, error: null });
4197
4058
  } catch (error) {
4198
4059
  setState({
4199
4060
  data: null,
4200
4061
  loading: false,
4201
- error: normalizeRuntimeError(error)
4062
+ error: normalizeError(error)
4202
4063
  });
4203
4064
  }
4204
- }, [authorizedFetch, resolvedAppType, resolvedFetch, servicePrefix]);
4205
- (0, import_react8.useEffect)(() => {
4206
- void reload();
4207
- }, [reload]);
4208
- const value = (0, import_react8.useMemo)(
4209
- () => ({
4210
- ...state,
4211
- appType: resolvedAppType,
4212
- servicePrefix,
4213
- fetchImpl: authorizedFetch,
4214
- reload,
4215
- setAccessToken
4216
- }),
4217
- [authorizedFetch, reload, resolvedAppType, servicePrefix, state]
4218
- );
4219
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(OpenXiangdaRuntimeContext.Provider, { value, children });
4220
- };
4221
- var useOpenXiangda = () => {
4222
- const context = (0, import_react8.useContext)(OpenXiangdaRuntimeContext);
4223
- if (!context) {
4224
- throw new Error("useOpenXiangda must be used inside OpenXiangdaProvider");
4225
- }
4226
- return context;
4227
- };
4228
- var useRuntimeBootstrap = () => useOpenXiangda();
4229
- var useAppMenus = () => {
4230
- const runtime = useOpenXiangda();
4231
- return {
4232
- ...runtime,
4233
- data: runtime.data?.menus || []
4234
- };
4235
- };
4236
- var usePermission = () => {
4237
- const runtime = useOpenXiangda();
4065
+ }, [auth]);
4066
+ (0, import_react7.useEffect)(() => {
4067
+ let disposed = false;
4068
+ const run = async () => {
4069
+ setState((prev) => ({ ...prev, loading: true, error: null }));
4070
+ try {
4071
+ const data = await auth.getMethods();
4072
+ if (!disposed) setState({ data, loading: false, error: null });
4073
+ } catch (error) {
4074
+ if (!disposed) {
4075
+ setState({
4076
+ data: null,
4077
+ loading: false,
4078
+ error: normalizeError(error)
4079
+ });
4080
+ }
4081
+ }
4082
+ };
4083
+ void run();
4084
+ return () => {
4085
+ disposed = true;
4086
+ };
4087
+ }, [auth]);
4238
4088
  return {
4239
- ...runtime,
4240
- data: runtime.data?.permissions || null
4089
+ ...state,
4090
+ methods: state.data?.methods || [],
4091
+ reload
4241
4092
  };
4242
4093
  };
4243
- var useCanAccessRoute = (input) => {
4094
+ var LoginPage = ({
4095
+ title,
4096
+ subtitle,
4097
+ className,
4098
+ style: style2,
4099
+ defaultMethod,
4100
+ redirectUrl,
4101
+ redirectOnSuccess = true,
4102
+ onSuccess,
4103
+ ...authOptions
4104
+ }) => {
4244
4105
  const runtime = useOpenXiangda();
4245
- const [state, setState] = (0, import_react8.useState)({
4246
- data: null,
4247
- loading: true,
4248
- error: null
4249
- });
4250
- (0, import_react8.useEffect)(() => {
4251
- let disposed = false;
4252
- const check = async () => {
4253
- const permissions = runtime.data?.permissions;
4254
- if (!runtime.appType || runtime.loading) {
4255
- setState((prev) => ({ ...prev, loading: runtime.loading }));
4256
- return;
4257
- }
4258
- if (runtime.error && !runtime.data) {
4259
- const snapshot = toRuntimeErrorSnapshot(runtime.error);
4260
- setState({
4261
- data: {
4262
- appType: runtime.appType,
4263
- canAccess: false,
4264
- status: snapshot.status,
4265
- code: snapshot.code,
4266
- message: snapshot.message,
4267
- errorType: snapshot.type,
4268
- payload: snapshot.payload
4269
- },
4270
- loading: false,
4271
- error: runtime.error
4272
- });
4273
- return;
4274
- }
4275
- if (permissions?.hasFullAccess) {
4276
- setState({
4277
- data: { appType: runtime.appType, canAccess: true, permissions },
4278
- loading: false,
4279
- error: null
4280
- });
4281
- return;
4282
- }
4283
- setState((prev) => ({ ...prev, loading: true, error: null }));
4284
- try {
4285
- const response = await runtime.fetchImpl(
4286
- buildServiceUrl3(
4287
- runtime.servicePrefix,
4288
- `/openxiangda-api/v1/apps/${encodeURIComponent(
4289
- runtime.appType
4290
- )}/runtime/routes/check`
4291
- ),
4106
+ const auth = useAuth(authOptions);
4107
+ const methodsState = useLoginMethods(authOptions);
4108
+ const [passwordForm] = import_antd2.Form.useForm();
4109
+ const [phoneForm] = import_antd2.Form.useForm();
4110
+ const [activeMethod, setActiveMethod] = (0, import_react7.useState)(
4111
+ defaultMethod || "password"
4112
+ );
4113
+ const [phonePurpose, setPhonePurpose] = (0, import_react7.useState)(
4114
+ "login"
4115
+ );
4116
+ const [phoneChallengeId, setPhoneChallengeId] = (0, import_react7.useState)("");
4117
+ const [submitting, setSubmitting] = (0, import_react7.useState)(false);
4118
+ const [sendingCode, setSendingCode] = (0, import_react7.useState)(false);
4119
+ const [error, setError] = (0, import_react7.useState)(null);
4120
+ const methods = methodsState.methods.filter((method4) => method4.enabled !== false);
4121
+ const passwordMethod = findMethod(methods, "password");
4122
+ const phoneMethod = findMethod(methods, "phone_code");
4123
+ const dingtalkMethod = findMethod(methods, "dingtalk");
4124
+ const ssoMethod = findMethod(methods, "sso");
4125
+ const guestMethod = findMethod(methods, "guest");
4126
+ const allowRegister = methodsState.data?.registration?.mode !== "reject";
4127
+ const tabItems = (0, import_react7.useMemo)(() => {
4128
+ const items = [];
4129
+ if (passwordMethod) {
4130
+ items.push({
4131
+ key: "password",
4132
+ label: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_antd2.Space, { size: 6, children: [
4133
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_icons.UserOutlined, {}),
4134
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { children: passwordMethod.label || "\u8D26\u53F7\u5BC6\u7801" })
4135
+ ] }),
4136
+ children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
4137
+ PasswordLoginForm,
4292
4138
  {
4293
- method: "POST",
4294
- credentials: "include",
4295
- headers: {
4296
- accept: "application/json",
4297
- "content-type": "application/json"
4139
+ form: passwordForm,
4140
+ loading: submitting,
4141
+ onFinish: async (values) => {
4142
+ setSubmitting(true);
4143
+ setError(null);
4144
+ try {
4145
+ await handleSuccess(
4146
+ await auth.passwordLogin({
4147
+ username: values.username,
4148
+ password: values.password
4149
+ })
4150
+ );
4151
+ } catch (loginError) {
4152
+ setError(normalizeError(loginError).message);
4153
+ } finally {
4154
+ setSubmitting(false);
4155
+ }
4156
+ }
4157
+ }
4158
+ )
4159
+ });
4160
+ }
4161
+ if (phoneMethod) {
4162
+ items.push({
4163
+ key: "phone_code",
4164
+ label: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_antd2.Space, { size: 6, children: [
4165
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_icons.MobileOutlined, {}),
4166
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { children: phoneMethod.label || "\u624B\u673A\u53F7" })
4167
+ ] }),
4168
+ children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
4169
+ PhoneCodeLoginForm,
4170
+ {
4171
+ allowRegister,
4172
+ form: phoneForm,
4173
+ loading: submitting,
4174
+ phonePurpose,
4175
+ sendingCode,
4176
+ onPurposeChange: setPhonePurpose,
4177
+ onSendCode: async () => {
4178
+ const values = await phoneForm.validateFields(["phone"]);
4179
+ setSendingCode(true);
4180
+ setError(null);
4181
+ try {
4182
+ const result = await auth.sendPhoneCode({
4183
+ phone: values.phone,
4184
+ purpose: phonePurpose
4185
+ });
4186
+ setPhoneChallengeId(result.challengeId);
4187
+ } catch (sendError) {
4188
+ setError(normalizeError(sendError).message);
4189
+ } finally {
4190
+ setSendingCode(false);
4191
+ }
4298
4192
  },
4299
- body: JSON.stringify(input)
4193
+ onFinish: async (values) => {
4194
+ setSubmitting(true);
4195
+ setError(null);
4196
+ try {
4197
+ const data = phonePurpose === "register" ? await auth.registerWithPhoneCode({
4198
+ phone: values.phone,
4199
+ code: values.code,
4200
+ challengeId: phoneChallengeId
4201
+ }) : await auth.phoneCodeLogin({
4202
+ phone: values.phone,
4203
+ code: values.code,
4204
+ challengeId: phoneChallengeId
4205
+ });
4206
+ await handleSuccess(data);
4207
+ } catch (loginError) {
4208
+ setError(normalizeError(loginError).message);
4209
+ } finally {
4210
+ setSubmitting(false);
4211
+ }
4212
+ }
4300
4213
  }
4301
- );
4302
- const payload = await readJsonPayload(response);
4303
- const code = payload?.code ?? response.status;
4304
- const canAccess = Boolean(payload?.data?.canAccess);
4305
- const errorType = canAccess ? void 0 : classifyRuntimeError(response.status, code);
4306
- const data = {
4307
- ...payload?.data || {
4308
- appType: runtime.appType,
4309
- canAccess: false
4310
- },
4311
- appType: payload?.data?.appType || runtime.appType,
4312
- canAccess,
4313
- status: response.status,
4314
- code,
4315
- message: payload?.message,
4316
- errorType,
4317
- payload
4318
- };
4319
- if (!disposed) {
4320
- const shouldTreatAsError = !response.ok || isServerErrorCode(code);
4321
- setState({
4322
- data,
4323
- loading: false,
4324
- error: shouldTreatAsError ? createRuntimeHttpError(
4325
- response,
4326
- payload,
4327
- payload?.message || `Route check failed: ${response.status}`
4328
- ) : null
4329
- });
4330
- }
4331
- } catch (error) {
4332
- if (!disposed) {
4333
- setState({
4334
- data: null,
4335
- loading: false,
4336
- error: normalizeRuntimeError(error)
4337
- });
4338
- }
4339
- }
4340
- };
4341
- void check();
4342
- return () => {
4343
- disposed = true;
4344
- };
4214
+ )
4215
+ });
4216
+ }
4217
+ return items;
4345
4218
  }, [
4346
- input.menuCode,
4347
- input.path,
4348
- input.routeCode,
4349
- runtime.appType,
4350
- runtime.data?.permissions,
4351
- runtime.fetchImpl,
4352
- runtime.loading,
4353
- runtime.servicePrefix
4219
+ allowRegister,
4220
+ auth,
4221
+ handleSuccess,
4222
+ passwordForm,
4223
+ passwordMethod,
4224
+ phoneChallengeId,
4225
+ phoneForm,
4226
+ phoneMethod,
4227
+ phonePurpose,
4228
+ sendingCode,
4229
+ submitting
4354
4230
  ]);
4355
- return {
4356
- ...state,
4357
- canAccess: Boolean(state.data?.canAccess)
4231
+ (0, import_react7.useEffect)(() => {
4232
+ const firstKey = tabItems[0]?.key;
4233
+ if (firstKey && !tabItems.some((item) => item.key === activeMethod)) {
4234
+ setActiveMethod(firstKey);
4235
+ }
4236
+ }, [activeMethod, tabItems]);
4237
+ const handleDingTalkLogin = async () => {
4238
+ setSubmitting(true);
4239
+ setError(null);
4240
+ try {
4241
+ const code = await requestDingTalkCode(dingtalkMethod);
4242
+ await handleSuccess(
4243
+ await auth.dingtalkLogin({
4244
+ code,
4245
+ corpId: getString(dingtalkMethod, "corpId")
4246
+ })
4247
+ );
4248
+ } catch (loginError) {
4249
+ setError(normalizeError(loginError).message);
4250
+ } finally {
4251
+ setSubmitting(false);
4252
+ }
4358
4253
  };
4359
- };
4360
- var PermissionBoundary = ({
4361
- children,
4362
- fallback = null,
4363
- loadingFallback = null,
4364
- routeCode,
4365
- menuCode,
4366
- path
4367
- }) => {
4368
- const runtime = useOpenXiangda();
4369
- const access = useCanAccessRoute({ routeCode, menuCode, path });
4370
- const fallbackState = createPermissionFallbackState(access, runtime);
4371
- if (access.loading) {
4372
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_jsx_runtime4.Fragment, { children: renderBoundaryFallback(loadingFallback, fallbackState) });
4373
- }
4374
- if (!access.canAccess) {
4375
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_jsx_runtime4.Fragment, { children: renderBoundaryFallback(fallback, fallbackState) });
4254
+ const handleSsoLogin = async () => {
4255
+ setSubmitting(true);
4256
+ setError(null);
4257
+ try {
4258
+ const redirectUri = getCurrentHref3();
4259
+ const result = await auth.getSsoLoginUrl({
4260
+ protocol: getString(ssoMethod, "protocol") || "cas",
4261
+ redirectUri
4262
+ });
4263
+ window.location.assign(result.loginUrl);
4264
+ } catch (loginError) {
4265
+ setError(normalizeError(loginError).message);
4266
+ setSubmitting(false);
4267
+ }
4268
+ };
4269
+ const handleGuestLogin = async () => {
4270
+ setSubmitting(true);
4271
+ setError(null);
4272
+ try {
4273
+ await handleSuccess(
4274
+ await auth.guestLogin({
4275
+ guestIdentifier: getOrCreateGuestIdentifier(auth.client),
4276
+ domain: getCurrentHostname()
4277
+ })
4278
+ );
4279
+ } catch (loginError) {
4280
+ setError(normalizeError(loginError).message);
4281
+ } finally {
4282
+ setSubmitting(false);
4283
+ }
4284
+ };
4285
+ async function handleSuccess(data) {
4286
+ await onSuccess?.(data);
4287
+ await runtime.reload();
4288
+ if (redirectOnSuccess && typeof window !== "undefined") {
4289
+ window.location.replace(
4290
+ redirectUrl || getCallbackUrl() || `/view/${auth.client.appType}`
4291
+ );
4292
+ }
4376
4293
  }
4377
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_jsx_runtime4.Fragment, { children });
4294
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
4295
+ "div",
4296
+ {
4297
+ className,
4298
+ style: {
4299
+ minHeight: "100vh",
4300
+ display: "grid",
4301
+ placeItems: "center",
4302
+ padding: 24,
4303
+ background: "#f6f8fb",
4304
+ ...style2
4305
+ },
4306
+ children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
4307
+ import_antd2.Card,
4308
+ {
4309
+ style: {
4310
+ width: "min(100%, 420px)",
4311
+ borderRadius: 8,
4312
+ boxShadow: "0 16px 48px rgba(15, 23, 42, 0.10)"
4313
+ },
4314
+ styles: { body: { padding: 28 } },
4315
+ children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_antd2.Space, { direction: "vertical", size: 20, style: { width: "100%" }, children: [
4316
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { children: [
4317
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_antd2.Typography.Title, { level: 3, style: { margin: 0 }, children: title || "\u5E94\u7528\u767B\u5F55" }),
4318
+ subtitle ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_antd2.Typography.Text, { type: "secondary", children: subtitle }) : null
4319
+ ] }),
4320
+ methodsState.error ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
4321
+ import_antd2.Alert,
4322
+ {
4323
+ showIcon: true,
4324
+ type: "error",
4325
+ message: methodsState.error.message
4326
+ }
4327
+ ) : null,
4328
+ error ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_antd2.Alert, { showIcon: true, type: "error", message: error }) : null,
4329
+ methodsState.loading ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_antd2.Button, { block: true, loading: true, children: "\u52A0\u8F7D\u4E2D" }) : tabItems.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
4330
+ import_antd2.Tabs,
4331
+ {
4332
+ activeKey: activeMethod,
4333
+ items: tabItems,
4334
+ onChange: setActiveMethod
4335
+ }
4336
+ ) : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_antd2.Empty, { description: "\u672A\u542F\u7528\u767B\u5F55\u65B9\u5F0F" }),
4337
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_antd2.Space, { direction: "vertical", size: 10, style: { width: "100%" }, children: [
4338
+ dingtalkMethod ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
4339
+ import_antd2.Button,
4340
+ {
4341
+ block: true,
4342
+ icon: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_icons.QrcodeOutlined, {}),
4343
+ loading: submitting,
4344
+ onClick: handleDingTalkLogin,
4345
+ children: dingtalkMethod.label || "\u9489\u9489\u514D\u767B"
4346
+ }
4347
+ ) : null,
4348
+ ssoMethod ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
4349
+ import_antd2.Button,
4350
+ {
4351
+ block: true,
4352
+ icon: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_icons.SafetyCertificateOutlined, {}),
4353
+ loading: submitting,
4354
+ onClick: handleSsoLogin,
4355
+ children: ssoMethod.label || "SSO \u767B\u5F55"
4356
+ }
4357
+ ) : null,
4358
+ guestMethod ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
4359
+ import_antd2.Button,
4360
+ {
4361
+ block: true,
4362
+ icon: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_icons.LoginOutlined, {}),
4363
+ loading: submitting,
4364
+ onClick: handleGuestLogin,
4365
+ children: guestMethod.label || "\u8BBF\u5BA2\u8BBF\u95EE"
4366
+ }
4367
+ ) : null
4368
+ ] })
4369
+ ] })
4370
+ }
4371
+ )
4372
+ }
4373
+ );
4378
4374
  };
4379
- var useRuntimeAuth = () => {
4380
- const runtime = useOpenXiangda();
4381
- const resolveLoginUrl2 = (0, import_react8.useCallback)(
4382
- async (options = {}) => {
4383
- const redirectUri = options.redirectUri || getCurrentHref4();
4384
- const domain = options.domain || getCurrentHostname2();
4385
- const appTenantId = getRecordString(runtime.data?.app, "tenantId");
4386
- try {
4387
- const statusUrl = withQuery(
4388
- buildServiceUrl3(runtime.servicePrefix, "/api/sso/status"),
4389
- { domain }
4390
- );
4391
- const statusResponse = await runtime.fetchImpl(statusUrl, {
4392
- credentials: "include",
4393
- headers: { accept: "application/json" }
4394
- });
4395
- const statusPayload = await readJsonPayload(statusResponse);
4396
- const sso = statusPayload?.data || {};
4397
- const enabled = Boolean(sso.enabled);
4398
- const shouldUseSso = Boolean(
4399
- enabled && (sso.forceLogin ?? sso.autoRedirect ?? true)
4400
- );
4401
- if (shouldUseSso) {
4402
- const loginUrlResponse = await runtime.fetchImpl(
4403
- withQuery(buildServiceUrl3(runtime.servicePrefix, "/api/sso/login-url"), {
4404
- domain,
4405
- redirectUri,
4406
- ...sso.tenantId || appTenantId ? { tenantId: String(sso.tenantId || appTenantId) } : {},
4407
- ...sso.protocol ? { protocol: String(sso.protocol) } : {}
4408
- }),
4375
+ var PasswordLoginForm = ({ form, loading, onFinish }) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_antd2.Form, { form, layout: "vertical", requiredMark: false, onFinish, children: [
4376
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
4377
+ import_antd2.Form.Item,
4378
+ {
4379
+ label: "\u8D26\u53F7",
4380
+ name: "username",
4381
+ rules: [{ required: true, message: "\u8BF7\u8F93\u5165\u8D26\u53F7" }],
4382
+ children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_antd2.Input, { autoComplete: "username" })
4383
+ }
4384
+ ),
4385
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
4386
+ import_antd2.Form.Item,
4387
+ {
4388
+ label: "\u5BC6\u7801",
4389
+ name: "password",
4390
+ rules: [{ required: true, message: "\u8BF7\u8F93\u5165\u5BC6\u7801" }],
4391
+ children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_antd2.Input.Password, { autoComplete: "current-password" })
4392
+ }
4393
+ ),
4394
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_antd2.Button, { block: true, htmlType: "submit", icon: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_icons.LoginOutlined, {}), loading, type: "primary", children: "\u767B\u5F55" })
4395
+ ] });
4396
+ var PhoneCodeLoginForm = ({
4397
+ allowRegister,
4398
+ form,
4399
+ loading,
4400
+ phonePurpose,
4401
+ sendingCode,
4402
+ onPurposeChange,
4403
+ onSendCode,
4404
+ onFinish
4405
+ }) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_antd2.Form, { form, layout: "vertical", requiredMark: false, onFinish, children: [
4406
+ allowRegister ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_antd2.Form.Item, { style: { marginBottom: 12 }, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
4407
+ import_antd2.Tabs,
4408
+ {
4409
+ activeKey: phonePurpose,
4410
+ items: [
4411
+ { key: "login", label: "\u767B\u5F55" },
4412
+ { key: "register", label: "\u6CE8\u518C" }
4413
+ ],
4414
+ onChange: (key) => onPurposeChange(key === "register" ? "register" : "login"),
4415
+ size: "small"
4416
+ }
4417
+ ) }) : null,
4418
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
4419
+ import_antd2.Form.Item,
4420
+ {
4421
+ label: "\u624B\u673A\u53F7",
4422
+ name: "phone",
4423
+ rules: [{ required: true, message: "\u8BF7\u8F93\u5165\u624B\u673A\u53F7" }],
4424
+ children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_antd2.Input, { autoComplete: "tel" })
4425
+ }
4426
+ ),
4427
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
4428
+ import_antd2.Form.Item,
4429
+ {
4430
+ label: "\u9A8C\u8BC1\u7801",
4431
+ name: "code",
4432
+ rules: [{ required: true, message: "\u8BF7\u8F93\u5165\u9A8C\u8BC1\u7801" }],
4433
+ children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
4434
+ import_antd2.Input,
4435
+ {
4436
+ addonAfter: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
4437
+ import_antd2.Button,
4409
4438
  {
4410
- credentials: "include",
4411
- headers: { accept: "application/json" }
4439
+ loading: sendingCode,
4440
+ onClick: onSendCode,
4441
+ size: "small",
4442
+ type: "link",
4443
+ children: "\u53D1\u9001"
4412
4444
  }
4413
- );
4414
- const loginUrlPayload = await readJsonPayload(loginUrlResponse);
4415
- const loginUrl = loginUrlPayload?.data?.loginUrl || loginUrlPayload?.data?.url || loginUrlPayload?.loginUrl || loginUrlPayload?.url;
4416
- if (loginUrl) return String(loginUrl);
4417
- }
4418
- } catch {
4419
- }
4420
- const configuredLoginUrl = options.loginUrl || getRuntimeEnv("OPENXIANGDA_LOGIN_URL") || getRuntimeEnv("VITE_OPENXIANGDA_LOGIN_URL") || getRuntimeEnv("APP_LOGIN_URL") || getRuntimeEnv("VITE_APP_LOGIN_URL") || getRuntimeEnv("VITE_BATH_AUTH_URL") || getRuntimeEnv("BATH_AUTH_URL");
4421
- if (configuredLoginUrl) {
4422
- return attachCallback(configuredLoginUrl, redirectUri);
4423
- }
4424
- return attachCallback("/platform/login", redirectUri);
4425
- },
4426
- [runtime.data?.app, runtime.fetchImpl, runtime.servicePrefix]
4427
- );
4428
- const redirectToLogin = (0, import_react8.useCallback)(
4429
- async (options = {}) => {
4430
- const loginUrl = await resolveLoginUrl2(options);
4431
- if (typeof window !== "undefined") {
4432
- if (options.replace === false) {
4433
- window.location.assign(loginUrl);
4434
- } else {
4435
- window.location.replace(loginUrl);
4445
+ ),
4446
+ autoComplete: "one-time-code"
4436
4447
  }
4437
- }
4438
- return loginUrl;
4439
- },
4440
- [resolveLoginUrl2]
4441
- );
4442
- const logout = (0, import_react8.useCallback)(async () => {
4443
- const response = await runtime.fetchImpl(
4444
- buildServiceUrl3(runtime.servicePrefix, "/api/auth/logout"),
4445
- {
4446
- method: "POST",
4447
- credentials: "include",
4448
- headers: { accept: "application/json" }
4449
- }
4450
- );
4451
- const payload = await readJsonPayload(response);
4452
- if (isRuntimeEnvelopeFailure(response, payload)) {
4453
- throw createRuntimeHttpError(
4454
- response,
4455
- payload,
4456
- payload?.message || `Logout failed: ${response.status}`
4457
- );
4448
+ )
4458
4449
  }
4459
- return payload;
4460
- }, [runtime.fetchImpl, runtime.servicePrefix]);
4461
- const logoutAndRedirect = (0, import_react8.useCallback)(
4462
- async (options = {}) => {
4463
- try {
4464
- await logout();
4465
- } catch (error) {
4466
- if (options.continueOnError === false) throw error;
4467
- }
4468
- return redirectToLogin(options);
4469
- },
4470
- [logout, redirectToLogin]
4471
- );
4472
- return {
4473
- logout,
4474
- logoutAndRedirect,
4475
- redirectToLogin,
4476
- resolveLoginUrl: resolveLoginUrl2
4477
- };
4478
- };
4479
- var buildServiceUrl3 = (servicePrefix, path) => {
4480
- const prefix2 = servicePrefix.endsWith("/") ? servicePrefix.slice(0, -1) : servicePrefix;
4481
- const suffix = path.startsWith("/") ? path : `/${path}`;
4482
- return `${prefix2}${suffix}`;
4450
+ ),
4451
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_antd2.Button, { block: true, htmlType: "submit", icon: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_icons.MobileOutlined, {}), loading, type: "primary", children: phonePurpose === "register" ? "\u6CE8\u518C" : "\u767B\u5F55" })
4452
+ ] });
4453
+ var findMethod = (methods, type4) => methods.find((method4) => method4.type === type4);
4454
+ var normalizeError = (error) => error instanceof Error ? error : new Error(String(error || "\u8BF7\u6C42\u5931\u8D25"));
4455
+ var getString = (value, key) => {
4456
+ if (!value || typeof value !== "object") return void 0;
4457
+ const result = value[key];
4458
+ return typeof result === "string" ? result : void 0;
4483
4459
  };
4484
- var createAuthorizedFetch = (baseFetch, accessToken) => {
4485
- if (!accessToken) return baseFetch;
4486
- return ((input, init = {}) => {
4487
- const headers = new Headers(init.headers || {});
4488
- if (!headers.has("authorization")) {
4489
- headers.set("authorization", `Bearer ${accessToken}`);
4460
+ var requestDingTalkCode = (method4) => {
4461
+ const dd = typeof window === "undefined" ? void 0 : window.dd;
4462
+ const corpId = getString(method4, "corpId");
4463
+ return new Promise((resolve, reject) => {
4464
+ const requestAuthCode = dd?.runtime?.permission?.requestAuthCode || dd?.requestAuthCode;
4465
+ if (!requestAuthCode) {
4466
+ reject(new Error("\u5F53\u524D\u73AF\u5883\u4E0D\u652F\u6301\u9489\u9489\u514D\u767B"));
4467
+ return;
4490
4468
  }
4491
- return baseFetch(input, {
4492
- ...init,
4493
- headers
4469
+ requestAuthCode({
4470
+ corpId,
4471
+ onSuccess: (result) => {
4472
+ if (result?.code) resolve(result.code);
4473
+ else reject(new Error("\u9489\u9489\u672A\u8FD4\u56DE\u514D\u767B\u7801"));
4474
+ },
4475
+ onFail: (error) => reject(normalizeError(error))
4494
4476
  });
4495
4477
  });
4496
4478
  };
4497
- var readJsonPayload = async (response) => {
4498
- try {
4499
- return await response.json();
4500
- } catch {
4501
- return null;
4502
- }
4479
+ var getOrCreateGuestIdentifier = (client) => {
4480
+ const key = `openxiangda:${client.appType}:guest_id`;
4481
+ if (typeof window === "undefined") return createGuestIdentifier();
4482
+ const current = window.localStorage.getItem(key);
4483
+ if (current) return current;
4484
+ const next = createGuestIdentifier();
4485
+ window.localStorage.setItem(key, next);
4486
+ return next;
4503
4487
  };
4504
- var createRuntimeHttpError = (response, payload, fallbackMessage) => createRuntimeError({
4505
- type: classifyRuntimeError(
4506
- response.status,
4507
- getRecordValue3(payload, "code")
4508
- ),
4509
- status: response.status,
4510
- code: getRecordValue3(payload, "code"),
4511
- message: getRecordValue3(payload, "message") || fallbackMessage,
4512
- payload
4513
- });
4514
- var createRuntimeError = (snapshot) => new RuntimeHttpError({
4515
- ...snapshot,
4516
- type: snapshot.type || "unknown",
4517
- message: snapshot.message || "Runtime request failed"
4518
- });
4519
- var normalizeRuntimeError = (error) => {
4520
- if (error instanceof RuntimeHttpError) return error;
4521
- if (error instanceof Error) {
4522
- const runtimeError = error;
4523
- runtimeError.type = runtimeError.type || classifyRuntimeError(runtimeError.status, runtimeError.code);
4524
- return runtimeError;
4525
- }
4526
- return createRuntimeError({
4527
- type: "unknown",
4528
- message: String(error)
4529
- });
4488
+ var createGuestIdentifier = () => `guest_${Date.now()}_${Math.random().toString(36).slice(2, 10)}`;
4489
+ var getCurrentHref3 = () => typeof window === "undefined" ? "" : window.location.href;
4490
+ var getCurrentHostname = () => typeof window === "undefined" ? "" : window.location.hostname;
4491
+ var getCallbackUrl = () => {
4492
+ if (typeof window === "undefined") return "";
4493
+ const query = new URLSearchParams(window.location.search);
4494
+ return query.get("callback") || query.get("redirectUri") || "";
4530
4495
  };
4531
- var toRuntimeErrorSnapshot = (error) => ({
4532
- type: error.type || classifyRuntimeError(error.status, error.code),
4533
- status: error.status,
4534
- code: error.code,
4535
- message: error.message || "Runtime request failed",
4536
- payload: error.payload
4537
- });
4538
- var classifyRuntimeError = (status, code) => {
4539
- const normalizedCode = typeof code === "string" ? Number(code) : code;
4540
- if (status === 401 || normalizedCode === 401) return "unauthenticated";
4541
- if (status === 403 || normalizedCode === 403) return "forbidden";
4542
- if (typeof code === "string") {
4543
- const normalizedText = code.toUpperCase();
4544
- if (normalizedText.includes("DENIED") || normalizedText.includes("FORBIDDEN")) {
4545
- return "forbidden";
4546
- }
4547
- if (normalizedText.includes("UNAUTH") || normalizedText.includes("LOGIN")) {
4548
- return "unauthenticated";
4549
- }
4496
+
4497
+ // packages/sdk/src/runtime/react/openxiangdaProvider.tsx
4498
+ var import_jsx_runtime4 = require("react/jsx-runtime");
4499
+ var RuntimeHttpError = class extends Error {
4500
+ constructor(snapshot) {
4501
+ super(snapshot.message);
4502
+ this.name = "RuntimeHttpError";
4503
+ this.type = snapshot.type;
4504
+ this.status = snapshot.status;
4505
+ this.code = snapshot.code;
4506
+ this.payload = snapshot.payload;
4550
4507
  }
4551
- if (!status && !normalizedCode) return "network";
4552
- return "unknown";
4553
- };
4554
- var createPermissionFallbackState = (access, runtime) => {
4555
- const error = access.error || runtime.error;
4556
- const accessData = access.data;
4557
- const errorType = accessData?.errorType || error?.type || (!access.canAccess ? "forbidden" : "unknown");
4558
- return {
4559
- access,
4560
- runtime,
4561
- error,
4562
- errorType,
4563
- status: accessData?.status || error?.status,
4564
- code: accessData?.code || error?.code,
4565
- message: accessData?.message || error?.message || (errorType === "unauthenticated" ? "\u5F53\u524D\u8D26\u53F7\u5C1A\u672A\u767B\u5F55" : "\u5F53\u524D\u8D26\u53F7\u6CA1\u6709\u8BBF\u95EE\u6743\u9650")
4566
- };
4567
4508
  };
4568
- var renderBoundaryFallback = (fallback, state) => typeof fallback === "function" ? fallback(state) : fallback;
4569
- var withQuery = (url2, params) => {
4570
- const query = new URLSearchParams();
4571
- Object.entries(params).forEach(([key, value]) => {
4572
- if (value === void 0 || value === "") return;
4573
- query.set(key, String(value));
4509
+ var OpenXiangdaRuntimeContext = (0, import_react8.createContext)(null);
4510
+ var OpenXiangdaProvider = ({
4511
+ appType,
4512
+ servicePrefix = "/service",
4513
+ fetchImpl,
4514
+ children
4515
+ }) => {
4516
+ const resolvedFetch = (0, import_react8.useMemo)(() => createBoundFetch(fetchImpl), [fetchImpl]);
4517
+ const resolvedAppType = (0, import_react8.useMemo)(
4518
+ () => appType || resolveAppTypeFromLocation(),
4519
+ [appType]
4520
+ );
4521
+ const [accessToken, setAccessTokenState] = (0, import_react8.useState)(null);
4522
+ const setAccessToken = (0, import_react8.useCallback)(
4523
+ (nextAccessToken) => {
4524
+ setAccessTokenState(nextAccessToken || null);
4525
+ },
4526
+ []
4527
+ );
4528
+ const authorizedFetch = (0, import_react8.useMemo)(
4529
+ () => createAuthorizedFetch(resolvedFetch, accessToken),
4530
+ [accessToken, resolvedFetch]
4531
+ );
4532
+ const [state, setState] = (0, import_react8.useState)({
4533
+ data: null,
4534
+ loading: true,
4535
+ error: null
4574
4536
  });
4575
- const serialized = query.toString();
4576
- if (!serialized) return url2;
4577
- return `${url2}${url2.includes("?") ? "&" : "?"}${serialized}`;
4578
- };
4579
- var attachCallback = (loginUrl, callback) => {
4580
- if (!callback) return loginUrl;
4581
- try {
4582
- const base = typeof window !== "undefined" ? window.location.origin : "http://localhost";
4583
- const parsed = new URL(loginUrl, base);
4584
- if (!parsed.searchParams.has("callback") && !parsed.searchParams.has("redirectUri")) {
4585
- parsed.searchParams.set("callback", callback);
4537
+ const reload = (0, import_react8.useCallback)(async (options = {}) => {
4538
+ if (!resolvedAppType) {
4539
+ setState({
4540
+ data: null,
4541
+ loading: false,
4542
+ error: createRuntimeError({
4543
+ message: "appType \u4E0D\u80FD\u4E3A\u7A7A",
4544
+ type: "unknown"
4545
+ })
4546
+ });
4547
+ return;
4586
4548
  }
4587
- if (loginUrl.startsWith("http://") || loginUrl.startsWith("https://")) {
4588
- return parsed.toString();
4549
+ setState((prev) => ({ ...prev, loading: true, error: null }));
4550
+ const requestFetch = options.accessToken !== void 0 ? createAuthorizedFetch(resolvedFetch, options.accessToken || null) : authorizedFetch;
4551
+ try {
4552
+ const response = await requestFetch(
4553
+ buildServiceUrl3(
4554
+ servicePrefix,
4555
+ `/openxiangda-api/v1/apps/${encodeURIComponent(
4556
+ resolvedAppType
4557
+ )}/runtime/bootstrap`
4558
+ ),
4559
+ {
4560
+ credentials: "include",
4561
+ headers: { accept: "application/json" }
4562
+ }
4563
+ );
4564
+ const payload = await readJsonPayload(response);
4565
+ if (isRuntimeEnvelopeFailure(response, payload)) {
4566
+ throw createRuntimeHttpError(
4567
+ response,
4568
+ payload,
4569
+ payload?.message || `Runtime bootstrap failed: ${response.status}`
4570
+ );
4571
+ }
4572
+ setState({
4573
+ data: payload?.data || null,
4574
+ loading: false,
4575
+ error: null
4576
+ });
4577
+ } catch (error) {
4578
+ setState({
4579
+ data: null,
4580
+ loading: false,
4581
+ error: normalizeRuntimeError(error)
4582
+ });
4589
4583
  }
4590
- return `${parsed.pathname}${parsed.search}${parsed.hash}`;
4591
- } catch {
4592
- const separator = loginUrl.includes("?") ? "&" : "?";
4593
- return `${loginUrl}${separator}callback=${encodeURIComponent(callback)}`;
4594
- }
4595
- };
4596
- var getCurrentHref4 = () => typeof window === "undefined" ? "" : window.location.href;
4597
- var getCurrentHostname2 = () => typeof window === "undefined" ? "" : window.location.hostname;
4598
- var getRuntimeEnv = (key) => {
4599
- const env = typeof process !== "undefined" ? process.env : void 0;
4600
- return env?.[key];
4601
- };
4602
- var getRecordValue3 = (value, key) => {
4603
- if (!value || typeof value !== "object") return void 0;
4604
- return value[key];
4605
- };
4606
- var isSuccessCode4 = (code) => {
4607
- if (code === void 0 || code === null || code === "") return true;
4608
- const normalized = Number(code);
4609
- return Number.isFinite(normalized) ? normalized === 0 || normalized >= 200 && normalized < 300 : false;
4584
+ }, [authorizedFetch, resolvedAppType, resolvedFetch, servicePrefix]);
4585
+ (0, import_react8.useEffect)(() => {
4586
+ void reload();
4587
+ }, [reload]);
4588
+ const value = (0, import_react8.useMemo)(
4589
+ () => ({
4590
+ ...state,
4591
+ appType: resolvedAppType,
4592
+ servicePrefix,
4593
+ fetchImpl: authorizedFetch,
4594
+ reload,
4595
+ setAccessToken
4596
+ }),
4597
+ [authorizedFetch, reload, resolvedAppType, servicePrefix, state]
4598
+ );
4599
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(OpenXiangdaRuntimeContext.Provider, { value, children });
4610
4600
  };
4611
- var isRuntimeEnvelopeFailure = (response, payload) => {
4612
- const code = getRecordValue3(payload, "code");
4613
- const success = getRecordValue3(payload, "success");
4614
- return !response.ok || success === false || !isSuccessCode4(code);
4601
+ var useOpenXiangda = () => {
4602
+ const context = (0, import_react8.useContext)(OpenXiangdaRuntimeContext);
4603
+ if (!context) {
4604
+ throw new Error("useOpenXiangda must be used inside OpenXiangdaProvider");
4605
+ }
4606
+ return context;
4615
4607
  };
4616
- var isServerErrorCode = (code) => {
4617
- const normalized = Number(code);
4618
- return Number.isFinite(normalized) && normalized >= 500;
4608
+ var useRuntimeBootstrap = () => useOpenXiangda();
4609
+ var OpenXiangdaPageProvider = ({
4610
+ children,
4611
+ page,
4612
+ route,
4613
+ env,
4614
+ message: message7,
4615
+ modal,
4616
+ navigation
4617
+ }) => {
4618
+ const runtime = useOpenXiangda();
4619
+ const context = (0, import_react8.useMemo)(() => {
4620
+ const bootstrap = runtime.data;
4621
+ const app = toRuntimeRecord(bootstrap?.app);
4622
+ const user = toRuntimeRecord(bootstrap?.user);
4623
+ const permissions = bootstrap?.permissions;
4624
+ const tenantId = toStringValue(getRecordValue3(app, "tenantId")) || toStringValue(getRecordValue3(user, "tenantId")) || toStringValue(getRecordValue3(app, "tenantCode")) || "";
4625
+ const appType = runtime.appType || bootstrap?.appType || toStringValue(getRecordValue3(app, "appType"));
4626
+ const routeInfo = buildBrowserRouteInfo(route);
4627
+ return createBrowserPageContext(
4628
+ {
4629
+ app: {
4630
+ ...app,
4631
+ appType,
4632
+ tenantId
4633
+ },
4634
+ page: {
4635
+ id: routeInfo.pathname || "react-spa",
4636
+ code: routeInfo.pathname || "react-spa",
4637
+ name: "OpenXiangda React SPA",
4638
+ type: "react-spa",
4639
+ rendererType: "react-spa",
4640
+ routeKey: routeInfo.pathname || "react-spa",
4641
+ status: "ACTIVE",
4642
+ props: {},
4643
+ route: {},
4644
+ dataSources: [],
4645
+ capabilities: {},
4646
+ buildId: toStringValue(
4647
+ getRecordValue3(bootstrap?.runtime, "activeBuildId")
4648
+ ),
4649
+ ...page
4650
+ },
4651
+ user: {
4652
+ ...user,
4653
+ id: toStringValue(getRecordValue3(user, "id")) || (user.isGuest ? "guest" : "current"),
4654
+ username: toStringValue(getRecordValue3(user, "username")) || toStringValue(getRecordValue3(user, "name")) || (user.isGuest ? "guest" : "current"),
4655
+ tenantId,
4656
+ isGuest: user.isGuest === true || user.userType === "guest" || Boolean(getRecordValue3(user, "publicAccess")),
4657
+ userType: user.userType === "guest" || user.isGuest === true ? "guest" : "normal"
4658
+ },
4659
+ env: {
4660
+ appType,
4661
+ servicePrefix: runtime.servicePrefix,
4662
+ runtimeMode: bootstrap?.runtime?.mode || "react-spa",
4663
+ ...env || {}
4664
+ },
4665
+ permissions: {
4666
+ canView: runtime.error?.type !== "forbidden",
4667
+ hasFullAccess: permissions?.hasFullAccess === true,
4668
+ ...permissions || {}
4669
+ },
4670
+ sdk: {
4671
+ packageName: "openxiangda",
4672
+ supportedBridgeMethods: ["transport.request", "transport.download"]
4673
+ }
4674
+ },
4675
+ {
4676
+ servicePrefix: runtime.servicePrefix,
4677
+ fetchImpl: runtime.fetchImpl,
4678
+ route: routeInfo,
4679
+ message: message7,
4680
+ modal,
4681
+ navigation
4682
+ }
4683
+ );
4684
+ }, [
4685
+ env,
4686
+ message7,
4687
+ modal,
4688
+ navigation,
4689
+ page,
4690
+ route,
4691
+ runtime.appType,
4692
+ runtime.data,
4693
+ runtime.error?.type,
4694
+ runtime.fetchImpl,
4695
+ runtime.servicePrefix
4696
+ ]);
4697
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(PageProvider, { context, children });
4619
4698
  };
4620
- var getRecordString = (value, key) => {
4621
- const result = getRecordValue3(value, key);
4622
- return typeof result === "string" ? result : void 0;
4699
+ var useAppMenus = () => {
4700
+ const runtime = useOpenXiangda();
4701
+ return {
4702
+ ...runtime,
4703
+ data: runtime.data?.menus || []
4704
+ };
4623
4705
  };
4624
- var resolveAppTypeFromLocation = () => {
4625
- if (typeof window === "undefined") return "";
4626
- const segments = window.location.pathname.split("/").filter(Boolean);
4627
- const viewIndex = segments[0] === "view" ? 1 : 0;
4628
- if (segments[viewIndex] === "submit") return segments[viewIndex + 1] || "";
4629
- if (segments[viewIndex] === "preview") return segments[viewIndex + 1] || "";
4630
- return segments[viewIndex] || "";
4706
+ var usePermission = () => {
4707
+ const runtime = useOpenXiangda();
4708
+ return {
4709
+ ...runtime,
4710
+ data: runtime.data?.permissions || null
4711
+ };
4631
4712
  };
4632
-
4633
- // packages/sdk/src/runtime/react/publicAccess.tsx
4634
- init_cjs_shims();
4635
- var import_react9 = require("react");
4636
- var import_jsx_runtime5 = require("react/jsx-runtime");
4637
- var usePublicAccess = (options = {}) => {
4713
+ var useCanAccessRoute = (input) => {
4638
4714
  const runtime = useOpenXiangda();
4639
- const {
4640
- appType: runtimeAppType,
4641
- servicePrefix: runtimeServicePrefix,
4642
- fetchImpl: runtimeFetchImpl,
4643
- reload: reloadRuntime,
4644
- setAccessToken
4645
- } = runtime;
4646
- const {
4647
- appType = runtimeAppType,
4648
- servicePrefix = runtimeServicePrefix,
4649
- fetchImpl = runtimeFetchImpl,
4650
- autoStart = true,
4651
- ...sessionInput
4652
- } = options;
4653
- const [session, setSession] = (0, import_react9.useState)(null);
4654
- const [error, setError] = (0, import_react9.useState)(null);
4655
- const [loading, setLoading] = (0, import_react9.useState)(Boolean(autoStart));
4656
- const sessionInputKey = JSON.stringify(sessionInput);
4657
- const stableSessionInput = (0, import_react9.useMemo)(() => sessionInput, [sessionInputKey]);
4658
- const client = (0, import_react9.useMemo)(
4659
- () => createPublicAccessClient({
4660
- appType,
4661
- servicePrefix,
4662
- fetchImpl
4663
- }),
4664
- [appType, fetchImpl, servicePrefix]
4665
- );
4666
- const startSession = (0, import_react9.useCallback)(
4667
- async (input = {}) => {
4668
- setLoading(true);
4669
- setError(null);
4670
- try {
4671
- const data = await client.startSession({
4672
- ...stableSessionInput,
4673
- ...input,
4674
- ticket: input.ticket || stableSessionInput.ticket || readTicketFromLocation(),
4675
- path: input.path || stableSessionInput.path || readPathFromLocation()
4715
+ const [state, setState] = (0, import_react8.useState)({
4716
+ data: null,
4717
+ loading: true,
4718
+ error: null
4719
+ });
4720
+ (0, import_react8.useEffect)(() => {
4721
+ let disposed = false;
4722
+ const check = async () => {
4723
+ const permissions = runtime.data?.permissions;
4724
+ if (!runtime.appType || runtime.loading) {
4725
+ setState((prev) => ({ ...prev, loading: runtime.loading }));
4726
+ return;
4727
+ }
4728
+ if (runtime.error && !runtime.data) {
4729
+ const snapshot = toRuntimeErrorSnapshot(runtime.error);
4730
+ setState({
4731
+ data: {
4732
+ appType: runtime.appType,
4733
+ canAccess: false,
4734
+ status: snapshot.status,
4735
+ code: snapshot.code,
4736
+ message: snapshot.message,
4737
+ errorType: snapshot.type,
4738
+ payload: snapshot.payload
4739
+ },
4740
+ loading: false,
4741
+ error: runtime.error
4676
4742
  });
4677
- setSession(data);
4678
- setAccessToken(data.accessToken);
4679
- await reloadRuntime({ accessToken: data.accessToken });
4680
- setLoading(false);
4681
- return data;
4682
- } catch (caught) {
4683
- const nextError = caught instanceof PublicAccessClientError ? caught : new PublicAccessClientError(
4684
- caught instanceof Error ? caught.message : "\u516C\u5F00\u8BBF\u95EE\u4F1A\u8BDD\u521B\u5EFA\u5931\u8D25",
4685
- { payload: caught }
4743
+ return;
4744
+ }
4745
+ if (permissions?.hasFullAccess) {
4746
+ setState({
4747
+ data: { appType: runtime.appType, canAccess: true, permissions },
4748
+ loading: false,
4749
+ error: null
4750
+ });
4751
+ return;
4752
+ }
4753
+ setState((prev) => ({ ...prev, loading: true, error: null }));
4754
+ try {
4755
+ const response = await runtime.fetchImpl(
4756
+ buildServiceUrl3(
4757
+ runtime.servicePrefix,
4758
+ `/openxiangda-api/v1/apps/${encodeURIComponent(
4759
+ runtime.appType
4760
+ )}/runtime/routes/check`
4761
+ ),
4762
+ {
4763
+ method: "POST",
4764
+ credentials: "include",
4765
+ headers: {
4766
+ accept: "application/json",
4767
+ "content-type": "application/json"
4768
+ },
4769
+ body: JSON.stringify(input)
4770
+ }
4686
4771
  );
4687
- setError(nextError);
4688
- setLoading(false);
4689
- throw nextError;
4772
+ const payload = await readJsonPayload(response);
4773
+ const code = payload?.code ?? response.status;
4774
+ const canAccess = Boolean(payload?.data?.canAccess);
4775
+ const errorType = canAccess ? void 0 : classifyRuntimeError(response.status, code);
4776
+ const data = {
4777
+ ...payload?.data || {
4778
+ appType: runtime.appType,
4779
+ canAccess: false
4780
+ },
4781
+ appType: payload?.data?.appType || runtime.appType,
4782
+ canAccess,
4783
+ status: response.status,
4784
+ code,
4785
+ message: payload?.message,
4786
+ errorType,
4787
+ payload
4788
+ };
4789
+ if (!disposed) {
4790
+ const shouldTreatAsError = !response.ok || isServerErrorCode(code);
4791
+ setState({
4792
+ data,
4793
+ loading: false,
4794
+ error: shouldTreatAsError ? createRuntimeHttpError(
4795
+ response,
4796
+ payload,
4797
+ payload?.message || `Route check failed: ${response.status}`
4798
+ ) : null
4799
+ });
4800
+ }
4801
+ } catch (error) {
4802
+ if (!disposed) {
4803
+ setState({
4804
+ data: null,
4805
+ loading: false,
4806
+ error: normalizeRuntimeError(error)
4807
+ });
4808
+ }
4690
4809
  }
4691
- },
4692
- [client, reloadRuntime, setAccessToken, stableSessionInput]
4693
- );
4694
- (0, import_react9.useEffect)(() => {
4695
- if (!autoStart) return;
4696
- void startSession().catch(() => void 0);
4697
- }, [autoStart, startSession]);
4810
+ };
4811
+ void check();
4812
+ return () => {
4813
+ disposed = true;
4814
+ };
4815
+ }, [
4816
+ input.menuCode,
4817
+ input.path,
4818
+ input.routeCode,
4819
+ runtime.appType,
4820
+ runtime.data?.permissions,
4821
+ runtime.fetchImpl,
4822
+ runtime.loading,
4823
+ runtime.servicePrefix
4824
+ ]);
4698
4825
  return {
4699
- loading,
4700
- error,
4701
- session,
4702
- publicAccess: session?.publicAccess || null,
4703
- startSession
4826
+ ...state,
4827
+ canAccess: Boolean(state.data?.canAccess)
4704
4828
  };
4705
4829
  };
4706
- var PublicAccessGate = ({
4830
+ var PermissionBoundary = ({
4707
4831
  children,
4708
4832
  fallback = null,
4709
- errorFallback = null,
4710
- ...options
4833
+ loadingFallback = null,
4834
+ routeCode,
4835
+ menuCode,
4836
+ path
4711
4837
  }) => {
4712
- const state = usePublicAccess(options);
4713
- if (state.loading) return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_jsx_runtime5.Fragment, { children: fallback });
4714
- if (state.error) {
4715
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_jsx_runtime5.Fragment, { children: typeof errorFallback === "function" ? errorFallback(state.error) : errorFallback });
4716
- }
4717
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_jsx_runtime5.Fragment, { children });
4718
- };
4719
- var readTicketFromLocation = () => {
4720
- if (typeof window === "undefined") return void 0;
4721
- return new URLSearchParams(window.location.search).get("ticket") || void 0;
4722
- };
4723
- var readPathFromLocation = () => typeof window === "undefined" ? void 0 : window.location.pathname;
4724
-
4725
- // packages/sdk/src/runtime/host/index.ts
4726
- init_cjs_shims();
4727
-
4728
- // packages/sdk/src/runtime/host/browserHost.ts
4729
- init_cjs_shims();
4730
- var NAMESPACE_ROOT_CLASS2 = "sy-app-workspace";
4731
- var defaultModuleLoader = (url2) => import(
4732
- /* @vite-ignore */
4733
- url2
4734
- );
4735
- var trimTrailingSlash = (value) => value.replace(/\/+$/, "");
4736
- var getDefaultServicePrefix = () => typeof window !== "undefined" ? trimTrailingSlash(window.__OPENXIANGDA_SERVICE_PREFIX__ || "/service") : "/service";
4737
- var joinServicePath = (servicePrefix, path) => {
4738
- if (/^https?:\/\//i.test(path)) return path;
4739
- const normalizedPrefix = trimTrailingSlash(servicePrefix || "/service");
4740
- if (path.startsWith(normalizedPrefix)) return path;
4741
- return `${normalizedPrefix}${path.startsWith("/") ? path : `/${path}`}`;
4742
- };
4743
- var appendQuery = (url2, query) => {
4744
- if (!query) return url2;
4745
- return `${url2}${url2.includes("?") ? "&" : "?"}${query}`;
4746
- };
4747
- var normalizeMethod2 = (method4) => {
4748
- const value = String(method4 || "get").toUpperCase();
4749
- return ["GET", "POST", "PUT", "DELETE", "PATCH"].includes(value) ? value : "GET";
4750
- };
4751
- var normalizeCssIsolation2 = (value) => {
4752
- if (value === "namespace" || value === "shadow" || value === "none") {
4753
- return value;
4754
- }
4755
- return "none";
4756
- };
4757
- var normalizeEnvelopeCode2 = (value, fallback) => {
4758
- if (value === void 0 || value === null || value === "") return fallback;
4759
- const normalized = Number(value);
4760
- return Number.isFinite(normalized) ? normalized : String(value);
4761
- };
4762
- var isSuccessCode5 = (value) => {
4763
- if (value === void 0 || value === null || value === "") return true;
4764
- const normalized = Number(value);
4765
- return Number.isFinite(normalized) ? normalized === 0 || normalized >= 200 && normalized < 300 : false;
4766
- };
4767
- var parseJsonResponse = async (response) => {
4768
- const payload = await response.json().catch(() => null);
4769
- if (!response.ok) {
4770
- const message7 = payload && typeof payload === "object" ? payload.message || payload.error || response.statusText : response.statusText;
4771
- throw new Error(message7 || "\u8BF7\u6C42\u5931\u8D25");
4838
+ const runtime = useOpenXiangda();
4839
+ const access = useCanAccessRoute({ routeCode, menuCode, path });
4840
+ const fallbackState = createPermissionFallbackState(access, runtime);
4841
+ if (access.loading) {
4842
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_jsx_runtime4.Fragment, { children: renderBoundaryFallback(loadingFallback, fallbackState) });
4772
4843
  }
4773
- if (payload && typeof payload === "object" && "code" in payload) {
4774
- const code = normalizeEnvelopeCode2(payload.code, response.status);
4775
- return {
4776
- code,
4777
- success: payload.success !== false && isSuccessCode5(code),
4778
- message: payload.message,
4779
- result: payload.result ?? payload.data ?? null,
4780
- data: payload.data,
4781
- raw: payload
4782
- };
4844
+ if (!access.canAccess) {
4845
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_jsx_runtime4.Fragment, { children: renderBoundaryFallback(fallback, fallbackState) });
4783
4846
  }
4784
- return {
4785
- code: response.status,
4786
- success: response.ok,
4787
- message: response.statusText,
4788
- result: payload,
4789
- data: payload,
4790
- raw: payload
4791
- };
4847
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_jsx_runtime4.Fragment, { children });
4792
4848
  };
4793
- var createBrowserPageBridge = (options = {}) => {
4794
- const servicePrefix = options.servicePrefix || getDefaultServicePrefix();
4795
- const fetchImpl = createBoundFetch(options.fetchImpl);
4796
- const request = async (payload) => {
4797
- if (!payload?.path) {
4798
- throw new Error("transport.request \u9700\u8981 path");
4799
- }
4800
- const url2 = appendQuery(joinServicePath(servicePrefix, payload.path), payload.query);
4801
- const headers = new Headers(payload.headers);
4802
- let body;
4803
- if (payload.body !== void 0) {
4804
- if (payload.body instanceof FormData) {
4805
- body = payload.body;
4806
- } else {
4807
- headers.set("Content-Type", headers.get("Content-Type") || "application/json");
4808
- body = JSON.stringify(payload.body);
4849
+ var useRuntimeAuth = () => {
4850
+ const runtime = useOpenXiangda();
4851
+ const resolveLoginUrl2 = (0, import_react8.useCallback)(
4852
+ async (options = {}) => {
4853
+ const redirectUri = options.redirectUri || getCurrentHref4();
4854
+ const domain = options.domain || getCurrentHostname2();
4855
+ const appTenantId = getRecordString(runtime.data?.app, "tenantId");
4856
+ try {
4857
+ const statusUrl = withQuery(
4858
+ buildServiceUrl3(runtime.servicePrefix, "/api/sso/status"),
4859
+ { domain }
4860
+ );
4861
+ const statusResponse = await runtime.fetchImpl(statusUrl, {
4862
+ credentials: "include",
4863
+ headers: { accept: "application/json" }
4864
+ });
4865
+ const statusPayload = await readJsonPayload(statusResponse);
4866
+ const sso = statusPayload?.data || {};
4867
+ const enabled = Boolean(sso.enabled);
4868
+ const shouldUseSso = Boolean(
4869
+ enabled && (sso.forceLogin ?? sso.autoRedirect ?? true)
4870
+ );
4871
+ if (shouldUseSso) {
4872
+ const loginUrlResponse = await runtime.fetchImpl(
4873
+ withQuery(buildServiceUrl3(runtime.servicePrefix, "/api/sso/login-url"), {
4874
+ domain,
4875
+ redirectUri,
4876
+ ...sso.tenantId || appTenantId ? { tenantId: String(sso.tenantId || appTenantId) } : {},
4877
+ ...sso.protocol ? { protocol: String(sso.protocol) } : {}
4878
+ }),
4879
+ {
4880
+ credentials: "include",
4881
+ headers: { accept: "application/json" }
4882
+ }
4883
+ );
4884
+ const loginUrlPayload = await readJsonPayload(loginUrlResponse);
4885
+ const loginUrl = loginUrlPayload?.data?.loginUrl || loginUrlPayload?.data?.url || loginUrlPayload?.loginUrl || loginUrlPayload?.url;
4886
+ if (loginUrl) return String(loginUrl);
4887
+ }
4888
+ } catch {
4809
4889
  }
4810
- }
4811
- const response = await fetchImpl(url2, {
4812
- method: normalizeMethod2(payload.method),
4813
- headers,
4814
- body,
4815
- credentials: "include"
4816
- });
4817
- return parseJsonResponse(response);
4818
- };
4819
- const download = async (payload) => {
4820
- if (!payload?.path) {
4821
- throw new Error("transport.download \u9700\u8981 path");
4822
- }
4823
- const url2 = appendQuery(joinServicePath(servicePrefix, payload.path), payload.query);
4824
- const headers = new Headers(payload.headers);
4825
- let body;
4826
- if (payload.body !== void 0) {
4827
- if (payload.body instanceof FormData) {
4828
- body = payload.body;
4829
- } else {
4830
- headers.set("Content-Type", headers.get("Content-Type") || "application/json");
4831
- body = JSON.stringify(payload.body);
4890
+ const configuredLoginUrl = options.loginUrl || getRuntimeEnv("OPENXIANGDA_LOGIN_URL") || getRuntimeEnv("VITE_OPENXIANGDA_LOGIN_URL") || getRuntimeEnv("APP_LOGIN_URL") || getRuntimeEnv("VITE_APP_LOGIN_URL") || getRuntimeEnv("VITE_BATH_AUTH_URL") || getRuntimeEnv("BATH_AUTH_URL");
4891
+ if (configuredLoginUrl) {
4892
+ return attachCallback(configuredLoginUrl, redirectUri);
4893
+ }
4894
+ return attachCallback("/platform/login", redirectUri);
4895
+ },
4896
+ [runtime.data?.app, runtime.fetchImpl, runtime.servicePrefix]
4897
+ );
4898
+ const redirectToLogin = (0, import_react8.useCallback)(
4899
+ async (options = {}) => {
4900
+ const loginUrl = await resolveLoginUrl2(options);
4901
+ if (typeof window !== "undefined") {
4902
+ if (options.replace === false) {
4903
+ window.location.assign(loginUrl);
4904
+ } else {
4905
+ window.location.replace(loginUrl);
4906
+ }
4907
+ }
4908
+ return loginUrl;
4909
+ },
4910
+ [resolveLoginUrl2]
4911
+ );
4912
+ const logout = (0, import_react8.useCallback)(async () => {
4913
+ const response = await runtime.fetchImpl(
4914
+ buildServiceUrl3(runtime.servicePrefix, "/api/auth/logout"),
4915
+ {
4916
+ method: "POST",
4917
+ credentials: "include",
4918
+ headers: { accept: "application/json" }
4832
4919
  }
4920
+ );
4921
+ const payload = await readJsonPayload(response);
4922
+ if (isRuntimeEnvelopeFailure(response, payload)) {
4923
+ throw createRuntimeHttpError(
4924
+ response,
4925
+ payload,
4926
+ payload?.message || `Logout failed: ${response.status}`
4927
+ );
4833
4928
  }
4834
- const response = await fetchImpl(url2, {
4835
- method: normalizeMethod2(payload.method),
4836
- headers,
4837
- body,
4838
- credentials: "include"
4839
- });
4840
- if (!response.ok) {
4841
- throw new Error(response.statusText || "\u4E0B\u8F7D\u5931\u8D25");
4842
- }
4843
- return {
4844
- blob: await response.blob(),
4845
- contentType: response.headers.get("content-type") || void 0,
4846
- fileName: response.headers.get("content-disposition") || void 0,
4847
- headers: Object.fromEntries(response.headers.entries())
4848
- };
4849
- };
4929
+ return payload;
4930
+ }, [runtime.fetchImpl, runtime.servicePrefix]);
4931
+ const logoutAndRedirect = (0, import_react8.useCallback)(
4932
+ async (options = {}) => {
4933
+ try {
4934
+ await logout();
4935
+ } catch (error) {
4936
+ if (options.continueOnError === false) throw error;
4937
+ }
4938
+ return redirectToLogin(options);
4939
+ },
4940
+ [logout, redirectToLogin]
4941
+ );
4850
4942
  return {
4851
- invoke: async (method4, payload) => {
4852
- if (method4 === "transport.request") return await request(payload);
4853
- if (method4 === "transport.download") return await download(payload);
4854
- throw new Error(`\u4E0D\u652F\u6301\u7684 bridge \u65B9\u6CD5: ${method4}`);
4855
- }
4943
+ logout,
4944
+ logoutAndRedirect,
4945
+ redirectToLogin,
4946
+ resolveLoginUrl: resolveLoginUrl2
4856
4947
  };
4857
4948
  };
4858
- var createBrowserPageContext = (bootstrap, options = {}) => {
4859
- const route = {
4860
- pathname: options.route?.pathname || (typeof window !== "undefined" ? window.location.pathname : ""),
4861
- fullPath: options.route?.fullPath || (typeof window !== "undefined" ? `${window.location.pathname}${window.location.search}${window.location.hash}` : ""),
4862
- params: options.route?.params || {},
4863
- query: options.route?.query || {},
4864
- hash: options.route?.hash || (typeof window !== "undefined" ? window.location.hash : "")
4865
- };
4866
- return {
4867
- protocolVersion: bootstrap.asset?.protocolVersion || "1.0",
4868
- app: bootstrap.app,
4869
- page: {
4870
- ...bootstrap.page,
4871
- version: bootstrap.asset?.version || bootstrap.page.version,
4872
- buildId: bootstrap.asset?.buildId || bootstrap.page.buildId
4873
- },
4874
- user: bootstrap.user,
4875
- route,
4876
- env: bootstrap.env || {},
4877
- permissions: {
4878
- canView: bootstrap.permissions?.canView !== false,
4879
- hasFullAccess: bootstrap.permissions?.hasFullAccess === true,
4880
- ...bootstrap.permissions || {}
4881
- },
4882
- capabilities: Array.from(
4883
- /* @__PURE__ */ new Set([
4884
- "navigation",
4885
- "ui.message",
4886
- "ui.modal",
4887
- "transport.request",
4888
- "transport.download",
4889
- ...bootstrap.sdk?.supportedBridgeMethods || []
4890
- ])
4891
- ),
4892
- ui: {
4893
- message: {
4894
- success: (text) => options.message?.success?.(text),
4895
- error: (text) => options.message?.error?.(text),
4896
- warning: (text) => options.message?.warning?.(text),
4897
- info: (text) => options.message?.info?.(text),
4898
- loading: (text) => options.message?.loading?.(text) || (() => void 0)
4899
- },
4900
- modal: {
4901
- confirm: (input) => options.modal?.confirm?.(input) || Promise.resolve(false)
4902
- }
4903
- },
4904
- navigation: {
4905
- pushPage: (pageKey, query) => options.navigation?.pushPage?.(pageKey, query),
4906
- replacePage: (pageKey, query) => options.navigation?.replacePage?.(pageKey, query),
4907
- pushRoute: (routeValue, query) => options.navigation?.pushRoute?.(routeValue, query),
4908
- replaceRoute: (routeValue, query) => options.navigation?.replaceRoute?.(routeValue, query),
4909
- updateQuery: (query) => options.navigation?.updateQuery?.(query),
4910
- setHash: (hash) => options.navigation?.setHash?.(hash),
4911
- back: () => options.navigation?.back?.()
4912
- },
4913
- bridge: createBrowserPageBridge(options),
4914
- sdk: {
4915
- ...bootstrap.sdk,
4916
- supportedBridgeMethods: ["transport.request", "transport.download"]
4949
+ var buildServiceUrl3 = (servicePrefix, path) => {
4950
+ const prefix2 = servicePrefix.endsWith("/") ? servicePrefix.slice(0, -1) : servicePrefix;
4951
+ const suffix = path.startsWith("/") ? path : `/${path}`;
4952
+ return `${prefix2}${suffix}`;
4953
+ };
4954
+ var createAuthorizedFetch = (baseFetch, accessToken) => {
4955
+ if (!accessToken) return baseFetch;
4956
+ return ((input, init = {}) => {
4957
+ const headers = new Headers(init.headers || {});
4958
+ if (!headers.has("authorization")) {
4959
+ headers.set("authorization", `Bearer ${accessToken}`);
4917
4960
  }
4918
- };
4961
+ return baseFetch(input, {
4962
+ ...init,
4963
+ headers
4964
+ });
4965
+ });
4919
4966
  };
4920
- var resolveRuntimeAssets = async (bootstrap, fetchImpl = fetch) => {
4921
- const boundFetch = createBoundFetch(fetchImpl);
4922
- const fallback = {
4923
- entryUrl: bootstrap.runtimeAssets?.entryUrl || bootstrap.asset?.entryUrl || "",
4924
- cssUrls: bootstrap.runtimeAssets?.cssUrls || bootstrap.asset?.cssAssets || [],
4925
- jsUrls: bootstrap.runtimeAssets?.jsUrls || bootstrap.asset?.jsAssets || [],
4926
- cssIsolation: normalizeCssIsolation2(
4927
- bootstrap.runtimeAssets?.cssIsolation || bootstrap.page.capabilities?.cssIsolation
4928
- )
4929
- };
4930
- if (bootstrap.runtimeAssets?.entryUrl || !bootstrap.asset?.manifestUrl) {
4931
- return fallback;
4932
- }
4967
+ var readJsonPayload = async (response) => {
4933
4968
  try {
4934
- const response = await boundFetch(bootstrap.asset.manifestUrl, {
4935
- credentials: "omit"
4936
- });
4937
- if (!response.ok) return fallback;
4938
- const manifest = await response.json();
4939
- const base = bootstrap.asset.manifestUrl;
4940
- const resolveUrl = (value) => new URL(value, base).toString();
4941
- return {
4942
- entryUrl: manifest?.entry?.url ? resolveUrl(manifest.entry.url) : fallback.entryUrl,
4943
- cssUrls: Array.isArray(manifest?.assets?.css) ? manifest.assets.css.map(resolveUrl) : fallback.cssUrls,
4944
- jsUrls: Array.isArray(manifest?.assets?.js) ? manifest.assets.js.map(resolveUrl) : fallback.jsUrls,
4945
- cssIsolation: normalizeCssIsolation2(
4946
- manifest?.style?.isolation || fallback.cssIsolation
4947
- )
4948
- };
4969
+ return await response.json();
4949
4970
  } catch {
4950
- return fallback;
4971
+ return null;
4951
4972
  }
4952
4973
  };
4953
- var fetchBrowserRuntimeBootstrap = async ({
4954
- appType,
4955
- bootstrapPath,
4956
- fetchImpl = fetch,
4957
- pageKey,
4958
- servicePrefix
4959
- }) => {
4960
- if (!appType) {
4961
- throw new Error("appType \u7F3A\u5931");
4974
+ var createRuntimeHttpError = (response, payload, fallbackMessage) => createRuntimeError({
4975
+ type: classifyRuntimeError(
4976
+ response.status,
4977
+ getRecordValue3(payload, "code")
4978
+ ),
4979
+ status: response.status,
4980
+ code: getRecordValue3(payload, "code"),
4981
+ message: getRecordValue3(payload, "message") || fallbackMessage,
4982
+ payload
4983
+ });
4984
+ var createRuntimeError = (snapshot) => new RuntimeHttpError({
4985
+ ...snapshot,
4986
+ type: snapshot.type || "unknown",
4987
+ message: snapshot.message || "Runtime request failed"
4988
+ });
4989
+ var normalizeRuntimeError = (error) => {
4990
+ if (error instanceof RuntimeHttpError) return error;
4991
+ if (error instanceof Error) {
4992
+ const runtimeError = error;
4993
+ runtimeError.type = runtimeError.type || classifyRuntimeError(runtimeError.status, runtimeError.code);
4994
+ return runtimeError;
4962
4995
  }
4963
- if (!pageKey) {
4964
- throw new Error("pageKey \u7F3A\u5931");
4996
+ return createRuntimeError({
4997
+ type: "unknown",
4998
+ message: String(error)
4999
+ });
5000
+ };
5001
+ var toRuntimeErrorSnapshot = (error) => ({
5002
+ type: error.type || classifyRuntimeError(error.status, error.code),
5003
+ status: error.status,
5004
+ code: error.code,
5005
+ message: error.message || "Runtime request failed",
5006
+ payload: error.payload
5007
+ });
5008
+ var classifyRuntimeError = (status, code) => {
5009
+ const normalizedCode = typeof code === "string" ? Number(code) : code;
5010
+ if (status === 401 || normalizedCode === 401) return "unauthenticated";
5011
+ if (status === 403 || normalizedCode === 403) return "forbidden";
5012
+ if (typeof code === "string") {
5013
+ const normalizedText = code.toUpperCase();
5014
+ if (normalizedText.includes("DENIED") || normalizedText.includes("FORBIDDEN")) {
5015
+ return "forbidden";
5016
+ }
5017
+ if (normalizedText.includes("UNAUTH") || normalizedText.includes("LOGIN")) {
5018
+ return "unauthenticated";
5019
+ }
4965
5020
  }
4966
- const path = bootstrapPath || `/openxiangda-api/v1/apps/${encodeURIComponent(
4967
- appType
4968
- )}/pages/${encodeURIComponent(pageKey)}/bootstrap`;
4969
- const boundFetch = createBoundFetch(fetchImpl);
4970
- const response = await boundFetch(
4971
- joinServicePath(servicePrefix || getDefaultServicePrefix(), path),
4972
- {
4973
- method: "GET",
4974
- credentials: "include"
5021
+ if (!status && !normalizedCode) return "network";
5022
+ return "unknown";
5023
+ };
5024
+ var createPermissionFallbackState = (access, runtime) => {
5025
+ const error = access.error || runtime.error;
5026
+ const accessData = access.data;
5027
+ const errorType = accessData?.errorType || error?.type || (!access.canAccess ? "forbidden" : "unknown");
5028
+ return {
5029
+ access,
5030
+ runtime,
5031
+ error,
5032
+ errorType,
5033
+ status: accessData?.status || error?.status,
5034
+ code: accessData?.code || error?.code,
5035
+ message: accessData?.message || error?.message || (errorType === "unauthenticated" ? "\u5F53\u524D\u8D26\u53F7\u5C1A\u672A\u767B\u5F55" : "\u5F53\u524D\u8D26\u53F7\u6CA1\u6709\u8BBF\u95EE\u6743\u9650")
5036
+ };
5037
+ };
5038
+ var renderBoundaryFallback = (fallback, state) => typeof fallback === "function" ? fallback(state) : fallback;
5039
+ var withQuery = (url2, params) => {
5040
+ const query = new URLSearchParams();
5041
+ Object.entries(params).forEach(([key, value]) => {
5042
+ if (value === void 0 || value === "") return;
5043
+ query.set(key, String(value));
5044
+ });
5045
+ const serialized = query.toString();
5046
+ if (!serialized) return url2;
5047
+ return `${url2}${url2.includes("?") ? "&" : "?"}${serialized}`;
5048
+ };
5049
+ var attachCallback = (loginUrl, callback) => {
5050
+ if (!callback) return loginUrl;
5051
+ try {
5052
+ const base = typeof window !== "undefined" ? window.location.origin : "http://localhost";
5053
+ const parsed = new URL(loginUrl, base);
5054
+ if (!parsed.searchParams.has("callback") && !parsed.searchParams.has("redirectUri")) {
5055
+ parsed.searchParams.set("callback", callback);
4975
5056
  }
4976
- );
4977
- const payload = await response.json().catch(() => null);
4978
- if (!response.ok) {
4979
- throw new Error(payload?.message || response.statusText || "\u83B7\u53D6\u9875\u9762\u8FD0\u884C\u65F6\u5931\u8D25");
4980
- }
4981
- if (payload && typeof payload === "object" && ("code" in payload || "success" in payload)) {
4982
- if (payload.success === false || payload.code && Number(payload.code) !== 200) {
4983
- throw new Error(payload.message || "\u83B7\u53D6\u9875\u9762\u8FD0\u884C\u65F6\u5931\u8D25");
5057
+ if (loginUrl.startsWith("http://") || loginUrl.startsWith("https://")) {
5058
+ return parsed.toString();
4984
5059
  }
4985
- return payload.data || {};
5060
+ return `${parsed.pathname}${parsed.search}${parsed.hash}`;
5061
+ } catch {
5062
+ const separator = loginUrl.includes("?") ? "&" : "?";
5063
+ return `${loginUrl}${separator}callback=${encodeURIComponent(callback)}`;
4986
5064
  }
4987
- return payload || {};
4988
5065
  };
4989
- var resolveBrowserRuntimeRoute = async ({
4990
- appType,
4991
- fetchImpl = fetch,
4992
- path,
4993
- resolvePath,
4994
- search,
4995
- servicePrefix
4996
- }) => {
4997
- if (!appType) {
4998
- throw new Error("appType \u7F3A\u5931");
4999
- }
5000
- const currentPath = path || (typeof window !== "undefined" ? window.location.pathname : "/");
5001
- const currentSearch = search !== void 0 ? search : typeof window !== "undefined" ? window.location.search : "";
5002
- const endpoint = resolvePath || `/openxiangda-api/v1/apps/${encodeURIComponent(
5003
- appType
5004
- )}/runtime/routes/resolve`;
5005
- const boundFetch = createBoundFetch(fetchImpl);
5006
- const response = await boundFetch(
5007
- joinServicePath(servicePrefix || getDefaultServicePrefix(), endpoint),
5008
- {
5009
- method: "POST",
5010
- credentials: "include",
5011
- headers: {
5012
- "Content-Type": "application/json"
5013
- },
5014
- body: JSON.stringify({
5015
- path: currentPath,
5016
- search: currentSearch
5017
- })
5018
- }
5019
- );
5020
- const parsed = await parseJsonResponse(response);
5021
- if (!parsed.success || !parsed.result) {
5022
- throw new Error(parsed.message || "\u89E3\u6790\u8FD0\u884C\u65F6\u8DEF\u7531\u5931\u8D25");
5023
- }
5024
- return parsed.result;
5066
+ var getCurrentHref4 = () => typeof window === "undefined" ? "" : window.location.href;
5067
+ var getCurrentHostname2 = () => typeof window === "undefined" ? "" : window.location.hostname;
5068
+ var getRuntimeEnv = (key) => {
5069
+ const env = typeof process !== "undefined" ? process.env : void 0;
5070
+ return env?.[key];
5025
5071
  };
5026
- var loadRuntimeScriptModules = async (jsUrls = [], moduleLoader = defaultModuleLoader) => {
5027
- const urls = Array.from(
5028
- new Set(jsUrls.map((url2) => String(url2 || "").trim()).filter(Boolean))
5029
- );
5030
- for (const url2 of urls) {
5031
- await moduleLoader(url2);
5032
- }
5072
+ var getRecordValue3 = (value, key) => {
5073
+ if (!value || typeof value !== "object") return void 0;
5074
+ return value[key];
5033
5075
  };
5034
- var loadCustomPageModule = async (entryUrl, moduleLoader = defaultModuleLoader, jsUrls = []) => {
5035
- if (!entryUrl) {
5036
- throw new Error("\u4EE3\u7801\u9875\u9762\u7F3A\u5C11 entryUrl");
5037
- }
5038
- await loadRuntimeScriptModules(
5039
- jsUrls.filter((url2) => url2 && url2 !== entryUrl),
5040
- moduleLoader
5041
- );
5042
- const loaded = await moduleLoader(entryUrl);
5043
- return loaded?.default && (loaded.default.mount || loaded.default.unmount) ? { ...loaded.default, ...loaded } : loaded || {};
5076
+ var toRuntimeRecord = (value) => {
5077
+ return value && typeof value === "object" ? { ...value } : {};
5044
5078
  };
5045
- var mountCustomPageRuntime = async ({
5046
- assets,
5047
- container,
5048
- context,
5049
- module: module2
5050
- }) => {
5051
- const cssIsolation = normalizeCssIsolation2(assets?.cssIsolation);
5052
- const mountedLinks = [];
5053
- container.innerHTML = "";
5054
- container.classList.toggle(NAMESPACE_ROOT_CLASS2, cssIsolation === "namespace");
5055
- (assets?.cssUrls || []).forEach((href) => {
5056
- const link = document.createElement("link");
5057
- link.rel = "stylesheet";
5058
- link.href = href;
5059
- link.setAttribute("data-openxiangda-runtime-style", href);
5060
- document.head.appendChild(link);
5061
- mountedLinks.push(link);
5079
+ var toStringValue = (value) => {
5080
+ if (value === void 0 || value === null) return "";
5081
+ return String(value);
5082
+ };
5083
+ var parseBrowserQuery = () => {
5084
+ if (typeof window === "undefined") return {};
5085
+ const query = {};
5086
+ const params = new URLSearchParams(window.location.search);
5087
+ params.forEach((value, key) => {
5088
+ const currentValue = query[key];
5089
+ if (currentValue === void 0) {
5090
+ query[key] = value;
5091
+ return;
5092
+ }
5093
+ query[key] = Array.isArray(currentValue) ? [...currentValue, value] : [currentValue, value];
5062
5094
  });
5063
- await module2.mount?.(container, context);
5064
- return async () => {
5065
- await module2.unmount?.(container, context);
5066
- mountedLinks.forEach((link) => link.remove());
5067
- container.classList.remove(NAMESPACE_ROOT_CLASS2);
5068
- container.innerHTML = "";
5095
+ return query;
5096
+ };
5097
+ var buildBrowserRouteInfo = (route) => {
5098
+ const pathname = route?.pathname || (typeof window !== "undefined" ? window.location.pathname : "");
5099
+ const search = typeof window !== "undefined" ? window.location.search : "";
5100
+ const hash = route?.hash || (typeof window !== "undefined" ? window.location.hash : "");
5101
+ return {
5102
+ pathname,
5103
+ fullPath: route?.fullPath || (typeof window !== "undefined" ? `${pathname}${search}${hash}` : pathname),
5104
+ params: route?.params || {},
5105
+ query: route?.query || parseBrowserQuery(),
5106
+ hash
5069
5107
  };
5070
5108
  };
5071
- var mountBrowserPageRuntime = async (options) => {
5072
- const bootstrap = await fetchBrowserRuntimeBootstrap({
5073
- appType: options.appType,
5074
- pageKey: options.pageKey,
5075
- bootstrapPath: options.bootstrapPath,
5076
- fetchImpl: options.fetchImpl,
5077
- servicePrefix: options.servicePrefix
5078
- });
5079
- if (bootstrap.permissions?.canView === false) {
5080
- throw new Error(String(bootstrap.permissions.message || "\u60A8\u6CA1\u6709\u6743\u9650\u8BBF\u95EE\u8BE5\u9875\u9762"));
5081
- }
5082
- const assets = await resolveRuntimeAssets(bootstrap, options.fetchImpl || fetch);
5083
- if (!assets.entryUrl) {
5084
- throw new Error("\u4EE3\u7801\u9875\u9762\u7F3A\u5C11 entryUrl");
5085
- }
5086
- const context = createBrowserPageContext(bootstrap, options);
5087
- const module2 = await loadCustomPageModule(
5088
- assets.entryUrl,
5089
- options.moduleLoader,
5090
- assets.jsUrls
5109
+ var isSuccessCode5 = (code) => {
5110
+ if (code === void 0 || code === null || code === "") return true;
5111
+ const normalized = Number(code);
5112
+ return Number.isFinite(normalized) ? normalized === 0 || normalized >= 200 && normalized < 300 : false;
5113
+ };
5114
+ var isRuntimeEnvelopeFailure = (response, payload) => {
5115
+ const code = getRecordValue3(payload, "code");
5116
+ const success = getRecordValue3(payload, "success");
5117
+ return !response.ok || success === false || !isSuccessCode5(code);
5118
+ };
5119
+ var isServerErrorCode = (code) => {
5120
+ const normalized = Number(code);
5121
+ return Number.isFinite(normalized) && normalized >= 500;
5122
+ };
5123
+ var getRecordString = (value, key) => {
5124
+ const result = getRecordValue3(value, key);
5125
+ return typeof result === "string" ? result : void 0;
5126
+ };
5127
+ var resolveAppTypeFromLocation = () => {
5128
+ if (typeof window === "undefined") return "";
5129
+ const segments = window.location.pathname.split("/").filter(Boolean);
5130
+ const viewIndex = segments[0] === "view" ? 1 : 0;
5131
+ if (segments[viewIndex] === "submit") return segments[viewIndex + 1] || "";
5132
+ if (segments[viewIndex] === "preview") return segments[viewIndex + 1] || "";
5133
+ return segments[viewIndex] || "";
5134
+ };
5135
+
5136
+ // packages/sdk/src/runtime/react/publicAccess.tsx
5137
+ init_cjs_shims();
5138
+ var import_react9 = require("react");
5139
+ var import_jsx_runtime5 = require("react/jsx-runtime");
5140
+ var usePublicAccess = (options = {}) => {
5141
+ const runtime = useOpenXiangda();
5142
+ const {
5143
+ appType: runtimeAppType,
5144
+ servicePrefix: runtimeServicePrefix,
5145
+ fetchImpl: runtimeFetchImpl,
5146
+ reload: reloadRuntime,
5147
+ setAccessToken
5148
+ } = runtime;
5149
+ const {
5150
+ appType = runtimeAppType,
5151
+ servicePrefix = runtimeServicePrefix,
5152
+ fetchImpl = runtimeFetchImpl,
5153
+ autoStart = true,
5154
+ ...sessionInput
5155
+ } = options;
5156
+ const [session, setSession] = (0, import_react9.useState)(null);
5157
+ const [error, setError] = (0, import_react9.useState)(null);
5158
+ const [loading, setLoading] = (0, import_react9.useState)(Boolean(autoStart));
5159
+ const sessionInputKey = JSON.stringify(sessionInput);
5160
+ const stableSessionInput = (0, import_react9.useMemo)(() => sessionInput, [sessionInputKey]);
5161
+ const client = (0, import_react9.useMemo)(
5162
+ () => createPublicAccessClient({
5163
+ appType,
5164
+ servicePrefix,
5165
+ fetchImpl
5166
+ }),
5167
+ [appType, fetchImpl, servicePrefix]
5091
5168
  );
5092
- const cleanup2 = await mountCustomPageRuntime({
5093
- assets,
5094
- container: options.container,
5095
- context,
5096
- module: module2
5097
- });
5169
+ const startSession = (0, import_react9.useCallback)(
5170
+ async (input = {}) => {
5171
+ setLoading(true);
5172
+ setError(null);
5173
+ try {
5174
+ const data = await client.startSession({
5175
+ ...stableSessionInput,
5176
+ ...input,
5177
+ ticket: input.ticket || stableSessionInput.ticket || readTicketFromLocation(),
5178
+ path: input.path || stableSessionInput.path || readPathFromLocation()
5179
+ });
5180
+ setSession(data);
5181
+ setAccessToken(data.accessToken);
5182
+ await reloadRuntime({ accessToken: data.accessToken });
5183
+ setLoading(false);
5184
+ return data;
5185
+ } catch (caught) {
5186
+ const nextError = caught instanceof PublicAccessClientError ? caught : new PublicAccessClientError(
5187
+ caught instanceof Error ? caught.message : "\u516C\u5F00\u8BBF\u95EE\u4F1A\u8BDD\u521B\u5EFA\u5931\u8D25",
5188
+ { payload: caught }
5189
+ );
5190
+ setError(nextError);
5191
+ setLoading(false);
5192
+ throw nextError;
5193
+ }
5194
+ },
5195
+ [client, reloadRuntime, setAccessToken, stableSessionInput]
5196
+ );
5197
+ (0, import_react9.useEffect)(() => {
5198
+ if (!autoStart) return;
5199
+ void startSession().catch(() => void 0);
5200
+ }, [autoStart, startSession]);
5098
5201
  return {
5099
- bootstrap,
5100
- assets,
5101
- context,
5102
- module: module2,
5103
- cleanup: cleanup2
5202
+ loading,
5203
+ error,
5204
+ session,
5205
+ publicAccess: session?.publicAccess || null,
5206
+ startSession
5104
5207
  };
5105
5208
  };
5209
+ var PublicAccessGate = ({
5210
+ children,
5211
+ fallback = null,
5212
+ errorFallback = null,
5213
+ ...options
5214
+ }) => {
5215
+ const state = usePublicAccess(options);
5216
+ if (state.loading) return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_jsx_runtime5.Fragment, { children: fallback });
5217
+ if (state.error) {
5218
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_jsx_runtime5.Fragment, { children: typeof errorFallback === "function" ? errorFallback(state.error) : errorFallback });
5219
+ }
5220
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_jsx_runtime5.Fragment, { children });
5221
+ };
5222
+ var readTicketFromLocation = () => {
5223
+ if (typeof window === "undefined") return void 0;
5224
+ return new URLSearchParams(window.location.search).get("ticket") || void 0;
5225
+ };
5226
+ var readPathFromLocation = () => typeof window === "undefined" ? void 0 : window.location.pathname;
5227
+
5228
+ // packages/sdk/src/runtime/host/index.ts
5229
+ init_cjs_shims();
5106
5230
 
5107
5231
  // packages/sdk/src/runtime/host/builtinRouteRenderer.tsx
5108
5232
  init_cjs_shims();