@nocobase/utils 2.2.0-beta.8 → 2.2.0

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.
@@ -0,0 +1,10 @@
1
+ /**
2
+ * This file is part of the NocoBase (R) project.
3
+ * Copyright (c) 2020-2024 NocoBase Co., Ltd.
4
+ * Authors: NocoBase Team.
5
+ *
6
+ * This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
7
+ * For more information, please refer to: https://www.nocobase.com/agreement.
8
+ */
9
+ export type AuthCookieType = 'authToken' | 'authenticator' | 'role' | 'csrfToken';
10
+ export declare function getAuthCookieName(type: AuthCookieType, appName?: string): string;
@@ -0,0 +1,46 @@
1
+ /**
2
+ * This file is part of the NocoBase (R) project.
3
+ * Copyright (c) 2020-2024 NocoBase Co., Ltd.
4
+ * Authors: NocoBase Team.
5
+ *
6
+ * This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
7
+ * For more information, please refer to: https://www.nocobase.com/agreement.
8
+ */
9
+
10
+ var __defProp = Object.defineProperty;
11
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
12
+ var __getOwnPropNames = Object.getOwnPropertyNames;
13
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
14
+ var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
15
+ var __export = (target, all) => {
16
+ for (var name in all)
17
+ __defProp(target, name, { get: all[name], enumerable: true });
18
+ };
19
+ var __copyProps = (to, from, except, desc) => {
20
+ if (from && typeof from === "object" || typeof from === "function") {
21
+ for (let key of __getOwnPropNames(from))
22
+ if (!__hasOwnProp.call(to, key) && key !== except)
23
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
24
+ }
25
+ return to;
26
+ };
27
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
28
+ var auth_cookie_name_exports = {};
29
+ __export(auth_cookie_name_exports, {
30
+ getAuthCookieName: () => getAuthCookieName
31
+ });
32
+ module.exports = __toCommonJS(auth_cookie_name_exports);
33
+ const cookieNamePrefixes = {
34
+ authToken: "nb_auth_token",
35
+ authenticator: "nb_authenticator",
36
+ role: "nb_role",
37
+ csrfToken: "nb_csrf_token"
38
+ };
39
+ function getAuthCookieName(type, appName) {
40
+ return `${cookieNamePrefixes[type]}_${appName || "main"}`;
41
+ }
42
+ __name(getAuthCookieName, "getAuthCookieName");
43
+ // Annotate the CommonJS export names for ESM import in node:
44
+ 0 && (module.exports = {
45
+ getAuthCookieName
46
+ });
@@ -0,0 +1,21 @@
1
+ /**
2
+ * This file is part of the NocoBase (R) project.
3
+ * Copyright (c) 2020-2024 NocoBase Co., Ltd.
4
+ * Authors: NocoBase Team.
5
+ *
6
+ * This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
7
+ * For more information, please refer to: https://www.nocobase.com/agreement.
8
+ */
9
+ export type { AuthCookieType } from './auth-cookie-name';
10
+ export { getAuthCookieName } from './auth-cookie-name';
11
+ export declare function getAuthCookieOptions(ctx: {
12
+ protocol?: string;
13
+ headers?: Record<string, string | string[] | undefined>;
14
+ }, httpOnly?: boolean, maxAge?: number): {
15
+ maxAge?: number;
16
+ secureProxy?: boolean;
17
+ httpOnly: boolean;
18
+ sameSite: "lax";
19
+ secure: boolean;
20
+ path: string;
21
+ };
@@ -0,0 +1,62 @@
1
+ /**
2
+ * This file is part of the NocoBase (R) project.
3
+ * Copyright (c) 2020-2024 NocoBase Co., Ltd.
4
+ * Authors: NocoBase Team.
5
+ *
6
+ * This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
7
+ * For more information, please refer to: https://www.nocobase.com/agreement.
8
+ */
9
+
10
+ var __defProp = Object.defineProperty;
11
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
12
+ var __getOwnPropNames = Object.getOwnPropertyNames;
13
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
14
+ var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
15
+ var __export = (target, all) => {
16
+ for (var name in all)
17
+ __defProp(target, name, { get: all[name], enumerable: true });
18
+ };
19
+ var __copyProps = (to, from, except, desc) => {
20
+ if (from && typeof from === "object" || typeof from === "function") {
21
+ for (let key of __getOwnPropNames(from))
22
+ if (!__hasOwnProp.call(to, key) && key !== except)
23
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
24
+ }
25
+ return to;
26
+ };
27
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
28
+ var auth_cookie_exports = {};
29
+ __export(auth_cookie_exports, {
30
+ getAuthCookieName: () => import_auth_cookie_name.getAuthCookieName,
31
+ getAuthCookieOptions: () => getAuthCookieOptions
32
+ });
33
+ module.exports = __toCommonJS(auth_cookie_exports);
34
+ var import_auth_cookie_name = require("./auth-cookie-name");
35
+ function getFirstHeaderValue(value) {
36
+ var _a;
37
+ const header = Array.isArray(value) ? value[0] : value;
38
+ return (_a = header == null ? void 0 : header.split(",")[0]) == null ? void 0 : _a.trim().toLowerCase();
39
+ }
40
+ __name(getFirstHeaderValue, "getFirstHeaderValue");
41
+ function getAuthCookieOptions(ctx, httpOnly = true, maxAge) {
42
+ var _a, _b;
43
+ const forwardedProto = (_a = ctx.headers) == null ? void 0 : _a["x-forwarded-proto"];
44
+ const forwardedProtocol = getFirstHeaderValue(forwardedProto);
45
+ const protocol = forwardedProtocol || ctx.protocol;
46
+ const publicPath = ((_b = process.env.APP_PUBLIC_PATH) == null ? void 0 : _b.replace(/\/+$/g, "")) || "/";
47
+ const secure = protocol === "https";
48
+ return {
49
+ httpOnly,
50
+ sameSite: "lax",
51
+ secure: secure && !forwardedProtocol,
52
+ path: publicPath || "/",
53
+ ...secure && forwardedProtocol ? { secureProxy: true } : {},
54
+ ...maxAge ? { maxAge } : {}
55
+ };
56
+ }
57
+ __name(getAuthCookieOptions, "getAuthCookieOptions");
58
+ // Annotate the CommonJS export names for ESM import in node:
59
+ 0 && (module.exports = {
60
+ getAuthCookieName,
61
+ getAuthCookieOptions
62
+ });
package/lib/client.d.ts CHANGED
@@ -11,8 +11,10 @@ import { dayjs } from './dayjs';
11
11
  import { getDayRangeByParams, getOffsetRangeByParams } from './dateRangeUtils';
12
12
  export * from './collections-graph';
13
13
  export * from './common';
14
+ export * from './auth-cookie-name';
14
15
  export * from './date';
15
16
  export * from './forEach';
17
+ export { generateFlowModelRdFromToken } from './flow-model-rd';
16
18
  export * from './getValuesByPath';
17
19
  export * from './handlebars';
18
20
  export * from './isValidFilter';
package/lib/client.js CHANGED
@@ -38,6 +38,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
38
38
  var client_exports = {};
39
39
  __export(client_exports, {
40
40
  dayjs: () => import_dayjs.dayjs,
41
+ generateFlowModelRdFromToken: () => import_flow_model_rd.generateFlowModelRdFromToken,
41
42
  getDayRangeByParams: () => import_dateRangeUtils.getDayRangeByParams,
42
43
  getOffsetRangeByParams: () => import_dateRangeUtils.getOffsetRangeByParams,
43
44
  lodash: () => import_lodash.default
@@ -48,8 +49,10 @@ var import_dayjs = require("./dayjs");
48
49
  var import_dateRangeUtils = require("./dateRangeUtils");
49
50
  __reExport(client_exports, require("./collections-graph"), module.exports);
50
51
  __reExport(client_exports, require("./common"), module.exports);
52
+ __reExport(client_exports, require("./auth-cookie-name"), module.exports);
51
53
  __reExport(client_exports, require("./date"), module.exports);
52
54
  __reExport(client_exports, require("./forEach"), module.exports);
55
+ var import_flow_model_rd = require("./flow-model-rd");
53
56
  __reExport(client_exports, require("./getValuesByPath"), module.exports);
54
57
  __reExport(client_exports, require("./handlebars"), module.exports);
55
58
  __reExport(client_exports, require("./isValidFilter"), module.exports);
@@ -74,11 +77,13 @@ __reExport(client_exports, require("./liquidjs"), module.exports);
74
77
  // Annotate the CommonJS export names for ESM import in node:
75
78
  0 && (module.exports = {
76
79
  dayjs,
80
+ generateFlowModelRdFromToken,
77
81
  getDayRangeByParams,
78
82
  getOffsetRangeByParams,
79
83
  lodash,
80
84
  ...require("./collections-graph"),
81
85
  ...require("./common"),
86
+ ...require("./auth-cookie-name"),
82
87
  ...require("./date"),
83
88
  ...require("./forEach"),
84
89
  ...require("./getValuesByPath"),
package/lib/cors.d.ts ADDED
@@ -0,0 +1,19 @@
1
+ /**
2
+ * This file is part of the NocoBase (R) project.
3
+ * Copyright (c) 2020-2024 NocoBase Co., Ltd.
4
+ * Authors: NocoBase Team.
5
+ *
6
+ * This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
7
+ * For more information, please refer to: https://www.nocobase.com/agreement.
8
+ */
9
+ export interface OriginContext {
10
+ protocol?: string;
11
+ headers?: Record<string, string | string[] | undefined>;
12
+ get(name: string): string;
13
+ }
14
+ export declare function getFirstHeaderValue(value: string | string[] | undefined): string;
15
+ export declare function getCorsWhitelist(): Set<string>;
16
+ export declare function getRequestOrigin(ctx: OriginContext): string;
17
+ export declare function getOrigin(value: string): string;
18
+ export declare function isSameOrigin(ctx: OriginContext, origin: string): boolean;
19
+ export declare function isTrustedOrigin(ctx: OriginContext, origin: string): boolean;
package/lib/cors.js ADDED
@@ -0,0 +1,92 @@
1
+ /**
2
+ * This file is part of the NocoBase (R) project.
3
+ * Copyright (c) 2020-2024 NocoBase Co., Ltd.
4
+ * Authors: NocoBase Team.
5
+ *
6
+ * This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
7
+ * For more information, please refer to: https://www.nocobase.com/agreement.
8
+ */
9
+
10
+ var __defProp = Object.defineProperty;
11
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
12
+ var __getOwnPropNames = Object.getOwnPropertyNames;
13
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
14
+ var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
15
+ var __export = (target, all) => {
16
+ for (var name in all)
17
+ __defProp(target, name, { get: all[name], enumerable: true });
18
+ };
19
+ var __copyProps = (to, from, except, desc) => {
20
+ if (from && typeof from === "object" || typeof from === "function") {
21
+ for (let key of __getOwnPropNames(from))
22
+ if (!__hasOwnProp.call(to, key) && key !== except)
23
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
24
+ }
25
+ return to;
26
+ };
27
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
28
+ var cors_exports = {};
29
+ __export(cors_exports, {
30
+ getCorsWhitelist: () => getCorsWhitelist,
31
+ getFirstHeaderValue: () => getFirstHeaderValue,
32
+ getOrigin: () => getOrigin,
33
+ getRequestOrigin: () => getRequestOrigin,
34
+ isSameOrigin: () => isSameOrigin,
35
+ isTrustedOrigin: () => isTrustedOrigin
36
+ });
37
+ module.exports = __toCommonJS(cors_exports);
38
+ function getFirstHeaderValue(value) {
39
+ var _a;
40
+ const header = Array.isArray(value) ? value[0] : value;
41
+ return (_a = header == null ? void 0 : header.split(",")[0]) == null ? void 0 : _a.trim();
42
+ }
43
+ __name(getFirstHeaderValue, "getFirstHeaderValue");
44
+ function getCorsWhitelist() {
45
+ const whitelistString = process.env.CORS_ORIGIN_WHITELIST;
46
+ if (!whitelistString) {
47
+ return null;
48
+ }
49
+ return new Set(
50
+ whitelistString.split(",").map((item) => item.trim()).filter(Boolean)
51
+ );
52
+ }
53
+ __name(getCorsWhitelist, "getCorsWhitelist");
54
+ function getRequestOrigin(ctx) {
55
+ var _a, _b, _c;
56
+ const protocol = (_b = getFirstHeaderValue((_a = ctx.headers) == null ? void 0 : _a["x-forwarded-proto"]) || ctx.protocol) == null ? void 0 : _b.toLowerCase();
57
+ const host = getFirstHeaderValue((_c = ctx.headers) == null ? void 0 : _c["x-forwarded-host"]) || ctx.get("host");
58
+ return host ? `${protocol}://${host}` : void 0;
59
+ }
60
+ __name(getRequestOrigin, "getRequestOrigin");
61
+ function getOrigin(value) {
62
+ try {
63
+ return new URL(value).origin;
64
+ } catch {
65
+ return null;
66
+ }
67
+ }
68
+ __name(getOrigin, "getOrigin");
69
+ function isSameOrigin(ctx, origin) {
70
+ return origin === getRequestOrigin(ctx);
71
+ }
72
+ __name(isSameOrigin, "isSameOrigin");
73
+ function isTrustedOrigin(ctx, origin) {
74
+ if (isSameOrigin(ctx, origin)) {
75
+ return true;
76
+ }
77
+ const whitelist = getCorsWhitelist();
78
+ if (!whitelist) {
79
+ return false;
80
+ }
81
+ return whitelist.has("*") || whitelist.has(origin);
82
+ }
83
+ __name(isTrustedOrigin, "isTrustedOrigin");
84
+ // Annotate the CommonJS export names for ESM import in node:
85
+ 0 && (module.exports = {
86
+ getCorsWhitelist,
87
+ getFirstHeaderValue,
88
+ getOrigin,
89
+ getRequestOrigin,
90
+ isSameOrigin,
91
+ isTrustedOrigin
92
+ });
@@ -0,0 +1,20 @@
1
+ /**
2
+ * This file is part of the NocoBase (R) project.
3
+ * Copyright (c) 2020-2024 NocoBase Co., Ltd.
4
+ * Authors: NocoBase Team.
5
+ *
6
+ * This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
7
+ * For more information, please refer to: https://www.nocobase.com/agreement.
8
+ */
9
+ type JwtSessionPayload = {
10
+ userId?: unknown;
11
+ signInTime?: unknown;
12
+ jti?: unknown;
13
+ };
14
+ export declare function decodeJwtSessionPayload(token?: string | null): JwtSessionPayload | null;
15
+ export declare function getFlowModelRdSessionId(payload?: JwtSessionPayload | null): string;
16
+ export declare function getFlowModelRdSessionIdFromToken(token?: string | null): string;
17
+ export declare function generateFlowModelRd(flowModelUid?: string | number | null, sessionId?: string | null): string;
18
+ export declare function generateFlowModelRdFromToken(flowModelUid?: string | number | null, token?: string | null): string;
19
+ export declare function resolveFlowModelUidFromRd(rd?: string | null, sessionId?: string | null): string;
20
+ export {};
@@ -0,0 +1,136 @@
1
+ /**
2
+ * This file is part of the NocoBase (R) project.
3
+ * Copyright (c) 2020-2024 NocoBase Co., Ltd.
4
+ * Authors: NocoBase Team.
5
+ *
6
+ * This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
7
+ * For more information, please refer to: https://www.nocobase.com/agreement.
8
+ */
9
+
10
+ var __defProp = Object.defineProperty;
11
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
12
+ var __getOwnPropNames = Object.getOwnPropertyNames;
13
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
14
+ var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
15
+ var __export = (target, all) => {
16
+ for (var name in all)
17
+ __defProp(target, name, { get: all[name], enumerable: true });
18
+ };
19
+ var __copyProps = (to, from, except, desc) => {
20
+ if (from && typeof from === "object" || typeof from === "function") {
21
+ for (let key of __getOwnPropNames(from))
22
+ if (!__hasOwnProp.call(to, key) && key !== except)
23
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
24
+ }
25
+ return to;
26
+ };
27
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
28
+ var flow_model_rd_exports = {};
29
+ __export(flow_model_rd_exports, {
30
+ decodeJwtSessionPayload: () => decodeJwtSessionPayload,
31
+ generateFlowModelRd: () => generateFlowModelRd,
32
+ generateFlowModelRdFromToken: () => generateFlowModelRdFromToken,
33
+ getFlowModelRdSessionId: () => getFlowModelRdSessionId,
34
+ getFlowModelRdSessionIdFromToken: () => getFlowModelRdSessionIdFromToken,
35
+ resolveFlowModelUidFromRd: () => resolveFlowModelUidFromRd
36
+ });
37
+ module.exports = __toCommonJS(flow_model_rd_exports);
38
+ const rdCache = /* @__PURE__ */ new Map();
39
+ function toBase64Url(input) {
40
+ if (typeof btoa === "function") {
41
+ return btoa(input).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/g, "");
42
+ }
43
+ return Buffer.from(input, "binary").toString("base64url");
44
+ }
45
+ __name(toBase64Url, "toBase64Url");
46
+ function fromBase64Url(input) {
47
+ const normalized = input.replace(/-/g, "+").replace(/_/g, "/");
48
+ const padded = normalized.padEnd(normalized.length + (4 - normalized.length % 4) % 4, "=");
49
+ if (typeof atob === "function") {
50
+ return atob(padded);
51
+ }
52
+ return Buffer.from(padded, "base64").toString("binary");
53
+ }
54
+ __name(fromBase64Url, "fromBase64Url");
55
+ function fastHash(input) {
56
+ let hash = 2166136261;
57
+ for (let index = 0; index < input.length; index += 1) {
58
+ hash ^= input.charCodeAt(index);
59
+ hash = Math.imul(hash, 16777619);
60
+ }
61
+ return (hash >>> 0).toString(36);
62
+ }
63
+ __name(fastHash, "fastHash");
64
+ function xorString(input, mask) {
65
+ if (!mask) return "";
66
+ let output = "";
67
+ for (let index = 0; index < input.length; index += 1) {
68
+ output += String.fromCharCode(input.charCodeAt(index) ^ mask.charCodeAt(index % mask.length));
69
+ }
70
+ return output;
71
+ }
72
+ __name(xorString, "xorString");
73
+ function decodeJwtSessionPayload(token) {
74
+ if (!token || typeof token !== "string") return null;
75
+ const parts = token.split(".");
76
+ if (parts.length < 2) return null;
77
+ try {
78
+ return JSON.parse(fromBase64Url(parts[1]));
79
+ } catch {
80
+ return null;
81
+ }
82
+ }
83
+ __name(decodeJwtSessionPayload, "decodeJwtSessionPayload");
84
+ function getFlowModelRdSessionId(payload) {
85
+ const userId = payload == null ? void 0 : payload.userId;
86
+ const sessionKey = (payload == null ? void 0 : payload.signInTime) || (payload == null ? void 0 : payload.jti);
87
+ if (typeof userId !== "string" && typeof userId !== "number" || typeof sessionKey !== "string" && typeof sessionKey !== "number") {
88
+ return "";
89
+ }
90
+ return `${userId}:${sessionKey}`;
91
+ }
92
+ __name(getFlowModelRdSessionId, "getFlowModelRdSessionId");
93
+ function getFlowModelRdSessionIdFromToken(token) {
94
+ return getFlowModelRdSessionId(decodeJwtSessionPayload(token));
95
+ }
96
+ __name(getFlowModelRdSessionIdFromToken, "getFlowModelRdSessionIdFromToken");
97
+ function generateFlowModelRd(flowModelUid, sessionId) {
98
+ if (typeof flowModelUid !== "string" && typeof flowModelUid !== "number" || !sessionId) return void 0;
99
+ const uid = String(flowModelUid);
100
+ const cacheKey = `${sessionId}
101
+ ${uid}`;
102
+ const cached = rdCache.get(cacheKey);
103
+ if (cached) return cached;
104
+ const cipher = toBase64Url(xorString(uid, fastHash(`mask:v1:${sessionId}`)));
105
+ const check = fastHash(`check:v1:${sessionId}:${uid}`);
106
+ const rd = `v1.${cipher}.${check}`;
107
+ rdCache.set(cacheKey, rd);
108
+ return rd;
109
+ }
110
+ __name(generateFlowModelRd, "generateFlowModelRd");
111
+ function generateFlowModelRdFromToken(flowModelUid, token) {
112
+ return generateFlowModelRd(flowModelUid, getFlowModelRdSessionIdFromToken(token));
113
+ }
114
+ __name(generateFlowModelRdFromToken, "generateFlowModelRdFromToken");
115
+ function resolveFlowModelUidFromRd(rd, sessionId) {
116
+ if (!rd || !sessionId || typeof rd !== "string") return void 0;
117
+ const parts = rd.split(".");
118
+ if (parts.length !== 3 || parts[0] !== "v1" || !parts[1] || !parts[2]) return void 0;
119
+ try {
120
+ const uid = xorString(fromBase64Url(parts[1]), fastHash(`mask:v1:${sessionId}`));
121
+ const check = fastHash(`check:v1:${sessionId}:${uid}`);
122
+ return check === parts[2] ? uid : void 0;
123
+ } catch {
124
+ return void 0;
125
+ }
126
+ }
127
+ __name(resolveFlowModelUidFromRd, "resolveFlowModelUidFromRd");
128
+ // Annotate the CommonJS export names for ESM import in node:
129
+ 0 && (module.exports = {
130
+ decodeJwtSessionPayload,
131
+ generateFlowModelRd,
132
+ generateFlowModelRdFromToken,
133
+ getFlowModelRdSessionId,
134
+ getFlowModelRdSessionIdFromToken,
135
+ resolveFlowModelUidFromRd
136
+ });
package/lib/index.d.ts CHANGED
@@ -9,14 +9,17 @@
9
9
  import lodash from 'lodash';
10
10
  export { Schema } from '@formily/json-schema';
11
11
  export * from './assign';
12
+ export * from './auth-cookie';
12
13
  export * from './collections-graph';
13
14
  export * from './common';
15
+ export * from './cors';
14
16
  export * from './crypto';
15
17
  export * from './date';
16
18
  export * from './dateRangeUtils';
17
19
  export * from './dayjs';
18
20
  export { dayjs } from './dayjs';
19
21
  export * from './forEach';
22
+ export * from './flow-model-rd';
20
23
  export * from './fs-exists';
21
24
  export * from './handlebars';
22
25
  export * from './i18n';
package/lib/index.js CHANGED
@@ -45,14 +45,17 @@ module.exports = __toCommonJS(src_exports);
45
45
  var import_lodash = __toESM(require("lodash"));
46
46
  var import_json_schema = require("@formily/json-schema");
47
47
  __reExport(src_exports, require("./assign"), module.exports);
48
+ __reExport(src_exports, require("./auth-cookie"), module.exports);
48
49
  __reExport(src_exports, require("./collections-graph"), module.exports);
49
50
  __reExport(src_exports, require("./common"), module.exports);
51
+ __reExport(src_exports, require("./cors"), module.exports);
50
52
  __reExport(src_exports, require("./crypto"), module.exports);
51
53
  __reExport(src_exports, require("./date"), module.exports);
52
54
  __reExport(src_exports, require("./dateRangeUtils"), module.exports);
53
55
  __reExport(src_exports, require("./dayjs"), module.exports);
54
56
  var import_dayjs = require("./dayjs");
55
57
  __reExport(src_exports, require("./forEach"), module.exports);
58
+ __reExport(src_exports, require("./flow-model-rd"), module.exports);
56
59
  __reExport(src_exports, require("./fs-exists"), module.exports);
57
60
  __reExport(src_exports, require("./handlebars"), module.exports);
58
61
  __reExport(src_exports, require("./i18n"), module.exports);
@@ -88,13 +91,16 @@ __reExport(src_exports, require("./storage-path"), module.exports);
88
91
  dayjs,
89
92
  lodash,
90
93
  ...require("./assign"),
94
+ ...require("./auth-cookie"),
91
95
  ...require("./collections-graph"),
92
96
  ...require("./common"),
97
+ ...require("./cors"),
93
98
  ...require("./crypto"),
94
99
  ...require("./date"),
95
100
  ...require("./dateRangeUtils"),
96
101
  ...require("./dayjs"),
97
102
  ...require("./forEach"),
103
+ ...require("./flow-model-rd"),
98
104
  ...require("./fs-exists"),
99
105
  ...require("./handlebars"),
100
106
  ...require("./i18n"),
@@ -30,7 +30,8 @@ export declare function checkUrlAgainstWhitelist(url?: string): void;
30
30
  * Drop-in replacement for `axios.request()` with built-in SSRF protection.
31
31
  *
32
32
  * Validates `config.url` against {@link checkUrlAgainstWhitelist} before
33
- * forwarding to axios. Use this instead of calling axios directly for all
33
+ * forwarding to axios. Every redirect destination is validated before the
34
+ * next request is sent. Use this instead of calling axios directly for all
34
35
  * server-initiated outbound HTTP requests.
35
36
  */
36
37
  export declare function serverRequest<T = any>(config: AxiosRequestConfig): Promise<AxiosResponse<T>>;
@@ -44,7 +44,12 @@ __export(server_request_exports, {
44
44
  module.exports = __toCommonJS(server_request_exports);
45
45
  var import_ipaddr = __toESM(require("ipaddr.js"));
46
46
  var import_axios = __toESM(require("axios"));
47
+ var import_promises = require("dns/promises");
47
48
  const ALLOWED_PROTOCOLS = /* @__PURE__ */ new Set(["http:", "https:"]);
49
+ const SSRF_RISK_IP_RANGES = /* @__PURE__ */ new Set(["loopback", "private", "linkLocal", "uniqueLocal", "unspecified"]);
50
+ const SSRF_RISK_HOSTNAMES = /* @__PURE__ */ new Set(["localhost", "metadata.google.internal"]);
51
+ const DNS_LOOKUP_WARNING_TIMEOUT = 1e3;
52
+ const warnedSsrfRiskTargets = /* @__PURE__ */ new Set();
48
53
  function matchesIpEntry(hostname, entry) {
49
54
  try {
50
55
  let addr = import_ipaddr.default.parse(hostname);
@@ -87,6 +92,88 @@ function matchesEntry(hostname, entry) {
87
92
  return import_ipaddr.default.isValid(hostname) ? matchesIpEntry(hostname, e) : matchesDomainPattern(hostname, e);
88
93
  }
89
94
  __name(matchesEntry, "matchesEntry");
95
+ function getNormalizedHost(url) {
96
+ const { hostname } = url;
97
+ return hostname.startsWith("[") && hostname.endsWith("]") ? hostname.slice(1, -1) : hostname;
98
+ }
99
+ __name(getNormalizedHost, "getNormalizedHost");
100
+ function getSsrfRiskReason(hostname) {
101
+ const host = hostname.toLowerCase();
102
+ if (SSRF_RISK_HOSTNAMES.has(host)) {
103
+ return "known local or metadata hostname";
104
+ }
105
+ if (!import_ipaddr.default.isValid(host)) {
106
+ return;
107
+ }
108
+ try {
109
+ let addr = import_ipaddr.default.parse(host);
110
+ if (addr.kind() === "ipv6" && addr.isIPv4MappedAddress()) {
111
+ addr = addr.toIPv4Address();
112
+ }
113
+ const range = addr.range();
114
+ return SSRF_RISK_IP_RANGES.has(range) ? range : void 0;
115
+ } catch {
116
+ return;
117
+ }
118
+ }
119
+ __name(getSsrfRiskReason, "getSsrfRiskReason");
120
+ function warnSsrfRiskTarget(host, reason, key = `${host}:${reason}`) {
121
+ if (warnedSsrfRiskTargets.has(key)) return;
122
+ warnedSsrfRiskTargets.add(key);
123
+ console.warn(
124
+ [
125
+ `Outbound request to "${host}" targets a potential SSRF risk target (${reason}) and is allowed because SERVER_REQUEST_WHITELIST is not configured.`,
126
+ "Configure SERVER_REQUEST_WHITELIST to explicitly allow required outbound targets.",
127
+ "A future version may block private, loopback, link-local, and metadata targets by default."
128
+ ].join(" ")
129
+ );
130
+ }
131
+ __name(warnSsrfRiskTarget, "warnSsrfRiskTarget");
132
+ function warnIfSsrfRiskTarget(host) {
133
+ const reason = getSsrfRiskReason(host);
134
+ if (!reason) return;
135
+ warnSsrfRiskTarget(host, reason);
136
+ }
137
+ __name(warnIfSsrfRiskTarget, "warnIfSsrfRiskTarget");
138
+ function hasServerRequestWhitelist() {
139
+ const whitelist = process.env.SERVER_REQUEST_WHITELIST;
140
+ return Boolean(whitelist && whitelist.trim());
141
+ }
142
+ __name(hasServerRequestWhitelist, "hasServerRequestWhitelist");
143
+ async function resolveHostAddresses(host) {
144
+ try {
145
+ return await Promise.race([
146
+ (0, import_promises.lookup)(host, { all: true, verbatim: true }),
147
+ new Promise((resolve) => {
148
+ setTimeout(() => resolve([]), DNS_LOOKUP_WARNING_TIMEOUT);
149
+ })
150
+ ]);
151
+ } catch {
152
+ return [];
153
+ }
154
+ }
155
+ __name(resolveHostAddresses, "resolveHostAddresses");
156
+ async function warnIfResolvedSsrfRiskTarget(url) {
157
+ if (!url || !url.includes("://") || hasServerRequestWhitelist()) return;
158
+ let parsed;
159
+ try {
160
+ parsed = new URL(url);
161
+ } catch {
162
+ return;
163
+ }
164
+ if (!ALLOWED_PROTOCOLS.has(parsed.protocol)) return;
165
+ const host = getNormalizedHost(parsed);
166
+ if (getSsrfRiskReason(host) || import_ipaddr.default.isValid(host)) return;
167
+ const addresses = await resolveHostAddresses(host);
168
+ for (const address of addresses) {
169
+ const reason = getSsrfRiskReason(address.address);
170
+ if (reason) {
171
+ warnSsrfRiskTarget(host, `resolves to ${address.address} (${reason})`, `${host}:resolved-risk`);
172
+ return;
173
+ }
174
+ }
175
+ }
176
+ __name(warnIfResolvedSsrfRiskTarget, "warnIfResolvedSsrfRiskTarget");
90
177
  function checkUrlAgainstWhitelist(url) {
91
178
  if (!url) return;
92
179
  if (!url.includes("://")) return;
@@ -101,12 +188,14 @@ function checkUrlAgainstWhitelist(url) {
101
188
  `URL scheme "${parsed.protocol.replace(":", "")}" is not allowed. Only http and https are permitted.`
102
189
  );
103
190
  }
191
+ const host = getNormalizedHost(parsed);
104
192
  const whitelist = process.env.SERVER_REQUEST_WHITELIST;
105
- if (!whitelist || !whitelist.trim()) return;
193
+ if (!whitelist || !whitelist.trim()) {
194
+ warnIfSsrfRiskTarget(host);
195
+ return;
196
+ }
106
197
  const entries = whitelist.split(",").map((e) => e.trim()).filter(Boolean);
107
198
  if (entries.length === 0) return;
108
- const { hostname } = parsed;
109
- const host = hostname.startsWith("[") && hostname.endsWith("]") ? hostname.slice(1, -1) : hostname;
110
199
  for (const entry of entries) {
111
200
  if (matchesEntry(host, entry)) return;
112
201
  }
@@ -117,7 +206,19 @@ function checkUrlAgainstWhitelist(url) {
117
206
  __name(checkUrlAgainstWhitelist, "checkUrlAgainstWhitelist");
118
207
  async function serverRequest(config) {
119
208
  checkUrlAgainstWhitelist(config.url);
120
- return import_axios.default.request(config);
209
+ await warnIfResolvedSsrfRiskTarget(config.url);
210
+ return import_axios.default.request({
211
+ ...config,
212
+ // Axios request interceptors do not run again for redirects. In Node.js,
213
+ // beforeRedirect runs after Location is resolved and before the next hop
214
+ // is sent, so every redirect destination must be checked here.
215
+ beforeRedirect: /* @__PURE__ */ __name((options) => {
216
+ if (typeof options.href !== "string") {
217
+ throw new Error("Unable to validate redirect destination.");
218
+ }
219
+ checkUrlAgainstWhitelist(options.href);
220
+ }, "beforeRedirect")
221
+ });
121
222
  }
122
223
  __name(serverRequest, "serverRequest");
123
224
  // Annotate the CommonJS export names for ESM import in node:
@@ -9,6 +9,14 @@
9
9
  export type JSONValue = string | {
10
10
  [key: string]: JSONValue;
11
11
  } | JSONValue[];
12
+ export type VariableUsageExtractionResult = {
13
+ unsupportedDynamicPath: boolean;
14
+ usage: Record<string, string[]>;
15
+ };
16
+ /**
17
+ * 提取模板中的 ctx 使用情况。该函数只做静态扫描,不执行表达式。
18
+ */
19
+ export declare function extractVariableUsage(template: JSONValue): VariableUsageExtractionResult;
12
20
  /**
13
21
  * 提取模板中使用到的 ctx 顶层变量名集合。
14
22
  * - 支持点语法与顶层括号变量:ctx.user / ctx["user"]
@@ -28,111 +28,289 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
28
28
  var variable_usage_exports = {};
29
29
  __export(variable_usage_exports, {
30
30
  extractUsedVariableNames: () => extractUsedVariableNames,
31
- extractUsedVariablePaths: () => extractUsedVariablePaths
31
+ extractUsedVariablePaths: () => extractUsedVariablePaths,
32
+ extractVariableUsage: () => extractVariableUsage
32
33
  });
33
34
  module.exports = __toCommonJS(variable_usage_exports);
34
- function extractUsedVariableNames(template) {
35
- const result = /* @__PURE__ */ new Set();
36
- const visit = /* @__PURE__ */ __name((src) => {
37
- if (typeof src === "string") {
38
- const regex = /\{\{\s*([^}]+?)\s*\}\}/g;
39
- let m;
40
- while ((m = regex.exec(src)) !== null) {
41
- const expr = m[1];
42
- const pathRegex = /ctx\.([a-zA-Z_$][a-zA-Z0-9_$]*)/g;
43
- let pm;
44
- while ((pm = pathRegex.exec(expr)) !== null) {
45
- result.add(pm[1]);
46
- }
47
- const bracketVarRegex = /ctx\[\s*(["'])\s*([a-zA-Z_$][a-zA-Z0-9_$]*)\s*\1\s*\]/g;
48
- let bm;
49
- while ((bm = bracketVarRegex.exec(expr)) !== null) {
50
- result.add(bm[2]);
51
- }
35
+ function isObject(value) {
36
+ return !!value && typeof value === "object" && !Array.isArray(value);
37
+ }
38
+ __name(isObject, "isObject");
39
+ function isIdentifierStart(char) {
40
+ return !!char && /[a-zA-Z_$]/.test(char);
41
+ }
42
+ __name(isIdentifierStart, "isIdentifierStart");
43
+ function isIdentifierPart(char) {
44
+ return !!char && /[a-zA-Z0-9_$]/.test(char);
45
+ }
46
+ __name(isIdentifierPart, "isIdentifierPart");
47
+ function skipSpaces(input, index) {
48
+ let next = index;
49
+ while (/\s/.test(input[next] || "")) next += 1;
50
+ return next;
51
+ }
52
+ __name(skipSpaces, "skipSpaces");
53
+ function skipPostfix(input, index) {
54
+ let next = skipSpaces(input, index);
55
+ while (input[next] === "!") {
56
+ next = skipSpaces(input, next + 1);
57
+ }
58
+ return next;
59
+ }
60
+ __name(skipPostfix, "skipPostfix");
61
+ function previousNonSpaceIndex(input, index) {
62
+ let next = index;
63
+ while (next >= 0 && /\s/.test(input[next] || "")) next -= 1;
64
+ return next;
65
+ }
66
+ __name(previousNonSpaceIndex, "previousNonSpaceIndex");
67
+ function isGroupedCtxReference(input, start) {
68
+ const openParenIndex = previousNonSpaceIndex(input, start - 1);
69
+ if (input[openParenIndex] !== "(") return false;
70
+ const beforeOpenParenIndex = previousNonSpaceIndex(input, openParenIndex - 1);
71
+ if (beforeOpenParenIndex < 0) return true;
72
+ const beforeOpenParen = input[beforeOpenParenIndex];
73
+ return !isIdentifierPart(beforeOpenParen) && beforeOpenParen !== ")" && beforeOpenParen !== "]";
74
+ }
75
+ __name(isGroupedCtxReference, "isGroupedCtxReference");
76
+ function hasGroupedContinuation(input, index) {
77
+ const closeParenIndex = skipSpaces(input, index);
78
+ if (input[closeParenIndex] !== ")") return false;
79
+ const continuationIndex = skipSpaces(input, closeParenIndex + 1);
80
+ return input[continuationIndex] === "." || input[continuationIndex] === "[" || input.startsWith("?.", continuationIndex);
81
+ }
82
+ __name(hasGroupedContinuation, "hasGroupedContinuation");
83
+ function readIdentifier(input, index) {
84
+ if (!isIdentifierStart(input[index])) return null;
85
+ let next = index + 1;
86
+ while (isIdentifierPart(input[next])) next += 1;
87
+ return { value: input.slice(index, next), next };
88
+ }
89
+ __name(readIdentifier, "readIdentifier");
90
+ function readNumber(input, index) {
91
+ const match = input.slice(index).match(/^\d+/);
92
+ return match ? { value: match[0], next: index + match[0].length } : null;
93
+ }
94
+ __name(readNumber, "readNumber");
95
+ function readQuotedProperty(input, index) {
96
+ const quote = input[index];
97
+ if (quote !== '"' && quote !== "'") return null;
98
+ let next = index + 1;
99
+ let value = "";
100
+ while (next < input.length) {
101
+ const char = input[next];
102
+ if (char === "\\") {
103
+ value += input.slice(next, next + 2);
104
+ next += 2;
105
+ continue;
106
+ }
107
+ if (char === quote) {
108
+ return { value, next: next + 1 };
109
+ }
110
+ value += char;
111
+ next += 1;
112
+ }
113
+ return null;
114
+ }
115
+ __name(readQuotedProperty, "readQuotedProperty");
116
+ function readBracketAccess(input, index) {
117
+ let next = skipSpaces(input, index + 1);
118
+ const quoted = readQuotedProperty(input, next);
119
+ if (quoted) {
120
+ next = skipSpaces(input, quoted.next);
121
+ if (input[next] !== "]") return { dynamic: true };
122
+ return { dynamic: false, kind: "property", next: next + 1, value: quoted.value };
123
+ }
124
+ const number = readNumber(input, next);
125
+ if (number) {
126
+ next = skipSpaces(input, number.next);
127
+ if (input[next] !== "]") return { dynamic: true };
128
+ return { dynamic: false, kind: "index", next: next + 1, value: number.value };
129
+ }
130
+ return { dynamic: true };
131
+ }
132
+ __name(readBracketAccess, "readBracketAccess");
133
+ function appendPathSegment(segments, kind, value) {
134
+ if (kind === "property") {
135
+ segments.push(value);
136
+ return;
137
+ }
138
+ if (segments.length) {
139
+ segments[segments.length - 1] = `${segments[segments.length - 1]}[${value}]`;
140
+ return;
141
+ }
142
+ segments.push(`[${value}]`);
143
+ }
144
+ __name(appendPathSegment, "appendPathSegment");
145
+ function readAccess(input, index) {
146
+ const next = skipPostfix(input, index);
147
+ if (input[next] === "[") {
148
+ return readBracketAccess(input, next);
149
+ }
150
+ if (input.startsWith("?.", next)) {
151
+ const propertyStart = skipSpaces(input, next + 2);
152
+ if (input[propertyStart] === "[") {
153
+ return readBracketAccess(input, propertyStart);
154
+ }
155
+ const identifier = readIdentifier(input, propertyStart) || readNumber(input, propertyStart);
156
+ return identifier ? { dynamic: false, kind: "property", next: identifier.next, value: identifier.value } : null;
157
+ }
158
+ if (input[next] === ".") {
159
+ const propertyStart = skipSpaces(input, next + 1);
160
+ const identifier = readIdentifier(input, propertyStart) || readNumber(input, propertyStart);
161
+ return identifier ? { dynamic: false, kind: "property", next: identifier.next, value: identifier.value } : null;
162
+ }
163
+ return null;
164
+ }
165
+ __name(readAccess, "readAccess");
166
+ function parseDotOnlyCtxReference(expr) {
167
+ const trimmed = expr.trim();
168
+ const segment = "(?:[a-zA-Z_$][a-zA-Z0-9_$-]*|\\d+)";
169
+ const match = trimmed.match(new RegExp(`^ctx\\.([a-zA-Z_$][a-zA-Z0-9_$]*)(?:\\.(${segment}(?:\\.${segment})*))?$`));
170
+ if (!match) return null;
171
+ return {
172
+ methodCall: false,
173
+ path: match[2] || void 0,
174
+ unsupportedDynamicPath: false,
175
+ varName: match[1]
176
+ };
177
+ }
178
+ __name(parseDotOnlyCtxReference, "parseDotOnlyCtxReference");
179
+ function parseCtxReference(input, start) {
180
+ if (input.slice(start, start + 3) !== "ctx") return null;
181
+ if (isIdentifierPart(input[start - 1]) || isIdentifierPart(input[start + 3])) return null;
182
+ if (isGroupedCtxReference(input, start)) {
183
+ return {
184
+ next: start + 3,
185
+ reference: {
186
+ methodCall: false,
187
+ unsupportedDynamicPath: true,
188
+ varName: ""
52
189
  }
53
- } else if (Array.isArray(src)) {
54
- src.forEach(visit);
55
- } else if (src && typeof src === "object") {
56
- Object.values(src).forEach(visit);
190
+ };
191
+ }
192
+ const rootAccess = readAccess(input, start + 3);
193
+ if (!rootAccess) return null;
194
+ if (rootAccess.dynamic) {
195
+ return {
196
+ next: start + 3,
197
+ reference: {
198
+ methodCall: false,
199
+ unsupportedDynamicPath: true,
200
+ varName: ""
201
+ }
202
+ };
203
+ }
204
+ if (rootAccess.kind !== "property") {
205
+ return null;
206
+ }
207
+ const segments = [];
208
+ let next = rootAccess.next;
209
+ let methodCall = false;
210
+ let unsupportedDynamicPath = false;
211
+ while (next < input.length) {
212
+ const accessStart = skipPostfix(input, next);
213
+ if ((input[accessStart] === "(" || input.startsWith("?.(", accessStart)) && !segments.length) {
214
+ methodCall = true;
215
+ next = accessStart;
216
+ break;
57
217
  }
58
- }, "visit");
59
- visit(template);
60
- return result;
218
+ const access = readAccess(input, next);
219
+ if (!access) {
220
+ next = accessStart;
221
+ if (input[accessStart] === "[" || input[accessStart] === "." || input.startsWith("?.", accessStart)) {
222
+ unsupportedDynamicPath = true;
223
+ }
224
+ break;
225
+ }
226
+ if (access.dynamic) {
227
+ unsupportedDynamicPath = true;
228
+ next = accessStart;
229
+ break;
230
+ }
231
+ appendPathSegment(segments, access.kind, access.value);
232
+ next = access.next;
233
+ }
234
+ const path = segments.join(".");
235
+ return {
236
+ next: Math.max(next, rootAccess.next),
237
+ reference: {
238
+ methodCall,
239
+ path: path || void 0,
240
+ unsupportedDynamicPath: unsupportedDynamicPath || hasGroupedContinuation(input, next),
241
+ varName: rootAccess.value
242
+ }
243
+ };
61
244
  }
62
- __name(extractUsedVariableNames, "extractUsedVariableNames");
63
- function extractUsedVariablePaths(template) {
245
+ __name(parseCtxReference, "parseCtxReference");
246
+ function collectCtxReferences(expr) {
247
+ const dotOnly = parseDotOnlyCtxReference(expr);
248
+ if (dotOnly) return [dotOnly];
249
+ const references = [];
250
+ for (let index = 0; index < expr.length; index += 1) {
251
+ if (expr.slice(index, index + 3) !== "ctx") continue;
252
+ const parsed = parseCtxReference(expr, index);
253
+ if (!parsed) continue;
254
+ references.push(parsed.reference);
255
+ index = Math.max(index, parsed.next - 1);
256
+ }
257
+ return references;
258
+ }
259
+ __name(collectCtxReferences, "collectCtxReferences");
260
+ function addUsagePath(usage, varName, path, methodCall) {
261
+ usage[varName] = usage[varName] || [];
262
+ if (path) {
263
+ usage[varName].push(path);
264
+ } else if (methodCall && !usage[varName].includes("")) {
265
+ usage[varName].push("");
266
+ }
267
+ }
268
+ __name(addUsagePath, "addUsagePath");
269
+ function extractVariableUsage(template) {
64
270
  const usage = {};
271
+ let unsupportedDynamicPath = false;
272
+ const visitExpression = /* @__PURE__ */ __name((expr) => {
273
+ for (const reference of collectCtxReferences(expr)) {
274
+ if (reference.unsupportedDynamicPath) {
275
+ unsupportedDynamicPath = true;
276
+ continue;
277
+ }
278
+ if (!reference.varName) continue;
279
+ addUsagePath(usage, reference.varName, reference.path, reference.methodCall);
280
+ }
281
+ }, "visitExpression");
65
282
  const visit = /* @__PURE__ */ __name((src) => {
66
283
  if (typeof src === "string") {
67
284
  const regex = /\{\{\s*([^}]+?)\s*\}\}/g;
68
- let m;
69
- while ((m = regex.exec(src)) !== null) {
70
- const expr = m[1];
71
- const pathRegex = /ctx\.([a-zA-Z_$][a-zA-Z0-9_$]*)([^\s)]*)/g;
72
- let pm;
73
- while ((pm = pathRegex.exec(expr)) !== null) {
74
- const varName = pm[1];
75
- const after = pm[2] || "";
76
- usage[varName] = usage[varName] || [];
77
- if (after.startsWith(".")) {
78
- usage[varName].push(after.slice(1));
79
- } else if (after.startsWith("[")) {
80
- const mm = after.match(/^\[\s*(["'])\s*([^'"\]]+)\s*\1\s*\](.*)$/);
81
- if (mm) {
82
- const first = mm[2];
83
- const rest = mm[3] || "";
84
- usage[varName].push(`${first}${rest}`);
85
- } else {
86
- const mn = after.match(/^\[(\d+)\](.*)$/);
87
- if (mn) {
88
- const idx = mn[1];
89
- const rest = mn[2] || "";
90
- usage[varName].push(`[${idx}]${rest}`);
91
- }
92
- }
93
- } else if (after.startsWith("(")) {
94
- if (!usage[varName].length) usage[varName].push("");
95
- }
96
- }
97
- const bracketVarRegex = /ctx\[\s*(["'])\s*([a-zA-Z_$][a-zA-Z0-9_$]*)\s*\1\s*\]([^\s)]*)/g;
98
- let bm;
99
- while ((bm = bracketVarRegex.exec(expr)) !== null) {
100
- const varName = bm[2];
101
- const after = bm[3] || "";
102
- usage[varName] = usage[varName] || [];
103
- if (after.startsWith(".")) {
104
- usage[varName].push(after.slice(1));
105
- } else if (after.startsWith("[")) {
106
- const mm = after.match(/^\[\s*(["'])\s*([^'"\]]+)\s*\1\s*\](.*)$/);
107
- if (mm) {
108
- const first = mm[2];
109
- const rest = mm[3] || "";
110
- usage[varName].push(`${first}${rest}`);
111
- } else {
112
- const mn = after.match(/^\[(\d+)\](.*)$/);
113
- if (mn) {
114
- const idx = mn[1];
115
- const rest = mn[2] || "";
116
- usage[varName].push(`[${idx}]${rest}`);
117
- }
118
- }
119
- } else if (after.startsWith("(")) {
120
- if (!usage[varName].length) usage[varName].push("");
121
- }
122
- }
285
+ let match;
286
+ while ((match = regex.exec(src)) !== null) {
287
+ visitExpression(match[1]);
123
288
  }
124
- } else if (Array.isArray(src)) {
289
+ return;
290
+ }
291
+ if (Array.isArray(src)) {
125
292
  src.forEach(visit);
126
- } else if (src && typeof src === "object") {
293
+ return;
294
+ }
295
+ if (isObject(src)) {
127
296
  Object.values(src).forEach(visit);
128
297
  }
129
298
  }, "visit");
130
299
  visit(template);
131
- return usage;
300
+ return { unsupportedDynamicPath, usage };
301
+ }
302
+ __name(extractVariableUsage, "extractVariableUsage");
303
+ function extractUsedVariableNames(template) {
304
+ return new Set(Object.keys(extractVariableUsage(template).usage));
305
+ }
306
+ __name(extractUsedVariableNames, "extractUsedVariableNames");
307
+ function extractUsedVariablePaths(template) {
308
+ return extractVariableUsage(template).usage;
132
309
  }
133
310
  __name(extractUsedVariablePaths, "extractUsedVariablePaths");
134
311
  // Annotate the CommonJS export names for ESM import in node:
135
312
  0 && (module.exports = {
136
313
  extractUsedVariableNames,
137
- extractUsedVariablePaths
314
+ extractUsedVariablePaths,
315
+ extractVariableUsage
138
316
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nocobase/utils",
3
- "version": "2.2.0-beta.8",
3
+ "version": "2.2.0",
4
4
  "main": "lib/index.js",
5
5
  "types": "./lib/index.d.ts",
6
6
  "license": "Apache-2.0",
@@ -21,5 +21,5 @@
21
21
  "object-path": "^0.11.8",
22
22
  "ses": "^1.14.0"
23
23
  },
24
- "gitHead": "fa2502c1e9faf6d74b3f51b42dbc6546638d46af"
24
+ "gitHead": "d5052cae37177054c236f1bf7bba3aed6ef029d0"
25
25
  }