@vobs/runtime 1.7.8 → 1.8.1

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 (127) hide show
  1. package/README.md +1 -1
  2. package/dist/async-boundary.cjs +14 -4
  3. package/dist/async-boundary.cjs.map +1 -1
  4. package/dist/async-boundary.js +17 -7
  5. package/dist/async-boundary.js.map +1 -1
  6. package/dist/bind.cjs +43 -3
  7. package/dist/bind.cjs.map +1 -1
  8. package/dist/bind.js +48 -8
  9. package/dist/bind.js.map +1 -1
  10. package/dist/boundary.cjs +159 -84
  11. package/dist/boundary.cjs.map +1 -1
  12. package/dist/boundary.js +162 -87
  13. package/dist/boundary.js.map +1 -1
  14. package/dist/client-only.cjs +323 -0
  15. package/dist/client-only.cjs.map +1 -0
  16. package/dist/client-only.d.cts +42 -0
  17. package/dist/client-only.d.ts +42 -0
  18. package/dist/client-only.js +299 -0
  19. package/dist/client-only.js.map +1 -0
  20. package/dist/debug.cjs.map +1 -1
  21. package/dist/debug.d.cts +12 -2
  22. package/dist/debug.d.ts +12 -2
  23. package/dist/debug.js.map +1 -1
  24. package/dist/dom-events.cjs +46 -0
  25. package/dist/dom-events.cjs.map +1 -0
  26. package/dist/dom-events.d.cts +23 -0
  27. package/dist/dom-events.d.ts +23 -0
  28. package/dist/dom-events.js +21 -0
  29. package/dist/dom-events.js.map +1 -0
  30. package/dist/dom-props.cjs +154 -0
  31. package/dist/dom-props.cjs.map +1 -0
  32. package/dist/dom-props.d.cts +21 -0
  33. package/dist/dom-props.d.ts +21 -0
  34. package/dist/dom-props.js +128 -0
  35. package/dist/dom-props.js.map +1 -0
  36. package/dist/dynamic.cjs +68 -14
  37. package/dist/dynamic.cjs.map +1 -1
  38. package/dist/dynamic.js +73 -19
  39. package/dist/dynamic.js.map +1 -1
  40. package/dist/error-boundary.cjs +159 -84
  41. package/dist/error-boundary.cjs.map +1 -1
  42. package/dist/error-boundary.js +162 -87
  43. package/dist/error-boundary.js.map +1 -1
  44. package/dist/error.cjs +52 -20
  45. package/dist/error.cjs.map +1 -1
  46. package/dist/error.d.cts +8 -0
  47. package/dist/error.d.ts +8 -0
  48. package/dist/error.js +52 -20
  49. package/dist/error.js.map +1 -1
  50. package/dist/fragment.cjs.map +1 -1
  51. package/dist/fragment.js +1 -1
  52. package/dist/fragment.js.map +1 -1
  53. package/dist/hmr.cjs +137 -1
  54. package/dist/hmr.cjs.map +1 -1
  55. package/dist/hmr.js +135 -1
  56. package/dist/hmr.js.map +1 -1
  57. package/dist/index.cjs +713 -236
  58. package/dist/index.cjs.map +1 -1
  59. package/dist/index.d.cts +5 -2
  60. package/dist/index.d.ts +5 -2
  61. package/dist/index.js +715 -245
  62. package/dist/index.js.map +1 -1
  63. package/dist/ops.cjs +561 -107
  64. package/dist/ops.cjs.map +1 -1
  65. package/dist/ops.d.cts +49 -4
  66. package/dist/ops.d.ts +49 -4
  67. package/dist/ops.js +557 -108
  68. package/dist/ops.js.map +1 -1
  69. package/dist/profiler.cjs +14 -4
  70. package/dist/profiler.cjs.map +1 -1
  71. package/dist/profiler.js +17 -7
  72. package/dist/profiler.js.map +1 -1
  73. package/dist/ref.cjs +160 -5
  74. package/dist/ref.cjs.map +1 -1
  75. package/dist/ref.js +160 -5
  76. package/dist/ref.js.map +1 -1
  77. package/dist/renderer.cjs.map +1 -1
  78. package/dist/renderer.d.cts +2 -0
  79. package/dist/renderer.d.ts +2 -0
  80. package/dist/show.cjs +196 -0
  81. package/dist/show.cjs.map +1 -0
  82. package/dist/show.d.cts +54 -0
  83. package/dist/show.d.ts +54 -0
  84. package/dist/show.js +171 -0
  85. package/dist/show.js.map +1 -0
  86. package/dist/svg.cjs +102 -0
  87. package/dist/svg.cjs.map +1 -0
  88. package/dist/svg.d.cts +21 -0
  89. package/dist/svg.d.ts +21 -0
  90. package/dist/svg.js +76 -0
  91. package/dist/svg.js.map +1 -0
  92. package/package.json +20 -2
  93. package/src/bind.ts +30 -2
  94. package/src/boundaries.test.ts +54 -1
  95. package/src/boundary.ts +24 -8
  96. package/src/clear-remove.test.ts +85 -0
  97. package/src/client-only.test.ts +140 -0
  98. package/src/client-only.ts +65 -0
  99. package/src/component-empty-return.test.ts +123 -0
  100. package/src/debug.ts +12 -1
  101. package/src/dom-events.ts +32 -0
  102. package/src/dom-props-consistency.test.ts +149 -0
  103. package/src/dom-props.test.ts +70 -0
  104. package/src/dom-props.ts +107 -0
  105. package/src/dynamic.ts +91 -14
  106. package/src/error.test.ts +76 -1
  107. package/src/error.ts +87 -22
  108. package/src/event-cleanup-growth.test.ts +128 -0
  109. package/src/events.test.ts +16 -3
  110. package/src/hmr.ts +8 -2
  111. package/src/index.ts +13 -1
  112. package/src/invariants.test.ts +252 -0
  113. package/src/list-fast-path.test.ts +83 -0
  114. package/src/list-reactive-item.test.ts +77 -0
  115. package/src/ops.ts +419 -60
  116. package/src/props.test.ts +61 -1
  117. package/src/ref-cleanup.test.ts +144 -0
  118. package/src/ref.ts +57 -8
  119. package/src/remove-listener-identity.test.ts +97 -0
  120. package/src/renderer.ts +3 -1
  121. package/src/select-hidden-subscription.test.ts +104 -0
  122. package/src/show-classlist.test.ts +205 -0
  123. package/src/show.ts +107 -0
  124. package/src/spread-stable-shape.test.ts +109 -0
  125. package/src/spread.test.ts +86 -0
  126. package/src/svg.test.ts +28 -1
  127. package/src/svg.ts +34 -0
package/dist/ops.js CHANGED
@@ -2,7 +2,7 @@ var __defProp = Object.defineProperty;
2
2
  var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
3
3
 
4
4
  // packages/runtime/src/ops.ts
5
- import { createOwner, getCurrentOwner as getCurrentOwner3, setOwnerDebugName, untrack } from "@vobs/reactivity";
5
+ import { createOwner, effect, getCurrentOwner as getCurrentOwner3, setOwnerDebugName, untrack } from "@vobs/reactivity";
6
6
 
7
7
  // packages/runtime/src/fragment.ts
8
8
  import { getCurrentOwner } from "@vobs/reactivity";
@@ -11,6 +11,337 @@ function isVobsFragment(value) {
11
11
  }
12
12
  __name(isVobsFragment, "isVobsFragment");
13
13
 
14
+ // packages/runtime/src/dom-props.ts
15
+ var PROPERTY_NAMES = /* @__PURE__ */ new Set([
16
+ // 表单状态
17
+ "value",
18
+ "checked",
19
+ "selected",
20
+ "disabled",
21
+ "multiple",
22
+ "readOnly",
23
+ "required",
24
+ "defaultValue",
25
+ "defaultChecked",
26
+ "indeterminate",
27
+ /*
28
+ * `autoFocus` 用**驼峰**(与同表 readOnly/tabIndex/defaultChecked 一致),且必须走
29
+ * **property 通道**:`autofocus` 是**布尔属性**(只看存在与否、与值无关),
30
+ * 所以 `autoFocus={false}` 必须"移除属性"而不是写 `autofocus="false"`
31
+ * (attribute 通道只能把 false 序列化成字符串,属性照样存在 = 仍然聚焦)。
32
+ *
33
+ * ⚠️ 但 `setProperty` 必须把它映射成 **IDL 名 `autofocus`(全小写)** ——
34
+ * `Reflect.set(el, 'autoFocus', …)` 只会挂一个不反射的 expando,**静默无效**。
35
+ * 映射表见 ops.ts 的 `PROPERTY_IDL_NAMES`。实测(jsdom,与真实 DOM 同语义):
36
+ * IDL `autofocus` → 写 true 得 has=true/idl=true;写 false 得 has=false/idl=false ✓
37
+ * JSX `autoFocus` → 属性与 IDL 都不变(只是 expando) ✗
38
+ */
39
+ "autoFocus",
40
+ "hidden",
41
+ "tabIndex",
42
+ "colSpan",
43
+ "rowSpan",
44
+ "open",
45
+ // 只能走 property 的(attribute 路径会静默无效)
46
+ "innerHTML",
47
+ "innerText",
48
+ "textContent",
49
+ // 媒体
50
+ "muted",
51
+ "volume",
52
+ "currentTime",
53
+ "playbackRate"
54
+ ]);
55
+ var ATTRIBUTE_ALIASES = {
56
+ className: "class",
57
+ htmlFor: "for",
58
+ autoComplete: "autocomplete",
59
+ spellCheck: "spellcheck"
60
+ };
61
+ var SVG_KEBAB_ATTRIBUTES = /* @__PURE__ */ new Set([
62
+ // stroke
63
+ "strokeWidth",
64
+ "strokeLinecap",
65
+ "strokeLinejoin",
66
+ "strokeDasharray",
67
+ "strokeDashoffset",
68
+ "strokeMiterlimit",
69
+ "strokeOpacity",
70
+ // fill
71
+ "fillOpacity",
72
+ "fillRule",
73
+ // clip(注意 clipPathUnits 是结构属性,不在这里)
74
+ "clipPath",
75
+ "clipRule",
76
+ // 文本对齐
77
+ "textAnchor",
78
+ "dominantBaseline",
79
+ "alignmentBaseline",
80
+ "baselineShift",
81
+ // 字体
82
+ "fontFamily",
83
+ "fontSize",
84
+ "fontSizeAdjust",
85
+ "fontStretch",
86
+ "fontStyle",
87
+ "fontVariant",
88
+ "fontWeight",
89
+ "letterSpacing",
90
+ "wordSpacing",
91
+ // marker 的表现属性(markerWidth / markerHeight / markerUnits 是结构属性,不在这里)
92
+ "markerStart",
93
+ "markerMid",
94
+ "markerEnd",
95
+ // 颜色
96
+ "colorInterpolation",
97
+ "colorInterpolationFilters",
98
+ "colorProfile",
99
+ "colorRendering",
100
+ "floodColor",
101
+ "floodOpacity",
102
+ "lightingColor",
103
+ "stopColor",
104
+ "stopOpacity",
105
+ "glyphOrientationHorizontal",
106
+ "glyphOrientationVertical",
107
+ // 渲染与合成
108
+ "imageRendering",
109
+ "paintOrder",
110
+ "pointerEvents",
111
+ "shapeRendering",
112
+ "textDecoration",
113
+ "textRendering",
114
+ "transformOrigin",
115
+ "vectorEffect",
116
+ "writingMode"
117
+ ]);
118
+ function isPropertyName(name) {
119
+ return PROPERTY_NAMES.has(name);
120
+ }
121
+ __name(isPropertyName, "isPropertyName");
122
+ function domAttributeName(name) {
123
+ const alias = ATTRIBUTE_ALIASES[name];
124
+ if (alias !== void 0) return alias;
125
+ if (SVG_KEBAB_ATTRIBUTES.has(name)) return name.replace(/[A-Z]/gu, (letter) => `-${letter.toLowerCase()}`);
126
+ return name;
127
+ }
128
+ __name(domAttributeName, "domAttributeName");
129
+
130
+ // packages/runtime/src/dom-events.ts
131
+ var EVENT_NAME_ALIASES = {
132
+ // React 风格的 onDoubleClick 对应的 DOM 事件是 dblclick
133
+ doubleclick: "dblclick",
134
+ // 少数人按 addEventListener 的写法写成 onDblClick
135
+ dblclick: "dblclick"
136
+ };
137
+ function resolveEventName(attributeName) {
138
+ if (!attributeName.startsWith("on") || attributeName.length <= 2) return void 0;
139
+ const lowered = attributeName.slice(2).toLowerCase();
140
+ return EVENT_NAME_ALIASES[lowered] ?? lowered;
141
+ }
142
+ __name(resolveEventName, "resolveEventName");
143
+
144
+ // packages/runtime/src/svg.ts
145
+ var SVG_NAMESPACE = "http://www.w3.org/2000/svg";
146
+ var SVG_TAGS = /* @__PURE__ */ new Set([
147
+ "animate",
148
+ "animateMotion",
149
+ "animateTransform",
150
+ "circle",
151
+ "clipPath",
152
+ "defs",
153
+ "desc",
154
+ "ellipse",
155
+ "feBlend",
156
+ "feColorMatrix",
157
+ "feComponentTransfer",
158
+ "feComposite",
159
+ "feConvolveMatrix",
160
+ "feDiffuseLighting",
161
+ "feDisplacementMap",
162
+ "feDistantLight",
163
+ "feDropShadow",
164
+ "feFlood",
165
+ "feFuncA",
166
+ "feFuncB",
167
+ "feFuncG",
168
+ "feFuncR",
169
+ "feGaussianBlur",
170
+ "feImage",
171
+ "feMerge",
172
+ "feMergeNode",
173
+ "feMorphology",
174
+ "feOffset",
175
+ "fePointLight",
176
+ "feSpecularLighting",
177
+ "feSpotLight",
178
+ "feTile",
179
+ "feTurbulence",
180
+ "filter",
181
+ "foreignObject",
182
+ "g",
183
+ "image",
184
+ "line",
185
+ "linearGradient",
186
+ "marker",
187
+ "mask",
188
+ "metadata",
189
+ "mpath",
190
+ "path",
191
+ "pattern",
192
+ "polygon",
193
+ "polyline",
194
+ "radialGradient",
195
+ "rect",
196
+ "set",
197
+ "stop",
198
+ "svg",
199
+ "switch",
200
+ "symbol",
201
+ "text",
202
+ "textPath",
203
+ "tspan",
204
+ "use",
205
+ "view"
206
+ ]);
207
+ function isSvgTag(tag) {
208
+ return SVG_TAGS.has(tag);
209
+ }
210
+ __name(isSvgTag, "isSvgTag");
211
+
212
+ // packages/runtime/src/error.ts
213
+ var _VobsError = class _VobsError extends Error {
214
+ constructor(options) {
215
+ super(options.message);
216
+ this.name = "VobsError";
217
+ this.code = options.code;
218
+ this.severity = options.severity ?? "error";
219
+ this.layer = options.layer ?? "runtime";
220
+ this.cause = options.cause;
221
+ this.fix = options.fix;
222
+ this.location = options.location;
223
+ this.trace = options.trace;
224
+ this.example = options.example;
225
+ this.docs = options.docs;
226
+ this.codeFrame = options.codeFrame;
227
+ }
228
+ };
229
+ __name(_VobsError, "VobsError");
230
+ var VobsError = _VobsError;
231
+ function isVobsError(value) {
232
+ return value instanceof VobsError || Boolean(value && typeof value === "object" && typeof value.code === "string" && typeof value.message === "string");
233
+ }
234
+ __name(isVobsError, "isVobsError");
235
+ function isForeignVobsError(value) {
236
+ return value instanceof Error && value.name === "VobsError" && typeof value.code === "string";
237
+ }
238
+ __name(isForeignVobsError, "isForeignVobsError");
239
+ function describeUnknown(value) {
240
+ if (value === null || value === void 0) return String(value);
241
+ if (typeof value === "string") return value;
242
+ if (typeof value === "object") {
243
+ const message = value.message;
244
+ if (typeof message === "string" && message !== "") return message;
245
+ try {
246
+ return JSON.stringify(value) ?? String(value);
247
+ } catch {
248
+ return String(value);
249
+ }
250
+ }
251
+ return String(value);
252
+ }
253
+ __name(describeUnknown, "describeUnknown");
254
+ function adoptVobsError(value, defaults) {
255
+ return new VobsError({
256
+ code: value.code,
257
+ message: value.message,
258
+ severity: value.severity ?? defaults.severity,
259
+ layer: value.layer ?? defaults.layer,
260
+ cause: value.cause ?? (value instanceof Error ? value : void 0),
261
+ fix: value.fix ?? defaults.fix,
262
+ location: value.location,
263
+ trace: value.trace,
264
+ example: value.example,
265
+ docs: value.docs,
266
+ codeFrame: value.codeFrame
267
+ });
268
+ }
269
+ __name(adoptVobsError, "adoptVobsError");
270
+ function normalizeVobsError(value, defaults = {}) {
271
+ if (value instanceof VobsError) return value;
272
+ if (isForeignVobsError(value)) return adoptVobsError(value, defaults);
273
+ if (value instanceof Error) {
274
+ const metadata = value;
275
+ const code = defaults.code ?? (typeof metadata.vobsCode === "string" ? metadata.vobsCode : void 0);
276
+ if (code) {
277
+ defineErrorMetadata(value, "code", code);
278
+ if (value.code !== code) {
279
+ return new VobsError({
280
+ code,
281
+ message: value.message,
282
+ severity: defaults.severity ?? "error",
283
+ layer: defaults.layer ?? "runtime",
284
+ cause: value,
285
+ fix: defaults.fix ?? (typeof metadata.vobsHint === "string" ? metadata.vobsHint : void 0)
286
+ });
287
+ }
288
+ }
289
+ defineErrorMetadata(value, "severity", defaults.severity ?? "error");
290
+ defineErrorMetadata(value, "layer", defaults.layer ?? "runtime");
291
+ const fix = defaults.fix ?? (typeof metadata.vobsHint === "string" ? metadata.vobsHint : void 0);
292
+ if (fix) defineErrorMetadata(value, "fix", fix);
293
+ const source = metadata.vobsSource;
294
+ if (source && typeof source === "object" && typeof source.file === "string" && typeof source.line === "number" && typeof source.column === "number") {
295
+ defineErrorMetadata(value, "location", source);
296
+ }
297
+ return value;
298
+ }
299
+ if (isVobsError(value)) return adoptVobsError(value, defaults);
300
+ return new VobsError({
301
+ code: defaults.code ?? "VOBS_UNKNOWN",
302
+ message: describeUnknown(value),
303
+ severity: defaults.severity ?? "error",
304
+ layer: defaults.layer ?? "runtime",
305
+ cause: void 0,
306
+ fix: defaults.fix
307
+ });
308
+ }
309
+ __name(normalizeVobsError, "normalizeVobsError");
310
+ function defineErrorMetadata(target, key, value) {
311
+ if (key in target) return;
312
+ try {
313
+ Object.defineProperty(target, key, { configurable: true, enumerable: false, value, writable: true });
314
+ } catch {
315
+ }
316
+ }
317
+ __name(defineErrorMetadata, "defineErrorMetadata");
318
+ function formatVobsError(value, options = {}) {
319
+ const error = normalizeVobsError(value);
320
+ const code = error.code || "VOBS_UNKNOWN";
321
+ const severity = error.severity || "error";
322
+ if (options.environment === "production") return `[Vobs ${code}] ${error.message}`;
323
+ const lines = [`[Vobs ${capitalize(severity)}] ${error.message}`, `Code: ${code}`];
324
+ if (error.location) lines.push(`Location: ${error.location.file}:${error.location.line}:${error.location.column}`);
325
+ if (error.codeFrame) lines.push("", error.codeFrame);
326
+ if (error.cause !== void 0) lines.push(`Cause: ${formatCause(error.cause)}`);
327
+ if (error.trace?.length) lines.push("", `Trace: ${error.trace.join(" \u2192 ")}`);
328
+ if (error.fix) lines.push("", `Fix: ${error.fix}`);
329
+ if (error.example) lines.push("", `Example:
330
+ ${error.example}`);
331
+ if (error.docs) lines.push(`Docs: ${error.docs}`);
332
+ if (options.includeStack && error.stack) lines.push("", error.stack);
333
+ return lines.join("\n");
334
+ }
335
+ __name(formatVobsError, "formatVobsError");
336
+ function formatCause(value) {
337
+ return value instanceof Error ? `${value.name}: ${value.message}` : String(value);
338
+ }
339
+ __name(formatCause, "formatCause");
340
+ function capitalize(value) {
341
+ return value.slice(0, 1).toUpperCase() + value.slice(1);
342
+ }
343
+ __name(capitalize, "capitalize");
344
+
14
345
  // packages/runtime/src/debug.ts
15
346
  var activeRuntimeDebugHooks = null;
16
347
  function getRuntimeDebugHooks() {
@@ -76,15 +407,34 @@ __name(associateHmrInstance, "associateHmrInstance");
76
407
 
77
408
  // packages/runtime/src/ref.ts
78
409
  import { getCurrentOwner as getCurrentOwner2 } from "@vobs/reactivity";
410
+ var refCleanups = /* @__PURE__ */ new WeakMap();
411
+ function rememberRefCleanup(owner, target, cleanup) {
412
+ let byTarget = refCleanups.get(owner);
413
+ if (!byTarget) {
414
+ byTarget = /* @__PURE__ */ new WeakMap();
415
+ refCleanups.set(owner, byTarget);
416
+ }
417
+ const previous = byTarget.get(target);
418
+ if (previous) owner.removeCleanup(previous);
419
+ byTarget.set(target, cleanup);
420
+ }
421
+ __name(rememberRefCleanup, "rememberRefCleanup");
79
422
  function setRef(node, target) {
80
423
  if (!isRefTarget(target)) return;
81
424
  if (isSSRNode(node)) return;
82
425
  const owner = getCurrentOwner2();
83
426
  assignRef(target, node);
84
- owner?.onDispose(() => {
85
- if (isObjectRef(target) && target.current !== node) return;
86
- assignRef(target, null);
87
- });
427
+ if (!owner) return;
428
+ const cleanup = /* @__PURE__ */ __name(() => {
429
+ try {
430
+ if (isObjectRef(target) && target.current !== node) return;
431
+ assignRef(target, null);
432
+ } catch (error) {
433
+ console.error(formatVobsError(error, { includeStack: true }));
434
+ }
435
+ }, "cleanup");
436
+ rememberRefCleanup(owner, target, cleanup);
437
+ owner.onDispose(cleanup);
88
438
  }
89
439
  __name(setRef, "setRef");
90
440
  function isSSRNode(value) {
@@ -104,7 +454,8 @@ function assignRef(target, value) {
104
454
  try {
105
455
  if (typeof target === "function") target(value);
106
456
  else target.current = value;
107
- } catch {
457
+ } catch (error) {
458
+ console.error(formatVobsError(error, { includeStack: true }));
108
459
  }
109
460
  }
110
461
  __name(assignRef, "assignRef");
@@ -113,8 +464,22 @@ __name(assignRef, "assignRef");
113
464
  var currentRenderer = null;
114
465
  var nodeOwners = /* @__PURE__ */ new WeakMap();
115
466
  var eventBindings = /* @__PURE__ */ new WeakMap();
467
+ var eventCleanupSlots = /* @__PURE__ */ new WeakMap();
468
+ var eventCleanupNodeIds = /* @__PURE__ */ new WeakMap();
469
+ var nextEventCleanupNodeId = 0;
470
+ function eventCleanupKey(node, event) {
471
+ let id = eventCleanupNodeIds.get(node);
472
+ if (id === void 0) {
473
+ id = ++nextEventCleanupNodeId;
474
+ eventCleanupNodeIds.set(node, id);
475
+ }
476
+ return `${id}:${event}`;
477
+ }
478
+ __name(eventCleanupKey, "eventCleanupKey");
116
479
  function setRenderer(renderer) {
117
- currentRenderer = renderer;
480
+ const previous = currentRenderer;
481
+ currentRenderer = renderer ?? null;
482
+ return previous;
118
483
  }
119
484
  __name(setRenderer, "setRenderer");
120
485
  function getRenderer() {
@@ -128,76 +493,20 @@ function createText(content) {
128
493
  return getRenderer().createText(content);
129
494
  }
130
495
  __name(createText, "createText");
131
- var SVG_NAMESPACE = "http://www.w3.org/2000/svg";
132
- var SVG_TAGS = /* @__PURE__ */ new Set([
133
- "animate",
134
- "animateMotion",
135
- "animateTransform",
136
- "circle",
137
- "clipPath",
138
- "defs",
139
- "desc",
140
- "ellipse",
141
- "feBlend",
142
- "feColorMatrix",
143
- "feComponentTransfer",
144
- "feComposite",
145
- "feConvolveMatrix",
146
- "feDiffuseLighting",
147
- "feDisplacementMap",
148
- "feDistantLight",
149
- "feDropShadow",
150
- "feFlood",
151
- "feFuncA",
152
- "feFuncB",
153
- "feFuncG",
154
- "feFuncR",
155
- "feGaussianBlur",
156
- "feImage",
157
- "feMerge",
158
- "feMergeNode",
159
- "feMorphology",
160
- "feOffset",
161
- "fePointLight",
162
- "feSpecularLighting",
163
- "feSpotLight",
164
- "feTile",
165
- "feTurbulence",
166
- "filter",
167
- "foreignObject",
168
- "g",
169
- "image",
170
- "line",
171
- "linearGradient",
172
- "marker",
173
- "mask",
174
- "metadata",
175
- "mpath",
176
- "path",
177
- "pattern",
178
- "polygon",
179
- "polyline",
180
- "radialGradient",
181
- "rect",
182
- "set",
183
- "stop",
184
- "svg",
185
- "switch",
186
- "symbol",
187
- "text",
188
- "textPath",
189
- "tspan",
190
- "use",
191
- "view"
192
- ]);
193
496
  function createElement(tag) {
194
- if (SVG_TAGS.has(tag)) {
497
+ if (isSvgTag(tag)) {
195
498
  const renderer = getRenderer();
196
499
  if (renderer.createSvgElement) return renderer.createSvgElement(tag);
197
500
  }
198
501
  return getRenderer().createElement(tag);
199
502
  }
200
503
  __name(createElement, "createElement");
504
+ function createSvgElement(tag) {
505
+ const renderer = getRenderer();
506
+ if (renderer.createSvgElement) return renderer.createSvgElement(tag);
507
+ return renderer.createElement(tag);
508
+ }
509
+ __name(createSvgElement, "createSvgElement");
201
510
  function createComment(content) {
202
511
  return getRenderer().createComment(content);
203
512
  }
@@ -231,7 +540,7 @@ function syncSelectValue(parent) {
231
540
  while (current !== null) {
232
541
  if (current.nodeName === "SELECT") {
233
542
  const read = selectValueReaders.get(current);
234
- if (read !== void 0) setProperty(current, "value", read());
543
+ if (read !== void 0) setProperty(current, "value", untrack(read));
235
544
  return;
236
545
  }
237
546
  current = current.parentNode;
@@ -239,9 +548,9 @@ function syncSelectValue(parent) {
239
548
  }
240
549
  __name(syncSelectValue, "syncSelectValue");
241
550
  function removeChild(parent, child) {
242
- disposeNodeOwner(child);
243
551
  if (isVobsFragment(child)) {
244
552
  child.unmount(parent);
553
+ disposeNodeOwner(child);
245
554
  return;
246
555
  }
247
556
  getRenderer().removeChild(parent, child);
@@ -252,6 +561,7 @@ function removeChild(parent, child) {
252
561
  parent: describeDebugNode(parent)
253
562
  });
254
563
  }
564
+ disposeNodeOwner(child);
255
565
  }
256
566
  __name(removeChild, "removeChild");
257
567
  function setTextContent(node, content) {
@@ -269,7 +579,7 @@ function setTextContent(node, content) {
269
579
  __name(setTextContent, "setTextContent");
270
580
  function setProperty(node, key, value) {
271
581
  const previousValue = getRuntimeDebugHooks() ? readDebugValue(() => Reflect.get(node, key)) : void 0;
272
- getRenderer().setProperty(node, key, value);
582
+ getRenderer().setProperty(node, PROPERTY_IDL_NAMES[key] ?? key, value);
273
583
  if (key === "value" && node.tagName === "SELECT") {
274
584
  scheduleSelectValueSync(node, value);
275
585
  }
@@ -290,12 +600,13 @@ function scheduleSelectValueSync(node, value) {
290
600
  pendingSelectValues.set(node, value);
291
601
  if (selectSyncScheduled.has(node)) return;
292
602
  selectSyncScheduled.add(node);
603
+ const renderer = getRenderer();
293
604
  queueMicrotask(() => {
294
605
  selectSyncScheduled.delete(node);
295
606
  if (!pendingSelectValues.has(node)) return;
296
607
  const pending = pendingSelectValues.get(node);
297
608
  pendingSelectValues.delete(node);
298
- getRenderer().setProperty(node, "value", pending);
609
+ renderer.setProperty(node, "value", pending);
299
610
  });
300
611
  }
301
612
  __name(scheduleSelectValueSync, "scheduleSelectValueSync");
@@ -314,48 +625,131 @@ function setAttribute(node, key, value) {
314
625
  }
315
626
  __name(setAttribute, "setAttribute");
316
627
  function spreadProps(node, props) {
317
- for (const [key, value] of Object.entries(props)) {
318
- if (key === "key" || value === null || value === void 0) continue;
628
+ applySpreadProps(node, null, props);
629
+ }
630
+ __name(spreadProps, "spreadProps");
631
+ function removeAttribute(node, key) {
632
+ const renderer = getRenderer();
633
+ if (renderer.removeAttribute) {
634
+ renderer.removeAttribute(node, key);
635
+ return;
636
+ }
637
+ renderer.setAttribute(node, key, "");
638
+ }
639
+ __name(removeAttribute, "removeAttribute");
640
+ var BOOLEAN_PROPERTIES = /* @__PURE__ */ new Set([
641
+ "checked",
642
+ "selected",
643
+ "disabled",
644
+ "multiple",
645
+ "readOnly",
646
+ "required",
647
+ "hidden",
648
+ "open",
649
+ "indeterminate",
650
+ "defaultChecked",
651
+ "muted",
652
+ "autoFocus"
653
+ ]);
654
+ var PROPERTY_IDL_NAMES = {
655
+ autoFocus: "autofocus"
656
+ };
657
+ function applySpreadProps(node, previous, next) {
658
+ for (const [key, value] of Object.entries(next)) {
659
+ if (key === "key") continue;
660
+ if (previous !== null && Object.is(previous[key], value)) continue;
661
+ if (key.startsWith("on")) {
662
+ const eventName = resolveEventName(key) ?? key.slice(2).toLowerCase();
663
+ const previousHandler = previous?.[key];
664
+ if (typeof previousHandler === "function") {
665
+ removeEventListener(node, eventName, previousHandler);
666
+ }
667
+ if (typeof value === "function") {
668
+ addEventListener(node, eventName, value);
669
+ }
670
+ continue;
671
+ }
319
672
  if (key === "ref") {
320
673
  setRef(node, value);
321
674
  continue;
322
675
  }
323
- if (key.startsWith("on") && typeof value === "function") addEventListener(node, key.slice(2).toLowerCase(), value);
324
- else if (isPropertyKey(key)) setProperty(node, key, value);
676
+ if (value === null || value === void 0) continue;
677
+ else if (isClassListKey(key)) applyClassList(node, value);
678
+ else if (isPropertyName(key)) setProperty(node, key, value);
325
679
  else if (value === false) continue;
326
680
  else setAttribute(node, domAttributeName(key), key === "style" && isStyleObject(value) ? formatStyle(value) : String(value));
327
681
  }
682
+ if (previous === null) return;
683
+ for (const key of Object.keys(previous)) {
684
+ if (key in next || key === "key") continue;
685
+ if (key.startsWith("on")) {
686
+ const eventName = resolveEventName(key) ?? key.slice(2).toLowerCase();
687
+ const previousHandler = previous[key];
688
+ if (typeof previousHandler === "function") {
689
+ removeEventListener(node, eventName, previousHandler);
690
+ }
691
+ continue;
692
+ }
693
+ if (key === "ref") continue;
694
+ if (isPropertyName(key)) setProperty(node, key, BOOLEAN_PROPERTIES.has(key) ? false : "");
695
+ else removeAttribute(node, domAttributeName(key));
696
+ }
328
697
  }
329
- __name(spreadProps, "spreadProps");
698
+ __name(applySpreadProps, "applySpreadProps");
699
+ var classListContributions = /* @__PURE__ */ new WeakMap();
700
+ function parseClassList(value) {
701
+ const names = [];
702
+ const push = /* @__PURE__ */ __name((candidate) => {
703
+ if (typeof candidate === "string") {
704
+ for (const name of candidate.split(/\s+/u)) if (name !== "") names.push(name);
705
+ }
706
+ }, "push");
707
+ if (Array.isArray(value)) {
708
+ for (const entry of value) {
709
+ if (entry !== null && typeof entry === "object") {
710
+ for (const [name, on] of Object.entries(entry)) if (on) push(name);
711
+ } else push(entry);
712
+ }
713
+ } else if (value !== null && typeof value === "object") {
714
+ for (const [name, on] of Object.entries(value)) if (on) push(name);
715
+ } else push(value);
716
+ return names;
717
+ }
718
+ __name(parseClassList, "parseClassList");
719
+ function applyClassList(node, value) {
720
+ const next = parseClassList(value);
721
+ const previous = classListContributions.get(node) ?? [];
722
+ classListContributions.set(node, next);
723
+ const base = (node.getAttribute("class") ?? "").split(/\s+/u).filter((name) => name !== "" && !previous.includes(name));
724
+ const merged = [.../* @__PURE__ */ new Set([...base, ...next])];
725
+ setAttribute(node, "class", merged.join(" "));
726
+ }
727
+ __name(applyClassList, "applyClassList");
728
+ function isClassListKey(key) {
729
+ return key === "classList";
730
+ }
731
+ __name(isClassListKey, "isClassListKey");
732
+ function bindSpreadProps(node, source) {
733
+ let previous = null;
734
+ effect(() => {
735
+ const next = source() ?? {};
736
+ const snapshot = { ...next };
737
+ applySpreadProps(node, previous, next);
738
+ previous = snapshot;
739
+ });
740
+ }
741
+ __name(bindSpreadProps, "bindSpreadProps");
330
742
  function setStaticProps(node, props) {
331
743
  for (const [key, value] of Object.entries(props)) {
332
744
  if (key === "key" || key === "ref" || key.startsWith("on")) continue;
333
745
  if (value === null || value === void 0) continue;
334
- if (isPropertyKey(key)) setProperty(node, key, value);
746
+ if (isClassListKey(key)) applyClassList(node, value);
747
+ else if (isPropertyName(key)) setProperty(node, key, value);
335
748
  else if (value === false) continue;
336
749
  else setAttribute(node, domAttributeName(key), key === "style" && isStyleObject(value) ? formatStyle(value) : String(value));
337
750
  }
338
751
  }
339
752
  __name(setStaticProps, "setStaticProps");
340
- function isPropertyKey(key) {
341
- return key === "value" || key === "checked" || key === "selected" || key === "disabled" || key === "multiple" || key === "readOnly" || key === "required" || key === "autofocus" || key === "hidden" || key === "tabIndex" || key === "colSpan" || key === "rowSpan";
342
- }
343
- __name(isPropertyKey, "isPropertyKey");
344
- function domAttributeName(key) {
345
- switch (key) {
346
- case "className":
347
- return "class";
348
- case "htmlFor":
349
- return "for";
350
- case "autoComplete":
351
- return "autocomplete";
352
- case "spellCheck":
353
- return "spellcheck";
354
- default:
355
- return key;
356
- }
357
- }
358
- __name(domAttributeName, "domAttributeName");
359
753
  function isStyleObject(value) {
360
754
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
361
755
  }
@@ -374,7 +768,9 @@ function addEventListener(node, event, handler) {
374
768
  }
375
769
  const previous = bindings.get(event);
376
770
  if (previous && previous.original === handler && previous.owner === owner) return;
377
- if (previous) renderer.removeEventListener(node, event, previous.handler);
771
+ if (previous) {
772
+ renderer.removeEventListener(node, event, previous.handler);
773
+ }
378
774
  const listener = owner ? (reason) => {
379
775
  if (owner.disposed) return;
380
776
  try {
@@ -388,28 +784,66 @@ function addEventListener(node, event, handler) {
388
784
  handled,
389
785
  recovery: handled ? "handled" : "propagated"
390
786
  });
787
+ if (handled && getRuntimeDebugHooks()?.error === void 0) {
788
+ console.error(formatVobsError(error, { includeStack: true }));
789
+ }
391
790
  if (!handled) throw error;
392
791
  }
393
792
  } : handler;
394
793
  const binding = { handler: listener, owner, original: handler };
395
794
  bindings.set(event, binding);
396
795
  renderer.addEventListener(node, event, listener);
397
- owner?.onDispose(() => {
398
- if (bindings?.get(event) !== binding) return;
399
- bindings.delete(event);
400
- renderer.removeEventListener(node, event, listener);
796
+ if (!owner) return;
797
+ const key = eventCleanupKey(node, event);
798
+ let slots = eventCleanupSlots.get(owner);
799
+ if (!slots) {
800
+ slots = /* @__PURE__ */ new Map();
801
+ eventCleanupSlots.set(owner, slots);
802
+ }
803
+ const hasSlot = slots.has(key);
804
+ slots.set(key, binding);
805
+ if (hasSlot) return;
806
+ owner.onDispose(() => {
807
+ const current = eventCleanupSlots.get(owner)?.get(key);
808
+ if (!current) return;
809
+ eventCleanupSlots.get(owner)?.delete(key);
810
+ const map = eventBindings.get(node);
811
+ if (map?.get(event) === current) map.delete(event);
812
+ renderer.removeEventListener(node, event, current.handler);
401
813
  });
402
814
  }
403
815
  __name(addEventListener, "addEventListener");
404
816
  function removeEventListener(node, event, handler) {
405
817
  const renderer = getRenderer();
406
- const binding = eventBindings.get(node)?.get(event);
818
+ const map = eventBindings.get(node);
819
+ const binding = map?.get(event);
820
+ if (binding) {
821
+ if (binding.handler !== handler) return;
822
+ map?.delete(event);
823
+ }
407
824
  renderer.removeEventListener(node, event, binding?.handler ?? handler);
408
- eventBindings.get(node)?.delete(event);
409
825
  }
410
826
  __name(removeEventListener, "removeEventListener");
411
827
  function clear(container) {
828
+ let firstError;
829
+ const walk = /* @__PURE__ */ __name((node) => {
830
+ try {
831
+ disposeNodeOwner(node);
832
+ } catch (error) {
833
+ firstError ?? (firstError = error);
834
+ }
835
+ for (const child of Array.from(node.childNodes)) walk(child);
836
+ }, "walk");
837
+ for (const child of Array.from(container.childNodes)) walk(child);
412
838
  getRenderer().clear(container);
839
+ if (getRuntimeDebugHooks()) {
840
+ invokeRuntimeDebug("domMutation", {
841
+ operation: "clear",
842
+ target: describeDebugNode(container),
843
+ parent: describeDebugNode(container)
844
+ });
845
+ }
846
+ if (firstError) throw firstError;
413
847
  }
414
848
  __name(clear, "clear");
415
849
  function createComponent(component, props, source) {
@@ -431,15 +865,16 @@ function createComponent(component, props, source) {
431
865
  owner.onDispose(cleanup);
432
866
  }
433
867
  const renderScope = owner.mark();
434
- let node;
868
+ let rendered;
435
869
  try {
436
- node = owner.run(() => untrack(() => component(props)));
870
+ rendered = owner.run(() => untrack(() => component(props)));
437
871
  } catch (error) {
438
872
  owner.dispose();
439
873
  attachSourceLocation(error, source);
440
874
  attachComponentContext(error, componentName, owner.id);
441
875
  throw error;
442
876
  }
877
+ const node = isRenderableNode(rendered) ? rendered : createComment("vobs:empty");
443
878
  associateNodeOwner(node, owner);
444
879
  if (instance) {
445
880
  instance.node = node;
@@ -510,7 +945,16 @@ function createBlock(factory) {
510
945
  return node;
511
946
  }
512
947
  __name(createBlock, "createBlock");
948
+ function isRenderableNode(value) {
949
+ return typeof value === "object" && value !== null || typeof value === "function";
950
+ }
951
+ __name(isRenderableNode, "isRenderableNode");
513
952
  function associateNodeOwner(node, owner) {
953
+ if (node === null || node === void 0 || typeof node !== "object" && typeof node !== "function") {
954
+ throw new Error(
955
+ `Vobs: associateNodeOwner \u6536\u5230\u975E\u8282\u70B9\u503C\uFF08${String(node)}\uFF09\u3002\u7EC4\u4EF6\u5FC5\u987B\u8FD4\u56DE VobsNode\uFF1B\u8FD4\u56DE null/undefined \u7531 createComponent \u8F6C\u6210\u7A7A\u6CE8\u91CA\u8282\u70B9\u3002`
956
+ );
957
+ }
514
958
  nodeOwners.set(node, owner);
515
959
  }
516
960
  __name(associateNodeOwner, "associateNodeOwner");
@@ -524,17 +968,22 @@ __name(disposeNodeOwner, "disposeNodeOwner");
524
968
  export {
525
969
  SVG_NAMESPACE,
526
970
  addEventListener,
971
+ applyClassList,
527
972
  associateNodeOwner,
973
+ bindSpreadProps,
528
974
  clear,
529
975
  createBlock,
530
976
  createComment,
531
977
  createComponent,
532
978
  createElement,
979
+ createSvgElement,
533
980
  createText,
534
981
  disposeNodeOwner,
535
982
  getRenderer,
536
983
  insertBefore,
984
+ parseClassList,
537
985
  registerSelectValueBinding,
986
+ removeAttribute,
538
987
  removeChild,
539
988
  removeEventListener,
540
989
  setAttribute,