tyaff 0.0.1 → 0.0.2

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.
package/src/core.js ADDED
@@ -0,0 +1,1278 @@
1
+ // ============================================================================
2
+ // VDOM Library — custom VDOM, diff/patch, context tree, portals, refs
3
+ // ============================================================================
4
+
5
+ const Fragment = Symbol('Fragment');
6
+ const Portal = Symbol('Portal');
7
+ const SVG_NS = 'http://www.w3.org/2000/svg';
8
+ const XLINK_NS = 'http://www.w3.org/1999/xlink';
9
+ const HTML_NS = 'http://www.w3.org/1999/xhtml';
10
+
11
+ function pushAll(target, source) {
12
+ if (source == null) return;
13
+ if (Array.isArray(source)) {
14
+ for (let i = 0; i < source.length; i++) target.push(source[i]);
15
+ } else {
16
+ target.push(source);
17
+ }
18
+ }
19
+
20
+ const PREPEND_CHUNK_SIZE = 20000;
21
+
22
+ function prependAll(parent, nodes) {
23
+ if (!nodes || nodes.length === 0) return;
24
+ if (nodes.length <= PREPEND_CHUNK_SIZE) {
25
+ parent.prepend(...nodes);
26
+ return;
27
+ }
28
+ for (let i = nodes.length; i > 0; i -= PREPEND_CHUNK_SIZE) {
29
+ const start = Math.max(0, i - PREPEND_CHUNK_SIZE);
30
+ parent.prepend(...nodes.slice(start, i));
31
+ }
32
+ }
33
+
34
+ function collectDOMNodes(childs) {
35
+ const result = [];
36
+ function walk(node) {
37
+ if (node == null) return;
38
+ if (Array.isArray(node)) {
39
+ for (let i = 0; i < node.length; i++) walk(node[i]);
40
+ return;
41
+ }
42
+ if (typeof node !== 'object') return;
43
+ if (node.nodeType) {
44
+ result.push(node);
45
+ return;
46
+ }
47
+ if (node._instance) {
48
+ const inst = node._instance;
49
+ if (inst._anchor && inst._anchor.nodeType) result.push(inst._anchor);
50
+ // Для Portal НЕ recurse в _nodes — они физически в другом контейнере
51
+ if (!inst._isPortal && Array.isArray(inst._nodes)) {
52
+ for (let i = 0; i < inst._nodes.length; i++) walk(inst._nodes[i]);
53
+ }
54
+ return;
55
+ }
56
+ if (node._el) {
57
+ result.push(node._el);
58
+ return;
59
+ }
60
+ if (Array.isArray(node._nodes)) {
61
+ for (let i = 0; i < node._nodes.length; i++) walk(node._nodes[i]);
62
+ }
63
+ }
64
+ walk(childs);
65
+ return result;
66
+ }
67
+
68
+ function h(type, props, ...children) {
69
+ const normalized = [];
70
+ for (let i = 0; i < children.length; i++) {
71
+ const c = children[i];
72
+ if (c == null || c === false || c === true) {
73
+ normalized.push(null);
74
+ } else if (typeof c === 'string' || typeof c === 'number') {
75
+ normalized.push({ _text: String(c) });
76
+ } else {
77
+ normalized.push(c);
78
+ }
79
+ }
80
+ return { tag: type, props: props || {}, childs: normalized };
81
+ }
82
+
83
+ function createPortal(children, containerGetter) {
84
+ const kids = Array.isArray(children) ? children : [children];
85
+ return { tag: Portal, props: { containerGetter }, childs: kids };
86
+ }
87
+
88
+ function Component(definition) {
89
+ function ComponentClass() {
90
+ for (const key in definition) {
91
+ if (key === 'context') continue;
92
+ const val = definition[key];
93
+ if (typeof val !== 'function') {
94
+ this[key] = val;
95
+ }
96
+ }
97
+ this._definition = definition;
98
+ this._parentContext = null;
99
+ this._incomingProps = null;
100
+ this.props = {};
101
+ this._vdom = null;
102
+ this._nodes = [];
103
+ this._parentDOM = null;
104
+ this._prevMemo = null;
105
+ this._keyMap = new Map();
106
+ this._refCollectors = {};
107
+ this._isMounted = false;
108
+ this._isUpdating = false;
109
+ this._isRendering = false;
110
+ this._isInitializing = false;
111
+ this._inContextCall = false;
112
+ this._namespace = HTML_NS;
113
+ }
114
+ ComponentClass._definition = definition;
115
+ return ComponentClass;
116
+ }
117
+
118
+ const batchQueue = new Set();
119
+ let isBatchScheduled = false;
120
+ let isFlushing = false;
121
+ let nestedUpdateCount = 0;
122
+ const NESTED_UPDATE_LIMIT = 50;
123
+
124
+ let refreshResolvers = [];
125
+
126
+ function scheduleUpdate(inst) {
127
+ batchQueue.add(inst);
128
+ if (!isBatchScheduled) {
129
+ isBatchScheduled = true;
130
+ if (!isFlushing) nestedUpdateCount = 0;
131
+ Promise.resolve().then(flushBatch);
132
+ }
133
+ }
134
+
135
+ function flushRefreshResolvers() {
136
+ if (refreshResolvers.length === 0) return;
137
+ const resolvers = refreshResolvers;
138
+ refreshResolvers = [];
139
+ for (const finish of resolvers) {
140
+ try {
141
+ finish();
142
+ } catch (err) {
143
+ console.error('Error in refresh resolver:', err);
144
+ }
145
+ }
146
+ }
147
+
148
+ function flushBatch() {
149
+ isFlushing = true;
150
+ try {
151
+ nestedUpdateCount++;
152
+ if (nestedUpdateCount > NESTED_UPDATE_LIMIT) {
153
+ console.error(
154
+ '❌ Maximum update depth exceeded (' + NESTED_UPDATE_LIMIT + ').\n' +
155
+ 'This happens when a component repeatedly calls update() ' +
156
+ 'inside onUpdated() or another lifecycle method.'
157
+ );
158
+ batchQueue.clear();
159
+ isBatchScheduled = false;
160
+ flushRefreshResolvers();
161
+ return;
162
+ }
163
+
164
+ const toUpdate = Array.from(batchQueue);
165
+ batchQueue.clear();
166
+ isBatchScheduled = false;
167
+
168
+ for (const inst of toUpdate) {
169
+ try {
170
+ inst._rerender();
171
+ } catch (err) {
172
+ const name = inst._definition && inst._definition.name
173
+ ? inst._definition.name : 'Component';
174
+ console.error('❌ Error in component "' + name + '":\n', err);
175
+ }
176
+ }
177
+
178
+ if (batchQueue.size > 0 && !isBatchScheduled) {
179
+ isBatchScheduled = true;
180
+ Promise.resolve().then(flushBatch);
181
+ } else if (batchQueue.size === 0) {
182
+ flushRefreshResolvers();
183
+ }
184
+ } finally {
185
+ isFlushing = false;
186
+ }
187
+ }
188
+
189
+ function attachInstanceAPI(inst) {
190
+ const def = inst._definition;
191
+
192
+ inst._rerender = function() {
193
+ if (this._isUpdating) return;
194
+ this._isUpdating = true;
195
+ try {
196
+ const d = this._definition;
197
+ if (d.props) {
198
+ this.props = d.props.call(this, this._incomingProps);
199
+ } else {
200
+ this.props = this._incomingProps || {};
201
+ }
202
+
203
+ let shouldRender = true;
204
+ if (d.memo) {
205
+ const newDeps = d.memo.call(this, this.props);
206
+ if (this._prevMemo && newDeps.length === this._prevMemo.length) {
207
+ let same = true;
208
+ for (let i = 0; i < newDeps.length; i++) {
209
+ if (newDeps[i] !== this._prevMemo[i]) { same = false; break; }
210
+ }
211
+ if (same) shouldRender = false;
212
+ }
213
+ this._prevMemo = newDeps;
214
+ }
215
+
216
+ const oldVdom = this._vdom;
217
+ let newVdom;
218
+
219
+ if (shouldRender) {
220
+ this._keyMap.clear();
221
+ if (oldVdom) populateKeyMap(oldVdom, '', this._keyMap);
222
+ this._isRendering = true;
223
+ try {
224
+ newVdom = d.render.call(this, this.props);
225
+ } finally {
226
+ this._isRendering = false;
227
+ }
228
+ } else {
229
+ newVdom = oldVdom;
230
+ }
231
+
232
+ const oldNodes = this._nodes;
233
+ const wasFirstRender = !oldVdom;
234
+
235
+ const newNodes = reconcile(
236
+ oldVdom, newVdom, this._parentDOM, this, '',
237
+ this._keyMap, this._namespace
238
+ );
239
+ const flat = Array.isArray(newNodes) ? newNodes : (newNodes ? [newNodes] : []);
240
+
241
+ if (!wasFirstRender && this._parentDOM) {
242
+ syncDOMChildren(this._parentDOM, oldNodes, flat);
243
+ }
244
+
245
+ this._nodes = flat;
246
+ this._vdom = newVdom;
247
+
248
+ // onUpdated вызывается после ЛЮБОГО реального render (включая первый)
249
+ if (shouldRender && !wasFirstRender && d.onUpdated) {
250
+ d.onUpdated.call(this);
251
+ }
252
+ } finally {
253
+ this._isUpdating = false;
254
+ }
255
+ };
256
+
257
+ inst.update = function(patch) {
258
+ if (this._isRendering) {
259
+ console.error(
260
+ '❌ Cannot call update() inside render().\n' +
261
+ 'Use direct assignment instead: this.value = 22;'
262
+ );
263
+ return;
264
+ }
265
+ if (patch && typeof patch === 'object') {
266
+ if (Object.keys(patch).length === 0) {
267
+ if (this._isInitializing) return;
268
+ scheduleUpdate(this);
269
+ return;
270
+ }
271
+ let changed = false;
272
+ for (const k in patch) {
273
+ if (this[k] !== patch[k]) { changed = true; break; }
274
+ }
275
+ if (!changed) return;
276
+ Object.assign(this, patch);
277
+ }
278
+ if (this._isInitializing) return;
279
+ scheduleUpdate(this);
280
+ };
281
+
282
+ inst._refCollectors = {};
283
+ inst.refs = function(name) {
284
+ if (!inst._refCollectors[name]) {
285
+ inst._refCollectors[name] = (node) => {
286
+ inst.refs[name] = node;
287
+ };
288
+ }
289
+ return inst._refCollectors[name];
290
+ };
291
+
292
+ inst.context = function(key, ...args) {
293
+ let p = this._parentContext;
294
+ while (p) {
295
+ const ctx = p._definition.context;
296
+ if (ctx && typeof ctx[key] === 'function') {
297
+ return ctx[key].apply(p, args);
298
+ }
299
+ p = p._parentContext;
300
+ }
301
+ return undefined;
302
+ };
303
+
304
+ inst.contextSelf = function(key, ...args) {
305
+ if (this._inContextCall) throw new Error('contextSelf recursion');
306
+ const ctx = this._definition.context;
307
+ if (ctx && typeof ctx[key] === 'function') {
308
+ this._inContextCall = true;
309
+ try {
310
+ const res = ctx[key].apply(this, args);
311
+ if (res !== undefined) return res;
312
+ } finally {
313
+ this._inContextCall = false;
314
+ }
315
+ }
316
+ return this.context(key, ...args);
317
+ };
318
+
319
+ const reserved = [
320
+ 'init', 'render', 'props', 'memo',
321
+ 'onMounted', 'onUpdated', 'onUnmounted', 'context',
322
+ 'update', 'refs', 'contextSelf', '_rerender'
323
+ ];
324
+ for (const key in def) {
325
+ const val = def[key];
326
+ if (typeof val === 'function' && !reserved.includes(key)) {
327
+ if (inst[key] === undefined) {
328
+ inst[key] = val.bind(inst);
329
+ }
330
+ }
331
+ }
332
+ }
333
+
334
+ function makeMapKey(vnode, index, path) {
335
+ if (vnode && vnode.props && vnode.props.key !== undefined) {
336
+ const userKey = String(vnode.props.key).replace(/,/g, ',,');
337
+ return '#' + userKey;
338
+ }
339
+ return path;
340
+ }
341
+
342
+ function populateKeyMap(vnode, path, keyMap) {
343
+ if (vnode == null) return;
344
+ if (Array.isArray(vnode)) {
345
+ for (let i = 0; i < vnode.length; i++) {
346
+ populateKeyMap(vnode[i], path + ',' + i, keyMap);
347
+ }
348
+ return;
349
+ }
350
+ if (vnode._text !== undefined) return;
351
+
352
+ if (vnode.tag === Fragment) {
353
+ const hasKey = vnode.props && vnode.props.key !== undefined;
354
+
355
+ if (hasKey && vnode._instance) {
356
+ const key = makeMapKey(vnode, 0, path);
357
+ keyMap.set(key, vnode._instance);
358
+ }
359
+
360
+ const basePath = hasKey ? '' : path;
361
+ if (vnode.childs) {
362
+ for (let i = 0; i < vnode.childs.length; i++) {
363
+ populateKeyMap(vnode.childs[i], basePath + ',' + i, keyMap);
364
+ }
365
+ }
366
+ return;
367
+ }
368
+
369
+ if (vnode._instance) {
370
+ const key = makeMapKey(vnode, 0, path);
371
+ keyMap.set(key, vnode._instance);
372
+ }
373
+
374
+ if (vnode.childs) {
375
+ for (let i = 0; i < vnode.childs.length; i++) {
376
+ populateKeyMap(vnode.childs[i], path + ',' + i, keyMap);
377
+ }
378
+ }
379
+ }
380
+
381
+ const RECREATE_ATTRS = ['type', 'is'];
382
+ const CAMEL_TO_ATTR = {
383
+ className: 'class',
384
+ htmlFor: 'for',
385
+ tabIndex: 'tabindex'
386
+ };
387
+
388
+ function applyProp(dom, key, value, namespace) {
389
+ if (key === 'key' || key === 'ref' || key === 'children') return;
390
+ const isSVG = namespace === SVG_NS;
391
+ const tag = dom.tagName;
392
+
393
+ if (!isSVG) {
394
+ if (tag === 'INPUT' || tag === 'TEXTAREA' || tag === 'SELECT') {
395
+ if (key === 'value') {
396
+ if (tag === 'SELECT' && dom.multiple) {
397
+ const values = Array.isArray(value) ? value : (value == null ? [] : [value]);
398
+ for (let i = 0; i < dom.options.length; i++) {
399
+ dom.options[i].selected = values.includes(dom.options[i].value);
400
+ }
401
+ return;
402
+ }
403
+ if (tag === 'INPUT' && dom.type === 'file') return;
404
+ const strVal = value == null ? '' : String(value);
405
+ if (dom.value !== strVal) dom.value = strVal;
406
+ dom.setAttribute('value', strVal);
407
+ return;
408
+ }
409
+ if (key === 'checked') {
410
+ dom.checked = !!value;
411
+ if (value) dom.setAttribute('checked', '');
412
+ else dom.removeAttribute('checked');
413
+ return;
414
+ }
415
+ if (tag === 'SELECT' && key === 'multiple') {
416
+ dom.multiple = !!value;
417
+ if (value) dom.setAttribute('multiple', '');
418
+ else dom.removeAttribute('multiple');
419
+ return;
420
+ }
421
+ }
422
+ if (tag === 'OPTION' && key === 'selected') {
423
+ dom.selected = !!value;
424
+ if (value) dom.setAttribute('selected', '');
425
+ else dom.removeAttribute('selected');
426
+ return;
427
+ }
428
+ }
429
+
430
+ if (key.length > 2 && key[0] === 'o' && key[1] === 'n') {
431
+ const eventType = key.substring(2).toLowerCase();
432
+ const store = dom._evtStore || (dom._evtStore = {});
433
+ const oldHandler = store[eventType];
434
+ if (oldHandler) dom.removeEventListener(eventType, oldHandler);
435
+ if (typeof value === 'function') {
436
+ dom.addEventListener(eventType, value);
437
+ store[eventType] = value;
438
+ } else {
439
+ delete store[eventType];
440
+ }
441
+ return;
442
+ }
443
+
444
+ if (key === 'dangerouslySetInnerHTML') {
445
+ if (value && value.__html != null) dom.innerHTML = value.__html;
446
+ return;
447
+ }
448
+
449
+ if (key === 'style') {
450
+ if (value == null) {
451
+ dom.removeAttribute('style');
452
+ } else if (typeof value === 'object') {
453
+ let css = '';
454
+ for (const p in value) {
455
+ const cssProp = p.replace(/[A-Z]/g, m => '-' + m.toLowerCase());
456
+ css += cssProp + ':' + value[p] + ';';
457
+ }
458
+ dom.setAttribute('style', css);
459
+ } else {
460
+ dom.setAttribute('style', String(value));
461
+ }
462
+ return;
463
+ }
464
+
465
+ if (value === false || value == null) {
466
+ if (isSVG) {
467
+ if (key === 'xlinkHref') {
468
+ dom.removeAttributeNS(XLINK_NS, 'href');
469
+ } else {
470
+ dom.removeAttribute(key);
471
+ }
472
+ } else {
473
+ const attr = CAMEL_TO_ATTR[key] || key.toLowerCase();
474
+ dom.removeAttribute(attr);
475
+ }
476
+ return;
477
+ }
478
+
479
+ if (isSVG) {
480
+ if (key === 'xlinkHref') {
481
+ dom.setAttributeNS(XLINK_NS, 'xlink:href', value);
482
+ } else {
483
+ dom.setAttribute(key, value === true ? '' : String(value));
484
+ }
485
+ return;
486
+ }
487
+
488
+ const attr = CAMEL_TO_ATTR[key] || key.toLowerCase();
489
+ dom.setAttribute(attr, value === true ? '' : String(value));
490
+ }
491
+
492
+ function applyProps(dom, oldProps, newProps, namespace) {
493
+ oldProps = oldProps || {};
494
+ newProps = newProps || {};
495
+
496
+ for (const k in oldProps) {
497
+ if (!(k in newProps)) applyProp(dom, k, null, namespace);
498
+ }
499
+ for (const k in newProps) {
500
+ if (oldProps[k] !== newProps[k]) {
501
+ applyProp(dom, k, newProps[k], namespace);
502
+ }
503
+ }
504
+ }
505
+
506
+ function callRefs(vnode, seen = new WeakSet()) {
507
+ if (vnode == null) return;
508
+ if (typeof vnode !== 'object') return;
509
+ if (seen.has(vnode)) return;
510
+ seen.add(vnode);
511
+
512
+ if (Array.isArray(vnode)) {
513
+ for (let i = 0; i < vnode.length; i++) callRefs(vnode[i], seen);
514
+ return;
515
+ }
516
+ if (vnode._text !== undefined) return;
517
+
518
+ if (vnode.tag === Portal) {
519
+ const inst = vnode._instance;
520
+ if (vnode.props && vnode.props.ref && inst) vnode.props.ref(inst);
521
+ if (inst && inst._rendered) callRefs(inst._rendered, seen);
522
+ return;
523
+ }
524
+ if (typeof vnode.tag === 'function' && vnode.tag._definition) {
525
+ const inst = vnode._instance;
526
+ if (vnode.props && vnode.props.ref && inst) vnode.props.ref(inst);
527
+ if (inst && inst._vdom) callRefs(inst._vdom, seen);
528
+ return;
529
+ }
530
+ if (vnode.props && vnode.props.ref && vnode._el) {
531
+ vnode.props.ref(vnode._el);
532
+ }
533
+ if (vnode.childs) {
534
+ for (let i = 0; i < vnode.childs.length; i++) callRefs(vnode.childs[i], seen);
535
+ }
536
+ }
537
+
538
+ function triggerMounted(roots) {
539
+ const components = [];
540
+ const stack = Array.isArray(roots) ? roots.slice() : [roots];
541
+ const seen = new WeakSet();
542
+
543
+ while (stack.length) {
544
+ const vnode = stack.pop();
545
+ if (vnode == null || typeof vnode !== 'object') continue;
546
+ if (seen.has(vnode)) continue;
547
+ seen.add(vnode);
548
+
549
+ if (Array.isArray(vnode)) {
550
+ for (let i = vnode.length - 1; i >= 0; i--) stack.push(vnode[i]);
551
+ continue;
552
+ }
553
+ if (vnode._text !== undefined) continue;
554
+
555
+ if (vnode.tag === Portal) {
556
+ const inst = vnode._instance;
557
+ if (inst && inst._rendered) stack.push(inst._rendered);
558
+ continue;
559
+ }
560
+ if (typeof vnode.tag === 'function' && vnode.tag._definition) {
561
+ const inst = vnode._instance;
562
+ if (inst && !inst._isMounted) {
563
+ inst._isMounted = true;
564
+ components.push(inst);
565
+ }
566
+ if (inst && inst._vdom) stack.push(inst._vdom);
567
+ continue;
568
+ }
569
+ if (vnode.childs) {
570
+ for (let i = vnode.childs.length - 1; i >= 0; i--) {
571
+ stack.push(vnode.childs[i]);
572
+ }
573
+ }
574
+ }
575
+
576
+ for (let i = components.length - 1; i >= 0; i--) {
577
+ const inst = components[i];
578
+ const d = inst._definition;
579
+ try {
580
+ if (d.onMounted) d.onMounted.call(inst);
581
+ } catch (err) {
582
+ const name = d.name || 'Component';
583
+ console.error('❌ Error in onMounted of "' + name + '":\n', err);
584
+ }
585
+ }
586
+ }
587
+
588
+ function unmountVdom(vnode, seen = new WeakSet()) {
589
+ if (vnode == null || typeof vnode !== 'object') return;
590
+ if (seen.has(vnode)) return;
591
+ seen.add(vnode);
592
+
593
+ if (Array.isArray(vnode)) {
594
+ for (let i = 0; i < vnode.length; i++) unmountVdom(vnode[i], seen);
595
+ return;
596
+ }
597
+ if (vnode._text !== undefined) return;
598
+
599
+ if (vnode.tag === Portal) {
600
+ const inst = vnode._instance;
601
+ if (inst) {
602
+ if (vnode.props && vnode.props.ref) vnode.props.ref(null);
603
+ if (inst._rendered) unmountVdom(inst._rendered, seen);
604
+ if (inst._anchor && inst._anchor.parentNode) {
605
+ inst._anchor.parentNode.removeChild(inst._anchor);
606
+ }
607
+ if (inst._container) {
608
+ for (let i = 0; i < inst._nodes.length; i++) {
609
+ const n = inst._nodes[i];
610
+ if (n && n.parentNode === inst._container) {
611
+ inst._container.removeChild(n);
612
+ }
613
+ }
614
+ }
615
+ }
616
+ return;
617
+ }
618
+ if (typeof vnode.tag === 'function' && vnode.tag._definition) {
619
+ const inst = vnode._instance;
620
+ if (inst) {
621
+ if (vnode.props && vnode.props.ref) vnode.props.ref(null);
622
+ const d = inst._definition;
623
+ if (d.onUnmounted) d.onUnmounted.call(inst);
624
+ if (inst._vdom) unmountVdom(inst._vdom, seen);
625
+ }
626
+ return;
627
+ }
628
+ if (typeof vnode.tag === 'string') {
629
+ if (vnode.props && vnode.props.ref && vnode._el) {
630
+ vnode.props.ref(null);
631
+ }
632
+ }
633
+ if (vnode.childs) {
634
+ for (let i = 0; i < vnode.childs.length; i++) unmountVdom(vnode.childs[i], seen);
635
+ }
636
+ }
637
+
638
+ function syncDOMChildren(parentDOM, oldNodes, newNodes) {
639
+ let oi = 0;
640
+ for (let i = 0; i < newNodes.length; i++) {
641
+ const n = newNodes[i];
642
+ const o = oi < oldNodes.length ? oldNodes[oi] : null;
643
+ if (n === o) {
644
+ oi++;
645
+ } else {
646
+ let ref = null;
647
+ for (let j = oi; j < oldNodes.length; j++) {
648
+ if (oldNodes[j] && oldNodes[j].parentNode === parentDOM) {
649
+ ref = oldNodes[j]; break;
650
+ }
651
+ }
652
+ parentDOM.insertBefore(n, ref);
653
+ }
654
+ }
655
+ while (oi < oldNodes.length) {
656
+ const o = oldNodes[oi++];
657
+ if (o && o.parentNode === parentDOM) parentDOM.removeChild(o);
658
+ }
659
+ }
660
+
661
+ function reconcile(oldNode, newNode, parentDOM, ctx, path, keyMap, namespace) {
662
+ if (oldNode === newNode) {
663
+ if (Array.isArray(newNode)) {
664
+ const nodes = [];
665
+ for (let i = 0; i < newNode.length; i++) {
666
+ const child = newNode[i];
667
+ pushAll(nodes, reconcile(child, child, parentDOM, ctx, path + ',' + i, keyMap, namespace));
668
+ }
669
+ return nodes;
670
+ }
671
+ if (newNode && (
672
+ (typeof newNode.tag === 'function' && newNode.tag._definition) ||
673
+ newNode.tag === Portal
674
+ )) {
675
+ // Продолжить ниже
676
+ } else if (newNode && typeof newNode.tag === 'string') {
677
+ if (newNode.childs) {
678
+ for (let i = 0; i < newNode.childs.length; i++) {
679
+ const child = newNode.childs[i];
680
+ reconcile(child, child, newNode._el, ctx, path + ',' + i, keyMap, namespace);
681
+ }
682
+ }
683
+ return extractNodes(newNode);
684
+ } else if (newNode && newNode.tag === Fragment) {
685
+ const hasKey = newNode.props && newNode.props.key !== undefined;
686
+ const basePath = hasKey ? '' : path;
687
+
688
+ if (newNode.childs) {
689
+ for (let i = 0; i < newNode.childs.length; i++) {
690
+ const child = newNode.childs[i];
691
+ reconcile(child, child, parentDOM, ctx, basePath + ',' + i, keyMap, namespace);
692
+ }
693
+ }
694
+ return newNode._nodes;
695
+ } else {
696
+ return extractNodes(newNode);
697
+ }
698
+ }
699
+
700
+ if (newNode == null) {
701
+ if (oldNode != null) unmountVdom(oldNode);
702
+ return null;
703
+ }
704
+ if (oldNode == null) {
705
+ return mountNode(newNode, parentDOM, ctx, path, keyMap, namespace);
706
+ }
707
+
708
+ const oldIsText = oldNode._text !== undefined;
709
+ const newIsText = newNode._text !== undefined;
710
+
711
+ if (oldIsText && newIsText) {
712
+ if (oldNode._el) {
713
+ oldNode._el.nodeValue = newNode._text;
714
+ newNode._el = oldNode._el;
715
+ return newNode._el;
716
+ }
717
+ return mountNode(newNode, parentDOM, ctx, path, keyMap, namespace);
718
+ }
719
+ if (oldIsText || newIsText) {
720
+ unmountVdom(oldNode);
721
+ return mountNode(newNode, parentDOM, ctx, path, keyMap, namespace);
722
+ }
723
+
724
+ if (Array.isArray(newNode)) {
725
+ if (Array.isArray(oldNode)) {
726
+ return reconcileChildren(oldNode, newNode, parentDOM, ctx, path, keyMap, namespace);
727
+ }
728
+ unmountVdom(oldNode);
729
+ return mountNode(newNode, parentDOM, ctx, path, keyMap, namespace);
730
+ }
731
+ if (Array.isArray(oldNode)) {
732
+ unmountVdom(oldNode);
733
+ return mountNode(newNode, parentDOM, ctx, path, keyMap, namespace);
734
+ }
735
+
736
+ if (oldNode.tag !== newNode.tag) {
737
+ unmountVdom(oldNode);
738
+ return mountNode(newNode, parentDOM, ctx, path, keyMap, namespace);
739
+ }
740
+
741
+ const tag = newNode.tag;
742
+ if (tag === Fragment) {
743
+ return reconcileFragment(oldNode, newNode, parentDOM, ctx, path, keyMap, namespace);
744
+ }
745
+ if (tag === Portal) {
746
+ return reconcilePortal(oldNode, newNode, parentDOM, ctx, path, keyMap, namespace);
747
+ }
748
+ if (typeof tag === 'function' && tag._definition) {
749
+ return reconcileComponent(oldNode, newNode, parentDOM, ctx, path, keyMap, namespace);
750
+ }
751
+ if (typeof tag === 'string') {
752
+ return reconcileHTML(oldNode, newNode, parentDOM, ctx, path, keyMap, namespace);
753
+ }
754
+ return null;
755
+ }
756
+
757
+ function extractNodes(vnode) {
758
+ if (vnode == null) return null;
759
+ if (Array.isArray(vnode)) {
760
+ const r = [];
761
+ for (let i = 0; i < vnode.length; i++) pushAll(r, extractNodes(vnode[i]));
762
+ return r;
763
+ }
764
+ if (vnode._text !== undefined) return vnode._el || null;
765
+ if (vnode._el) return vnode._el;
766
+ if (Array.isArray(vnode._nodes)) return vnode._nodes;
767
+ return null;
768
+ }
769
+
770
+ function reconcileChildren(oldChilds, newChilds, parentDOM, ctx, path, keyMap, namespace) {
771
+ const oldNodes = collectDOMNodes(oldChilds);
772
+ const newNodes = [];
773
+ const max = Math.max(oldChilds ? oldChilds.length : 0, newChilds ? newChilds.length : 0);
774
+ for (let i = 0; i < max; i++) {
775
+ const childPath = path + ',' + i;
776
+ const oc = oldChilds && i < oldChilds.length ? oldChilds[i] : null;
777
+ const nc = newChilds && i < newChilds.length ? newChilds[i] : null;
778
+ const r = reconcile(oc, nc, parentDOM, ctx, childPath, keyMap, namespace);
779
+ pushAll(newNodes, r);
780
+ }
781
+ if (parentDOM) syncDOMChildren(parentDOM, oldNodes, newNodes);
782
+ return newNodes;
783
+ }
784
+
785
+ // ВАЖНО: Для SELECT value применяется ПОСЛЕ добавления options
786
+ function mountHTML(vnode, parentDOM, ctx, path, keyMap, namespace) {
787
+ if (vnode.tag === 'svg') namespace = SVG_NS;
788
+ const isForeignObject = vnode.tag === 'foreignObject';
789
+ const dom = namespace === SVG_NS
790
+ ? document.createElementNS(SVG_NS, vnode.tag)
791
+ : document.createElement(vnode.tag);
792
+ vnode._el = dom;
793
+
794
+ const isSelect = dom.tagName === 'SELECT';
795
+
796
+ // Для SELECT: value применяется после options
797
+ if (isSelect) {
798
+ const propsWithoutValue = {};
799
+ for (const k in vnode.props) {
800
+ if (k !== 'value') propsWithoutValue[k] = vnode.props[k];
801
+ }
802
+ applyProps(dom, {}, propsWithoutValue, namespace);
803
+ } else {
804
+ applyProps(dom, {}, vnode.props, namespace);
805
+ }
806
+
807
+ if (vnode.tag === 'textarea') return dom;
808
+
809
+ const childNodes = [];
810
+ const childNamespace = isForeignObject ? HTML_NS : namespace;
811
+ for (let i = 0; i < vnode.childs.length; i++) {
812
+ const childPath = path + ',' + i;
813
+ const r = mountNode(vnode.childs[i], dom, ctx, childPath, keyMap, childNamespace);
814
+ pushAll(childNodes, r);
815
+ }
816
+ prependAll(dom, childNodes);
817
+
818
+ // Для SELECT: value после options
819
+ if (isSelect && vnode.props && 'value' in vnode.props) {
820
+ applyProp(dom, 'value', vnode.props.value, namespace);
821
+ }
822
+
823
+ return dom;
824
+ }
825
+
826
+ // ВАЖНО: Для SELECT value применяется ПОСЛЕ reconcile options
827
+ function reconcileHTML(oldVnode, newVnode, parentDOM, ctx, path, keyMap, namespace) {
828
+ if (newVnode.tag === 'svg') namespace = SVG_NS;
829
+ const isForeignObject = newVnode.tag === 'foreignObject';
830
+
831
+ let shouldRecreate = false;
832
+ for (const attr of RECREATE_ATTRS) {
833
+ if (oldVnode.props[attr] !== newVnode.props[attr]) {
834
+ shouldRecreate = true;
835
+ break;
836
+ }
837
+ }
838
+ if (oldVnode.tag === 'select' &&
839
+ oldVnode.props.multiple !== newVnode.props.multiple) {
840
+ shouldRecreate = true;
841
+ }
842
+
843
+ if (shouldRecreate) {
844
+ unmountVdom(oldVnode);
845
+ return mountNode(newVnode, parentDOM, ctx, path, keyMap, namespace);
846
+ }
847
+
848
+ const dom = oldVnode._el;
849
+ newVnode._el = dom;
850
+
851
+ const isSelect = dom.tagName === 'SELECT';
852
+
853
+ if (isSelect) {
854
+ const oldPropsWithoutValue = {};
855
+ for (const k in oldVnode.props) {
856
+ if (k !== 'value') oldPropsWithoutValue[k] = oldVnode.props[k];
857
+ }
858
+ const newPropsWithoutValue = {};
859
+ for (const k in newVnode.props) {
860
+ if (k !== 'value') newPropsWithoutValue[k] = newVnode.props[k];
861
+ }
862
+ applyProps(dom, oldPropsWithoutValue, newPropsWithoutValue, namespace);
863
+ } else {
864
+ applyProps(dom, oldVnode.props, newVnode.props, namespace);
865
+ }
866
+
867
+ if (newVnode.tag === 'textarea') {
868
+ newVnode._nodes = [];
869
+ return dom;
870
+ }
871
+
872
+ const childNamespace = isForeignObject ? HTML_NS : namespace;
873
+ newVnode._nodes = reconcileChildren(
874
+ oldVnode.childs, newVnode.childs, dom, ctx, path, keyMap, childNamespace
875
+ );
876
+
877
+ // Для SELECT: value после reconcile options
878
+ if (isSelect && 'value' in newVnode.props) {
879
+ if (oldVnode.props.value !== newVnode.props.value) {
880
+ applyProp(dom, 'value', newVnode.props.value, namespace);
881
+ }
882
+ }
883
+
884
+ return dom;
885
+ }
886
+
887
+ function buildIncomingProps(rawProps, childs) {
888
+ const out = {};
889
+ for (const k in rawProps) {
890
+ if (k !== 'children' && k !== 'key' && k !== 'ref') {
891
+ out[k] = rawProps[k];
892
+ }
893
+ }
894
+ if (rawProps && rawProps.children !== undefined) {
895
+ out.children = rawProps.children;
896
+ } else if (childs && childs.length > 0) {
897
+ const filtered = childs.filter(c => c !== null);
898
+ out.children = filtered.length === 1 ? filtered[0] : filtered;
899
+ } else {
900
+ out.children = null;
901
+ }
902
+ return out;
903
+ }
904
+
905
+ function mountComponent(vnode, parentDOM, ctx, path, keyMap, namespace) {
906
+ const def = vnode.tag._definition;
907
+ const mapKey = makeMapKey(vnode, 0, path);
908
+ let inst = keyMap ? keyMap.get(mapKey) : null;
909
+
910
+ if (inst && inst._definition !== def) {
911
+ unmountVdom({ tag: vnode.tag, _instance: inst });
912
+ if (keyMap) keyMap.delete(mapKey);
913
+ inst = null;
914
+ }
915
+
916
+ if (inst) {
917
+ keyMap.delete(mapKey);
918
+ inst._incomingProps = buildIncomingProps(vnode.props, vnode.childs);
919
+ inst._parentContext = ctx;
920
+ inst._parentDOM = parentDOM;
921
+ inst._namespace = namespace;
922
+ try {
923
+ inst._rerender();
924
+ } catch (err) {
925
+ const name = def.name || 'Component';
926
+ console.error('❌ Error in component "' + name + '":\n', err);
927
+ }
928
+ vnode._instance = inst;
929
+ return inst._nodes;
930
+ }
931
+ inst = new vnode.tag();
932
+ attachInstanceAPI(inst);
933
+ inst._incomingProps = buildIncomingProps(vnode.props, vnode.childs);
934
+ inst._parentContext = ctx;
935
+ inst._parentDOM = parentDOM;
936
+ inst._namespace = namespace;
937
+ vnode._instance = inst;
938
+
939
+ if (def.props) {
940
+ inst.props = def.props.call(inst, inst._incomingProps);
941
+ } else {
942
+ inst.props = inst._incomingProps;
943
+ }
944
+
945
+ inst._isInitializing = true;
946
+ if (def.init) def.init.call(inst, inst.props);
947
+ inst._isInitializing = false;
948
+
949
+ try {
950
+ inst._rerender();
951
+ } catch (err) {
952
+ const name = def.name || 'Component';
953
+ console.error('❌ Error in component "' + name + '":\n', err);
954
+ inst._nodes = [];
955
+ }
956
+ return inst._nodes;
957
+ }
958
+
959
+ function reconcileComponent(oldVnode, newVnode, parentDOM, ctx, path, keyMap, namespace) {
960
+ const inst = oldVnode._instance;
961
+ newVnode._instance = inst;
962
+ inst._incomingProps = buildIncomingProps(newVnode.props, newVnode.childs);
963
+ inst._parentContext = ctx;
964
+ inst._parentDOM = parentDOM;
965
+ inst._namespace = namespace;
966
+ try {
967
+ inst._rerender();
968
+ } catch (err) {
969
+ const name = inst._definition?.name || 'Component';
970
+ console.error('❌ Error in component "' + name + '":\n', err);
971
+ }
972
+ return inst._nodes;
973
+ }
974
+
975
+ function mountFragment(vnode, parentDOM, ctx, path, keyMap, namespace) {
976
+ const hasKey = vnode.props && vnode.props.key !== undefined;
977
+
978
+ if (hasKey && keyMap) {
979
+ const mapKey = makeMapKey(vnode, 0, path);
980
+ const oldVnode = keyMap.get(mapKey);
981
+
982
+ if (oldVnode && oldVnode.tag === Fragment) {
983
+ keyMap.delete(mapKey);
984
+
985
+ const nodes = reconcileChildren(
986
+ oldVnode.childs, vnode.childs, parentDOM, ctx,
987
+ '', keyMap, namespace
988
+ );
989
+
990
+ vnode._nodes = nodes;
991
+ vnode._instance = oldVnode._instance;
992
+ return nodes;
993
+ }
994
+ }
995
+
996
+ const basePath = hasKey ? '' : path;
997
+ const nodes = [];
998
+ for (let i = 0; i < vnode.childs.length; i++) {
999
+ const childPath = basePath + ',' + i;
1000
+ const r = mountNode(vnode.childs[i], parentDOM, ctx, childPath, keyMap, namespace);
1001
+ pushAll(nodes, r);
1002
+ }
1003
+ vnode._nodes = nodes;
1004
+
1005
+ if (hasKey) {
1006
+ vnode._instance = { _isKeyedFragment: true };
1007
+ }
1008
+
1009
+ return nodes;
1010
+ }
1011
+
1012
+ function reconcileFragment(oldVnode, newVnode, parentDOM, ctx, path, keyMap, namespace) {
1013
+ const hasKey = newVnode.props && newVnode.props.key !== undefined;
1014
+ const basePath = hasKey ? '' : path;
1015
+
1016
+ const nodes = reconcileChildren(
1017
+ oldVnode.childs, newVnode.childs, parentDOM, ctx,
1018
+ basePath, keyMap, namespace
1019
+ );
1020
+
1021
+ newVnode._nodes = nodes;
1022
+
1023
+ if (hasKey) {
1024
+ newVnode._instance = oldVnode._instance || { _isKeyedFragment: true };
1025
+ }
1026
+
1027
+ return nodes;
1028
+ }
1029
+
1030
+ function mountPortal(vnode, parentDOM, ctx, path, keyMap, namespace) {
1031
+ const inst = {
1032
+ _isPortal: true,
1033
+ _rendered: null,
1034
+ _nodes: [],
1035
+ _anchor: document.createTextNode(''),
1036
+ _container: null,
1037
+ _mounted: false,
1038
+ _namespace: namespace
1039
+ };
1040
+ vnode._instance = inst;
1041
+ const container = vnode.props.containerGetter();
1042
+ if (container) {
1043
+ inst._container = container;
1044
+ const childNodes = mountNode(vnode.childs, container, ctx, path, keyMap, namespace);
1045
+ inst._rendered = vnode.childs;
1046
+ inst._nodes = Array.isArray(childNodes)
1047
+ ? childNodes
1048
+ : (childNodes ? [childNodes] : []);
1049
+ prependAll(container, inst._nodes);
1050
+ callRefs(vnode.childs);
1051
+ triggerMounted(vnode.childs);
1052
+ inst._mounted = true;
1053
+ }
1054
+ return [inst._anchor];
1055
+ }
1056
+
1057
+ function reconcilePortal(oldVnode, newVnode, parentDOM, ctx, path, keyMap, namespace) {
1058
+ const inst = oldVnode._instance;
1059
+ newVnode._instance = inst;
1060
+ inst._namespace = namespace;
1061
+ const container = newVnode.props.containerGetter();
1062
+
1063
+ if (!inst._container && container) {
1064
+ inst._container = container;
1065
+ const childNodes = mountNode(newVnode.childs, container, ctx, path, keyMap, namespace);
1066
+ inst._rendered = newVnode.childs;
1067
+ inst._nodes = Array.isArray(childNodes)
1068
+ ? childNodes
1069
+ : (childNodes ? [childNodes] : []);
1070
+ prependAll(container, inst._nodes);
1071
+ callRefs(newVnode.childs);
1072
+ triggerMounted(newVnode.childs);
1073
+ inst._mounted = true;
1074
+ } else if (inst._container && !container) {
1075
+ if (inst._rendered) unmountVdom(inst._rendered);
1076
+ for (let i = 0; i < inst._nodes.length; i++) {
1077
+ const n = inst._nodes[i];
1078
+ if (n && n.parentNode) n.parentNode.removeChild(n);
1079
+ }
1080
+ inst._rendered = null;
1081
+ inst._nodes = [];
1082
+ inst._container = null;
1083
+ inst._mounted = false;
1084
+ } else if (inst._container && container) {
1085
+ if (inst._container !== container) {
1086
+ if (inst._rendered) unmountVdom(inst._rendered);
1087
+ for (let i = 0; i < inst._nodes.length; i++) {
1088
+ const n = inst._nodes[i];
1089
+ if (n && n.parentNode) n.parentNode.removeChild(n);
1090
+ }
1091
+ inst._container = container;
1092
+ const childNodes = mountNode(newVnode.childs, container, ctx, path, keyMap, namespace);
1093
+ inst._rendered = newVnode.childs;
1094
+ inst._nodes = Array.isArray(childNodes)
1095
+ ? childNodes
1096
+ : (childNodes ? [childNodes] : []);
1097
+ prependAll(container, inst._nodes);
1098
+ callRefs(newVnode.childs);
1099
+ triggerMounted(newVnode.childs);
1100
+ } else {
1101
+ inst._nodes = reconcileChildren(
1102
+ inst._rendered, newVnode.childs, container, ctx, path, keyMap, namespace
1103
+ );
1104
+ inst._rendered = newVnode.childs;
1105
+ }
1106
+ }
1107
+ return [inst._anchor];
1108
+ }
1109
+
1110
+ function mountNode(vnode, parentDOM, ctx, path, keyMap, namespace) {
1111
+ if (vnode == null) return null;
1112
+ if (vnode._text !== undefined) {
1113
+ const t = document.createTextNode(vnode._text);
1114
+ vnode._el = t;
1115
+ return t;
1116
+ }
1117
+ if (Array.isArray(vnode)) {
1118
+ const nodes = [];
1119
+ for (let i = 0; i < vnode.length; i++) {
1120
+ const childPath = path + ',' + i;
1121
+ const r = mountNode(vnode[i], parentDOM, ctx, childPath, keyMap, namespace);
1122
+ pushAll(nodes, r);
1123
+ }
1124
+ return nodes;
1125
+ }
1126
+ const tag = vnode.tag;
1127
+ if (tag === Fragment) {
1128
+ return mountFragment(vnode, parentDOM, ctx, path, keyMap, namespace);
1129
+ }
1130
+ if (tag === Portal) {
1131
+ return mountPortal(vnode, parentDOM, ctx, path, keyMap, namespace);
1132
+ }
1133
+ if (typeof tag === 'function' && tag._definition) {
1134
+ return mountComponent(vnode, parentDOM, ctx, path, keyMap, namespace);
1135
+ }
1136
+ if (typeof tag === 'string') {
1137
+ return mountHTML(vnode, parentDOM, ctx, path, keyMap, namespace);
1138
+ }
1139
+ return null;
1140
+ }
1141
+
1142
+ function normalizeMountInput(input) {
1143
+ if (input === null || input === undefined) return null;
1144
+ if (typeof input === 'function' && input._definition) return h(input, {});
1145
+ if (Array.isArray(input)) return h(Fragment, {}, ...input);
1146
+ if (typeof input === 'string' || typeof input === 'number') {
1147
+ return { _text: String(input) };
1148
+ }
1149
+ if (typeof input === 'object' && input !== null) return input;
1150
+ throw new Error('mount(): unsupported input type: ' + typeof input);
1151
+ }
1152
+
1153
+ function collectAllInstances(vnode) {
1154
+ const result = [];
1155
+
1156
+ function walk(node) {
1157
+ if (!node) return;
1158
+
1159
+ if (Array.isArray(node)) {
1160
+ for (const child of node) walk(child);
1161
+ return;
1162
+ }
1163
+
1164
+ if (typeof node !== 'object') return;
1165
+
1166
+ if (typeof node.tag === 'function' && node.tag._definition) {
1167
+ if (node._instance && node._instance._rerender) {
1168
+ result.push(node._instance);
1169
+ if (node._instance._vdom) walk(node._instance._vdom);
1170
+ }
1171
+ return;
1172
+ }
1173
+
1174
+ if (node.tag === Portal) {
1175
+ if (node._instance && node._instance._rendered) {
1176
+ walk(node._instance._rendered);
1177
+ }
1178
+ return;
1179
+ }
1180
+
1181
+ if (node.tag === Fragment) {
1182
+ if (Array.isArray(node._nodes)) {
1183
+ for (const child of node._nodes) walk(child);
1184
+ }
1185
+ return;
1186
+ }
1187
+
1188
+ if (node.childs) {
1189
+ for (const child of node.childs) walk(child);
1190
+ }
1191
+ }
1192
+
1193
+ walk(vnode);
1194
+ return result;
1195
+ }
1196
+
1197
+ const mountedTrees = new WeakMap();
1198
+ const mountedContainers = new Set();
1199
+
1200
+ function mount(input, container) {
1201
+ const vnode = normalizeMountInput(input);
1202
+ const oldVnode = mountedTrees.get(container);
1203
+
1204
+ if (vnode === null) {
1205
+ if (oldVnode) {
1206
+ unmountVdom(oldVnode);
1207
+ container.replaceChildren();
1208
+ mountedTrees.delete(container);
1209
+ mountedContainers.delete(container);
1210
+ }
1211
+ return;
1212
+ }
1213
+
1214
+ if (oldVnode) {
1215
+ const keyMap = new Map();
1216
+ populateKeyMap(oldVnode, '', keyMap);
1217
+
1218
+ const oldNodes = collectDOMNodes([oldVnode]);
1219
+ reconcile(oldVnode, vnode, container, null, '', keyMap, HTML_NS);
1220
+ const newNodes = collectDOMNodes([vnode]);
1221
+ syncDOMChildren(container, oldNodes, newNodes);
1222
+ mountedTrees.set(container, vnode);
1223
+ callRefs(vnode);
1224
+ return vnode;
1225
+ }
1226
+
1227
+ const nodes = mountNode(vnode, container, null, '', null, HTML_NS);
1228
+ const flat = Array.isArray(nodes) ? nodes : (nodes ? [nodes] : []);
1229
+ prependAll(container, flat);
1230
+ mountedTrees.set(container, vnode);
1231
+ mountedContainers.add(container);
1232
+ callRefs(vnode);
1233
+ triggerMounted(vnode);
1234
+ return vnode;
1235
+ }
1236
+
1237
+ function refresh() {
1238
+ const start = performance.now();
1239
+
1240
+ for (const container of mountedContainers) {
1241
+ const vnode = mountedTrees.get(container);
1242
+ if (!vnode) continue;
1243
+
1244
+ const instances = collectAllInstances(vnode);
1245
+ for (const inst of instances) {
1246
+ try {
1247
+ inst.update();
1248
+ } catch (err) {
1249
+ console.error('refresh():', inst._definition?.name || 'Component', err);
1250
+ }
1251
+ }
1252
+ }
1253
+
1254
+ return new Promise(resolve => {
1255
+ const finish = () => resolve(performance.now() - start);
1256
+
1257
+ if (batchQueue.size === 0 && !isBatchScheduled) {
1258
+ finish();
1259
+ } else {
1260
+ refreshResolvers.push(finish);
1261
+ }
1262
+ });
1263
+ }
1264
+
1265
+ // Только для тестов — не использовать в продакшене
1266
+ function _cleanupAll() {
1267
+ for (const container of Array.from(mountedContainers)) {
1268
+ try {
1269
+ mount(null, container);
1270
+ } catch (e) {}
1271
+ }
1272
+ }
1273
+
1274
+ export { h, Component, createPortal, Fragment, mount, refresh, _cleanupAll };
1275
+
1276
+ if (typeof window !== 'undefined') {
1277
+ window.VDOM = { h, Component, createPortal, Fragment, mount, refresh, _cleanupAll };
1278
+ }