ilha 0.14.10 → 0.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1,4 @@
1
- import { _ as skip, a as ISLAND, b as failureMessage, c as KEEP, d as errorView, f as isBigInt, g as isString, h as isObject, i as Fragment, l as KEY_ATTR, m as isNumber, n as isSetupFn, o as ISLAND_MOUNT_INTERNAL, p as isFunction, r as isVNode, s as ISLAND_SLOT_TAG, u as SLOT_ATTR, v as unwrap, x as toError, y as asFailure } from "./vnode-M-oJMKcv.js";
1
+ import { S as toError, _ as isString, a as ISLAND, b as asFailure, c as KEEP, f as errorView, g as isObject, h as isNumber, i as Fragment, l as KEY_ATTR, m as isFunction, n as isSetupFn, o as ISLAND_MOUNT_INTERNAL, p as isBigInt, r as isVNode, s as ISLAND_SLOT_TAG, u as SLOT_ATTR, v as skip, x as failureMessage, y as unwrap } from "./vnode-BHUY61Rm.js";
2
2
  import * as Atom from "effect/reactivity/Atom";
3
3
  import { Equal } from "effect";
4
4
  import * as Effect$1 from "effect/Effect";
@@ -44,6 +44,8 @@ const clearFiberView = (fiber) => {
44
44
  fiber.watchI = 0;
45
45
  fiber.primitives = void 0;
46
46
  fiber.primitiveI = 0;
47
+ for (const release of fiber.primitiveHolds ?? []) release();
48
+ fiber.primitiveHolds = void 0;
47
49
  fiber.islandFrame = void 0;
48
50
  fiber.keyedHoles = void 0;
49
51
  for (const h of fiber.holes) h.dispose();
@@ -93,6 +95,7 @@ const makeRuntime = (opts) => {
93
95
  holeSeq += 1;
94
96
  return holeSeq;
95
97
  },
98
+ onError: opts?.onError,
96
99
  registry,
97
100
  scope,
98
101
  setIdle: (onReady) => {
@@ -148,6 +151,20 @@ const reportFiberError = (fiber, error) => {
148
151
  }
149
152
  return false;
150
153
  };
154
+ /**
155
+ * Open a mount's root fiber. A failing root component reports to
156
+ * `runtime.onError`, paints the error view, and settles the mount.
157
+ */
158
+ const makeRootFiber = ({ runtime, root, paint, settle }) => {
159
+ const fiber = makeFiber(runtime, root, paint, { onFail: (e) => {
160
+ const error = toError(e);
161
+ runtime.onError?.(error);
162
+ console.error(error);
163
+ fiber.paint(errorView(error));
164
+ settle();
165
+ } });
166
+ return fiber;
167
+ };
151
168
 
152
169
  //#endregion
153
170
  //#region src/atom.ts
@@ -272,6 +289,19 @@ const usePrimitiveSlot = (fiber, make) => {
272
289
  fresh: true
273
290
  };
274
291
  };
292
+ /**
293
+ * Keep a component's own state atom in the registry while its fiber lives.
294
+ *
295
+ * The registry drops an atom nothing subscribes to. State that a handler
296
+ * writes and the view never reads would otherwise reset between events.
297
+ */
298
+ const holdState = (fiber, atom) => {
299
+ if (!fiber.runtime.ssr) {
300
+ fiber.primitiveHolds ??= [];
301
+ fiber.primitiveHolds.push(fiber.registry.mount(atom));
302
+ }
303
+ return atom;
304
+ };
275
305
  const withTrackGetRun = (fiber, fn, onOk, onErr) => {
276
306
  const deps = /* @__PURE__ */ new Set();
277
307
  const get = (a) => {
@@ -378,9 +408,9 @@ const makeAtom = (init, options) => {
378
408
  if (Effect$1.isEffect(seed) || Stream$1.isStream(seed)) return Atom.make(seed);
379
409
  const made = Atom.make(seed);
380
410
  const eq = options?.equals;
381
- if (eq === void 0) return made;
411
+ if (eq === void 0) return holdState(fiber, made);
382
412
  const equalsFn = eq === "structural" ? structuralEqual : eq;
383
- return Atom.withEquality(made, equalsFn);
413
+ return holdState(fiber, Atom.withEquality(made, equalsFn));
384
414
  });
385
415
  const handle = wrapHandle(a, fiber);
386
416
  if (fresh && fiber.runtime.ssr) fiber.runtime.ssrValues.push(fiber.registry.get(a));
@@ -396,7 +426,7 @@ const atomLazy = (init) => {
396
426
  fiber.runtime.hydrateI += 1;
397
427
  if (v !== void 0) seed = v;
398
428
  }
399
- return Atom.make(seed);
429
+ return holdState(fiber, Atom.make(seed));
400
430
  });
401
431
  const handle = wrapHandle(a, fiber);
402
432
  if (fresh && fiber.runtime.ssr) fiber.runtime.ssrValues.push(fiber.registry.get(a));
@@ -502,7 +532,7 @@ const runSetup = (fiber, fn) => {
502
532
 
503
533
  //#endregion
504
534
  //#region src/events.ts
505
- const ACTION_CALL$1 = Symbol.for("ilha.actionCall");
535
+ const ACTION_CALL = Symbol.for("ilha.actionCall");
506
536
  const eventTypeFromProp = (key) => {
507
537
  if (key.length < 4) return;
508
538
  let type;
@@ -537,7 +567,7 @@ const bindSsrActions = (el, props, fiber) => {
537
567
  seen.add(type);
538
568
  const fn = props[key];
539
569
  if (!isFunction(fn)) continue;
540
- const branded = fn[ACTION_CALL$1];
570
+ const branded = fn[ACTION_CALL];
541
571
  const rec = branded?.k ? {
542
572
  a: branded.a ?? [],
543
573
  k: branded.k
@@ -1124,6 +1154,41 @@ const sweepComponentSlots = (fiber) => {
1124
1154
  }
1125
1155
  slots.length = cframe.i;
1126
1156
  };
1157
+ /** Close a child hole through the parent's hole list, so it disposes once. */
1158
+ const releaseHole = (fiber, hole) => {
1159
+ const idx = fiber.holes.findIndex((h) => h.holeFiber === hole);
1160
+ if (idx === -1) closeFiber(hole);
1161
+ else {
1162
+ const [removed] = fiber.holes.splice(idx, 1);
1163
+ removed?.dispose();
1164
+ }
1165
+ };
1166
+ /** Close the keyed components the paint that just ran did not reach. */
1167
+ const sweepKeyedHoles = (fiber) => {
1168
+ const { keyedHoles, keyedGen } = fiber;
1169
+ if (!keyedHoles) return;
1170
+ for (const [k, hole] of keyedHoles) {
1171
+ if (!hole.closed && hole.keyedSeen === keyedGen) continue;
1172
+ keyedHoles.delete(k);
1173
+ if (!hole.closed) releaseHole(fiber, hole);
1174
+ }
1175
+ };
1176
+ /**
1177
+ * Run `fn` as one paint of `fiber`: the keyed components it materializes stay
1178
+ * alive, and the ones it no longer reaches are closed afterwards.
1179
+ */
1180
+ const pass = (fiber, fn) => {
1181
+ fiber.keyedHoles ??= /* @__PURE__ */ new Map();
1182
+ fiber.keyedGen = (fiber.keyedGen ?? 0) + 1;
1183
+ fn();
1184
+ sweepKeyedHoles(fiber);
1185
+ };
1186
+ const warnedKeys = /* @__PURE__ */ new Set();
1187
+ const warnDuplicateKey = (key) => {
1188
+ if (warnedKeys.has(key)) return;
1189
+ warnedKeys.add(key);
1190
+ console.warn(`ilha: key "${key}" is used by more than one component in the same parent component; keys must be unique there. The duplicate keeps its state by position instead.`);
1191
+ };
1127
1192
  const sweepIslandSlots = (fiber) => {
1128
1193
  const frame = fiber.islandFrame;
1129
1194
  if (!frame) return;
@@ -1218,6 +1283,7 @@ const createPainter = (ops) => {
1218
1283
  const applyProps = (el, props, fiber) => {
1219
1284
  for (const [k, v] of Object.entries(props)) {
1220
1285
  if (isSkippedPropKey(k)) continue;
1286
+ if (ops.setProp?.(el, k, v) === true) continue;
1221
1287
  if (k === "className") {
1222
1288
  el.setAttribute("class", String(v ?? ""));
1223
1289
  continue;
@@ -1263,6 +1329,7 @@ const createPainter = (ops) => {
1263
1329
  onFail: (e) => {
1264
1330
  const error = toError(e);
1265
1331
  if (reportFiberError(child, error)) return;
1332
+ parent.runtime.onError?.(error);
1266
1333
  console.error(error);
1267
1334
  api.paintHole(child, errorView(error));
1268
1335
  },
@@ -1273,57 +1340,58 @@ const createPainter = (ops) => {
1273
1340
  nodes: [host]
1274
1341
  };
1275
1342
  },
1276
- paintHole: (fiber, view) => commit(() => {
1343
+ paintHole: (fiber, view) => {
1277
1344
  if (view === KEEP) return;
1278
- let frame = fiber.islandFrame;
1279
- if (!frame) {
1280
- frame = {
1281
- i: 0,
1282
- slots: []
1283
- };
1284
- fiber.islandFrame = frame;
1285
- }
1286
- frame.i = 0;
1287
- let cframe = fiber.componentFrame;
1288
- if (!cframe) {
1289
- cframe = {
1290
- i: 0,
1291
- slots: []
1292
- };
1293
- fiber.componentFrame = cframe;
1294
- }
1295
- cframe.i = 0;
1296
- const list = Array.isArray(view) ? view : null;
1297
- if (list && list.length > 0 && list.every((v) => isVNode(v) && isFunction(v.type) && v.key !== null && v.key !== void 0)) {
1298
- paintFns.keyedPaintHole(fiber, list);
1299
- sweepComponentSlots(fiber);
1300
- sweepIslandSlots(fiber);
1301
- return;
1302
- }
1303
- const kept = /* @__PURE__ */ new Set();
1304
- for (const s of cframe.slots) if (s && !s.hole.closed) kept.add(s.hole);
1305
- const keptIslands = new Set(frame.slots);
1306
- const keep = [];
1307
- for (const h of fiber.holes) {
1308
- if (h.holeFiber && kept.has(h.holeFiber) || h.islandSlot && keptIslands.has(h.islandSlot)) {
1309
- keep.push(h);
1310
- continue;
1311
- }
1312
- h.dispose();
1313
- }
1314
- fiber.holes = keep;
1315
- const fresh = api.materialize(view, fiber);
1316
- if (canMorphRoot(fiber.root)) {
1317
- const tmp = document.createElement("div");
1318
- for (const n of fresh) tmp.append(n);
1319
- morphInner(fiber.root, tmp);
1320
- } else {
1321
- ops.clearRoot(fiber.root);
1322
- api.insert(fiber, fresh);
1323
- }
1324
- sweepComponentSlots(fiber);
1325
- sweepIslandSlots(fiber);
1326
- })
1345
+ commit(() => {
1346
+ pass(fiber, () => {
1347
+ let frame = fiber.islandFrame;
1348
+ if (!frame) {
1349
+ frame = {
1350
+ i: 0,
1351
+ slots: []
1352
+ };
1353
+ fiber.islandFrame = frame;
1354
+ }
1355
+ frame.i = 0;
1356
+ let cframe = fiber.componentFrame;
1357
+ if (!cframe) {
1358
+ cframe = {
1359
+ i: 0,
1360
+ slots: []
1361
+ };
1362
+ fiber.componentFrame = cframe;
1363
+ }
1364
+ cframe.i = 0;
1365
+ const list = Array.isArray(view) ? view : null;
1366
+ if (list && list.length > 0 && list.every((v) => isVNode(v) && isFunction(v.type) && v.key !== null && v.key !== void 0)) {
1367
+ paintFns.keyedPaintHole(fiber, list);
1368
+ sweepComponentSlots(fiber);
1369
+ sweepIslandSlots(fiber);
1370
+ return;
1371
+ }
1372
+ const kept = /* @__PURE__ */ new Set();
1373
+ for (const s of cframe.slots) if (s && !s.hole.closed) kept.add(s.hole);
1374
+ const keptIslands = new Set(frame.slots);
1375
+ const keep = [];
1376
+ for (const h of fiber.holes) {
1377
+ if (h.holeFiber && kept.has(h.holeFiber) || h.islandSlot && keptIslands.has(h.islandSlot)) {
1378
+ keep.push(h);
1379
+ continue;
1380
+ }
1381
+ h.dispose();
1382
+ }
1383
+ fiber.holes = keep;
1384
+ const fresh = api.materialize(view, fiber);
1385
+ if (canMorphRoot(fiber.root)) {
1386
+ const tmp = document.createElement("div");
1387
+ for (const n of fresh) tmp.append(n);
1388
+ morphInner(fiber.root, tmp);
1389
+ } else ops.placeChildren(fiber.root, fresh);
1390
+ sweepComponentSlots(fiber);
1391
+ sweepIslandSlots(fiber);
1392
+ });
1393
+ });
1394
+ }
1327
1395
  };
1328
1396
  const materializeAtom = (view, fiber) => {
1329
1397
  const reused = findReusableAtomHost(fiber, view.atom);
@@ -1465,7 +1533,13 @@ const createPainter = (ops) => {
1465
1533
  const k = view.key === null || view.key === void 0 ? void 0 : String(view.key);
1466
1534
  const reuse = k ? fiber.keyedHoles?.get(k) : void 0;
1467
1535
  const run = type;
1536
+ const gen = fiber.keyedGen;
1537
+ if (k && reuse && !reuse.closed && reuse.keyedSeen === gen) {
1538
+ warnDuplicateKey(k);
1539
+ return materializeUnkeyedComponent(run, type, props, fiber);
1540
+ }
1468
1541
  if (reuse && !reuse.closed) {
1542
+ reuse.keyedSeen = gen;
1469
1543
  if (reuse.propsBox) reuse.propsBox.current = props;
1470
1544
  const next = withFiber(fiber, () => run(reuse.propsBox?.current ?? props));
1471
1545
  if (next && !isThenable(next) && !isSetupFn(next)) reuse.paint(next);
@@ -1474,8 +1548,8 @@ const createPainter = (ops) => {
1474
1548
  if (!k) return materializeUnkeyedComponent(run, type, props, fiber);
1475
1549
  const { fiber: khole, nodes: knodes } = api.openHole(fiber);
1476
1550
  khole.propsBox = { current: props };
1477
- fiber.keyedHoles ??= /* @__PURE__ */ new Map();
1478
- fiber.keyedHoles.set(k, khole);
1551
+ khole.keyedSeen = gen;
1552
+ fiber.keyedHoles?.set(k, khole);
1479
1553
  fiber.runtime.later(() => {
1480
1554
  if (khole.closed) return;
1481
1555
  const box = khole.propsBox;
@@ -1492,7 +1566,6 @@ const createPainter = (ops) => {
1492
1566
  const el = ops.createElement(tag, ns);
1493
1567
  if (view.key !== null && view.key !== void 0) el.setAttribute(KEY_ATTR, String(view.key));
1494
1568
  api.applyProps(el, view.props, fiber);
1495
- fiber.keyedHoles ??= /* @__PURE__ */ new Map();
1496
1569
  const childFiber = {
1497
1570
  ...fiber,
1498
1571
  holes: fiber.holes,
@@ -1526,25 +1599,16 @@ const createPainter = (ops) => {
1526
1599
  return [ops.createText(String(view))];
1527
1600
  };
1528
1601
  paintFns.keyedPaintHole = (fiber, views) => {
1529
- const prev = fiber.keyedHoles ?? /* @__PURE__ */ new Map();
1530
- const keep = new Set(views.map((v) => String(v.key)));
1531
- for (const [k, h] of prev) if (!keep.has(k)) closeFiber(h);
1532
- const next = /* @__PURE__ */ new Map();
1533
1602
  const nodes = [];
1534
1603
  for (const v of views) {
1535
- const k = String(v.key);
1536
- const reuse = prev.get(k);
1604
+ const reuse = fiber.keyedHoles?.get(String(v.key));
1537
1605
  if (reuse && !reuse.closed) {
1538
- next.set(k, reuse);
1606
+ reuse.keyedSeen = fiber.keyedGen;
1539
1607
  nodes.push(ops.asNode(reuse.root));
1540
1608
  continue;
1541
1609
  }
1542
- const made = api.materialize(v, fiber);
1543
- const hole = fiber.keyedHoles?.get(k);
1544
- if (hole) next.set(k, hole);
1545
- nodes.push(...made);
1610
+ nodes.push(...api.materialize(v, fiber));
1546
1611
  }
1547
- fiber.keyedHoles = next;
1548
1612
  ops.placeChildren(fiber.root, nodes);
1549
1613
  };
1550
1614
  return {
@@ -1553,508 +1617,10 @@ const createPainter = (ops) => {
1553
1617
  disposeHoles: (fiber, opts) => disposeHoles(fiber, opts),
1554
1618
  insert: api.insert,
1555
1619
  materialize: api.materialize,
1556
- paintHole: api.paintHole
1557
- };
1558
- };
1559
-
1560
- //#endregion
1561
- //#region src/paint.ts
1562
- const noopDisconnect = () => {};
1563
- const core$1 = createPainter({
1564
- appendRoot: (root, node) => insertRendered(root, node, null),
1565
- asElement: (el) => el,
1566
- asNode: (host) => host,
1567
- asRoot: (el) => el,
1568
- bindEvents,
1569
- clearRoot: (root) => {
1570
- if (root instanceof Element) root.replaceChildren();
1571
- },
1572
- committed: committedElement,
1573
- createElement: (tag, ns) => ns ? document.createElementNS(ns, tag) : document.createElement(tag),
1574
- createRaw: (html, parent) => {
1575
- const range = document.createRange();
1576
- range.selectNodeContents(parent);
1577
- return [...range.createContextualFragment(html).childNodes];
1578
- },
1579
- createSlotHost: (slotId) => {
1580
- const host = document.createElement("span");
1581
- if (slotId !== null) host.setAttribute(SLOT_ATTR, slotId);
1582
- host.style.display = "contents";
1583
- return host;
1584
- },
1585
- createText: (text) => document.createTextNode(text),
1586
- disconnect: noopDisconnect,
1587
- placeChildren: (root, nodes) => {
1588
- if (root instanceof Element) placeChildren(root, nodes);
1589
- },
1590
- reuseNode: (host) => host instanceof Element && host.isConnected ? standInFor(host) : host,
1591
- setFormControl: (el, key, v) => {
1592
- const node = el;
1593
- if (key === "value") {
1594
- if (v === null || v === void 0) {
1595
- el.removeAttribute("value");
1596
- node.value = "";
1597
- } else {
1598
- el.setAttribute("value", String(v));
1599
- node.value = String(v);
1600
- }
1601
- } else if (key === "checked") {
1602
- node.checked = Boolean(v);
1603
- if (v) el.setAttribute("checked", "");
1604
- else el.removeAttribute("checked");
1605
- } else {
1606
- node.selected = Boolean(v);
1607
- if (v) el.setAttribute("selected", "");
1608
- else el.removeAttribute("selected");
1609
- }
1610
- },
1611
- setStyle: (el, css) => {
1612
- el.style.cssText = css;
1613
- }
1614
- });
1615
- const refreshAtomHost = (fiber, atom, host, hole) => {
1616
- if (hole.closed || !host.isConnected) return;
1617
- const next = unwrap(fiber.registry.get(atom));
1618
- if (next === KEEP) return;
1619
- core$1.paintHole(hole, next);
1620
- };
1621
- const refreshAllAtomHosts = (fiber) => {
1622
- for (const h of fiber.holes) if (h.atom && h.host && h.holeFiber) refreshAtomHost(fiber, h.atom, h.host, h.holeFiber);
1623
- };
1624
- const pruneDisconnectedAtomHoles = (fiber) => {
1625
- fiber.holes = fiber.holes.filter((h) => {
1626
- if (!h.atom || !h.host || !h.holeFiber) return true;
1627
- if (h.host.isConnected) return true;
1628
- h.dispose();
1629
- return false;
1630
- });
1631
- };
1632
- const canHydrate = (fiber, view) => {
1633
- if (!isVNode(view) || !isString(view.type)) return false;
1634
- const el = fiber.root.firstElementChild;
1635
- return !!el && el.tagName === view.type.toUpperCase();
1636
- };
1637
- const paintHydrate = (fiber, view) => {
1638
- const el = fiber.root.firstElementChild;
1639
- if (!el) return;
1640
- fiber.hydrate = false;
1641
- fiber.liveEl = el;
1642
- core$1.applyProps(el, view.props, fiber);
1643
- const childFiber = {
1644
- ...fiber,
1645
- holes: fiber.holes,
1646
- root: el
1647
- };
1648
- el.replaceChildren();
1649
- for (const c of view.children) for (const n of core$1.materialize(c, childFiber)) insertRendered(el, n, null);
1650
- };
1651
- const paintMorphLive = (fiber, view) => {
1652
- if (!fiber.liveEl) return;
1653
- core$1.disposeHoles(fiber, { morph: true });
1654
- core$1.applyProps(fiber.liveEl, view.props, fiber);
1655
- const liveNs = fiber.liveEl.namespaceURI;
1656
- const tmp = liveNs && liveNs !== "http://www.w3.org/1999/xhtml" ? document.createElementNS(liveNs, fiber.liveEl.localName) : document.createElement(fiber.liveEl.localName);
1657
- const childFiber = {
1658
- ...fiber,
1659
- holes: fiber.holes,
1660
- root: tmp
1620
+ paintHole: api.paintHole,
1621
+ pass
1661
1622
  };
1662
- for (const c of view.children) for (const n of core$1.materialize(c, childFiber)) tmp.append(n);
1663
- morphInner(fiber.liveEl, tmp);
1664
- refreshAllAtomHosts(fiber);
1665
- pruneDisconnectedAtomHoles(fiber);
1666
- };
1667
- const paintRoot = (fiber, view) => {
1668
- fiber.islandFrame ??= {
1669
- i: 0,
1670
- slots: []
1671
- };
1672
- if (fiber.hydrate) {
1673
- if (canHydrate(fiber, view)) {
1674
- paintHydrate(fiber, view);
1675
- return;
1676
- }
1677
- console.warn("[ilha] hydrate mismatch, full mount");
1678
- fiber.hydrate = false;
1679
- if (fiber.root instanceof Element) fiber.root.replaceChildren();
1680
- }
1681
- if (fiber.liveEl && isVNode(view) && isString(view.type) && fiber.liveEl.localName === view.type) {
1682
- paintMorphLive(fiber, view);
1683
- return;
1684
- }
1685
- core$1.disposeHoles(fiber, { morph: true });
1686
- if (fiber.root instanceof Element && fiber.root.childNodes.length > 0) {
1687
- const tmp = document.createElement("div");
1688
- for (const n of core$1.materialize(view, fiber)) tmp.append(n);
1689
- morphInner(fiber.root, tmp);
1690
- const first = fiber.root.firstElementChild;
1691
- if (first) fiber.liveEl = first;
1692
- return;
1693
- }
1694
- core$1.insert(fiber, core$1.materialize(view, fiber));
1695
- const first = fiber.root instanceof Element ? fiber.root.firstElementChild : null;
1696
- if (first) fiber.liveEl = first;
1697
- };
1698
- const paint$1 = (fiber, view) => {
1699
- core$1.commit(() => {
1700
- paintRoot(fiber, view);
1701
- });
1702
- };
1703
- const paintError$1 = (fiber, e) => {
1704
- console.error(e);
1705
- paint$1(fiber, errorView(e));
1706
- };
1707
-
1708
- //#endregion
1709
- //#region src/snapshot.ts
1710
- const MAX_SNAPSHOT_CHARS = 262144;
1711
- const MAX_SNAPSHOT_DEPTH = 32;
1712
- const exceedsMaxDepth = (value, depth) => {
1713
- if (depth > MAX_SNAPSHOT_DEPTH) return true;
1714
- if (value === null || !isObject(value)) {
1715
- if (Array.isArray(value)) {
1716
- for (const item of value) if (exceedsMaxDepth(item, depth + 1)) return true;
1717
- }
1718
- return false;
1719
- }
1720
- const obj = value;
1721
- for (const key of Object.keys(obj)) if (exceedsMaxDepth(obj[key], depth + 1)) return true;
1722
- return false;
1723
- };
1724
- const deleteUnsafeKey = (obj, key) => {
1725
- if (Object.hasOwn(obj, key)) Reflect.deleteProperty(obj, key);
1726
- };
1727
- const stripUnsafeKeys = (value) => {
1728
- if (Array.isArray(value)) {
1729
- for (const item of value) stripUnsafeKeys(item);
1730
- return;
1731
- }
1732
- if (value === null || !isObject(value)) return;
1733
- const obj = value;
1734
- deleteUnsafeKey(obj, "__proto__");
1735
- deleteUnsafeKey(obj, "constructor");
1736
- deleteUnsafeKey(obj, "prototype");
1737
- for (const key of Object.getOwnPropertyNames(obj)) stripUnsafeKeys(obj[key]);
1738
- };
1739
- const safeParseSnapshot = (raw) => {
1740
- if (raw.length > MAX_SNAPSHOT_CHARS) return;
1741
- let parsed;
1742
- try {
1743
- parsed = JSON.parse(raw);
1744
- } catch {
1745
- return;
1746
- }
1747
- if (exceedsMaxDepth(parsed, 1)) return;
1748
- if (!isObject(parsed) || Array.isArray(parsed)) return;
1749
- stripUnsafeKeys(parsed);
1750
- return parsed;
1751
- };
1752
- const encodeSnapshot = (values) => JSON.stringify({ v: values });
1753
- const decodeSnapshot = (raw) => {
1754
- const values = safeParseSnapshot(raw)?.v;
1755
- return Array.isArray(values) ? values : void 0;
1756
- };
1757
-
1758
- //#endregion
1759
- //#region src/ssr-dom.ts
1760
- const VOID = /* @__PURE__ */ new Set([
1761
- "area",
1762
- "base",
1763
- "br",
1764
- "col",
1765
- "embed",
1766
- "hr",
1767
- "img",
1768
- "input",
1769
- "link",
1770
- "meta",
1771
- "param",
1772
- "source",
1773
- "track",
1774
- "wbr"
1775
- ]);
1776
- /** Tags whose children are raw text — do not HTML-escape content. */
1777
- const RAW_TEXT = /* @__PURE__ */ new Set(["script", "style"]);
1778
- const escapeText = (s) => s.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;");
1779
- const escapeAttr = (s) => escapeText(s).replaceAll("\"", "&quot;");
1780
- const createSsrRaw = (html) => ({
1781
- html,
1782
- kind: "raw"
1783
- });
1784
- const createSsrText = (text) => ({
1785
- kind: "text",
1786
- text
1787
- });
1788
- const serializeRawText = (tag, text) => {
1789
- if (new RegExp(`</${tag}\\b`, "iu").test(text)) throw new Error(`ilha: unsafe raw text in <${tag}> (contains "</${tag}")`);
1790
- return text;
1791
- };
1792
- const serializeNode = (node) => {
1793
- if (node.kind === "text") return escapeText(node.text);
1794
- if (node.kind === "raw") return node.html;
1795
- const parts = [];
1796
- for (const [name, value] of node.attrs) parts.push(`${name}="${escapeAttr(value)}"`);
1797
- const attr = parts.length > 0 ? ` ${parts.join(" ")}` : "";
1798
- const { tag } = node;
1799
- if (VOID.has(tag)) return `<${tag}${attr}>`;
1800
- if (RAW_TEXT.has(tag)) return `<${tag}${attr}>${serializeRawText(tag, node.textContent)}</${tag}>`;
1801
- return `<${tag}${attr}>${node.children.map(serializeNode).join("")}</${tag}>`;
1802
- };
1803
- const toDataAttr = (key) => `data-${key.replaceAll(/[A-Z]/gu, (ch) => `-${ch.toLowerCase()}`)}`;
1804
- const isSymbolKey = (value) => Object.prototype.toString.call(value) === "[object Symbol]";
1805
- const createSsrElement = (tag) => {
1806
- const lower = tag.toLowerCase();
1807
- const children = [];
1808
- const el = {
1809
- append: (...nodes) => {
1810
- children.push(...nodes);
1811
- },
1812
- appendChild: (node) => {
1813
- children.push(node);
1814
- },
1815
- attrs: /* @__PURE__ */ new Map(),
1816
- children,
1817
- dataset: new Proxy({}, {
1818
- get: (_t, prop) => {
1819
- if (prop === "undefined" || isSymbolKey(prop)) return;
1820
- return el.getAttribute(toDataAttr(prop)) ?? void 0;
1821
- },
1822
- set: (_t, prop, value) => {
1823
- if (isSymbolKey(prop)) return false;
1824
- const attr = toDataAttr(prop);
1825
- if (value === void 0 || value === null) el.removeAttribute(attr);
1826
- else el.setAttribute(attr, String(value));
1827
- return true;
1828
- }
1829
- }),
1830
- getAttribute: (name) => {
1831
- const value = el.attrs.get(name);
1832
- return value === void 0 ? null : value;
1833
- },
1834
- hasAttribute: (name) => el.attrs.has(name),
1835
- isConnected: true,
1836
- kind: "element",
1837
- localName: lower,
1838
- removeAttribute: (name) => {
1839
- el.attrs.delete(name);
1840
- },
1841
- replaceChildren: (...nodes) => {
1842
- children.length = 0;
1843
- children.push(...nodes);
1844
- },
1845
- setAttribute: (name, value) => {
1846
- el.attrs.set(name, value);
1847
- },
1848
- tag: lower,
1849
- tagName: lower.toUpperCase(),
1850
- get textContent() {
1851
- return children.map((n) => {
1852
- if (n.kind === "text") return n.text;
1853
- if (n.kind === "raw") return n.html;
1854
- return n.textContent;
1855
- }).join("");
1856
- },
1857
- set textContent(value) {
1858
- children.length = 0;
1859
- children.push(createSsrText(value));
1860
- }
1861
- };
1862
- return el;
1863
- };
1864
- const createSsrRoot = () => {
1865
- const children = [];
1866
- return {
1867
- append: (...nodes) => {
1868
- children.push(...nodes);
1869
- },
1870
- appendChild: (node) => {
1871
- children.push(node);
1872
- },
1873
- children,
1874
- get innerHTML() {
1875
- return children.map(serializeNode).join("");
1876
- },
1877
- kind: "root",
1878
- replaceChildren: () => {
1879
- children.length = 0;
1880
- }
1881
- };
1882
- };
1883
-
1884
- //#endregion
1885
- //#region src/ssr-paint.ts
1886
- const ssrAs = (n) => n;
1887
- const ACTION_CALL = Symbol.for("ilha.actionCall");
1888
- const defer$1 = () => {
1889
- const { promise, resolve } = Promise.withResolvers();
1890
- return {
1891
- promise,
1892
- resolve: () => {
1893
- resolve(null);
1894
- }
1895
- };
1896
- };
1897
- const bindSsrEvents = (el, props, fiber) => {
1898
- const seen = /* @__PURE__ */ new Set();
1899
- for (const key of Object.keys(props)) {
1900
- const type = eventTypeFromProp(key);
1901
- if (!type || seen.has(type)) continue;
1902
- seen.add(type);
1903
- const fn = props[key];
1904
- if (!isFunction(fn)) continue;
1905
- const branded = fn[ACTION_CALL];
1906
- const rec = branded?.k ? {
1907
- a: branded.a ?? [],
1908
- k: branded.k
1909
- } : void 0;
1910
- if (!rec) continue;
1911
- const id = `${type}:${fiber.runtime.ssrEventI}`;
1912
- fiber.runtime.ssrEventI += 1;
1913
- fiber.runtime.ssrActions[id] = rec;
1914
- const existing = el.dataset.ilhaOn;
1915
- el.dataset.ilhaOn = existing ? `${existing},${id}` : id;
1916
- }
1917
- };
1918
- const ssrOps = {
1919
- appendRoot: (root, node) => ssrAs(root).append(node),
1920
- asElement: (el) => ssrAs(el),
1921
- asNode: (host) => ssrAs(host),
1922
- asRoot: (el) => ssrAs(el),
1923
- bindEvents: bindSsrEvents,
1924
- clearRoot: (root) => ssrAs(root).replaceChildren(),
1925
- committed: (el) => el,
1926
- createElement: createSsrElement,
1927
- createRaw: (html, _parent) => [createSsrRaw(html)],
1928
- createSlotHost: (slotId) => {
1929
- const host = createSsrElement("span");
1930
- if (slotId !== null) host.setAttribute(SLOT_ATTR, slotId);
1931
- host.setAttribute("style", "display:contents");
1932
- return host;
1933
- },
1934
- createText: createSsrText,
1935
- disconnect: (el) => {
1936
- el.isConnected = false;
1937
- },
1938
- placeChildren: (root, nodes) => {
1939
- const host = ssrAs(root);
1940
- host.replaceChildren();
1941
- for (const n of nodes) host.append(n);
1942
- },
1943
- reuseNode: (host) => ssrAs(host),
1944
- setFormControl: (el, key, v) => {
1945
- if (key === "value") el.setAttribute("value", String(v ?? ""));
1946
- else if (v) el.setAttribute(key, "");
1947
- else el.removeAttribute(key);
1948
- },
1949
- setStyle: (el, css) => el.setAttribute("style", css)
1950
- };
1951
- const core = createPainter(ssrOps);
1952
- const paint = (fiber, view) => {
1953
- fiber.islandFrame ??= {
1954
- i: 0,
1955
- slots: []
1956
- };
1957
- core.disposeHoles(fiber);
1958
- ssrOps.clearRoot(fiber.root);
1959
- core.insert(fiber, core.materialize(view, fiber));
1960
- };
1961
- const paintError = (fiber, e) => {
1962
- console.error(e);
1963
- paint(fiber, errorView(e));
1964
- };
1965
- const attachSsr = (fn, opts) => {
1966
- const root = createSsrRoot();
1967
- const runtime = makeRuntime({
1968
- ssr: true,
1969
- ssrCapture: opts?.ssrCapture === true
1970
- });
1971
- const { promise: ready, resolve } = defer$1();
1972
- const fiber = makeFiber(runtime, ssrAs(root), paint, { onFail: (e) => {
1973
- opts?.onError?.(e);
1974
- paintError(fiber, e);
1975
- resolve();
1976
- } });
1977
- runtime.begin();
1978
- runtime.setIdle(resolve);
1979
- runSetup(fiber, fn);
1980
- runtime.end();
1981
- return {
1982
- ready,
1983
- root,
1984
- runtime,
1985
- unmount: () => {
1986
- closeFiber(fiber);
1987
- runtime.close();
1988
- root.replaceChildren();
1989
- }
1990
- };
1991
- };
1992
-
1993
- //#endregion
1994
- //#region src/mount.ts
1995
- const defer = () => {
1996
- const { promise, resolve } = Promise.withResolvers();
1997
- return {
1998
- promise,
1999
- resolve: () => {
2000
- resolve(null);
2001
- }
2002
- };
2003
- };
2004
- const readHydrate = (el) => {
2005
- const raw = (el.matches("[data-ilha]") ? el : el.querySelector("[data-ilha]"))?.dataset.ilhaState;
2006
- if (raw) return decodeSnapshot(raw);
2007
- const first = el.firstElementChild;
2008
- if (first?.tagName === "TEMPLATE" && first.matches("[data-ilha-state]")) {
2009
- const tplRaw = first.dataset.ilhaState;
2010
- first.remove();
2011
- return tplRaw ? decodeSnapshot(tplRaw) : void 0;
2012
- }
2013
- };
2014
- const delay = (ms) => {
2015
- const { promise, resolve } = defer();
2016
- setTimeout(resolve, ms);
2017
- return promise;
2018
- };
2019
- const attach = (el, fn, opts) => {
2020
- if (!opts?.hydrate) el.innerHTML = "";
2021
- const runtime = makeRuntime({ hydrate: opts?.hydrate ? readHydrate(el) : void 0 });
2022
- const { promise: ready, resolve } = defer();
2023
- const fiber = makeFiber(runtime, el, paint$1, { onFail: (e) => {
2024
- opts?.onError?.(e);
2025
- paintError$1(fiber, e);
2026
- resolve();
2027
- } });
2028
- if (opts?.hydrate) fiber.hydrate = true;
2029
- runtime.begin();
2030
- runtime.setIdle(resolve);
2031
- runSetup(fiber, fn);
2032
- runtime.end();
2033
- return {
2034
- ready,
2035
- runtime,
2036
- unmount: () => {
2037
- closeFiber(fiber);
2038
- runtime.close();
2039
- if (!opts?.hydrate) el.innerHTML = "";
2040
- }
2041
- };
2042
- };
2043
- const mount = (el, fn, opts) => attach(el, fn, opts).unmount;
2044
- const renderToString = async (fn, opts) => {
2045
- const snapshot = opts?.snapshot !== false;
2046
- const markers = opts?.markers !== false;
2047
- const { unmount, ready, runtime, root } = attachSsr(fn, { ssrCapture: opts?.captureActions === true });
2048
- const timeout = opts?.timeout;
2049
- await (timeout === void 0 ? ready : Promise.race([ready, delay(timeout)]));
2050
- let inner = root.innerHTML;
2051
- const actions = runtime.ssrActions;
2052
- if (Object.keys(actions).length > 0) inner = `<template data-ilha-actions="${escapeAttr(JSON.stringify(actions))}"></template>${inner}`;
2053
- const snap = snapshot ? encodeSnapshot(runtime.ssrValues) : void 0;
2054
- unmount();
2055
- if (!markers) return snap ? `<template data-ilha-state="${escapeAttr(snap)}"></template>${inner}` : inner;
2056
- return `<div data-ilha${snap ? ` data-ilha-state="${escapeAttr(snap)}"` : ""}>${inner}</div>`;
2057
1623
  };
2058
1624
 
2059
1625
  //#endregion
2060
- export { makeFiber as _, interpret as a, handleOwner as c, structuralEqual as d, untrack as f, getFiber as g, closeFiber as h, runSetup as i, instr as l, clearFiberView as m, renderToString as n, atom as o, wrapHandle as p, unsafe as r, batch as s, mount as t, isAtomHandle as u };
1626
+ export { makeFiber as C, getFiber as S, makeRuntime as T, structuralEqual as _, morphInner as a, clearFiberView as b, bindEvents as c, interpret as d, atom as f, isAtomHandle as g, instr as h, insertRendered as i, eventTypeFromProp as l, handleOwner as m, unsafe as n, placeChildren as o, batch as p, committedElement as r, standInFor as s, createPainter as t, runSetup as u, untrack as v, makeRootFiber as w, closeFiber as x, wrapHandle as y };