openxiangda 1.0.89 → 1.0.90
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +16 -6
- package/openxiangda-skills/SKILL.md +3 -3
- package/openxiangda-skills/references/architecture-design.md +2 -1
- package/openxiangda-skills/references/openxiangda-api.md +1 -1
- package/openxiangda-skills/references/pages/page-sdk.md +17 -8
- package/openxiangda-skills/references/resource-manifest-cheatsheet.md +15 -5
- package/package.json +1 -1
- package/packages/sdk/dist/runtime/index.cjs +1495 -1371
- package/packages/sdk/dist/runtime/index.cjs.map +1 -1
- package/packages/sdk/dist/runtime/index.d.mts +1 -1
- package/packages/sdk/dist/runtime/index.d.ts +1 -1
- package/packages/sdk/dist/runtime/index.mjs +504 -380
- package/packages/sdk/dist/runtime/index.mjs.map +1 -1
- package/packages/sdk/dist/runtime/react.cjs +311 -6
- package/packages/sdk/dist/runtime/react.cjs.map +1 -1
- package/packages/sdk/dist/runtime/react.d.mts +11 -1
- package/packages/sdk/dist/runtime/react.d.ts +11 -1
- package/packages/sdk/dist/runtime/react.mjs +311 -6
- package/packages/sdk/dist/runtime/react.mjs.map +1 -1
- package/templates/openxiangda-react-spa/src/app/router.tsx +4 -1
|
@@ -32,6 +32,7 @@ var react_exports = {};
|
|
|
32
32
|
__export(react_exports, {
|
|
33
33
|
AuthClientError: () => AuthClientError,
|
|
34
34
|
LoginPage: () => LoginPage,
|
|
35
|
+
OpenXiangdaPageProvider: () => OpenXiangdaPageProvider,
|
|
35
36
|
OpenXiangdaProvider: () => OpenXiangdaProvider,
|
|
36
37
|
PageProvider: () => PageProvider,
|
|
37
38
|
PermissionBoundary: () => PermissionBoundary,
|
|
@@ -2547,6 +2548,187 @@ var createBoundFetch = (fetchImpl) => {
|
|
|
2547
2548
|
return ((input, init) => baseFetch.call(globalThis, input, init));
|
|
2548
2549
|
};
|
|
2549
2550
|
|
|
2551
|
+
// packages/sdk/src/runtime/host/browserHost.ts
|
|
2552
|
+
var trimTrailingSlash = (value) => value.replace(/\/+$/, "");
|
|
2553
|
+
var getDefaultServicePrefix = () => typeof window !== "undefined" ? trimTrailingSlash(window.__OPENXIANGDA_SERVICE_PREFIX__ || "/service") : "/service";
|
|
2554
|
+
var joinServicePath = (servicePrefix, path) => {
|
|
2555
|
+
if (/^https?:\/\//i.test(path)) return path;
|
|
2556
|
+
const normalizedPrefix = trimTrailingSlash(servicePrefix || "/service");
|
|
2557
|
+
if (path.startsWith(normalizedPrefix)) return path;
|
|
2558
|
+
return `${normalizedPrefix}${path.startsWith("/") ? path : `/${path}`}`;
|
|
2559
|
+
};
|
|
2560
|
+
var appendQuery = (url, query) => {
|
|
2561
|
+
if (!query) return url;
|
|
2562
|
+
return `${url}${url.includes("?") ? "&" : "?"}${query}`;
|
|
2563
|
+
};
|
|
2564
|
+
var normalizeMethod2 = (method) => {
|
|
2565
|
+
const value = String(method || "get").toUpperCase();
|
|
2566
|
+
return ["GET", "POST", "PUT", "DELETE", "PATCH"].includes(value) ? value : "GET";
|
|
2567
|
+
};
|
|
2568
|
+
var normalizeEnvelopeCode2 = (value, fallback) => {
|
|
2569
|
+
if (value === void 0 || value === null || value === "") return fallback;
|
|
2570
|
+
const normalized = Number(value);
|
|
2571
|
+
return Number.isFinite(normalized) ? normalized : String(value);
|
|
2572
|
+
};
|
|
2573
|
+
var isSuccessCode2 = (value) => {
|
|
2574
|
+
if (value === void 0 || value === null || value === "") return true;
|
|
2575
|
+
const normalized = Number(value);
|
|
2576
|
+
return Number.isFinite(normalized) ? normalized === 0 || normalized >= 200 && normalized < 300 : false;
|
|
2577
|
+
};
|
|
2578
|
+
var parseJsonResponse = async (response) => {
|
|
2579
|
+
const payload = await response.json().catch(() => null);
|
|
2580
|
+
if (!response.ok) {
|
|
2581
|
+
const message2 = payload && typeof payload === "object" ? payload.message || payload.error || response.statusText : response.statusText;
|
|
2582
|
+
throw new Error(message2 || "\u8BF7\u6C42\u5931\u8D25");
|
|
2583
|
+
}
|
|
2584
|
+
if (payload && typeof payload === "object" && "code" in payload) {
|
|
2585
|
+
const code = normalizeEnvelopeCode2(payload.code, response.status);
|
|
2586
|
+
return {
|
|
2587
|
+
code,
|
|
2588
|
+
success: payload.success !== false && isSuccessCode2(code),
|
|
2589
|
+
message: payload.message,
|
|
2590
|
+
result: payload.result ?? payload.data ?? null,
|
|
2591
|
+
data: payload.data,
|
|
2592
|
+
raw: payload
|
|
2593
|
+
};
|
|
2594
|
+
}
|
|
2595
|
+
return {
|
|
2596
|
+
code: response.status,
|
|
2597
|
+
success: response.ok,
|
|
2598
|
+
message: response.statusText,
|
|
2599
|
+
result: payload,
|
|
2600
|
+
data: payload,
|
|
2601
|
+
raw: payload
|
|
2602
|
+
};
|
|
2603
|
+
};
|
|
2604
|
+
var createBrowserPageBridge = (options = {}) => {
|
|
2605
|
+
const servicePrefix = options.servicePrefix || getDefaultServicePrefix();
|
|
2606
|
+
const fetchImpl = createBoundFetch(options.fetchImpl);
|
|
2607
|
+
const request = async (payload) => {
|
|
2608
|
+
if (!payload?.path) {
|
|
2609
|
+
throw new Error("transport.request \u9700\u8981 path");
|
|
2610
|
+
}
|
|
2611
|
+
const url = appendQuery(joinServicePath(servicePrefix, payload.path), payload.query);
|
|
2612
|
+
const headers = new Headers(payload.headers);
|
|
2613
|
+
let body;
|
|
2614
|
+
if (payload.body !== void 0) {
|
|
2615
|
+
if (payload.body instanceof FormData) {
|
|
2616
|
+
body = payload.body;
|
|
2617
|
+
} else {
|
|
2618
|
+
headers.set("Content-Type", headers.get("Content-Type") || "application/json");
|
|
2619
|
+
body = JSON.stringify(payload.body);
|
|
2620
|
+
}
|
|
2621
|
+
}
|
|
2622
|
+
const response = await fetchImpl(url, {
|
|
2623
|
+
method: normalizeMethod2(payload.method),
|
|
2624
|
+
headers,
|
|
2625
|
+
body,
|
|
2626
|
+
credentials: "include"
|
|
2627
|
+
});
|
|
2628
|
+
return parseJsonResponse(response);
|
|
2629
|
+
};
|
|
2630
|
+
const download = async (payload) => {
|
|
2631
|
+
if (!payload?.path) {
|
|
2632
|
+
throw new Error("transport.download \u9700\u8981 path");
|
|
2633
|
+
}
|
|
2634
|
+
const url = appendQuery(joinServicePath(servicePrefix, payload.path), payload.query);
|
|
2635
|
+
const headers = new Headers(payload.headers);
|
|
2636
|
+
let body;
|
|
2637
|
+
if (payload.body !== void 0) {
|
|
2638
|
+
if (payload.body instanceof FormData) {
|
|
2639
|
+
body = payload.body;
|
|
2640
|
+
} else {
|
|
2641
|
+
headers.set("Content-Type", headers.get("Content-Type") || "application/json");
|
|
2642
|
+
body = JSON.stringify(payload.body);
|
|
2643
|
+
}
|
|
2644
|
+
}
|
|
2645
|
+
const response = await fetchImpl(url, {
|
|
2646
|
+
method: normalizeMethod2(payload.method),
|
|
2647
|
+
headers,
|
|
2648
|
+
body,
|
|
2649
|
+
credentials: "include"
|
|
2650
|
+
});
|
|
2651
|
+
if (!response.ok) {
|
|
2652
|
+
throw new Error(response.statusText || "\u4E0B\u8F7D\u5931\u8D25");
|
|
2653
|
+
}
|
|
2654
|
+
return {
|
|
2655
|
+
blob: await response.blob(),
|
|
2656
|
+
contentType: response.headers.get("content-type") || void 0,
|
|
2657
|
+
fileName: response.headers.get("content-disposition") || void 0,
|
|
2658
|
+
headers: Object.fromEntries(response.headers.entries())
|
|
2659
|
+
};
|
|
2660
|
+
};
|
|
2661
|
+
return {
|
|
2662
|
+
invoke: async (method, payload) => {
|
|
2663
|
+
if (method === "transport.request") return await request(payload);
|
|
2664
|
+
if (method === "transport.download") return await download(payload);
|
|
2665
|
+
throw new Error(`\u4E0D\u652F\u6301\u7684 bridge \u65B9\u6CD5: ${method}`);
|
|
2666
|
+
}
|
|
2667
|
+
};
|
|
2668
|
+
};
|
|
2669
|
+
var createBrowserPageContext = (bootstrap, options = {}) => {
|
|
2670
|
+
const route = {
|
|
2671
|
+
pathname: options.route?.pathname || (typeof window !== "undefined" ? window.location.pathname : ""),
|
|
2672
|
+
fullPath: options.route?.fullPath || (typeof window !== "undefined" ? `${window.location.pathname}${window.location.search}${window.location.hash}` : ""),
|
|
2673
|
+
params: options.route?.params || {},
|
|
2674
|
+
query: options.route?.query || {},
|
|
2675
|
+
hash: options.route?.hash || (typeof window !== "undefined" ? window.location.hash : "")
|
|
2676
|
+
};
|
|
2677
|
+
return {
|
|
2678
|
+
protocolVersion: bootstrap.asset?.protocolVersion || "1.0",
|
|
2679
|
+
app: bootstrap.app,
|
|
2680
|
+
page: {
|
|
2681
|
+
...bootstrap.page,
|
|
2682
|
+
version: bootstrap.asset?.version || bootstrap.page.version,
|
|
2683
|
+
buildId: bootstrap.asset?.buildId || bootstrap.page.buildId
|
|
2684
|
+
},
|
|
2685
|
+
user: bootstrap.user,
|
|
2686
|
+
route,
|
|
2687
|
+
env: bootstrap.env || {},
|
|
2688
|
+
permissions: {
|
|
2689
|
+
canView: bootstrap.permissions?.canView !== false,
|
|
2690
|
+
hasFullAccess: bootstrap.permissions?.hasFullAccess === true,
|
|
2691
|
+
...bootstrap.permissions || {}
|
|
2692
|
+
},
|
|
2693
|
+
capabilities: Array.from(
|
|
2694
|
+
/* @__PURE__ */ new Set([
|
|
2695
|
+
"navigation",
|
|
2696
|
+
"ui.message",
|
|
2697
|
+
"ui.modal",
|
|
2698
|
+
"transport.request",
|
|
2699
|
+
"transport.download",
|
|
2700
|
+
...bootstrap.sdk?.supportedBridgeMethods || []
|
|
2701
|
+
])
|
|
2702
|
+
),
|
|
2703
|
+
ui: {
|
|
2704
|
+
message: {
|
|
2705
|
+
success: (text) => options.message?.success?.(text),
|
|
2706
|
+
error: (text) => options.message?.error?.(text),
|
|
2707
|
+
warning: (text) => options.message?.warning?.(text),
|
|
2708
|
+
info: (text) => options.message?.info?.(text),
|
|
2709
|
+
loading: (text) => options.message?.loading?.(text) || (() => void 0)
|
|
2710
|
+
},
|
|
2711
|
+
modal: {
|
|
2712
|
+
confirm: (input) => options.modal?.confirm?.(input) || Promise.resolve(false)
|
|
2713
|
+
}
|
|
2714
|
+
},
|
|
2715
|
+
navigation: {
|
|
2716
|
+
pushPage: (pageKey, query) => options.navigation?.pushPage?.(pageKey, query),
|
|
2717
|
+
replacePage: (pageKey, query) => options.navigation?.replacePage?.(pageKey, query),
|
|
2718
|
+
pushRoute: (routeValue, query) => options.navigation?.pushRoute?.(routeValue, query),
|
|
2719
|
+
replaceRoute: (routeValue, query) => options.navigation?.replaceRoute?.(routeValue, query),
|
|
2720
|
+
updateQuery: (query) => options.navigation?.updateQuery?.(query),
|
|
2721
|
+
setHash: (hash) => options.navigation?.setHash?.(hash),
|
|
2722
|
+
back: () => options.navigation?.back?.()
|
|
2723
|
+
},
|
|
2724
|
+
bridge: createBrowserPageBridge(options),
|
|
2725
|
+
sdk: {
|
|
2726
|
+
...bootstrap.sdk,
|
|
2727
|
+
supportedBridgeMethods: ["transport.request", "transport.download"]
|
|
2728
|
+
}
|
|
2729
|
+
};
|
|
2730
|
+
};
|
|
2731
|
+
|
|
2550
2732
|
// packages/sdk/src/runtime/react/auth.tsx
|
|
2551
2733
|
var import_react7 = require("react");
|
|
2552
2734
|
var import_antd2 = require("antd");
|
|
@@ -2585,7 +2767,7 @@ var createAuthClient = ({
|
|
|
2585
2767
|
const payload = await readPayload(response);
|
|
2586
2768
|
const code = getRecordValue(payload, "code");
|
|
2587
2769
|
const success = getRecordValue(payload, "success");
|
|
2588
|
-
if (!response.ok || success === false || !
|
|
2770
|
+
if (!response.ok || success === false || !isSuccessCode3(code)) {
|
|
2589
2771
|
throw new AuthClientError(
|
|
2590
2772
|
String(getRecordValue(payload, "message") || `Auth request failed: ${response.status}`),
|
|
2591
2773
|
{ status: response.status, code, payload }
|
|
@@ -2638,7 +2820,7 @@ var getRecordValue = (value, key) => {
|
|
|
2638
2820
|
if (!value || typeof value !== "object") return void 0;
|
|
2639
2821
|
return value[key];
|
|
2640
2822
|
};
|
|
2641
|
-
var
|
|
2823
|
+
var isSuccessCode3 = (code) => {
|
|
2642
2824
|
if (code === void 0 || code === null || code === "") return true;
|
|
2643
2825
|
const normalized = Number(code);
|
|
2644
2826
|
return Number.isFinite(normalized) ? normalized === 0 || normalized >= 200 && normalized < 300 : false;
|
|
@@ -3267,6 +3449,96 @@ var useOpenXiangda = () => {
|
|
|
3267
3449
|
return context;
|
|
3268
3450
|
};
|
|
3269
3451
|
var useRuntimeBootstrap = () => useOpenXiangda();
|
|
3452
|
+
var OpenXiangdaPageProvider = ({
|
|
3453
|
+
children,
|
|
3454
|
+
page,
|
|
3455
|
+
route,
|
|
3456
|
+
env,
|
|
3457
|
+
message: message2,
|
|
3458
|
+
modal,
|
|
3459
|
+
navigation
|
|
3460
|
+
}) => {
|
|
3461
|
+
const runtime = useOpenXiangda();
|
|
3462
|
+
const context = (0, import_react8.useMemo)(() => {
|
|
3463
|
+
const bootstrap = runtime.data;
|
|
3464
|
+
const app = toRuntimeRecord(bootstrap?.app);
|
|
3465
|
+
const user = toRuntimeRecord(bootstrap?.user);
|
|
3466
|
+
const permissions = bootstrap?.permissions;
|
|
3467
|
+
const tenantId = toStringValue(getRecordValue2(app, "tenantId")) || toStringValue(getRecordValue2(user, "tenantId")) || toStringValue(getRecordValue2(app, "tenantCode")) || "";
|
|
3468
|
+
const appType = runtime.appType || bootstrap?.appType || toStringValue(getRecordValue2(app, "appType"));
|
|
3469
|
+
const routeInfo = buildBrowserRouteInfo(route);
|
|
3470
|
+
return createBrowserPageContext(
|
|
3471
|
+
{
|
|
3472
|
+
app: {
|
|
3473
|
+
...app,
|
|
3474
|
+
appType,
|
|
3475
|
+
tenantId
|
|
3476
|
+
},
|
|
3477
|
+
page: {
|
|
3478
|
+
id: routeInfo.pathname || "react-spa",
|
|
3479
|
+
code: routeInfo.pathname || "react-spa",
|
|
3480
|
+
name: "OpenXiangda React SPA",
|
|
3481
|
+
type: "react-spa",
|
|
3482
|
+
rendererType: "react-spa",
|
|
3483
|
+
routeKey: routeInfo.pathname || "react-spa",
|
|
3484
|
+
status: "ACTIVE",
|
|
3485
|
+
props: {},
|
|
3486
|
+
route: {},
|
|
3487
|
+
dataSources: [],
|
|
3488
|
+
capabilities: {},
|
|
3489
|
+
buildId: toStringValue(
|
|
3490
|
+
getRecordValue2(bootstrap?.runtime, "activeBuildId")
|
|
3491
|
+
),
|
|
3492
|
+
...page
|
|
3493
|
+
},
|
|
3494
|
+
user: {
|
|
3495
|
+
...user,
|
|
3496
|
+
id: toStringValue(getRecordValue2(user, "id")) || (user.isGuest ? "guest" : "current"),
|
|
3497
|
+
username: toStringValue(getRecordValue2(user, "username")) || toStringValue(getRecordValue2(user, "name")) || (user.isGuest ? "guest" : "current"),
|
|
3498
|
+
tenantId,
|
|
3499
|
+
isGuest: user.isGuest === true || user.userType === "guest" || Boolean(getRecordValue2(user, "publicAccess")),
|
|
3500
|
+
userType: user.userType === "guest" || user.isGuest === true ? "guest" : "normal"
|
|
3501
|
+
},
|
|
3502
|
+
env: {
|
|
3503
|
+
appType,
|
|
3504
|
+
servicePrefix: runtime.servicePrefix,
|
|
3505
|
+
runtimeMode: bootstrap?.runtime?.mode || "react-spa",
|
|
3506
|
+
...env || {}
|
|
3507
|
+
},
|
|
3508
|
+
permissions: {
|
|
3509
|
+
canView: runtime.error?.type !== "forbidden",
|
|
3510
|
+
hasFullAccess: permissions?.hasFullAccess === true,
|
|
3511
|
+
...permissions || {}
|
|
3512
|
+
},
|
|
3513
|
+
sdk: {
|
|
3514
|
+
packageName: "openxiangda",
|
|
3515
|
+
supportedBridgeMethods: ["transport.request", "transport.download"]
|
|
3516
|
+
}
|
|
3517
|
+
},
|
|
3518
|
+
{
|
|
3519
|
+
servicePrefix: runtime.servicePrefix,
|
|
3520
|
+
fetchImpl: runtime.fetchImpl,
|
|
3521
|
+
route: routeInfo,
|
|
3522
|
+
message: message2,
|
|
3523
|
+
modal,
|
|
3524
|
+
navigation
|
|
3525
|
+
}
|
|
3526
|
+
);
|
|
3527
|
+
}, [
|
|
3528
|
+
env,
|
|
3529
|
+
message2,
|
|
3530
|
+
modal,
|
|
3531
|
+
navigation,
|
|
3532
|
+
page,
|
|
3533
|
+
route,
|
|
3534
|
+
runtime.appType,
|
|
3535
|
+
runtime.data,
|
|
3536
|
+
runtime.error?.type,
|
|
3537
|
+
runtime.fetchImpl,
|
|
3538
|
+
runtime.servicePrefix
|
|
3539
|
+
]);
|
|
3540
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(PageProvider, { context, children });
|
|
3541
|
+
};
|
|
3270
3542
|
var useAppMenus = () => {
|
|
3271
3543
|
const runtime = useOpenXiangda();
|
|
3272
3544
|
return {
|
|
@@ -3644,7 +3916,40 @@ var getRecordValue2 = (value, key) => {
|
|
|
3644
3916
|
if (!value || typeof value !== "object") return void 0;
|
|
3645
3917
|
return value[key];
|
|
3646
3918
|
};
|
|
3647
|
-
var
|
|
3919
|
+
var toRuntimeRecord = (value) => {
|
|
3920
|
+
return value && typeof value === "object" ? { ...value } : {};
|
|
3921
|
+
};
|
|
3922
|
+
var toStringValue = (value) => {
|
|
3923
|
+
if (value === void 0 || value === null) return "";
|
|
3924
|
+
return String(value);
|
|
3925
|
+
};
|
|
3926
|
+
var parseBrowserQuery = () => {
|
|
3927
|
+
if (typeof window === "undefined") return {};
|
|
3928
|
+
const query = {};
|
|
3929
|
+
const params = new URLSearchParams(window.location.search);
|
|
3930
|
+
params.forEach((value, key) => {
|
|
3931
|
+
const currentValue = query[key];
|
|
3932
|
+
if (currentValue === void 0) {
|
|
3933
|
+
query[key] = value;
|
|
3934
|
+
return;
|
|
3935
|
+
}
|
|
3936
|
+
query[key] = Array.isArray(currentValue) ? [...currentValue, value] : [currentValue, value];
|
|
3937
|
+
});
|
|
3938
|
+
return query;
|
|
3939
|
+
};
|
|
3940
|
+
var buildBrowserRouteInfo = (route) => {
|
|
3941
|
+
const pathname = route?.pathname || (typeof window !== "undefined" ? window.location.pathname : "");
|
|
3942
|
+
const search = typeof window !== "undefined" ? window.location.search : "";
|
|
3943
|
+
const hash = route?.hash || (typeof window !== "undefined" ? window.location.hash : "");
|
|
3944
|
+
return {
|
|
3945
|
+
pathname,
|
|
3946
|
+
fullPath: route?.fullPath || (typeof window !== "undefined" ? `${pathname}${search}${hash}` : pathname),
|
|
3947
|
+
params: route?.params || {},
|
|
3948
|
+
query: route?.query || parseBrowserQuery(),
|
|
3949
|
+
hash
|
|
3950
|
+
};
|
|
3951
|
+
};
|
|
3952
|
+
var isSuccessCode4 = (code) => {
|
|
3648
3953
|
if (code === void 0 || code === null || code === "") return true;
|
|
3649
3954
|
const normalized = Number(code);
|
|
3650
3955
|
return Number.isFinite(normalized) ? normalized === 0 || normalized >= 200 && normalized < 300 : false;
|
|
@@ -3652,7 +3957,7 @@ var isSuccessCode3 = (code) => {
|
|
|
3652
3957
|
var isRuntimeEnvelopeFailure = (response, payload) => {
|
|
3653
3958
|
const code = getRecordValue2(payload, "code");
|
|
3654
3959
|
const success = getRecordValue2(payload, "success");
|
|
3655
|
-
return !response.ok || success === false || !
|
|
3960
|
+
return !response.ok || success === false || !isSuccessCode4(code);
|
|
3656
3961
|
};
|
|
3657
3962
|
var isServerErrorCode = (code) => {
|
|
3658
3963
|
const normalized = Number(code);
|
|
@@ -3722,7 +4027,7 @@ var createPublicAccessClient = ({
|
|
|
3722
4027
|
const payload = await readPayload2(response);
|
|
3723
4028
|
const code = getRecordValue3(payload, "code");
|
|
3724
4029
|
const success = getRecordValue3(payload, "success");
|
|
3725
|
-
if (!response.ok || success === false || !
|
|
4030
|
+
if (!response.ok || success === false || !isSuccessCode5(code)) {
|
|
3726
4031
|
throw new PublicAccessClientError(
|
|
3727
4032
|
String(
|
|
3728
4033
|
getRecordValue3(payload, "message") || `Public access session failed: ${response.status}`
|
|
@@ -3760,7 +4065,7 @@ var getRecordValue3 = (value, key) => {
|
|
|
3760
4065
|
if (!value || typeof value !== "object") return void 0;
|
|
3761
4066
|
return value[key];
|
|
3762
4067
|
};
|
|
3763
|
-
var
|
|
4068
|
+
var isSuccessCode5 = (code) => {
|
|
3764
4069
|
if (code === void 0 || code === null || code === "") return true;
|
|
3765
4070
|
const normalized = Number(code);
|
|
3766
4071
|
return Number.isFinite(normalized) ? normalized === 0 || normalized >= 200 && normalized < 300 : false;
|