@jxsuite/runtime 3.1.0 → 4.0.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/runtime.js CHANGED
@@ -177,6 +177,13 @@ class EffectScope {
177
177
  function effectScope(detached) {
178
178
  return new EffectScope(detached);
179
179
  }
180
+ function onScopeDispose(fn, failSilently = false) {
181
+ if (activeEffectScope) {
182
+ activeEffectScope.cleanups.push(fn);
183
+ } else if (!failSilently) {
184
+ warn(`onScopeDispose() is called when there is no active effect scope to be associated with.`);
185
+ }
186
+ }
180
187
  var activeSub;
181
188
  var pausedQueueEffects = /* @__PURE__ */ new WeakSet;
182
189
 
@@ -1012,6 +1019,7 @@ class ReadonlyReactiveHandler extends BaseReactiveHandler {
1012
1019
  }
1013
1020
  var mutableHandlers = /* @__PURE__ */ new MutableReactiveHandler;
1014
1021
  var readonlyHandlers = /* @__PURE__ */ new ReadonlyReactiveHandler;
1022
+ var shallowReactiveHandlers = /* @__PURE__ */ new MutableReactiveHandler(true);
1015
1023
  var toShallow = (value) => value;
1016
1024
  var getProto = (v) => Reflect.getPrototypeOf(v);
1017
1025
  function createIterableMethod(method, isReadonly2, isShallow2) {
@@ -1189,6 +1197,9 @@ function createInstrumentationGetter(isReadonly2, shallow) {
1189
1197
  var mutableCollectionHandlers = {
1190
1198
  get: /* @__PURE__ */ createInstrumentationGetter(false, false)
1191
1199
  };
1200
+ var shallowCollectionHandlers = {
1201
+ get: /* @__PURE__ */ createInstrumentationGetter(false, true)
1202
+ };
1192
1203
  var readonlyCollectionHandlers = {
1193
1204
  get: /* @__PURE__ */ createInstrumentationGetter(true, false)
1194
1205
  };
@@ -1223,6 +1234,9 @@ function reactive(target) {
1223
1234
  }
1224
1235
  return createReactiveObject(target, false, mutableHandlers, mutableCollectionHandlers, reactiveMap);
1225
1236
  }
1237
+ function shallowReactive(target) {
1238
+ return createReactiveObject(target, false, shallowReactiveHandlers, shallowCollectionHandlers, shallowReactiveMap);
1239
+ }
1226
1240
  function readonly(target) {
1227
1241
  return createReactiveObject(target, true, readonlyHandlers, readonlyCollectionHandlers, readonlyMap);
1228
1242
  }
@@ -1384,6 +1398,79 @@ function computed(getterOrOptions, debugOptions, isSSR = false) {
1384
1398
  return cRef;
1385
1399
  }
1386
1400
 
1401
+ // ../schema/src/guards.ts
1402
+ function isJsonObject(value) {
1403
+ return typeof value === "object" && value !== null && !Array.isArray(value);
1404
+ }
1405
+ function isRef2(value) {
1406
+ return isJsonObject(value) && typeof value.$ref === "string";
1407
+ }
1408
+ function isFunctionDef(value) {
1409
+ return isJsonObject(value) && value.$prototype === "Function";
1410
+ }
1411
+ function isExpressionDef(value) {
1412
+ return isJsonObject(value) && isJsonObject(value.$expression);
1413
+ }
1414
+ function isNamedFormulaDef(value) {
1415
+ return isExpressionDef(value) && Array.isArray(value.parameters) && value.parameters.length > 0;
1416
+ }
1417
+ function hasStructuredBody(value) {
1418
+ return isFunctionDef(value) && Array.isArray(value.body);
1419
+ }
1420
+ function isPrototypeDef(value) {
1421
+ return isJsonObject(value) && typeof value.$prototype === "string" && value.$prototype !== "Function";
1422
+ }
1423
+ function isServerFnDef(value) {
1424
+ return isJsonObject(value) && value.timing === "server" && typeof value.$src === "string" && typeof value.$export === "string" && !value.$prototype;
1425
+ }
1426
+ var SCHEMA_KEYWORDS = new Set([
1427
+ "type",
1428
+ "enum",
1429
+ "minimum",
1430
+ "maximum",
1431
+ "minLength",
1432
+ "maxLength",
1433
+ "pattern",
1434
+ "format",
1435
+ "items",
1436
+ "properties",
1437
+ "required",
1438
+ "description",
1439
+ "title",
1440
+ "$comment"
1441
+ ]);
1442
+ function isMappedArray(value) {
1443
+ return isJsonObject(value) && value.$prototype === "Array";
1444
+ }
1445
+ function isTemplateString(value) {
1446
+ return typeof value === "string" && value.includes("${");
1447
+ }
1448
+ function bodyReturnsValue(body) {
1449
+ return /\breturn\b[^\S\n]*(?![;}\s]|$)/.test(body);
1450
+ }
1451
+ function paramNames(parameters) {
1452
+ if (!parameters) {
1453
+ return [];
1454
+ }
1455
+ return parameters.map((p) => typeof p === "string" ? p : p.name);
1456
+ }
1457
+ function isTagExpression(tagName) {
1458
+ if (!isJsonObject(tagName) || !isJsonObject(tagName.$expression)) {
1459
+ return false;
1460
+ }
1461
+ const expression = tagName.$expression;
1462
+ if (expression.operator === "?:") {
1463
+ return typeof expression.value === "string" && typeof expression.initial === "string";
1464
+ }
1465
+ if (expression.operator === "switch") {
1466
+ return isJsonObject(expression.cases) && Object.values(expression.cases).every((tag) => typeof tag === "string") && typeof expression.default === "string";
1467
+ }
1468
+ return false;
1469
+ }
1470
+ function isPrivateStateKey(key) {
1471
+ return key.startsWith("#");
1472
+ }
1473
+
1387
1474
  // src/css.ts
1388
1475
  function camelToKebab(s) {
1389
1476
  return s.replaceAll(/[A-Z]/g, (c) => `-${c.toLowerCase()}`);
@@ -1399,6 +1486,90 @@ function resolveAtQuery(atKey, mediaQueries) {
1399
1486
  }
1400
1487
  return null;
1401
1488
  }
1489
+ function isDeclarationAtRule(atKey) {
1490
+ return atKey.startsWith("@position-try") || atKey.startsWith("@property") || atKey.startsWith("@font-face") || atKey.startsWith("@counter-style");
1491
+ }
1492
+ function isKeyframesAtRule(atKey) {
1493
+ return atKey.startsWith("@keyframes");
1494
+ }
1495
+ function resolveNestedSelector(scope, key) {
1496
+ const resolved = [];
1497
+ for (const scopePart of splitSelectorList(scope)) {
1498
+ for (const keyPart of splitSelectorList(key)) {
1499
+ resolved.push(resolveOneNestedSelector(scopePart, keyPart));
1500
+ }
1501
+ }
1502
+ return resolved.join(", ");
1503
+ }
1504
+ function resolveOneNestedSelector(scope, key) {
1505
+ if (key.startsWith("&")) {
1506
+ return key.replaceAll("&", scope);
1507
+ }
1508
+ if (key.startsWith("[") || key.startsWith(":") || key.startsWith(".")) {
1509
+ return `${scope}${key}`;
1510
+ }
1511
+ return `${scope} ${key}`;
1512
+ }
1513
+ function splitSelectorList(selector) {
1514
+ const members = [];
1515
+ let depth = 0;
1516
+ let quote = null;
1517
+ let start = 0;
1518
+ for (let i = 0;i < selector.length; i += 1) {
1519
+ const ch = selector[i];
1520
+ if (quote) {
1521
+ if (ch === "\\") {
1522
+ i += 1;
1523
+ } else if (ch === quote) {
1524
+ quote = null;
1525
+ }
1526
+ continue;
1527
+ }
1528
+ if (ch === '"' || ch === "'") {
1529
+ quote = ch;
1530
+ } else if (ch === "(" || ch === "[") {
1531
+ depth += 1;
1532
+ } else if (ch === ")" || ch === "]") {
1533
+ depth = Math.max(0, depth - 1);
1534
+ } else if (ch === "," && depth === 0) {
1535
+ members.push(selector.slice(start, i));
1536
+ start = i + 1;
1537
+ }
1538
+ }
1539
+ members.push(selector.slice(start));
1540
+ const trimmed = members.map((member) => member.trim()).filter((member) => member !== "");
1541
+ return trimmed.length > 0 ? trimmed : [selector];
1542
+ }
1543
+ function transposeCanvasPopoverSelector(selector) {
1544
+ return transposeCanvasOverlaySelector(selector);
1545
+ }
1546
+ var COMPOUND_BREAK = /[\s>+~,()]/;
1547
+ var LEADING_TYPE = /^[A-Za-z_\u{00A0}-\u{FFFF}][\w\u{00A0}-\u{FFFF}-]*/u;
1548
+ function transposeOpenAttribute(selector, options) {
1549
+ let out = "";
1550
+ let from = 0;
1551
+ for (;; ) {
1552
+ const at = selector.indexOf("[open]", from);
1553
+ if (at === -1) {
1554
+ return out + selector.slice(from);
1555
+ }
1556
+ let start = at;
1557
+ while (start > 0 && !COMPOUND_BREAK.test(selector[start - 1])) {
1558
+ start -= 1;
1559
+ }
1560
+ const type = LEADING_TYPE.exec(selector.slice(start, at))?.[0]?.toLowerCase();
1561
+ const dialog = type === undefined ? options.dialog === true : type === "dialog";
1562
+ out += selector.slice(from, at) + (dialog ? "[data-jx-dialog-open]" : "[open]");
1563
+ from = at + "[open]".length;
1564
+ }
1565
+ }
1566
+ function transposeCanvasOverlaySelector(selector, options = {}) {
1567
+ if (selector.includes("::backdrop")) {
1568
+ return null;
1569
+ }
1570
+ const transposed = selector.replaceAll(":popover-open", "[data-jx-popover-open]").replaceAll(":modal", "[data-jx-dialog-open]").replaceAll("[inert]", "[data-jx-inert]");
1571
+ return transposeOpenAttribute(transposed, options);
1572
+ }
1402
1573
  var COLOR_SCHEME_ATTR = "data-color-scheme";
1403
1574
  var COLOR_SCHEME_STORAGE_KEY = "jx-color-scheme";
1404
1575
  function pureSchemeOf(query) {
@@ -1417,6 +1588,195 @@ function schemeSelectors(selector, scheme) {
1417
1588
  forced: `:where(:root[${COLOR_SCHEME_ATTR}="${scheme}"]) ${selector}`
1418
1589
  };
1419
1590
  }
1591
+ function isNestedSelectorKey(key) {
1592
+ return key.startsWith(":") || key.startsWith(".") || key.startsWith("&") || key.startsWith("[");
1593
+ }
1594
+ function cssPropertyName(property) {
1595
+ return property.startsWith("--") ? property : camelToKebab(property);
1596
+ }
1597
+ function hashCss(text) {
1598
+ let h = 2166136261;
1599
+ for (let i = 0;i < text.length; i += 1) {
1600
+ h ^= text.charCodeAt(i);
1601
+ h = Math.imul(h, 16777619);
1602
+ }
1603
+ return (h >>> 0).toString(36);
1604
+ }
1605
+ function cssRuleText(conditions, selector, declarations) {
1606
+ const body = declarations.map(([property, value]) => `${property}: ${value}`).join("; ");
1607
+ let text = selector === null ? body : `${selector} { ${body} }`;
1608
+ for (let i = conditions.length - 1;i >= 0; i -= 1) {
1609
+ text = `${conditions[i]} { ${text} }`;
1610
+ }
1611
+ return text;
1612
+ }
1613
+ function cssKeyframesText(conditions, name, blocks) {
1614
+ const body = blocks.map(({ selector, declarations }) => `${selector} { ${declarations.map(([property, value]) => `${property}: ${value}`).join("; ")} }`).join(" ");
1615
+ let text = `@keyframes ${name} { ${body} }`;
1616
+ for (let i = conditions.length - 1;i >= 0; i -= 1) {
1617
+ text = `${conditions[i]} { ${text} }`;
1618
+ }
1619
+ return text;
1620
+ }
1621
+ function withoutKeyframes(block) {
1622
+ const keyframeKeys = Object.keys(block).filter((key) => isKeyframesAtRule(key));
1623
+ if (keyframeKeys.length === 0) {
1624
+ return block;
1625
+ }
1626
+ const rest = { ...block };
1627
+ for (const key of keyframeKeys) {
1628
+ delete rest[key];
1629
+ }
1630
+ return rest;
1631
+ }
1632
+ function compoundsOntoScope(key) {
1633
+ const rest = key.startsWith("&") ? key.slice(1) : key;
1634
+ return rest === "" || rest.startsWith(":") || rest.startsWith(".") || rest.startsWith("[");
1635
+ }
1636
+ function buildStyleRules(style, options = {}) {
1637
+ const {
1638
+ scope = null,
1639
+ mediaQueries = {},
1640
+ transposeValue = (value) => value,
1641
+ transposeSelector = (selector) => selector,
1642
+ resolveValue
1643
+ } = options;
1644
+ const rules = [];
1645
+ const isBlock = (value) => value !== null && typeof value === "object" && !Array.isArray(value) && !isRef2(value);
1646
+ const blocksOf = (key, value) => {
1647
+ if (isBlock(value)) {
1648
+ return [value];
1649
+ }
1650
+ if (!isDeclarationAtRule(key) || !Array.isArray(value) || value.length === 0) {
1651
+ return [];
1652
+ }
1653
+ return value.every((entry) => isBlock(entry)) ? value : [];
1654
+ };
1655
+ const declarationValue = (property, value, reactive, target) => {
1656
+ if (value === undefined || value === null) {
1657
+ return null;
1658
+ }
1659
+ if (isRef2(value)) {
1660
+ return reactive ? resolveValue?.(property, value, target) ?? null : null;
1661
+ }
1662
+ if (typeof value === "object") {
1663
+ return null;
1664
+ }
1665
+ const raw = String(value);
1666
+ if (isTemplateString(raw)) {
1667
+ return reactive ? resolveValue?.(property, raw, target) ?? null : null;
1668
+ }
1669
+ return transposeValue(raw);
1670
+ };
1671
+ const declarationsOf = (node, skipSelectorKeys, reactive = true, target = "self") => {
1672
+ const declarations = [];
1673
+ for (const [key, value] of Object.entries(node)) {
1674
+ if (blocksOf(key, value).length > 0) {
1675
+ continue;
1676
+ }
1677
+ if (skipSelectorKeys && (key.startsWith("@") || isNestedSelectorKey(key))) {
1678
+ continue;
1679
+ }
1680
+ if (key.startsWith("$")) {
1681
+ continue;
1682
+ }
1683
+ const resolved = declarationValue(key, value, reactive, target);
1684
+ if (resolved !== null) {
1685
+ declarations.push([cssPropertyName(key), resolved]);
1686
+ }
1687
+ }
1688
+ return declarations;
1689
+ };
1690
+ const emit = (conditions, selector, declarations, target, description) => {
1691
+ if (declarations.length === 0) {
1692
+ return;
1693
+ }
1694
+ let resolved = selector;
1695
+ if (resolved !== null) {
1696
+ const transposed = transposeSelector(resolved);
1697
+ if (transposed === null) {
1698
+ return;
1699
+ }
1700
+ resolved = transposed;
1701
+ }
1702
+ const text = cssRuleText(conditions, resolved, declarations);
1703
+ rules.push({
1704
+ text,
1705
+ conditions: [...conditions],
1706
+ selector: resolved,
1707
+ declarations,
1708
+ target,
1709
+ key: hashCss(text),
1710
+ ...typeof description === "string" && description !== "" ? { description } : {}
1711
+ });
1712
+ };
1713
+ const emitKeyframes = (atKey, block, conditions) => {
1714
+ const name = atKey.slice("@keyframes".length).trim();
1715
+ if (name === "") {
1716
+ return;
1717
+ }
1718
+ const blocks = [];
1719
+ for (const [key, value] of Object.entries(block)) {
1720
+ if (!isBlock(value)) {
1721
+ continue;
1722
+ }
1723
+ const declarations = declarationsOf(value, true, false, "unscoped");
1724
+ if (declarations.length > 0) {
1725
+ blocks.push({ declarations, selector: key.trim() });
1726
+ }
1727
+ }
1728
+ if (blocks.length === 0) {
1729
+ return;
1730
+ }
1731
+ const text = cssKeyframesText(conditions, name, blocks);
1732
+ rules.push({
1733
+ blocks,
1734
+ conditions: [...conditions],
1735
+ declarations: [],
1736
+ key: hashCss(text),
1737
+ selector: null,
1738
+ target: "unscoped",
1739
+ text
1740
+ });
1741
+ };
1742
+ const walkAt = (atKey, block, selector, conditions, target) => {
1743
+ if (atKey === "@--") {
1744
+ return;
1745
+ }
1746
+ if (isDeclarationAtRule(atKey)) {
1747
+ emit([...conditions, atKey], null, declarationsOf(block, false, true, "unscoped"), "unscoped", block["$description"]);
1748
+ return;
1749
+ }
1750
+ if (isKeyframesAtRule(atKey)) {
1751
+ emitKeyframes(atKey, block, conditions);
1752
+ return;
1753
+ }
1754
+ const query = resolveAtQuery(atKey, mediaQueries);
1755
+ const atRule = query === null ? atKey : `@media ${query}`;
1756
+ const scheme = query === null ? null : pureSchemeOf(query);
1757
+ if (scheme !== null && selector !== null) {
1758
+ const { auto, forced } = schemeSelectors(selector, scheme);
1759
+ walk(block, auto, [...conditions, atRule], target);
1760
+ walk(withoutKeyframes(block), forced, conditions, target);
1761
+ return;
1762
+ }
1763
+ walk(block, selector, [...conditions, atRule], target);
1764
+ };
1765
+ function walk(node, selector, conditions, target) {
1766
+ emit(conditions, selector, declarationsOf(node, true, true, target), target, node["$description"]);
1767
+ for (const [key, value] of Object.entries(node)) {
1768
+ for (const block of blocksOf(key, value)) {
1769
+ if (key.startsWith("@")) {
1770
+ walkAt(key, block, selector, conditions, target);
1771
+ } else if (selector !== null) {
1772
+ walk(block, resolveNestedSelector(selector, key), conditions, compoundsOntoScope(key) ? target : "descendant");
1773
+ }
1774
+ }
1775
+ }
1776
+ }
1777
+ walk(style, scope, [], "self");
1778
+ return rules;
1779
+ }
1420
1780
 
1421
1781
  // ../schema/src/intl.ts
1422
1782
  var DEFAULT_FORMAT_LOCALE = "en-US";
@@ -1648,8 +2008,8 @@ function withPageLocale(path, args, state) {
1648
2008
  filled[index] = locale;
1649
2009
  return filled;
1650
2010
  }
1651
- function isBlessedGlobal(ref2) {
1652
- return ref2.startsWith("window#/") && BLESSED_GLOBALS.has(ref2.slice("window#/".length));
2011
+ function isBlessedGlobal(ref) {
2012
+ return ref.startsWith("window#/") && BLESSED_GLOBALS.has(ref.slice("window#/".length));
1653
2013
  }
1654
2014
  function isMutating(op) {
1655
2015
  return MUTATING_OPS.has(op);
@@ -1688,24 +2048,43 @@ function resolveOperand(operand, state, event, iterCtx, trace) {
1688
2048
  trace?.report(trace.path ?? [], operand);
1689
2049
  return operand;
1690
2050
  }
1691
- function resolveExprRef(ref2, state, event, iterCtx) {
1692
- if (ref2 === "$reduce/acc") {
2051
+ function calleeOwner(ref, state, event, iterCtx) {
2052
+ const parentRef = (prefix) => {
2053
+ const rest = ref.slice(prefix.length);
2054
+ if (!rest.includes("/")) {
2055
+ return state;
2056
+ }
2057
+ return resolveExprRef(ref.slice(0, ref.lastIndexOf("/")), state, event, iterCtx);
2058
+ };
2059
+ if (ref.startsWith("#/state/")) {
2060
+ return parentRef("#/state/");
2061
+ }
2062
+ if (ref.startsWith("parent#/")) {
2063
+ return parentRef("parent#/");
2064
+ }
2065
+ if (ref.startsWith("$map/") || ref.startsWith("$args/")) {
2066
+ return ref.split("/").length > 2 ? resolveExprRef(ref.slice(0, ref.lastIndexOf("/")), state, event, iterCtx) : undefined;
2067
+ }
2068
+ return;
2069
+ }
2070
+ function resolveExprRef(ref, state, event, iterCtx) {
2071
+ if (ref === "$reduce/acc") {
1693
2072
  return iterCtx?.acc;
1694
2073
  }
1695
- if (ref2.startsWith("$reduce/")) {
2074
+ if (ref.startsWith("$reduce/")) {
1696
2075
  return iterCtx?.acc;
1697
2076
  }
1698
- if (ref2.startsWith("$args/")) {
1699
- const parts = ref2.slice("$args/".length).split("/");
2077
+ if (ref.startsWith("$args/")) {
2078
+ const parts = ref.slice("$args/".length).split("/");
1700
2079
  const base = iterCtx?.args?.[parts[0]];
1701
2080
  return parts.length > 1 ? getPath(base, parts.slice(1).join("/")) : base;
1702
2081
  }
1703
- if (ref2.startsWith("event#/")) {
1704
- const path = ref2.slice("event#/".length);
2082
+ if (ref.startsWith("event#/")) {
2083
+ const path = ref.slice("event#/".length);
1705
2084
  return getPath(event, path);
1706
2085
  }
1707
- if (ref2.startsWith("$map/")) {
1708
- const parts = ref2.split("/");
2086
+ if (ref.startsWith("$map/")) {
2087
+ const parts = ref.split("/");
1709
2088
  const [, key] = parts;
1710
2089
  let base;
1711
2090
  const map = scopeMap(state);
@@ -1718,23 +2097,23 @@ function resolveExprRef(ref2, state, event, iterCtx) {
1718
2097
  }
1719
2098
  return parts.length > 2 ? getPath(base, parts.slice(2).join("/")) : base;
1720
2099
  }
1721
- if (ref2.startsWith("#/state/")) {
1722
- return readPath(state, ref2.slice("#/state/".length));
2100
+ if (ref.startsWith("#/state/")) {
2101
+ return readPath(state, ref.slice("#/state/".length));
1723
2102
  }
1724
- if (ref2.startsWith("parent#/")) {
1725
- return readPath(state, ref2.slice("parent#/".length));
2103
+ if (ref.startsWith("parent#/")) {
2104
+ return readPath(state, ref.slice("parent#/".length));
1726
2105
  }
1727
- if (ref2.startsWith("window#/")) {
1728
- return getPath(globalThis.window, ref2.slice("window#/".length));
2106
+ if (ref.startsWith("window#/")) {
2107
+ return getPath(globalThis.window, ref.slice("window#/".length));
1729
2108
  }
1730
- if (ref2.startsWith("document#/")) {
1731
- return getPath(globalThis.document, ref2.slice("document#/".length));
2109
+ if (ref.startsWith("document#/")) {
2110
+ return getPath(globalThis.document, ref.slice("document#/".length));
1732
2111
  }
1733
- return readPath(state, ref2) ?? null;
2112
+ return readPath(state, ref) ?? null;
1734
2113
  }
1735
- function resolveWritableRef(ref2, state, _event, iterCtx) {
1736
- if (ref2.startsWith("$map/")) {
1737
- const parts = ref2.split("/");
2114
+ function resolveWritableRef(ref, state, _event, iterCtx) {
2115
+ if (ref.startsWith("$map/")) {
2116
+ const parts = ref.split("/");
1738
2117
  const [, key] = parts;
1739
2118
  let base;
1740
2119
  const map = scopeMap(state);
@@ -1756,8 +2135,8 @@ function resolveWritableRef(ref2, state, _event, iterCtx) {
1756
2135
  obj: scopeMap(state) ?? state
1757
2136
  };
1758
2137
  }
1759
- if (ref2.startsWith("#/state/")) {
1760
- const sub = ref2.slice("#/state/".length);
2138
+ if (ref.startsWith("#/state/")) {
2139
+ const sub = ref.slice("#/state/".length);
1761
2140
  const slash = sub.indexOf("/");
1762
2141
  if (slash === -1) {
1763
2142
  return { key: sub, obj: state };
@@ -1770,7 +2149,7 @@ function resolveWritableRef(ref2, state, _event, iterCtx) {
1770
2149
  }
1771
2150
  return { key: lastKey, obj };
1772
2151
  }
1773
- return { key: ref2, obj: state };
2152
+ return { key: ref, obj: state };
1774
2153
  }
1775
2154
  function evaluateExpression(node, state, event, iterCtx, trace) {
1776
2155
  const result = evaluateNode(node, state, event, iterCtx, trace);
@@ -1817,19 +2196,19 @@ function evaluateNode(node, state, event, iterCtx, trace) {
1817
2196
  }
1818
2197
  const callee = resolveExprRef(calleeRef, state, event, iterCtx);
1819
2198
  if (typeof callee === "function") {
1820
- return callee(...argValues);
2199
+ return callee.apply(calleeOwner(calleeRef, state, event, iterCtx), argValues);
1821
2200
  }
1822
2201
  if (callee && typeof callee === "object" && "$expression" in callee) {
1823
- const def2 = callee;
2202
+ const def = callee;
1824
2203
  const args = {};
1825
- for (const [i, p] of (def2.parameters ?? []).entries()) {
2204
+ for (const [i, p] of (def.parameters ?? []).entries()) {
1826
2205
  const name = typeof p === "string" ? p : p?.name ?? "";
1827
2206
  if (!name) {
1828
2207
  continue;
1829
2208
  }
1830
2209
  args[name] = argValues[i] === undefined && typeof p === "object" && p !== null && "default" in p ? p.default : argValues[i];
1831
2210
  }
1832
- return evaluateExpression(def2.$expression, state, event, { args, callDepth });
2211
+ return evaluateExpression(def.$expression, state, event, { args, callDepth });
1833
2212
  }
1834
2213
  throw new TypeError(`$expression: call target "${calleeRef}" is not callable`);
1835
2214
  }
@@ -2003,69 +2382,6 @@ function evaluateOperand(operand, state, event, iterCtx) {
2003
2382
  return resolveOperand(operand, state, event, iterCtx);
2004
2383
  }
2005
2384
 
2006
- // ../schema/src/guards.ts
2007
- function isJsonObject(value) {
2008
- return typeof value === "object" && value !== null && !Array.isArray(value);
2009
- }
2010
- function isRef2(value) {
2011
- return isJsonObject(value) && typeof value.$ref === "string";
2012
- }
2013
- function isFunctionDef(value) {
2014
- return isJsonObject(value) && value.$prototype === "Function";
2015
- }
2016
- function isExpressionDef(value) {
2017
- return isJsonObject(value) && isJsonObject(value.$expression);
2018
- }
2019
- function isNamedFormulaDef(value) {
2020
- return isExpressionDef(value) && Array.isArray(value.parameters) && value.parameters.length > 0;
2021
- }
2022
- function hasStructuredBody(value) {
2023
- return isFunctionDef(value) && Array.isArray(value.body);
2024
- }
2025
- function isPrototypeDef(value) {
2026
- return isJsonObject(value) && typeof value.$prototype === "string" && value.$prototype !== "Function";
2027
- }
2028
- function isServerFnDef(value) {
2029
- return isJsonObject(value) && value.timing === "server" && typeof value.$src === "string" && typeof value.$export === "string" && !value.$prototype;
2030
- }
2031
- var SCHEMA_KEYWORDS = new Set([
2032
- "type",
2033
- "enum",
2034
- "minimum",
2035
- "maximum",
2036
- "minLength",
2037
- "maxLength",
2038
- "pattern",
2039
- "format",
2040
- "items",
2041
- "properties",
2042
- "required",
2043
- "description",
2044
- "title",
2045
- "$comment"
2046
- ]);
2047
- function isMappedArray(value) {
2048
- return isJsonObject(value) && value.$prototype === "Array";
2049
- }
2050
- function isTemplateString(value) {
2051
- return typeof value === "string" && value.includes("${");
2052
- }
2053
- function bodyReturnsValue(body) {
2054
- return /\breturn\b[^\S\n]*(?![;}\s]|$)/.test(body);
2055
- }
2056
- function paramNames(parameters) {
2057
- if (!parameters) {
2058
- return [];
2059
- }
2060
- return parameters.map((p) => typeof p === "string" ? p : p.name);
2061
- }
2062
- function isTagExpression(tagName) {
2063
- return isJsonObject(tagName) && isJsonObject(tagName.$expression);
2064
- }
2065
- function isPrivateStateKey(key) {
2066
- return key.startsWith("#");
2067
- }
2068
-
2069
2385
  // ../schema/src/asset-paths.ts
2070
2386
  var URL_TERMINATOR = String.raw`"'\s<>()\\`;
2071
2387
  function parseSrcset(value) {
@@ -2118,6 +2434,12 @@ function statementKind(statement) {
2118
2434
  if ("$switch" in statement) {
2119
2435
  return "switch";
2120
2436
  }
2437
+ if ("stopPropagation" in statement) {
2438
+ return "stopPropagation";
2439
+ }
2440
+ if ("preventDefault" in statement) {
2441
+ return "preventDefault";
2442
+ }
2121
2443
  return "dispatch";
2122
2444
  }
2123
2445
  async function runStatements(statements, state, event, opts = {}) {
@@ -2151,7 +2473,8 @@ async function runStatements(statements, state, event, opts = {}) {
2151
2473
  }
2152
2474
  case "dispatch": {
2153
2475
  const dispatch = statement;
2154
- const target = opts.target ?? event?.currentTarget;
2476
+ const fallback = typeof opts.target === "function" ? opts.target() : opts.target;
2477
+ const target = event?.currentTarget ?? fallback;
2155
2478
  if (!target) {
2156
2479
  break;
2157
2480
  }
@@ -2168,6 +2491,14 @@ async function runStatements(statements, state, event, opts = {}) {
2168
2491
  target.dispatchEvent(new CustomEvent(dispatch.dispatchEvent, init));
2169
2492
  break;
2170
2493
  }
2494
+ case "stopPropagation": {
2495
+ event?.stopPropagation();
2496
+ break;
2497
+ }
2498
+ case "preventDefault": {
2499
+ event?.preventDefault();
2500
+ break;
2501
+ }
2171
2502
  default: {
2172
2503
  break;
2173
2504
  }
@@ -2230,28 +2561,99 @@ function checkSchemaVersion($schema) {
2230
2561
  async function Jx(source, target = document.body, options) {
2231
2562
  const base = options?.base ? new URL(options.base, location.href).href : typeof source === "string" ? new URL(source, location.href).href : location.href;
2232
2563
  const doc = await resolve(source);
2564
+ if (doc.$media) {
2565
+ _rootMedia = doc.$media;
2566
+ }
2567
+ const mounted = await mount(doc, target, {
2568
+ base,
2569
+ ...options?.onNodeCreated ? { onNodeCreated: options.onNodeCreated } : {},
2570
+ ...options?.signal ? { signal: options.signal } : {}
2571
+ });
2572
+ return mounted.scope;
2573
+ }
2574
+ var HOST_SCOPE_RESERVED = /^[$#]/;
2575
+ function assertHostScope(scope) {
2576
+ for (const key of Object.keys(scope)) {
2577
+ if (HOST_SCOPE_RESERVED.test(key)) {
2578
+ throw new TypeError(`Jx mount: host scope key "${key}" is reserved — names beginning with "$" or "#" are ` + `runtime vocabulary`);
2579
+ }
2580
+ }
2581
+ }
2582
+ async function mount(doc, target, options = {}) {
2583
+ const hostScope = options.scope ?? {};
2584
+ assertHostScope(hostScope);
2585
+ const base = options.base ? new URL(options.base, location.href).href : location.href;
2586
+ const ctx = {
2587
+ base,
2588
+ media: options.media ?? doc.$media ?? null,
2589
+ root: null,
2590
+ ...options.resolver ? { resolver: options.resolver } : {},
2591
+ ...options.skipServerFunctions === undefined ? {} : { skipServerFunctions: options.skipServerFunctions },
2592
+ ...options.skipAutoRequests === undefined ? {} : { skipAutoRequests: options.skipAutoRequests }
2593
+ };
2233
2594
  checkSchemaVersion(doc.$schema);
2234
2595
  if (doc.$elements) {
2235
- await registerElements(doc.$elements, base);
2596
+ await registerElements(doc.$elements, base, ctx);
2236
2597
  }
2237
2598
  if (doc.$head) {
2238
2599
  injectHead(doc.$head, base);
2239
2600
  }
2240
- if (doc.$media) {
2241
- _rootMedia = doc.$media;
2242
- }
2243
- const state = await buildScope(doc, {}, base);
2244
- target.append(renderNode(doc, state, options));
2245
- if (typeof state.onMount === "function") {
2246
- state.onMount(state);
2601
+ const scope = await buildScope(doc, hostScope, base, ctx);
2602
+ const elements = new Set;
2603
+ const renderOptions = {
2604
+ _ctx: ctx,
2605
+ onNodeCreated: (el, path, def, state) => {
2606
+ if (el instanceof Element && el.tagName.includes("-")) {
2607
+ elements.add(el.tagName.toLowerCase());
2608
+ }
2609
+ options.onNodeCreated?.(el, path, def, state);
2610
+ },
2611
+ ...options.onNodeMoved ? { onNodeMoved: options.onNodeMoved } : {}
2612
+ };
2613
+ const { result: root, stop } = runScoped(() => renderNode(doc, scope, renderOptions));
2614
+ ctx.root = root;
2615
+ let mounted = false;
2616
+ let disposed = false;
2617
+ const dispose = () => {
2618
+ if (disposed) {
2619
+ return;
2620
+ }
2621
+ disposed = true;
2622
+ options.signal?.removeEventListener("abort", dispose);
2623
+ stop();
2624
+ if (mounted && typeof scope.onUnmount === "function") {
2625
+ scope.onUnmount(scope);
2626
+ }
2627
+ root.remove();
2628
+ };
2629
+ if (options.signal?.aborted) {
2630
+ dispose();
2631
+ } else {
2632
+ options.signal?.addEventListener("abort", dispose, { once: true });
2633
+ target.append(root);
2634
+ mounted = true;
2635
+ if (typeof scope.onMount === "function") {
2636
+ scope.onMount(scope);
2637
+ }
2247
2638
  }
2248
- return state;
2639
+ return { scope, root, elements, dispose };
2249
2640
  }
2250
2641
  var _resolveCache = new Map;
2251
- async function resolve(source) {
2642
+ function preloadDocument(url, doc) {
2643
+ const settled = Promise.resolve(doc);
2644
+ _resolveCache.set(url, settled);
2645
+ try {
2646
+ _resolveCache.set(new URL(url, location.href).href, settled);
2647
+ } catch {}
2648
+ }
2649
+ async function resolve(source, ctx) {
2252
2650
  if (typeof source !== "string") {
2253
2651
  return source;
2254
2652
  }
2653
+ const served = ctx?.resolver?.(source);
2654
+ if (served) {
2655
+ return served;
2656
+ }
2255
2657
  if (_resolveCache.has(source)) {
2256
2658
  return _resolveCache.get(source);
2257
2659
  }
@@ -2337,10 +2739,36 @@ var _canvasDelinkAnchors = false;
2337
2739
  function setCanvasDelinkAnchors(on) {
2338
2740
  _canvasDelinkAnchors = on;
2339
2741
  }
2742
+ var _canvasDelinkPopovers = false;
2743
+ function setCanvasDelinkPopovers(on) {
2744
+ _canvasDelinkPopovers = on;
2745
+ }
2746
+ var _canvasDelinkCommands = false;
2747
+ function setCanvasDelinkCommands(on) {
2748
+ _canvasDelinkCommands = on;
2749
+ }
2750
+ var _canvasInsideStampedHost = false;
2751
+ function canvasStamped(el) {
2752
+ return _canvasInsideStampedHost || el.dataset.jxPath !== undefined;
2753
+ }
2340
2754
  function canvasAttrName(el, key) {
2341
2755
  if (_canvasDelinkAnchors && key === "href" && (el.tagName === "A" || el.tagName === "AREA")) {
2342
2756
  return "data-jx-href";
2343
2757
  }
2758
+ if (_canvasDelinkPopovers && key === "popover" && canvasStamped(el)) {
2759
+ return "data-jx-popover";
2760
+ }
2761
+ if (_canvasDelinkCommands && canvasStamped(el)) {
2762
+ if (key === "commandfor") {
2763
+ return "data-jx-commandfor";
2764
+ }
2765
+ if (key === "inert") {
2766
+ return "data-jx-inert";
2767
+ }
2768
+ if (key === "open" && el.tagName === "DIALOG") {
2769
+ return "data-jx-open";
2770
+ }
2771
+ }
2344
2772
  return key;
2345
2773
  }
2346
2774
  var _canvasAssetResolver = null;
@@ -2420,71 +2848,84 @@ function boundPropKey(key, val, state) {
2420
2848
  }
2421
2849
  return sub;
2422
2850
  }
2423
- async function buildScope(doc, parentScope = {}, base = location.href) {
2851
+ async function buildScope(doc, parentScope = {}, base = location.href, ctx) {
2852
+ const dispatchRoot = () => ctx?.root ?? null;
2424
2853
  const raw = {};
2425
- for (const [key, val] of Object.entries(parentScope)) {
2426
- raw[key] = val;
2427
- }
2428
2854
  const defs = doc.state ?? {};
2855
+ const linked = isReactive(parentScope);
2856
+ for (const key of Object.keys(parentScope)) {
2857
+ if (linked && !(key in defs)) {
2858
+ Object.defineProperty(raw, key, {
2859
+ configurable: true,
2860
+ enumerable: true,
2861
+ get: () => parentScope[key],
2862
+ set: (value) => {
2863
+ parentScope[key] = value;
2864
+ }
2865
+ });
2866
+ } else {
2867
+ raw[key] = parentScope[key];
2868
+ }
2869
+ }
2429
2870
  const imports = doc.imports ?? {};
2430
- for (const [, def2] of Object.entries(defs)) {
2431
- if (isPrototypeDef(def2) && !def2.$src) {
2432
- const mapped = imports[def2.$prototype];
2871
+ for (const [, def] of Object.entries(defs)) {
2872
+ if (isPrototypeDef(def) && !def.$src) {
2873
+ const mapped = imports[def.$prototype];
2433
2874
  if (mapped) {
2434
2875
  if (!mapped.endsWith(".class.json")) {
2435
- console.warn(`Jx: import "${def2.$prototype}" must map to a .class.json path, got "${mapped}"`);
2876
+ console.warn(`Jx: import "${def.$prototype}" must map to a .class.json path, got "${mapped}"`);
2436
2877
  continue;
2437
2878
  }
2438
- def2.$src = mapped;
2879
+ def.$src = mapped;
2439
2880
  }
2440
2881
  }
2441
2882
  }
2442
- for (const [key, def2] of Object.entries(defs)) {
2443
- if (typeof def2 === "string") {
2444
- if (!def2.includes("${")) {
2445
- raw[key] = def2;
2883
+ for (const [key, def] of Object.entries(defs)) {
2884
+ if (typeof def === "string") {
2885
+ if (!def.includes("${")) {
2886
+ raw[key] = def;
2446
2887
  }
2447
2888
  continue;
2448
2889
  }
2449
- if (typeof def2 === "number" || typeof def2 === "boolean" || def2 === null) {
2450
- raw[key] = def2;
2890
+ if (typeof def === "number" || typeof def === "boolean" || def === null) {
2891
+ raw[key] = def;
2451
2892
  continue;
2452
2893
  }
2453
- if (Array.isArray(def2)) {
2454
- raw[key] = def2;
2894
+ if (Array.isArray(def)) {
2895
+ raw[key] = def;
2455
2896
  continue;
2456
2897
  }
2457
- if (typeof def2 === "object") {
2458
- if (def2.$prototype) {
2898
+ if (typeof def === "object") {
2899
+ if (def.$prototype) {
2459
2900
  continue;
2460
2901
  }
2461
- if (isExpressionDef(def2)) {
2902
+ if (isExpressionDef(def)) {
2462
2903
  continue;
2463
2904
  }
2464
- if (isServerFnDef(def2)) {
2905
+ if (isServerFnDef(def)) {
2465
2906
  continue;
2466
2907
  }
2467
- if ("default" in def2) {
2468
- raw[key] = def2.default;
2908
+ if ("default" in def) {
2909
+ raw[key] = def.default;
2469
2910
  continue;
2470
2911
  }
2471
- if (hasSchemaKeywords(def2)) {
2912
+ if (hasSchemaKeywords(def)) {
2472
2913
  continue;
2473
2914
  }
2474
- raw[key] = def2;
2915
+ raw[key] = def;
2475
2916
  }
2476
2917
  }
2477
2918
  const state = reactive(raw);
2478
- for (const [key, def2] of Object.entries(defs)) {
2479
- if (typeof def2 === "string" && def2.includes("${")) {
2480
- state[key] = computed(() => evaluateTemplate(def2, state));
2919
+ for (const [key, def] of Object.entries(defs)) {
2920
+ if (typeof def === "string" && def.includes("${")) {
2921
+ state[key] = computed(() => evaluateTemplate(def, state));
2481
2922
  }
2482
2923
  }
2483
- for (const [key, def2] of Object.entries(defs)) {
2484
- if (isExpressionDef(def2)) {
2485
- const node = def2.$expression;
2486
- if (isNamedFormulaDef(def2)) {
2487
- const params = def2.parameters;
2924
+ for (const [key, def] of Object.entries(defs)) {
2925
+ if (isExpressionDef(def)) {
2926
+ const node = def.$expression;
2927
+ if (isNamedFormulaDef(def)) {
2928
+ const params = def.parameters;
2488
2929
  state[key] = (...argValues) => {
2489
2930
  const args = {};
2490
2931
  for (const [i, p] of params.entries()) {
@@ -2504,11 +2945,11 @@ async function buildScope(doc, parentScope = {}, base = location.href) {
2504
2945
  }
2505
2946
  }
2506
2947
  }
2507
- for (const [key, def2] of Object.entries(defs)) {
2508
- if (hasStructuredBody(def2)) {
2509
- const { body } = def2;
2510
- if (Array.isArray(def2.parameters) && def2.parameters.length > 0) {
2511
- const params = def2.parameters;
2948
+ for (const [key, def] of Object.entries(defs)) {
2949
+ if (hasStructuredBody(def)) {
2950
+ const { body } = def;
2951
+ if (Array.isArray(def.parameters) && def.parameters.length > 0) {
2952
+ const params = def.parameters;
2512
2953
  state[key] = (...argValues) => {
2513
2954
  const args = {};
2514
2955
  for (const [i, p] of params.entries()) {
@@ -2518,34 +2959,35 @@ async function buildScope(doc, parentScope = {}, base = location.href) {
2518
2959
  }
2519
2960
  args[name] = argValues[i] === undefined && typeof p === "object" && p !== null && "default" in p ? p.default : argValues[i];
2520
2961
  }
2521
- return runStatements(body, state, null, { args });
2962
+ return runStatements(body, state, null, { args, target: dispatchRoot });
2522
2963
  };
2523
2964
  } else {
2524
2965
  const handler = (s, event) => {
2525
- runStatements(body, s, event ?? null);
2966
+ runStatements(body, s, event ?? null, { target: dispatchRoot });
2526
2967
  };
2527
2968
  state[key] = handler;
2528
2969
  }
2529
- } else if (isFunctionDef(def2)) {
2530
- state[key] = await resolveFunction(def2, state, key, base);
2970
+ } else if (isFunctionDef(def)) {
2971
+ state[key] = await resolveFunction(def, state, key, base);
2531
2972
  }
2532
2973
  }
2533
- for (const [key, def2] of Object.entries(defs)) {
2534
- if (isPrototypeDef(def2)) {
2535
- state[key] = await resolvePrototype(def2, state, key, base);
2974
+ for (const [key, def] of Object.entries(defs)) {
2975
+ if (isPrototypeDef(def)) {
2976
+ state[key] = await resolvePrototype(def, state, key, base, ctx);
2536
2977
  }
2537
2978
  }
2538
- if (!_serverFnConfig.skip) {
2539
- for (const [key, def2] of Object.entries(defs)) {
2540
- if (isServerFnDef(def2)) {
2541
- state[key] = await resolveServerFunction(def2, state, key, base);
2979
+ if (!(ctx?.skipServerFunctions ?? _serverFnConfig.skip)) {
2980
+ for (const [key, def] of Object.entries(defs)) {
2981
+ if (isServerFnDef(def)) {
2982
+ state[key] = await resolveServerFunction(def, state, key, base);
2542
2983
  }
2543
2984
  }
2544
2985
  }
2986
+ const rootMedia = ctx?.media ?? _rootMedia;
2545
2987
  if (doc.$media) {
2546
2988
  state["$media"] = doc.$media;
2547
- } else if (!state["$media"] && Object.keys(_rootMedia).length > 0) {
2548
- state["$media"] = _rootMedia;
2989
+ } else if (!state["$media"] && Object.keys(rootMedia).length > 0) {
2990
+ state["$media"] = rootMedia;
2549
2991
  }
2550
2992
  return state;
2551
2993
  }
@@ -2589,36 +3031,72 @@ function evaluateAttrTemplate(str, state) {
2589
3031
  return fn(state, $map, $map?.item, $map?.index);
2590
3032
  }
2591
3033
  var _moduleCache = new Map;
2592
- async function resolveFunction(def2, state, key, base) {
2593
- if (def2.body && def2.$src) {
3034
+ var _moduleLoaders = new Map;
3035
+ function preloadModule(specifier, mod) {
3036
+ if (typeof mod === "function") {
3037
+ _moduleCache.delete(specifier);
3038
+ _moduleLoaders.set(specifier, mod);
3039
+ return;
3040
+ }
3041
+ _moduleLoaders.delete(specifier);
3042
+ _moduleCache.set(specifier, mod);
3043
+ }
3044
+ function seededModule(src) {
3045
+ const cached = _moduleCache.get(src);
3046
+ if (cached) {
3047
+ return cached;
3048
+ }
3049
+ const pending = _moduleLoaders.get(src);
3050
+ if (!pending) {
3051
+ return;
3052
+ }
3053
+ if (typeof pending !== "function") {
3054
+ return pending;
3055
+ }
3056
+ const load = Promise.resolve(pending()).then((mod) => {
3057
+ _moduleCache.set(src, mod);
3058
+ _moduleLoaders.delete(src);
3059
+ return mod;
3060
+ }, (error) => {
3061
+ _moduleLoaders.set(src, pending);
3062
+ throw error;
3063
+ });
3064
+ _moduleLoaders.set(src, load);
3065
+ return load;
3066
+ }
3067
+ async function resolveFunction(def, state, key, base) {
3068
+ if (def.body && def.$src) {
2594
3069
  throw new Error(`Jx: '${key}' declares both body and $src — these are mutually exclusive`);
2595
3070
  }
2596
- if (!def2.body && !def2.$src) {
2597
- const params = resolveParamNames(def2);
3071
+ if (!def.body && !def.$src) {
3072
+ const params = resolveParamNames(def);
2598
3073
  const noop = new Function(...params, "");
2599
3074
  Object.defineProperty(noop, "name", {
2600
3075
  configurable: true,
2601
- value: def2.name ?? key
3076
+ value: def.name ?? key
2602
3077
  });
2603
3078
  return noop;
2604
3079
  }
2605
3080
  let fn;
2606
- if (typeof def2.body === "string") {
2607
- const params = resolveParamNames(def2);
2608
- fn = new Function(...params, def2.body);
3081
+ if (typeof def.body === "string") {
3082
+ const params = resolveParamNames(def);
3083
+ fn = new Function(...params, def.body);
2609
3084
  Object.defineProperty(fn, "name", {
2610
3085
  configurable: true,
2611
- value: def2.name ?? key
3086
+ value: def.name ?? key
2612
3087
  });
2613
3088
  } else {
2614
- const src = def2.$src;
3089
+ const src = def.$src;
2615
3090
  if (!src) {
2616
3091
  throw new Error(`Jx: '${key}' has neither body nor $src`);
2617
3092
  }
2618
- const exportName = def2.$export ?? key;
3093
+ const exportName = def.$export ?? key;
2619
3094
  let mod;
2620
- if (_moduleCache.has(src)) {
2621
- mod = _moduleCache.get(src);
3095
+ const seeded = seededModule(src);
3096
+ if (seeded instanceof Promise) {
3097
+ mod = await seeded;
3098
+ } else if (seeded) {
3099
+ mod = seeded;
2622
3100
  } else {
2623
3101
  if (base) {
2624
3102
  const resolvedSrc = new URL(src, base).href;
@@ -2638,11 +3116,11 @@ async function resolveFunction(def2, state, key, base) {
2638
3116
  }
2639
3117
  fn = candidate;
2640
3118
  }
2641
- const hasParams = (def2.parameters ?? def2.arguments ?? []).length > 0;
3119
+ const hasParams = (def.parameters ?? def.arguments ?? []).length > 0;
2642
3120
  let isComputed = false;
2643
3121
  if (!hasParams) {
2644
- if (typeof def2.body === "string") {
2645
- isComputed = bodyReturnsValue(def2.body);
3122
+ if (typeof def.body === "string") {
3123
+ isComputed = bodyReturnsValue(def.body);
2646
3124
  } else if (fn) {
2647
3125
  isComputed = fn.length <= 1 && bodyReturnsValue(fn.toString());
2648
3126
  }
@@ -2652,8 +3130,8 @@ async function resolveFunction(def2, state, key, base) {
2652
3130
  }
2653
3131
  return fn;
2654
3132
  }
2655
- function resolveParamNames(def2) {
2656
- const names = def2.parameters ? paramNames(def2.parameters) : def2.arguments ?? [];
3133
+ function resolveParamNames(def) {
3134
+ const names = def.parameters ? paramNames(def.parameters) : def.arguments ?? [];
2657
3135
  return names.length > 0 && names[0] === "state" ? names : ["state", ...names];
2658
3136
  }
2659
3137
  var RESERVED_KEYS = new Set([
@@ -2702,19 +3180,19 @@ function elementNamespace(tagName, inherited) {
2702
3180
  }
2703
3181
  return inherited ?? null;
2704
3182
  }
2705
- function renderNode(def2, state, options) {
3183
+ function renderNode(def, state, options) {
2706
3184
  const path = options?._path ?? [];
2707
- if (typeof def2 === "string" || typeof def2 === "number" || typeof def2 === "boolean") {
2708
- const textNode = document.createTextNode(String(def2));
2709
- if (typeof def2 === "string" && isTemplateString(def2)) {
3185
+ if (typeof def === "string" || typeof def === "number" || typeof def === "boolean") {
3186
+ const textNode = document.createTextNode(String(def));
3187
+ if (typeof def === "string" && isTemplateString(def)) {
2710
3188
  effect(() => {
2711
- textNode.textContent = evaluateTemplate(def2, state);
3189
+ textNode.textContent = evaluateTemplate(def, state);
2712
3190
  });
2713
3191
  }
2714
3192
  return textNode;
2715
3193
  }
2716
3194
  let localState = state;
2717
- for (const [key, val] of Object.entries(def2)) {
3195
+ for (const [key, val] of Object.entries(def)) {
2718
3196
  if (key.startsWith("$") && !RESERVED_KEYS.has(key)) {
2719
3197
  if (localState === state) {
2720
3198
  localState = Object.create(state);
@@ -2722,38 +3200,41 @@ function renderNode(def2, state, options) {
2722
3200
  localState[key] = isRefObj(val) ? resolveRef(val.$ref, state) : val;
2723
3201
  }
2724
3202
  }
2725
- if ("$ref" in def2 && !def2.tagName) {
2726
- const refTarget = String(def2.$ref ?? "");
3203
+ if ("$ref" in def && !def.tagName) {
3204
+ const refTarget = String(def.$ref ?? "");
2727
3205
  if (!warnedRefChildren.has(refTarget)) {
2728
3206
  warnedRefChildren.add(refTarget);
2729
3207
  console.warn(`Jx: a $ref child ("${refTarget}") is not a supported component instance; register the ` + `component in $elements and use its custom-element tag instead (spec §13).`);
2730
3208
  }
2731
3209
  }
2732
- const tagName = resolveTagName(def2.tagName, localState);
3210
+ const tagName = resolveTagName(def.tagName, localState);
2733
3211
  const isCustomEl = tagName.includes("-") && customElements.get(tagName);
2734
- if (def2.$props && isCustomEl) {
2735
- return renderCustomElementWithProps(def2, localState, options, path);
3212
+ if (def.$props && isCustomEl) {
3213
+ return renderCustomElementWithProps(def, localState, options, path);
2736
3214
  }
2737
- if (def2.$props) {
3215
+ if (def.$props) {
2738
3216
  if (tagName.includes("-")) {
2739
- return renderCustomElementWithProps(def2, localState, options, path);
3217
+ return renderCustomElementWithProps(def, localState, options, path);
2740
3218
  }
2741
- const { $props: _$props, ...rest } = def2;
2742
- return renderNode(rest, mergeProps(def2, localState), options);
3219
+ const { $props: _$props, ...rest } = def;
3220
+ return renderNode(rest, mergeProps(def, localState), options);
2743
3221
  }
2744
- if (def2.$switch) {
2745
- return renderSwitch(def2, localState, options);
3222
+ if (def.$switch) {
3223
+ return renderSwitch(def, localState, options);
2746
3224
  }
2747
3225
  const ns = elementNamespace(tagName, options?._ns);
2748
3226
  const childNs = tagName === "foreignObject" ? null : ns;
2749
3227
  const el = ns ? document.createElementNS(ns, tagName) : document.createElement(tagName);
2750
3228
  if (options?.onNodeCreated) {
2751
- options.onNodeCreated(el, path, def2, localState);
3229
+ options.onNodeCreated(el, path, def, localState);
3230
+ }
3231
+ applyProperties(el, def, localState);
3232
+ if (def.style && tagName.includes("-")) {
3233
+ callSiteStyles.set(el, def.style);
2752
3234
  }
2753
- applyProperties(el, def2, localState);
2754
- applyStyle(el, def2.style ?? {}, localState["$media"] ?? {}, localState);
2755
- applyAttributes(el, def2.attributes ?? {}, localState);
2756
- const kids = def2.children;
3235
+ applyStyle(el, def.style ?? {}, localState["$media"] ?? {}, localState);
3236
+ applyAttributes(el, def.attributes ?? {}, localState);
3237
+ const kids = def.children;
2757
3238
  if (isMappedArray(kids)) {
2758
3239
  const arrOpts = options || childNs ? { ...options, _ns: childNs, _path: [...path, "children"] } : undefined;
2759
3240
  renderMappedArrayInto(el, kids, localState, arrOpts);
@@ -2770,49 +3251,58 @@ function renderNode(def2, state, options) {
2770
3251
  }
2771
3252
  return el;
2772
3253
  }
2773
- function applyProperties(el, def2, state) {
2774
- for (const [key, val] of Object.entries(def2)) {
3254
+ function applyProperties(el, def, state) {
3255
+ for (const [key, val] of Object.entries(def)) {
2775
3256
  if (RESERVED_KEYS.has(key)) {
2776
3257
  continue;
2777
3258
  }
2778
3259
  if (key.startsWith("$")) {
2779
3260
  continue;
2780
3261
  }
2781
- if (key.startsWith("on")) {
2782
- if (isRefObj(val)) {
2783
- const handler = resolveRef(val.$ref, state);
2784
- if (typeof handler === "function") {
2785
- const scope = state;
2786
- const handlerFn = handler;
2787
- el.addEventListener(key.slice(2), (e) => handlerFn(scope, e));
2788
- }
2789
- continue;
2790
- }
2791
- if (hasStructuredBody(val)) {
2792
- const { body } = val;
2793
- const scope = state;
2794
- el.addEventListener(key.slice(2), (e) => {
2795
- runStatements(body, scope, e);
2796
- });
2797
- continue;
2798
- }
2799
- if (isFunctionDef(val) && typeof val.body === "string") {
2800
- const params = resolveParamNames(val);
2801
- const fn = new Function(...params, val.body);
2802
- const scope = state;
2803
- el.addEventListener(key.slice(2), (e) => fn(scope, e));
2804
- continue;
2805
- }
2806
- if (isExpressionDef(val)) {
2807
- const node = val.$expression;
2808
- const scope = state;
2809
- el.addEventListener(key.slice(2), (e) => evaluateExpression(node, scope, e));
2810
- continue;
2811
- }
3262
+ if (key.startsWith("on") && bindHandler(el, key, val, state)) {
3263
+ continue;
2812
3264
  }
2813
3265
  bindProperty(el, key, val, state);
2814
3266
  }
2815
3267
  }
3268
+ function bindHandler(el, key, val, state) {
3269
+ const type = key.slice(2);
3270
+ const scope = state;
3271
+ if (isRefObj(val)) {
3272
+ const handler = resolveRef(val.$ref, state);
3273
+ if (typeof handler === "function") {
3274
+ const handlerFn = handler;
3275
+ el.addEventListener(type, (e) => handlerFn(scope, e));
3276
+ }
3277
+ return true;
3278
+ }
3279
+ if (hasStructuredBody(val)) {
3280
+ const { body } = val;
3281
+ el.addEventListener(type, (e) => {
3282
+ runStatements(body, scope, e);
3283
+ });
3284
+ return true;
3285
+ }
3286
+ if (isFunctionDef(val) && typeof val.body === "string") {
3287
+ const params = resolveParamNames(val);
3288
+ const fn = new Function(...params, val.body);
3289
+ el.addEventListener(type, (e) => fn(scope, e));
3290
+ return true;
3291
+ }
3292
+ if (isExpressionDef(val)) {
3293
+ const node = val.$expression;
3294
+ el.addEventListener(type, (e) => evaluateExpression(node, scope, e));
3295
+ return true;
3296
+ }
3297
+ return false;
3298
+ }
3299
+ function bindDefinitionHandlers(host, def, state) {
3300
+ for (const [key, val] of Object.entries(def)) {
3301
+ if (key.startsWith("on") && !RESERVED_KEYS.has(key)) {
3302
+ bindHandler(host, key, val, state);
3303
+ }
3304
+ }
3305
+ }
2816
3306
  function bindProperty(el, key, val, state) {
2817
3307
  const target = el;
2818
3308
  const boundProp = boundPropKey(key, val, state);
@@ -2826,6 +3316,9 @@ function bindProperty(el, key, val, state) {
2826
3316
  node.setAttribute("class", resolved == null ? "" : String(resolved));
2827
3317
  return;
2828
3318
  }
3319
+ if (target[key] === resolved) {
3320
+ return;
3321
+ }
2829
3322
  target[key] = resolved;
2830
3323
  };
2831
3324
  if (isRefObj(val)) {
@@ -2847,144 +3340,300 @@ function bindProperty(el, key, val, state) {
2847
3340
  }
2848
3341
  write(asAsset(val));
2849
3342
  }
2850
- function applyStyle(el, styleDef, mediaQueries = {}, state = {}) {
2851
- const nested = {};
2852
- const media = {};
2853
- const baseDecls = {};
2854
- const mediaOverriddenProps = new Set;
2855
- for (const [prop, val] of Object.entries(styleDef)) {
2856
- if (prop.startsWith("@") && val && typeof val === "object") {
2857
- for (const k of Object.keys(val)) {
2858
- if (!k.startsWith(":") && !k.startsWith(".") && !k.startsWith("&") && !k.startsWith("[") && !k.startsWith("@")) {
2859
- mediaOverriddenProps.add(k);
3343
+ function cssomSink(sheet, detach) {
3344
+ const entries = [];
3345
+ return {
3346
+ detach,
3347
+ insert(texts) {
3348
+ const rules = [];
3349
+ for (const text of texts) {
3350
+ const index = sheet.cssRules.length;
3351
+ try {
3352
+ sheet.insertRule(text, index);
3353
+ } catch {
3354
+ continue;
3355
+ }
3356
+ const rule = sheet.cssRules[index];
3357
+ if (rule) {
3358
+ rules.push(rule);
2860
3359
  }
2861
3360
  }
2862
- }
2863
- }
2864
- for (const [prop, val] of Object.entries(styleDef)) {
2865
- if (val !== null && typeof val === "object" && !Array.isArray(val)) {
2866
- if (prop.startsWith("@")) {
2867
- media[prop] = val;
2868
- } else {
2869
- nested[prop] = val;
3361
+ const inserted = { rules, texts };
3362
+ entries.push(inserted);
3363
+ return inserted;
3364
+ },
3365
+ remove(inserted) {
3366
+ const at = entries.indexOf(inserted);
3367
+ if (at !== -1) {
3368
+ entries.splice(at, 1);
2870
3369
  }
2871
- continue;
2872
- }
2873
- if (prop.startsWith("@") || isNestedSelector(prop)) {
2874
- continue;
2875
- }
2876
- if (val === undefined) {
2877
- continue;
3370
+ if (inserted.rules.length === 0) {
3371
+ return;
3372
+ }
3373
+ const list = [...sheet.cssRules];
3374
+ const indices = inserted.rules.map((rule) => list.indexOf(rule)).filter((index) => index >= 0).toSorted((a, b) => b - a);
3375
+ for (const index of indices) {
3376
+ sheet.deleteRule(index);
3377
+ }
3378
+ inserted.rules.length = 0;
3379
+ },
3380
+ replaceAll(texts) {
3381
+ entries.length = 0;
3382
+ entries.push({ rules: [], texts });
3383
+ sheet.replaceSync(texts.join(`
3384
+ `));
3385
+ },
3386
+ text() {
3387
+ return entries.flatMap((entry) => entry.texts.join(`
3388
+ `)).join(`
3389
+ `);
2878
3390
  }
2879
- const scalar = String(val);
2880
- if (prop.startsWith("--")) {
2881
- if (isTemplateString(val)) {
2882
- effect(() => {
2883
- el.style.setProperty(prop, canvasStyleValue(evaluateTemplate(val, state)));
2884
- });
2885
- } else {
2886
- el.style.setProperty(prop, canvasStyleValue(scalar));
3391
+ };
3392
+ }
3393
+ function textSink(tag) {
3394
+ const entries = [];
3395
+ const write = () => {
3396
+ tag.textContent = entries.map((entry) => entry.texts.join(`
3397
+ `)).join(`
3398
+ `);
3399
+ };
3400
+ return {
3401
+ detach() {
3402
+ tag.remove();
3403
+ },
3404
+ insert(texts) {
3405
+ const inserted = { rules: [], texts };
3406
+ entries.push(inserted);
3407
+ write();
3408
+ return inserted;
3409
+ },
3410
+ remove(inserted) {
3411
+ const index = entries.indexOf(inserted);
3412
+ if (index !== -1) {
3413
+ entries.splice(index, 1);
3414
+ write();
2887
3415
  }
2888
- } else if (isTemplateString(val)) {
2889
- effect(() => {
2890
- el.style[prop] = canvasStyleValue(evaluateTemplate(val, state));
2891
- });
2892
- } else if (mediaOverriddenProps.has(prop)) {
2893
- baseDecls[prop] = scalar;
2894
- } else {
2895
- el.style[prop] = canvasStyleValue(scalar);
3416
+ },
3417
+ replaceAll(texts) {
3418
+ entries.length = 0;
3419
+ entries.push({ rules: [], texts });
3420
+ write();
3421
+ },
3422
+ text() {
3423
+ return tag.textContent ?? "";
2896
3424
  }
3425
+ };
3426
+ }
3427
+ function openSink(doc) {
3428
+ const view = doc.defaultView;
3429
+ const SheetConstructor = view?.CSSStyleSheet;
3430
+ if (typeof SheetConstructor === "function" && Array.isArray(doc.adoptedStyleSheets)) {
3431
+ try {
3432
+ const sheet = new SheetConstructor;
3433
+ doc.adoptedStyleSheets = [...doc.adoptedStyleSheets, sheet];
3434
+ return cssomSink(sheet, () => {
3435
+ doc.adoptedStyleSheets = doc.adoptedStyleSheets.filter((s) => s !== sheet);
3436
+ });
3437
+ } catch {}
3438
+ }
3439
+ const tag = doc.createElement("style");
3440
+ tag.dataset.jxSheet = "";
3441
+ (doc.head ?? doc.documentElement).append(tag);
3442
+ return tag.sheet ? cssomSink(tag.sheet, () => tag.remove()) : textSink(tag);
3443
+ }
3444
+ var sheetStates = new WeakMap;
3445
+ function sheetStateFor(doc) {
3446
+ const existing = sheetStates.get(doc);
3447
+ if (existing) {
3448
+ return existing;
3449
+ }
3450
+ const created = {
3451
+ hoisted: new Map,
3452
+ interned: new Map,
3453
+ scratch: null,
3454
+ scratchOwners: new Map,
3455
+ serial: 0,
3456
+ shared: openSink(doc)
3457
+ };
3458
+ sheetStates.set(doc, created);
3459
+ return created;
3460
+ }
3461
+ var elementStyles = new WeakMap;
3462
+ var SCOPE_TOKEN = "\x01jx-scope\x01";
3463
+ function rewriteScratch(state) {
3464
+ if (!state.scratch) {
3465
+ return;
2897
3466
  }
2898
- const hasNested = Object.keys(nested).length > 0;
2899
- const hasMedia = Object.keys(media).length > 0;
2900
- const hasBaseDecls = Object.keys(baseDecls).length > 0;
2901
- if (!hasNested && !hasMedia && !hasBaseDecls) {
3467
+ state.scratch.replaceAll([...state.scratchOwners.values()].flat());
3468
+ }
3469
+ function releaseInterned(state, table, key) {
3470
+ const entry = table.get(key);
3471
+ if (!entry) {
2902
3472
  return;
2903
3473
  }
2904
- const uid = `jx-${Math.random().toString(36).slice(2, 7)}`;
2905
- el.dataset.jx = uid;
2906
- let css = "";
2907
- const baseCSS = toCSSText(baseDecls);
2908
- if (baseCSS) {
2909
- css += `[data-jx="${uid}"] { ${baseCSS} }
2910
- `;
2911
- }
2912
- function emitNested(scope, rules) {
2913
- const props = toCSSText(rules);
2914
- if (props) {
2915
- css += `${scope} { ${props} }
2916
- `;
2917
- }
2918
- for (const [sel, sub] of Object.entries(rules)) {
2919
- if (sub === null || typeof sub !== "object" || Array.isArray(sub)) {
2920
- continue;
2921
- }
2922
- if (sel.startsWith("@")) {
2923
- continue;
2924
- }
2925
- const resolved = sel.startsWith("&") ? sel.replace("&", scope) : sel.startsWith("[") ? `${scope}${sel}` : sel.startsWith(":") || sel.startsWith(".") ? `${scope}${sel}` : `${scope} ${sel}`;
2926
- emitNested(resolved, sub);
2927
- }
3474
+ entry.refs -= 1;
3475
+ if (entry.refs <= 0) {
3476
+ table.delete(key);
3477
+ state.shared.remove(entry.inserted);
2928
3478
  }
2929
- for (const [sel, rules] of Object.entries(nested)) {
2930
- const resolved = sel.startsWith("&") ? sel.replace("&", `[data-jx="${uid}"]`) : sel.startsWith("[") ? `[data-jx="${uid}"]${sel}` : sel.startsWith(":") || sel.startsWith(".") ? `[data-jx="${uid}"]${sel}` : `[data-jx="${uid}"] ${sel}`;
2931
- emitNested(resolved, rules);
3479
+ }
3480
+ function retainInterned(state, table, key, texts) {
3481
+ const existing = table.get(key);
3482
+ if (existing) {
3483
+ existing.refs += 1;
3484
+ return;
2932
3485
  }
2933
- function emitMediaNested(atRule, parentSel, obj) {
2934
- for (const [sel, sub] of Object.entries(obj)) {
2935
- if (sub === null || typeof sub !== "object" || Array.isArray(sub)) {
2936
- continue;
2937
- }
2938
- if (sel.startsWith("@")) {
2939
- continue;
2940
- }
2941
- const resolved = sel.startsWith("&") ? sel.replace("&", parentSel) : sel.startsWith("[") ? `${parentSel}${sel}` : sel.startsWith(":") || sel.startsWith(".") ? `${parentSel}${sel}` : `${parentSel} ${sel}`;
2942
- const props = toCSSText(sub);
2943
- if (props) {
2944
- css += `${atRule} { ${resolved} { ${props} } }
2945
- `;
2946
- }
2947
- emitMediaNested(atRule, resolved, sub);
2948
- }
3486
+ table.set(key, { inserted: state.shared.insert(texts), refs: 1 });
3487
+ }
3488
+ function releaseElementStyles(el, only) {
3489
+ const entry = elementStyles.get(el);
3490
+ if (!entry || only !== undefined && entry !== only) {
3491
+ return;
2949
3492
  }
2950
- for (const [key, rules] of Object.entries(media)) {
2951
- if (key === "@--") {
2952
- continue;
2953
- }
2954
- const query = resolveAtQuery(key, mediaQueries);
2955
- const atRule = query === null ? key : `@media ${query}`;
2956
- const scope = `[data-jx="${uid}"]`;
2957
- const scheme = query === null ? null : pureSchemeOf(query);
2958
- if (scheme) {
2959
- const { auto, forced } = schemeSelectors(scope, scheme);
2960
- css += `${atRule} { ${auto} { ${toCSSText(rules)} } }
2961
- `;
2962
- emitMediaNested(atRule, auto, rules);
2963
- emitNested(forced, rules);
2964
- } else {
2965
- css += `${atRule} { ${scope} { ${toCSSText(rules)} } }
2966
- `;
2967
- emitMediaNested(atRule, scope, rules);
3493
+ elementStyles.delete(el);
3494
+ entry.stop?.();
3495
+ for (const name of entry.vars) {
3496
+ el.style.removeProperty(name);
3497
+ }
3498
+ delete el.dataset.jx;
3499
+ if (sheetStates.get(entry.doc) !== entry.state) {
3500
+ return;
3501
+ }
3502
+ const { state } = entry;
3503
+ if (entry.interned === null) {
3504
+ if (state.scratchOwners.delete(el)) {
3505
+ rewriteScratch(state);
2968
3506
  }
3507
+ } else {
3508
+ releaseInterned(state, state.interned, entry.interned);
3509
+ }
3510
+ for (const key of entry.hoisted) {
3511
+ releaseInterned(state, state.hoisted, key);
2969
3512
  }
2970
- const tag = document.createElement("style");
2971
- tag.textContent = css;
2972
- tag.dataset.jxOwner = uid;
2973
- document.head.append(tag);
2974
- elementStyleTags.set(el, tag);
2975
3513
  }
2976
- var elementStyleTags = new WeakMap;
3514
+ function resetDocumentStyles(doc = document) {
3515
+ const state = sheetStates.get(doc);
3516
+ if (!state) {
3517
+ return;
3518
+ }
3519
+ sheetStates.delete(doc);
3520
+ for (const el of state.scratchOwners.keys()) {
3521
+ elementStyles.delete(el);
3522
+ delete el.dataset.jx;
3523
+ }
3524
+ state.scratchOwners.clear();
3525
+ state.interned.clear();
3526
+ state.hoisted.clear();
3527
+ state.shared.detach();
3528
+ state.scratch?.detach();
3529
+ }
3530
+ function documentStyleText(doc = document) {
3531
+ const state = sheetStates.get(doc);
3532
+ if (!state) {
3533
+ return "";
3534
+ }
3535
+ return [state.shared.text(), state.scratch?.text() ?? ""].filter(Boolean).join(`
3536
+ `);
3537
+ }
3538
+ function declaresDisplay(style) {
3539
+ return Boolean(style) && typeof style === "object" && "display" in style;
3540
+ }
3541
+ var callSiteStyles = new WeakMap;
3542
+ function mergeStyle(base, over) {
3543
+ if (!over) {
3544
+ return base;
3545
+ }
3546
+ const out = { ...base };
3547
+ for (const [key, value] of Object.entries(over)) {
3548
+ const prior = out[key];
3549
+ out[key] = prior !== null && typeof prior === "object" && value !== null && typeof value === "object" && !Array.isArray(value) ? mergeStyle(prior, value) : value;
3550
+ }
3551
+ return out;
3552
+ }
3553
+ function applyStyle(el, styleDef, mediaQueries = {}, state = {}) {
3554
+ applyStyleInto(el, styleDef, mediaQueries, state, false);
3555
+ }
2977
3556
  function reapplyStyle(el, styleDef, mediaQueries = {}, state = {}) {
2978
- const prev = elementStyleTags.get(el);
2979
- if (prev) {
2980
- prev.remove();
2981
- elementStyleTags.delete(el);
3557
+ applyStyleInto(el, styleDef ?? {}, mediaQueries, state, true);
3558
+ }
3559
+ function applyStyleInto(el, styleDef, mediaQueries, state, live) {
3560
+ releaseElementStyles(el);
3561
+ const doc = el.ownerDocument ?? document;
3562
+ const sheetState = sheetStateFor(doc);
3563
+ const transposeSelector = (_canvasDelinkPopovers || _canvasDelinkCommands) && canvasStamped(el) ? (selector) => transposeCanvasOverlaySelector(selector, {
3564
+ dialog: el.tagName === "DIALOG"
3565
+ }) : (selector) => selector;
3566
+ let serial = -1;
3567
+ let indirect = 0;
3568
+ const sources = [];
3569
+ const rules = buildStyleRules(styleDef, {
3570
+ mediaQueries,
3571
+ resolveValue: (property, value, target) => {
3572
+ if (target === "self" && property.startsWith("--")) {
3573
+ sources.push({ name: property, value });
3574
+ return null;
3575
+ }
3576
+ if (serial < 0) {
3577
+ ({ serial } = sheetState);
3578
+ sheetState.serial += 1;
3579
+ }
3580
+ const name = `--jx-r${serial}-${indirect}`;
3581
+ indirect += 1;
3582
+ sources.push({ name, value });
3583
+ return `var(${name})`;
3584
+ },
3585
+ scope: SCOPE_TOKEN,
3586
+ transposeSelector,
3587
+ transposeValue: canvasStyleValue
3588
+ });
3589
+ if (rules.length === 0 && sources.length === 0) {
3590
+ return;
2982
3591
  }
2983
- el.style.cssText = "";
2984
- delete el.dataset.jx;
2985
- applyStyle(el, styleDef ?? {}, mediaQueries, state);
3592
+ const scoped = rules.filter((rule) => rule.target !== "unscoped");
3593
+ const unscoped = rules.filter((rule) => rule.target === "unscoped");
3594
+ for (const rule of unscoped) {
3595
+ retainInterned(sheetState, sheetState.hoisted, rule.text, [rule.text]);
3596
+ }
3597
+ const uid = `jx-${hashCss(scoped.map((rule) => rule.key).join("|"))}`;
3598
+ const texts = scoped.map((rule) => rule.text.replaceAll(SCOPE_TOKEN, `[data-jx="${uid}"]`));
3599
+ if (texts.length > 0) {
3600
+ el.dataset.jx = uid;
3601
+ if (live) {
3602
+ sheetState.scratch ??= openSink(doc);
3603
+ sheetState.scratchOwners.set(el, texts);
3604
+ rewriteScratch(sheetState);
3605
+ } else {
3606
+ retainInterned(sheetState, sheetState.interned, uid, texts);
3607
+ }
3608
+ }
3609
+ const entry = {
3610
+ doc,
3611
+ hoisted: unscoped.map((rule) => rule.text),
3612
+ interned: texts.length > 0 && !live ? uid : null,
3613
+ state: sheetState,
3614
+ stop: null,
3615
+ uid,
3616
+ vars: sources.map((source) => source.name)
3617
+ };
3618
+ if (sources.length > 0) {
3619
+ const scope = effectScope();
3620
+ scope.run(() => {
3621
+ for (const { name, value } of sources) {
3622
+ effect(() => {
3623
+ const resolved = isRefObj(value) ? resolveRef(value.$ref, state) : evaluateTemplate(value, state);
3624
+ el.style.setProperty(name, canvasStyleValue(String(resolved ?? "")));
3625
+ });
3626
+ }
3627
+ });
3628
+ entry.stop = () => scope.stop();
3629
+ }
3630
+ elementStyles.set(el, entry);
3631
+ onScopeDispose(() => releaseElementStyles(el, entry), true);
2986
3632
  }
2987
3633
  var ENUMERATED_ATTRS = new Set(["contenteditable", "draggable", "spellcheck"]);
3634
+ function enumeratedAttrNames() {
3635
+ return [...ENUMERATED_ATTRS].toSorted();
3636
+ }
2988
3637
  function booleanAttrValue(name, value) {
2989
3638
  const lower = name.toLowerCase();
2990
3639
  if (lower.startsWith("aria-") || ENUMERATED_ATTRS.has(lower)) {
@@ -2996,6 +3645,10 @@ function applyAttributes(el, attrs, state) {
2996
3645
  for (const [k, v] of Object.entries(attrs)) {
2997
3646
  const attr = canvasAttrName(el, k);
2998
3647
  const write = (resolved) => {
3648
+ if (resolved === null || resolved === undefined) {
3649
+ el.removeAttribute(attr);
3650
+ return;
3651
+ }
2999
3652
  if (typeof resolved === "boolean") {
3000
3653
  const text = booleanAttrValue(attr, resolved);
3001
3654
  if (text === null) {
@@ -3005,7 +3658,7 @@ function applyAttributes(el, attrs, state) {
3005
3658
  }
3006
3659
  return;
3007
3660
  }
3008
- el.setAttribute(attr, canvasAssetValue(el.tagName, k, String(resolved ?? "")));
3661
+ el.setAttribute(attr, canvasAssetValue(el.tagName, k, String(resolved)));
3009
3662
  };
3010
3663
  if (isRefObj(v)) {
3011
3664
  effect(() => write(resolveRef(v.$ref, state)));
@@ -3020,8 +3673,69 @@ function renderMappedArrayInto(parentEl, arrayDef, state, options) {
3020
3673
  const path = options?._path ?? [];
3021
3674
  const anchor = document.createComment("jx-array");
3022
3675
  parentEl.append(anchor);
3023
- const { items: itemsSrc, map: mapDef, filter: filterRef, sort: sortRef } = arrayDef;
3024
- effect(() => {
3676
+ const { items: itemsSrc, map: mapDef, filter: filterRef, sort: sortRef, key: keyRef } = arrayDef;
3677
+ const keyPointer = isRefObj(keyRef) ? keyRef.$ref : null;
3678
+ let warned = false;
3679
+ const warnOnce = (message) => {
3680
+ if (!warned) {
3681
+ warned = true;
3682
+ console.warn(`Jx $map: ${message}`);
3683
+ }
3684
+ };
3685
+ let rows = [];
3686
+ const teardown = (row) => {
3687
+ row.scope.stop();
3688
+ row.node.remove();
3689
+ };
3690
+ const createRow = (key, item, index) => {
3691
+ const map = shallowReactive({ index, item });
3692
+ const child = Object.create(state);
3693
+ child.$map = map;
3694
+ Object.defineProperty(child, "$map/item", { enumerable: true, get: () => map.item });
3695
+ Object.defineProperty(child, "$map/index", { enumerable: true, get: () => map.index });
3696
+ const scope = effectScope(true);
3697
+ const childOpts = options ? { ...options, _path: [...path, "map", index] } : undefined;
3698
+ const node = scope.run(() => renderNode(mapDef, child, childOpts));
3699
+ return { key, map, node, scope };
3700
+ };
3701
+ const reconcile = (list, keys) => {
3702
+ const previous = new Map;
3703
+ for (const row of rows) {
3704
+ previous.set(row.key, row);
3705
+ }
3706
+ const next = [];
3707
+ for (let index = 0;index < list.length; index++) {
3708
+ const key = keys[index];
3709
+ const item = list[index];
3710
+ const existing = previous.get(key);
3711
+ if (existing) {
3712
+ previous.delete(key);
3713
+ if (existing.map.item !== item) {
3714
+ existing.map.item = item;
3715
+ }
3716
+ if (existing.map.index !== index) {
3717
+ existing.map.index = index;
3718
+ options?.onNodeMoved?.(existing.node, [...path, "map", index]);
3719
+ }
3720
+ next.push(existing);
3721
+ } else {
3722
+ next.push(createRow(key, item, index));
3723
+ }
3724
+ }
3725
+ for (const row of previous.values()) {
3726
+ teardown(row);
3727
+ }
3728
+ let cursor = rows.find((row) => !previous.has(row.key))?.node ?? anchor;
3729
+ for (const row of next) {
3730
+ if (row.node === cursor) {
3731
+ cursor = cursor.nextSibling ?? anchor;
3732
+ continue;
3733
+ }
3734
+ cursor.before(row.node);
3735
+ }
3736
+ rows = next;
3737
+ };
3738
+ const update = () => {
3025
3739
  let items = isRefObj(itemsSrc) ? resolveRef(itemsSrc.$ref, state) : itemsSrc;
3026
3740
  if (Array.isArray(items) && isRefObj(filterRef)) {
3027
3741
  const fn = resolveRef(filterRef.$ref, state);
@@ -3035,30 +3749,65 @@ function renderMappedArrayInto(parentEl, arrayDef, state, options) {
3035
3749
  items = [...items].toSorted(fn);
3036
3750
  }
3037
3751
  }
3038
- if (!Array.isArray(items) || !mapDef) {
3039
- return;
3040
- }
3041
- const scope = effectScope(true);
3042
- const nodes = [];
3043
- scope.run(() => {
3044
- for (const [index, item] of items.entries()) {
3045
- const child = Object.create(state);
3046
- child.$map = { index, item };
3047
- child["$map/item"] = item;
3048
- child["$map/index"] = index;
3049
- const childOpts = options ? { ...options, _path: [...path, "map", index] } : undefined;
3050
- const node = renderNode(mapDef, child, childOpts);
3051
- anchor.before(node);
3052
- nodes.push(node);
3752
+ const list = Array.isArray(items) && mapDef ? items : [];
3753
+ const keys = list.map((item, index) => mappedRowKey(keyPointer, item, index, warnOnce));
3754
+ const seen = new Set;
3755
+ for (let i = 0;i < keys.length; i++) {
3756
+ if (seen.has(keys[i])) {
3757
+ warnOnce(`duplicate key ${String(keys[i])} — every occurrence after the first is rebuilt on each change`);
3758
+ keys[i] = { duplicate: i };
3759
+ } else {
3760
+ seen.add(keys[i]);
3053
3761
  }
3054
- });
3055
- onEffectCleanup(() => {
3056
- scope.stop();
3057
- for (const n of nodes) {
3058
- n.remove();
3762
+ }
3763
+ pauseTracking();
3764
+ try {
3765
+ reconcile(list, keys);
3766
+ } finally {
3767
+ resetTracking();
3768
+ }
3769
+ };
3770
+ let queued = false;
3771
+ let stopped = false;
3772
+ const runner = effect(update, {
3773
+ scheduler: () => {
3774
+ if (queued) {
3775
+ return;
3059
3776
  }
3060
- });
3777
+ queued = true;
3778
+ queueMicrotask(() => {
3779
+ queued = false;
3780
+ if (!stopped) {
3781
+ runner();
3782
+ }
3783
+ });
3784
+ }
3061
3785
  });
3786
+ onScopeDispose(() => {
3787
+ stopped = true;
3788
+ for (const row of rows) {
3789
+ teardown(row);
3790
+ }
3791
+ rows = [];
3792
+ }, true);
3793
+ }
3794
+ function mappedRowKey(pointer, item, index, warnOnce) {
3795
+ if (pointer === null) {
3796
+ return index;
3797
+ }
3798
+ if (pointer === "$map/item") {
3799
+ return item;
3800
+ }
3801
+ if (pointer.startsWith("$map/item/")) {
3802
+ const key = readPath(item, pointer.slice("$map/item/".length));
3803
+ if (key === undefined || key === null) {
3804
+ warnOnce(`key "${pointer}" is empty for some items — those rows fall back to their index`);
3805
+ return index;
3806
+ }
3807
+ return key;
3808
+ }
3809
+ warnOnce(`key "${pointer}" is not a $map/item pointer — rows fall back to their index`);
3810
+ return index;
3062
3811
  }
3063
3812
  function resolveTagName(tagName, scope) {
3064
3813
  if (typeof tagName === "string") {
@@ -3074,65 +3823,86 @@ function resolveTagName(tagName, scope) {
3074
3823
  const key = evaluateOperand(expression.target, scope, null);
3075
3824
  return expression.cases[String(key)] ?? expression.default;
3076
3825
  }
3077
- function renderSwitch(def2, state, options) {
3826
+ function renderSwitch(def, state, options) {
3078
3827
  const path = options?._path ?? [];
3079
- const container = document.createElement(resolveTagName(def2.tagName, state));
3828
+ const container = document.createElement(resolveTagName(def.tagName, state));
3080
3829
  if (options?.onNodeCreated) {
3081
- options.onNodeCreated(container, path, def2, state);
3830
+ options.onNodeCreated(container, path, def, state);
3082
3831
  }
3083
- applyProperties(container, def2, state);
3084
- applyStyle(container, def2.style ?? {}, state["$media"] ?? {}, state);
3085
- applyAttributes(container, def2.attributes ?? {}, state);
3832
+ applyProperties(container, def, state);
3833
+ applyStyle(container, def.style ?? {}, state["$media"] ?? {}, state);
3834
+ applyAttributes(container, def.attributes ?? {}, state);
3086
3835
  let generation = 0;
3836
+ let live = null;
3837
+ let renderedKey = null;
3838
+ const retire = () => {
3839
+ live?.stop();
3840
+ live = null;
3841
+ };
3087
3842
  effect(() => {
3088
- container.replaceChildren();
3089
- if (!isRefObj(def2.$switch)) {
3843
+ if (!isRefObj(def.$switch)) {
3844
+ retire();
3845
+ container.replaceChildren();
3846
+ generation += 1;
3847
+ return;
3848
+ }
3849
+ const key = String(resolveRef(def.$switch.$ref, state));
3850
+ if (key === renderedKey) {
3090
3851
  return;
3091
3852
  }
3092
- const key = resolveRef(def2.$switch.$ref, state);
3093
- const caseDef = def2.cases?.[key];
3853
+ renderedKey = key;
3854
+ retire();
3855
+ container.replaceChildren();
3856
+ generation += 1;
3857
+ const caseDef = def.cases?.[key];
3094
3858
  if (!caseDef) {
3095
3859
  return;
3096
3860
  }
3861
+ const gen = generation;
3862
+ const scope = effectScope(true);
3863
+ live = scope;
3864
+ const childOpts = options ? { ...options, _path: [...path, "cases", key] } : undefined;
3097
3865
  if (isRefObj(caseDef)) {
3098
- generation += 1;
3099
- const gen = generation;
3100
- const { href } = new URL(caseDef.$ref, location.href);
3101
- resolve(href).then(async (doc) => {
3866
+ const { href } = new URL(caseDef.$ref, options?._ctx?.base ?? location.href);
3867
+ resolve(href, options?._ctx).then(async (doc) => {
3102
3868
  if (gen !== generation) {
3103
3869
  return;
3104
3870
  }
3105
- const childScope = await buildScope(doc, {}, href);
3871
+ const childScope = await buildScope(doc, {}, href, options?._ctx);
3106
3872
  if (gen !== generation) {
3107
3873
  return;
3108
3874
  }
3109
3875
  container.replaceChildren();
3110
- const childOpts2 = options ? { ...options, _path: [...path, "cases", key] } : undefined;
3111
- container.append(renderNode(doc, childScope, childOpts2));
3876
+ scope.run(() => container.append(renderNode(doc, childScope, childOpts)));
3112
3877
  }).catch((error) => console.error("Jx $switch: failed to load external case", caseDef.$ref, error));
3113
3878
  return;
3114
3879
  }
3115
- const childOpts = options ? { ...options, _path: [...path, "cases", key] } : undefined;
3116
- container.append(renderNode(caseDef, state, childOpts));
3880
+ pauseTracking();
3881
+ try {
3882
+ scope.run(() => container.append(renderNode(caseDef, state, childOpts)));
3883
+ } finally {
3884
+ resetTracking();
3885
+ }
3117
3886
  });
3887
+ onScopeDispose(retire, true);
3118
3888
  return container;
3119
3889
  }
3120
- async function resolvePrototype(def2, state, key, base) {
3121
- if (def2.$src) {
3122
- return resolveExternalPrototype(def2, state, key, base);
3890
+ async function resolvePrototype(def, state, key, base, ctx) {
3891
+ if (def.$src) {
3892
+ return resolveExternalPrototype(def, state, key, base);
3123
3893
  }
3124
- switch (def2.$prototype) {
3894
+ switch (def.$prototype) {
3125
3895
  case "Request": {
3126
3896
  const s = ref(null);
3127
- const debounceMs = def2.debounce ?? 0;
3897
+ const debounceMs = def.debounce ?? 0;
3128
3898
  let debounceTimer = null;
3129
- if (!def2.manual && !_autoRequestConfig.skip) {
3899
+ if (!def.manual && !(ctx?.skipAutoRequests ?? _autoRequestConfig.skip)) {
3130
3900
  effect(() => {
3131
3901
  let url;
3132
- if (isTemplateString(def2.url)) {
3133
- url = evaluateTemplate(def2.url, state);
3902
+ if (isTemplateString(def.url)) {
3903
+ url = evaluateTemplate(def.url, state);
3134
3904
  } else {
3135
- ({ url } = def2);
3905
+ ({ url } = def);
3136
3906
  }
3137
3907
  if (!url || url === "undefined" || url.includes("undefined")) {
3138
3908
  return;
@@ -3145,11 +3915,11 @@ async function resolvePrototype(def2, state, key, base) {
3145
3915
  }
3146
3916
  });
3147
3917
  const doFetch = () => fetch(url, {
3148
- method: def2.method ?? "GET",
3918
+ method: def.method ?? "GET",
3149
3919
  signal: controller.signal,
3150
- ...def2.headers && { headers: def2.headers },
3151
- ...def2.body && {
3152
- body: typeof def2.body === "object" ? JSON.stringify(def2.body) : def2.body
3920
+ ...def.headers && { headers: def.headers },
3921
+ ...def.body && {
3922
+ body: typeof def.body === "object" ? JSON.stringify(def.body) : def.body
3153
3923
  }
3154
3924
  }).then((r) => r.ok ? r.json() : Promise.reject(r.statusText)).then((d) => {
3155
3925
  s.value = d;
@@ -3170,7 +3940,7 @@ async function resolvePrototype(def2, state, key, base) {
3170
3940
  case "URLSearchParams": {
3171
3941
  return computed(() => {
3172
3942
  const p = {};
3173
- for (const [k, v] of Object.entries(def2)) {
3943
+ for (const [k, v] of Object.entries(def)) {
3174
3944
  if (k !== "$prototype") {
3175
3945
  p[k] = isRefObj(v) ? resolveRef(v.$ref, state) : isTemplateString(v) ? evaluateTemplate(v, state) : v;
3176
3946
  }
@@ -3180,14 +3950,14 @@ async function resolvePrototype(def2, state, key, base) {
3180
3950
  }
3181
3951
  case "LocalStorage":
3182
3952
  case "SessionStorage": {
3183
- const store = def2.$prototype === "LocalStorage" ? localStorage : sessionStorage;
3184
- const k = def2.key ?? key;
3953
+ const store = def.$prototype === "LocalStorage" ? localStorage : sessionStorage;
3954
+ const k = def.key ?? key;
3185
3955
  let init;
3186
3956
  try {
3187
3957
  const s = store.getItem(k);
3188
- init = s !== null ? JSON.parse(s) : def2.default ?? null;
3958
+ init = s !== null ? JSON.parse(s) : def.default ?? null;
3189
3959
  } catch {
3190
- init = def2.default ?? null;
3960
+ init = def.default ?? null;
3191
3961
  }
3192
3962
  const storageState = ref(init);
3193
3963
  effect(() => {
@@ -3205,7 +3975,7 @@ async function resolvePrototype(def2, state, key, base) {
3205
3975
  return storageState;
3206
3976
  }
3207
3977
  case "Cookie": {
3208
- const name = def2.name ?? key;
3978
+ const name = def.name ?? key;
3209
3979
  const read = () => {
3210
3980
  const raw = readCookie(document.cookie, name);
3211
3981
  if (raw === null) {
@@ -3217,9 +3987,9 @@ async function resolvePrototype(def2, state, key, base) {
3217
3987
  return raw;
3218
3988
  }
3219
3989
  };
3220
- const cookieState = ref(read() ?? def2.default ?? null);
3990
+ const cookieState = ref(read() ?? def.default ?? null);
3221
3991
  effect(() => {
3222
- document.cookie = serializeCookie(name, cookieState.value, def2);
3992
+ document.cookie = serializeCookie(name, cookieState.value, def);
3223
3993
  });
3224
3994
  return cookieState;
3225
3995
  }
@@ -3232,7 +4002,7 @@ async function resolvePrototype(def2, state, key, base) {
3232
4002
  keyPath = "id",
3233
4003
  autoIncrement = true,
3234
4004
  indexes = []
3235
- } = def2;
4005
+ } = def;
3236
4006
  if (!database || !store) {
3237
4007
  throw new Error(`Jx: IndexedDB entry '${key}' requires database and store`);
3238
4008
  }
@@ -3262,28 +4032,28 @@ async function resolvePrototype(def2, state, key, base) {
3262
4032
  return idbState;
3263
4033
  }
3264
4034
  case "Set": {
3265
- return new Set(Array.isArray(def2.default) ? def2.default : []);
4035
+ return new Set(Array.isArray(def.default) ? def.default : []);
3266
4036
  }
3267
4037
  case "Map": {
3268
- return new Map(Object.entries(isJsonObject(def2.default) ? def2.default : {}));
4038
+ return new Map(Object.entries(isJsonObject(def.default) ? def.default : {}));
3269
4039
  }
3270
4040
  case "FormData": {
3271
4041
  const fd = new FormData;
3272
- for (const [k, v] of Object.entries(def2.fields ?? {})) {
4042
+ for (const [k, v] of Object.entries(def.fields ?? {})) {
3273
4043
  fd.append(k, v);
3274
4044
  }
3275
4045
  return fd;
3276
4046
  }
3277
4047
  case "Blob": {
3278
- return new Blob(def2.parts ?? [], {
3279
- type: typeof def2.type === "string" ? def2.type : "text/plain"
4048
+ return new Blob(def.parts ?? [], {
4049
+ type: typeof def.type === "string" ? def.type : "text/plain"
3280
4050
  });
3281
4051
  }
3282
4052
  case "ReadableStream": {
3283
4053
  return null;
3284
4054
  }
3285
4055
  default: {
3286
- console.warn(`Jx: unknown $prototype "${def2.$prototype}" for "${key}". Did you mean to add '$src'?`);
4056
+ console.warn(`Jx: unknown $prototype "${def.$prototype}" for "${key}". Did you mean to add '$src'?`);
3287
4057
  return ref(null);
3288
4058
  }
3289
4059
  }
@@ -3300,17 +4070,20 @@ var EXTERNAL_RESERVED = new Set([
3300
4070
  "arguments",
3301
4071
  "name"
3302
4072
  ]);
3303
- async function resolveExternalPrototype(def2, state, key, base) {
3304
- const src = def2.$src;
4073
+ async function resolveExternalPrototype(def, state, key, base) {
4074
+ const src = def.$src;
3305
4075
  if (!src || !src.endsWith(".class.json")) {
3306
- throw new Error(`Jx: $prototype "${def2.$prototype}" requires a .class.json $src, got "${src}". Wrap the class in a .class.json schema with $implementation.`);
4076
+ throw new Error(`Jx: $prototype "${def.$prototype}" requires a .class.json $src, got "${src}". Wrap the class in a .class.json schema with $implementation.`);
3307
4077
  }
3308
- return resolveClassJson(def2, state, key, base);
4078
+ return resolveClassJson(def, state, key, base);
3309
4079
  }
3310
- async function importAndInstantiate(def2, src, exportName, base) {
4080
+ async function importAndInstantiate(def, src, exportName, base) {
3311
4081
  let mod;
3312
- if (_moduleCache.has(src)) {
3313
- mod = _moduleCache.get(src);
4082
+ const seeded = seededModule(src);
4083
+ if (seeded instanceof Promise) {
4084
+ mod = await seeded;
4085
+ } else if (seeded) {
4086
+ mod = seeded;
3314
4087
  } else {
3315
4088
  try {
3316
4089
  mod = await import(src);
@@ -3332,7 +4105,7 @@ async function importAndInstantiate(def2, src, exportName, base) {
3332
4105
  throw new TypeError(`Jx: "${exportName}" from "${src}" is not a class`);
3333
4106
  }
3334
4107
  const config = {};
3335
- for (const [k, v] of Object.entries(def2)) {
4108
+ for (const [k, v] of Object.entries(def)) {
3336
4109
  if (!EXTERNAL_RESERVED.has(k)) {
3337
4110
  config[k] = v;
3338
4111
  }
@@ -3355,15 +4128,15 @@ async function importAndInstantiate(def2, src, exportName, base) {
3355
4128
  }
3356
4129
  return s;
3357
4130
  }
3358
- async function resolveClassJson(def2, state, key, base) {
3359
- const src = def2.$src;
4131
+ async function resolveClassJson(def, state, key, base) {
4132
+ const src = def.$src;
3360
4133
  if (!src) {
3361
4134
  throw new Error(`Jx: class entry '${key}' has no $src`);
3362
4135
  }
3363
4136
  let classDef;
3364
4137
  const isBareSpecifier = !src.startsWith(".") && !src.startsWith("/") && !src.startsWith("http") && !src.startsWith("file:");
3365
4138
  if (isBareSpecifier) {
3366
- return resolveViaDevProxy(def2, state, key, base);
4139
+ return resolveViaDevProxy(def, state, key, base);
3367
4140
  }
3368
4141
  try {
3369
4142
  const url = base ? new URL(src, base).href : src;
@@ -3373,25 +4146,25 @@ async function resolveClassJson(def2, state, key, base) {
3373
4146
  }
3374
4147
  classDef = await res.json();
3375
4148
  } catch {
3376
- return resolveViaDevProxy(def2, state, key, base);
4149
+ return resolveViaDevProxy(def, state, key, base);
3377
4150
  }
3378
4151
  if (classDef.$implementation) {
3379
4152
  const schemaStr = JSON.stringify(classDef);
3380
4153
  if (schemaStr.includes('"#/$context/')) {
3381
- return resolveViaDevProxy(def2, state, key, base);
4154
+ return resolveViaDevProxy(def, state, key, base);
3382
4155
  }
3383
4156
  const schemaUrl = base ? new URL(src, base).href : new URL(src, location.href).href;
3384
4157
  const implSrc = new URL(classDef.$implementation, schemaUrl).href;
3385
- const exportName = def2.$export ?? classDef.title ?? def2.$prototype;
4158
+ const exportName = def.$export ?? classDef.title ?? def.$prototype;
3386
4159
  try {
3387
- return await importAndInstantiate(def2, implSrc, exportName, base);
4160
+ return await importAndInstantiate(def, implSrc, exportName, base);
3388
4161
  } catch {
3389
- return resolveViaDevProxy(def2, state, key, base);
4162
+ return resolveViaDevProxy(def, state, key, base);
3390
4163
  }
3391
4164
  }
3392
4165
  const DynClass = classFromSchema(classDef);
3393
4166
  const config = {};
3394
- for (const [k, v] of Object.entries(def2)) {
4167
+ for (const [k, v] of Object.entries(def)) {
3395
4168
  if (!EXTERNAL_RESERVED.has(k)) {
3396
4169
  config[k] = v;
3397
4170
  }
@@ -3478,9 +4251,9 @@ function classFromSchema(classDef) {
3478
4251
  const dynCtor = DynClass;
3479
4252
  return dynCtor;
3480
4253
  }
3481
- async function resolveViaDevProxy(def2, state, key, base) {
4254
+ async function resolveViaDevProxy(def, state, key, base) {
3482
4255
  const config = {};
3483
- for (const [k, v] of Object.entries(def2)) {
4256
+ for (const [k, v] of Object.entries(def)) {
3484
4257
  if (!EXTERNAL_RESERVED.has(k)) {
3485
4258
  config[k] = v;
3486
4259
  }
@@ -3489,9 +4262,9 @@ async function resolveViaDevProxy(def2, state, key, base) {
3489
4262
  const doResolve = (resolvedConfig) => fetch(resolveProxyPath("/__jx_resolve__"), {
3490
4263
  body: JSON.stringify({
3491
4264
  $base: base,
3492
- $export: def2.$export,
3493
- $prototype: def2.$prototype,
3494
- $src: def2.$src,
4265
+ $export: def.$export,
4266
+ $prototype: def.$prototype,
4267
+ $src: def.$src,
3495
4268
  ...resolvedConfig
3496
4269
  }),
3497
4270
  headers: { "Content-Type": "application/json" },
@@ -3520,12 +4293,15 @@ async function resolveViaDevProxy(def2, state, key, base) {
3520
4293
  }
3521
4294
  return s;
3522
4295
  }
3523
- async function resolveServerFunction(def2, state, key, base) {
3524
- const src = def2.$src;
3525
- const exportName = def2.$export;
4296
+ async function resolveServerFunction(def, state, key, base) {
4297
+ const src = def.$src;
4298
+ const exportName = def.$export;
3526
4299
  let mod;
3527
- if (_moduleCache.has(src)) {
3528
- mod = _moduleCache.get(src);
4300
+ const seeded = seededModule(src);
4301
+ if (seeded instanceof Promise) {
4302
+ mod = await seeded;
4303
+ } else if (seeded) {
4304
+ mod = seeded;
3529
4305
  } else {
3530
4306
  try {
3531
4307
  mod = await import(src);
@@ -3535,10 +4311,10 @@ async function resolveServerFunction(def2, state, key, base) {
3535
4311
  const resolvedSrc = new URL(src, base).href;
3536
4312
  mod = await import(resolvedSrc);
3537
4313
  } catch {
3538
- return resolveServerFunctionViaProxy(def2, state, key, base);
4314
+ return resolveServerFunctionViaProxy(def, state, key, base);
3539
4315
  }
3540
4316
  } else {
3541
- return resolveServerFunctionViaProxy(def2, state, key, base);
4317
+ return resolveServerFunctionViaProxy(def, state, key, base);
3542
4318
  }
3543
4319
  }
3544
4320
  _moduleCache.set(src, mod);
@@ -3551,7 +4327,7 @@ async function resolveServerFunction(def2, state, key, base) {
3551
4327
  throw new TypeError(`Jx: "${exportName}" from "${src}" is not a function`);
3552
4328
  }
3553
4329
  const fn = candidate;
3554
- const rawArgs = def2.arguments ?? {};
4330
+ const rawArgs = def.arguments ?? {};
3555
4331
  const hasReactiveArg = Object.values(rawArgs).some((v) => isRefObj(v));
3556
4332
  const resolveArgs = () => {
3557
4333
  const args = {};
@@ -3574,8 +4350,8 @@ async function resolveServerFunction(def2, state, key, base) {
3574
4350
  }
3575
4351
  return s;
3576
4352
  }
3577
- async function resolveServerFunctionViaProxy(def2, state, key, base) {
3578
- const rawArgs = def2.arguments ?? {};
4353
+ async function resolveServerFunctionViaProxy(def, state, key, base) {
4354
+ const rawArgs = def.arguments ?? {};
3579
4355
  const hasReactiveArg = Object.values(rawArgs).some((v) => isRefObj(v));
3580
4356
  const resolveArgs = () => {
3581
4357
  const args = {};
@@ -3587,8 +4363,8 @@ async function resolveServerFunctionViaProxy(def2, state, key, base) {
3587
4363
  const doResolve = (args) => fetch(resolveProxyPath("/__jx_server__"), {
3588
4364
  body: JSON.stringify({
3589
4365
  $base: base,
3590
- $export: def2.$export,
3591
- $src: def2.$src,
4366
+ $export: def.$export,
4367
+ $src: def.$src,
3592
4368
  arguments: args
3593
4369
  }),
3594
4370
  headers: { "Content-Type": "application/json" },
@@ -3619,8 +4395,8 @@ function resolveRef(refPath, state) {
3619
4395
  if (typeof refPath !== "string") {
3620
4396
  return refPath;
3621
4397
  }
3622
- if (refPath.startsWith("$map/")) {
3623
- const parts = refPath.split("/");
4398
+ if (refPath.startsWith("$map/") || refPath.startsWith("#/$map/")) {
4399
+ const parts = (refPath.startsWith("#/") ? refPath.slice(2) : refPath).split("/");
3624
4400
  const [, key] = parts;
3625
4401
  const map = state.$map;
3626
4402
  const base = map?.[key] ?? state[`$map/${key}`];
@@ -3646,12 +4422,41 @@ function isSignal(v) {
3646
4422
  function isRefObj(v) {
3647
4423
  return isRef2(v);
3648
4424
  }
3649
- function isNestedSelector(k) {
3650
- return k.startsWith(":") || k.startsWith(".") || k.startsWith("&") || k.startsWith("[");
3651
- }
3652
4425
  function getPath2(obj, path) {
3653
4426
  return readPath(obj, path);
3654
4427
  }
4428
+ function absorbAttribute(state, name, value, defaults) {
4429
+ const camelKey = name.replaceAll(/-([a-z])/g, (_, c) => c.toUpperCase());
4430
+ const current = state[camelKey];
4431
+ if (typeof current === "boolean") {
4432
+ state[camelKey] = value !== null && value !== "false";
4433
+ return;
4434
+ }
4435
+ if (value === null) {
4436
+ const declared = defaults?.get(camelKey);
4437
+ state[camelKey] = declared === undefined ? typeof current === "number" ? 0 : "" : declared;
4438
+ return;
4439
+ }
4440
+ state[camelKey] = typeof current === "number" ? Number(value) : value;
4441
+ }
4442
+ function declaredDefaults(def) {
4443
+ const out = new Map;
4444
+ for (const [key, entry] of Object.entries(def.state ?? {})) {
4445
+ if (entry !== null && typeof entry === "object") {
4446
+ const obj = entry;
4447
+ if ("default" in obj) {
4448
+ out.set(key, obj["default"]);
4449
+ continue;
4450
+ }
4451
+ if (COMPUTED_STATE_KEYS.some((marker) => (marker in obj))) {
4452
+ continue;
4453
+ }
4454
+ }
4455
+ out.set(key, entry);
4456
+ }
4457
+ return out;
4458
+ }
4459
+ var COMPUTED_STATE_KEYS = ["$expression", "$prototype", "$ref", "$src"];
3655
4460
  var _privatePropWarned = new Set;
3656
4461
  function instanceSupplies(el, key) {
3657
4462
  if (Object.hasOwn(el, key)) {
@@ -3677,9 +4482,9 @@ function refusePrivateProp(key, where) {
3677
4482
  function _resetPrivatePropWarnings() {
3678
4483
  _privatePropWarned.clear();
3679
4484
  }
3680
- function mergeProps(def2, parentState) {
4485
+ function mergeProps(def, parentState) {
3681
4486
  const child = Object.create(parentState);
3682
- for (const [k, v] of Object.entries(def2.$props ?? {})) {
4487
+ for (const [k, v] of Object.entries(def.$props ?? {})) {
3683
4488
  if (refusePrivateProp(k, "$props")) {
3684
4489
  continue;
3685
4490
  }
@@ -3688,14 +4493,37 @@ function mergeProps(def2, parentState) {
3688
4493
  return child;
3689
4494
  }
3690
4495
  function toCSSText(rules) {
3691
- return Object.entries(rules).filter(([k, v]) => !isNestedSelector(k) && (v === null || typeof v !== "object")).map(([p, v]) => `${camelToKebab(p)}: ${canvasStyleValue(String(v))}`).join("; ");
4496
+ return Object.entries(rules).filter(([k, v]) => !isNestedSelectorKey(k) && (v === null || typeof v !== "object")).map(([p, v]) => `${cssPropertyName(p)}: ${canvasStyleValue(String(v))}`).join("; ");
3692
4497
  }
3693
4498
  var _rootMedia = {};
3694
4499
  var _elementDefs = new Map;
4500
+ function elementDefinition(tagName) {
4501
+ return _elementDefs.get(tagName);
4502
+ }
4503
+ async function redefineElement(doc, baseUrl) {
4504
+ const base = baseUrl ?? location.href;
4505
+ const { tagName } = doc;
4506
+ if (!tagName || !tagName.includes("-")) {
4507
+ throw new Error(`Jx redefineElement: tagName "${tagName}" must contain a hyphen`);
4508
+ }
4509
+ if (!customElements.get(tagName)) {
4510
+ await defineElement(doc, base);
4511
+ return;
4512
+ }
4513
+ if (doc.$elements) {
4514
+ await registerElements(doc.$elements, base);
4515
+ }
4516
+ const before = _elementDefs.get(tagName)?.doc.observedAttributes ?? [];
4517
+ const after = doc.observedAttributes ?? [];
4518
+ if (JSON.stringify(before) !== JSON.stringify(after)) {
4519
+ console.warn(`Jx redefineElement: <${tagName}> keeps observedAttributes [${before.join(", ")}] as first defined; [${after.join(", ")}] takes effect only in a fresh realm`);
4520
+ }
4521
+ _elementDefs.set(tagName, { base, doc });
4522
+ }
3695
4523
  function setRootMedia(map) {
3696
4524
  _rootMedia = map ?? {};
3697
4525
  }
3698
- async function registerElements(elements, base) {
4526
+ async function registerElements(elements, base, ctx) {
3699
4527
  for (const entry of elements) {
3700
4528
  if (typeof entry === "string") {
3701
4529
  try {
@@ -3710,7 +4538,7 @@ async function registerElements(elements, base) {
3710
4538
  continue;
3711
4539
  }
3712
4540
  const { href } = new URL(entry.$ref, base);
3713
- const doc = await resolve(href);
4541
+ const doc = await resolve(href, ctx);
3714
4542
  if (!doc.tagName || !doc.tagName.includes("-")) {
3715
4543
  continue;
3716
4544
  }
@@ -3718,7 +4546,7 @@ async function registerElements(elements, base) {
3718
4546
  continue;
3719
4547
  }
3720
4548
  if (doc.$elements) {
3721
- await registerElements(doc.$elements, href);
4549
+ await registerElements(doc.$elements, href, ctx);
3722
4550
  }
3723
4551
  await defineElement(doc, href);
3724
4552
  }
@@ -3780,8 +4608,9 @@ async function defineElement(source, baseUrl) {
3780
4608
  await registerElements(source_.$elements, base);
3781
4609
  }
3782
4610
  _elementDefs.set(tagName, { base, doc: source_ });
3783
- const def2 = source_;
3784
- const observedAttrs = def2.observedAttributes ?? [];
4611
+ const initialDef = source_;
4612
+ const initialBase = base;
4613
+ const observedAttrs = initialDef.observedAttributes ?? [];
3785
4614
  const ElementClass = class extends HTMLElement {
3786
4615
  _jxInitialized = false;
3787
4616
  _state = null;
@@ -3796,7 +4625,15 @@ async function defineElement(source, baseUrl) {
3796
4625
  return;
3797
4626
  }
3798
4627
  this._jxInitialized = true;
3799
- const state = await buildScope(def2, {}, base);
4628
+ const live = _elementDefs.get(tagName) ?? { base: initialBase, doc: initialDef };
4629
+ const def = live.doc;
4630
+ const defBase = live.base;
4631
+ const state = await buildScope(def, {}, defBase, { base: defBase, media: null, root: this });
4632
+ for (const attr of observedAttrs) {
4633
+ if (this.hasAttribute(attr)) {
4634
+ absorbAttribute(state, attr, this.getAttribute(attr));
4635
+ }
4636
+ }
3800
4637
  const propsAttr = this.dataset.jxProps;
3801
4638
  if (propsAttr) {
3802
4639
  try {
@@ -3805,7 +4642,7 @@ async function defineElement(source, baseUrl) {
3805
4642
  if (refusePrivateProp(key, "data-jx-props")) {
3806
4643
  continue;
3807
4644
  }
3808
- if (key in (def2.state ?? {})) {
4645
+ if (key in (def.state ?? {})) {
3809
4646
  state[key] = val;
3810
4647
  }
3811
4648
  }
@@ -3819,12 +4656,12 @@ async function defineElement(source, baseUrl) {
3819
4656
  this.removeAttribute(name);
3820
4657
  continue;
3821
4658
  }
3822
- if (key in (def2.state ?? {})) {
4659
+ if (key in (def.state ?? {})) {
3823
4660
  state[key] = this.getAttribute(name);
3824
4661
  this.removeAttribute(name);
3825
4662
  }
3826
4663
  }
3827
- for (const key of Object.keys(def2.state ?? {})) {
4664
+ for (const key of Object.keys(def.state ?? {})) {
3828
4665
  if (isPrivateStateKey(key)) {
3829
4666
  continue;
3830
4667
  }
@@ -3832,7 +4669,7 @@ async function defineElement(source, baseUrl) {
3832
4669
  state[key] = this[key];
3833
4670
  }
3834
4671
  }
3835
- for (const key of Object.keys(def2.state ?? {})) {
4672
+ for (const key of Object.keys(def.state ?? {})) {
3836
4673
  if (isPrivateStateKey(key)) {
3837
4674
  continue;
3838
4675
  }
@@ -3849,22 +4686,28 @@ async function defineElement(source, baseUrl) {
3849
4686
  this._state = state;
3850
4687
  const slottedChildren = [...this.childNodes];
3851
4688
  this.replaceChildren();
3852
- if (!this.style.display) {
3853
- this.style.display = "block";
3854
- }
3855
- applyStyle(this, def2.style ?? {}, state["$media"] ?? {}, state);
3856
- applyAttributes(this, def2.attributes ?? {}, state);
3857
- if (!Array.isArray(def2.children) && def2.textContent !== undefined) {
3858
- bindProperty(this, "textContent", def2.textContent, state);
3859
- }
3860
- const children = Array.isArray(def2.children) ? def2.children : [];
3861
- for (const childDef of children) {
3862
- this.append(renderNode(childDef, state));
4689
+ const defStyle = declaresDisplay(def.style) ? def.style : { display: "block", ...def.style };
4690
+ const hostStyle = mergeStyle(defStyle, callSiteStyles.get(this));
4691
+ applyStyle(this, hostStyle, state["$media"] ?? {}, state);
4692
+ applyAttributes(this, def.attributes ?? {}, state);
4693
+ bindDefinitionHandlers(this, def, state);
4694
+ if (!Array.isArray(def.children) && def.textContent !== undefined) {
4695
+ bindProperty(this, "textContent", def.textContent, state);
4696
+ }
4697
+ const children = Array.isArray(def.children) ? def.children : [];
4698
+ const wasInside = _canvasInsideStampedHost;
4699
+ _canvasInsideStampedHost ||= this.dataset.jxPath !== undefined;
4700
+ try {
4701
+ for (const childDef of children) {
4702
+ this.append(renderNode(childDef, state));
4703
+ }
4704
+ } finally {
4705
+ _canvasInsideStampedHost = wasInside;
3863
4706
  }
3864
4707
  distributeSlots(this, slottedChildren);
3865
4708
  const { onMount } = state;
3866
4709
  if (typeof onMount === "function") {
3867
- queueMicrotask(() => onMount(state));
4710
+ queueMicrotask(() => onMount(state, this));
3868
4711
  }
3869
4712
  }
3870
4713
  disconnectedCallback() {
@@ -3881,25 +4724,17 @@ async function defineElement(source, baseUrl) {
3881
4724
  if (!this._state || oldVal === newVal) {
3882
4725
  return;
3883
4726
  }
3884
- const camelKey = name.replaceAll(/-([a-z])/g, (_, c) => c.toUpperCase());
3885
- const current = this._state[camelKey];
3886
- if (typeof current === "number") {
3887
- this._state[camelKey] = Number(newVal);
3888
- } else if (typeof current === "boolean") {
3889
- this._state[camelKey] = newVal !== null && newVal !== "false";
3890
- } else {
3891
- this._state[camelKey] = newVal;
3892
- }
4727
+ absorbAttribute(this._state, name, newVal, declaredDefaults(_elementDefs.get(tagName)?.doc ?? initialDef));
3893
4728
  }
3894
4729
  };
3895
4730
  customElements.define(tagName, ElementClass);
3896
4731
  }
3897
- function renderCustomElementWithProps(def2, state, options, path) {
3898
- const el = document.createElement(resolveTagName(def2.tagName, state));
4732
+ function renderCustomElementWithProps(def, state, options, path) {
4733
+ const el = document.createElement(resolveTagName(def.tagName, state));
3899
4734
  if (options?.onNodeCreated) {
3900
- options.onNodeCreated(el, path ?? [], def2, state);
4735
+ options.onNodeCreated(el, path ?? [], def, state);
3901
4736
  }
3902
- for (const [key, val] of Object.entries(def2.$props ?? {})) {
4737
+ for (const [key, val] of Object.entries(def.$props ?? {})) {
3903
4738
  if (refusePrivateProp(key, "$props")) {
3904
4739
  continue;
3905
4740
  }
@@ -3920,19 +4755,25 @@ function renderCustomElementWithProps(def2, state, options, path) {
3920
4755
  write(val);
3921
4756
  }
3922
4757
  }
3923
- applyStyle(el, def2.style ?? {}, state["$media"] ?? {}, state);
3924
- applyAttributes(el, def2.attributes ?? {}, state);
3925
- const children = Array.isArray(def2.children) ? def2.children : [];
4758
+ if (def.style) {
4759
+ callSiteStyles.set(el, def.style);
4760
+ }
4761
+ applyStyle(el, def.style ?? {}, state["$media"] ?? {}, state);
4762
+ applyAttributes(el, def.attributes ?? {}, state);
4763
+ applyProperties(el, def, state);
4764
+ const children = Array.isArray(def.children) ? def.children : [];
3926
4765
  for (let i = 0;i < children.length; i++) {
4766
+ const child = children[i];
3927
4767
  const childOpts = options && path ? { ...options, _path: [...path, "children", i] } : undefined;
3928
- el.append(renderNode(children[i], state, childOpts));
4768
+ if (isMappedArray(child)) {
4769
+ renderMappedArrayInto(el, child, state, childOpts);
4770
+ } else {
4771
+ el.append(renderNode(child, state, childOpts));
4772
+ }
3929
4773
  }
3930
4774
  return el;
3931
4775
  }
3932
4776
  function distributeSlots(host, slottedChildren) {
3933
- if (slottedChildren.length === 0) {
3934
- return;
3935
- }
3936
4777
  const slots = host.querySelectorAll("slot");
3937
4778
  if (slots.length === 0) {
3938
4779
  return;
@@ -3953,12 +4794,7 @@ function distributeSlots(host, slottedChildren) {
3953
4794
  for (const slot of slots) {
3954
4795
  const name = slot.getAttribute("name");
3955
4796
  const matches = name ? named.get(name) ?? [] : unnamed;
3956
- if (matches.length > 0) {
3957
- slot.replaceChildren();
3958
- for (const child of matches) {
3959
- slot.append(child);
3960
- }
3961
- }
4797
+ slot.replaceWith(...matches.length > 0 ? matches : [...slot.childNodes]);
3962
4798
  }
3963
4799
  }
3964
4800
  export {
@@ -3971,19 +4807,35 @@ export {
3971
4807
  applyStyle,
3972
4808
  booleanAttrValue,
3973
4809
  buildScope,
4810
+ buildStyleRules,
3974
4811
  camelToKebab,
3975
4812
  canvasStyleValue,
4813
+ cssPropertyName,
4814
+ cssRuleText,
3976
4815
  defineElement,
3977
- elementStyleTags,
4816
+ documentStyleText,
4817
+ elementDefinition,
4818
+ enumeratedAttrNames,
3978
4819
  hasSchemaKeywords,
4820
+ hashCss,
4821
+ isDeclarationAtRule,
4822
+ isKeyframesAtRule,
4823
+ isNestedSelectorKey,
3979
4824
  isSignal,
3980
4825
  isSingleExpression,
4826
+ mount,
3981
4827
  observeScope,
4828
+ preloadDocument,
4829
+ preloadModule,
3982
4830
  pureSchemeOf,
3983
4831
  reapplyStyle,
4832
+ redefineElement,
4833
+ releaseElementStyles,
3984
4834
  renderNode,
4835
+ resetDocumentStyles,
3985
4836
  resolve,
3986
4837
  resolveAtQuery,
4838
+ resolveNestedSelector,
3987
4839
  resolvePrototype,
3988
4840
  resolveRef,
3989
4841
  resolveTagName,
@@ -3991,6 +4843,8 @@ export {
3991
4843
  schemeSelectors,
3992
4844
  setCanvasAssetResolver,
3993
4845
  setCanvasDelinkAnchors,
4846
+ setCanvasDelinkCommands,
4847
+ setCanvasDelinkPopovers,
3994
4848
  setCanvasViewportTranspose,
3995
4849
  setResolveToken,
3996
4850
  setRootMedia,
@@ -3998,9 +4852,12 @@ export {
3998
4852
  setSkipContentResolution,
3999
4853
  setSkipServerFunctions,
4000
4854
  setStampPropBindings,
4855
+ splitSelectorList,
4001
4856
  toCSSText,
4857
+ transposeCanvasOverlaySelector,
4858
+ transposeCanvasPopoverSelector,
4002
4859
  transposeCanvasUnits
4003
4860
  };
4004
4861
 
4005
- //# debugId=899C7FD9D6AF1F1D64756E2164756E21
4862
+ //# debugId=A89AF23A7F8742C664756E2164756E21
4006
4863
  //# sourceMappingURL=runtime.js.map