ilha 0.14.0 → 0.14.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.
@@ -40,6 +40,12 @@ const runInScope = (effect, registry, scope, onOk, onErr, onDone) => {
40
40
  function closeFiber(fiber) {
41
41
  if (fiber.closed) return;
42
42
  fiber.closed = true;
43
+ fiber.trackRestore?.();
44
+ fiber.trackRestore = void 0;
45
+ fiber.renderSub?.();
46
+ fiber.renderSub = void 0;
47
+ for (const slot of fiber.watchSlots ?? []) slot?.dispose();
48
+ fiber.watchSlots = [];
43
49
  for (const h of fiber.holes) h.dispose();
44
50
  fiber.holes = [];
45
51
  Effect.runFork(Scope.close(fiber.scope, Exit.void));
@@ -150,11 +156,124 @@ function isAtomHandle(x) {
150
156
  return typeof x === "function" && x.$$atom === 1;
151
157
  }
152
158
  let trackGet;
159
+ const trackStack = [];
160
+ function beginPrimitiveFrame(fiber) {
161
+ const used = fiber.primitiveI ?? 0;
162
+ const slots = fiber.primitives?.length ?? 0;
163
+ if (used > 0 && used !== slots) console.warn(`ilha: atom/watch slot count changed between renders (${slots} → ${used}); keep primitive order stable`);
164
+ fiber.primitiveI = 0;
165
+ fiber.watchI = 0;
166
+ const islandFrame = fiber.islandFrame ??= {
167
+ i: 0,
168
+ slots: []
169
+ };
170
+ const usedIslands = islandFrame.i;
171
+ if (usedIslands < islandFrame.slots.length) {
172
+ for (let j = usedIslands; j < islandFrame.slots.length; j++) {
173
+ const slot = islandFrame.slots[j];
174
+ if (slot) {
175
+ slot.unmount?.();
176
+ islandFrame.slots[j] = void 0;
177
+ }
178
+ }
179
+ islandFrame.slots.length = usedIslands;
180
+ }
181
+ islandFrame.i = 0;
182
+ }
183
+ function readTracked(atom, registry) {
184
+ const frame = trackStack.at(-1);
185
+ const active = getActiveFiber();
186
+ if (trackGet && frame && active === frame.fiber) return trackGet(atom);
187
+ return registry.get(atom);
188
+ }
189
+ function usePrimitiveSlot(fiber, make) {
190
+ const i = fiber.primitiveI ?? 0;
191
+ fiber.primitiveI = i + 1;
192
+ fiber.primitives ??= [];
193
+ const existing = fiber.primitives[i];
194
+ if (existing) return {
195
+ atom: existing,
196
+ fresh: false
197
+ };
198
+ const a = make();
199
+ fiber.primitives[i] = a;
200
+ return {
201
+ atom: a,
202
+ fresh: true
203
+ };
204
+ }
205
+ function withTrackGetRun(fiber, fn, onOk, onErr) {
206
+ const deps = /* @__PURE__ */ new Set();
207
+ const get = (a) => {
208
+ deps.add(a);
209
+ return fiber.registry.get(a);
210
+ };
211
+ const restore = () => {
212
+ if (fiber.trackRestore !== restore) return;
213
+ const top = trackStack.pop();
214
+ if (top?.get !== get) {
215
+ if (top) trackStack.push(top);
216
+ const i = trackStack.findIndex((frame) => frame.get === get);
217
+ if (i >= 0) trackStack.splice(i, 1);
218
+ }
219
+ trackGet = trackStack.at(-1)?.get;
220
+ fiber.trackRestore = void 0;
221
+ };
222
+ trackStack.push({
223
+ fiber,
224
+ deps,
225
+ get
226
+ });
227
+ trackGet = get;
228
+ fiber.trackRestore = restore;
229
+ try {
230
+ const result = withFiber(fiber, () => fn());
231
+ if (result instanceof Promise) {
232
+ restore();
233
+ result.then((value) => {
234
+ if (fiber.closed) return;
235
+ onOk({
236
+ value,
237
+ deps
238
+ });
239
+ }).catch((e) => {
240
+ if (fiber.closed) return;
241
+ onErr(e);
242
+ });
243
+ return;
244
+ }
245
+ restore();
246
+ onOk({
247
+ value: result,
248
+ deps
249
+ });
250
+ } catch (e) {
251
+ restore();
252
+ onErr(e);
253
+ }
254
+ }
255
+ function subscribeRenderDeps(fiber, deps, run) {
256
+ if (deps.size === 0) return () => {};
257
+ let scheduled = false;
258
+ const schedule = () => {
259
+ if (scheduled) return;
260
+ scheduled = true;
261
+ fiber.runtime.later(() => {
262
+ scheduled = false;
263
+ run();
264
+ });
265
+ };
266
+ const unsubs = [];
267
+ for (const dep of deps) unsubs.push(fiber.registry.subscribe(dep, schedule));
268
+ return () => {
269
+ for (const unsub of unsubs) unsub();
270
+ };
271
+ }
153
272
  function wrapHandle(atom, fiber) {
154
273
  const registry = fiber.registry;
155
274
  const read = (() => {
156
275
  guardHandleFiber(read);
157
- return trackGet ? trackGet(atom) : registry.get(atom);
276
+ return readTracked(atom, registry);
158
277
  });
159
278
  handleOwner.set(read, fiber);
160
279
  Object.defineProperties(read, {
@@ -171,34 +290,40 @@ function wrapHandle(atom, fiber) {
171
290
  });
172
291
  return read;
173
292
  }
174
- function atom(init) {
293
+ function makeAtom(init) {
175
294
  const fiber = getFiber();
176
- const computed = typeof init === "function" && !Atom.isAtom(init) && !Effect.isEffect(init) && !Stream.isStream(init);
177
- let seed = init;
178
295
  const snap = fiber.runtime.hydrateValues;
179
- if (snap && !Atom.isAtom(init) && !Effect.isEffect(init) && !Stream.isStream(init)) {
180
- const v = snap[fiber.runtime.hydrateI++];
181
- if (!computed && v !== void 0) seed = v;
182
- }
183
- let a;
184
- if (Atom.isAtom(seed)) a = seed;
185
- else if (Effect.isEffect(seed) || Stream.isStream(seed)) a = Atom.make(seed);
186
- else if (computed) {
187
- const fn = init;
188
- a = Atom.make((get) => {
189
- const prev = trackGet;
190
- trackGet = (other) => get(other);
191
- try {
192
- return fn();
193
- } finally {
194
- trackGet = prev;
195
- }
196
- });
197
- } else a = Atom.make(seed);
296
+ const { atom: a, fresh } = usePrimitiveSlot(fiber, () => {
297
+ let seed = init;
298
+ if (snap && !Atom.isAtom(init) && !Effect.isEffect(init) && !Stream.isStream(init)) {
299
+ const v = snap[fiber.runtime.hydrateI++];
300
+ if (v !== void 0) seed = v;
301
+ }
302
+ if (Atom.isAtom(seed)) return seed;
303
+ if (Effect.isEffect(seed) || Stream.isStream(seed)) return Atom.make(seed);
304
+ return Atom.make(seed);
305
+ });
306
+ const handle = wrapHandle(a, fiber);
307
+ if (fresh && fiber.runtime.ssr) fiber.runtime.ssrValues.push(fiber.registry.get(a));
308
+ return handle;
309
+ }
310
+ function atomLazy(init) {
311
+ const fiber = getFiber();
312
+ const snap = fiber.runtime.hydrateValues;
313
+ const { atom: a, fresh } = usePrimitiveSlot(fiber, () => {
314
+ let seed = init();
315
+ if (snap) {
316
+ const v = snap[fiber.runtime.hydrateI++];
317
+ if (v !== void 0) seed = v;
318
+ }
319
+ return Atom.make(seed);
320
+ });
198
321
  const handle = wrapHandle(a, fiber);
199
- if (fiber.runtime.ssr) fiber.runtime.ssrValues.push(fiber.registry.get(a));
322
+ if (fresh && fiber.runtime.ssr) fiber.runtime.ssrValues.push(fiber.registry.get(a));
200
323
  return handle;
201
324
  }
325
+ const atom = Object.assign(makeAtom, { lazy: atomLazy });
326
+ const batch = Atom.batch;
202
327
 
203
328
  //#endregion
204
329
  //#region src/errors.ts
@@ -308,12 +433,12 @@ function bindEvents(node, props, fiber) {
308
433
  //#endregion
309
434
  //#region src/morph.ts
310
435
  const PRESERVE_ATTR = "data-morph-preserve";
311
- const KEY_ATTR = "data-ilha-key";
312
- const SLOT_ATTR = "data-ilha-slot";
436
+ const KEY_ATTR$1 = "data-ilha-key";
437
+ const SLOT_ATTR$1 = "data-ilha-slot";
313
438
  function morphKeyOf(el) {
314
- const k = el.getAttribute(KEY_ATTR);
439
+ const k = el.getAttribute(KEY_ATTR$1);
315
440
  if (k !== null) return `k:${k}`;
316
- const s = el.getAttribute(SLOT_ATTR);
441
+ const s = el.getAttribute(SLOT_ATTR$1);
317
442
  return s === null ? null : `s:${s}`;
318
443
  }
319
444
  function shouldPreserveMorphAttr(el, name) {
@@ -396,17 +521,17 @@ function morphChildren(fromParent, toParent) {
396
521
  if (fromNode instanceof Element) {
397
522
  const fromKey = morphKeyOf(fromNode);
398
523
  if (fromKey !== null && fromKey !== toKey && toKeys.has(fromKey)) {
399
- fromParent.insertBefore(toNode.cloneNode(true), fromNode);
524
+ fromParent.insertBefore(toNode, fromNode);
400
525
  continue;
401
526
  }
402
527
  }
403
528
  }
404
529
  if (!fromNode) {
405
- fromParent.appendChild(toNode.cloneNode(true));
530
+ fromParent.appendChild(toNode);
406
531
  continue;
407
532
  }
408
533
  if (fromNode.nodeType !== toNode.nodeType) {
409
- fromParent.replaceChild(toNode.cloneNode(true), fromNode);
534
+ fromParent.replaceChild(toNode, fromNode);
410
535
  continue;
411
536
  }
412
537
  if (fromNode.nodeType === 3 || fromNode.nodeType === 8) {
@@ -417,10 +542,10 @@ function morphChildren(fromParent, toParent) {
417
542
  const fromEl = fromNode;
418
543
  const toEl = toNode;
419
544
  if (fromEl.localName !== toEl.localName) {
420
- fromParent.replaceChild(toEl.cloneNode(true), fromEl);
545
+ fromParent.replaceChild(toEl, fromEl);
421
546
  continue;
422
547
  }
423
- const slotId = toEl.getAttribute(SLOT_ATTR);
548
+ const slotId = toEl.getAttribute(SLOT_ATTR$1);
424
549
  if (slotId !== null && fromEl.getAttribute("data-ilha-slot") === slotId) continue;
425
550
  if (fromEl.localName === "input") {
426
551
  const hadChecked = fromEl.hasAttribute("checked");
@@ -552,19 +677,39 @@ function finish(fiber, view) {
552
677
  fiber.park();
553
678
  }
554
679
  function runSetup(fiber, fn) {
555
- try {
556
- const out = withFiber(fiber, () => fn());
557
- if (isGen(out)) interpret((() => out), fiber);
558
- else {
559
- fiber.runtime.begin();
560
- Promise.resolve(out).then((view) => {
561
- withFiber(fiber, () => finish(fiber, view));
562
- fiber.runtime.end();
563
- }, (e) => {
680
+ const disposeRenderSub = () => {
681
+ fiber.renderSub?.();
682
+ fiber.renderSub = void 0;
683
+ };
684
+ const commit = (out, deps) => {
685
+ if (isGen(out)) {
686
+ disposeRenderSub();
687
+ interpret((() => out), fiber);
688
+ return;
689
+ }
690
+ disposeRenderSub();
691
+ if (!fiber.runtime.ssr) fiber.renderSub = subscribeRenderDeps(fiber, deps, render);
692
+ withFiber(fiber, () => finish(fiber, out));
693
+ };
694
+ const render = () => {
695
+ if (fiber.closed) return;
696
+ fiber.runtime.begin();
697
+ beginPrimitiveFrame(fiber);
698
+ withTrackGetRun(fiber, () => fn(), ({ value: out, deps }) => {
699
+ try {
700
+ commit(out, deps);
701
+ } catch (e) {
564
702
  withFiber(fiber, () => fiber.fail(asFailure(e)));
703
+ } finally {
565
704
  fiber.runtime.end();
566
- });
567
- }
705
+ }
706
+ }, (e) => {
707
+ withFiber(fiber, () => fiber.fail(asFailure(e)));
708
+ fiber.runtime.end();
709
+ });
710
+ };
711
+ try {
712
+ render();
568
713
  } catch (e) {
569
714
  fiber.fail(asFailure(e));
570
715
  }
@@ -572,20 +717,27 @@ function runSetup(fiber, fn) {
572
717
 
573
718
  //#endregion
574
719
  //#region src/paint.ts
575
- const ISLAND = Symbol.for("ilha.island");
576
- const ISLAND_MOUNT_INTERNAL = Symbol.for("ilha.islandMountInternal");
577
- const ISLAND_SLOT_TAG = Symbol.for("ilha.islandSlotTag");
578
- function insert(fiber, nodes) {
720
+ const ISLAND$1 = Symbol.for("ilha.island");
721
+ const ISLAND_MOUNT_INTERNAL$1 = Symbol.for("ilha.islandMountInternal");
722
+ const ISLAND_SLOT_TAG$1 = Symbol.for("ilha.islandSlotTag");
723
+ function insert$1(fiber, nodes) {
579
724
  for (const n of nodes) fiber.root.appendChild(n);
580
725
  }
581
- function disposeHoles(fiber) {
582
- for (const h of fiber.holes) h.dispose();
583
- fiber.holes = [];
726
+ function disposeHoles$1(fiber, opts) {
727
+ const keep = [];
728
+ for (const h of fiber.holes) {
729
+ if (opts?.morph && h.keepOnMorph) {
730
+ keep.push(h);
731
+ continue;
732
+ }
733
+ h.dispose();
734
+ }
735
+ fiber.holes = keep;
584
736
  }
585
- function skip(x) {
737
+ function skip$1(x) {
586
738
  return x === null || x === void 0 || typeof x === "boolean";
587
739
  }
588
- function applyProps(el, props, fiber) {
740
+ function applyProps$1(el, props, fiber) {
589
741
  for (const [k, v] of Object.entries(props)) {
590
742
  if (k === "children" || k === "key" || k === "ref") continue;
591
743
  if (k === "className") {
@@ -640,10 +792,10 @@ function applyProps(el, props, fiber) {
640
792
  fiber.holes.push({ dispose: () => ref(null) });
641
793
  }
642
794
  }
643
- const KEEP = Symbol("keep");
644
- function unwrap(value, keep) {
795
+ const KEEP$1 = Symbol("keep");
796
+ function unwrap$1(value, keep) {
645
797
  if (AsyncResult.isAsyncResult(value)) {
646
- if (AsyncResult.isWaiting(value) && keep !== void 0) return KEEP;
798
+ if (AsyncResult.isWaiting(value) && keep !== void 0) return KEEP$1;
647
799
  if (AsyncResult.isFailure(value) && !AsyncResult.isWaiting(value)) return String(AsyncResult.error(value) ?? "error");
648
800
  const v = AsyncResult.value(value);
649
801
  if (v._tag === "Some") return v.value;
@@ -651,18 +803,23 @@ function unwrap(value, keep) {
651
803
  }
652
804
  return value;
653
805
  }
654
- function paintHole(fiber, view) {
655
- if (view === KEEP) return;
806
+ function paintHole$1(fiber, view) {
807
+ if (view === KEEP$1) return;
808
+ const frame = fiber.islandFrame ??= {
809
+ i: 0,
810
+ slots: []
811
+ };
812
+ frame.i = 0;
656
813
  const list = Array.isArray(view) ? view : null;
657
814
  if (list && list.length > 0 && list.every((v) => isVNode(v) && v.key != null)) {
658
- keyedPaintHole(fiber, list);
815
+ keyedPaintHole$1(fiber, list);
659
816
  return;
660
817
  }
661
- disposeHoles(fiber);
818
+ disposeHoles$1(fiber);
662
819
  if (fiber.root instanceof Element) fiber.root.replaceChildren();
663
- insert(fiber, materialize(view, fiber));
820
+ insert$1(fiber, materialize$1(view, fiber));
664
821
  }
665
- function keyedPaintHole(fiber, views) {
822
+ function keyedPaintHole$1(fiber, views) {
666
823
  const prev = fiber.keyedHoles ?? /* @__PURE__ */ new Map();
667
824
  const keep = new Set(views.map((v) => String(v.key)));
668
825
  for (const [k, h] of prev) if (!keep.has(k)) closeFiber(h);
@@ -676,34 +833,34 @@ function keyedPaintHole(fiber, views) {
676
833
  nodes.push(reuse.root);
677
834
  continue;
678
835
  }
679
- const made = materialize(v, fiber);
836
+ const made = materialize$1(v, fiber);
680
837
  const hole = fiber.keyedHoles?.get(k);
681
838
  if (hole) next.set(k, hole);
682
839
  nodes.push(...made);
683
840
  }
684
841
  fiber.keyedHoles = next;
685
842
  if (fiber.root instanceof Element) fiber.root.replaceChildren();
686
- insert(fiber, nodes);
843
+ insert$1(fiber, nodes);
687
844
  }
688
- function openHole(parent) {
845
+ function openHole$1(parent) {
689
846
  const id = parent.runtime.nextHole();
690
847
  const host = document.createElement("span");
691
- host.setAttribute(SLOT_ATTR, String(id));
848
+ host.setAttribute(SLOT_ATTR$1, String(id));
692
849
  host.style.display = "contents";
693
850
  const child = makeFiber(parent.runtime, host, (f, v) => {
694
- disposeHoles(f);
851
+ disposeHoles$1(f);
695
852
  if (f.root instanceof Element) f.root.replaceChildren();
696
- insert(f, materialize(v, f));
853
+ insert$1(f, materialize$1(v, f));
697
854
  }, { onFail: (e) => {
698
855
  console.error(e);
699
- paintHole(child, errorView(e));
856
+ paintHole$1(child, errorView$1(e));
700
857
  } });
701
858
  return {
702
859
  fiber: child,
703
860
  nodes: [host]
704
861
  };
705
862
  }
706
- function errorView(e) {
863
+ function errorView$1(e) {
707
864
  return {
708
865
  $$ilha: 1,
709
866
  type: "pre",
@@ -711,67 +868,133 @@ function errorView(e) {
711
868
  children: [failureMessage(e)]
712
869
  };
713
870
  }
714
- function trackHole(parent, hole, extra) {
715
- parent.holes.push({ dispose() {
716
- extra?.();
717
- closeFiber(hole);
718
- } });
871
+ function findReusableAtomHost$1(fiber, atom) {
872
+ for (const h of fiber.holes) if (h.atom === atom && h.host?.isConnected && h.holeFiber && !h.holeFiber.closed) return {
873
+ host: h.host,
874
+ hole: h.holeFiber
875
+ };
719
876
  }
720
- function materialize(view, fiber) {
721
- if (skip(view)) return [];
877
+ function refreshAtomHost(fiber, atom, host, hole) {
878
+ if (hole.closed || !host.isConnected) return;
879
+ const next = unwrap$1(fiber.registry.get(atom), void 0);
880
+ if (next === KEEP$1) return;
881
+ paintHole$1(hole, next);
882
+ }
883
+ function refreshAllAtomHosts(fiber) {
884
+ for (const h of fiber.holes) if (h.atom && h.host && h.holeFiber) refreshAtomHost(fiber, h.atom, h.host, h.holeFiber);
885
+ }
886
+ function pruneDisconnectedAtomHoles(fiber) {
887
+ fiber.holes = fiber.holes.filter((h) => {
888
+ if (!h.atom || !h.host || !h.holeFiber) return true;
889
+ if (h.host.isConnected) return true;
890
+ h.dispose();
891
+ return false;
892
+ });
893
+ }
894
+ function atomHostPlaceholder$1(host) {
895
+ const placeholder = document.createElement("span");
896
+ const slot = host.getAttribute(SLOT_ATTR$1);
897
+ if (slot !== null) placeholder.setAttribute(SLOT_ATTR$1, slot);
898
+ placeholder.style.display = "contents";
899
+ return placeholder;
900
+ }
901
+ function trackHole$1(parent, hole, extra, meta) {
902
+ parent.holes.push({
903
+ keepOnMorph: !!(meta?.atom && meta?.host) || !!meta?.keyed,
904
+ atom: meta?.atom,
905
+ host: meta?.host,
906
+ holeFiber: hole,
907
+ dispose() {
908
+ extra?.();
909
+ closeFiber(hole);
910
+ }
911
+ });
912
+ }
913
+ function materialize$1(view, fiber) {
914
+ if (skip$1(view)) return [];
722
915
  if (typeof view === "string" || typeof view === "number" || typeof view === "bigint") return [document.createTextNode(String(view))];
723
916
  if (isAtomHandle(view)) {
724
- const { fiber: hole, nodes } = openHole(fiber);
917
+ const reused = findReusableAtomHost$1(fiber, view.atom);
918
+ if (reused) return [atomHostPlaceholder$1(reused.host)];
919
+ const { fiber: hole, nodes } = openHole$1(fiber);
920
+ const host = nodes[0];
725
921
  let unsub = () => {};
726
922
  fiber.runtime.later(() => {
727
923
  if (hole.closed) return;
728
924
  let keep;
729
925
  unsub = fiber.registry.subscribe(view.atom, (v) => {
730
926
  if (hole.closed) return;
731
- const next = unwrap(v, keep);
732
- if (next === KEEP) return;
927
+ const next = unwrap$1(v, keep);
928
+ if (next === KEEP$1) return;
733
929
  keep = next;
734
- paintHole(hole, next);
930
+ paintHole$1(hole, next);
735
931
  }, { immediate: true });
736
932
  });
737
- trackHole(fiber, hole, () => unsub());
933
+ trackHole$1(fiber, hole, () => unsub(), {
934
+ atom: view.atom,
935
+ host
936
+ });
738
937
  return nodes;
739
938
  }
740
939
  if (Stream.isStream(view)) {
741
- const { fiber: hole, nodes } = openHole(fiber);
940
+ const { fiber: hole, nodes } = openHole$1(fiber);
742
941
  const src = fiber.runtime.ssr ? view.pipe(Stream.take(1)) : view;
743
942
  hole.run(Stream.runForEach(src, (v) => Effect.sync(() => {
744
- if (!hole.closed) paintHole(hole, v);
943
+ if (!hole.closed) paintHole$1(hole, v);
745
944
  })).pipe(Effect.catch((e) => Effect.sync(() => {
746
- if (!hole.closed) paintHole(hole, String(e));
945
+ if (!hole.closed) paintHole$1(hole, String(e));
747
946
  }))), fiber.runtime.ssr ? () => {} : void 0);
748
- trackHole(fiber, hole);
947
+ trackHole$1(fiber, hole);
749
948
  return nodes;
750
949
  }
751
950
  if (isSetupFn(view)) {
752
- const { fiber: hole, nodes } = openHole(fiber);
951
+ const { fiber: hole, nodes } = openHole$1(fiber);
753
952
  fiber.runtime.later(() => {
754
953
  if (!hole.closed) runSetup(hole, view);
755
954
  });
756
- trackHole(fiber, hole);
955
+ trackHole$1(fiber, hole);
757
956
  return nodes;
758
957
  }
759
958
  if (isVNode(view)) {
760
- if (view.type === Fragment) return view.children.flatMap((c) => materialize(c, fiber));
959
+ if (view.type === Fragment) return view.children.flatMap((c) => materialize$1(c, fiber));
761
960
  if (typeof view.type === "function") {
762
961
  const props = {
763
962
  ...view.props,
764
963
  children: view.children
765
964
  };
766
965
  const type = view.type;
767
- if (type[ISLAND] === true) {
768
- const tag = typeof type[ISLAND_SLOT_TAG] === "string" ? type[ISLAND_SLOT_TAG] : "div";
966
+ if (type[ISLAND$1] === true) {
967
+ const frame = fiber.islandFrame ??= {
968
+ i: 0,
969
+ slots: []
970
+ };
971
+ const i = frame.i++;
972
+ const existing = frame.slots[i];
973
+ if (existing && existing.type === type && existing.host.isConnected && typeof existing.updateProps === "function") {
974
+ existing.updateProps(props);
975
+ return [existing.host];
976
+ }
977
+ existing?.unmount?.();
978
+ const tag = typeof type[ISLAND_SLOT_TAG$1] === "string" ? type[ISLAND_SLOT_TAG$1] : "div";
769
979
  const el = document.createElement(tag);
770
980
  el.setAttribute("data-ilha", "");
771
- const mountFn = type[ISLAND_MOUNT_INTERNAL];
981
+ const mountFn = type[ISLAND_MOUNT_INTERNAL$1];
772
982
  if (typeof mountFn === "function") {
773
983
  const handle = mountFn(el, props);
774
- fiber.holes.push({ dispose: () => handle?.unmount?.() });
984
+ const slot = {
985
+ type,
986
+ host: el,
987
+ updateProps: handle?.updateProps,
988
+ unmount: handle?.unmount
989
+ };
990
+ frame.slots[i] = slot;
991
+ fiber.holes.push({
992
+ keepOnMorph: true,
993
+ dispose: () => {
994
+ handle?.unmount?.();
995
+ if (frame.slots[i] === slot) frame.slots[i] = void 0;
996
+ }
997
+ });
775
998
  }
776
999
  return [el];
777
1000
  }
@@ -783,7 +1006,7 @@ function materialize(view, fiber) {
783
1006
  if (next && typeof next.then !== "function" && !isSetupFn(next)) reuse.paint(next);
784
1007
  return [reuse.root];
785
1008
  }
786
- const { fiber: hole, nodes } = openHole(fiber);
1009
+ const { fiber: hole, nodes } = openHole$1(fiber);
787
1010
  hole.propsBox = { current: props };
788
1011
  if (k) {
789
1012
  fiber.keyedHoles ??= /* @__PURE__ */ new Map();
@@ -792,12 +1015,12 @@ function materialize(view, fiber) {
792
1015
  fiber.runtime.later(() => {
793
1016
  if (!hole.closed) runSetup(hole, () => type(hole.propsBox.current));
794
1017
  });
795
- trackHole(fiber, hole);
1018
+ trackHole$1(fiber, hole, void 0, k ? { keyed: true } : void 0);
796
1019
  return nodes;
797
1020
  }
798
1021
  const el = document.createElement(view.type);
799
- if (view.key != null) el.setAttribute(KEY_ATTR, String(view.key));
800
- applyProps(el, view.props, fiber);
1022
+ if (view.key != null) el.setAttribute(KEY_ATTR$1, String(view.key));
1023
+ applyProps$1(el, view.props, fiber);
801
1024
  fiber.keyedHoles ??= /* @__PURE__ */ new Map();
802
1025
  const childFiber = {
803
1026
  ...fiber,
@@ -805,10 +1028,10 @@ function materialize(view, fiber) {
805
1028
  holes: fiber.holes,
806
1029
  keyedHoles: fiber.keyedHoles
807
1030
  };
808
- for (const c of view.children) for (const n of materialize(c, childFiber)) el.appendChild(n);
1031
+ for (const c of view.children) for (const n of materialize$1(c, childFiber)) el.appendChild(n);
809
1032
  return [el];
810
1033
  }
811
- if (Array.isArray(view) || view && typeof view === "object" && Symbol.iterator in Object(view)) return [...view].flatMap((c) => materialize(c, fiber));
1034
+ if (Array.isArray(view) || view && typeof view === "object" && Symbol.iterator in Object(view)) return [...view].flatMap((c) => materialize$1(c, fiber));
812
1035
  return [document.createTextNode(String(view))];
813
1036
  }
814
1037
  function canHydrate(fiber, view) {
@@ -816,20 +1039,24 @@ function canHydrate(fiber, view) {
816
1039
  const el = fiber.root.firstElementChild;
817
1040
  return !!el && el.tagName === view.type.toUpperCase();
818
1041
  }
819
- function paint(fiber, view) {
1042
+ function paint$1(fiber, view) {
1043
+ fiber.islandFrame ??= {
1044
+ i: 0,
1045
+ slots: []
1046
+ };
820
1047
  if (fiber.hydrate) {
821
1048
  if (canHydrate(fiber, view)) {
822
1049
  const el = fiber.root.firstElementChild;
823
1050
  fiber.hydrate = false;
824
1051
  fiber.liveEl = el;
825
- applyProps(el, view.props, fiber);
1052
+ applyProps$1(el, view.props, fiber);
826
1053
  const childFiber = {
827
1054
  ...fiber,
828
1055
  root: el,
829
1056
  holes: fiber.holes
830
1057
  };
831
1058
  el.replaceChildren();
832
- for (const c of view.children) for (const n of materialize(c, childFiber)) el.appendChild(n);
1059
+ for (const c of view.children) for (const n of materialize$1(c, childFiber)) el.appendChild(n);
833
1060
  return;
834
1061
  } else {
835
1062
  console.warn("[ilha] hydrate mismatch, full mount");
@@ -838,28 +1065,31 @@ function paint(fiber, view) {
838
1065
  }
839
1066
  }
840
1067
  if (fiber.liveEl && isVNode(view) && typeof view.type === "string" && fiber.liveEl.localName === view.type) {
841
- applyProps(fiber.liveEl, view.props, fiber);
1068
+ disposeHoles$1(fiber, { morph: true });
1069
+ applyProps$1(fiber.liveEl, view.props, fiber);
842
1070
  const tmp = document.createElement(fiber.liveEl.localName);
843
- for (const c of view.children) for (const n of materialize(c, fiber)) tmp.appendChild(n);
1071
+ for (const c of view.children) for (const n of materialize$1(c, fiber)) tmp.appendChild(n);
844
1072
  morphInner(fiber.liveEl, tmp);
1073
+ refreshAllAtomHosts(fiber);
1074
+ pruneDisconnectedAtomHoles(fiber);
845
1075
  return;
846
1076
  }
847
- disposeHoles(fiber);
1077
+ disposeHoles$1(fiber);
848
1078
  if (fiber.root instanceof Element && fiber.root.childNodes.length > 0) {
849
1079
  const tmp = document.createElement("div");
850
- for (const n of materialize(view, fiber)) tmp.appendChild(n);
1080
+ for (const n of materialize$1(view, fiber)) tmp.appendChild(n);
851
1081
  morphInner(fiber.root, tmp);
852
1082
  const first = fiber.root.firstElementChild;
853
1083
  if (first) fiber.liveEl = first;
854
1084
  return;
855
1085
  }
856
- insert(fiber, materialize(view, fiber));
1086
+ insert$1(fiber, materialize$1(view, fiber));
857
1087
  const first = fiber.root instanceof Element ? fiber.root.firstElementChild : null;
858
1088
  if (first) fiber.liveEl = first;
859
1089
  }
860
- function paintError(fiber, e) {
1090
+ function paintError$1(fiber, e) {
861
1091
  console.error(e);
862
- paint(fiber, errorView(e));
1092
+ paint$1(fiber, errorView$1(e));
863
1093
  }
864
1094
 
865
1095
  //#endregion
@@ -912,10 +1142,466 @@ function decodeSnapshot(raw) {
912
1142
  }
913
1143
 
914
1144
  //#endregion
915
- //#region src/mount.ts
1145
+ //#region src/ssr-dom.ts
1146
+ const VOID = /* @__PURE__ */ new Set([
1147
+ "area",
1148
+ "base",
1149
+ "br",
1150
+ "col",
1151
+ "embed",
1152
+ "hr",
1153
+ "img",
1154
+ "input",
1155
+ "link",
1156
+ "meta",
1157
+ "param",
1158
+ "source",
1159
+ "track",
1160
+ "wbr"
1161
+ ]);
1162
+ /** Tags whose children are raw text — do not HTML-escape content. */
1163
+ const RAW_TEXT = /* @__PURE__ */ new Set(["script", "style"]);
1164
+ function escapeText(s) {
1165
+ return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
1166
+ }
916
1167
  function escapeAttr(s) {
917
- return s.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
1168
+ return escapeText(s).replace(/"/g, "&quot;");
1169
+ }
1170
+ function createSsrText(text) {
1171
+ return {
1172
+ kind: "text",
1173
+ text
1174
+ };
1175
+ }
1176
+ function createSsrElement(tag) {
1177
+ const lower = tag.toLowerCase();
1178
+ const children = [];
1179
+ const el = {
1180
+ kind: "element",
1181
+ tag: lower,
1182
+ attrs: /* @__PURE__ */ new Map(),
1183
+ children,
1184
+ isConnected: true,
1185
+ tagName: lower.toUpperCase(),
1186
+ localName: lower,
1187
+ setAttribute(name, value) {
1188
+ el.attrs.set(name, value);
1189
+ },
1190
+ getAttribute(name) {
1191
+ return el.attrs.has(name) ? el.attrs.get(name) : null;
1192
+ },
1193
+ removeAttribute(name) {
1194
+ el.attrs.delete(name);
1195
+ },
1196
+ hasAttribute(name) {
1197
+ return el.attrs.has(name);
1198
+ },
1199
+ replaceChildren(...nodes) {
1200
+ children.length = 0;
1201
+ children.push(...nodes);
1202
+ },
1203
+ appendChild(node) {
1204
+ children.push(node);
1205
+ },
1206
+ get textContent() {
1207
+ return children.map((n) => n.kind === "text" ? n.text : n.textContent).join("");
1208
+ },
1209
+ set textContent(value) {
1210
+ children.length = 0;
1211
+ children.push(createSsrText(value));
1212
+ }
1213
+ };
1214
+ return el;
1215
+ }
1216
+ function createSsrRoot() {
1217
+ const children = [];
1218
+ return {
1219
+ kind: "root",
1220
+ children,
1221
+ appendChild(node) {
1222
+ children.push(node);
1223
+ },
1224
+ replaceChildren() {
1225
+ children.length = 0;
1226
+ },
1227
+ get innerHTML() {
1228
+ return children.map(serializeNode).join("");
1229
+ }
1230
+ };
1231
+ }
1232
+ function serializeRawText(tag, text) {
1233
+ if (new RegExp(`</${tag}\\b`, "i").test(text)) throw new Error(`ilha: unsafe raw text in <${tag}> (contains "</${tag}")`);
1234
+ return text;
1235
+ }
1236
+ function serializeNode(node) {
1237
+ if (node.kind === "text") return escapeText(node.text);
1238
+ const parts = [];
1239
+ for (const [name, value] of node.attrs) parts.push(`${name}="${escapeAttr(value)}"`);
1240
+ const attr = parts.length > 0 ? ` ${parts.join(" ")}` : "";
1241
+ const tag = node.tag;
1242
+ if (VOID.has(tag)) return `<${tag}${attr}>`;
1243
+ if (RAW_TEXT.has(tag)) return `<${tag}${attr}>${serializeRawText(tag, node.textContent)}</${tag}>`;
1244
+ return `<${tag}${attr}>${node.children.map(serializeNode).join("")}</${tag}>`;
1245
+ }
1246
+
1247
+ //#endregion
1248
+ //#region src/ssr-paint.ts
1249
+ const KEY_ATTR = "data-ilha-key";
1250
+ const SLOT_ATTR = "data-ilha-slot";
1251
+ const ISLAND = Symbol.for("ilha.island");
1252
+ const ISLAND_MOUNT_INTERNAL = Symbol.for("ilha.islandMountInternal");
1253
+ const ISLAND_SLOT_TAG = Symbol.for("ilha.islandSlotTag");
1254
+ function insert(fiber, nodes) {
1255
+ const root = fiber.root;
1256
+ for (const n of nodes) root.appendChild(n);
1257
+ }
1258
+ function disposeHoles(fiber) {
1259
+ for (const h of fiber.holes) h.dispose();
1260
+ fiber.holes = [];
1261
+ }
1262
+ function skip(x) {
1263
+ return x === null || x === void 0 || typeof x === "boolean";
1264
+ }
1265
+ function bindSsrEvents(el, props, fiber) {
1266
+ const seen = /* @__PURE__ */ new Set();
1267
+ for (const key of Object.keys(props)) {
1268
+ const type = eventTypeFromProp(key);
1269
+ if (!type || seen.has(type)) continue;
1270
+ seen.add(type);
1271
+ const fn = props[key];
1272
+ if (typeof fn !== "function") continue;
1273
+ const branded = fn[Symbol.for("ilha.actionCall")];
1274
+ const rec = branded?.k ? {
1275
+ k: branded.k,
1276
+ a: branded.a ?? []
1277
+ } : void 0;
1278
+ if (!rec) continue;
1279
+ const id = `${type}:${fiber.runtime.ssrEventI++}`;
1280
+ fiber.runtime.ssrActions[id] = rec;
1281
+ const existing = el.getAttribute("data-ilha-on");
1282
+ el.setAttribute("data-ilha-on", existing ? `${existing},${id}` : id);
1283
+ }
1284
+ }
1285
+ function applyProps(el, props, fiber) {
1286
+ for (const [k, v] of Object.entries(props)) {
1287
+ if (k === "children" || k === "key" || k === "ref") continue;
1288
+ if (k === "className") {
1289
+ el.setAttribute("class", String(v ?? ""));
1290
+ continue;
1291
+ }
1292
+ if (k === "htmlFor") {
1293
+ el.setAttribute("for", String(v ?? ""));
1294
+ continue;
1295
+ }
1296
+ if (k.toLowerCase() === "srcdoc") continue;
1297
+ if (k === "style" && v != null && v !== false) {
1298
+ const css = typeof v === "string" || typeof v === "object" ? serializeStyleAttr(v) : "";
1299
+ if (css) el.setAttribute("style", css);
1300
+ continue;
1301
+ }
1302
+ if (isEventProp(k)) continue;
1303
+ if (/^on[A-Z]/.test(k)) continue;
1304
+ if (k === "value" || k === "checked" || k === "selected") {
1305
+ const setProp = (x) => {
1306
+ if (k === "value") el.setAttribute("value", String(x ?? ""));
1307
+ else if (k === "checked") {
1308
+ if (x) el.setAttribute("checked", "");
1309
+ else el.removeAttribute("checked");
1310
+ } else if (x) el.setAttribute("selected", "");
1311
+ else el.removeAttribute("selected");
1312
+ };
1313
+ if (isAtomHandle(v)) {
1314
+ const unsub = fiber.registry.subscribe(v.atom, setProp, { immediate: true });
1315
+ fiber.holes.push({ dispose: unsub });
1316
+ } else setProp(v);
1317
+ continue;
1318
+ }
1319
+ if (typeof v === "function") continue;
1320
+ if (v === false || v === null || v === void 0) el.removeAttribute(k);
1321
+ else {
1322
+ const str = v === true ? "" : String(v);
1323
+ if (isUrlAttributeName(k) && !isSafeUrlAttrValue(el.tagName, k, str)) continue;
1324
+ el.setAttribute(k, str);
1325
+ }
1326
+ }
1327
+ bindSsrEvents(el, props, fiber);
1328
+ const ref = props.ref;
1329
+ if (typeof ref === "function") {
1330
+ ref(el);
1331
+ fiber.holes.push({ dispose: () => ref(null) });
1332
+ }
1333
+ }
1334
+ const KEEP = Symbol("keep");
1335
+ function unwrap(value, keep) {
1336
+ if (AsyncResult.isAsyncResult(value)) {
1337
+ if (AsyncResult.isWaiting(value) && keep !== void 0) return KEEP;
1338
+ if (AsyncResult.isFailure(value) && !AsyncResult.isWaiting(value)) return String(AsyncResult.error(value) ?? "error");
1339
+ const v = AsyncResult.value(value);
1340
+ if (v._tag === "Some") return v.value;
1341
+ return "";
1342
+ }
1343
+ return value;
1344
+ }
1345
+ function paintHole(fiber, view) {
1346
+ if (view === KEEP) return;
1347
+ const frame = fiber.islandFrame ??= {
1348
+ i: 0,
1349
+ slots: []
1350
+ };
1351
+ frame.i = 0;
1352
+ const list = Array.isArray(view) ? view : null;
1353
+ if (list && list.length > 0 && list.every((v) => isVNode(v) && v.key != null)) {
1354
+ keyedPaintHole(fiber, list);
1355
+ return;
1356
+ }
1357
+ disposeHoles(fiber);
1358
+ fiber.root.replaceChildren();
1359
+ insert(fiber, materialize(view, fiber));
1360
+ }
1361
+ function keyedPaintHole(fiber, views) {
1362
+ const prev = fiber.keyedHoles ?? /* @__PURE__ */ new Map();
1363
+ const keep = new Set(views.map((v) => String(v.key)));
1364
+ for (const [k, h] of prev) if (!keep.has(k)) closeFiber(h);
1365
+ const next = /* @__PURE__ */ new Map();
1366
+ const nodes = [];
1367
+ for (const v of views) {
1368
+ const k = String(v.key);
1369
+ const reuse = prev.get(k);
1370
+ if (reuse && !reuse.closed) {
1371
+ next.set(k, reuse);
1372
+ nodes.push(reuse.root);
1373
+ continue;
1374
+ }
1375
+ const made = materialize(v, fiber);
1376
+ const hole = fiber.keyedHoles?.get(k);
1377
+ if (hole) next.set(k, hole);
1378
+ nodes.push(...made);
1379
+ }
1380
+ fiber.keyedHoles = next;
1381
+ fiber.root.replaceChildren();
1382
+ insert(fiber, nodes);
1383
+ }
1384
+ function openHole(parent) {
1385
+ const id = parent.runtime.nextHole();
1386
+ const host = createSsrElement("span");
1387
+ host.setAttribute(SLOT_ATTR, String(id));
1388
+ host.setAttribute("style", "display:contents");
1389
+ const child = makeFiber(parent.runtime, host, paint, { onFail: (e) => {
1390
+ console.error(e);
1391
+ paintHole(child, errorView(e));
1392
+ } });
1393
+ return {
1394
+ fiber: child,
1395
+ nodes: [host]
1396
+ };
1397
+ }
1398
+ function errorView(e) {
1399
+ return {
1400
+ $$ilha: 1,
1401
+ type: "pre",
1402
+ props: { "data-ilha-error": "" },
1403
+ children: [failureMessage(e)]
1404
+ };
918
1405
  }
1406
+ function findReusableAtomHost(fiber, atom) {
1407
+ for (const h of fiber.holes) {
1408
+ const host = h.host;
1409
+ if (h.atom === atom && host?.isConnected && h.holeFiber && !h.holeFiber.closed) return {
1410
+ host,
1411
+ hole: h.holeFiber
1412
+ };
1413
+ }
1414
+ }
1415
+ function atomHostPlaceholder(host) {
1416
+ const placeholder = createSsrElement("span");
1417
+ const slot = host.getAttribute(SLOT_ATTR);
1418
+ if (slot !== null) placeholder.setAttribute(SLOT_ATTR, slot);
1419
+ placeholder.setAttribute("style", "display:contents");
1420
+ return placeholder;
1421
+ }
1422
+ function trackHole(parent, hole, extra, meta) {
1423
+ parent.holes.push({
1424
+ keepOnMorph: !!(meta?.atom && meta?.host) || !!meta?.keyed,
1425
+ atom: meta?.atom,
1426
+ host: meta?.host,
1427
+ holeFiber: hole,
1428
+ dispose() {
1429
+ extra?.();
1430
+ if (meta?.host) meta.host.isConnected = false;
1431
+ closeFiber(hole);
1432
+ }
1433
+ });
1434
+ }
1435
+ function materialize(view, fiber) {
1436
+ if (skip(view)) return [];
1437
+ if (typeof view === "string" || typeof view === "number" || typeof view === "bigint") return [createSsrText(String(view))];
1438
+ if (isAtomHandle(view)) {
1439
+ const reused = findReusableAtomHost(fiber, view.atom);
1440
+ if (reused) return [atomHostPlaceholder(reused.host)];
1441
+ const { fiber: hole, nodes } = openHole(fiber);
1442
+ const host = nodes[0];
1443
+ let unsub = () => {};
1444
+ fiber.runtime.later(() => {
1445
+ if (hole.closed) return;
1446
+ let keep;
1447
+ unsub = fiber.registry.subscribe(view.atom, (v) => {
1448
+ if (hole.closed) return;
1449
+ const next = unwrap(v, keep);
1450
+ if (next === KEEP) return;
1451
+ keep = next;
1452
+ paintHole(hole, next);
1453
+ }, { immediate: true });
1454
+ });
1455
+ trackHole(fiber, hole, () => unsub(), {
1456
+ atom: view.atom,
1457
+ host
1458
+ });
1459
+ return nodes;
1460
+ }
1461
+ if (Stream.isStream(view)) {
1462
+ const { fiber: hole, nodes } = openHole(fiber);
1463
+ hole.run(Stream.runForEach(view.pipe(Stream.take(1)), (v) => Effect.sync(() => {
1464
+ if (!hole.closed) paintHole(hole, v);
1465
+ })).pipe(Effect.catch((e) => Effect.sync(() => {
1466
+ if (!hole.closed) paintHole(hole, String(e));
1467
+ }))), () => {});
1468
+ trackHole(fiber, hole);
1469
+ return nodes;
1470
+ }
1471
+ if (isSetupFn(view)) {
1472
+ const { fiber: hole, nodes } = openHole(fiber);
1473
+ fiber.runtime.later(() => {
1474
+ if (!hole.closed) runSetup(hole, view);
1475
+ });
1476
+ trackHole(fiber, hole);
1477
+ return nodes;
1478
+ }
1479
+ if (isVNode(view)) {
1480
+ if (view.type === Fragment) return view.children.flatMap((c) => materialize(c, fiber));
1481
+ if (typeof view.type === "function") {
1482
+ const props = {
1483
+ ...view.props,
1484
+ children: view.children
1485
+ };
1486
+ const type = view.type;
1487
+ if (type[ISLAND] === true) {
1488
+ const frame = fiber.islandFrame ??= {
1489
+ i: 0,
1490
+ slots: []
1491
+ };
1492
+ const i = frame.i++;
1493
+ const existing = frame.slots[i];
1494
+ if (existing && existing.type === type && existing.host.isConnected && typeof existing.updateProps === "function") {
1495
+ existing.updateProps(props);
1496
+ return [existing.host];
1497
+ }
1498
+ existing?.unmount?.();
1499
+ const tag = typeof type[ISLAND_SLOT_TAG] === "string" ? type[ISLAND_SLOT_TAG] : "div";
1500
+ const el = createSsrElement(tag);
1501
+ el.setAttribute("data-ilha", "");
1502
+ const mountFn = type[ISLAND_MOUNT_INTERNAL];
1503
+ if (typeof mountFn === "function") {
1504
+ const handle = mountFn(el, props);
1505
+ const slot = {
1506
+ type,
1507
+ host: el,
1508
+ updateProps: handle?.updateProps,
1509
+ unmount: handle?.unmount
1510
+ };
1511
+ frame.slots[i] = slot;
1512
+ fiber.holes.push({
1513
+ keepOnMorph: true,
1514
+ dispose: () => {
1515
+ el.isConnected = false;
1516
+ handle?.unmount?.();
1517
+ if (frame.slots[i] === slot) frame.slots[i] = void 0;
1518
+ }
1519
+ });
1520
+ }
1521
+ return [el];
1522
+ }
1523
+ const k = view.key == null ? void 0 : String(view.key);
1524
+ const reuse = k ? fiber.keyedHoles?.get(k) : void 0;
1525
+ if (reuse && !reuse.closed) {
1526
+ if (reuse.propsBox) reuse.propsBox.current = props;
1527
+ const next = withFiber(fiber, () => type(reuse.propsBox?.current ?? props));
1528
+ if (next && typeof next.then !== "function" && !isSetupFn(next)) reuse.paint(next);
1529
+ return [reuse.root];
1530
+ }
1531
+ const { fiber: hole, nodes } = openHole(fiber);
1532
+ hole.propsBox = { current: props };
1533
+ if (k) {
1534
+ fiber.keyedHoles ??= /* @__PURE__ */ new Map();
1535
+ fiber.keyedHoles.set(k, hole);
1536
+ }
1537
+ fiber.runtime.later(() => {
1538
+ if (!hole.closed) runSetup(hole, () => type(hole.propsBox.current));
1539
+ });
1540
+ trackHole(fiber, hole, void 0, k ? { keyed: true } : void 0);
1541
+ return nodes;
1542
+ }
1543
+ const el = createSsrElement(view.type);
1544
+ if (view.key != null) el.setAttribute(KEY_ATTR, String(view.key));
1545
+ applyProps(el, view.props, fiber);
1546
+ fiber.keyedHoles ??= /* @__PURE__ */ new Map();
1547
+ const childFiber = {
1548
+ ...fiber,
1549
+ root: el,
1550
+ holes: fiber.holes,
1551
+ keyedHoles: fiber.keyedHoles
1552
+ };
1553
+ for (const c of view.children) for (const n of materialize(c, childFiber)) el.appendChild(n);
1554
+ return [el];
1555
+ }
1556
+ if (Array.isArray(view) || view && typeof view === "object" && Symbol.iterator in Object(view)) return [...view].flatMap((c) => materialize(c, fiber));
1557
+ return [createSsrText(String(view))];
1558
+ }
1559
+ function paint(fiber, view) {
1560
+ fiber.islandFrame ??= {
1561
+ i: 0,
1562
+ slots: []
1563
+ };
1564
+ disposeHoles(fiber);
1565
+ fiber.root.replaceChildren();
1566
+ insert(fiber, materialize(view, fiber));
1567
+ }
1568
+ function paintError(fiber, e) {
1569
+ console.error(e);
1570
+ paint(fiber, errorView(e));
1571
+ }
1572
+ function attachSsr(fn, opts) {
1573
+ const root = createSsrRoot();
1574
+ const runtime = makeRuntime({
1575
+ ssr: true,
1576
+ ssrCapture: opts?.ssrCapture === true
1577
+ });
1578
+ let resolve;
1579
+ const ready = new Promise((r) => {
1580
+ resolve = r;
1581
+ });
1582
+ const fiber = makeFiber(runtime, root, paint, { onFail: (e) => {
1583
+ opts?.onError?.(e);
1584
+ paintError(fiber, e);
1585
+ resolve();
1586
+ } });
1587
+ runtime.begin();
1588
+ runtime.setIdle(resolve);
1589
+ runSetup(fiber, fn);
1590
+ runtime.end();
1591
+ return {
1592
+ root,
1593
+ ready,
1594
+ runtime,
1595
+ unmount() {
1596
+ closeFiber(fiber);
1597
+ runtime.close();
1598
+ root.replaceChildren();
1599
+ }
1600
+ };
1601
+ }
1602
+
1603
+ //#endregion
1604
+ //#region src/mount.ts
919
1605
  function readHydrate(el) {
920
1606
  const raw = (el.hasAttribute("data-ilha") ? el : el.querySelector("[data-ilha]"))?.getAttribute("data-ilha-state");
921
1607
  if (raw) return decodeSnapshot(raw);
@@ -928,18 +1614,14 @@ function readHydrate(el) {
928
1614
  }
929
1615
  function attach(el, fn, opts) {
930
1616
  if (!opts?.hydrate) el.innerHTML = "";
931
- const runtime = makeRuntime({
932
- ssr: opts?.ssr,
933
- ssrCapture: opts?.ssrCapture,
934
- hydrate: opts?.hydrate ? readHydrate(el) : void 0
935
- });
1617
+ const runtime = makeRuntime({ hydrate: opts?.hydrate ? readHydrate(el) : void 0 });
936
1618
  let resolve;
937
1619
  const ready = new Promise((r) => {
938
1620
  resolve = r;
939
1621
  });
940
- const fiber = makeFiber(runtime, el, paint, { onFail: (e) => {
1622
+ const fiber = makeFiber(runtime, el, paint$1, { onFail: (e) => {
941
1623
  opts?.onError?.(e);
942
- paintError(fiber, e);
1624
+ paintError$1(fiber, e);
943
1625
  resolve();
944
1626
  } });
945
1627
  if (opts?.hydrate) fiber.hydrate = true;
@@ -960,26 +1642,13 @@ function attach(el, fn, opts) {
960
1642
  function mount(el, fn, opts) {
961
1643
  return attach(el, fn, opts).unmount;
962
1644
  }
963
- let domReady;
964
- function ensureDocument() {
965
- if (typeof globalThis.document !== "undefined") return Promise.resolve();
966
- domReady ??= import("@happy-dom/global-registrator").then(({ GlobalRegistrator }) => {
967
- if (typeof globalThis.document === "undefined") GlobalRegistrator.register();
968
- });
969
- return domReady;
970
- }
971
1645
  async function renderToString(fn, opts) {
972
- await ensureDocument();
973
1646
  const snapshot = opts?.snapshot !== false;
974
1647
  const markers = opts?.markers !== false;
975
- const el = document.createElement("div");
976
- const { unmount, ready, runtime } = attach(el, fn, {
977
- ssr: true,
978
- ssrCapture: opts?.captureActions === true
979
- });
1648
+ const { unmount, ready, runtime, root } = attachSsr(fn, { ssrCapture: opts?.captureActions === true });
980
1649
  if (opts?.timeout == null) await ready;
981
1650
  else await Promise.race([ready, new Promise((r) => setTimeout(r, opts.timeout))]);
982
- let inner = el.innerHTML;
1651
+ let inner = root.innerHTML;
983
1652
  const actions = runtime.ssrActions;
984
1653
  if (Object.keys(actions).length > 0) inner = `<template data-ilha-actions="${escapeAttr(JSON.stringify(actions))}"></template>${inner}`;
985
1654
  const snap = snapshot ? encodeSnapshot(runtime.ssrValues) : void 0;
@@ -989,4 +1658,4 @@ async function renderToString(fn, opts) {
989
1658
  }
990
1659
 
991
1660
  //#endregion
992
- export { atom as a, isAtomHandle as c, makeFiber as d, interpret as i, closeFiber as l, renderToString as n, handleOwner as o, paint as r, instr as s, mount as t, getFiber as u };
1661
+ export { atom as a, instr as c, getFiber as d, makeFiber as f, interpret as i, isAtomHandle as l, renderToString as n, batch as o, paint$1 as r, handleOwner as s, mount as t, closeFiber as u };