@variojs/core 0.1.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.
Files changed (209) hide show
  1. package/__tests__/README.md +101 -0
  2. package/__tests__/errors.test.ts +139 -0
  3. package/__tests__/expression/cache.test.ts +118 -0
  4. package/__tests__/expression/compiler.test.ts +111 -0
  5. package/__tests__/expression/evaluate.test.ts +95 -0
  6. package/__tests__/expression/parser.test.ts +57 -0
  7. package/__tests__/expression/whitelist.test.ts +59 -0
  8. package/__tests__/performance.test.ts +379 -0
  9. package/__tests__/runtime/create-context.test.ts +78 -0
  10. package/__tests__/runtime/loop-context-pool.test.ts +74 -0
  11. package/__tests__/runtime/path.test.ts +128 -0
  12. package/__tests__/vm/executor-timeout.test.ts +117 -0
  13. package/__tests__/vm/executor.test.ts +173 -0
  14. package/__tests__/vm/handlers/array.test.ts +113 -0
  15. package/__tests__/vm/handlers/call.test.ts +93 -0
  16. package/dist/errors.d.ts +100 -0
  17. package/dist/errors.d.ts.map +1 -0
  18. package/dist/errors.js +132 -0
  19. package/dist/errors.js.map +1 -0
  20. package/dist/expression/cache.d.ts +53 -0
  21. package/dist/expression/cache.d.ts.map +1 -0
  22. package/dist/expression/cache.js +158 -0
  23. package/dist/expression/cache.js.map +1 -0
  24. package/dist/expression/compiler.d.ts +34 -0
  25. package/dist/expression/compiler.d.ts.map +1 -0
  26. package/dist/expression/compiler.js +123 -0
  27. package/dist/expression/compiler.js.map +1 -0
  28. package/dist/expression/dependencies.d.ts +17 -0
  29. package/dist/expression/dependencies.d.ts.map +1 -0
  30. package/dist/expression/dependencies.js +106 -0
  31. package/dist/expression/dependencies.js.map +1 -0
  32. package/dist/expression/evaluate.d.ts +22 -0
  33. package/dist/expression/evaluate.d.ts.map +1 -0
  34. package/dist/expression/evaluate.js +75 -0
  35. package/dist/expression/evaluate.js.map +1 -0
  36. package/dist/expression/evaluator.d.ts +22 -0
  37. package/dist/expression/evaluator.d.ts.map +1 -0
  38. package/dist/expression/evaluator.js +506 -0
  39. package/dist/expression/evaluator.js.map +1 -0
  40. package/dist/expression/index.d.ts +12 -0
  41. package/dist/expression/index.d.ts.map +1 -0
  42. package/dist/expression/index.js +12 -0
  43. package/dist/expression/index.js.map +1 -0
  44. package/dist/expression/parser.d.ts +15 -0
  45. package/dist/expression/parser.d.ts.map +1 -0
  46. package/dist/expression/parser.js +42 -0
  47. package/dist/expression/parser.js.map +1 -0
  48. package/dist/expression/utils.d.ts +46 -0
  49. package/dist/expression/utils.d.ts.map +1 -0
  50. package/dist/expression/utils.js +78 -0
  51. package/dist/expression/utils.js.map +1 -0
  52. package/dist/expression/whitelist.d.ts +24 -0
  53. package/dist/expression/whitelist.d.ts.map +1 -0
  54. package/dist/expression/whitelist.js +198 -0
  55. package/dist/expression/whitelist.js.map +1 -0
  56. package/dist/index.d.ts +19 -0
  57. package/dist/index.d.ts.map +1 -0
  58. package/dist/index.js +21 -0
  59. package/dist/index.js.map +1 -0
  60. package/dist/runtime/context.d.ts +8 -0
  61. package/dist/runtime/context.d.ts.map +1 -0
  62. package/dist/runtime/context.js +7 -0
  63. package/dist/runtime/context.js.map +1 -0
  64. package/dist/runtime/create-context.d.ts +50 -0
  65. package/dist/runtime/create-context.d.ts.map +1 -0
  66. package/dist/runtime/create-context.js +73 -0
  67. package/dist/runtime/create-context.js.map +1 -0
  68. package/dist/runtime/index.d.ts +10 -0
  69. package/dist/runtime/index.d.ts.map +1 -0
  70. package/dist/runtime/index.js +10 -0
  71. package/dist/runtime/index.js.map +1 -0
  72. package/dist/runtime/loop-context-pool.d.ts +58 -0
  73. package/dist/runtime/loop-context-pool.d.ts.map +1 -0
  74. package/dist/runtime/loop-context-pool.js +114 -0
  75. package/dist/runtime/loop-context-pool.js.map +1 -0
  76. package/dist/runtime/path.d.ts +114 -0
  77. package/dist/runtime/path.d.ts.map +1 -0
  78. package/dist/runtime/path.js +302 -0
  79. package/dist/runtime/path.js.map +1 -0
  80. package/dist/runtime/proxy.d.ts +18 -0
  81. package/dist/runtime/proxy.d.ts.map +1 -0
  82. package/dist/runtime/proxy.js +53 -0
  83. package/dist/runtime/proxy.js.map +1 -0
  84. package/dist/runtime/sandbox.d.ts +20 -0
  85. package/dist/runtime/sandbox.d.ts.map +1 -0
  86. package/dist/runtime/sandbox.js +32 -0
  87. package/dist/runtime/sandbox.js.map +1 -0
  88. package/dist/types.d.ts +175 -0
  89. package/dist/types.d.ts.map +1 -0
  90. package/dist/types.js +22 -0
  91. package/dist/types.js.map +1 -0
  92. package/dist/vm/action.d.ts +11 -0
  93. package/dist/vm/action.d.ts.map +1 -0
  94. package/dist/vm/action.js +10 -0
  95. package/dist/vm/action.js.map +1 -0
  96. package/dist/vm/errors.d.ts +5 -0
  97. package/dist/vm/errors.d.ts.map +1 -0
  98. package/dist/vm/errors.js +5 -0
  99. package/dist/vm/errors.js.map +1 -0
  100. package/dist/vm/executor.d.ts +35 -0
  101. package/dist/vm/executor.d.ts.map +1 -0
  102. package/dist/vm/executor.js +137 -0
  103. package/dist/vm/executor.js.map +1 -0
  104. package/dist/vm/handlers/array/pop.d.ts +12 -0
  105. package/dist/vm/handlers/array/pop.d.ts.map +1 -0
  106. package/dist/vm/handlers/array/pop.js +28 -0
  107. package/dist/vm/handlers/array/pop.js.map +1 -0
  108. package/dist/vm/handlers/array/push.d.ts +13 -0
  109. package/dist/vm/handlers/array/push.d.ts.map +1 -0
  110. package/dist/vm/handlers/array/push.js +42 -0
  111. package/dist/vm/handlers/array/push.js.map +1 -0
  112. package/dist/vm/handlers/array/shift.d.ts +12 -0
  113. package/dist/vm/handlers/array/shift.d.ts.map +1 -0
  114. package/dist/vm/handlers/array/shift.js +28 -0
  115. package/dist/vm/handlers/array/shift.js.map +1 -0
  116. package/dist/vm/handlers/array/splice.d.ts +12 -0
  117. package/dist/vm/handlers/array/splice.d.ts.map +1 -0
  118. package/dist/vm/handlers/array/splice.js +59 -0
  119. package/dist/vm/handlers/array/splice.js.map +1 -0
  120. package/dist/vm/handlers/array/unshift.d.ts +13 -0
  121. package/dist/vm/handlers/array/unshift.d.ts.map +1 -0
  122. package/dist/vm/handlers/array/unshift.js +42 -0
  123. package/dist/vm/handlers/array/unshift.js.map +1 -0
  124. package/dist/vm/handlers/array/utils.d.ts +10 -0
  125. package/dist/vm/handlers/array/utils.d.ts.map +1 -0
  126. package/dist/vm/handlers/array/utils.js +33 -0
  127. package/dist/vm/handlers/array/utils.js.map +1 -0
  128. package/dist/vm/handlers/batch.d.ts +12 -0
  129. package/dist/vm/handlers/batch.d.ts.map +1 -0
  130. package/dist/vm/handlers/batch.js +40 -0
  131. package/dist/vm/handlers/batch.js.map +1 -0
  132. package/dist/vm/handlers/call.d.ts +14 -0
  133. package/dist/vm/handlers/call.d.ts.map +1 -0
  134. package/dist/vm/handlers/call.js +65 -0
  135. package/dist/vm/handlers/call.js.map +1 -0
  136. package/dist/vm/handlers/emit.d.ts +12 -0
  137. package/dist/vm/handlers/emit.d.ts.map +1 -0
  138. package/dist/vm/handlers/emit.js +26 -0
  139. package/dist/vm/handlers/emit.js.map +1 -0
  140. package/dist/vm/handlers/if.d.ts +13 -0
  141. package/dist/vm/handlers/if.d.ts.map +1 -0
  142. package/dist/vm/handlers/if.js +35 -0
  143. package/dist/vm/handlers/if.js.map +1 -0
  144. package/dist/vm/handlers/index.d.ts +11 -0
  145. package/dist/vm/handlers/index.d.ts.map +1 -0
  146. package/dist/vm/handlers/index.js +46 -0
  147. package/dist/vm/handlers/index.js.map +1 -0
  148. package/dist/vm/handlers/log.d.ts +12 -0
  149. package/dist/vm/handlers/log.d.ts.map +1 -0
  150. package/dist/vm/handlers/log.js +41 -0
  151. package/dist/vm/handlers/log.js.map +1 -0
  152. package/dist/vm/handlers/loop.d.ts +12 -0
  153. package/dist/vm/handlers/loop.d.ts.map +1 -0
  154. package/dist/vm/handlers/loop.js +71 -0
  155. package/dist/vm/handlers/loop.js.map +1 -0
  156. package/dist/vm/handlers/navigate.d.ts +12 -0
  157. package/dist/vm/handlers/navigate.d.ts.map +1 -0
  158. package/dist/vm/handlers/navigate.js +43 -0
  159. package/dist/vm/handlers/navigate.js.map +1 -0
  160. package/dist/vm/handlers/set.d.ts +15 -0
  161. package/dist/vm/handlers/set.d.ts.map +1 -0
  162. package/dist/vm/handlers/set.js +30 -0
  163. package/dist/vm/handlers/set.js.map +1 -0
  164. package/dist/vm/index.d.ts +8 -0
  165. package/dist/vm/index.d.ts.map +1 -0
  166. package/dist/vm/index.js +7 -0
  167. package/dist/vm/index.js.map +1 -0
  168. package/package.json +34 -0
  169. package/src/errors.ts +194 -0
  170. package/src/expression/README.md +192 -0
  171. package/src/expression/cache.ts +199 -0
  172. package/src/expression/compiler.ts +144 -0
  173. package/src/expression/dependencies.ts +116 -0
  174. package/src/expression/evaluate.ts +95 -0
  175. package/src/expression/evaluator.ts +640 -0
  176. package/src/expression/index.ts +27 -0
  177. package/src/expression/parser.ts +54 -0
  178. package/src/expression/utils.ts +89 -0
  179. package/src/expression/whitelist.ts +224 -0
  180. package/src/globals.d.ts +10 -0
  181. package/src/index.ts +72 -0
  182. package/src/runtime/context.ts +8 -0
  183. package/src/runtime/create-context.ts +133 -0
  184. package/src/runtime/index.ts +28 -0
  185. package/src/runtime/loop-context-pool.ts +134 -0
  186. package/src/runtime/path.ts +372 -0
  187. package/src/runtime/proxy.ts +66 -0
  188. package/src/runtime/sandbox.ts +43 -0
  189. package/src/types.ts +177 -0
  190. package/src/vm/errors.ts +10 -0
  191. package/src/vm/executor.ts +210 -0
  192. package/src/vm/handlers/array/pop.ts +47 -0
  193. package/src/vm/handlers/array/push.ts +68 -0
  194. package/src/vm/handlers/array/shift.ts +47 -0
  195. package/src/vm/handlers/array/splice.ts +78 -0
  196. package/src/vm/handlers/array/unshift.ts +68 -0
  197. package/src/vm/handlers/array/utils.ts +41 -0
  198. package/src/vm/handlers/batch.ts +57 -0
  199. package/src/vm/handlers/call.ts +92 -0
  200. package/src/vm/handlers/emit.ts +39 -0
  201. package/src/vm/handlers/if.ts +48 -0
  202. package/src/vm/handlers/index.ts +52 -0
  203. package/src/vm/handlers/log.ts +54 -0
  204. package/src/vm/handlers/loop.ts +102 -0
  205. package/src/vm/handlers/navigate.ts +64 -0
  206. package/src/vm/handlers/set.ts +43 -0
  207. package/src/vm/index.ts +8 -0
  208. package/tsconfig.json +17 -0
  209. package/vitest.config.ts +30 -0
@@ -0,0 +1,22 @@
1
+ /**
2
+ * 表达式求值入口函数
3
+ *
4
+ * 整合解析、验证、缓存、求值流程
5
+ */
6
+ import type { RuntimeContext, ExpressionOptions } from '../types.js';
7
+ /**
8
+ * 求值表达式(完整流程)
9
+ *
10
+ * @param expr 表达式字符串
11
+ * @param ctx 运行时上下文
12
+ * @returns 求值结果(类型无法静态推导,返回 unknown)
13
+ *
14
+ * 注意:表达式求值结果类型无法在编译时确定,因为:
15
+ * 1. 表达式是运行时字符串
16
+ * 2. 状态类型是动态的
17
+ * 3. 表达式可能返回任意类型
18
+ *
19
+ * 如果需要类型安全,应在使用结果时进行类型守卫或类型断言
20
+ */
21
+ export declare function evaluate(expr: string, ctx: RuntimeContext, options?: ExpressionOptions): unknown;
22
+ //# sourceMappingURL=evaluate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"evaluate.d.ts","sourceRoot":"","sources":["../../src/expression/evaluate.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAA;AAYpE;;;;;;;;;;;;;GAaG;AACH,wBAAgB,QAAQ,CACtB,IAAI,EAAE,MAAM,EACZ,GAAG,EAAE,cAAc,EACnB,OAAO,GAAE,iBAAsB,GAC9B,OAAO,CA0DT"}
@@ -0,0 +1,75 @@
1
+ /**
2
+ * 表达式求值入口函数
3
+ *
4
+ * 整合解析、验证、缓存、求值流程
5
+ */
6
+ import { ExpressionError, ErrorCodes } from '../errors.js';
7
+ import { parseExpression } from './parser.js';
8
+ import { validateAST } from './whitelist.js';
9
+ import { evaluateExpression } from './evaluator.js';
10
+ import { extractDependencies } from './dependencies.js';
11
+ import { getCompiledExpression } from './compiler.js';
12
+ import { getCachedExpression, setCachedExpression, } from './cache.js';
13
+ /**
14
+ * 求值表达式(完整流程)
15
+ *
16
+ * @param expr 表达式字符串
17
+ * @param ctx 运行时上下文
18
+ * @returns 求值结果(类型无法静态推导,返回 unknown)
19
+ *
20
+ * 注意:表达式求值结果类型无法在编译时确定,因为:
21
+ * 1. 表达式是运行时字符串
22
+ * 2. 状态类型是动态的
23
+ * 3. 表达式可能返回任意类型
24
+ *
25
+ * 如果需要类型安全,应在使用结果时进行类型守卫或类型断言
26
+ */
27
+ export function evaluate(expr, ctx, options = {}) {
28
+ try {
29
+ // 合并选项:ctx.$exprOptions 优先级低于直接传入的 options
30
+ const mergedOptions = {
31
+ ...ctx.$exprOptions,
32
+ ...options
33
+ };
34
+ // 1. 检查结果缓存
35
+ const cached = getCachedExpression(expr, ctx);
36
+ if (cached !== null) {
37
+ return cached;
38
+ }
39
+ // 2. 解析为 AST
40
+ const ast = parseExpression(expr);
41
+ // 3. AST 白名单校验(传递 allowGlobals 和 maxNestingDepth 选项)
42
+ validateAST(ast, {
43
+ allowGlobals: mergedOptions.allowGlobals,
44
+ maxNestingDepth: mergedOptions.maxNestingDepth
45
+ });
46
+ // 4. 尝试使用编译缓存(简单表达式)
47
+ const compiled = getCompiledExpression(expr, ast);
48
+ if (compiled) {
49
+ const result = compiled(ctx);
50
+ // 提取依赖并缓存结果
51
+ const dependencies = extractDependencies(ast);
52
+ setCachedExpression(expr, result, dependencies, ctx);
53
+ return result;
54
+ }
55
+ // 5. 复杂表达式,使用解释执行
56
+ const result = evaluateExpression(ast, ctx, mergedOptions);
57
+ // 6. 提取依赖并缓存
58
+ const dependencies = extractDependencies(ast);
59
+ setCachedExpression(expr, result, dependencies, ctx);
60
+ return result;
61
+ }
62
+ catch (error) {
63
+ if (error instanceof ExpressionError) {
64
+ throw error;
65
+ }
66
+ const errorMessage = error instanceof Error ? error.message : String(error);
67
+ throw new ExpressionError(expr, `Expression evaluation failed: ${errorMessage}`, ErrorCodes.EXPRESSION_EVALUATION_ERROR, {
68
+ metadata: {
69
+ originalError: error instanceof Error ? error.name : 'Unknown',
70
+ stack: error instanceof Error ? error.stack?.split('\n').slice(0, 5) : undefined
71
+ }
72
+ });
73
+ }
74
+ }
75
+ //# sourceMappingURL=evaluate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"evaluate.js","sourceRoot":"","sources":["../../src/expression/evaluate.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AAC1D,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA;AAC7C,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAA;AAC5C,OAAO,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAA;AACnD,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAA;AACvD,OAAO,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAA;AACrD,OAAO,EACL,mBAAmB,EACnB,mBAAmB,GACpB,MAAM,YAAY,CAAA;AAEnB;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,QAAQ,CACtB,IAAY,EACZ,GAAmB,EACnB,UAA6B,EAAE;IAE/B,IAAI,CAAC;QACH,2CAA2C;QAC3C,MAAM,aAAa,GAAG;YACpB,GAAG,GAAG,CAAC,YAAY;YACnB,GAAG,OAAO;SACX,CAAA;QAED,YAAY;QACZ,MAAM,MAAM,GAAG,mBAAmB,CAAC,IAAI,EAAE,GAAG,CAAC,CAAA;QAC7C,IAAI,MAAM,KAAK,IAAI,EAAE,CAAC;YACpB,OAAO,MAAM,CAAA;QACf,CAAC;QAED,aAAa;QACb,MAAM,GAAG,GAAG,eAAe,CAAC,IAAI,CAAC,CAAA;QAEjC,qDAAqD;QACrD,WAAW,CAAC,GAAG,EAAE;YACf,YAAY,EAAE,aAAa,CAAC,YAAY;YACxC,eAAe,EAAE,aAAa,CAAC,eAAe;SAC/C,CAAC,CAAA;QAEF,qBAAqB;QACrB,MAAM,QAAQ,GAAG,qBAAqB,CAAC,IAAI,EAAE,GAAG,CAAC,CAAA;QACjD,IAAI,QAAQ,EAAE,CAAC;YACb,MAAM,MAAM,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAA;YAC5B,YAAY;YACZ,MAAM,YAAY,GAAG,mBAAmB,CAAC,GAAG,CAAC,CAAA;YAC7C,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,YAAY,EAAE,GAAG,CAAC,CAAA;YACpD,OAAO,MAAM,CAAA;QACf,CAAC;QAED,kBAAkB;QAClB,MAAM,MAAM,GAAG,kBAAkB,CAAC,GAAG,EAAE,GAAG,EAAE,aAAa,CAAC,CAAA;QAE1D,aAAa;QACb,MAAM,YAAY,GAAG,mBAAmB,CAAC,GAAG,CAAC,CAAA;QAC7C,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,YAAY,EAAE,GAAG,CAAC,CAAA;QAEpD,OAAO,MAAM,CAAA;IACf,CAAC;IAAC,OAAO,KAAc,EAAE,CAAC;QACxB,IAAI,KAAK,YAAY,eAAe,EAAE,CAAC;YACrC,MAAM,KAAK,CAAA;QACb,CAAC;QACD,MAAM,YAAY,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;QAC3E,MAAM,IAAI,eAAe,CACvB,IAAI,EACJ,iCAAiC,YAAY,EAAE,EAC/C,UAAU,CAAC,2BAA2B,EACtC;YACE,QAAQ,EAAE;gBACR,aAAa,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS;gBAC9D,KAAK,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS;aACjF;SACF,CACF,CAAA;IACH,CAAC;AACH,CAAC"}
@@ -0,0 +1,22 @@
1
+ /**
2
+ * 表达式求值器
3
+ *
4
+ * 功能:
5
+ * - 安全求值 AST
6
+ * - 支持白名单函数调用
7
+ * - 执行步数/时间限制
8
+ */
9
+ import type * as ESTree from '@babel/types';
10
+ import type { RuntimeContext, ExpressionOptions } from '../types.js';
11
+ /**
12
+ * 安全求值 AST
13
+ *
14
+ * @param ast AST 节点
15
+ * @param ctx 运行时上下文
16
+ * @param options 求值选项
17
+ * @returns 求值结果(类型无法静态推导)
18
+ *
19
+ * 注意:表达式求值结果类型无法在编译时确定,返回 unknown
20
+ */
21
+ export declare function evaluateExpression(ast: ESTree.Node, ctx: RuntimeContext, options?: ExpressionOptions): unknown;
22
+ //# sourceMappingURL=evaluator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"evaluator.d.ts","sourceRoot":"","sources":["../../src/expression/evaluator.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,KAAK,MAAM,MAAM,cAAc,CAAA;AAC3C,OAAO,KAAK,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAA;AAoFpE;;;;;;;;;GASG;AACH,wBAAgB,kBAAkB,CAChC,GAAG,EAAE,MAAM,CAAC,IAAI,EAChB,GAAG,EAAE,cAAc,EACnB,OAAO,GAAE,iBAAsB,GAC9B,OAAO,CAmhBT"}
@@ -0,0 +1,506 @@
1
+ /**
2
+ * 表达式求值器
3
+ *
4
+ * 功能:
5
+ * - 安全求值 AST
6
+ * - 支持白名单函数调用
7
+ * - 执行步数/时间限制
8
+ */
9
+ import { ExpressionError, ErrorCodes } from '../errors.js';
10
+ import { isSafePropertyAccess } from '../runtime/sandbox.js';
11
+ /**
12
+ * 白名单全局函数
13
+ */
14
+ const WHITELISTED_GLOBALS = new Set([
15
+ 'String', 'Number', 'Boolean', 'BigInt', 'Symbol',
16
+ 'Array', 'Object', 'Math', 'Date',
17
+ ]);
18
+ /**
19
+ * 白名单函数(带命名空间)
20
+ */
21
+ const WHITELISTED_FUNCTIONS = new Set([
22
+ 'Array.isArray',
23
+ 'Object.is',
24
+ 'Number.isFinite',
25
+ 'Number.isInteger',
26
+ 'Number.isNaN',
27
+ 'Number.isSafeInteger',
28
+ 'Math.abs',
29
+ 'Math.round',
30
+ 'Math.floor',
31
+ 'Math.ceil',
32
+ 'Math.random',
33
+ 'Math.max',
34
+ 'Math.min',
35
+ 'Date.now',
36
+ ]);
37
+ /**
38
+ * 安全的数组方法(只读或返回新数组,不修改原状态)
39
+ */
40
+ const SAFE_ARRAY_METHODS = new Set([
41
+ // 返回新数组
42
+ 'slice',
43
+ 'concat',
44
+ 'filter',
45
+ 'map',
46
+ 'flat',
47
+ 'flatMap',
48
+ 'toReversed', // ES2023 不修改原数组的 reverse
49
+ 'toSorted', // ES2023 不修改原数组的 sort
50
+ 'toSpliced', // ES2023 不修改原数组的 splice
51
+ 'with', // ES2023 不修改原数组的索引赋值
52
+ // 只读方法
53
+ 'indexOf',
54
+ 'lastIndexOf',
55
+ 'includes',
56
+ 'find',
57
+ 'findIndex',
58
+ 'findLast',
59
+ 'findLastIndex',
60
+ 'every',
61
+ 'some',
62
+ 'at',
63
+ // 返回字符串
64
+ 'join',
65
+ 'toString',
66
+ 'toLocaleString',
67
+ // 在链式调用中安全使用(如 slice().reverse())
68
+ 'reverse',
69
+ 'sort',
70
+ ]);
71
+ /**
72
+ * 运行时辅助函数
73
+ */
74
+ const RUNTIME_HELPERS = {
75
+ '$truncate': (str, length) => {
76
+ if (typeof str !== 'string')
77
+ return str;
78
+ const len = typeof length === 'number' ? length : 0;
79
+ return str.length > len ? str.slice(0, len) + '...' : str;
80
+ },
81
+ '$format': (date, _format) => {
82
+ const d = typeof date === 'number' ? new Date(date) : date;
83
+ // 简单格式化,可根据需要扩展
84
+ // format 参数暂未实现,保留接口以便未来扩展
85
+ return d.toISOString();
86
+ },
87
+ };
88
+ /**
89
+ * 安全求值 AST
90
+ *
91
+ * @param ast AST 节点
92
+ * @param ctx 运行时上下文
93
+ * @param options 求值选项
94
+ * @returns 求值结果(类型无法静态推导)
95
+ *
96
+ * 注意:表达式求值结果类型无法在编译时确定,返回 unknown
97
+ */
98
+ export function evaluateExpression(ast, ctx, options = {}) {
99
+ const mergedOptions = {
100
+ ...ctx.$exprOptions,
101
+ ...options
102
+ };
103
+ const allowGlobals = mergedOptions.allowGlobals === true;
104
+ const maxSteps = mergedOptions.maxSteps ?? 1000;
105
+ const timeout = mergedOptions.timeout ?? 100;
106
+ let stepCount = 0;
107
+ const startTime = Date.now();
108
+ function getGlobalValueByName(name) {
109
+ if (!allowGlobals)
110
+ return undefined;
111
+ // 只有在 allowGlobals 为 true 时才返回全局对象
112
+ if (name === 'globalThis')
113
+ return globalThis;
114
+ if (name === 'window' && typeof window !== 'undefined')
115
+ return window;
116
+ if (name === 'document' && typeof document !== 'undefined')
117
+ return document;
118
+ if (name === 'global' && typeof global !== 'undefined')
119
+ return global;
120
+ if (name === 'self' && typeof self !== 'undefined')
121
+ return self;
122
+ // 只有在 allowGlobals 为 true 时才访问 globalThis 的属性
123
+ return globalThis[name];
124
+ }
125
+ function isGlobalObjectName(name) {
126
+ return ['window', 'document', 'global', 'globalThis', 'self'].includes(name);
127
+ }
128
+ function checkLimits() {
129
+ stepCount++;
130
+ if (stepCount > maxSteps) {
131
+ throw new ExpressionError(JSON.stringify(ast), `Expression evaluation exceeded max steps (${maxSteps})`, ErrorCodes.EXPRESSION_MAX_STEPS_EXCEEDED, {
132
+ metadata: {
133
+ maxSteps,
134
+ currentSteps: stepCount
135
+ }
136
+ });
137
+ }
138
+ if (Date.now() - startTime > timeout) {
139
+ throw new ExpressionError(JSON.stringify(ast), `Expression evaluation exceeded timeout (${timeout}ms)`, ErrorCodes.EXPRESSION_TIMEOUT, {
140
+ metadata: {
141
+ timeout,
142
+ elapsedTime: Date.now() - startTime
143
+ }
144
+ });
145
+ }
146
+ }
147
+ function evaluate(node) {
148
+ checkLimits();
149
+ // 使用类型断言处理 node.type,因为 @babel/types 的类型定义可能不完整
150
+ // TypeScript 的严格类型检查可能不识别某些节点类型,使用类型断言绕过
151
+ const nodeType = node.type;
152
+ switch (nodeType) {
153
+ case 'NumericLiteral':
154
+ case 'StringLiteral':
155
+ case 'BooleanLiteral':
156
+ case 'BigIntLiteral':
157
+ case 'DecimalLiteral':
158
+ case 'RegExpLiteral': {
159
+ // 这些字面量类型都有 value 属性
160
+ return node.value;
161
+ }
162
+ case 'NullLiteral': {
163
+ return null;
164
+ }
165
+ case 'Literal': {
166
+ const literal = node;
167
+ // 处理不同类型的字面量
168
+ // @babel/types 的 Literal 类型包含多种字面量类型
169
+ // 使用类型守卫安全访问 value 属性
170
+ if ('value' in literal && literal.value !== undefined) {
171
+ return literal.value;
172
+ }
173
+ // NullLiteral 或 BigIntLiteral 可能没有 value 属性,或 value 为 null
174
+ // 检查类型名称
175
+ const literalType = literal.type;
176
+ if (literalType === 'NullLiteral') {
177
+ return null;
178
+ }
179
+ // 其他情况:尝试访问 value,如果不存在则返回 undefined
180
+ return literal.value ?? undefined;
181
+ }
182
+ case 'Identifier': {
183
+ const name = node.name;
184
+ // 检查是否为全局对象名称(无论是否存在,都应该被禁止,除非 allowGlobals)
185
+ // 必须在检查 ctx 之前,防止 ctx 中有同名属性绕过检查
186
+ if (isGlobalObjectName(name) && !allowGlobals) {
187
+ throw new ExpressionError(name, `Access to global "${name}" is not allowed in expressions`, ErrorCodes.EXPRESSION_UNSAFE_ACCESS);
188
+ }
189
+ // 从上下文获取值(优先从 ctx,但全局对象名称已经被上面的检查拦截)
190
+ // 注意:即使 ctx 中有 window/document 等属性,也不应该访问(安全考虑)
191
+ if (name in ctx && !isGlobalObjectName(name)) {
192
+ return ctx[name];
193
+ }
194
+ // 允许直接访问全局对象(由 allowGlobals 控制)
195
+ if (allowGlobals) {
196
+ const globalValue = getGlobalValueByName(name);
197
+ if (globalValue !== undefined) {
198
+ return globalValue;
199
+ }
200
+ }
201
+ // 如果是白名单全局函数,允许访问(但仅用于函数调用,这里返回 undefined)
202
+ if (WHITELISTED_GLOBALS.has(name)) {
203
+ return globalThis[name];
204
+ }
205
+ // 未定义的标识符返回 undefined(而不是抛出错误,允许可选链等场景)
206
+ return undefined;
207
+ }
208
+ case 'MemberExpression': {
209
+ const member = node;
210
+ const object = evaluate(member.object);
211
+ // null/undefined 检查
212
+ if (object == null) {
213
+ return undefined;
214
+ }
215
+ // 禁止访问全局对象(检查对象是否为全局对象)
216
+ if (!allowGlobals) {
217
+ // 检查对象是否为全局对象
218
+ const isGlobal = object === globalThis ||
219
+ (typeof window !== 'undefined' && object === window) ||
220
+ (typeof global !== 'undefined' && object === global) ||
221
+ (typeof self !== 'undefined' && object === self);
222
+ if (isGlobal) {
223
+ throw new ExpressionError(JSON.stringify(node), 'Access to global object is not allowed in expressions', ErrorCodes.EXPRESSION_UNSAFE_ACCESS);
224
+ }
225
+ }
226
+ if (member.computed) {
227
+ // 计算属性:obj[key]
228
+ const key = evaluate(member.property);
229
+ return object[key];
230
+ }
231
+ else {
232
+ // 静态属性:obj.prop
233
+ const prop = member.property.name;
234
+ return object[prop];
235
+ }
236
+ }
237
+ case 'OptionalMemberExpression': {
238
+ const member = node;
239
+ const object = evaluate(member.object);
240
+ // null/undefined 检查(可选链特性)
241
+ if (object == null) {
242
+ return undefined;
243
+ }
244
+ // 禁止访问全局对象(检查对象是否为全局对象)
245
+ if (!allowGlobals) {
246
+ // 检查对象是否为全局对象
247
+ const isGlobal = object === globalThis ||
248
+ (typeof window !== 'undefined' && object === window) ||
249
+ (typeof global !== 'undefined' && object === global) ||
250
+ (typeof self !== 'undefined' && object === self);
251
+ if (isGlobal) {
252
+ throw new ExpressionError(JSON.stringify(node), 'Access to global object is not allowed in expressions', ErrorCodes.EXPRESSION_UNSAFE_ACCESS);
253
+ }
254
+ }
255
+ if (member.computed) {
256
+ // 计算属性:obj?.[key]
257
+ const key = evaluate(member.property);
258
+ return object?.[key];
259
+ }
260
+ else {
261
+ // 静态属性:obj?.prop
262
+ const prop = member.property.name;
263
+ return object?.[prop];
264
+ }
265
+ }
266
+ case 'BinaryExpression': {
267
+ const binary = node;
268
+ const left = evaluate(binary.left);
269
+ const right = evaluate(binary.right);
270
+ const operator = binary.operator;
271
+ // 类型安全的二元运算
272
+ switch (operator) {
273
+ case '+': return left + right;
274
+ case '-': return left - right;
275
+ case '*': return left * right;
276
+ case '/': return left / right;
277
+ case '%': return left % right;
278
+ case '**': return left ** right;
279
+ case '==': return left == right;
280
+ case '!=': return left != right;
281
+ case '===': return left === right;
282
+ case '!==': return left !== right;
283
+ case '<': return left < right;
284
+ case '<=': return left <= right;
285
+ case '>': return left > right;
286
+ case '>=': return left >= right;
287
+ case '<<': return left << right;
288
+ case '>>': return left >> right;
289
+ case '>>>': return left >>> right;
290
+ case '&': return left & right;
291
+ case '|': return left | right;
292
+ case '^': return left ^ right;
293
+ case 'in': return left in right;
294
+ case 'instanceof': {
295
+ // instanceof 需要检查 right 是否为构造函数
296
+ if (typeof right !== 'function' && typeof right !== 'object') {
297
+ throw new ExpressionError(JSON.stringify(node), 'Right-hand side of instanceof must be a constructor', ErrorCodes.EXPRESSION_EVALUATION_ERROR);
298
+ }
299
+ return left instanceof right;
300
+ }
301
+ default:
302
+ throw new ExpressionError(JSON.stringify(node), `Unsupported binary operator: ${operator}`, ErrorCodes.EXPRESSION_EVALUATION_ERROR);
303
+ }
304
+ }
305
+ case 'LogicalExpression': {
306
+ const logical = node;
307
+ const left = evaluate(logical.left);
308
+ const operator = logical.operator;
309
+ if (operator === '&&') {
310
+ return left && evaluate(logical.right);
311
+ }
312
+ else if (operator === '||') {
313
+ return left || evaluate(logical.right);
314
+ }
315
+ else if (operator === '??') {
316
+ return left ?? evaluate(logical.right);
317
+ }
318
+ throw new ExpressionError(JSON.stringify(node), `Unsupported logical operator: ${operator}`, ErrorCodes.EXPRESSION_EVALUATION_ERROR);
319
+ }
320
+ case 'UnaryExpression': {
321
+ const unary = node;
322
+ const argument = evaluate(unary.argument);
323
+ const operator = unary.operator;
324
+ switch (operator) {
325
+ case '+': return +argument;
326
+ case '-': return -argument;
327
+ case '!': return !argument;
328
+ case '~': return ~argument;
329
+ case 'typeof': return typeof argument;
330
+ case 'void': return void argument;
331
+ case 'delete': throw new ExpressionError(JSON.stringify(node), 'delete operator is not allowed', ErrorCodes.EXPRESSION_EVALUATION_ERROR);
332
+ default:
333
+ throw new ExpressionError(JSON.stringify(node), `Unsupported unary operator: ${operator}`, ErrorCodes.EXPRESSION_EVALUATION_ERROR);
334
+ }
335
+ }
336
+ case 'ConditionalExpression': {
337
+ const conditional = node;
338
+ const test = evaluate(conditional.test);
339
+ return test ? evaluate(conditional.consequent) : evaluate(conditional.alternate);
340
+ }
341
+ // NullishCoalescingExpression 在 @babel/types 中可能不存在
342
+ // 使用 LogicalExpression 处理 ?? 运算符(operator === '??')
343
+ // 如果类型系统支持,可以添加此 case
344
+ case 'CallExpression': {
345
+ const call = node;
346
+ const callee = call.callee;
347
+ // 获取函数名和对象
348
+ let funcName;
349
+ let funcObj;
350
+ let callContext = null; // 函数调用的上下文(this)
351
+ if (callee.type === 'Identifier') {
352
+ // 直接函数调用:String(), Math.max()
353
+ funcName = callee.name;
354
+ // 检查是否为全局对象(禁止访问)
355
+ if (!isSafePropertyAccess(funcName, ctx, { allowGlobals })) {
356
+ throw new ExpressionError(funcName, `Access to global "${funcName}" is not allowed in expressions`, 'UNSAFE_ACCESS');
357
+ }
358
+ // 从上下文或全局获取函数
359
+ funcObj = ctx[funcName] ?? globalThis[funcName];
360
+ callContext = null; // 全局函数调用,this 为 null
361
+ }
362
+ else if (callee.type === 'MemberExpression') {
363
+ // 成员函数调用:Math.max(), Array.isArray(), array.slice()
364
+ const member = callee;
365
+ const obj = evaluate(member.object);
366
+ // null/undefined 检查
367
+ if (obj == null) {
368
+ throw new ExpressionError(JSON.stringify(node), `Cannot call method on ${obj === null ? 'null' : 'undefined'}`, ErrorCodes.EXPRESSION_EVALUATION_ERROR);
369
+ }
370
+ // 检查对象是否为全局对象(禁止访问)
371
+ if (!allowGlobals) {
372
+ // 检查对象是否为全局对象
373
+ const isGlobal = obj === globalThis ||
374
+ (typeof window !== 'undefined' && obj === window) ||
375
+ (typeof global !== 'undefined' && obj === global) ||
376
+ (typeof self !== 'undefined' && obj === self);
377
+ if (isGlobal) {
378
+ throw new ExpressionError(JSON.stringify(node), 'Access to global object is not allowed in expressions', ErrorCodes.EXPRESSION_UNSAFE_ACCESS);
379
+ }
380
+ }
381
+ // 检查是否是数组实例方法调用(安全的只读方法)
382
+ if (Array.isArray(obj) && !member.computed && member.property.type === 'Identifier') {
383
+ const methodName = member.property.name;
384
+ if (SAFE_ARRAY_METHODS.has(methodName)) {
385
+ // 这是安全的数组方法,直接使用
386
+ funcObj = obj[methodName];
387
+ funcName = `Array.${methodName}`; // 用于标识和错误信息
388
+ callContext = obj; // 数组方法调用,this 为数组本身
389
+ }
390
+ else {
391
+ // 不是安全的数组方法,需要检查白名单
392
+ const objName = member.object.type === 'Identifier'
393
+ ? member.object.name
394
+ : String(obj);
395
+ funcName = `${objName}.${methodName}`;
396
+ funcObj = obj?.[methodName];
397
+ callContext = obj;
398
+ }
399
+ }
400
+ else {
401
+ // 非数组对象,正常处理
402
+ // 获取对象标识符名称(如果是 Identifier)
403
+ let objName = '';
404
+ if (member.object.type === 'Identifier') {
405
+ objName = member.object.name;
406
+ }
407
+ else {
408
+ // 对于其他情况,使用字符串表示,但可能不在白名单内
409
+ objName = String(obj);
410
+ }
411
+ if (member.computed) {
412
+ const prop = evaluate(member.property);
413
+ funcName = `${objName}[${String(prop)}]`;
414
+ funcObj = obj?.[prop];
415
+ callContext = obj;
416
+ }
417
+ else {
418
+ const prop = member.property.name;
419
+ funcName = `${objName}.${prop}`;
420
+ funcObj = obj?.[prop];
421
+ callContext = obj;
422
+ }
423
+ }
424
+ }
425
+ else {
426
+ throw new ExpressionError(JSON.stringify(node), 'Invalid function call: only Identifier and MemberExpression are allowed', ErrorCodes.EXPRESSION_EVALUATION_ERROR);
427
+ }
428
+ // 检查白名单
429
+ // 如果是数组方法(funcName 以 Array. 开头且方法在安全列表中),则允许
430
+ const isArrayMethod = funcName.startsWith('Array.') && SAFE_ARRAY_METHODS.has(funcName.split('.')[1]);
431
+ const isWhitelisted = isArrayMethod ||
432
+ WHITELISTED_FUNCTIONS.has(funcName) ||
433
+ WHITELISTED_GLOBALS.has(funcName.split('.')[0]) ||
434
+ RUNTIME_HELPERS[funcName] !== undefined;
435
+ if (!allowGlobals && !isWhitelisted) {
436
+ throw new ExpressionError(funcName, `Function "${funcName}" is not in whitelist`, ErrorCodes.EXPRESSION_FUNCTION_NOT_WHITELISTED);
437
+ }
438
+ // 执行函数调用
439
+ const args = call.arguments.map(arg => {
440
+ if (arg.type === 'SpreadElement') {
441
+ throw new ExpressionError(JSON.stringify(node), 'Spread operator is not allowed', ErrorCodes.EXPRESSION_EVALUATION_ERROR);
442
+ }
443
+ return evaluate(arg);
444
+ });
445
+ // 调用运行时辅助函数
446
+ if (RUNTIME_HELPERS[funcName]) {
447
+ return RUNTIME_HELPERS[funcName](...args);
448
+ }
449
+ // 调用白名单全局函数或数组方法
450
+ if (typeof funcObj === 'function') {
451
+ try {
452
+ // 如果有上下文(如数组方法),使用 apply;否则直接调用
453
+ return callContext !== null
454
+ ? funcObj.apply(callContext, args)
455
+ : funcObj(...args);
456
+ }
457
+ catch (error) {
458
+ const errorMessage = error instanceof Error ? error.message : String(error);
459
+ throw new ExpressionError(funcName, `Error calling "${funcName}": ${errorMessage}`, ErrorCodes.EXPRESSION_EVALUATION_ERROR);
460
+ }
461
+ }
462
+ throw new ExpressionError(funcName, `"${funcName}" is not a function${funcObj === undefined ? ' (undefined)' : funcObj === null ? ' (null)' : ''}`, ErrorCodes.EXPRESSION_EVALUATION_ERROR);
463
+ }
464
+ case 'ArrayExpression': {
465
+ const array = node;
466
+ return array.elements.map(el => {
467
+ if (el == null)
468
+ return null;
469
+ if (el.type === 'SpreadElement') {
470
+ throw new ExpressionError(JSON.stringify(node), 'Spread operator is not allowed', ErrorCodes.EXPRESSION_EVALUATION_ERROR);
471
+ }
472
+ return evaluate(el);
473
+ });
474
+ }
475
+ case 'ObjectExpression': {
476
+ const object = node;
477
+ const result = {};
478
+ for (const prop of object.properties) {
479
+ if (prop.type === 'ObjectMethod' || prop.type === 'SpreadElement') {
480
+ throw new ExpressionError(JSON.stringify(node), 'Object methods and spread are not allowed', ErrorCodes.EXPRESSION_EVALUATION_ERROR);
481
+ }
482
+ const key = prop.key.type === 'Identifier'
483
+ ? prop.key.name
484
+ : String(evaluate(prop.key));
485
+ result[key] = evaluate(prop.value);
486
+ }
487
+ return result;
488
+ }
489
+ case 'TemplateLiteral': {
490
+ const template = node;
491
+ let result = '';
492
+ for (let i = 0; i < template.quasis.length; i++) {
493
+ result += template.quasis[i].value.cooked;
494
+ if (i < template.expressions.length) {
495
+ result += String(evaluate(template.expressions[i]));
496
+ }
497
+ }
498
+ return result;
499
+ }
500
+ default:
501
+ throw new ExpressionError(JSON.stringify(node), `Unsupported node type: ${node.type}`, ErrorCodes.EXPRESSION_EVALUATION_ERROR);
502
+ }
503
+ }
504
+ return evaluate(ast);
505
+ }
506
+ //# sourceMappingURL=evaluator.js.map