@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,379 @@
1
+ /**
2
+ * Vario Core 性能压力测试
3
+ *
4
+ * 测试场景:
5
+ * - 路径解析性能(大量路径操作)
6
+ * - 表达式求值性能(复杂表达式)
7
+ * - 缓存系统性能(命中率、失效速度)
8
+ * - 上下文创建性能(大量实例)
9
+ * - 指令执行性能(批量指令)
10
+ */
11
+
12
+ import { describe, it, expect } from 'vitest'
13
+ import {
14
+ createRuntimeContext,
15
+ evaluate,
16
+ execute,
17
+ parsePath,
18
+ getPathValue,
19
+ setPathValue
20
+ } from '../src/index.js'
21
+ import type { RuntimeContext } from '../src/types.js'
22
+
23
+ describe('Vario Core 性能压力测试', () => {
24
+ describe('路径解析性能', () => {
25
+ it('应该能快速解析 10000 个路径', () => {
26
+ const startTime = performance.now()
27
+
28
+ const paths = Array.from({ length: 10000 }, (_, i) =>
29
+ `user.${i}.profile.name`
30
+ )
31
+
32
+ for (const path of paths) {
33
+ parsePath(path)
34
+ }
35
+
36
+ const endTime = performance.now()
37
+ const parseTime = endTime - startTime
38
+
39
+ expect(parseTime).toBeLessThan(100) // 应该在 100ms 内完成
40
+ console.log(`✓ 10000 个路径解析耗时: ${parseTime.toFixed(2)}ms`)
41
+ })
42
+
43
+ it('应该能快速处理深度路径', () => {
44
+ const startTime = performance.now()
45
+
46
+ // 创建 20 层深度路径
47
+ const deepPath = Array.from({ length: 20 }, (_, i) => `level${i}`).join('.')
48
+
49
+ for (let i = 0; i < 1000; i++) {
50
+ parsePath(deepPath)
51
+ }
52
+
53
+ const endTime = performance.now()
54
+ const parseTime = endTime - startTime
55
+
56
+ expect(parseTime).toBeLessThan(50)
57
+ console.log(`✓ 1000 个深度路径解析耗时: ${parseTime.toFixed(2)}ms`)
58
+ })
59
+ })
60
+
61
+ describe('路径操作性能', () => {
62
+ it('应该能快速读取 10000 个路径值', () => {
63
+ const obj: Record<string, unknown> = {
64
+ user: {
65
+ profile: {
66
+ name: 'Test',
67
+ age: 30
68
+ }
69
+ }
70
+ }
71
+
72
+ const startTime = performance.now()
73
+
74
+ for (let i = 0; i < 10000; i++) {
75
+ getPathValue(obj, 'user.profile.name')
76
+ getPathValue(obj, 'user.profile.age')
77
+ }
78
+
79
+ const endTime = performance.now()
80
+ const readTime = endTime - startTime
81
+
82
+ expect(readTime).toBeLessThan(200)
83
+ console.log(`✓ 10000 次路径读取耗时: ${readTime.toFixed(2)}ms`)
84
+ })
85
+
86
+ it('应该能快速设置 10000 个路径值', () => {
87
+ const obj: Record<string, unknown> = {}
88
+
89
+ const startTime = performance.now()
90
+
91
+ for (let i = 0; i < 10000; i++) {
92
+ setPathValue(obj, `user.${i}.name`, `User ${i}`)
93
+ }
94
+
95
+ const endTime = performance.now()
96
+ const writeTime = endTime - startTime
97
+
98
+ expect(writeTime).toBeLessThan(500)
99
+ console.log(`✓ 10000 次路径写入耗时: ${writeTime.toFixed(2)}ms`)
100
+ })
101
+
102
+ it('应该能快速处理数组路径', () => {
103
+ const obj: Record<string, unknown> = {
104
+ items: Array.from({ length: 1000 }, (_, i) => ({ id: i, value: i * 2 }))
105
+ }
106
+
107
+ const startTime = performance.now()
108
+
109
+ for (let i = 0; i < 1000; i++) {
110
+ getPathValue(obj, `items.${i}.value`)
111
+ setPathValue(obj, `items.${i}.value`, i * 3)
112
+ }
113
+
114
+ const endTime = performance.now()
115
+ const arrayTime = endTime - startTime
116
+
117
+ expect(arrayTime).toBeLessThan(300)
118
+ console.log(`✓ 1000 次数组路径操作耗时: ${arrayTime.toFixed(2)}ms`)
119
+ })
120
+ })
121
+
122
+ describe('表达式求值性能', () => {
123
+ it('应该能快速求值 1000 个简单表达式', () => {
124
+ const ctx = createRuntimeContext({
125
+ a: 10,
126
+ b: 20,
127
+ c: 30
128
+ })
129
+
130
+ const startTime = performance.now()
131
+
132
+ for (let i = 0; i < 1000; i++) {
133
+ evaluate('a + b + c', ctx)
134
+ evaluate('a * b - c', ctx)
135
+ evaluate('(a + b) * c', ctx)
136
+ }
137
+
138
+ const endTime = performance.now()
139
+ const evalTime = endTime - startTime
140
+
141
+ expect(evalTime).toBeLessThan(1000)
142
+ console.log(`✓ 1000 个表达式求值耗时: ${evalTime.toFixed(2)}ms`)
143
+ })
144
+
145
+ it('应该能利用缓存加速重复表达式', () => {
146
+ const ctx = createRuntimeContext({
147
+ count: 5,
148
+ multiplier: 2
149
+ })
150
+
151
+ const expr = 'count * multiplier + 10'
152
+
153
+ // 第一次求值(无缓存)
154
+ const start1 = performance.now()
155
+ for (let i = 0; i < 100; i++) {
156
+ evaluate(expr, ctx)
157
+ }
158
+ const time1 = performance.now() - start1
159
+
160
+ // 第二次求值(有缓存)
161
+ const start2 = performance.now()
162
+ for (let i = 0; i < 100; i++) {
163
+ evaluate(expr, ctx)
164
+ }
165
+ const time2 = performance.now() - start2
166
+
167
+ // 缓存应该显著提升性能
168
+ expect(time2).toBeLessThan(time1)
169
+ console.log(`✓ 无缓存: ${time1.toFixed(2)}ms, 有缓存: ${time2.toFixed(2)}ms, 提升: ${((time1 - time2) / time1 * 100).toFixed(1)}%`)
170
+ })
171
+
172
+ it('应该能处理复杂嵌套表达式', () => {
173
+ const ctx = createRuntimeContext({
174
+ items: Array.from({ length: 100 }, (_, i) => ({ value: i })),
175
+ multiplier: 2
176
+ })
177
+
178
+ const startTime = performance.now()
179
+
180
+ // 复杂表达式:数组操作 + 计算
181
+ for (let i = 0; i < 100; i++) {
182
+ evaluate(`items[${i}].value * multiplier`, ctx)
183
+ }
184
+
185
+ const endTime = performance.now()
186
+ const evalTime = endTime - startTime
187
+
188
+ expect(evalTime).toBeLessThan(500)
189
+ console.log(`✓ 100 个复杂表达式求值耗时: ${evalTime.toFixed(2)}ms`)
190
+ })
191
+ })
192
+
193
+ describe('上下文创建性能', () => {
194
+ it('应该能快速创建 1000 个上下文实例', () => {
195
+ const startTime = performance.now()
196
+
197
+ for (let i = 0; i < 1000; i++) {
198
+ createRuntimeContext({
199
+ count: i,
200
+ name: `Instance ${i}`
201
+ })
202
+ }
203
+
204
+ const endTime = performance.now()
205
+ const createTime = endTime - startTime
206
+
207
+ expect(createTime).toBeLessThan(1000)
208
+ console.log(`✓ 1000 个上下文创建耗时: ${createTime.toFixed(2)}ms`)
209
+ })
210
+
211
+ it('应该能快速创建带大量状态的上下文', () => {
212
+ const largeState: Record<string, unknown> = {}
213
+ for (let i = 0; i < 1000; i++) {
214
+ largeState[`field${i}`] = `value${i}`
215
+ }
216
+
217
+ const startTime = performance.now()
218
+
219
+ const ctx = createRuntimeContext(largeState)
220
+
221
+ const endTime = performance.now()
222
+ const createTime = endTime - startTime
223
+
224
+ expect(ctx).toBeDefined()
225
+ expect(createTime).toBeLessThan(500)
226
+ console.log(`✓ 创建包含 1000 个字段的上下文耗时: ${createTime.toFixed(2)}ms`)
227
+ })
228
+ })
229
+
230
+ describe('动作执行性能', () => {
231
+ it('应该能快速执行 1000 个 set 动作', async () => {
232
+ const ctx = createRuntimeContext<{ count: number }>({ count: 0 })
233
+
234
+ const actions = Array.from({ length: 1000 }, (_, i) => ({
235
+ type: 'set' as const,
236
+ path: 'count',
237
+ value: i
238
+ }))
239
+
240
+ const startTime = performance.now()
241
+
242
+ await execute(actions, ctx)
243
+
244
+ const endTime = performance.now()
245
+ const execTime = endTime - startTime
246
+
247
+ expect(ctx._get('count')).toBe(999)
248
+ expect(execTime).toBeLessThan(1000)
249
+ console.log(`✓ 1000 个 set 动作执行耗时: ${execTime.toFixed(2)}ms`)
250
+ })
251
+
252
+ it('应该能快速执行批量动作', async () => {
253
+ const ctx = createRuntimeContext<{
254
+ a: number
255
+ b: number
256
+ c: number
257
+ }>({ a: 0, b: 0, c: 0 })
258
+
259
+ // 使用直接数值更新,避免表达式求值的复杂性
260
+ const actions = Array.from({ length: 100 }, (_, i) => ({
261
+ type: 'batch' as const,
262
+ actions: [
263
+ { type: 'set' as const, path: 'a', value: i + 1 },
264
+ { type: 'set' as const, path: 'b', value: i + 1 },
265
+ { type: 'set' as const, path: 'c', value: i + 1 }
266
+ ]
267
+ }))
268
+
269
+ const startTime = performance.now()
270
+
271
+ await execute(actions, ctx)
272
+
273
+ const endTime = performance.now()
274
+ const execTime = endTime - startTime
275
+
276
+ expect(ctx._get('a')).toBe(100)
277
+ expect(ctx._get('b')).toBe(100)
278
+ expect(ctx._get('c')).toBe(100)
279
+ expect(execTime).toBeLessThan(2000)
280
+ console.log(`✓ 100 个批量指令执行耗时: ${execTime.toFixed(2)}ms`)
281
+ })
282
+ })
283
+
284
+ describe('状态更新性能', () => {
285
+ it('应该能快速更新状态 10000 次', () => {
286
+ const ctx = createRuntimeContext<{ count: number }>({ count: 0 })
287
+
288
+ const startTime = performance.now()
289
+
290
+ for (let i = 0; i < 10000; i++) {
291
+ ctx._set('count', i)
292
+ }
293
+
294
+ const endTime = performance.now()
295
+ const updateTime = endTime - startTime
296
+
297
+ expect(ctx._get('count')).toBe(9999)
298
+ expect(updateTime).toBeLessThan(500)
299
+ console.log(`✓ 10000 次状态更新耗时: ${updateTime.toFixed(2)}ms`)
300
+ })
301
+
302
+ it('应该能快速更新嵌套路径', () => {
303
+ const ctx = createRuntimeContext<{
304
+ user: {
305
+ profile: {
306
+ name: string
307
+ age: number
308
+ }
309
+ }
310
+ }>({
311
+ user: {
312
+ profile: {
313
+ name: '',
314
+ age: 0
315
+ }
316
+ }
317
+ })
318
+
319
+ const startTime = performance.now()
320
+
321
+ for (let i = 0; i < 1000; i++) {
322
+ ctx._set('user.profile.name', `User ${i}`)
323
+ ctx._set('user.profile.age', i)
324
+ }
325
+
326
+ const endTime = performance.now()
327
+ const updateTime = endTime - startTime
328
+
329
+ expect(ctx._get('user.profile.name')).toBe('User 999')
330
+ expect(ctx._get('user.profile.age')).toBe(999)
331
+ expect(updateTime).toBeLessThan(500)
332
+ console.log(`✓ 1000 次嵌套路径更新耗时: ${updateTime.toFixed(2)}ms`)
333
+ })
334
+ })
335
+
336
+ describe('混合压力测试', () => {
337
+ it('应该能处理复杂的混合操作', async () => {
338
+ const ctx = createRuntimeContext<{
339
+ items: Array<{ id: number; value: number }>
340
+ count: number
341
+ multiplier: number
342
+ }>({
343
+ items: [],
344
+ count: 0,
345
+ multiplier: 1
346
+ })
347
+
348
+ const startTime = performance.now()
349
+
350
+ // 混合操作:创建数据、更新状态、执行指令、求值表达式
351
+ for (let i = 0; i < 100; i++) {
352
+ // 添加项目
353
+ const currentItems = ctx._get('items') as Array<{ id: number; value: number }>
354
+ ctx._set('items', [...currentItems, { id: i, value: i * 2 }])
355
+
356
+ // 更新计数
357
+ ctx._set('count', i)
358
+
359
+ // 更新倍数
360
+ ctx._set('multiplier', (i % 10) + 1)
361
+
362
+ // 求值表达式
363
+ evaluate('count * multiplier', ctx)
364
+
365
+ // 执行指令(直接使用数值)
366
+ const currentCount = ctx._get('count') as number
367
+ ctx._set('count', currentCount + 1)
368
+ }
369
+
370
+ const endTime = performance.now()
371
+ const mixedTime = endTime - startTime
372
+
373
+ expect(ctx._get('items')).toHaveLength(100)
374
+ expect(ctx._get('count')).toBe(100) // 最后一次循环 i=99,然后 count = 99 + 1 = 100
375
+ expect(mixedTime).toBeLessThan(5000)
376
+ console.log(`✓ 混合操作测试耗时: ${mixedTime.toFixed(2)}ms`)
377
+ })
378
+ })
379
+ })
@@ -0,0 +1,78 @@
1
+ /**
2
+ * RuntimeContext 创建测试
3
+ */
4
+
5
+ import { describe, it, expect } from 'vitest'
6
+ import { createRuntimeContext } from '../../src/runtime/create-context'
7
+
8
+ describe('createRuntimeContext', () => {
9
+ it('应该创建基本的运行时上下文', () => {
10
+ const ctx = createRuntimeContext({
11
+ user: { name: 'John', age: 30 }
12
+ })
13
+
14
+ expect(ctx.user).toEqual({ name: 'John', age: 30 })
15
+ expect(ctx.$emit).toBeDefined()
16
+ expect(ctx.$methods).toBeDefined()
17
+ expect(ctx._get).toBeDefined()
18
+ expect(ctx._set).toBeDefined()
19
+ })
20
+
21
+ it('应该拒绝冲突的属性名', () => {
22
+ expect(() => {
23
+ createRuntimeContext({
24
+ $emit: 'invalid' // 冲突
25
+ })
26
+ }).toThrow('conflicts with system API')
27
+
28
+ expect(() => {
29
+ createRuntimeContext({
30
+ _get: 'invalid' // 冲突
31
+ })
32
+ }).toThrow('conflicts with system API')
33
+ })
34
+
35
+ it('应该支持路径访问', () => {
36
+ const ctx = createRuntimeContext({
37
+ user: { name: 'John', age: 30 }
38
+ })
39
+
40
+ expect(ctx._get('user.name')).toBe('John')
41
+ expect(ctx._get('user.age')).toBe(30)
42
+ })
43
+
44
+ it('应该支持路径设置', () => {
45
+ const ctx = createRuntimeContext({
46
+ user: { name: 'John' }
47
+ })
48
+
49
+ ctx._set('user.age', 30)
50
+ expect(ctx.user.age).toBe(30)
51
+ })
52
+
53
+ it('应该防止覆盖系统 API', () => {
54
+ const ctx = createRuntimeContext()
55
+
56
+ expect(() => {
57
+ ctx.$emit = () => {}
58
+ }).toThrow('Cannot override system API')
59
+
60
+ expect(() => {
61
+ ctx._get = () => {}
62
+ }).toThrow('Cannot override system API')
63
+ })
64
+
65
+ it('应该支持事件触发', () => {
66
+ const events: Array<{ event: string; data?: any }> = []
67
+ const ctx = createRuntimeContext({}, {
68
+ onEmit: (event, data) => {
69
+ events.push({ event, data })
70
+ }
71
+ })
72
+
73
+ ctx.$emit('test', { value: 123 })
74
+ expect(events).toHaveLength(1)
75
+ expect(events[0].event).toBe('test')
76
+ expect(events[0].data).toEqual({ value: 123 })
77
+ })
78
+ })
@@ -0,0 +1,74 @@
1
+ /**
2
+ * 循环上下文对象池测试
3
+ */
4
+
5
+ import { describe, it, expect, beforeEach } from 'vitest'
6
+ import {
7
+ createLoopContext,
8
+ releaseLoopContext,
9
+ getLoopContextPool,
10
+ clearLoopContextPool
11
+ } from '../../src/runtime/loop-context-pool.js'
12
+ import { createRuntimeContext } from '../../src/runtime/create-context.js'
13
+
14
+ describe('循环上下文对象池', () => {
15
+ beforeEach(() => {
16
+ clearLoopContextPool()
17
+ })
18
+
19
+ it('应该创建循环上下文', () => {
20
+ const parentCtx = createRuntimeContext({ count: 0 })
21
+ const loopCtx = createLoopContext(parentCtx, 'item1', 0)
22
+
23
+ expect(loopCtx.$item).toBe('item1')
24
+ expect(loopCtx.$index).toBe(0)
25
+ expect(loopCtx.count).toBe(0)
26
+ })
27
+
28
+ it('应该复用父上下文的属性', () => {
29
+ const parentCtx = createRuntimeContext({ user: { name: 'John' } })
30
+ const loopCtx = createLoopContext(parentCtx, 'item', 0)
31
+
32
+ expect(loopCtx.user).toEqual({ name: 'John' })
33
+ expect(loopCtx.$item).toBe('item')
34
+ })
35
+
36
+ it('应该释放循环上下文回对象池', () => {
37
+ const pool = getLoopContextPool()
38
+ const initialSize = pool.size
39
+
40
+ const parentCtx = createRuntimeContext({ count: 0 })
41
+ const loopCtx = createLoopContext(parentCtx, 'item', 0)
42
+
43
+ releaseLoopContext(loopCtx)
44
+
45
+ expect(pool.size).toBeGreaterThan(initialSize)
46
+ })
47
+
48
+ it('应该清理循环相关属性', () => {
49
+ const parentCtx = createRuntimeContext({ count: 0 })
50
+ const loopCtx = createLoopContext(parentCtx, 'item', 0)
51
+ loopCtx.tempVar = 'temp'
52
+
53
+ releaseLoopContext(loopCtx)
54
+
55
+ // 再次获取应该清理了循环属性
56
+ const newCtx = createLoopContext(parentCtx, 'newItem', 1)
57
+ expect(newCtx.$item).toBe('newItem')
58
+ expect(newCtx.$index).toBe(1)
59
+ })
60
+
61
+ it('应该复用对象池中的对象', () => {
62
+ const pool = getLoopContextPool()
63
+ const parentCtx = createRuntimeContext({ count: 0 })
64
+
65
+ // 创建并释放多个上下文
66
+ for (let i = 0; i < 5; i++) {
67
+ const loopCtx = createLoopContext(parentCtx, `item${i}`, i)
68
+ releaseLoopContext(loopCtx)
69
+ }
70
+
71
+ // 对象池应该有一些对象
72
+ expect(pool.size).toBeGreaterThan(0)
73
+ })
74
+ })
@@ -0,0 +1,128 @@
1
+ import { describe, it, expect } from 'vitest'
2
+ import { parsePath, setPathValue, getPathValue } from '../../src/runtime/path'
3
+
4
+ describe('parsePath', () => {
5
+ describe('基础语法', () => {
6
+ it('应该解析点语法路径', () => {
7
+ expect(parsePath('user.name')).toEqual(['user', 'name'])
8
+ expect(parsePath('a.b.c')).toEqual(['a', 'b', 'c'])
9
+ })
10
+
11
+ it('应该将纯数字段解析为数字', () => {
12
+ expect(parsePath('items.0.text')).toEqual(['items', 0, 'text'])
13
+ expect(parsePath('users.1.name')).toEqual(['users', 1, 'name'])
14
+ })
15
+
16
+ it('应该处理空路径', () => {
17
+ expect(parsePath('')).toEqual([])
18
+ expect(parsePath(null as any)).toEqual([])
19
+ expect(parsePath(undefined as any)).toEqual([])
20
+ })
21
+ })
22
+
23
+ describe('[] 语法', () => {
24
+ it('应该解析数字索引', () => {
25
+ expect(parsePath('users[0].name')).toEqual(['users', 0, 'name'])
26
+ expect(parsePath('items[1]')).toEqual(['items', 1])
27
+ expect(parsePath('data[10].value')).toEqual(['data', 10, 'value'])
28
+ })
29
+
30
+ it('应该解析空括号为动态索引 (-1)', () => {
31
+ expect(parsePath('users[].name')).toEqual(['users', -1, 'name'])
32
+ expect(parsePath('items[]')).toEqual(['items', -1])
33
+ })
34
+
35
+ it('应该解析字符串键', () => {
36
+ expect(parsePath('obj[key].value')).toEqual(['obj', 'key', 'value'])
37
+ })
38
+
39
+ it('应该处理多个括号', () => {
40
+ expect(parsePath('matrix[0][1]')).toEqual(['matrix', 0, 1])
41
+ expect(parsePath('data[0].items[1].name')).toEqual(['data', 0, 'items', 1, 'name'])
42
+ })
43
+
44
+ it('应该处理混合语法', () => {
45
+ expect(parsePath('users[0].profile.tags[1]')).toEqual(['users', 0, 'profile', 'tags', 1])
46
+ expect(parsePath('data.users[0].name')).toEqual(['data', 'users', 0, 'name'])
47
+ })
48
+
49
+ it('应该处理连续括号和点', () => {
50
+ expect(parsePath('[0].name')).toEqual([0, 'name'])
51
+ expect(parsePath('users[0][1]')).toEqual(['users', 0, 1])
52
+ })
53
+ })
54
+ })
55
+
56
+ describe('setPathValue 与 [] 语法', () => {
57
+ it('应该根据数字索引创建数组', () => {
58
+ const obj: Record<string, unknown> = {}
59
+ setPathValue(obj, 'users[0].name', 'John')
60
+
61
+ expect(Array.isArray(obj.users)).toBe(true)
62
+ expect((obj.users as any)[0].name).toBe('John')
63
+ })
64
+
65
+ it('应该根据字符串键创建对象', () => {
66
+ const obj: Record<string, unknown> = {}
67
+ setPathValue(obj, 'data.user.name', 'John')
68
+
69
+ expect(typeof obj.data).toBe('object')
70
+ expect((obj.data as any).user.name).toBe('John')
71
+ })
72
+
73
+ it('应该处理嵌套数组', () => {
74
+ const obj: Record<string, unknown> = {}
75
+ setPathValue(obj, 'matrix[0][1]', 'value')
76
+
77
+ expect(Array.isArray(obj.matrix)).toBe(true)
78
+ expect(Array.isArray((obj.matrix as any)[0])).toBe(true)
79
+ expect((obj.matrix as any)[0][1]).toBe('value')
80
+ })
81
+
82
+ it('应该处理混合嵌套', () => {
83
+ const obj: Record<string, unknown> = {}
84
+ setPathValue(obj, 'data.users[0].profile.tags[1]', 'tag1')
85
+
86
+ expect(typeof obj.data).toBe('object')
87
+ expect(Array.isArray((obj.data as any).users)).toBe(true)
88
+ expect(typeof (obj.data as any).users[0].profile).toBe('object')
89
+ expect(Array.isArray((obj.data as any).users[0].profile.tags)).toBe(true)
90
+ expect((obj.data as any).users[0].profile.tags[1]).toBe('tag1')
91
+ })
92
+ })
93
+
94
+ describe('getPathValue 与 [] 语法', () => {
95
+ it('应该获取数组索引的值', () => {
96
+ const obj = {
97
+ users: [
98
+ { name: 'John' },
99
+ { name: 'Jane' }
100
+ ]
101
+ }
102
+
103
+ expect(getPathValue(obj, 'users[0].name')).toBe('John')
104
+ expect(getPathValue(obj, 'users[1].name')).toBe('Jane')
105
+ })
106
+
107
+ it('应该获取嵌套数组的值', () => {
108
+ const obj = {
109
+ matrix: [[1, 2], [3, 4]]
110
+ }
111
+
112
+ expect(getPathValue(obj, 'matrix[0][0]')).toBe(1)
113
+ expect(getPathValue(obj, 'matrix[1][1]')).toBe(4)
114
+ })
115
+
116
+ it('应该处理混合路径', () => {
117
+ const obj = {
118
+ data: {
119
+ users: [
120
+ { profile: { tags: ['a', 'b'] } }
121
+ ]
122
+ }
123
+ }
124
+
125
+ expect(getPathValue(obj, 'data.users[0].profile.tags[0]')).toBe('a')
126
+ expect(getPathValue(obj, 'data.users[0].profile.tags[1]')).toBe('b')
127
+ })
128
+ })