openxiangda 1.0.89 → 1.0.91

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,1614 @@ 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]
4140
- );
4141
- const [accessToken, setAccessTokenState] = (0, import_react8.useState)(null);
4142
- const setAccessToken = (0, import_react8.useCallback)(
4143
- (nextAccessToken) => {
4144
- setAccessTokenState(nextAccessToken || null);
4145
- },
4146
- []
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
+ }
4147
3917
  );
4148
- const authorizedFetch = (0, import_react8.useMemo)(
4149
- () => createAuthorizedFetch(resolvedFetch, accessToken),
4150
- [accessToken, resolvedFetch]
4151
- );
4152
- const [state, setState] = (0, import_react8.useState)({
4153
- data: null,
4154
- loading: true,
4155
- error: null
4156
- });
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
- }
4169
- setState((prev) => ({ ...prev, loading: true, error: null }));
4170
- const requestFetch = options.accessToken !== void 0 ? createAuthorizedFetch(resolvedFetch, options.accessToken || null) : authorizedFetch;
4171
- 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
- });
4197
- } catch (error) {
4198
- setState({
4199
- data: null,
4200
- loading: false,
4201
- error: normalizeRuntimeError(error)
4202
- });
4203
- }
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]
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))
4218
3927
  );
4219
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(OpenXiangdaRuntimeContext.Provider, { value, children });
3928
+ for (const url2 of urls) {
3929
+ await moduleLoader(url2);
3930
+ }
4220
3931
  };
4221
- var useOpenXiangda = () => {
4222
- const context = (0, import_react8.useContext)(OpenXiangdaRuntimeContext);
4223
- if (!context) {
4224
- throw new Error("useOpenXiangda must be used inside OpenXiangdaProvider");
3932
+ var loadCustomPageModule = async (entryUrl, moduleLoader = defaultModuleLoader, jsUrls = []) => {
3933
+ if (!entryUrl) {
3934
+ throw new Error("\u4EE3\u7801\u9875\u9762\u7F3A\u5C11 entryUrl");
4225
3935
  }
4226
- return context;
3936
+ await loadRuntimeScriptModules(
3937
+ jsUrls.filter((url2) => url2 && url2 !== entryUrl),
3938
+ moduleLoader
3939
+ );
3940
+ const loaded = await moduleLoader(entryUrl);
3941
+ return loaded?.default && (loaded.default.mount || loaded.default.unmount) ? { ...loaded.default, ...loaded } : loaded || {};
4227
3942
  };
4228
- var useRuntimeBootstrap = () => useOpenXiangda();
4229
- var useAppMenus = () => {
4230
- const runtime = useOpenXiangda();
4231
- return {
4232
- ...runtime,
4233
- data: runtime.data?.menus || []
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 = "";
4234
3967
  };
4235
3968
  };
4236
- var usePermission = () => {
4237
- const runtime = useOpenXiangda();
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
+ });
4238
3996
  return {
4239
- ...runtime,
4240
- data: runtime.data?.permissions || null
3997
+ bootstrap,
3998
+ assets,
3999
+ context,
4000
+ module: module2,
4001
+ cleanup: cleanup2
4241
4002
  };
4242
4003
  };
4243
- var useCanAccessRoute = (input) => {
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 = {}) => {
4244
4012
  const runtime = useOpenXiangda();
4245
- const [state, setState] = (0, import_react8.useState)({
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)({
4246
4049
  data: null,
4247
4050
  loading: true,
4248
4051
  error: null
4249
4052
  });
4250
- (0, import_react8.useEffect)(() => {
4053
+ const reload = (0, import_react7.useCallback)(async () => {
4054
+ setState((prev) => ({ ...prev, loading: true, error: null }));
4055
+ try {
4056
+ const data = await auth.getMethods();
4057
+ setState({ data, loading: false, error: null });
4058
+ } catch (error) {
4059
+ setState({
4060
+ data: null,
4061
+ loading: false,
4062
+ error: normalizeError(error)
4063
+ });
4064
+ }
4065
+ }, [auth]);
4066
+ (0, import_react7.useEffect)(() => {
4251
4067
  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
- }
4068
+ const run = async () => {
4283
4069
  setState((prev) => ({ ...prev, loading: true, error: null }));
4284
4070
  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
- ),
4292
- {
4293
- method: "POST",
4294
- credentials: "include",
4295
- headers: {
4296
- accept: "application/json",
4297
- "content-type": "application/json"
4298
- },
4299
- body: JSON.stringify(input)
4300
- }
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
- }
4071
+ const data = await auth.getMethods();
4072
+ if (!disposed) setState({ data, loading: false, error: null });
4331
4073
  } catch (error) {
4332
4074
  if (!disposed) {
4333
4075
  setState({
4334
4076
  data: null,
4335
4077
  loading: false,
4336
- error: normalizeRuntimeError(error)
4078
+ error: normalizeError(error)
4337
4079
  });
4338
4080
  }
4339
4081
  }
4340
4082
  };
4341
- void check();
4083
+ void run();
4342
4084
  return () => {
4343
4085
  disposed = true;
4344
4086
  };
4345
- }, [
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
4354
- ]);
4087
+ }, [auth]);
4355
4088
  return {
4356
4089
  ...state,
4357
- canAccess: Boolean(state.data?.canAccess)
4090
+ methods: state.data?.methods || [],
4091
+ reload
4358
4092
  };
4359
4093
  };
4360
- var PermissionBoundary = ({
4361
- children,
4362
- fallback = null,
4363
- loadingFallback = null,
4364
- routeCode,
4365
- menuCode,
4366
- path
4094
+ var LoginPage = ({
4095
+ title,
4096
+ subtitle,
4097
+ className,
4098
+ style: style2,
4099
+ defaultMethod,
4100
+ redirectUrl,
4101
+ redirectOnSuccess = true,
4102
+ onSuccess,
4103
+ ...authOptions
4367
4104
  }) => {
4368
4105
  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) });
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,
4138
+ {
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
+ }
4192
+ },
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
+ }
4213
+ }
4214
+ )
4215
+ });
4216
+ }
4217
+ return items;
4218
+ }, [
4219
+ allowRegister,
4220
+ auth,
4221
+ handleSuccess,
4222
+ passwordForm,
4223
+ passwordMethod,
4224
+ phoneChallengeId,
4225
+ phoneForm,
4226
+ phoneMethod,
4227
+ phonePurpose,
4228
+ sendingCode,
4229
+ submitting
4230
+ ]);
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
+ }
4253
+ };
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
- };
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));
4574
- });
4575
- const serialized = query.toString();
4576
- if (!serialized) return url2;
4577
- return `${url2}${url2.includes("?") ? "&" : "?"}${serialized}`;
4578
4508
  };
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);
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 accessTokenRef = (0, import_react8.useRef)(null);
4523
+ const setAccessToken = (0, import_react8.useCallback)(
4524
+ (nextAccessToken) => {
4525
+ const normalizedAccessToken = nextAccessToken || null;
4526
+ accessTokenRef.current = normalizedAccessToken;
4527
+ setAccessTokenState(normalizedAccessToken);
4528
+ },
4529
+ []
4530
+ );
4531
+ const authorizedFetch = (0, import_react8.useMemo)(
4532
+ () => createAuthorizedFetch(resolvedFetch, accessToken),
4533
+ [accessToken, resolvedFetch]
4534
+ );
4535
+ const [state, setState] = (0, import_react8.useState)({
4536
+ data: null,
4537
+ loading: true,
4538
+ error: null
4539
+ });
4540
+ const reload = (0, import_react8.useCallback)(async (options = {}) => {
4541
+ if (!resolvedAppType) {
4542
+ setState({
4543
+ data: null,
4544
+ loading: false,
4545
+ error: createRuntimeError({
4546
+ message: "appType \u4E0D\u80FD\u4E3A\u7A7A",
4547
+ type: "unknown"
4548
+ })
4549
+ });
4550
+ return;
4586
4551
  }
4587
- if (loginUrl.startsWith("http://") || loginUrl.startsWith("https://")) {
4588
- return parsed.toString();
4552
+ setState((prev) => ({ ...prev, loading: true, error: null }));
4553
+ const requestFetch = options.accessToken !== void 0 ? createAuthorizedFetch(resolvedFetch, options.accessToken || null) : createAuthorizedFetch(resolvedFetch, accessTokenRef.current);
4554
+ try {
4555
+ const response = await requestFetch(
4556
+ buildServiceUrl3(
4557
+ servicePrefix,
4558
+ `/openxiangda-api/v1/apps/${encodeURIComponent(
4559
+ resolvedAppType
4560
+ )}/runtime/bootstrap`
4561
+ ),
4562
+ {
4563
+ credentials: "include",
4564
+ headers: { accept: "application/json" }
4565
+ }
4566
+ );
4567
+ const payload = await readJsonPayload(response);
4568
+ if (isRuntimeEnvelopeFailure(response, payload)) {
4569
+ throw createRuntimeHttpError(
4570
+ response,
4571
+ payload,
4572
+ payload?.message || `Runtime bootstrap failed: ${response.status}`
4573
+ );
4574
+ }
4575
+ setState({
4576
+ data: payload?.data || null,
4577
+ loading: false,
4578
+ error: null
4579
+ });
4580
+ } catch (error) {
4581
+ setState({
4582
+ data: null,
4583
+ loading: false,
4584
+ error: normalizeRuntimeError(error)
4585
+ });
4589
4586
  }
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;
4587
+ }, [resolvedAppType, resolvedFetch, servicePrefix]);
4588
+ (0, import_react8.useEffect)(() => {
4589
+ void reload();
4590
+ }, [reload]);
4591
+ const value = (0, import_react8.useMemo)(
4592
+ () => ({
4593
+ ...state,
4594
+ appType: resolvedAppType,
4595
+ servicePrefix,
4596
+ fetchImpl: authorizedFetch,
4597
+ baseFetchImpl: resolvedFetch,
4598
+ reload,
4599
+ setAccessToken
4600
+ }),
4601
+ [authorizedFetch, reload, resolvedAppType, resolvedFetch, servicePrefix, state]
4602
+ );
4603
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(OpenXiangdaRuntimeContext.Provider, { value, children });
4610
4604
  };
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);
4605
+ var useOpenXiangda = () => {
4606
+ const context = (0, import_react8.useContext)(OpenXiangdaRuntimeContext);
4607
+ if (!context) {
4608
+ throw new Error("useOpenXiangda must be used inside OpenXiangdaProvider");
4609
+ }
4610
+ return context;
4615
4611
  };
4616
- var isServerErrorCode = (code) => {
4617
- const normalized = Number(code);
4618
- return Number.isFinite(normalized) && normalized >= 500;
4612
+ var useRuntimeBootstrap = () => useOpenXiangda();
4613
+ var OpenXiangdaPageProvider = ({
4614
+ children,
4615
+ page,
4616
+ route,
4617
+ env,
4618
+ message: message7,
4619
+ modal,
4620
+ navigation
4621
+ }) => {
4622
+ const runtime = useOpenXiangda();
4623
+ const context = (0, import_react8.useMemo)(() => {
4624
+ const bootstrap = runtime.data;
4625
+ const app = toRuntimeRecord(bootstrap?.app);
4626
+ const user = toRuntimeRecord(bootstrap?.user);
4627
+ const permissions = bootstrap?.permissions;
4628
+ const tenantId = toStringValue(getRecordValue3(app, "tenantId")) || toStringValue(getRecordValue3(user, "tenantId")) || toStringValue(getRecordValue3(app, "tenantCode")) || "";
4629
+ const appType = runtime.appType || bootstrap?.appType || toStringValue(getRecordValue3(app, "appType"));
4630
+ const routeInfo = buildBrowserRouteInfo(route);
4631
+ return createBrowserPageContext(
4632
+ {
4633
+ app: {
4634
+ ...app,
4635
+ appType,
4636
+ tenantId
4637
+ },
4638
+ page: {
4639
+ id: routeInfo.pathname || "react-spa",
4640
+ code: routeInfo.pathname || "react-spa",
4641
+ name: "OpenXiangda React SPA",
4642
+ type: "react-spa",
4643
+ rendererType: "react-spa",
4644
+ routeKey: routeInfo.pathname || "react-spa",
4645
+ status: "ACTIVE",
4646
+ props: {},
4647
+ route: {},
4648
+ dataSources: [],
4649
+ capabilities: {},
4650
+ buildId: toStringValue(
4651
+ getRecordValue3(bootstrap?.runtime, "activeBuildId")
4652
+ ),
4653
+ ...page
4654
+ },
4655
+ user: {
4656
+ ...user,
4657
+ id: toStringValue(getRecordValue3(user, "id")) || (user.isGuest ? "guest" : "current"),
4658
+ username: toStringValue(getRecordValue3(user, "username")) || toStringValue(getRecordValue3(user, "name")) || (user.isGuest ? "guest" : "current"),
4659
+ tenantId,
4660
+ isGuest: user.isGuest === true || user.userType === "guest" || Boolean(getRecordValue3(user, "publicAccess")),
4661
+ userType: user.userType === "guest" || user.isGuest === true ? "guest" : "normal"
4662
+ },
4663
+ env: {
4664
+ appType,
4665
+ servicePrefix: runtime.servicePrefix,
4666
+ runtimeMode: bootstrap?.runtime?.mode || "react-spa",
4667
+ ...env || {}
4668
+ },
4669
+ permissions: {
4670
+ canView: runtime.error?.type !== "forbidden",
4671
+ hasFullAccess: permissions?.hasFullAccess === true,
4672
+ ...permissions || {}
4673
+ },
4674
+ sdk: {
4675
+ packageName: "openxiangda",
4676
+ supportedBridgeMethods: ["transport.request", "transport.download"]
4677
+ }
4678
+ },
4679
+ {
4680
+ servicePrefix: runtime.servicePrefix,
4681
+ fetchImpl: runtime.fetchImpl,
4682
+ route: routeInfo,
4683
+ message: message7,
4684
+ modal,
4685
+ navigation
4686
+ }
4687
+ );
4688
+ }, [
4689
+ env,
4690
+ message7,
4691
+ modal,
4692
+ navigation,
4693
+ page,
4694
+ route,
4695
+ runtime.appType,
4696
+ runtime.data,
4697
+ runtime.error?.type,
4698
+ runtime.fetchImpl,
4699
+ runtime.servicePrefix
4700
+ ]);
4701
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(PageProvider, { context, children });
4619
4702
  };
4620
- var getRecordString = (value, key) => {
4621
- const result = getRecordValue3(value, key);
4622
- return typeof result === "string" ? result : void 0;
4703
+ var useAppMenus = () => {
4704
+ const runtime = useOpenXiangda();
4705
+ return {
4706
+ ...runtime,
4707
+ data: runtime.data?.menus || []
4708
+ };
4623
4709
  };
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] || "";
4710
+ var usePermission = () => {
4711
+ const runtime = useOpenXiangda();
4712
+ return {
4713
+ ...runtime,
4714
+ data: runtime.data?.permissions || null
4715
+ };
4631
4716
  };
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 = {}) => {
4717
+ var useCanAccessRoute = (input) => {
4638
4718
  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()
4719
+ const [state, setState] = (0, import_react8.useState)({
4720
+ data: null,
4721
+ loading: true,
4722
+ error: null
4723
+ });
4724
+ (0, import_react8.useEffect)(() => {
4725
+ let disposed = false;
4726
+ const check = async () => {
4727
+ const permissions = runtime.data?.permissions;
4728
+ if (!runtime.appType || runtime.loading) {
4729
+ setState((prev) => ({ ...prev, loading: runtime.loading }));
4730
+ return;
4731
+ }
4732
+ if (runtime.error && !runtime.data) {
4733
+ const snapshot = toRuntimeErrorSnapshot(runtime.error);
4734
+ setState({
4735
+ data: {
4736
+ appType: runtime.appType,
4737
+ canAccess: false,
4738
+ status: snapshot.status,
4739
+ code: snapshot.code,
4740
+ message: snapshot.message,
4741
+ errorType: snapshot.type,
4742
+ payload: snapshot.payload
4743
+ },
4744
+ loading: false,
4745
+ error: runtime.error
4676
4746
  });
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 }
4747
+ return;
4748
+ }
4749
+ if (permissions?.hasFullAccess) {
4750
+ setState({
4751
+ data: { appType: runtime.appType, canAccess: true, permissions },
4752
+ loading: false,
4753
+ error: null
4754
+ });
4755
+ return;
4756
+ }
4757
+ setState((prev) => ({ ...prev, loading: true, error: null }));
4758
+ try {
4759
+ const response = await runtime.fetchImpl(
4760
+ buildServiceUrl3(
4761
+ runtime.servicePrefix,
4762
+ `/openxiangda-api/v1/apps/${encodeURIComponent(
4763
+ runtime.appType
4764
+ )}/runtime/routes/check`
4765
+ ),
4766
+ {
4767
+ method: "POST",
4768
+ credentials: "include",
4769
+ headers: {
4770
+ accept: "application/json",
4771
+ "content-type": "application/json"
4772
+ },
4773
+ body: JSON.stringify(input)
4774
+ }
4686
4775
  );
4687
- setError(nextError);
4688
- setLoading(false);
4689
- throw nextError;
4776
+ const payload = await readJsonPayload(response);
4777
+ const code = payload?.code ?? response.status;
4778
+ const canAccess = Boolean(payload?.data?.canAccess);
4779
+ const errorType = canAccess ? void 0 : classifyRuntimeError(response.status, code);
4780
+ const data = {
4781
+ ...payload?.data || {
4782
+ appType: runtime.appType,
4783
+ canAccess: false
4784
+ },
4785
+ appType: payload?.data?.appType || runtime.appType,
4786
+ canAccess,
4787
+ status: response.status,
4788
+ code,
4789
+ message: payload?.message,
4790
+ errorType,
4791
+ payload
4792
+ };
4793
+ if (!disposed) {
4794
+ const shouldTreatAsError = !response.ok || isServerErrorCode(code);
4795
+ setState({
4796
+ data,
4797
+ loading: false,
4798
+ error: shouldTreatAsError ? createRuntimeHttpError(
4799
+ response,
4800
+ payload,
4801
+ payload?.message || `Route check failed: ${response.status}`
4802
+ ) : null
4803
+ });
4804
+ }
4805
+ } catch (error) {
4806
+ if (!disposed) {
4807
+ setState({
4808
+ data: null,
4809
+ loading: false,
4810
+ error: normalizeRuntimeError(error)
4811
+ });
4812
+ }
4690
4813
  }
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]);
4814
+ };
4815
+ void check();
4816
+ return () => {
4817
+ disposed = true;
4818
+ };
4819
+ }, [
4820
+ input.menuCode,
4821
+ input.path,
4822
+ input.routeCode,
4823
+ runtime.appType,
4824
+ runtime.data?.permissions,
4825
+ runtime.fetchImpl,
4826
+ runtime.loading,
4827
+ runtime.servicePrefix
4828
+ ]);
4698
4829
  return {
4699
- loading,
4700
- error,
4701
- session,
4702
- publicAccess: session?.publicAccess || null,
4703
- startSession
4830
+ ...state,
4831
+ canAccess: Boolean(state.data?.canAccess)
4704
4832
  };
4705
4833
  };
4706
- var PublicAccessGate = ({
4834
+ var PermissionBoundary = ({
4707
4835
  children,
4708
4836
  fallback = null,
4709
- errorFallback = null,
4710
- ...options
4837
+ loadingFallback = null,
4838
+ routeCode,
4839
+ menuCode,
4840
+ path
4711
4841
  }) => {
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");
4842
+ const runtime = useOpenXiangda();
4843
+ const access = useCanAccessRoute({ routeCode, menuCode, path });
4844
+ const fallbackState = createPermissionFallbackState(access, runtime);
4845
+ if (access.loading) {
4846
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_jsx_runtime4.Fragment, { children: renderBoundaryFallback(loadingFallback, fallbackState) });
4772
4847
  }
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
- };
4848
+ if (!access.canAccess) {
4849
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_jsx_runtime4.Fragment, { children: renderBoundaryFallback(fallback, fallbackState) });
4783
4850
  }
4784
- return {
4785
- code: response.status,
4786
- success: response.ok,
4787
- message: response.statusText,
4788
- result: payload,
4789
- data: payload,
4790
- raw: payload
4791
- };
4851
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_jsx_runtime4.Fragment, { children });
4792
4852
  };
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);
4853
+ var useRuntimeAuth = () => {
4854
+ const runtime = useOpenXiangda();
4855
+ const resolveLoginUrl2 = (0, import_react8.useCallback)(
4856
+ async (options = {}) => {
4857
+ const redirectUri = options.redirectUri || getCurrentHref4();
4858
+ const domain = options.domain || getCurrentHostname2();
4859
+ const appTenantId = getRecordString(runtime.data?.app, "tenantId");
4860
+ try {
4861
+ const statusUrl = withQuery(
4862
+ buildServiceUrl3(runtime.servicePrefix, "/api/sso/status"),
4863
+ { domain }
4864
+ );
4865
+ const statusResponse = await runtime.fetchImpl(statusUrl, {
4866
+ credentials: "include",
4867
+ headers: { accept: "application/json" }
4868
+ });
4869
+ const statusPayload = await readJsonPayload(statusResponse);
4870
+ const sso = statusPayload?.data || {};
4871
+ const enabled = Boolean(sso.enabled);
4872
+ const shouldUseSso = Boolean(
4873
+ enabled && (sso.forceLogin ?? sso.autoRedirect ?? true)
4874
+ );
4875
+ if (shouldUseSso) {
4876
+ const loginUrlResponse = await runtime.fetchImpl(
4877
+ withQuery(buildServiceUrl3(runtime.servicePrefix, "/api/sso/login-url"), {
4878
+ domain,
4879
+ redirectUri,
4880
+ ...sso.tenantId || appTenantId ? { tenantId: String(sso.tenantId || appTenantId) } : {},
4881
+ ...sso.protocol ? { protocol: String(sso.protocol) } : {}
4882
+ }),
4883
+ {
4884
+ credentials: "include",
4885
+ headers: { accept: "application/json" }
4886
+ }
4887
+ );
4888
+ const loginUrlPayload = await readJsonPayload(loginUrlResponse);
4889
+ const loginUrl = loginUrlPayload?.data?.loginUrl || loginUrlPayload?.data?.url || loginUrlPayload?.loginUrl || loginUrlPayload?.url;
4890
+ if (loginUrl) return String(loginUrl);
4891
+ }
4892
+ } catch {
4809
4893
  }
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);
4894
+ 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");
4895
+ if (configuredLoginUrl) {
4896
+ return attachCallback(configuredLoginUrl, redirectUri);
4897
+ }
4898
+ return attachCallback("/platform/login", redirectUri);
4899
+ },
4900
+ [runtime.data?.app, runtime.fetchImpl, runtime.servicePrefix]
4901
+ );
4902
+ const redirectToLogin = (0, import_react8.useCallback)(
4903
+ async (options = {}) => {
4904
+ const loginUrl = await resolveLoginUrl2(options);
4905
+ if (typeof window !== "undefined") {
4906
+ if (options.replace === false) {
4907
+ window.location.assign(loginUrl);
4908
+ } else {
4909
+ window.location.replace(loginUrl);
4910
+ }
4911
+ }
4912
+ return loginUrl;
4913
+ },
4914
+ [resolveLoginUrl2]
4915
+ );
4916
+ const logout = (0, import_react8.useCallback)(async () => {
4917
+ const response = await runtime.fetchImpl(
4918
+ buildServiceUrl3(runtime.servicePrefix, "/api/auth/logout"),
4919
+ {
4920
+ method: "POST",
4921
+ credentials: "include",
4922
+ headers: { accept: "application/json" }
4832
4923
  }
4924
+ );
4925
+ const payload = await readJsonPayload(response);
4926
+ if (isRuntimeEnvelopeFailure(response, payload)) {
4927
+ throw createRuntimeHttpError(
4928
+ response,
4929
+ payload,
4930
+ payload?.message || `Logout failed: ${response.status}`
4931
+ );
4833
4932
  }
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
- };
4933
+ return payload;
4934
+ }, [runtime.fetchImpl, runtime.servicePrefix]);
4935
+ const logoutAndRedirect = (0, import_react8.useCallback)(
4936
+ async (options = {}) => {
4937
+ try {
4938
+ await logout();
4939
+ } catch (error) {
4940
+ if (options.continueOnError === false) throw error;
4941
+ }
4942
+ return redirectToLogin(options);
4943
+ },
4944
+ [logout, redirectToLogin]
4945
+ );
4850
4946
  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
- }
4947
+ logout,
4948
+ logoutAndRedirect,
4949
+ redirectToLogin,
4950
+ resolveLoginUrl: resolveLoginUrl2
4856
4951
  };
4857
4952
  };
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"]
4953
+ var buildServiceUrl3 = (servicePrefix, path) => {
4954
+ const prefix2 = servicePrefix.endsWith("/") ? servicePrefix.slice(0, -1) : servicePrefix;
4955
+ const suffix = path.startsWith("/") ? path : `/${path}`;
4956
+ return `${prefix2}${suffix}`;
4957
+ };
4958
+ var createAuthorizedFetch = (baseFetch, accessToken) => {
4959
+ if (!accessToken) return baseFetch;
4960
+ return ((input, init = {}) => {
4961
+ const headers = new Headers(init.headers || {});
4962
+ if (!headers.has("authorization")) {
4963
+ headers.set("authorization", `Bearer ${accessToken}`);
4917
4964
  }
4918
- };
4965
+ return baseFetch(input, {
4966
+ ...init,
4967
+ headers
4968
+ });
4969
+ });
4919
4970
  };
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
- }
4971
+ var readJsonPayload = async (response) => {
4933
4972
  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
- };
4973
+ return await response.json();
4949
4974
  } catch {
4950
- return fallback;
4975
+ return null;
4951
4976
  }
4952
4977
  };
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");
4978
+ var createRuntimeHttpError = (response, payload, fallbackMessage) => createRuntimeError({
4979
+ type: classifyRuntimeError(
4980
+ response.status,
4981
+ getRecordValue3(payload, "code")
4982
+ ),
4983
+ status: response.status,
4984
+ code: getRecordValue3(payload, "code"),
4985
+ message: getRecordValue3(payload, "message") || fallbackMessage,
4986
+ payload
4987
+ });
4988
+ var createRuntimeError = (snapshot) => new RuntimeHttpError({
4989
+ ...snapshot,
4990
+ type: snapshot.type || "unknown",
4991
+ message: snapshot.message || "Runtime request failed"
4992
+ });
4993
+ var normalizeRuntimeError = (error) => {
4994
+ if (error instanceof RuntimeHttpError) return error;
4995
+ if (error instanceof Error) {
4996
+ const runtimeError = error;
4997
+ runtimeError.type = runtimeError.type || classifyRuntimeError(runtimeError.status, runtimeError.code);
4998
+ return runtimeError;
4962
4999
  }
4963
- if (!pageKey) {
4964
- throw new Error("pageKey \u7F3A\u5931");
5000
+ return createRuntimeError({
5001
+ type: "unknown",
5002
+ message: String(error)
5003
+ });
5004
+ };
5005
+ var toRuntimeErrorSnapshot = (error) => ({
5006
+ type: error.type || classifyRuntimeError(error.status, error.code),
5007
+ status: error.status,
5008
+ code: error.code,
5009
+ message: error.message || "Runtime request failed",
5010
+ payload: error.payload
5011
+ });
5012
+ var classifyRuntimeError = (status, code) => {
5013
+ const normalizedCode = typeof code === "string" ? Number(code) : code;
5014
+ if (status === 401 || normalizedCode === 401) return "unauthenticated";
5015
+ if (status === 403 || normalizedCode === 403) return "forbidden";
5016
+ if (typeof code === "string") {
5017
+ const normalizedText = code.toUpperCase();
5018
+ if (normalizedText.includes("DENIED") || normalizedText.includes("FORBIDDEN")) {
5019
+ return "forbidden";
5020
+ }
5021
+ if (normalizedText.includes("UNAUTH") || normalizedText.includes("LOGIN")) {
5022
+ return "unauthenticated";
5023
+ }
4965
5024
  }
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"
5025
+ if (!status && !normalizedCode) return "network";
5026
+ return "unknown";
5027
+ };
5028
+ var createPermissionFallbackState = (access, runtime) => {
5029
+ const error = access.error || runtime.error;
5030
+ const accessData = access.data;
5031
+ const errorType = accessData?.errorType || error?.type || (!access.canAccess ? "forbidden" : "unknown");
5032
+ return {
5033
+ access,
5034
+ runtime,
5035
+ error,
5036
+ errorType,
5037
+ status: accessData?.status || error?.status,
5038
+ code: accessData?.code || error?.code,
5039
+ 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")
5040
+ };
5041
+ };
5042
+ var renderBoundaryFallback = (fallback, state) => typeof fallback === "function" ? fallback(state) : fallback;
5043
+ var withQuery = (url2, params) => {
5044
+ const query = new URLSearchParams();
5045
+ Object.entries(params).forEach(([key, value]) => {
5046
+ if (value === void 0 || value === "") return;
5047
+ query.set(key, String(value));
5048
+ });
5049
+ const serialized = query.toString();
5050
+ if (!serialized) return url2;
5051
+ return `${url2}${url2.includes("?") ? "&" : "?"}${serialized}`;
5052
+ };
5053
+ var attachCallback = (loginUrl, callback) => {
5054
+ if (!callback) return loginUrl;
5055
+ try {
5056
+ const base = typeof window !== "undefined" ? window.location.origin : "http://localhost";
5057
+ const parsed = new URL(loginUrl, base);
5058
+ if (!parsed.searchParams.has("callback") && !parsed.searchParams.has("redirectUri")) {
5059
+ parsed.searchParams.set("callback", callback);
4975
5060
  }
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");
5061
+ if (loginUrl.startsWith("http://") || loginUrl.startsWith("https://")) {
5062
+ return parsed.toString();
4984
5063
  }
4985
- return payload.data || {};
5064
+ return `${parsed.pathname}${parsed.search}${parsed.hash}`;
5065
+ } catch {
5066
+ const separator = loginUrl.includes("?") ? "&" : "?";
5067
+ return `${loginUrl}${separator}callback=${encodeURIComponent(callback)}`;
4986
5068
  }
4987
- return payload || {};
4988
5069
  };
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;
5070
+ var getCurrentHref4 = () => typeof window === "undefined" ? "" : window.location.href;
5071
+ var getCurrentHostname2 = () => typeof window === "undefined" ? "" : window.location.hostname;
5072
+ var getRuntimeEnv = (key) => {
5073
+ const env = typeof process !== "undefined" ? process.env : void 0;
5074
+ return env?.[key];
5025
5075
  };
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
- }
5076
+ var getRecordValue3 = (value, key) => {
5077
+ if (!value || typeof value !== "object") return void 0;
5078
+ return value[key];
5033
5079
  };
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 || {};
5080
+ var toRuntimeRecord = (value) => {
5081
+ return value && typeof value === "object" ? { ...value } : {};
5044
5082
  };
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);
5083
+ var toStringValue = (value) => {
5084
+ if (value === void 0 || value === null) return "";
5085
+ return String(value);
5086
+ };
5087
+ var parseBrowserQuery = () => {
5088
+ if (typeof window === "undefined") return {};
5089
+ const query = {};
5090
+ const params = new URLSearchParams(window.location.search);
5091
+ params.forEach((value, key) => {
5092
+ const currentValue = query[key];
5093
+ if (currentValue === void 0) {
5094
+ query[key] = value;
5095
+ return;
5096
+ }
5097
+ query[key] = Array.isArray(currentValue) ? [...currentValue, value] : [currentValue, value];
5062
5098
  });
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 = "";
5099
+ return query;
5100
+ };
5101
+ var buildBrowserRouteInfo = (route) => {
5102
+ const pathname = route?.pathname || (typeof window !== "undefined" ? window.location.pathname : "");
5103
+ const search = typeof window !== "undefined" ? window.location.search : "";
5104
+ const hash = route?.hash || (typeof window !== "undefined" ? window.location.hash : "");
5105
+ return {
5106
+ pathname,
5107
+ fullPath: route?.fullPath || (typeof window !== "undefined" ? `${pathname}${search}${hash}` : pathname),
5108
+ params: route?.params || {},
5109
+ query: route?.query || parseBrowserQuery(),
5110
+ hash
5069
5111
  };
5070
5112
  };
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
5113
+ var isSuccessCode5 = (code) => {
5114
+ if (code === void 0 || code === null || code === "") return true;
5115
+ const normalized = Number(code);
5116
+ return Number.isFinite(normalized) ? normalized === 0 || normalized >= 200 && normalized < 300 : false;
5117
+ };
5118
+ var isRuntimeEnvelopeFailure = (response, payload) => {
5119
+ const code = getRecordValue3(payload, "code");
5120
+ const success = getRecordValue3(payload, "success");
5121
+ return !response.ok || success === false || !isSuccessCode5(code);
5122
+ };
5123
+ var isServerErrorCode = (code) => {
5124
+ const normalized = Number(code);
5125
+ return Number.isFinite(normalized) && normalized >= 500;
5126
+ };
5127
+ var getRecordString = (value, key) => {
5128
+ const result = getRecordValue3(value, key);
5129
+ return typeof result === "string" ? result : void 0;
5130
+ };
5131
+ var resolveAppTypeFromLocation = () => {
5132
+ if (typeof window === "undefined") return "";
5133
+ const segments = window.location.pathname.split("/").filter(Boolean);
5134
+ const viewIndex = segments[0] === "view" ? 1 : 0;
5135
+ if (segments[viewIndex] === "submit") return segments[viewIndex + 1] || "";
5136
+ if (segments[viewIndex] === "preview") return segments[viewIndex + 1] || "";
5137
+ return segments[viewIndex] || "";
5138
+ };
5139
+
5140
+ // packages/sdk/src/runtime/react/publicAccess.tsx
5141
+ init_cjs_shims();
5142
+ var import_react9 = require("react");
5143
+ var import_jsx_runtime5 = require("react/jsx-runtime");
5144
+ var usePublicAccess = (options = {}) => {
5145
+ const runtime = useOpenXiangda();
5146
+ const {
5147
+ appType: runtimeAppType,
5148
+ servicePrefix: runtimeServicePrefix,
5149
+ baseFetchImpl: runtimeBaseFetchImpl,
5150
+ reload: reloadRuntime,
5151
+ setAccessToken
5152
+ } = runtime;
5153
+ const {
5154
+ appType = runtimeAppType,
5155
+ servicePrefix = runtimeServicePrefix,
5156
+ fetchImpl = runtimeBaseFetchImpl,
5157
+ autoStart = true,
5158
+ ...sessionInput
5159
+ } = options;
5160
+ const [session, setSession] = (0, import_react9.useState)(null);
5161
+ const [error, setError] = (0, import_react9.useState)(null);
5162
+ const [loading, setLoading] = (0, import_react9.useState)(Boolean(autoStart));
5163
+ const sessionInputKey = JSON.stringify(sessionInput);
5164
+ const stableSessionInput = (0, import_react9.useMemo)(() => sessionInput, [sessionInputKey]);
5165
+ const client = (0, import_react9.useMemo)(
5166
+ () => createPublicAccessClient({
5167
+ appType,
5168
+ servicePrefix,
5169
+ fetchImpl
5170
+ }),
5171
+ [appType, fetchImpl, servicePrefix]
5091
5172
  );
5092
- const cleanup2 = await mountCustomPageRuntime({
5093
- assets,
5094
- container: options.container,
5095
- context,
5096
- module: module2
5097
- });
5173
+ const startSession = (0, import_react9.useCallback)(
5174
+ async (input = {}) => {
5175
+ setLoading(true);
5176
+ setError(null);
5177
+ try {
5178
+ const data = await client.startSession({
5179
+ ...stableSessionInput,
5180
+ ...input,
5181
+ ticket: input.ticket || stableSessionInput.ticket || readTicketFromLocation(),
5182
+ path: input.path || stableSessionInput.path || readPathFromLocation()
5183
+ });
5184
+ setSession(data);
5185
+ setAccessToken(data.accessToken);
5186
+ await reloadRuntime({ accessToken: data.accessToken });
5187
+ setLoading(false);
5188
+ return data;
5189
+ } catch (caught) {
5190
+ const nextError = caught instanceof PublicAccessClientError ? caught : new PublicAccessClientError(
5191
+ caught instanceof Error ? caught.message : "\u516C\u5F00\u8BBF\u95EE\u4F1A\u8BDD\u521B\u5EFA\u5931\u8D25",
5192
+ { payload: caught }
5193
+ );
5194
+ setError(nextError);
5195
+ setLoading(false);
5196
+ throw nextError;
5197
+ }
5198
+ },
5199
+ [client, reloadRuntime, setAccessToken, stableSessionInput]
5200
+ );
5201
+ (0, import_react9.useEffect)(() => {
5202
+ if (!autoStart) return;
5203
+ void startSession().catch(() => void 0);
5204
+ }, [autoStart, startSession]);
5098
5205
  return {
5099
- bootstrap,
5100
- assets,
5101
- context,
5102
- module: module2,
5103
- cleanup: cleanup2
5206
+ loading,
5207
+ error,
5208
+ session,
5209
+ publicAccess: session?.publicAccess || null,
5210
+ startSession
5104
5211
  };
5105
5212
  };
5213
+ var PublicAccessGate = ({
5214
+ children,
5215
+ fallback = null,
5216
+ errorFallback = null,
5217
+ ...options
5218
+ }) => {
5219
+ const state = usePublicAccess(options);
5220
+ if (state.loading) return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_jsx_runtime5.Fragment, { children: fallback });
5221
+ if (state.error) {
5222
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_jsx_runtime5.Fragment, { children: typeof errorFallback === "function" ? errorFallback(state.error) : errorFallback });
5223
+ }
5224
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_jsx_runtime5.Fragment, { children });
5225
+ };
5226
+ var readTicketFromLocation = () => {
5227
+ if (typeof window === "undefined") return void 0;
5228
+ return new URLSearchParams(window.location.search).get("ticket") || void 0;
5229
+ };
5230
+ var readPathFromLocation = () => typeof window === "undefined" ? void 0 : window.location.pathname;
5231
+
5232
+ // packages/sdk/src/runtime/host/index.ts
5233
+ init_cjs_shims();
5106
5234
 
5107
5235
  // packages/sdk/src/runtime/host/builtinRouteRenderer.tsx
5108
5236
  init_cjs_shims();