streetui 1.7.0 → 1.9.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.
package/dist/testing.cjs CHANGED
@@ -22,24 +22,46 @@ var testing_exports = {};
22
22
  __export(testing_exports, {
23
23
  VERSION: () => VERSION,
24
24
  analyzeGraph: () => analyzeGraph,
25
+ blur: () => blur,
26
+ clickOutside: () => clickOutside,
27
+ closeOverlay: () => closeOverlay,
25
28
  findAllByRole: () => findAllByRole,
29
+ findAllComponents: () => findAllComponents,
26
30
  findByRole: () => findByRole,
27
31
  findByText: () => findByText,
32
+ findComponent: () => findComponent,
28
33
  flushUpdates: () => flushUpdates,
34
+ focus: () => focus,
29
35
  formatInspection: () => formatInspection,
36
+ getComponentName: () => getComponentName,
37
+ hydrateComponent: () => hydrateComponent,
30
38
  inspectCompilation: () => inspectCompilation,
39
+ openOverlay: () => openOverlay,
40
+ pressKey: () => pressKey,
31
41
  render: () => render,
42
+ renderComponent: () => renderComponent,
32
43
  renderOnce: () => renderOnce,
33
44
  renderServerThenHydrate: () => renderServerThenHydrate,
34
- waitFor: () => waitFor
45
+ trigger: () => trigger,
46
+ waitFor: () => waitFor,
47
+ waitForTransition: () => waitForTransition
35
48
  });
36
49
  module.exports = __toCommonJS(testing_exports);
37
50
 
38
51
  // ../core/src/identity.ts
39
52
  var _counter = 0;
53
+ function nextId() {
54
+ return ++_counter;
55
+ }
40
56
  function resetIdCounter() {
41
57
  _counter = 0;
42
58
  }
59
+ function createNodeId(value) {
60
+ return value;
61
+ }
62
+ function generateNodeId(prefix = "node") {
63
+ return createNodeId(`${prefix}:${nextId()}`);
64
+ }
43
65
 
44
66
  // ../core/src/lifecycle.ts
45
67
  var CleanupRegistry = class {
@@ -784,18 +806,25 @@ function trapFocus(dom, container) {
784
806
  dom.addEventListener(container, "keydown", onKeydown);
785
807
  return () => dom.removeEventListener(container, "keydown", onKeydown);
786
808
  }
809
+ var containmentStack = [];
787
810
  function containFocus(dom, container) {
788
811
  const body = dom.body();
789
812
  if (body === null) return () => {
790
813
  };
814
+ containmentStack.push(container);
791
815
  const onFocusIn = (event) => {
816
+ if (containmentStack[containmentStack.length - 1] !== container) return;
792
817
  const target = event.target;
793
818
  if (target !== null && !dom.contains(container, target)) {
794
819
  focusFirst(dom, container);
795
820
  }
796
821
  };
797
822
  dom.addEventListener(body, "focusin", onFocusIn);
798
- return () => dom.removeEventListener(body, "focusin", onFocusIn);
823
+ return () => {
824
+ dom.removeEventListener(body, "focusin", onFocusIn);
825
+ const index = containmentStack.lastIndexOf(container);
826
+ if (index !== -1) containmentStack.splice(index, 1);
827
+ };
799
828
  }
800
829
  function onEscape(dom, target, handler) {
801
830
  const onKeydown = (event) => {
@@ -804,6 +833,33 @@ function onEscape(dom, target, handler) {
804
833
  dom.addEventListener(target, "keydown", onKeydown);
805
834
  return () => dom.removeEventListener(target, "keydown", onKeydown);
806
835
  }
836
+ function rovingMenu(dom, container, selector = FOCUSABLE_SELECTOR) {
837
+ const onKeydown = (event) => {
838
+ const key = event.key;
839
+ const isActivate = key === "Enter" || key === " " || key === "Spacebar";
840
+ const isMove = key === "ArrowDown" || key === "ArrowUp" || key === "Home" || key === "End";
841
+ if (!isActivate && !isMove) return;
842
+ const items = getFocusable(dom, container, selector);
843
+ if (items.length === 0) return;
844
+ const active = dom.activeElement();
845
+ const index = active === null ? -1 : items.indexOf(active);
846
+ if (isActivate) {
847
+ if (index < 0) return;
848
+ event.preventDefault();
849
+ items[index].click?.();
850
+ return;
851
+ }
852
+ event.preventDefault();
853
+ let next;
854
+ if (key === "Home") next = 0;
855
+ else if (key === "End") next = items.length - 1;
856
+ else if (key === "ArrowDown") next = index < 0 ? 0 : (index + 1) % items.length;
857
+ else next = index <= 0 ? items.length - 1 : index - 1;
858
+ dom.focus(items[next]);
859
+ };
860
+ dom.addEventListener(container, "keydown", onKeydown);
861
+ return () => dom.removeEventListener(container, "keydown", onKeydown);
862
+ }
807
863
 
808
864
  // ../renderer/src/render-context.ts
809
865
  function createRenderContext(dom, graph, container, hydrationDiagnostics, staticHTML) {
@@ -1015,7 +1071,7 @@ function patchNode(ctx, graphNode, propKey, newValue) {
1015
1071
  }
1016
1072
 
1017
1073
  // ../renderer/src/reconciliation.ts
1018
- function reconcileChildren(ctx, parentDom, oldInstances, newNodes, mountFn) {
1074
+ function reconcileChildren(ctx, parentDom, oldInstances, newNodes, mountFn, hooks) {
1019
1075
  const oldByKey = /* @__PURE__ */ new Map();
1020
1076
  for (const inst of oldInstances) {
1021
1077
  const key = inst.graphNode.key ?? inst.graphNode.id;
@@ -1036,28 +1092,33 @@ function reconcileChildren(ctx, parentDom, oldInstances, newNodes, mountFn) {
1036
1092
  }
1037
1093
  newInstances.push(existing);
1038
1094
  } else {
1039
- const inst = mountFn(newNode, parentDom);
1040
- newInstances.push(inst);
1095
+ const reclaimed = hooks?.takeLeaving(key);
1096
+ if (reclaimed !== void 0) {
1097
+ patchExistingInstance(ctx, reclaimed, newNode);
1098
+ reconcileItemChildren(ctx, reclaimed, newNode, mountFn);
1099
+ hooks?.onEnter(reclaimed);
1100
+ newInstances.push(reclaimed);
1101
+ } else {
1102
+ const inst = mountFn(newNode, parentDom);
1103
+ hooks?.onEnter(inst);
1104
+ newInstances.push(inst);
1105
+ }
1041
1106
  }
1042
1107
  }
1043
1108
  const removed = [];
1044
1109
  for (const inst of oldInstances) {
1045
1110
  const key = inst.graphNode.key ?? inst.graphNode.id;
1046
- if (!usedKeys.has(key)) {
1047
- removed.push(inst);
1048
- }
1049
- }
1050
- for (const inst of removed) {
1111
+ if (usedKeys.has(key)) continue;
1112
+ if (hooks !== void 0 && hooks.beginLeave(inst)) continue;
1051
1113
  const parent = ctx.dom.parentNode(inst.domNode);
1052
- if (parent !== null) {
1053
- ctx.dom.removeChild(parent, inst.domNode);
1054
- }
1114
+ if (parent !== null) ctx.dom.removeChild(parent, inst.domNode);
1055
1115
  inst.dispose();
1116
+ removed.push(inst);
1056
1117
  }
1057
1118
  reorderDom(ctx, parentDom, newInstances);
1058
1119
  return { instances: newInstances, removed };
1059
1120
  }
1060
- function reconcileChildrenByPlan(ctx, parentDom, oldInstances, plan, mountFn) {
1121
+ function reconcileChildrenByPlan(ctx, parentDom, oldInstances, plan, mountFn, hooks) {
1061
1122
  const oldByKey = /* @__PURE__ */ new Map();
1062
1123
  for (const inst of oldInstances) {
1063
1124
  oldByKey.set(inst.graphNode.key ?? inst.graphNode.id, inst);
@@ -1084,21 +1145,34 @@ function reconcileChildrenByPlan(ctx, parentDom, oldInstances, plan, mountFn) {
1084
1145
  }
1085
1146
  newInstances.push(existing);
1086
1147
  } else {
1087
- const freshNode = entry.build();
1088
- built.push(freshNode);
1089
- const inst = mountFn(freshNode, parentDom);
1090
- newInstances.push(inst);
1148
+ const reclaimed = hooks?.takeLeaving(entry.key);
1149
+ if (reclaimed !== void 0) {
1150
+ const freshNode = entry.build();
1151
+ built.push(freshNode);
1152
+ patchExistingInstance(ctx, reclaimed, freshNode);
1153
+ reconcileItemChildren(ctx, reclaimed, freshNode, mountFn);
1154
+ reclaimed.graphNode.setProp("_sig", freshNode.getProp("_sig"));
1155
+ reclaimed.graphNode.setProp("_item", entry.item);
1156
+ hooks?.onEnter(reclaimed);
1157
+ newInstances.push(reclaimed);
1158
+ } else {
1159
+ const freshNode = entry.build();
1160
+ built.push(freshNode);
1161
+ const inst = mountFn(freshNode, parentDom);
1162
+ hooks?.onEnter(inst);
1163
+ newInstances.push(inst);
1164
+ }
1091
1165
  }
1092
1166
  }
1093
1167
  const removed = [];
1094
1168
  for (const inst of oldInstances) {
1095
1169
  const key = inst.graphNode.key ?? inst.graphNode.id;
1096
- if (!usedKeys.has(key)) removed.push(inst);
1097
- }
1098
- for (const inst of removed) {
1170
+ if (usedKeys.has(key)) continue;
1171
+ if (hooks !== void 0 && hooks.beginLeave(inst)) continue;
1099
1172
  const parent = ctx.dom.parentNode(inst.domNode);
1100
1173
  if (parent !== null) ctx.dom.removeChild(parent, inst.domNode);
1101
1174
  inst.dispose();
1175
+ removed.push(inst);
1102
1176
  }
1103
1177
  reorderDomMinimal(ctx, parentDom, oldInstances, newInstances);
1104
1178
  return { instances: newInstances, removed, built };
@@ -1224,6 +1298,176 @@ function patchExistingInstance(ctx, instance, newNode) {
1224
1298
  }
1225
1299
  }
1226
1300
 
1301
+ // ../renderer/src/transition.ts
1302
+ function getResolvedTransition(graph, nodeId) {
1303
+ const fn = graph.getHandler(`__transition__${nodeId}`);
1304
+ return fn === void 0 ? void 0 : fn();
1305
+ }
1306
+ function host() {
1307
+ return globalThis;
1308
+ }
1309
+ function nextFrame(cb) {
1310
+ const h = host();
1311
+ const raf = h.requestAnimationFrame;
1312
+ if (typeof raf === "function") {
1313
+ raf(() => raf(cb));
1314
+ } else {
1315
+ h.setTimeout(cb, 0);
1316
+ }
1317
+ }
1318
+ function splitClass(value) {
1319
+ if (value === null) return [];
1320
+ const out = [];
1321
+ for (const t of value.split(/\s+/)) if (t.length > 0) out.push(t);
1322
+ return out;
1323
+ }
1324
+ function addClasses(dom, el, classes2) {
1325
+ if (classes2.length === 0) return;
1326
+ const current = splitClass(dom.getAttribute(el, "class"));
1327
+ let changed = false;
1328
+ for (const c of classes2) {
1329
+ if (!current.includes(c)) {
1330
+ current.push(c);
1331
+ changed = true;
1332
+ }
1333
+ }
1334
+ if (changed) dom.setAttribute(el, "class", current.join(" "));
1335
+ }
1336
+ function removeClasses(dom, el, classes2) {
1337
+ if (classes2.length === 0) return;
1338
+ const current = splitClass(dom.getAttribute(el, "class"));
1339
+ const next = current.filter((c) => !classes2.includes(c));
1340
+ if (next.length !== current.length) {
1341
+ if (next.length === 0) dom.removeAttribute(el, "class");
1342
+ else dom.setAttribute(el, "class", next.join(" "));
1343
+ }
1344
+ }
1345
+ function startRun(dom, el, active, from, to, duration, onDone) {
1346
+ const h = host();
1347
+ let finished = false;
1348
+ let timer = null;
1349
+ const onEvent = (e) => {
1350
+ if (e.target !== el) return;
1351
+ finish();
1352
+ };
1353
+ const detach = () => {
1354
+ dom.removeEventListener(el, "transitionend", onEvent);
1355
+ dom.removeEventListener(el, "animationend", onEvent);
1356
+ if (timer !== null) {
1357
+ h.clearTimeout(timer);
1358
+ timer = null;
1359
+ }
1360
+ };
1361
+ const finish = () => {
1362
+ if (finished) return;
1363
+ finished = true;
1364
+ detach();
1365
+ removeClasses(dom, el, active);
1366
+ removeClasses(dom, el, to);
1367
+ removeClasses(dom, el, from);
1368
+ onDone();
1369
+ };
1370
+ addClasses(dom, el, from);
1371
+ addClasses(dom, el, active);
1372
+ dom.addEventListener(el, "transitionend", onEvent);
1373
+ dom.addEventListener(el, "animationend", onEvent);
1374
+ timer = h.setTimeout(finish, duration);
1375
+ nextFrame(() => {
1376
+ if (finished) return;
1377
+ removeClasses(dom, el, from);
1378
+ addClasses(dom, el, to);
1379
+ });
1380
+ return {
1381
+ cancel: () => {
1382
+ if (finished) return;
1383
+ finished = true;
1384
+ detach();
1385
+ removeClasses(dom, el, active);
1386
+ removeClasses(dom, el, to);
1387
+ removeClasses(dom, el, from);
1388
+ }
1389
+ };
1390
+ }
1391
+ var TransitionController = class {
1392
+ constructor(dom, graph, finalize) {
1393
+ this.dom = dom;
1394
+ this.graph = graph;
1395
+ this.finalize = finalize;
1396
+ }
1397
+ leaving = /* @__PURE__ */ new Map();
1398
+ /** True only in a real DOM environment (browser). */
1399
+ get browser() {
1400
+ return this.dom.body() !== null;
1401
+ }
1402
+ keyOf(inst) {
1403
+ return inst.graphNode.key ?? inst.graphNode.id;
1404
+ }
1405
+ resolved(node) {
1406
+ return getResolvedTransition(this.graph, node.id);
1407
+ }
1408
+ /** Run the enter animation for `inst` if it carries a transition (browser only). */
1409
+ enter(inst) {
1410
+ if (!this.browser) return;
1411
+ const rt = this.resolved(inst.graphNode);
1412
+ if (rt === void 0) return;
1413
+ const el = inst.domNode;
1414
+ if (!this.dom.isElement(el)) return;
1415
+ startRun(this.dom, el, rt.enterActive, rt.enterFrom, rt.enterTo, rt.duration, () => {
1416
+ });
1417
+ }
1418
+ /**
1419
+ * Play `appear` for any initial child that opted into it (fresh browser mount
1420
+ * only — hydration must never animate appear, §22, and this is called only on
1421
+ * the mount path).
1422
+ */
1423
+ appear(children) {
1424
+ if (!this.browser) return;
1425
+ for (const child of children) {
1426
+ const rt = this.resolved(child.graphNode);
1427
+ if (rt !== void 0 && rt.appear) this.enter(child);
1428
+ }
1429
+ }
1430
+ hooks() {
1431
+ return {
1432
+ takeLeaving: (key) => {
1433
+ const entry = this.leaving.get(key);
1434
+ if (entry === void 0) return void 0;
1435
+ entry.run.cancel();
1436
+ this.leaving.delete(key);
1437
+ return entry.inst;
1438
+ },
1439
+ beginLeave: (inst) => {
1440
+ if (!this.browser) return false;
1441
+ const rt = this.resolved(inst.graphNode);
1442
+ if (rt === void 0) return false;
1443
+ const el = inst.domNode;
1444
+ if (!this.dom.isElement(el)) return false;
1445
+ const key = this.keyOf(inst);
1446
+ const prior = this.leaving.get(key);
1447
+ if (prior !== void 0) prior.run.cancel();
1448
+ const run = startRun(
1449
+ this.dom,
1450
+ el,
1451
+ rt.leaveActive,
1452
+ rt.leaveFrom,
1453
+ rt.leaveTo,
1454
+ rt.duration,
1455
+ () => {
1456
+ const current = this.leaving.get(key);
1457
+ if (current !== void 0 && current.run === run) {
1458
+ this.leaving.delete(key);
1459
+ this.finalize(inst);
1460
+ }
1461
+ }
1462
+ );
1463
+ this.leaving.set(key, { inst, run });
1464
+ return true;
1465
+ },
1466
+ onEnter: (inst) => this.enter(inst)
1467
+ };
1468
+ }
1469
+ };
1470
+
1227
1471
  // ../renderer/src/mount.ts
1228
1472
  var SKIP_PROP_KEYS = /* @__PURE__ */ new Set([
1229
1473
  "text",
@@ -1376,7 +1620,7 @@ function mountNode(ctx, graphNode, parentDom) {
1376
1620
  instance2.addChild(childInstance);
1377
1621
  }
1378
1622
  dom.appendChild(parentDom, el2);
1379
- wireReactiveList(ctx, graphNode, instance2, el2);
1623
+ wireReactiveList(ctx, graphNode, instance2, el2, true);
1380
1624
  return instance2;
1381
1625
  }
1382
1626
  if (graphNode.type === "portal") {
@@ -1420,6 +1664,7 @@ function mountNode(ctx, graphNode, parentDom) {
1420
1664
  instance.addChild(childInstance);
1421
1665
  }
1422
1666
  dom.appendChild(parentDom, el);
1667
+ if (graphNode.type === "component") wireComponentBehavior(ctx, graphNode, instance);
1423
1668
  return instance;
1424
1669
  }
1425
1670
  function textUpdate(dom, el, textNode) {
@@ -1484,32 +1729,42 @@ function wireSignalBindings(ctx, graphNode, instance, onUpdate) {
1484
1729
  instance.trackCleanup(unsub);
1485
1730
  }
1486
1731
  }
1487
- function wireReactiveList(ctx, graphNode, instance, el) {
1732
+ function wireReactiveList(ctx, graphNode, instance, el, runAppear = false) {
1488
1733
  const plan = ctx.graph.getHandler(`__listplan__${graphNode.id}`);
1489
1734
  const build = ctx.graph.getHandler(`__listbuild__${graphNode.id}`);
1490
1735
  if (plan === void 0 && build === void 0) return;
1736
+ const controller = new TransitionController(ctx.dom, ctx.graph, (leaving) => {
1737
+ const parent = ctx.dom.parentNode(leaving.domNode);
1738
+ if (parent !== null) ctx.dom.removeChild(parent, leaving.domNode);
1739
+ leaving.dispose();
1740
+ forgetInstance(ctx, leaving);
1741
+ ctx.graph.detachNode(leaving.graphNode);
1742
+ });
1743
+ const hooks = controller.hooks();
1744
+ if (runAppear) controller.appear(instance.children);
1491
1745
  for (const stateRef of graphNode.stateRefs) {
1492
1746
  if (stateRef.propKey !== "items") continue;
1493
1747
  const sig = ctx.graph.getHandler(`__signal__${stateRef.signalId}`);
1494
1748
  if (sig === void 0 || typeof sig.subscribe !== "function") continue;
1495
1749
  const unsub = sig.subscribe((value) => {
1496
1750
  if (plan !== void 0) {
1497
- reconcileReactiveListByPlan(ctx, graphNode, instance, el, plan(value));
1751
+ reconcileReactiveListByPlan(ctx, graphNode, instance, el, plan(value), hooks);
1498
1752
  } else {
1499
- reconcileReactiveList(ctx, graphNode, instance, el, build(value));
1753
+ reconcileReactiveList(ctx, graphNode, instance, el, build(value), hooks);
1500
1754
  }
1501
1755
  });
1502
1756
  instance.trackCleanup(unsub);
1503
1757
  }
1504
1758
  }
1505
- function reconcileReactiveListByPlan(ctx, listNode, listInstance, listEl, plan) {
1759
+ function reconcileReactiveListByPlan(ctx, listNode, listInstance, listEl, plan, hooks) {
1506
1760
  const oldInstances = [...listInstance.children];
1507
1761
  const result = reconcileChildrenByPlan(
1508
1762
  ctx,
1509
1763
  listEl,
1510
1764
  oldInstances,
1511
1765
  plan,
1512
- (node, parent) => mountNode(ctx, node, parent)
1766
+ (node, parent) => mountNode(ctx, node, parent),
1767
+ hooks
1513
1768
  );
1514
1769
  listInstance.children.length = 0;
1515
1770
  for (const inst of result.instances) listInstance.children.push(inst);
@@ -1524,14 +1779,15 @@ function reconcileReactiveListByPlan(ctx, listNode, listInstance, listEl, plan)
1524
1779
  for (const child of [...listNode.children]) listNode.removeChild(child);
1525
1780
  for (const inst of result.instances) listNode.appendChild(inst.graphNode);
1526
1781
  }
1527
- function reconcileReactiveList(ctx, listNode, listInstance, listEl, newNodes) {
1782
+ function reconcileReactiveList(ctx, listNode, listInstance, listEl, newNodes, hooks) {
1528
1783
  const oldInstances = [...listInstance.children];
1529
1784
  const result = reconcileChildren(
1530
1785
  ctx,
1531
1786
  listEl,
1532
1787
  oldInstances,
1533
1788
  newNodes,
1534
- (node, parent) => mountNode(ctx, node, parent)
1789
+ (node, parent) => mountNode(ctx, node, parent),
1790
+ hooks
1535
1791
  );
1536
1792
  listInstance.children.length = 0;
1537
1793
  for (const inst of result.instances) listInstance.children.push(inst);
@@ -1567,11 +1823,14 @@ function wireOverlayBehavior(ctx, graphNode, instance, target) {
1567
1823
  const onOpenChange = (isOpen) => {
1568
1824
  if (isOpen) {
1569
1825
  if (desc.restoreFocus) saved = saveFocus(dom);
1570
- if (desc.takesFocus) focusInitial(dom, target, desc.initialFocusId);
1571
1826
  if (desc.modal) {
1572
1827
  active.push(trapFocus(dom, target));
1573
1828
  active.push(containFocus(dom, target));
1574
1829
  }
1830
+ if (desc.menu) {
1831
+ active.push(rovingMenu(dom, target));
1832
+ }
1833
+ if (desc.takesFocus) focusInitial(dom, target, desc.initialFocusId);
1575
1834
  if (desc.closeOnEscape && desc.onClose !== void 0) {
1576
1835
  active.push(onEscape(dom, target, desc.onClose));
1577
1836
  }
@@ -1588,6 +1847,11 @@ function wireOverlayBehavior(ctx, graphNode, instance, target) {
1588
1847
  instance.trackCleanup(teardown);
1589
1848
  if (openSig.peek() === true) onOpenChange(true);
1590
1849
  }
1850
+ function wireComponentBehavior(ctx, graphNode, instance) {
1851
+ const fn = ctx.graph.getHandler(`__component__${graphNode.id}`);
1852
+ if (fn === void 0) return;
1853
+ for (const cleanup of fn()) instance.trackCleanup(cleanup);
1854
+ }
1591
1855
 
1592
1856
  // ../renderer/src/hydration-diagnostics.ts
1593
1857
  function formatHydrationDiagnostic(d) {
@@ -1712,6 +1976,7 @@ function hydrateNode(ctx, graphNode, domNode, path) {
1712
1976
  return instance;
1713
1977
  }
1714
1978
  hydrateChildren(ctx, graphNode, instance, domNode, path);
1979
+ if (graphNode.type === "component") wireComponentBehavior(ctx, graphNode, instance);
1715
1980
  return instance;
1716
1981
  }
1717
1982
  }
@@ -1910,7 +2175,8 @@ function analyzeGraph(graph) {
1910
2175
  const isList = node.type === "reactive-list";
1911
2176
  const isConditional = node.type === "conditional";
1912
2177
  const isPortal = node.type === "portal";
1913
- const isStatic = node.stateRefs.length === 0 && !hasEvents && !isList && !isConditional && !isPortal;
2178
+ const isComponent = node.type === "component";
2179
+ const isStatic = node.stateRefs.length === 0 && !hasEvents && !isList && !isConditional && !isPortal && !isComponent;
1914
2180
  const isStaticSubtree = isStatic && allChildrenStatic;
1915
2181
  nodes.set(node.id, {
1916
2182
  isStatic,
@@ -2241,6 +2507,37 @@ async function waitFor(check, options = {}) {
2241
2507
  await new Promise((resolve) => setTimeout(resolve, interval));
2242
2508
  }
2243
2509
  }
2510
+ function focus(el) {
2511
+ el.focus?.();
2512
+ }
2513
+ function blur(el) {
2514
+ el.blur?.();
2515
+ }
2516
+ function pressKey(key, el = document.activeElement, init = {}) {
2517
+ if (el === null) return;
2518
+ el.dispatchEvent(
2519
+ new KeyboardEvent("keydown", { key, bubbles: true, cancelable: true, ...init })
2520
+ );
2521
+ }
2522
+ function clickOutside(container, target = document.body) {
2523
+ if (container.contains(target)) {
2524
+ throw new Error("[StreetUI Testing] clickOutside: target is inside the container");
2525
+ }
2526
+ target.dispatchEvent(new MouseEvent("mousedown", { bubbles: true, cancelable: true }));
2527
+ target.dispatchEvent(new MouseEvent("click", { bubbles: true, cancelable: true }));
2528
+ }
2529
+ async function openOverlay(open) {
2530
+ open.set(true);
2531
+ await flushUpdates();
2532
+ }
2533
+ async function closeOverlay(open) {
2534
+ open.set(false);
2535
+ await flushUpdates();
2536
+ }
2537
+ async function waitForTransition(el) {
2538
+ el.dispatchEvent(new Event("transitionend", { bubbles: true }));
2539
+ await flushUpdates();
2540
+ }
2244
2541
  function findByText(container, text) {
2245
2542
  const match = Array.from(container.querySelectorAll("*")).find(
2246
2543
  (el) => el.children.length === 0 && (el.textContent?.includes(text) ?? false)
@@ -2335,21 +2632,1161 @@ function renderServerThenHydrate(build, options = {}) {
2335
2632
  };
2336
2633
  }
2337
2634
 
2635
+ // ../dsl/src/transition.ts
2636
+ function classes(value) {
2637
+ if (value === void 0) return [];
2638
+ const out = [];
2639
+ for (const token of value.split(/\s+/)) {
2640
+ if (token.length > 0 && !out.includes(token)) out.push(token);
2641
+ }
2642
+ return out;
2643
+ }
2644
+ function merge(a, b) {
2645
+ const out = [...a];
2646
+ for (const token of b) if (!out.includes(token)) out.push(token);
2647
+ return out;
2648
+ }
2649
+ function resolveTransition(config) {
2650
+ const n = config.name;
2651
+ const enterActive = merge(
2652
+ classes(config.enter),
2653
+ classes(config.enterActive ?? (n !== void 0 ? `${n}-enter-active` : void 0))
2654
+ );
2655
+ const leaveActive = merge(
2656
+ classes(config.leave),
2657
+ classes(config.leaveActive ?? (n !== void 0 ? `${n}-leave-active` : void 0))
2658
+ );
2659
+ return {
2660
+ enterActive,
2661
+ enterFrom: classes(config.enterFrom ?? (n !== void 0 ? `${n}-enter-from` : void 0)),
2662
+ enterTo: classes(config.enterTo ?? (n !== void 0 ? `${n}-enter-to` : void 0)),
2663
+ leaveActive,
2664
+ leaveFrom: classes(config.leaveFrom ?? (n !== void 0 ? `${n}-leave-from` : void 0)),
2665
+ leaveTo: classes(config.leaveTo ?? (n !== void 0 ? `${n}-leave-to` : void 0)),
2666
+ appear: config.appear ?? false,
2667
+ duration: config.duration ?? 1e3
2668
+ };
2669
+ }
2670
+
2671
+ // ../state/src/signal.ts
2672
+ var _activeConsumer = null;
2673
+ function withConsumer(consumer, fn) {
2674
+ const prev = _activeConsumer;
2675
+ _activeConsumer = consumer;
2676
+ try {
2677
+ return fn();
2678
+ } finally {
2679
+ _activeConsumer = prev;
2680
+ }
2681
+ }
2682
+ var _batchDepth = 0;
2683
+ var _pendingFlushes = /* @__PURE__ */ new Map();
2684
+ function _enqueueBatchFlush(sig, value) {
2685
+ _pendingFlushes.set(sig, { signal: sig, value });
2686
+ }
2687
+ var Signal = class {
2688
+ _value;
2689
+ _subscribers = /* @__PURE__ */ new Set();
2690
+ _consumers = /* @__PURE__ */ new Set();
2691
+ constructor(initial) {
2692
+ this._value = initial;
2693
+ }
2694
+ get() {
2695
+ if (_activeConsumer !== null) {
2696
+ this._consumers.add(_activeConsumer);
2697
+ _activeConsumer._addSource(this);
2698
+ }
2699
+ return this._value;
2700
+ }
2701
+ peek() {
2702
+ return this._value;
2703
+ }
2704
+ set(value) {
2705
+ if (Object.is(this._value, value)) return;
2706
+ this._value = value;
2707
+ if (_batchDepth > 0) {
2708
+ _enqueueBatchFlush(this, value);
2709
+ } else {
2710
+ this._flush(value);
2711
+ }
2712
+ }
2713
+ update(fn) {
2714
+ this.set(fn(this._value));
2715
+ }
2716
+ subscribe(fn) {
2717
+ this._subscribers.add(fn);
2718
+ return () => {
2719
+ this._subscribers.delete(fn);
2720
+ };
2721
+ }
2722
+ _removeConsumer(consumer) {
2723
+ this._consumers.delete(consumer);
2724
+ }
2725
+ /**
2726
+ * Called by the batch machinery after the batch has completed.
2727
+ * Notifies subscribers with the final coalesced value.
2728
+ */
2729
+ _flushBatch(value) {
2730
+ this._flush(value);
2731
+ }
2732
+ _flush(value) {
2733
+ for (const sub of [...this._subscribers]) sub(value);
2734
+ for (const consumer of [...this._consumers]) consumer._invalidate();
2735
+ }
2736
+ /**
2737
+ * @internal DevTools inspection only. The number of live observers
2738
+ * (direct subscribers plus derived/effect consumers). Read-only; never
2739
+ * mutates reactive state.
2740
+ */
2741
+ _observerCount() {
2742
+ return this._subscribers.size + this._consumers.size;
2743
+ }
2744
+ };
2745
+ var DerivedSignal = class {
2746
+ _value = void 0;
2747
+ _dirty = true;
2748
+ _disposed = false;
2749
+ _fn;
2750
+ _subscribers = /* @__PURE__ */ new Set();
2751
+ /** All upstream sources this derived currently reads from. */
2752
+ _sources = /* @__PURE__ */ new Set();
2753
+ /** Downstream consumers that depend on this derived. */
2754
+ _consumers = /* @__PURE__ */ new Set();
2755
+ constructor(fn) {
2756
+ this._fn = fn;
2757
+ }
2758
+ get() {
2759
+ if (_activeConsumer !== null) {
2760
+ this._consumers.add(_activeConsumer);
2761
+ _activeConsumer._addSource(this);
2762
+ }
2763
+ if (this._dirty) this._recompute();
2764
+ return this._value;
2765
+ }
2766
+ peek() {
2767
+ if (this._dirty) this._recompute();
2768
+ return this._value;
2769
+ }
2770
+ subscribe(fn) {
2771
+ if (this._dirty) this._recompute();
2772
+ this._subscribers.add(fn);
2773
+ return () => {
2774
+ this._subscribers.delete(fn);
2775
+ };
2776
+ }
2777
+ _addSource(src) {
2778
+ this._sources.add(src);
2779
+ }
2780
+ _removeConsumer(consumer) {
2781
+ this._consumers.delete(consumer);
2782
+ }
2783
+ _invalidate() {
2784
+ if (this._disposed) return;
2785
+ this._dirty = true;
2786
+ const newVal = this.peek();
2787
+ for (const sub of [...this._subscribers]) sub(newVal);
2788
+ for (const consumer of [...this._consumers]) consumer._invalidate();
2789
+ }
2790
+ _recompute() {
2791
+ for (const src of this._sources) src._removeConsumer(this);
2792
+ this._sources.clear();
2793
+ this._value = withConsumer(this, this._fn);
2794
+ this._dirty = false;
2795
+ }
2796
+ dispose() {
2797
+ this._disposed = true;
2798
+ for (const src of this._sources) src._removeConsumer(this);
2799
+ this._sources.clear();
2800
+ this._subscribers.clear();
2801
+ this._consumers.clear();
2802
+ }
2803
+ /**
2804
+ * @internal DevTools inspection only. Live observers (subscribers plus
2805
+ * downstream consumers). Read-only.
2806
+ */
2807
+ _observerCount() {
2808
+ return this._subscribers.size + this._consumers.size;
2809
+ }
2810
+ };
2811
+ var Effect = class {
2812
+ _fn;
2813
+ _cleanup = void 0;
2814
+ _disposed = false;
2815
+ _sources = /* @__PURE__ */ new Set();
2816
+ constructor(fn) {
2817
+ this._fn = fn;
2818
+ this._run();
2819
+ }
2820
+ _addSource(src) {
2821
+ this._sources.add(src);
2822
+ }
2823
+ _invalidate() {
2824
+ if (this._disposed) return;
2825
+ this._run();
2826
+ }
2827
+ _run() {
2828
+ for (const src of this._sources) src._removeConsumer(this);
2829
+ this._sources.clear();
2830
+ if (typeof this._cleanup === "function") this._cleanup();
2831
+ const result = withConsumer(this, this._fn);
2832
+ this._cleanup = typeof result === "function" ? result : void 0;
2833
+ }
2834
+ dispose() {
2835
+ this._disposed = true;
2836
+ for (const src of this._sources) src._removeConsumer(this);
2837
+ this._sources.clear();
2838
+ if (typeof this._cleanup === "function") this._cleanup();
2839
+ this._cleanup = void 0;
2840
+ }
2841
+ };
2842
+ function signal(initial) {
2843
+ return new Signal(initial);
2844
+ }
2845
+ function derived(fn) {
2846
+ return new DerivedSignal(fn);
2847
+ }
2848
+ function effect(fn) {
2849
+ const e = new Effect(fn);
2850
+ return () => e.dispose();
2851
+ }
2852
+
2853
+ // ../dsl/src/builders.ts
2854
+ function isSignal(v) {
2855
+ return v !== null && typeof v === "object" && typeof v["get"] === "function" && typeof v["subscribe"] === "function";
2856
+ }
2857
+ function bindValue(graph, node, propKey, value) {
2858
+ if (isSignal(value)) {
2859
+ const signalId = `${node.id}:${propKey}`;
2860
+ node.stateRefs.push({ signalId, propKey });
2861
+ graph.registerHandler(`__signal__${signalId}`, value);
2862
+ return value.peek();
2863
+ }
2864
+ return value;
2865
+ }
2866
+ function applyA11yProps(props, options) {
2867
+ if (options.role !== void 0) props["role"] = options.role;
2868
+ if (options.tabIndex !== void 0) props["tabindex"] = String(options.tabIndex);
2869
+ if (options.ariaLabel !== void 0) props["aria-label"] = options.ariaLabel;
2870
+ if (options.ariaLabelledBy !== void 0) props["aria-labelledby"] = options.ariaLabelledBy;
2871
+ if (options.ariaDescribedBy !== void 0) props["aria-describedby"] = options.ariaDescribedBy;
2872
+ if (options.ariaExpanded !== void 0) props["aria-expanded"] = String(options.ariaExpanded);
2873
+ if (options.ariaControls !== void 0) props["aria-controls"] = options.ariaControls;
2874
+ if (options.ariaHidden !== void 0) props["aria-hidden"] = String(options.ariaHidden);
2875
+ if (options.ariaLive !== void 0) props["aria-live"] = options.ariaLive;
2876
+ if (options.ariaCurrent !== void 0) props["aria-current"] = String(options.ariaCurrent);
2877
+ if (options.ariaInvalid !== void 0) props["aria-invalid"] = String(options.ariaInvalid);
2878
+ if (options.ariaRequired !== void 0) props["aria-required"] = String(options.ariaRequired);
2879
+ if (options.ariaModal !== void 0) props["aria-modal"] = String(options.ariaModal);
2880
+ if (options.ariaOwns !== void 0) props["aria-owns"] = options.ariaOwns;
2881
+ if (options.ariaActiveDescendant !== void 0) props["aria-activedescendant"] = options.ariaActiveDescendant;
2882
+ if (options.ariaHasPopup !== void 0) props["aria-haspopup"] = String(options.ariaHasPopup);
2883
+ if (options.ariaSelected !== void 0) props["aria-selected"] = String(options.ariaSelected);
2884
+ }
2885
+ function registerTransition(graph, node, config) {
2886
+ if (config === void 0) return;
2887
+ const resolved = resolveTransition(config);
2888
+ graph.registerHandler(
2889
+ `__transition__${node.id}`,
2890
+ () => resolved
2891
+ );
2892
+ }
2893
+ function containerProps(options) {
2894
+ const props = {};
2895
+ if (options.class !== void 0) props["class"] = options.class;
2896
+ if (options.id !== void 0) props["id"] = options.id;
2897
+ if (options.key !== void 0) props["key"] = options.key;
2898
+ applyA11yProps(props, options);
2899
+ return props;
2900
+ }
2901
+ function itemIdentity(item, index) {
2902
+ if (item !== null && typeof item === "object") {
2903
+ const obj = item;
2904
+ if ("id" in obj) return `id:${String(obj["id"])}`;
2905
+ if ("key" in obj) return `key:${String(obj["key"])}`;
2906
+ return `idx:${index}`;
2907
+ }
2908
+ return `val:${String(item)}`;
2909
+ }
2910
+ function itemValueSignature(item) {
2911
+ try {
2912
+ return JSON.stringify(item) ?? String(item);
2913
+ } catch {
2914
+ return String(item);
2915
+ }
2916
+ }
2917
+ function reactiveListItemSignature(item) {
2918
+ return itemValueSignature(item);
2919
+ }
2920
+ function reactiveListItemKey(item, index) {
2921
+ return itemIdentity(item, index);
2922
+ }
2923
+ var OVERLAY_KINDS = {
2924
+ dialog: {
2925
+ role: "dialog",
2926
+ modal: true,
2927
+ takesFocus: true,
2928
+ ariaModal: true,
2929
+ defaultCloseOnEscape: true,
2930
+ defaultRestoreFocus: true
2931
+ },
2932
+ popover: {
2933
+ role: "dialog",
2934
+ modal: false,
2935
+ takesFocus: true,
2936
+ ariaModal: false,
2937
+ defaultCloseOnEscape: true,
2938
+ defaultRestoreFocus: true
2939
+ },
2940
+ tooltip: {
2941
+ role: "tooltip",
2942
+ modal: false,
2943
+ takesFocus: false,
2944
+ ariaModal: false,
2945
+ defaultCloseOnEscape: false,
2946
+ defaultRestoreFocus: false
2947
+ },
2948
+ dropdown: {
2949
+ role: "menu",
2950
+ modal: false,
2951
+ takesFocus: true,
2952
+ menu: true,
2953
+ ariaModal: false,
2954
+ defaultCloseOnEscape: true,
2955
+ defaultRestoreFocus: true
2956
+ },
2957
+ toast: {
2958
+ role: "status",
2959
+ modal: false,
2960
+ takesFocus: false,
2961
+ ariaModal: false,
2962
+ ariaLive: "polite",
2963
+ defaultCloseOnEscape: false,
2964
+ defaultRestoreFocus: false
2965
+ }
2966
+ };
2967
+ var ContentBuilderBase = class {
2968
+ constructor(_node, _graph) {
2969
+ this._node = _node;
2970
+ this._graph = _graph;
2971
+ }
2972
+ heading(text, options = {}) {
2973
+ const props = { level: options.level ?? 1 };
2974
+ if (options.class !== void 0) props["class"] = options.class;
2975
+ if (options.id !== void 0) props["id"] = options.id;
2976
+ applyA11yProps(props, options);
2977
+ const node = this._graph.createNode("heading", { parent: this._node, props });
2978
+ const resolved = bindValue(this._graph, node, "text", text);
2979
+ node.setProp("text", resolved);
2980
+ }
2981
+ text(content, options = {}) {
2982
+ const props = {};
2983
+ if (options.class !== void 0) props["class"] = options.class;
2984
+ if (options.id !== void 0) props["id"] = options.id;
2985
+ applyA11yProps(props, options);
2986
+ const node = this._graph.createNode("text", { parent: this._node, props });
2987
+ const resolved = bindValue(this._graph, node, "text", content);
2988
+ node.setProp("text", resolved);
2989
+ }
2990
+ button(label, options = {}) {
2991
+ const props = {};
2992
+ if (options.class !== void 0) props["class"] = options.class;
2993
+ if (options.id !== void 0) props["id"] = options.id;
2994
+ applyA11yProps(props, options);
2995
+ const node = this._graph.createNode("button", { parent: this._node, props });
2996
+ const resolved = bindValue(this._graph, node, "label", label);
2997
+ node.setProp("label", resolved);
2998
+ if (options.disabled !== void 0) {
2999
+ const resolvedDisabled = bindValue(this._graph, node, "disabled", options.disabled);
3000
+ node.setProp("disabled", resolvedDisabled);
3001
+ }
3002
+ if (options.onClick !== void 0) {
3003
+ const handlerKey = `click:${node.id}`;
3004
+ this._graph.registerHandler(handlerKey, options.onClick);
3005
+ node.addEvent({ type: "click", handlerKey });
3006
+ }
3007
+ }
3008
+ input(options = {}) {
3009
+ const props = {};
3010
+ props["inputType"] = options.type ?? "text";
3011
+ if (options.placeholder !== void 0) props["placeholder"] = options.placeholder;
3012
+ if (options.class !== void 0) props["class"] = options.class;
3013
+ if (options.id !== void 0) props["id"] = options.id;
3014
+ applyA11yProps(props, options);
3015
+ const nodeOpts = {
3016
+ props,
3017
+ parent: this._node
3018
+ };
3019
+ if (options.id !== void 0) nodeOpts.key = options.id;
3020
+ const node = this._graph.createNode("input", nodeOpts);
3021
+ const bindSignal = options.bind;
3022
+ const valueBindable = bindSignal !== void 0 ? bindSignal : options.value;
3023
+ const inputHandler = bindSignal !== void 0 ? (v) => bindSignal.set(v) : options.onInput;
3024
+ if (valueBindable !== void 0) {
3025
+ const resolved = bindValue(this._graph, node, "value", valueBindable);
3026
+ node.setProp("value", resolved);
3027
+ }
3028
+ if (options.disabled !== void 0) {
3029
+ const resolved = bindValue(this._graph, node, "disabled", options.disabled);
3030
+ node.setProp("disabled", resolved);
3031
+ }
3032
+ if (inputHandler !== void 0) {
3033
+ const handlerKey = `input:${node.id}`;
3034
+ this._graph.registerHandler(handlerKey, inputHandler);
3035
+ node.addEvent({ type: "input", handlerKey });
3036
+ }
3037
+ if (options.onChange !== void 0) {
3038
+ const handlerKey = `change:${node.id}`;
3039
+ this._graph.registerHandler(handlerKey, options.onChange);
3040
+ node.addEvent({ type: "change", handlerKey });
3041
+ }
3042
+ }
3043
+ image(options) {
3044
+ const props = {
3045
+ src: options.src,
3046
+ alt: options.alt
3047
+ };
3048
+ if (options.width !== void 0) props["width"] = options.width;
3049
+ if (options.height !== void 0) props["height"] = options.height;
3050
+ if (options.class !== void 0) props["class"] = options.class;
3051
+ if (options.id !== void 0) props["id"] = options.id;
3052
+ applyA11yProps(props, options);
3053
+ const nodeOpts = {
3054
+ props,
3055
+ parent: this._node
3056
+ };
3057
+ if (options.id !== void 0) nodeOpts.key = options.id;
3058
+ this._graph.createNode("image", nodeOpts);
3059
+ }
3060
+ link(label, options) {
3061
+ const props = {
3062
+ href: options.href,
3063
+ external: options.external ?? false
3064
+ };
3065
+ if (options.class !== void 0) props["class"] = options.class;
3066
+ if (options.id !== void 0) props["id"] = options.id;
3067
+ applyA11yProps(props, options);
3068
+ const node = this._graph.createNode("link", { parent: this._node, props });
3069
+ const resolved = bindValue(this._graph, node, "label", label);
3070
+ node.setProp("label", resolved);
3071
+ if (options.onClick !== void 0) {
3072
+ const handlerKey = `click:${node.id}`;
3073
+ this._graph.registerHandler(handlerKey, options.onClick);
3074
+ node.addEvent({ type: "click", handlerKey });
3075
+ }
3076
+ }
3077
+ };
3078
+ var ContainerBuilderBase = class extends ContentBuilderBase {
3079
+ section(key, builder, options = {}) {
3080
+ const node = this._graph.createNode("section", {
3081
+ key,
3082
+ parent: this._node,
3083
+ props: containerProps(options)
3084
+ });
3085
+ registerTransition(this._graph, node, options.transition);
3086
+ builder(new SectionBuilderImpl(node, this._graph));
3087
+ }
3088
+ container(key, builder, options = {}) {
3089
+ const node = this._graph.createNode("container", {
3090
+ key,
3091
+ parent: this._node,
3092
+ props: containerProps(options)
3093
+ });
3094
+ registerTransition(this._graph, node, options.transition);
3095
+ builder(new ContainerBuilderImpl(node, this._graph));
3096
+ }
3097
+ list(key, builder, options = {}) {
3098
+ const node = this._graph.createNode("list", {
3099
+ key,
3100
+ parent: this._node,
3101
+ props: containerProps(options)
3102
+ });
3103
+ registerTransition(this._graph, node, options.transition);
3104
+ builder(new ListBuilderImpl(node, this._graph));
3105
+ }
3106
+ listOf(key, items, renderItem, options = {}) {
3107
+ const graph = this._graph;
3108
+ const node = graph.createNode("reactive-list", {
3109
+ key,
3110
+ parent: this._node,
3111
+ props: containerProps(options)
3112
+ });
3113
+ registerTransition(graph, node, options.transition);
3114
+ const signalId = `${node.id}:items`;
3115
+ node.stateRefs.push({ signalId, propKey: "items" });
3116
+ graph.registerHandler(`__signal__${signalId}`, items);
3117
+ const buildItem = (item, index) => {
3118
+ const itemKey = reactiveListItemKey(item, index);
3119
+ const itemNode = graph.createNode("list-item", {
3120
+ key: itemKey,
3121
+ // `_item` records the source item *reference* so the reconciler can
3122
+ // short-circuit unchanged rows by identity (no signature hashing); `_sig`
3123
+ // is the content signature used to detect an in-place data change when the
3124
+ // reference differs. Both are internal metadata (leading `_`) and never
3125
+ // reach the DOM.
3126
+ props: {
3127
+ key: itemKey,
3128
+ _sig: reactiveListItemSignature(item),
3129
+ // The item reference is stored as opaque internal metadata (never
3130
+ // rendered); cast through `unknown` since `T` is not a `PropValue`.
3131
+ _item: item
3132
+ }
3133
+ });
3134
+ renderItem(item, index, new ContainerBuilderImpl(itemNode, graph));
3135
+ registerTransition(graph, itemNode, options.itemTransition);
3136
+ return itemNode;
3137
+ };
3138
+ const buildPlan = (raw) => {
3139
+ const arr = Array.isArray(raw) ? raw : [];
3140
+ const plan = new Array(arr.length);
3141
+ for (let i = 0; i < arr.length; i++) {
3142
+ const item = arr[i];
3143
+ const index = i;
3144
+ plan[i] = {
3145
+ key: reactiveListItemKey(item, index),
3146
+ item,
3147
+ sig: () => reactiveListItemSignature(item),
3148
+ build: () => buildItem(item, index)
3149
+ };
3150
+ }
3151
+ return plan;
3152
+ };
3153
+ graph.registerHandler(`__listplan__${node.id}`, buildPlan);
3154
+ const current = isSignal(items) ? items.peek() : items;
3155
+ const initial = Array.isArray(current) ? current : [];
3156
+ initial.forEach((item, i) => {
3157
+ node.appendChild(buildItem(item, i));
3158
+ });
3159
+ }
3160
+ form(key, builder, options = {}) {
3161
+ const props = containerProps(options);
3162
+ const node = this._graph.createNode("form", {
3163
+ key,
3164
+ parent: this._node,
3165
+ props
3166
+ });
3167
+ registerTransition(this._graph, node, options.transition);
3168
+ if (options.onSubmit !== void 0) {
3169
+ const handlerKey = `submit:${node.id}`;
3170
+ this._graph.registerHandler(handlerKey, options.onSubmit);
3171
+ node.addEvent({ type: "submit", handlerKey });
3172
+ }
3173
+ builder(new FormBuilderImpl(node, this._graph));
3174
+ }
3175
+ when(condition, builder, elseBuilder, options = {}) {
3176
+ const graph = this._graph;
3177
+ const node = graph.createNode("conditional", {
3178
+ parent: this._node,
3179
+ props: containerProps({})
3180
+ });
3181
+ const branchTransition = options.transition !== void 0 && options.appear === true ? { ...options.transition, appear: true } : options.transition;
3182
+ const buildBranch = (build, tag) => {
3183
+ const branchKey = `when-${tag}:${node.id}`;
3184
+ const branch = graph.createNode("container", {
3185
+ key: branchKey,
3186
+ props: { key: branchKey }
3187
+ });
3188
+ registerTransition(graph, branch, branchTransition);
3189
+ build(new ContainerBuilderImpl(branch, graph));
3190
+ return branch;
3191
+ };
3192
+ const buildAll = (raw) => {
3193
+ if (raw) return [buildBranch(builder, "then")];
3194
+ return elseBuilder !== void 0 ? [buildBranch(elseBuilder, "else")] : [];
3195
+ };
3196
+ if (isSignal(condition)) {
3197
+ const signalId = `${node.id}:items`;
3198
+ node.stateRefs.push({ signalId, propKey: "items" });
3199
+ graph.registerHandler(`__signal__${signalId}`, condition);
3200
+ graph.registerHandler(`__listbuild__${node.id}`, buildAll);
3201
+ const current = condition.peek();
3202
+ for (const child of buildAll(current)) node.appendChild(child);
3203
+ } else {
3204
+ for (const child of buildAll(condition)) node.appendChild(child);
3205
+ }
3206
+ }
3207
+ errorBoundary(id, builder, options) {
3208
+ const sources = options.source === void 0 ? [] : Array.isArray(options.source) ? [...options.source] : [options.source];
3209
+ const localError = signal(void 0);
3210
+ const retryNonce = signal(0);
3211
+ const readError = () => {
3212
+ const local = localError.peek();
3213
+ if (local !== void 0 && local !== null) return local;
3214
+ for (const s of sources) {
3215
+ const e = s.peek();
3216
+ if (e !== void 0 && e !== null) return e;
3217
+ }
3218
+ return void 0;
3219
+ };
3220
+ const hasError = derived(() => {
3221
+ retryNonce.get();
3222
+ localError.get();
3223
+ for (const s of sources) s.get();
3224
+ return readError() !== void 0;
3225
+ });
3226
+ const retry = () => {
3227
+ localError.set(void 0);
3228
+ options.onRetry?.();
3229
+ retryNonce.update((n) => n + 1);
3230
+ };
3231
+ this.container(id, (c) => {
3232
+ c.when(
3233
+ hasError,
3234
+ // Error state → fallback.
3235
+ (fb) => options.fallback(fb, readError(), retry),
3236
+ // Healthy state → body, guarded against synchronous build throws.
3237
+ (body) => {
3238
+ try {
3239
+ builder(body);
3240
+ } catch (err) {
3241
+ queueMicrotask(() => localError.set(err));
3242
+ }
3243
+ }
3244
+ );
3245
+ }, { id });
3246
+ }
3247
+ // ── Portals & overlays ──────────────────────────────────────────────────────
3248
+ portal(key, builder, options = {}) {
3249
+ const node = this._graph.createNode("portal", {
3250
+ key,
3251
+ parent: this._node,
3252
+ props: containerProps(options)
3253
+ });
3254
+ builder(new ContainerBuilderImpl(node, this._graph));
3255
+ }
3256
+ /**
3257
+ * Shared assembly for every overlay kind: a `portal` node whose single child
3258
+ * is a `when(open, panel)` conditional. The panel container carries the
3259
+ * kind's ARIA semantics; `builder` fills it. An `__overlay__<portalId>`
3260
+ * descriptor is registered so the renderer wires focus/keyboard behavior to
3261
+ * the same `open` signal that drives the panel. Reuses existing primitives
3262
+ * (portal + when + container) — no new render path.
3263
+ */
3264
+ _overlay(kind, key, options, builder) {
3265
+ const graph = this._graph;
3266
+ const portalNode = graph.createNode("portal", {
3267
+ key,
3268
+ parent: this._node,
3269
+ props: { key }
3270
+ });
3271
+ const openBindable = options.open;
3272
+ const openSignal = isSignal(openBindable) ? openBindable : signal(openBindable);
3273
+ const panelOptions = {
3274
+ role: options.role ?? kind.role,
3275
+ ...kind.ariaModal ? { ariaModal: true } : {},
3276
+ ...kind.ariaLive !== void 0 ? { ariaLive: kind.ariaLive } : {},
3277
+ ...options.class !== void 0 ? { class: options.class } : {},
3278
+ ...options.ariaLabel !== void 0 ? { ariaLabel: options.ariaLabel } : {},
3279
+ ...options.ariaLabelledBy !== void 0 ? { ariaLabelledBy: options.ariaLabelledBy } : {},
3280
+ ...options.ariaDescribedBy !== void 0 ? { ariaDescribedBy: options.ariaDescribedBy } : {}
3281
+ };
3282
+ const portalBuilder = new ContainerBuilderImpl(portalNode, graph);
3283
+ portalBuilder.when(
3284
+ openSignal,
3285
+ (panelHost) => {
3286
+ panelHost.container(`${key}__panel`, builder, panelOptions);
3287
+ },
3288
+ void 0,
3289
+ // Overlay open/close rides the panel's `when`; a transition animates the
3290
+ // panel in on open and — via the reconciler's deferred-leave — plays the
3291
+ // leave before the panel is removed (§10). Focus is restored at close-
3292
+ // request time (see wireOverlayBehavior), so it never stays trapped inside
3293
+ // a panel that is animating away.
3294
+ options.transition !== void 0 ? { transition: options.transition } : {}
3295
+ );
3296
+ const descriptor = {
3297
+ open: openSignal,
3298
+ modal: kind.modal,
3299
+ takesFocus: kind.takesFocus,
3300
+ menu: kind.menu ?? false,
3301
+ closeOnEscape: options.closeOnEscape ?? kind.defaultCloseOnEscape,
3302
+ restoreFocus: options.restoreFocus ?? kind.defaultRestoreFocus,
3303
+ ...options.initialFocusId !== void 0 ? { initialFocusId: options.initialFocusId } : {},
3304
+ ...options.onClose !== void 0 ? { onClose: options.onClose } : {}
3305
+ };
3306
+ graph.registerHandler(
3307
+ `__overlay__${portalNode.id}`,
3308
+ () => descriptor
3309
+ );
3310
+ }
3311
+ dialog(key, options, builder) {
3312
+ this._overlay(OVERLAY_KINDS.dialog, key, options, builder);
3313
+ }
3314
+ popover(key, options, builder) {
3315
+ this._overlay(OVERLAY_KINDS.popover, key, options, builder);
3316
+ }
3317
+ tooltip(key, options, builder) {
3318
+ this._overlay(OVERLAY_KINDS.tooltip, key, options, builder);
3319
+ }
3320
+ dropdown(key, options, builder) {
3321
+ this._overlay(OVERLAY_KINDS.dropdown, key, options, builder);
3322
+ }
3323
+ toast(key, options, builder) {
3324
+ this._overlay(OVERLAY_KINDS.toast, key, options, builder);
3325
+ }
3326
+ // ── Components ────────────────────────────────────────────────────────────
3327
+ /**
3328
+ * Instantiate a reusable component (§3–§9). Creates a `'component'` node
3329
+ * (rendered as a `<div>` wrapper — preserves the one-node/one-element
3330
+ * positional-hydration invariant), then runs `def.setup(props, ctx)`
3331
+ * synchronously to obtain the render function and fills the component's own
3332
+ * container scope with it — structurally identical to `container`/
3333
+ * `errorBoundary`. Cleanups the setup registers via `ctx.effect`/
3334
+ * `ctx.onCleanup` are collected into a closure and exposed to the renderer
3335
+ * through a `__component__<id>` handler (mirroring `__overlay__`); the mount/
3336
+ * hydrate paths read it and route each into `NodeInstance.trackCleanup`, so
3337
+ * teardown runs (children-first) when the component leaves the graph.
3338
+ *
3339
+ * `setup` runs once per instance here at build time. When this component sits
3340
+ * inside a keyed list / conditional, a rebuild disposes the old instance
3341
+ * (running its cleanups + pruning its `__component__` entry) and re-runs this
3342
+ * method for the new node — so re-invocation is safe and leak-free.
3343
+ */
3344
+ component(key, def, props, children) {
3345
+ const graph = this._graph;
3346
+ const node = graph.createNode("component", {
3347
+ key,
3348
+ parent: this._node,
3349
+ // `data-streetui-component` is a non-underscore prop, so it reaches the
3350
+ // DOM as an attribute and is visible to DevTools (§21) — unlike the
3351
+ // internal `_`-prefixed metadata the renderer hides.
3352
+ props: { key, "data-streetui-component": def.name }
3353
+ });
3354
+ const cleanups = [];
3355
+ const ctx = {
3356
+ key,
3357
+ onCleanup(fn) {
3358
+ cleanups.push(fn);
3359
+ },
3360
+ effect(fn) {
3361
+ cleanups.push(effect(fn));
3362
+ },
3363
+ renderChildren(content) {
3364
+ if (children !== void 0) children(content);
3365
+ }
3366
+ };
3367
+ let render2;
3368
+ try {
3369
+ render2 = def.setup(props, ctx);
3370
+ render2(new ContainerBuilderImpl(node, graph));
3371
+ } catch (err) {
3372
+ queueMicrotask(() => {
3373
+ throw err;
3374
+ });
3375
+ }
3376
+ if (cleanups.length > 0) {
3377
+ graph.registerHandler(
3378
+ `__component__${node.id}`,
3379
+ () => cleanups
3380
+ );
3381
+ }
3382
+ }
3383
+ };
3384
+ var SectionBuilderImpl = class extends ContainerBuilderBase {
3385
+ };
3386
+ var ContainerBuilderImpl = class extends ContainerBuilderBase {
3387
+ };
3388
+ var FormBuilderImpl = class extends ContainerBuilderBase {
3389
+ };
3390
+ var ListBuilderImpl = class extends ContentBuilderBase {
3391
+ item(key, builder, options = {}) {
3392
+ const node = this._graph.createNode("list-item", {
3393
+ key,
3394
+ parent: this._node,
3395
+ props: containerProps(options)
3396
+ });
3397
+ registerTransition(this._graph, node, options.transition);
3398
+ builder(new ContainerBuilderImpl(node, this._graph));
3399
+ }
3400
+ };
3401
+ var PageBuilderImpl = class extends ContainerBuilderBase {
3402
+ };
3403
+ var AppBuilder = class {
3404
+ constructor(_graph) {
3405
+ this._graph = _graph;
3406
+ }
3407
+ page(key, builder) {
3408
+ const node = this._graph.createNode("page", {
3409
+ key,
3410
+ parent: this._graph.root,
3411
+ props: { key }
3412
+ });
3413
+ builder(new PageBuilderImpl(node, this._graph));
3414
+ }
3415
+ };
3416
+
3417
+ // ../graph/src/graph-node.ts
3418
+ var GraphNode = class _GraphNode {
3419
+ id;
3420
+ type;
3421
+ key;
3422
+ props;
3423
+ events;
3424
+ stateRefs;
3425
+ children;
3426
+ parent;
3427
+ constructor(type, options = {}) {
3428
+ this.type = type;
3429
+ this.id = options.id ?? generateNodeId(type);
3430
+ this.key = options.key;
3431
+ this.props = options.props ?? {};
3432
+ this.events = options.events ?? [];
3433
+ this.stateRefs = options.stateRefs ?? [];
3434
+ this.children = [];
3435
+ this.parent = null;
3436
+ }
3437
+ // ── Child management ────────────────────────────────────────────────────────
3438
+ appendChild(child) {
3439
+ if (child.parent !== null) {
3440
+ child.parent.removeChild(child);
3441
+ }
3442
+ child.parent = this;
3443
+ this.children.push(child);
3444
+ }
3445
+ insertBefore(child, reference) {
3446
+ const idx = this.children.indexOf(reference);
3447
+ if (idx === -1) {
3448
+ this.appendChild(child);
3449
+ return;
3450
+ }
3451
+ if (child.parent !== null) {
3452
+ child.parent.removeChild(child);
3453
+ }
3454
+ child.parent = this;
3455
+ this.children.splice(idx, 0, child);
3456
+ }
3457
+ removeChild(child) {
3458
+ const idx = this.children.indexOf(child);
3459
+ if (idx === -1) return;
3460
+ this.children.splice(idx, 1);
3461
+ child.parent = null;
3462
+ }
3463
+ replaceChild(newChild, oldChild) {
3464
+ const idx = this.children.indexOf(oldChild);
3465
+ if (idx === -1) {
3466
+ throw new Error(`GraphNode.replaceChild: oldChild is not a child of this node`);
3467
+ }
3468
+ if (newChild.parent !== null) {
3469
+ newChild.parent.removeChild(newChild);
3470
+ }
3471
+ oldChild.parent = null;
3472
+ newChild.parent = this;
3473
+ this.children.splice(idx, 1, newChild);
3474
+ }
3475
+ // ── Prop helpers ────────────────────────────────────────────────────────────
3476
+ setProp(key, value) {
3477
+ this.props = { ...this.props, [key]: value };
3478
+ }
3479
+ getProp(key) {
3480
+ return this.props[key];
3481
+ }
3482
+ // ── Event helpers ───────────────────────────────────────────────────────────
3483
+ addEvent(descriptor) {
3484
+ this.events.push(descriptor);
3485
+ }
3486
+ removeEvent(type) {
3487
+ this.events = this.events.filter((e) => e.type !== type);
3488
+ }
3489
+ // ── Queries ─────────────────────────────────────────────────────────────────
3490
+ get isLeaf() {
3491
+ return this.children.length === 0;
3492
+ }
3493
+ get depth() {
3494
+ let d = 0;
3495
+ let node = this.parent;
3496
+ while (node !== null) {
3497
+ d++;
3498
+ node = node.parent;
3499
+ }
3500
+ return d;
3501
+ }
3502
+ get root() {
3503
+ let node = this;
3504
+ while (node.parent !== null) {
3505
+ node = node.parent;
3506
+ }
3507
+ return node;
3508
+ }
3509
+ /** Shallow clone — does not clone children. */
3510
+ shallowClone() {
3511
+ const opts = {
3512
+ props: { ...this.props },
3513
+ events: [...this.events],
3514
+ stateRefs: [...this.stateRefs]
3515
+ };
3516
+ if (this.key !== void 0) opts.key = this.key;
3517
+ return new _GraphNode(this.type, opts);
3518
+ }
3519
+ };
3520
+
3521
+ // ../graph/src/graph.ts
3522
+ var ApplicationGraph = class {
3523
+ root;
3524
+ name;
3525
+ version;
3526
+ _nodeIndex = /* @__PURE__ */ new Map();
3527
+ /** Handler registry — maps handlerKey → actual function */
3528
+ handlers = /* @__PURE__ */ new Map();
3529
+ constructor(options) {
3530
+ this.name = options.name;
3531
+ this.version = options.version ?? "0.0.1";
3532
+ this.root = new GraphNode("application", { props: { name: options.name } });
3533
+ this._nodeIndex.set(this.root.id, this.root);
3534
+ }
3535
+ // ── Node creation & attachment ────────────────────────────────────────────
3536
+ createNode(type, options = {}) {
3537
+ const nodeOpts = {};
3538
+ if (options.key !== void 0) nodeOpts.key = options.key;
3539
+ if (options.props !== void 0) nodeOpts.props = options.props;
3540
+ const node = new GraphNode(type, nodeOpts);
3541
+ this._nodeIndex.set(node.id, node);
3542
+ if (options.parent !== void 0) {
3543
+ options.parent.appendChild(node);
3544
+ }
3545
+ return node;
3546
+ }
3547
+ attachNode(node, parent) {
3548
+ this._nodeIndex.set(node.id, node);
3549
+ parent.appendChild(node);
3550
+ }
3551
+ detachNode(node) {
3552
+ if (node.parent !== null) {
3553
+ node.parent.removeChild(node);
3554
+ }
3555
+ this._removeFromIndex(node);
3556
+ }
3557
+ _removeFromIndex(node) {
3558
+ this._nodeIndex.delete(node.id);
3559
+ this._unregisterNodeHandlers(node);
3560
+ for (const child of node.children) {
3561
+ this._removeFromIndex(child);
3562
+ }
3563
+ }
3564
+ /**
3565
+ * Remove every handler-registry entry owned by a single node. A node owns:
3566
+ * - one entry per event descriptor (its `handlerKey`),
3567
+ * - one `__signal__<signalId>` entry per state ref (signalIds are namespaced
3568
+ * by node id, so they are never shared between nodes), and
3569
+ * - a `__listbuild__<id>` entry if it is a reactive-list.
3570
+ * Called for every node in a detached subtree so removing list items (or
3571
+ * discarding freshly-built-but-unadopted item subtrees) leaves no stale
3572
+ * registrations behind.
3573
+ */
3574
+ _unregisterNodeHandlers(node) {
3575
+ for (const event of node.events) {
3576
+ this.handlers.delete(event.handlerKey);
3577
+ }
3578
+ for (const ref of node.stateRefs) {
3579
+ this.handlers.delete(`__signal__${ref.signalId}`);
3580
+ }
3581
+ this.handlers.delete(`__listbuild__${node.id}`);
3582
+ this.handlers.delete(`__listplan__${node.id}`);
3583
+ this.handlers.delete(`__overlay__${node.id}`);
3584
+ this.handlers.delete(`__component__${node.id}`);
3585
+ this.handlers.delete(`__transition__${node.id}`);
3586
+ }
3587
+ // ── Handler registry ──────────────────────────────────────────────────────
3588
+ registerHandler(key, fn) {
3589
+ this.handlers.set(key, fn);
3590
+ }
3591
+ getHandler(key) {
3592
+ return this.handlers.get(key);
3593
+ }
3594
+ /** True if a handler is currently registered under `key`. Inspection helper. */
3595
+ hasHandler(key) {
3596
+ return this.handlers.has(key);
3597
+ }
3598
+ /** Number of currently-registered handlers. Inspection helper. */
3599
+ get handlerCount() {
3600
+ return this.handlers.size;
3601
+ }
3602
+ // ── Lookup ────────────────────────────────────────────────────────────────
3603
+ findById(id) {
3604
+ return this._nodeIndex.get(id);
3605
+ }
3606
+ findAll(predicate) {
3607
+ const results = [];
3608
+ this._walk(this.root, (node) => {
3609
+ if (predicate(node)) results.push(node);
3610
+ });
3611
+ return results;
3612
+ }
3613
+ findByType(type) {
3614
+ return this.findAll((n) => n.type === type);
3615
+ }
3616
+ // ── Traversal ─────────────────────────────────────────────────────────────
3617
+ walk(visitor) {
3618
+ this._walk(this.root, visitor, 0);
3619
+ }
3620
+ _walk(node, visitor, depth = 0) {
3621
+ visitor(node, depth);
3622
+ for (const child of node.children) {
3623
+ this._walk(child, visitor, depth + 1);
3624
+ }
3625
+ }
3626
+ get nodeCount() {
3627
+ return this._nodeIndex.size;
3628
+ }
3629
+ // ── Validation ────────────────────────────────────────────────────────────
3630
+ validate() {
3631
+ const dc = new DiagnosticCollector();
3632
+ this.walk((node) => {
3633
+ for (const event of node.events) {
3634
+ if (!this.handlers.has(event.handlerKey)) {
3635
+ dc.warn(
3636
+ "GRAPH_MISSING_HANDLER",
3637
+ `Node "${node.id}" references handler "${event.handlerKey}" which is not registered`,
3638
+ { nodeId: node.id }
3639
+ );
3640
+ }
3641
+ }
3642
+ if (node.type === "page" && node.parent?.type !== "application") {
3643
+ dc.error(
3644
+ "GRAPH_PAGE_DEPTH",
3645
+ `Page node "${node.id}" must be a direct child of the application root`,
3646
+ { nodeId: node.id }
3647
+ );
3648
+ }
3649
+ });
3650
+ return dc;
3651
+ }
3652
+ // ── Serialization ─────────────────────────────────────────────────────────
3653
+ serialize() {
3654
+ return {
3655
+ name: this.name,
3656
+ version: this.version,
3657
+ root: this._serializeNode(this.root)
3658
+ };
3659
+ }
3660
+ _serializeNode(node) {
3661
+ const result = {
3662
+ id: node.id,
3663
+ type: node.type,
3664
+ key: node.key,
3665
+ props: node.props,
3666
+ events: node.events,
3667
+ stateRefs: node.stateRefs,
3668
+ children: node.children.map((c) => this._serializeNode(c))
3669
+ };
3670
+ return result;
3671
+ }
3672
+ };
3673
+
3674
+ // ../dsl/src/dsl.ts
3675
+ var StreetApp = class {
3676
+ _graph;
3677
+ _builder;
3678
+ constructor(options) {
3679
+ const graphOpts = { name: options.name };
3680
+ if (options.version !== void 0) graphOpts.version = options.version;
3681
+ this._graph = new ApplicationGraph(graphOpts);
3682
+ this._builder = new AppBuilder(this._graph);
3683
+ }
3684
+ page(key, builder) {
3685
+ this._builder.page(key, builder);
3686
+ return this;
3687
+ }
3688
+ /** Compile to ApplicationGraph — validates and returns the graph. */
3689
+ build() {
3690
+ const dc = this._graph.validate();
3691
+ dc.throwIfErrors();
3692
+ return this._graph;
3693
+ }
3694
+ /** Access graph before building (useful for inspection). */
3695
+ get graph() {
3696
+ return this._graph;
3697
+ }
3698
+ };
3699
+ var streetui = {
3700
+ app(options) {
3701
+ return new StreetApp(options);
3702
+ }
3703
+ };
3704
+
3705
+ // ../testing/src/component.ts
3706
+ var COMPONENT_ATTR = "data-streetui-component";
3707
+ function renderComponent(def, props, children) {
3708
+ const app = streetui.app({ name: `test:${def.name}` });
3709
+ app.page("host", (page) => {
3710
+ page.component("root", def, props, children);
3711
+ });
3712
+ const result = render(app);
3713
+ const component = result.container.querySelector(`[${COMPONENT_ATTR}]`);
3714
+ if (component === null) {
3715
+ throw new Error(`[StreetUI Testing] renderComponent: no component element rendered for "${def.name}"`);
3716
+ }
3717
+ return { ...result, component };
3718
+ }
3719
+ function hydrateComponent(build, options = {}) {
3720
+ return renderServerThenHydrate(() => {
3721
+ const { def, props, children } = build();
3722
+ const app = streetui.app({ name: `test:${def.name}` });
3723
+ app.page("host", (page) => page.component("root", def, props, children));
3724
+ return app;
3725
+ }, options);
3726
+ }
3727
+ function findAllComponents(container, name) {
3728
+ const selector = name === void 0 ? `[${COMPONENT_ATTR}]` : `[${COMPONENT_ATTR}="${name}"]`;
3729
+ return Array.from(container.querySelectorAll(selector));
3730
+ }
3731
+ function findComponent(container, name) {
3732
+ const matches = findAllComponents(container, name);
3733
+ const named = name !== void 0 ? ` named "${name}"` : "";
3734
+ if (matches.length === 0) {
3735
+ throw new Error(`[StreetUI Testing] No component${named} found`);
3736
+ }
3737
+ if (matches.length > 1) {
3738
+ throw new Error(
3739
+ `[StreetUI Testing] Found ${matches.length} components${named} \u2014 pass a name to disambiguate`
3740
+ );
3741
+ }
3742
+ return matches[0];
3743
+ }
3744
+ function getComponentName(el) {
3745
+ return el.getAttribute(COMPONENT_ATTR);
3746
+ }
3747
+ function trigger(el, type, init = {}) {
3748
+ const base = { bubbles: true, cancelable: true, ...init };
3749
+ let event;
3750
+ if (type.startsWith("key")) {
3751
+ event = new KeyboardEvent(type, base);
3752
+ } else if (type.startsWith("mouse") || type === "click" || type === "dblclick") {
3753
+ event = new MouseEvent(type, base);
3754
+ } else if (type === "input" || type === "change") {
3755
+ event = new Event(type, base);
3756
+ } else {
3757
+ event = new Event(type, base);
3758
+ }
3759
+ el.dispatchEvent(event);
3760
+ }
3761
+
2338
3762
  // src/version.ts
2339
- var VERSION = "1.7.0";
3763
+ var VERSION = "1.9.0";
2340
3764
  // Annotate the CommonJS export names for ESM import in node:
2341
3765
  0 && (module.exports = {
2342
3766
  VERSION,
2343
3767
  analyzeGraph,
3768
+ blur,
3769
+ clickOutside,
3770
+ closeOverlay,
2344
3771
  findAllByRole,
3772
+ findAllComponents,
2345
3773
  findByRole,
2346
3774
  findByText,
3775
+ findComponent,
2347
3776
  flushUpdates,
3777
+ focus,
2348
3778
  formatInspection,
3779
+ getComponentName,
3780
+ hydrateComponent,
2349
3781
  inspectCompilation,
3782
+ openOverlay,
3783
+ pressKey,
2350
3784
  render,
3785
+ renderComponent,
2351
3786
  renderOnce,
2352
3787
  renderServerThenHydrate,
2353
- waitFor
3788
+ trigger,
3789
+ waitFor,
3790
+ waitForTransition
2354
3791
  });
2355
3792
  //# sourceMappingURL=testing.cjs.map