@vobs/runtime 1.3.7 → 1.4.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 (2) hide show
  1. package/package.json +2 -2
  2. package/src/events.test.ts +38 -0
package/package.json CHANGED
@@ -11,7 +11,7 @@
11
11
  "type": "git",
12
12
  "url": "git+https://github.com/vobsjs/vobs.git"
13
13
  },
14
- "version": "1.3.7",
14
+ "version": "1.4.0",
15
15
  "publishConfig": {
16
16
  "access": "public"
17
17
  },
@@ -39,6 +39,6 @@
39
39
  "./source/*": "./src/*"
40
40
  },
41
41
  "dependencies": {
42
- "@vobs/reactivity": "1.3.7"
42
+ "@vobs/reactivity": "1.4.0"
43
43
  }
44
44
  }
@@ -1,6 +1,8 @@
1
1
  // @vitest-environment jsdom
2
2
  import { describe, expect, it } from 'vitest'
3
3
  import { createDOMRenderer, setRenderer } from '@vobs/vobs'
4
+ import type { Signal } from '@vobs/vobs'
5
+ import { effect, createOwner, state } from '@vobs/reactivity'
4
6
  import { addEventListener, createElement, removeEventListener } from './index'
5
7
 
6
8
  describe('event binding dedupe', () => {
@@ -18,4 +20,40 @@ describe('event binding dedupe', () => {
18
20
  node.dispatchEvent(new MouseEvent('click'))
19
21
  expect(second).toBe(1)
20
22
  })
23
+
24
+ it('signals created inside event handlers survive owner disposal', async () => {
25
+ // 语义锁定:addEventListener 把编译事件处理器包进 owner.run 执行(错误路由 +
26
+ // 游离 DOM 防护)。处理器里创建的 state 是应用数据,作用域销毁后必须仍可写。
27
+ // 回归背景:下拉菜单关闭销毁作用域,曾把菜单点击创建的元素信号连带销毁,
28
+ // 导致"元素无法移动/缩放"(set 静默无效)。
29
+ setRenderer(createDOMRenderer())
30
+ const owner = createOwner()
31
+ const node = createElement('button')
32
+
33
+ let created: Signal<number> | null = null
34
+ owner.run(() => {
35
+ addEventListener(node, 'click', () => {
36
+ if (created === null) created = state(0, 'handler.created')
37
+ created.set(created.value + 1)
38
+ })
39
+ })
40
+
41
+ node.dispatchEvent(new MouseEvent('click'))
42
+ node.dispatchEvent(new MouseEvent('click'))
43
+ expect(created).not.toBeNull()
44
+ expect(created!.value).toBe(2)
45
+
46
+ // 作用域销毁(如菜单关闭):游离 DOM 防护继续生效——事件不再进入处理器
47
+ owner.dispose()
48
+ node.dispatchEvent(new MouseEvent('click'))
49
+ expect(created!.value).toBe(2)
50
+
51
+ // 但信号本身存活:仍可写、仍可被新效果订阅(数据寿命 = 可达性)
52
+ const seen: number[] = []
53
+ effect(() => { seen.push(created!.value) })
54
+ created!.set(7)
55
+ await Promise.resolve()
56
+ expect(created!.value).toBe(7)
57
+ expect(seen).toEqual([2, 7])
58
+ })
21
59
  })