@lokascript/compilation-service 3.1.0 → 3.2.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/index.cjs CHANGED
@@ -948,6 +948,37 @@ function makeTypeCheckHandler(negated) {
948
948
  };
949
949
  };
950
950
  }
951
+ function parseOfOperand(ctx, minBp = OF_OPERAND_BP) {
952
+ let right = ctx.parseExpr(minBp);
953
+ if (ctx.peek()?.value === "in") {
954
+ const inToken = ctx.advance();
955
+ const scope = ctx.parseExpr(minBp);
956
+ right = {
957
+ type: "binaryExpression",
958
+ operator: "in",
959
+ left: right,
960
+ right: scope,
961
+ start: right.start,
962
+ end: scope.end ?? inToken.end,
963
+ line: right.line ?? inToken.line,
964
+ column: right.column ?? inToken.column
965
+ };
966
+ }
967
+ return right;
968
+ }
969
+ function ofHandler(left, token, ctx) {
970
+ const right = parseOfOperand(ctx);
971
+ return {
972
+ type: "binaryExpression",
973
+ operator: token.value,
974
+ left,
975
+ right,
976
+ start: left.start,
977
+ end: right.end ?? token.end,
978
+ line: left.line ?? token.line,
979
+ column: left.column ?? token.column
980
+ };
981
+ }
951
982
  function report(kind, err2) {
952
983
  if (typeof console !== "undefined") {
953
984
  console.error(`[hyperfixi] ${kind} threw:`, err2);
@@ -1458,6 +1489,48 @@ function parseMaybeNamedArgument(ctx) {
1458
1489
  const value = ctx.parseExpression();
1459
1490
  return name !== void 0 ? { name, value } : { value };
1460
1491
  }
1492
+ function isNakedNamedArgStart(ctx) {
1493
+ if (ctx.check("{"))
1494
+ return false;
1495
+ if (!ctx.checkIdentifierLike())
1496
+ return false;
1497
+ const next = ctx.peekAt(1);
1498
+ return next !== null && next.value === ":";
1499
+ }
1500
+ function parseNakedNamedArgs(ctx, parseValue, command2) {
1501
+ const properties = [];
1502
+ const startPos = ctx.getPosition();
1503
+ do {
1504
+ if (!ctx.checkIdentifierLike())
1505
+ break;
1506
+ const keyToken = ctx.advance();
1507
+ const key = {
1508
+ type: "identifier",
1509
+ name: keyToken.value,
1510
+ start: keyToken.start,
1511
+ end: keyToken.end,
1512
+ line: keyToken.line,
1513
+ column: keyToken.column
1514
+ };
1515
+ ctx.consume(":", `Expected ':' after property name in ${command2} named arguments`);
1516
+ const value = parseValue();
1517
+ if (value) {
1518
+ properties.push({ key, value });
1519
+ }
1520
+ } while (ctx.match(",") && !ctx.isAtEnd());
1521
+ const endPos = ctx.getPosition();
1522
+ return {
1523
+ type: "objectLiteral",
1524
+ properties,
1525
+ start: startPos.start,
1526
+ end: endPos.end,
1527
+ line: startPos.line,
1528
+ column: startPos.column
1529
+ };
1530
+ }
1531
+ function isGluedBeepBang(beep, bang) {
1532
+ return beep?.value === "beep" && bang?.value === "!" && bang.start === beep.end;
1533
+ }
1461
1534
  function parseTriggerCommand(ctx, identifierNode) {
1462
1535
  const allArgs = [];
1463
1536
  let eventName = "";
@@ -1528,6 +1601,8 @@ function parseTriggerCommand(ctx, identifierNode) {
1528
1601
  const modifiers = {};
1529
1602
  while (!isCommandBoundary(ctx)) {
1530
1603
  if (ctx.check(KEYWORDS.ON) || ctx.check(KEYWORDS.TO)) {
1604
+ if (modifiers["on"])
1605
+ break;
1531
1606
  ctx.advance();
1532
1607
  const target = parseOneArgument(ctx, [KEYWORDS.WITH]);
1533
1608
  if (target)
@@ -1659,6 +1734,8 @@ function parseRepeatCommand(ctx, commandToken) {
1659
1734
  } else if (ctx.check(KEYWORDS.FOREVER)) {
1660
1735
  ctx.advance();
1661
1736
  loopType = KEYWORDS.FOREVER;
1737
+ } else if (ctx.checkIsCommand() || ctx.isCommand(ctx.peek().value)) {
1738
+ loopType = KEYWORDS.FOREVER;
1662
1739
  } else {
1663
1740
  times = ctx.parseExpression();
1664
1741
  if (ctx.check(KEYWORDS.TIMES)) {
@@ -1698,10 +1775,11 @@ function parseRepeatCommand(ctx, commandToken) {
1698
1775
  loopType = result.terminator;
1699
1776
  ctx.advance();
1700
1777
  condition = ctx.parseExpression();
1701
- if (!ctx.check("end")) {
1778
+ if (ctx.check("end")) {
1779
+ ctx.advance();
1780
+ } else if (!ctx.atEndOfInput()) {
1702
1781
  throw new Error('Expected "end" to close repeat block');
1703
1782
  }
1704
- ctx.advance();
1705
1783
  }
1706
1784
  } else {
1707
1785
  const result = ctx.parseCommandListUntilEndOrElse();
@@ -1908,7 +1986,7 @@ function parseIfCommand(ctx, commandToken) {
1908
1986
  }));
1909
1987
  }
1910
1988
  }
1911
- if (!consumedElseIf && !ctx.isAtEnd()) {
1989
+ if (!consumedElseIf && !ctx.atEndOfInput()) {
1912
1990
  ctx.consume(KEYWORDS.END, "Expected 'end' after if block");
1913
1991
  }
1914
1992
  } else {
@@ -1928,7 +2006,6 @@ function parseIfCommand(ctx, commandToken) {
1928
2006
  return CommandNodeBuilder.from(commandToken).withArgs(...args).endingAt(ctx.getPosition()).build();
1929
2007
  }
1930
2008
  function parseForCommand(ctx, commandToken) {
1931
- const args = [];
1932
2009
  let variable = null;
1933
2010
  let collection = null;
1934
2011
  if (ctx.check(KEYWORDS.EACH)) {
@@ -1968,30 +2045,20 @@ function parseForCommand(ctx, commandToken) {
1968
2045
  }
1969
2046
  }
1970
2047
  const commands = ctx.parseCommandListUntilEnd("for");
1971
- args.push({
1972
- type: "identifier",
1973
- name: "for",
1974
- start: commandToken.start,
1975
- end: commandToken.end,
1976
- line: commandToken.line,
1977
- column: commandToken.column
1978
- });
1979
- const forPos = {
2048
+ const pos2 = {
1980
2049
  start: commandToken.start,
1981
2050
  end: commandToken.end,
1982
2051
  line: commandToken.line,
1983
2052
  column: commandToken.column
1984
2053
  };
1985
- args.push(createStringLiteral(variable, forPos));
1986
- args.push(collection);
1987
- if (indexVariable) {
1988
- args.push(createStringLiteral(indexVariable, forPos));
1989
- }
1990
- args.push(createBlock(commands, { ...forPos, end: forPos.end || 0 }));
1991
- return CommandNodeBuilder.from({
1992
- ...commandToken,
1993
- value: "repeat"
1994
- }).withArgs(...args).endingAt(ctx.getPosition()).build();
2054
+ const modifiers = {
2055
+ loopType: toLegacyExpression(createStringLiteral(KEYWORDS.FOR, pos2)),
2056
+ for: toLegacyExpression(createStringLiteral(variable, pos2)),
2057
+ in: collection
2058
+ };
2059
+ if (indexVariable)
2060
+ modifiers["index"] = toLegacyExpression(createStringLiteral(indexVariable, pos2));
2061
+ return CommandNodeBuilder.from({ ...commandToken, value: "repeat" }).withArgs(createBlock(commands, { ...pos2, end: pos2.end || 0 })).withModifiers(modifiers).endingAt(ctx.getPosition()).build();
1995
2062
  }
1996
2063
  function parseMeasureCommand(ctx, identifierNode) {
1997
2064
  const args = [];
@@ -2041,73 +2108,142 @@ function parseMeasureCommand(ctx, identifierNode) {
2041
2108
  return builder.build();
2042
2109
  }
2043
2110
  function parseTransitionCommand(ctx, commandToken) {
2044
- const args = [];
2045
- const modifiers = {};
2046
- let property = null;
2047
- let target = null;
2048
- const leadToken = ctx.peek();
2049
- const startsTarget = ctx.checkAnySelector() || ctx.checkContextVar() || ctx.check("<") || isIdentifierLike(leadToken) && CONTEXT_POSSESSIVES.has(leadToken.value);
2050
- if (startsTarget) {
2051
- const parsed = ctx.parsePrimary();
2052
- if ((parsed.type === "possessiveExpression" || parsed.type === "memberExpression" && !parsed.computed) && parsed.object && parsed.property?.name) {
2053
- target = parsed.object;
2054
- property = {
2055
- type: "string",
2056
- value: parsed.property.name,
2057
- start: leadToken.start || 0,
2058
- end: ctx.getPosition().end,
2059
- line: leadToken.line,
2060
- column: leadToken.column
2061
- };
2062
- } else {
2063
- target = parsed;
2064
- if (ctx.check("'s"))
2065
- ctx.advance();
2066
- }
2067
- }
2068
- const firstToken = ctx.peek();
2069
- if (!property && (isIdentifierLike(firstToken) || firstToken.value === "*")) {
2070
- let propertyValue = "";
2071
- if (ctx.check("*")) {
2072
- propertyValue = "*";
2111
+ const pairs = [];
2112
+ do {
2113
+ pairs.push(parseTransitionPair(ctx));
2114
+ } while (startsAnotherPair(ctx));
2115
+ const [first, ...rest] = pairs;
2116
+ const args = first.owner ? [first.owner, first.property] : [first.property];
2117
+ const modifiers = { to: first.to };
2118
+ if (first.from)
2119
+ modifiers["from"] = first.from;
2120
+ if (rest.length > 0)
2121
+ modifiers["pairs"] = pairsNode(rest);
2122
+ for (let i = 0; i < 3 && !ctx.isAtEnd(); i++) {
2123
+ if (ctx.check("over") && !modifiers["over"]) {
2073
2124
  ctx.advance();
2074
- }
2075
- const hyphenatedName = parseHyphenatedName(ctx);
2076
- if (hyphenatedName) {
2077
- propertyValue += hyphenatedName;
2078
- property = {
2079
- type: "string",
2080
- value: propertyValue,
2081
- start: firstToken.start || 0,
2082
- end: ctx.getPosition().end,
2083
- line: firstToken.line,
2084
- column: firstToken.column
2085
- };
2125
+ modifiers["over"] = ctx.parseExpression();
2126
+ } else if (ctx.check("using") && !modifiers["using"]) {
2127
+ ctx.advance();
2128
+ modifiers["using"] = ctx.parseExpression();
2129
+ } else if (ctx.check(KEYWORDS.WITH) && !modifiers["with"]) {
2130
+ ctx.advance();
2131
+ modifiers["with"] = ctx.parsePrimary();
2132
+ } else {
2133
+ break;
2086
2134
  }
2087
2135
  }
2088
- if (!property) {
2089
- throw new Error("Transition command requires a CSS property");
2136
+ return CommandNodeBuilder.from(commandToken).withArgs(...args).withModifiers(modifiers).endingAt(ctx.getPosition()).build();
2137
+ }
2138
+ function parseTransitionPair(ctx) {
2139
+ const { owner, property } = parseTransitionProperty(ctx);
2140
+ let from = null;
2141
+ if (ctx.check("from")) {
2142
+ ctx.advance();
2143
+ from = ctx.parseExpression();
2090
2144
  }
2091
- if (target)
2092
- args.push(target);
2093
- args.push(property);
2094
2145
  if (!ctx.check(KEYWORDS.TO)) {
2095
2146
  throw new Error('Expected "to" keyword after property in transition command');
2096
2147
  }
2097
2148
  ctx.advance();
2098
- const value = ctx.parseExpression();
2099
- modifiers["to"] = value;
2100
- if (ctx.check("over")) {
2101
- ctx.advance();
2102
- const duration = ctx.parseExpression();
2103
- modifiers["over"] = duration;
2149
+ const to = ctx.parseExpression();
2150
+ return { owner, property, from, to };
2151
+ }
2152
+ function parseTransitionProperty(ctx) {
2153
+ const lead = ctx.peek();
2154
+ if (!lead || isKeyword(lead, [KEYWORDS.TO, "from"])) {
2155
+ throw new Error("Transition command requires a CSS property");
2104
2156
  }
2105
- if (ctx.check(KEYWORDS.WITH)) {
2157
+ const expr = ctx.parseExpression();
2158
+ const split = splitStyleExpression(expr);
2159
+ if (split)
2160
+ return { owner: split.owner, property: propertyNode(split.name, lead, ctx) };
2161
+ let name = "";
2162
+ if (ctx.check("*")) {
2163
+ name = "*";
2106
2164
  ctx.advance();
2107
- const timingFunction = ctx.parsePrimary();
2108
- modifiers["with"] = timingFunction;
2109
2165
  }
2110
- return CommandNodeBuilder.from(commandToken).withArgs(...args).withModifiers(modifiers).endingAt(ctx.getPosition()).build();
2166
+ const hyphenated = parseHyphenatedName(ctx);
2167
+ if (!hyphenated)
2168
+ throw new Error("Transition command requires a CSS property");
2169
+ return { owner: expr, property: propertyNode(name + hyphenated, lead, ctx) };
2170
+ }
2171
+ function splitStyleExpression(node) {
2172
+ const n = node;
2173
+ switch (n.type) {
2174
+ case "selector":
2175
+ return typeof n.value === "string" && n.value.startsWith("*") ? { owner: null, name: n.value } : null;
2176
+ case "identifier":
2177
+ return typeof n.name === "string" && !CONTEXT_REFS.has(n.name) ? { owner: null, name: n.name } : null;
2178
+ case "possessiveExpression":
2179
+ case "memberExpression":
2180
+ return !n.computed && n.object && typeof n.property?.name === "string" ? { owner: n.object, name: n.property.name } : null;
2181
+ case "binaryExpression": {
2182
+ if (n.operator !== "of" || !n.left || !n.right)
2183
+ return null;
2184
+ const inner = splitStyleExpression(n.left);
2185
+ return inner && !inner.owner ? { owner: n.right, name: inner.name } : null;
2186
+ }
2187
+ case "propertyOfExpression":
2188
+ return n.target && typeof n.property?.name === "string" ? { owner: n.target, name: n.property.name } : null;
2189
+ default:
2190
+ return null;
2191
+ }
2192
+ }
2193
+ function propertyNode(name, lead, ctx) {
2194
+ return {
2195
+ type: "string",
2196
+ value: name,
2197
+ start: lead.start || 0,
2198
+ end: ctx.getPosition().end,
2199
+ line: lead.line,
2200
+ column: lead.column
2201
+ };
2202
+ }
2203
+ function startsAnotherPair(ctx) {
2204
+ if (ctx.isAtEnd() || isComment(ctx.peek()))
2205
+ return false;
2206
+ if (isKeyword(ctx.peek(), ["over", "using", KEYWORDS.WITH]))
2207
+ return false;
2208
+ return !isCommandBoundary(ctx, ["catch", "finally", "on"]);
2209
+ }
2210
+ function pairsNode(pairs) {
2211
+ const field = (name, value) => ({
2212
+ key: {
2213
+ type: "identifier",
2214
+ name,
2215
+ start: value.start,
2216
+ end: value.end,
2217
+ line: value.line,
2218
+ column: value.column
2219
+ },
2220
+ value
2221
+ });
2222
+ const elements = pairs.map((pair) => {
2223
+ const properties = [field("property", pair.property), field("to", pair.to)];
2224
+ if (pair.owner)
2225
+ properties.push(field("owner", pair.owner));
2226
+ if (pair.from)
2227
+ properties.push(field("from", pair.from));
2228
+ return {
2229
+ type: "objectLiteral",
2230
+ properties,
2231
+ start: (pair.owner ?? pair.property).start,
2232
+ end: pair.to.end,
2233
+ line: (pair.owner ?? pair.property).line,
2234
+ column: (pair.owner ?? pair.property).column
2235
+ };
2236
+ });
2237
+ const firstEl = elements[0];
2238
+ const lastEl = elements[elements.length - 1];
2239
+ return {
2240
+ type: "arrayLiteral",
2241
+ elements,
2242
+ start: firstEl.start,
2243
+ end: lastEl.end,
2244
+ line: firstEl.line,
2245
+ column: firstEl.column
2246
+ };
2111
2247
  }
2112
2248
  function parseStartCommand(ctx, identifierNode) {
2113
2249
  if (!ctx.match("view")) {
@@ -2354,11 +2490,15 @@ function parseSwapCommand(ctx, identifierNode) {
2354
2490
  const targetExpr = isDelete ? ctx.parseExpression() : parseOneArgument(ctx, [KEYWORDS.WITH]);
2355
2491
  if (targetExpr)
2356
2492
  args.push(targetExpr);
2357
- if (!isDelete && consumeOptionalKeyword(ctx, KEYWORDS.WITH)) {
2493
+ const isMorph = identifierNode.name === "morph";
2494
+ if (!isDelete && (consumeOptionalKeyword(ctx, KEYWORDS.WITH) || isMorph && consumeOptionalKeyword(ctx, KEYWORDS.TO))) {
2358
2495
  const contentExpr = parseOneArgument(ctx, ["using"]);
2359
2496
  if (contentExpr)
2360
2497
  modifiers["with"] = contentExpr;
2361
2498
  }
2499
+ if (isMorph && modifiers["with"] === void 0) {
2500
+ ctx.addError("Expected 'to' and the content to morph into");
2501
+ }
2362
2502
  return CommandNodeBuilder.fromIdentifier(identifierNode).withArgs(...args).withModifiers({ ...modifiers, ...parseViewTransitionTail(ctx) }).endingAt(ctx.getPosition()).build();
2363
2503
  }
2364
2504
  function parseShowHideCommand(ctx, identifierNode) {
@@ -3033,6 +3173,8 @@ function atArgumentBoundary(ctx, grammar) {
3033
3173
  const next = ctx.peek();
3034
3174
  if (ctx.checkIsCommand() && ctx.peekAt(1)?.value === "(")
3035
3175
  return false;
3176
+ if (isGluedBeepBang(next, ctx.peekAt(1)))
3177
+ return false;
3036
3178
  if (isCommandBoundary(ctx, ["catch", "finally", "on"]))
3037
3179
  return true;
3038
3180
  return isKeyword(next, [...grammar.markers]);
@@ -3064,10 +3206,11 @@ function parseDeclaredCommand(ctx, commandToken, name, grammar) {
3064
3206
  }
3065
3207
  }
3066
3208
  const commaList = new Set(grammar.commaList ?? []);
3209
+ const namedArgs = new Set(grammar.namedArgs ?? []);
3067
3210
  while (!ctx.isAtEnd() && isKeyword(ctx.peek(), [...grammar.markers])) {
3068
3211
  const keyword = ctx.advance().value;
3069
3212
  const key = ctx.resolveKeyword(keyword).toLowerCase();
3070
- let value = ctx.parseExpression();
3213
+ let value = namedArgs.has(key) && isNakedNamedArgStart(ctx) ? parseNakedNamedArgs(ctx, () => ctx.parseExpression(), name) : ctx.parseExpression();
3071
3214
  if (value && commaList.has(key) && ctx.check(",")) {
3072
3215
  const elements = [value];
3073
3216
  while (ctx.match(",")) {
@@ -3193,8 +3336,8 @@ function parseFetchCommand(ctx, commandToken) {
3193
3336
  }
3194
3337
  if (ctx.check("with") && !modifiers["with"]) {
3195
3338
  ctx.advance();
3196
- if (isFetchNakedNamedArgStart(ctx)) {
3197
- modifiers["with"] = parseFetchNakedNamedArgs(ctx);
3339
+ if (isNakedNamedArgStart(ctx)) {
3340
+ modifiers["with"] = parseNakedNamedArgs(ctx, () => ctx.parsePrimary(), "fetch");
3198
3341
  } else {
3199
3342
  modifiers["with"] = ctx.parsePrimary();
3200
3343
  }
@@ -3226,45 +3369,6 @@ function parseFetchCommand(ctx, commandToken) {
3226
3369
  }
3227
3370
  return builder.build();
3228
3371
  }
3229
- function isFetchNakedNamedArgStart(ctx) {
3230
- if (ctx.check("{"))
3231
- return false;
3232
- if (!ctx.checkIdentifierLike())
3233
- return false;
3234
- const next = ctx.peekAt(1);
3235
- return next !== null && next.value === ":";
3236
- }
3237
- function parseFetchNakedNamedArgs(ctx) {
3238
- const properties = [];
3239
- const startPos = ctx.getPosition();
3240
- do {
3241
- if (!ctx.checkIdentifierLike())
3242
- break;
3243
- const keyToken = ctx.advance();
3244
- const key = {
3245
- type: "identifier",
3246
- name: keyToken.value,
3247
- start: keyToken.start,
3248
- end: keyToken.end,
3249
- line: keyToken.line,
3250
- column: keyToken.column
3251
- };
3252
- ctx.consume(":", "Expected ':' after property name in fetch named arguments");
3253
- const value = ctx.parsePrimary();
3254
- if (value) {
3255
- properties.push({ key, value });
3256
- }
3257
- } while (ctx.match(",") && !ctx.isAtEnd());
3258
- const endPos = ctx.getPosition();
3259
- return {
3260
- type: "objectLiteral",
3261
- properties,
3262
- start: startPos.start,
3263
- end: endPos.end,
3264
- line: startPos.line,
3265
- column: startPos.column
3266
- };
3267
- }
3268
3372
  function findJsEndBoundary(ctx, startPos) {
3269
3373
  const input = ctx.getInputSlice(startPos);
3270
3374
  if (!input) {
@@ -3378,6 +3482,7 @@ function parseTellCommand(ctx, identifierNode) {
3378
3482
  const commands = [];
3379
3483
  while (!ctx.isAtEnd()) {
3380
3484
  if (ctx.checkIsCommand()) {
3485
+ const word = ctx.peek().value;
3381
3486
  try {
3382
3487
  ctx.advance();
3383
3488
  const cmd = ctx.parseCommand();
@@ -3386,7 +3491,8 @@ function parseTellCommand(ctx, identifierNode) {
3386
3491
  } else {
3387
3492
  break;
3388
3493
  }
3389
- } catch {
3494
+ } catch (error) {
3495
+ ctx.addError(`'${word}' in a tell body failed to parse: ${error instanceof Error ? error.message : String(error)}`);
3390
3496
  break;
3391
3497
  }
3392
3498
  if (ctx.match(KEYWORDS.AND) || ctx.match(KEYWORDS.THEN)) {
@@ -3606,6 +3712,84 @@ function isNodeList(value) {
3606
3712
  function isDocumentFragment(value) {
3607
3713
  return value !== null && typeof value === "object" && "nodeType" in value && value.nodeType === 11 && "childNodes" in value;
3608
3714
  }
3715
+ function beepValues(me, values) {
3716
+ const outputs = values.map(describeBeepValue);
3717
+ const shown = outputs.filter((output) => announce(me, output.value));
3718
+ if (shown.length === 0)
3719
+ return outputs;
3720
+ console.group("\u{1F514} beep! Debug Output");
3721
+ for (const output of shown) {
3722
+ console.log(`Value:`, output.value);
3723
+ console.log(`Type:`, output.type);
3724
+ console.log(`Representation:`, output.representation);
3725
+ console.log("---");
3726
+ }
3727
+ console.groupEnd();
3728
+ return outputs;
3729
+ }
3730
+ function announce(me, value) {
3731
+ const target = me;
3732
+ if (typeof CustomEvent !== "function" || typeof target?.dispatchEvent !== "function")
3733
+ return true;
3734
+ return target.dispatchEvent(new CustomEvent("hyperscript:beep", {
3735
+ bubbles: true,
3736
+ cancelable: true,
3737
+ detail: { element: me, value }
3738
+ }));
3739
+ }
3740
+ function describeBeepValue(value) {
3741
+ return { value, type: typeOf(value), representation: representationOf(value) };
3742
+ }
3743
+ function typeOf(value) {
3744
+ if (value === null)
3745
+ return "null";
3746
+ if (value === void 0)
3747
+ return "undefined";
3748
+ if (Array.isArray(value))
3749
+ return "array";
3750
+ if (isHTMLElement$2(value))
3751
+ return "HTMLElement";
3752
+ if (typeof Element !== "undefined" && value instanceof Element)
3753
+ return "Element";
3754
+ if (typeof Node !== "undefined" && value instanceof Node)
3755
+ return "Node";
3756
+ if (value instanceof Error)
3757
+ return "Error";
3758
+ if (value instanceof Date)
3759
+ return "Date";
3760
+ if (value instanceof RegExp)
3761
+ return "RegExp";
3762
+ return typeof value;
3763
+ }
3764
+ function representationOf(value) {
3765
+ if (value === null)
3766
+ return "null";
3767
+ if (value === void 0)
3768
+ return "undefined";
3769
+ if (Array.isArray(value)) {
3770
+ return `Array(${value.length}) [${value.slice(0, 3).map((v2) => representationOf(v2)).join(", ")}${value.length > 3 ? "..." : ""}]`;
3771
+ }
3772
+ if (isHTMLElement$2(value)) {
3773
+ const el = value;
3774
+ const tag = el.tagName.toLowerCase();
3775
+ const id = el.id ? `#${el.id}` : "";
3776
+ const classes = el.className ? `.${el.className.split(" ").join(".")}` : "";
3777
+ return `<${tag}${id}${classes}/>`;
3778
+ }
3779
+ if (value instanceof Error)
3780
+ return `Error: ${value.message}`;
3781
+ if (typeof value === "string")
3782
+ return value.length > 50 ? `"${value.substring(0, 47)}..."` : `"${value}"`;
3783
+ if (typeof value === "object") {
3784
+ try {
3785
+ const keys = Object.keys(value);
3786
+ return `Object {${keys.slice(0, 3).join(", ")}${keys.length > 3 ? "..." : ""}}`;
3787
+ } catch {
3788
+ return "[Object]";
3789
+ }
3790
+ }
3791
+ return String(value);
3792
+ }
3609
3793
  function convertToNumber(value) {
3610
3794
  if (value === null || value === void 0) {
3611
3795
  return 0;
@@ -3637,6 +3821,16 @@ function convertToNumber(value) {
3637
3821
  }
3638
3822
  return 0;
3639
3823
  }
3824
+ function counterAmount(value) {
3825
+ return typeof value === "number" ? value : parseFloat(String(value));
3826
+ }
3827
+ function counterValue(value) {
3828
+ if (isHTMLElement$2(value)) {
3829
+ const element = value;
3830
+ return counterValue(element.value || element.textContent);
3831
+ }
3832
+ return value ? counterAmount(value) : 0;
3833
+ }
3640
3834
  function getVariableValue(name, context, preferredScope) {
3641
3835
  if (preferredScope === "element") {
3642
3836
  return getElementVar(context, name);
@@ -3738,32 +3932,36 @@ function setElementPropertyValue(propertyPath, value, context) {
3738
3932
  element[property] = value;
3739
3933
  }
3740
3934
  }
3935
+ function isObjectOwner(value) {
3936
+ return typeof value === "object" && value !== null && !Array.isArray(value) && !isHTMLElement$2(value);
3937
+ }
3741
3938
  function getCurrentNumericValue(target, property, scope, context) {
3742
3939
  if (typeof target === "number") {
3743
3940
  return target;
3744
3941
  }
3745
3942
  if (isHTMLElement$2(target)) {
3746
3943
  const element = target;
3944
+ if (property?.startsWith("@"))
3945
+ return counterValue(element.getAttribute(property.slice(1)));
3747
3946
  if (property) {
3748
3947
  if (property.startsWith("data-") || ["id", "class", "title", "alt", "src", "href"].includes(property)) {
3749
- const value = element.getAttribute(property);
3750
- return convertToNumber(value);
3948
+ return counterValue(element.getAttribute(property));
3751
3949
  } else {
3752
- const value = element[property];
3753
- return convertToNumber(value);
3950
+ return counterValue(element[property]);
3754
3951
  }
3755
3952
  } else {
3756
- const value = element.value || element.textContent;
3757
- return convertToNumber(value);
3953
+ return counterValue(element);
3758
3954
  }
3759
3955
  }
3956
+ if (property && isObjectOwner(target)) {
3957
+ return counterValue(target[property]);
3958
+ }
3760
3959
  if (typeof target === "string") {
3761
3960
  if (scope === "element") {
3762
- return convertToNumber(getVariableValue(target, context, "element"));
3961
+ return counterValue(getVariableValue(target, context, "element"));
3763
3962
  }
3764
3963
  if (scope === "global") {
3765
- const value2 = getVariableValue(target, context, "global");
3766
- return convertToNumber(value2);
3964
+ return counterValue(getVariableValue(target, context, "global"));
3767
3965
  }
3768
3966
  if (target.includes(".")) {
3769
3967
  return getElementPropertyValue(target, context);
@@ -3775,14 +3973,17 @@ function getCurrentNumericValue(target, property, scope, context) {
3775
3973
  } else if (target === "you" && context.you) {
3776
3974
  return convertToNumber(context.you.value || 0);
3777
3975
  }
3778
- const value = getVariableValue(target, context);
3779
- return convertToNumber(value);
3976
+ return counterValue(getVariableValue(target, context));
3780
3977
  }
3781
- return convertToNumber(target);
3978
+ return counterValue(target);
3782
3979
  }
3783
3980
  function setTargetValue(target, property, scope, newValue, context) {
3784
3981
  if (isHTMLElement$2(target)) {
3785
3982
  const element = target;
3983
+ if (property?.startsWith("@")) {
3984
+ element.setAttribute(property.slice(1), String(newValue));
3985
+ return;
3986
+ }
3786
3987
  if (property) {
3787
3988
  if (property.startsWith("data-") || ["id", "class", "title", "alt", "src", "href"].includes(property)) {
3788
3989
  element.setAttribute(property, String(newValue));
@@ -3798,6 +3999,10 @@ function setTargetValue(target, property, scope, newValue, context) {
3798
3999
  }
3799
4000
  return;
3800
4001
  }
4002
+ if (property && isObjectOwner(target)) {
4003
+ target[property] = newValue;
4004
+ return;
4005
+ }
3801
4006
  if (typeof target === "string") {
3802
4007
  if (scope === "element") {
3803
4008
  setVariableValue(target, newValue, context, "element");
@@ -3886,13 +4091,6 @@ function isFunction(value) {
3886
4091
  function isInputElement$1(el) {
3887
4092
  return el !== null && typeof el === "object" && "tagName" in el && el.tagName === "INPUT";
3888
4093
  }
3889
- function isFormElement$1(el) {
3890
- if (el === null || typeof el !== "object" || !("tagName" in el)) {
3891
- return false;
3892
- }
3893
- const tag = el.tagName;
3894
- return tag === "INPUT" || tag === "SELECT" || tag === "TEXTAREA";
3895
- }
3896
4094
  function isHTMLElement$1(el) {
3897
4095
  return el !== null && typeof el === "object" && "nodeType" in el && el.nodeType === 1 && "style" in el;
3898
4096
  }
@@ -5208,11 +5406,12 @@ async function evaluateBinaryExpression(node, context) {
5208
5406
  }
5209
5407
  }
5210
5408
  }
5211
- if (leftNode?.type === "selector" && leftNode.fromQuery && typeof leftNode.value === "string") {
5409
+ if (leftNode?.type === "selector" && typeof leftNode.value === "string" && (leftNode.fromQuery || operator === "in" && leftNode.value.startsWith("."))) {
5212
5410
  const root = await evaluateAST(node.right, context);
5213
5411
  const scope = root && typeof root.querySelectorAll === "function" ? root : null;
5214
5412
  if (scope) {
5215
- return Array.from(scope.querySelectorAll(leftNode.value));
5413
+ const query = leftNode.fromQuery ? leftNode.value : escapeSelectorForQuery(leftNode, leftNode.value);
5414
+ return Array.from(scope.querySelectorAll(query));
5216
5415
  }
5217
5416
  }
5218
5417
  }
@@ -5221,6 +5420,12 @@ async function evaluateBinaryExpression(node, context) {
5221
5420
  return getExpr(context, "styleRef").evaluate(context, leftNode.value.slice(1), el);
5222
5421
  }
5223
5422
  if (operator === "of") {
5423
+ if (leftNode?.type === "attributeAccess") {
5424
+ const target = await evaluateAST(node.right, context);
5425
+ if (target == null)
5426
+ return void 0;
5427
+ return getExpr(context, "possessive").evaluate(context, target, `@${leftNode.attributeName}`);
5428
+ }
5224
5429
  const path = propertyPathOf(node.left);
5225
5430
  if (path) {
5226
5431
  const target = await evaluateAST(node.right, context);
@@ -5228,6 +5433,10 @@ async function evaluateBinaryExpression(node, context) {
5228
5433
  for (const seg of path) {
5229
5434
  if (cur == null)
5230
5435
  return void 0;
5436
+ if (isMappableCollection(cur) && !(seg in cur)) {
5437
+ cur = await getExpr(context, "possessive").evaluate(context, cur, seg);
5438
+ continue;
5439
+ }
5231
5440
  cur = isElement(cur) ? getElementProperty$1(cur, seg) : cur[seg];
5232
5441
  }
5233
5442
  return typeof cur === "function" ? cur.bind(target) : cur;
@@ -5252,26 +5461,21 @@ async function evaluateBinaryExpression(node, context) {
5252
5461
  const R = node.ignoringCase ? applyCI(right) : right;
5253
5462
  switch (operator) {
5254
5463
  case "+": {
5255
- let addLeft = left;
5256
- let addRight = right;
5257
5464
  if (node.coerceNumeric) {
5258
- if (typeof addLeft === "string")
5259
- addLeft = convertToNumber(addLeft);
5260
- if (typeof addRight === "string")
5261
- addRight = convertToNumber(addRight);
5465
+ return counterValue(left) + counterAmount(right);
5262
5466
  }
5263
- if (typeof addLeft === "string" || typeof addRight === "string") {
5264
- return String(addLeft ?? "") + String(addRight ?? "");
5467
+ if (typeof left === "string" || typeof right === "string") {
5468
+ return String(left ?? "") + String(right ?? "");
5265
5469
  }
5266
- if (Array.isArray(addLeft)) {
5267
- return Array.isArray(addRight) ? [...addLeft, ...addRight] : [...addLeft, addRight];
5470
+ if (Array.isArray(left)) {
5471
+ return Array.isArray(right) ? [...left, ...right] : [...left, right];
5268
5472
  }
5269
- return unwrapTypedResult(await getExpr(context, "addition").evaluate(context, {
5270
- left: addLeft,
5271
- right: addRight
5272
- }));
5473
+ return unwrapTypedResult(await getExpr(context, "addition").evaluate(context, { left, right }));
5273
5474
  }
5274
5475
  case "-":
5476
+ if (node.coerceNumeric) {
5477
+ return counterValue(left) - counterAmount(right);
5478
+ }
5275
5479
  return unwrapTypedResult(await getExpr(context, "subtraction").evaluate(context, { left, right }));
5276
5480
  case "*":
5277
5481
  return unwrapTypedResult(await getExpr(context, "multiplication").evaluate(context, { left, right }));
@@ -5516,15 +5720,14 @@ async function evaluateAttributeAccessNode(node, context) {
5516
5720
  return `@${attributeName}`;
5517
5721
  }
5518
5722
  async function evaluatePropertyOfExpressionNode(node, context) {
5519
- const propertyNode = node.property;
5520
- if (propertyNode?.type !== "identifier") {
5723
+ const propertyNode2 = node.property;
5724
+ if (propertyNode2?.type !== "identifier") {
5521
5725
  throw new Error('Property name must be an identifier in "the X of Y" pattern');
5522
5726
  }
5523
- const propertyName = propertyNode.name;
5727
+ const propertyName = propertyNode2.name;
5524
5728
  const target = await evaluateAST(node.target, context);
5525
- if (target == null) {
5526
- throw new Error(`Cannot access property "${propertyName}" of ${target}`);
5527
- }
5729
+ if (target == null)
5730
+ return void 0;
5528
5731
  return getExpr(context, "possessive").evaluate(context, target, propertyName);
5529
5732
  }
5530
5733
  async function evaluateTemplateLiteralNode(node, context) {
@@ -5640,6 +5843,9 @@ async function evaluateUnaryExpression(node, context) {
5640
5843
  case "not":
5641
5844
  case "!":
5642
5845
  return logicalExpressions.not.evaluate(context, value);
5846
+ case "beep!":
5847
+ beepValues(context.me, [value]);
5848
+ return value;
5643
5849
  case "no":
5644
5850
  return logicalExpressions.no.evaluate(context, value);
5645
5851
  case "-":
@@ -5727,6 +5933,10 @@ async function evaluateCallExpression(node, context) {
5727
5933
  const args = await resolveCallArgs(node.arguments, funcName, context);
5728
5934
  switch (funcName) {
5729
5935
  case "closest":
5936
+ if (node.closestTo) {
5937
+ const start = unwrapElement(await evaluateAST(node.closestTo, context)) ?? null;
5938
+ return referencesExpressions.closest.evaluate(context, args[0], start);
5939
+ }
5730
5940
  return referencesExpressions.closest.evaluate(context, ...args);
5731
5941
  case "previous":
5732
5942
  case "next": {
@@ -6452,6 +6662,73 @@ function parseAttributeWithValue(expression) {
6452
6662
  const result = parseAttribute(expression);
6453
6663
  return { name: result.name, value: result.value ?? "" };
6454
6664
  }
6665
+ function _parseDurationComponents(value) {
6666
+ const trimmed = value.trim();
6667
+ const match = trimmed.match(/^(\d+(?:\.\d+)?)\s*(s|ms|sec|seconds?|milliseconds?)?$/i);
6668
+ if (!match) {
6669
+ return null;
6670
+ }
6671
+ const number = parseFloat(match[1]);
6672
+ const unit = (match[2] || "ms").toLowerCase();
6673
+ if (unit === "ms" || unit === "millisecond" || unit === "milliseconds") {
6674
+ return { number, milliseconds: Math.floor(number) };
6675
+ } else if (unit === "s" || unit === "sec" || unit === "second" || unit === "seconds") {
6676
+ return { number, milliseconds: Math.floor(number * 1e3) };
6677
+ }
6678
+ return null;
6679
+ }
6680
+ function parseDuration(value, defaultMs = 300) {
6681
+ if (typeof value === "number") {
6682
+ return Math.max(0, Math.floor(value));
6683
+ }
6684
+ if (typeof value === "string") {
6685
+ const result = _parseDurationComponents(value);
6686
+ return result ? result.milliseconds : defaultMs;
6687
+ }
6688
+ return defaultMs;
6689
+ }
6690
+ function parseDurationStrict(value) {
6691
+ if (typeof value === "number") {
6692
+ if (value < 0)
6693
+ throw new Error("Duration must be >= 0");
6694
+ return Math.floor(value);
6695
+ }
6696
+ if (typeof value === "string") {
6697
+ const result = _parseDurationComponents(value);
6698
+ if (!result) {
6699
+ throw new Error(`Invalid duration format: "${value}"`);
6700
+ }
6701
+ return result.milliseconds;
6702
+ }
6703
+ throw new Error(`Invalid duration type: ${typeof value}`);
6704
+ }
6705
+ function parseCSSDurations(durationString) {
6706
+ if (!durationString || durationString === "none") {
6707
+ return [0];
6708
+ }
6709
+ return durationString.split(",").map((duration) => {
6710
+ const value = parseFloat(duration.trim());
6711
+ if (isNaN(value))
6712
+ return 0;
6713
+ if (duration.includes("s") && !duration.includes("ms")) {
6714
+ return value * 1e3;
6715
+ }
6716
+ return value;
6717
+ });
6718
+ }
6719
+ function calculateMaxAnimationTime(durations, delays) {
6720
+ let maxTime = 0;
6721
+ for (let i = 0; i < durations.length; i++) {
6722
+ const duration = durations[i] || 0;
6723
+ const delay = delays[i] || 0;
6724
+ const totalTime = duration + delay;
6725
+ maxTime = Math.max(maxTime, totalTime);
6726
+ }
6727
+ return maxTime;
6728
+ }
6729
+ function camelToKebab(str) {
6730
+ return str.replace(/([A-Z])/g, "-$1").toLowerCase();
6731
+ }
6455
6732
  function batchApply(targets, operation) {
6456
6733
  for (const element of targets) {
6457
6734
  operation(element);
@@ -6514,15 +6791,18 @@ function toggleAttribute(element, name, value) {
6514
6791
  }
6515
6792
  }
6516
6793
  }
6794
+ function toCssPropertyName(property) {
6795
+ return property.startsWith("--") ? property : camelToKebab(property);
6796
+ }
6517
6797
  function batchSetStyles(targets, styles) {
6518
6798
  const entries = Object.entries(styles);
6519
6799
  return batchApplyItems(targets, entries, (element, [property, value]) => {
6520
- element.style.setProperty(property, value);
6800
+ element.style.setProperty(toCssPropertyName(property), value);
6521
6801
  });
6522
6802
  }
6523
6803
  function batchRemoveStyles(targets, properties) {
6524
6804
  return batchApplyItems(targets, properties, (element, property) => {
6525
- element.style.removeProperty(property);
6805
+ element.style.removeProperty(toCssPropertyName(property));
6526
6806
  });
6527
6807
  }
6528
6808
  function parseClasses(classValue) {
@@ -6870,73 +7150,6 @@ function removeElement(element) {
6870
7150
  }
6871
7151
  return false;
6872
7152
  }
6873
- function _parseDurationComponents(value) {
6874
- const trimmed = value.trim();
6875
- const match = trimmed.match(/^(\d+(?:\.\d+)?)\s*(s|ms|sec|seconds?|milliseconds?)?$/i);
6876
- if (!match) {
6877
- return null;
6878
- }
6879
- const number = parseFloat(match[1]);
6880
- const unit = (match[2] || "ms").toLowerCase();
6881
- if (unit === "ms" || unit === "millisecond" || unit === "milliseconds") {
6882
- return { number, milliseconds: Math.floor(number) };
6883
- } else if (unit === "s" || unit === "sec" || unit === "second" || unit === "seconds") {
6884
- return { number, milliseconds: Math.floor(number * 1e3) };
6885
- }
6886
- return null;
6887
- }
6888
- function parseDuration(value, defaultMs = 300) {
6889
- if (typeof value === "number") {
6890
- return Math.max(0, Math.floor(value));
6891
- }
6892
- if (typeof value === "string") {
6893
- const result = _parseDurationComponents(value);
6894
- return result ? result.milliseconds : defaultMs;
6895
- }
6896
- return defaultMs;
6897
- }
6898
- function parseDurationStrict(value) {
6899
- if (typeof value === "number") {
6900
- if (value < 0)
6901
- throw new Error("Duration must be >= 0");
6902
- return Math.floor(value);
6903
- }
6904
- if (typeof value === "string") {
6905
- const result = _parseDurationComponents(value);
6906
- if (!result) {
6907
- throw new Error(`Invalid duration format: "${value}"`);
6908
- }
6909
- return result.milliseconds;
6910
- }
6911
- throw new Error(`Invalid duration type: ${typeof value}`);
6912
- }
6913
- function parseCSSDurations(durationString) {
6914
- if (!durationString || durationString === "none") {
6915
- return [0];
6916
- }
6917
- return durationString.split(",").map((duration) => {
6918
- const value = parseFloat(duration.trim());
6919
- if (isNaN(value))
6920
- return 0;
6921
- if (duration.includes("s") && !duration.includes("ms")) {
6922
- return value * 1e3;
6923
- }
6924
- return value;
6925
- });
6926
- }
6927
- function calculateMaxAnimationTime(durations, delays) {
6928
- let maxTime = 0;
6929
- for (let i = 0; i < durations.length; i++) {
6930
- const duration = durations[i] || 0;
6931
- const delay = delays[i] || 0;
6932
- const totalTime = duration + delay;
6933
- maxTime = Math.max(maxTime, totalTime);
6934
- }
6935
- return maxTime;
6936
- }
6937
- function camelToKebab(str) {
6938
- return str.replace(/([A-Z])/g, "-$1").toLowerCase();
6939
- }
6940
7153
  function detectSmartElementType(elements) {
6941
7154
  if (elements.length === 0)
6942
7155
  return null;
@@ -7221,13 +7434,13 @@ async function resolveAnyPropertyTarget(node, evaluator, context) {
7221
7434
  const anyNode = node;
7222
7435
  if (anyNode?.type === "possessiveExpression" || anyNode?.type === "memberExpression") {
7223
7436
  const objectNode = anyNode.object;
7224
- const propertyNode = anyNode.property;
7437
+ const propertyNode2 = anyNode.property;
7225
7438
  let element = await evaluator.evaluate(objectNode, context);
7226
7439
  if (Array.isArray(element))
7227
7440
  element = element[0];
7228
7441
  if (!isHTMLElement$2(element))
7229
7442
  return null;
7230
- const propertyName = propertyNode?.name || propertyNode?.value;
7443
+ const propertyName = propertyNode2?.name || propertyNode2?.value;
7231
7444
  if (!propertyName)
7232
7445
  return null;
7233
7446
  return { element, property: propertyName };
@@ -7323,6 +7536,19 @@ function detectExpressionType(firstValue, firstArg) {
7323
7536
  }
7324
7537
  return { type: "class", expression: "" };
7325
7538
  }
7539
+ function arrayFragment(items) {
7540
+ const fragment = document.createDocumentFragment();
7541
+ for (const item of items) {
7542
+ if (isInsertableNode(item)) {
7543
+ fragment.append(item);
7544
+ continue;
7545
+ }
7546
+ const template = document.createElement("template");
7547
+ template.innerHTML = item === null ? "" : String(item);
7548
+ fragment.append(template.content);
7549
+ }
7550
+ return fragment;
7551
+ }
7326
7552
  function escapeHtml(value) {
7327
7553
  return value.replace(/[&<>"']/g, (c) => HTML_ESCAPE[c]);
7328
7554
  }
@@ -7653,10 +7879,28 @@ function withViewTransition(callback, options) {
7653
7879
  drain().catch(reject);
7654
7880
  });
7655
7881
  }
7882
+ function loneRootWithTag(content, tagName) {
7883
+ let root;
7884
+ if (typeof content === "string") {
7885
+ const template = document.createElement("template");
7886
+ template.innerHTML = content;
7887
+ root = template.content.firstElementChild;
7888
+ if (!root || root.nextElementSibling)
7889
+ return null;
7890
+ } else {
7891
+ root = content.cloneNode(true);
7892
+ }
7893
+ return root.tagName === tagName && isHTMLElement$2(root) ? root : null;
7894
+ }
7656
7895
  function executeSwap(target, content, strategy, morphOptions) {
7657
7896
  const contentStr = content !== null && !isHTMLElement$2(content) ? content : "";
7658
7897
  const contentEl = isHTMLElement$2(content) ? content : null;
7659
7898
  switch (strategy) {
7899
+ case "morphMerge": {
7900
+ const root = content !== null ? loneRootWithTag(content, target.tagName) : null;
7901
+ executeSwap(target, root ?? content, root ? "morphOuter" : "morph", morphOptions);
7902
+ break;
7903
+ }
7660
7904
  case "morph":
7661
7905
  if (content !== null) {
7662
7906
  try {
@@ -8415,6 +8659,19 @@ async function resolveWriteTarget(node, evaluator, context, options) {
8415
8659
  }
8416
8660
  return { kind: "property", target: property };
8417
8661
  }
8662
+ if (options.ofAndObjectProperty) {
8663
+ const access = ofOrPossessiveAccess(n);
8664
+ if (access) {
8665
+ const owner = await evaluator.evaluate(access.owner, context);
8666
+ const element = Array.isArray(owner) ? owner[0] : owner;
8667
+ if (access.isOf && isHTMLElement$2(element)) {
8668
+ return { kind: "property", target: { element, property: access.name } };
8669
+ }
8670
+ if (isObjectOwner(owner)) {
8671
+ return { kind: "member", container: owner, property: access.name };
8672
+ }
8673
+ }
8674
+ }
8418
8675
  if (options.bareReference && (nodeType === "identifier" || nodeType === "variable" || nodeType === "symbol") && typeof n.name === "string") {
8419
8676
  const rawScope = n.scope;
8420
8677
  const scope = rawScope === "element" || rawScope === "global" ? rawScope : void 0;
@@ -8422,6 +8679,13 @@ async function resolveWriteTarget(node, evaluator, context, options) {
8422
8679
  }
8423
8680
  return null;
8424
8681
  }
8682
+ function ofOrPossessiveAccess(n) {
8683
+ const [owner, property, isOf] = n.type === "possessiveExpression" ? [n.object, n.property, false] : n.type === "propertyOfExpression" ? [n.target, n.property, false] : n.type === "binaryExpression" && n.operator === "of" ? [n.right, n.left, true] : [void 0, void 0, false];
8684
+ const name = property?.name;
8685
+ if (!owner || typeof name !== "string" || !/^[A-Za-z_$][\w$]*$/.test(name))
8686
+ return null;
8687
+ return { owner, name, isOf };
8688
+ }
8425
8689
  function toSetInput(target, value) {
8426
8690
  switch (target.kind) {
8427
8691
  case "node-write":
@@ -8442,6 +8706,13 @@ function toSetInput(target, value) {
8442
8706
  property: target.target.property,
8443
8707
  value
8444
8708
  };
8709
+ case "member":
8710
+ return {
8711
+ type: "member-assignment",
8712
+ container: target.container,
8713
+ property: target.property,
8714
+ value
8715
+ };
8445
8716
  default:
8446
8717
  throw new Error(`set: unrequested write-target rung '${target.kind}'`);
8447
8718
  }
@@ -8451,12 +8722,24 @@ function getNodeType(node) {
8451
8722
  return "unknown";
8452
8723
  return node.type || "unknown";
8453
8724
  }
8725
+ function propertyTarget(node) {
8726
+ const n = node;
8727
+ const owner = n.type === "propertyOfExpression" ? n.target : n.type === "possessiveExpression" || n.type === "memberExpression" || n.type === "propertyAccess" ? n.object : void 0;
8728
+ if (!owner || n.computed)
8729
+ return null;
8730
+ const p = n.property;
8731
+ const name = typeof p === "string" ? p : typeof p?.name === "string" ? p.name : void 0;
8732
+ if (!name || !/^[A-Za-z_$][\w$-]*$/.test(name))
8733
+ return null;
8734
+ return { owner, name };
8735
+ }
8454
8736
  async function parseNumericTargetInput(raw, evaluator, context, commandName) {
8455
8737
  if (!raw.args || raw.args.length === 0) {
8456
8738
  throw new Error(`${commandName} command requires a target`);
8457
8739
  }
8458
8740
  const targetArg = raw.args[0];
8459
8741
  let target;
8742
+ let property;
8460
8743
  let extractedScope;
8461
8744
  let customWrite;
8462
8745
  const nodeType = getNodeType(targetArg);
@@ -8469,7 +8752,20 @@ async function parseNumericTargetInput(raw, evaluator, context, commandName) {
8469
8752
  target = targetArg.value;
8470
8753
  } else {
8471
8754
  const writer = getRegisteredNodeWriter(nodeType);
8472
- if (writer) {
8755
+ const attribute = writer ? null : await resolveAttributeWriteTarget(targetArg, evaluator, context, async () => context.me ? [context.me] : []);
8756
+ const prop = writer || attribute ? null : propertyTarget(targetArg);
8757
+ const owner = prop ? await evaluator.evaluate(prop.owner, context) : void 0;
8758
+ const ownerElement = Array.isArray(owner) ? owner[0] : owner;
8759
+ if (attribute?.elements[0]) {
8760
+ target = attribute.elements[0];
8761
+ property = `@${attribute.name}`;
8762
+ } else if (prop && isHTMLElement$2(ownerElement)) {
8763
+ target = ownerElement;
8764
+ property = prop.name;
8765
+ } else if (prop && isObjectOwner(owner)) {
8766
+ target = owner;
8767
+ property = prop.name;
8768
+ } else if (writer) {
8473
8769
  const currentRaw = await evaluator.evaluate(targetArg, context);
8474
8770
  const currentValue = typeof currentRaw === "number" ? currentRaw : currentRaw == null ? 0 : Number(currentRaw) || 0;
8475
8771
  target = currentValue;
@@ -8498,24 +8794,20 @@ async function parseNumericTargetInput(raw, evaluator, context, commandName) {
8498
8794
  const evaluated = await evaluator.evaluate(arg, context);
8499
8795
  if (typeof evaluated === "number") {
8500
8796
  amount = evaluated;
8797
+ } else if (evaluated == null) {
8798
+ amount = NaN;
8501
8799
  }
8502
8800
  }
8503
8801
  }
8504
8802
  const byModifier = raw.modifiers?.by;
8505
8803
  if (byModifier) {
8506
- const byValue = byModifier.value;
8507
- if (getNodeType(byModifier) === "literal" && typeof byValue === "number") {
8508
- amount = byValue;
8509
- } else if (getNodeType(byModifier) !== "literal") {
8510
- const evaluated = await evaluator.evaluate(byModifier, context);
8511
- if (typeof evaluated === "number") {
8512
- amount = evaluated;
8513
- }
8514
- }
8804
+ const value = getNodeType(byModifier) === "literal" ? byModifier.value : await evaluator.evaluate(byModifier, context);
8805
+ amount = counterAmount(value);
8515
8806
  }
8516
8807
  return {
8517
8808
  target,
8518
8809
  amount,
8810
+ ...property && { property },
8519
8811
  ...scope && { scope },
8520
8812
  ...customWrite && { customWrite }
8521
8813
  };
@@ -8722,7 +9014,10 @@ function createUntilLoopConfig(conditionExpr, evaluateCondition2, context, index
8722
9014
  };
8723
9015
  const config2 = {
8724
9016
  type: "until",
8725
- shouldContinue: () => !evaluateCondition2(conditionExpr, context)
9017
+ shouldContinue: () => {
9018
+ const met = evaluateCondition2(conditionExpr, context);
9019
+ return met instanceof Promise ? met.then((value) => !value) : !met;
9020
+ }
8726
9021
  };
8727
9022
  return { config: config2, iterCtx };
8728
9023
  }
@@ -8757,7 +9052,7 @@ function createForeverLoopConfig(indexVariable, maxIterations = 1e4) {
8757
9052
  };
8758
9053
  return { config: config2, iterCtx };
8759
9054
  }
8760
- async function slotText(node, evaluator, context) {
9055
+ async function slotText$1(node, evaluator, context) {
8761
9056
  if (!node)
8762
9057
  return void 0;
8763
9058
  const n = node;
@@ -8770,6 +9065,18 @@ async function slotText(node, evaluator, context) {
8770
9065
  const v2 = await evaluator.evaluate(node, context);
8771
9066
  return typeof v2 === "string" ? v2 : void 0;
8772
9067
  }
9068
+ function isEventTarget(value) {
9069
+ return typeof value?.addEventListener === "function";
9070
+ }
9071
+ async function evaluateLoopCondition(condition, context) {
9072
+ if (typeof condition === "function")
9073
+ return Boolean(await condition());
9074
+ return evaluateCondition(condition, context);
9075
+ }
9076
+ function loopCount(value) {
9077
+ const count = Number(value);
9078
+ return Number.isNaN(count) ? 0 : count;
9079
+ }
8773
9080
  function createPseudoCommand() {
8774
9081
  return new PseudoCommand();
8775
9082
  }
@@ -8805,6 +9112,28 @@ function namedPropertyOf(node) {
8805
9112
  }
8806
9113
  return null;
8807
9114
  }
9115
+ function elementsOf(value) {
9116
+ if (Array.isArray(value) || isNodeList(value)) {
9117
+ return Array.from(value).filter(isHTMLElement$2);
9118
+ }
9119
+ return isHTMLElement$2(value) ? [value] : [];
9120
+ }
9121
+ async function valueOf(node, evaluator, context) {
9122
+ const value = await evaluator.evaluate(node, context);
9123
+ const name = node.name;
9124
+ if (value === void 0 && node.type === "identifier" && typeof name === "string")
9125
+ return name;
9126
+ return value;
9127
+ }
9128
+ function longestTransitionMs(css) {
9129
+ let longest;
9130
+ for (const item of css.split(",")) {
9131
+ const times = [...item.matchAll(/(\d*\.?\d+)(ms|s)\b/g)].map((m) => parseFloat(m[1]) * (m[2] === "s" ? 1e3 : 1));
9132
+ if (times.length > 0)
9133
+ longest = Math.max(longest ?? 0, times[0] + (times[1] ?? 0));
9134
+ }
9135
+ return longest;
9136
+ }
8808
9137
  function scopedVariableTarget(node) {
8809
9138
  if (node?.["type"] !== "contextReference")
8810
9139
  return null;
@@ -9603,6 +9932,14 @@ function convertNode(node, infer) {
9603
9932
  ...pos(node)
9604
9933
  };
9605
9934
  case "identifier":
9935
+ if (node.scope === "element" || node.scope === "local" || node.scope === "global") {
9936
+ return {
9937
+ type: "variable",
9938
+ name: node.name ?? "",
9939
+ scope: node.scope,
9940
+ ...pos(node)
9941
+ };
9942
+ }
9606
9943
  return {
9607
9944
  type: "identifier",
9608
9945
  value: node.name ?? node.value ?? "",
@@ -9625,6 +9962,15 @@ function convertNode(node, infer) {
9625
9962
  operand: convertNode(node.argument ?? node.operand, infer),
9626
9963
  ...pos(node)
9627
9964
  };
9965
+ case "typeCheckExpression":
9966
+ return {
9967
+ type: "typeCheck",
9968
+ value: convertNode(node.value, infer),
9969
+ typeName: String(node.typeName),
9970
+ nullOk: node.nullOk !== false,
9971
+ negated: node.negated === true,
9972
+ ...pos(node)
9973
+ };
9628
9974
  case "timeExpression":
9629
9975
  return { type: "literal", value: node.value, ...pos(node) };
9630
9976
  case "templateLiteral":
@@ -9636,6 +9982,28 @@ function convertNode(node, infer) {
9636
9982
  scope: node.scope ?? "local",
9637
9983
  ...pos(node)
9638
9984
  };
9985
+ case "arrayLiteral":
9986
+ return {
9987
+ type: "array",
9988
+ elements: (node.elements ?? []).map((element) => convertNode(element, infer)),
9989
+ ...pos(node)
9990
+ };
9991
+ case "attributeAccess":
9992
+ return {
9993
+ type: "possessive",
9994
+ object: { type: "identifier", value: "me" },
9995
+ property: `@${node.attributeName}`,
9996
+ ...pos(node)
9997
+ };
9998
+ case "propertyOfExpression": {
9999
+ const property = node.property;
10000
+ return {
10001
+ type: "possessive",
10002
+ object: convertNode(node.target, infer),
10003
+ property: property?.name ?? property?.value ?? "",
10004
+ ...pos(node)
10005
+ };
10006
+ }
9639
10007
  case "htmlSelector":
9640
10008
  return {
9641
10009
  type: "selector",
@@ -9686,6 +10054,18 @@ function convertCommand$1(node, infer) {
9686
10054
  if (name === "repeat") {
9687
10055
  return convertRepeatCommand(node, infer);
9688
10056
  }
10057
+ const original = node.originalCommand;
10058
+ const sum = node.modifiers?.to;
10059
+ const counted = node.args?.[0];
10060
+ if (name === "set" && (original === "increment" || original === "decrement") && sum?.type === "binaryExpression" && counted) {
10061
+ return convertCommand$1({
10062
+ ...node,
10063
+ name: original,
10064
+ args: [counted],
10065
+ modifiers: { by: sum.right },
10066
+ originalCommand: void 0
10067
+ }, infer);
10068
+ }
9689
10069
  const args = (node.args ?? []).map((arg) => convertNode(arg, infer));
9690
10070
  const target = node.target ? convertNode(node.target, infer) : void 0;
9691
10071
  const modifiers = node.modifiers ? convertModifiers(node.modifiers, infer) : void 0;
@@ -9727,6 +10107,64 @@ function convertIfCommand(node, infer) {
9727
10107
  };
9728
10108
  }
9729
10109
  function convertRepeatCommand(node, infer) {
10110
+ const slots = node.modifiers ?? {};
10111
+ const form = slotText(slots.loopType);
10112
+ if (form === void 0)
10113
+ return convertPositionalRepeat(node, infer);
10114
+ const [bodyBlock, elseBlock] = node.args ?? [];
10115
+ const body = extractBlockCommands(bodyBlock, infer);
10116
+ const indexName = slotText(slots.index);
10117
+ const shared = {
10118
+ ...indexName ? { indexName } : {},
10119
+ ...elseBlock ? { elseBody: extractBlockCommands(elseBlock, infer) } : {},
10120
+ ...pos(node)
10121
+ };
10122
+ const operand = (slot) => slot ? convertNode(slot, infer) : void 0;
10123
+ switch (form) {
10124
+ case "for":
10125
+ return {
10126
+ type: "foreach",
10127
+ itemName: slotText(slots.for) ?? "it",
10128
+ collection: operand(slots.in) ?? { type: "literal", value: null },
10129
+ body,
10130
+ ...shared
10131
+ };
10132
+ case "times":
10133
+ return { type: "repeat", count: operand(slots.times), body, ...shared };
10134
+ case "while":
10135
+ case "until": {
10136
+ const test = operand(slots[form]) ?? { type: "literal", value: form === "until" };
10137
+ return {
10138
+ type: "while",
10139
+ condition: form === "until" ? { type: "unary", operator: "not", operand: test } : test,
10140
+ ...slots.bottomTested?.value === true ? { bottomTested: true } : {},
10141
+ body,
10142
+ ...shared
10143
+ };
10144
+ }
10145
+ case "until-event": {
10146
+ const event = slotText(slots.event);
10147
+ if (!event) {
10148
+ return { type: "error", message: "'repeat until event' has no event name", ...pos(node) };
10149
+ }
10150
+ const target = operand(slots.from);
10151
+ return {
10152
+ type: "repeat",
10153
+ untilEvent: event,
10154
+ ...target ? { untilEventTarget: target } : {},
10155
+ body,
10156
+ ...shared
10157
+ };
10158
+ }
10159
+ default:
10160
+ return { type: "repeat", body, ...shared };
10161
+ }
10162
+ }
10163
+ function slotText(slot) {
10164
+ const text = slot?.value ?? slot?.name;
10165
+ return typeof text === "string" ? text : void 0;
10166
+ }
10167
+ function convertPositionalRepeat(node, infer) {
9730
10168
  const args = node.args ?? [];
9731
10169
  if (args.length === 0) {
9732
10170
  return { type: "repeat", body: [], ...pos(node) };
@@ -9846,13 +10284,13 @@ function buildEventModifiers(node) {
9846
10284
  ...node.selector && !node.from ? { from: node.selector } : {}
9847
10285
  };
9848
10286
  }
9849
- function extractBlockCommands(block, infer) {
9850
- if (!block)
10287
+ function extractBlockCommands(block2, infer) {
10288
+ if (!block2)
9851
10289
  return [];
9852
- if (block.type === "block") {
9853
- return (block.commands ?? []).map((cmd) => convertNode(cmd, infer));
10290
+ if (block2.type === "block") {
10291
+ return (block2.commands ?? []).map((cmd) => convertNode(cmd, infer));
9854
10292
  }
9855
- return [convertNode(block, infer)];
10293
+ return [convertNode(block2, infer)];
9856
10294
  }
9857
10295
  function inferRoles(name, args, modifiers, target, infer) {
9858
10296
  const roles = {};
@@ -10012,6 +10450,15 @@ function toCoreAST(node) {
10012
10450
  prefix: true,
10013
10451
  ...nodePos(node)
10014
10452
  };
10453
+ case "typeCheck":
10454
+ return {
10455
+ type: "typeCheckExpression",
10456
+ value: toCoreAST(node.value),
10457
+ typeName: node.typeName,
10458
+ nullOk: node.nullOk,
10459
+ negated: node.negated,
10460
+ ...nodePos(node)
10461
+ };
10015
10462
  case "member":
10016
10463
  return {
10017
10464
  type: "memberExpression",
@@ -10020,6 +10467,12 @@ function toCoreAST(node) {
10020
10467
  computed: node.computed ?? false,
10021
10468
  ...nodePos(node)
10022
10469
  };
10470
+ case "array":
10471
+ return {
10472
+ type: "arrayLiteral",
10473
+ elements: node.elements.map((element) => toCoreAST(element)),
10474
+ ...nodePos(node)
10475
+ };
10023
10476
  case "possessive":
10024
10477
  return {
10025
10478
  type: "possessiveExpression",
@@ -10079,67 +10532,79 @@ function convertCommand(node) {
10079
10532
  }
10080
10533
  return result;
10081
10534
  }
10535
+ function block(nodes) {
10536
+ return { type: "block", commands: nodes.map((n) => toCoreAST(n)), ...POS };
10537
+ }
10082
10538
  function convertIf(node) {
10083
- const result = {
10539
+ const [elseIf, ...laterElseIfs] = node.elseIfBranches ?? [];
10540
+ const elseBranch = elseIf ? [
10541
+ {
10542
+ type: "if",
10543
+ condition: elseIf.condition,
10544
+ thenBranch: elseIf.body,
10545
+ elseIfBranches: laterElseIfs,
10546
+ ...node.elseBranch ? { elseBranch: node.elseBranch } : {}
10547
+ }
10548
+ ] : node.elseBranch;
10549
+ return {
10084
10550
  type: "command",
10085
10551
  name: "if",
10086
10552
  isBlocking: true,
10087
- condition: toCoreAST(node.condition),
10088
- thenBranch: node.thenBranch.map((n) => toCoreAST(n)),
10089
- args: [],
10553
+ args: [
10554
+ toCoreAST(node.condition),
10555
+ block(node.thenBranch),
10556
+ ...elseBranch ? [block(elseBranch)] : []
10557
+ ],
10090
10558
  ...nodePos(node)
10091
10559
  };
10092
- if (node.elseBranch) {
10093
- result.elseBranch = node.elseBranch.map((n) => toCoreAST(n));
10094
- }
10095
- return result;
10096
10560
  }
10097
- function convertRepeat(node) {
10098
- const result = {
10561
+ function loopCommand(node, form, operands) {
10562
+ const modifiers = { loopType: slotString(form) };
10563
+ for (const [slot, value] of Object.entries(operands)) {
10564
+ if (value)
10565
+ modifiers[slot] = value;
10566
+ }
10567
+ if (node.indexName)
10568
+ modifiers.index = slotString(node.indexName);
10569
+ return {
10099
10570
  type: "command",
10100
10571
  name: "repeat",
10101
10572
  isBlocking: true,
10102
- body: node.body.map((n) => toCoreAST(n)),
10103
- args: [],
10573
+ args: [block(node.body), ...node.elseBody ? [block(node.elseBody)] : []],
10574
+ modifiers,
10104
10575
  ...nodePos(node)
10105
10576
  };
10577
+ }
10578
+ function slotString(value) {
10579
+ return { type: "string", value, ...POS };
10580
+ }
10581
+ function convertRepeat(node) {
10106
10582
  if (node.count !== void 0) {
10107
- result.count = typeof node.count === "number" ? { type: "literal", value: node.count, raw: String(node.count), ...POS } : toCoreAST(node.count);
10108
- result.loopVariant = "times";
10109
- } else if (node.whileCondition !== void 0) {
10110
- result.condition = toCoreAST(node.whileCondition);
10111
- result.loopVariant = "while";
10112
- } else if (node.untilEvent !== void 0) {
10113
- result.untilEvent = node.untilEvent;
10114
- result.loopVariant = "until";
10583
+ const times = typeof node.count === "number" ? { type: "literal", value: node.count, raw: String(node.count), ...POS } : toCoreAST(node.count);
10584
+ return loopCommand(node, "times", { times });
10115
10585
  }
10116
- return result;
10586
+ if (node.whileCondition !== void 0) {
10587
+ return loopCommand(node, "while", { while: toCoreAST(node.whileCondition) });
10588
+ }
10589
+ if (node.untilEvent !== void 0) {
10590
+ return loopCommand(node, "until-event", {
10591
+ event: slotString(node.untilEvent),
10592
+ from: node.untilEventTarget ? toCoreAST(node.untilEventTarget) : void 0
10593
+ });
10594
+ }
10595
+ return loopCommand(node, "forever", {});
10117
10596
  }
10118
10597
  function convertForEach(node) {
10119
- return {
10120
- type: "command",
10121
- name: "repeat",
10122
- isBlocking: true,
10123
- loopVariant: "for",
10124
- itemName: node.itemName,
10125
- ...node.indexName ? { indexName: node.indexName } : {},
10126
- collection: toCoreAST(node.collection),
10127
- body: node.body.map((n) => toCoreAST(n)),
10128
- args: [],
10129
- ...nodePos(node)
10130
- };
10598
+ return loopCommand(node, "for", {
10599
+ for: slotString(node.itemName),
10600
+ in: toCoreAST(node.collection)
10601
+ });
10131
10602
  }
10132
10603
  function convertWhile(node) {
10133
- return {
10134
- type: "command",
10135
- name: "repeat",
10136
- isBlocking: true,
10137
- loopVariant: "while",
10138
- condition: toCoreAST(node.condition),
10139
- body: node.body.map((n) => toCoreAST(n)),
10140
- args: [],
10141
- ...nodePos(node)
10142
- };
10604
+ return loopCommand(node, "while", {
10605
+ while: toCoreAST(node.condition),
10606
+ bottomTested: node.bottomTested ? { type: "literal", value: true, raw: "true", ...POS } : void 0
10607
+ });
10143
10608
  }
10144
10609
  function flattenEventModifiers(modifiers) {
10145
10610
  if (!modifiers)
@@ -10258,7 +10723,7 @@ async function semanticNodeToRuntimeAST(node) {
10258
10723
  const fw = await getFramework();
10259
10724
  return fw.semanticNodeToRuntimeAST(node);
10260
10725
  }
10261
- var COMMAND_NAMES, COMMAND_MANIFEST, KEYWORDS, COMMAND_TERMINATORS, COMMANDS, CONTROL_FLOW_COMMANDS, PUT_OPERATIONS, PUT_OPERATION_KEYWORDS, HYPERSCRIPT_KEYWORDS, CommandClassification, CSS_FUNCTIONS, CONTEXT_VARS, LOGICAL_OPERATORS, COMPARISON_OPERATORS, DOM_EVENTS, TOKENIZER_KEYWORDS, TokenKind, MATHEMATICAL_OPERATORS, TIME_UNITS, isDebugEnabled$1, DEBUG, debug, STOP_TOKENS, STOP_DELIMITERS, CORE_FRAGMENT, PARSER_COMPARISON_FRAGMENT, ASSIGNMENT_FRAGMENT, PARSER_TABLE, GLOBAL_WRITE_HOOKS, GLOBAL_READ_HOOKS, LOCAL_WRITE_HOOKS, LOCAL_READ_HOOKS, FEATURE_REGISTRY, NODE_EVALUATORS, NODE_WRITERS, ParserExtensionRegistry, SINGLETON, WINDOW_KEY, DEFAULT_BOUNDARY_KEYWORDS, CommandNodeBuilder, OPERAND_INTRODUCERS, CONTEXT_POSSESSIVES, SWAP_STRATEGY_KEYWORDS, ELEMENT_PRONOUNS, GO_POSITIONS, SCROLL_BY_SCAN_BOUNDARY, SCROLL_POSITIONS, SCROLL_DIRECTIONS, SCROLL_STOP, COMPOUND_COMMAND_PARSERS, COMPOUND_COMMAND_NAMES, GENERIC_FALLBACK_GRAMMAR, URL_HARD_STOPS, NONE, ONE_EXPR, BEEP, COMMAND_GRAMMAR, PLUGIN_COMMAND_GRAMMAR, STRING_POSTFIX_UNITS, TIME_POSTFIX_UNITS, STRING_POSTFIX_BP, Parser, HookRegistry, sharedGlobals, elementScopes, ExpressionTypeRegistry, expressionTypeRegistry, BOOLEAN_ATTRIBUTES, SPECIAL_DOM_PROPERTIES, NUMBER_WORDS, conversionConfig, defaultConversions, TYPE_ALIASES, asExpression, isExpression, asyncExpression, conversionExpressions, meExpression, youExpression, itExpression, itsExpression$1, resultExpression, querySelectorExpression, querySelectorAllExpression, idExpression, classExpression, closestExpression, parentExpression, windowExpression, documentExpression, elementWithSelectorExpression, styleRefExpression, possessiveStyleRefExpression, ofStyleRefExpression, referencesExpressions, isProduction, skipValidation, addDescribeToValidator, v, LRUCache, HyperscriptCache, globalCache, PerformanceTracker, performanceTracker, cleanupInterval, ComparisonInputSchema, PatternMatchingInputSchema, equalsExpression, strictEqualsExpression, notEqualsExpression, strictNotEqualsExpression, lessThanExpression, lessThanOrEqualExpression, greaterThanExpression, greaterThanOrEqualExpression, andExpression, orExpression, notExpression, isEmptyExpression, noExpression, isNotEmptyExpression, existsExpression, doesNotExistExpression, containsExpression, doesNotContainExpression, startsWithExpression, endsWithExpression, betweenExpression, matchesExpression, hasExpression, doesNotHaveExpression, logicalExpressions, UNSAFE_PROPS, possessiveExpression, myExpression, itsExpression, yourExpression, ofExpression, attributeExpression, attributeWithValueExpression, classReferenceExpression, idReferenceExpression, propertiesExpressions, EVALUABLE_KINDS, EVALUABLE_KIND_SET, NotSyncEvaluable, SYNC_EVALUABLE_KINDS, SYNC_EVALUABLE_KIND_SET, cachedFullRegistry, RAW_ARG_BUILTINS, CSS_PSEUDO_CLASSES, PSEUDO_CLASS_COLON_RE, BARE_REF_ESCAPE_RE, QUERY_REF_INTERP_RE, CleanupRegistry, EventSourceRegistry, canonicalEvaluator, CommandAdapterV2, CommandRegistryV2, defaultRegistry, RegistryIntegration, StrayControlFlowError, eventRecursionDepth, RuntimeBase, BaseExpressionImpl, StringLiteralInputSchema, NumberLiteralInputSchema, BooleanLiteralInputSchema, BinaryOperationInputSchema, StringLiteralExpression, NumberLiteralExpression, BooleanLiteralExpression, StringConcatenationExpression, specialExpressions, AdditionExpression, SubtractionExpression, MultiplicationExpression, DivisionExpression, ModuloExpression, PowerExpression, mathematicalExpressions, firstExpression, lastExpression, atExpression, nextExpression, previousExpression, DOCUMENT_POSITION_PRECEDING, DOCUMENT_POSITION_FOLLOWING, nextWithinExpression, previousWithinExpression, positionalExpressions, index$1, VisibilityCommandBase, HideCommand, createHideCommand, ShowCommand, createShowCommand, SMART_ELEMENT_TAGS, DOMModificationBase, AddCommand, createAddCommand, positionMap, RemoveCommand, createRemoveCommand, PATTERN, BOOL_PROPS, isPropertyOfExpressionNode, isPropertyAccessNode, ToggleCommand, createToggleCommand, PutCommand, createPutCommand, HTML_ESCAPE, MakeCommand, createMakeCommand, EmptyCommand, createEmptyCommand, OpenCommand, createOpenCommand, CloseCommand, createCloseCommand, SelectCommand, createSelectCommand, ResetCommand, createResetCommand, SUPPORTS_MOVE_BEFORE, ELEMENT_NODE_TYPE, TEXT_NODE_TYPE, IS_PARENT_NODE_TYPE, Operation, candidateNodes, candidateElements, candidateElementsWithIds, unmatchedNodes, unmatchedElements, whitespaceNodes, _idArrayMap, _idSetMap, _options, _Morph_instances, morphOneToMany_fn, morphOneToOne_fn, morphMatchingElements_fn, morphNonMatchingElements_fn, morphOtherNode_fn, visitAttributes_fn, visitTextArea_fn, replaceNode_fn, removeNode_fn, mapIdArraysForEach_fn, mapIdArrays_fn, mapIdSets_fn, Morph, morphAdapter, queue, processing, STRATEGY_KEYWORDS, SwapCommand, MorphCommand, createSwapCommand, createMorphCommand, CRITICAL_LAYOUT_ELEMENTS, STRUCTURAL_SEMANTIC_ELEMENTS, WARNING_ELEMENTS, DEFAULT_VALIDATION_CONFIG, globalConfig, STYLES, SEVERITY_ICONS, SEVERITY_LABELS, STRATEGY_MAP, ProcessPartialsCommand, createProcessPartialsCommand, WaitCommand, createWaitCommand, customResponseTypes, DEFAULT_FETCH_THROWS_ON, fetchThrowsOnPatterns, FetchCommand, createFetchCommand, SetCommand, createSetCommand, GetCommand, createGetCommand, NumericModifyCommand, createIncrementCommand, ClearCommand, createClearCommand, LogCommand, createLogCommand, EventDispatchCommand, createTriggerCommand, GoCommand, createGoCommand, HistoryCommand, createPushUrlCommand, ScrollCommand, createScrollCommand, ConditionalCommand, createConditionalCommand, createIfCommand, RepeatCommand, createRepeatCommand, ControlFlowSignalBase, BreakCommand, createBreakCommand, ContinueCommand, createContinueCommand, HaltCommand, createHaltCommand, ReturnCommand, createReturnCommand, ExitCommand, createExitCommand, ThrowCommand, createThrowCommand, CallCommand, createCallCommand, FocusCommand, createFocusCommand, BlurCommand, createBlurCommand, PseudoCommand, ContentInsertionCommand, AppendCommand, createAppendCommand, PrependCommand, createPrependCommand, TransitionCommand, createTransitionCommand, MeasureCommand, createMeasureCommand, SettleCommand, createSettleCommand, StartViewTransitionCommand, createStartViewTransitionCommand, TakeCommand, createTakeCommand, JsCommand, createJsCommand, DefaultCommand, createDefaultCommand, TellCommand, createTellCommand, CopyCommand, createCopyCommand, PickCommand, createPickCommand, BeepCommand, createBeepCommand, BreakpointCommand, createBreakpointCommand, InstallCommand, RenderCommand, COMMAND_FACTORIES, Runtime, DEFAULT_CONFIDENCE_THRESHOLD, debugEnabled, debugEventTarget, stats, VERSION, HistorySwapBehavior, BoostedBehavior, DEFAULT_LANGUAGE$1, injectedHost, LAZY_HEADER, IMMEDIATE_EVENTS, POWERED_ATTRIBUTE, SCRIPT_SELECTOR, processedElements, lazyStubs, AttributeProcessor, defaultAttributeProcessor, RingBuffer, HISTORY_CAPACITY, DebugController, DEFAULT_LANGUAGE, ASTCache, astCache, registeredFrontEnd, defaultFrontEnd, config, _defaultRuntime, _debugController, hyperscript, lokascript, POS, DiagnosticSeverity, SymbolKind, CompletionItemKind, _semanticModule, SemanticGrammarBridge, bridge, _fw, index;
10726
+ var COMMAND_NAMES, COMMAND_MANIFEST, KEYWORDS, COMMAND_TERMINATORS, COMMANDS, CONTROL_FLOW_COMMANDS, PUT_OPERATIONS, PUT_OPERATION_KEYWORDS, HYPERSCRIPT_KEYWORDS, CommandClassification, CSS_FUNCTIONS, CONTEXT_VARS, LOGICAL_OPERATORS, COMPARISON_OPERATORS, DOM_EVENTS, TOKENIZER_KEYWORDS, TokenKind, MATHEMATICAL_OPERATORS, TIME_UNITS, isDebugEnabled$1, DEBUG, debug, STOP_TOKENS, STOP_DELIMITERS, CORE_FRAGMENT, OF_OPERAND_BP, THE_OF_TARGET_BP, PARSER_COMPARISON_FRAGMENT, ASSIGNMENT_FRAGMENT, PARSER_TABLE, GLOBAL_WRITE_HOOKS, GLOBAL_READ_HOOKS, LOCAL_WRITE_HOOKS, LOCAL_READ_HOOKS, FEATURE_REGISTRY, NODE_EVALUATORS, NODE_WRITERS, ParserExtensionRegistry, SINGLETON, WINDOW_KEY, DEFAULT_BOUNDARY_KEYWORDS, CommandNodeBuilder, OPERAND_INTRODUCERS, CONTEXT_REFS, SWAP_STRATEGY_KEYWORDS, ELEMENT_PRONOUNS, GO_POSITIONS, SCROLL_BY_SCAN_BOUNDARY, SCROLL_POSITIONS, SCROLL_DIRECTIONS, SCROLL_STOP, COMPOUND_COMMAND_PARSERS, COMPOUND_COMMAND_NAMES, GENERIC_FALLBACK_GRAMMAR, URL_HARD_STOPS, NONE, ONE_EXPR, COMMAND_GRAMMAR, PLUGIN_COMMAND_GRAMMAR, STRING_POSTFIX_UNITS, TIME_POSTFIX_UNITS, STRING_POSTFIX_BP, Parser, HookRegistry, sharedGlobals, elementScopes, ExpressionTypeRegistry, expressionTypeRegistry, BOOLEAN_ATTRIBUTES, SPECIAL_DOM_PROPERTIES, NUMBER_WORDS, conversionConfig, defaultConversions, TYPE_ALIASES, asExpression, isExpression, asyncExpression, conversionExpressions, meExpression, youExpression, itExpression, itsExpression$1, resultExpression, querySelectorExpression, querySelectorAllExpression, idExpression, classExpression, closestExpression, parentExpression, windowExpression, documentExpression, elementWithSelectorExpression, styleRefExpression, possessiveStyleRefExpression, ofStyleRefExpression, referencesExpressions, isProduction, skipValidation, addDescribeToValidator, v, LRUCache, HyperscriptCache, globalCache, PerformanceTracker, performanceTracker, cleanupInterval, ComparisonInputSchema, PatternMatchingInputSchema, equalsExpression, strictEqualsExpression, notEqualsExpression, strictNotEqualsExpression, lessThanExpression, lessThanOrEqualExpression, greaterThanExpression, greaterThanOrEqualExpression, andExpression, orExpression, notExpression, isEmptyExpression, noExpression, isNotEmptyExpression, existsExpression, doesNotExistExpression, containsExpression, doesNotContainExpression, startsWithExpression, endsWithExpression, betweenExpression, matchesExpression, hasExpression, doesNotHaveExpression, logicalExpressions, UNSAFE_PROPS, possessiveExpression, myExpression, itsExpression, yourExpression, ofExpression, attributeExpression, attributeWithValueExpression, classReferenceExpression, idReferenceExpression, propertiesExpressions, EVALUABLE_KINDS, EVALUABLE_KIND_SET, NotSyncEvaluable, SYNC_EVALUABLE_KINDS, SYNC_EVALUABLE_KIND_SET, cachedFullRegistry, RAW_ARG_BUILTINS, CSS_PSEUDO_CLASSES, PSEUDO_CLASS_COLON_RE, BARE_REF_ESCAPE_RE, QUERY_REF_INTERP_RE, CleanupRegistry, EventSourceRegistry, canonicalEvaluator, CommandAdapterV2, CommandRegistryV2, defaultRegistry, RegistryIntegration, StrayControlFlowError, eventRecursionDepth, RuntimeBase, BaseExpressionImpl, StringLiteralInputSchema, NumberLiteralInputSchema, BooleanLiteralInputSchema, BinaryOperationInputSchema, StringLiteralExpression, NumberLiteralExpression, BooleanLiteralExpression, StringConcatenationExpression, specialExpressions, AdditionExpression, SubtractionExpression, MultiplicationExpression, DivisionExpression, ModuloExpression, PowerExpression, mathematicalExpressions, firstExpression, lastExpression, atExpression, nextExpression, previousExpression, DOCUMENT_POSITION_PRECEDING, DOCUMENT_POSITION_FOLLOWING, nextWithinExpression, previousWithinExpression, positionalExpressions, index$1, VisibilityCommandBase, HideCommand, createHideCommand, ShowCommand, createShowCommand, SMART_ELEMENT_TAGS, DOMModificationBase, AddCommand, createAddCommand, positionMap, RemoveCommand, createRemoveCommand, PATTERN, BOOL_PROPS, isPropertyOfExpressionNode, isPropertyAccessNode, ToggleCommand, createToggleCommand, PutCommand, createPutCommand, HTML_ESCAPE, MakeCommand, createMakeCommand, EmptyCommand, createEmptyCommand, OpenCommand, createOpenCommand, CloseCommand, createCloseCommand, SelectCommand, createSelectCommand, ResetCommand, createResetCommand, SUPPORTS_MOVE_BEFORE, ELEMENT_NODE_TYPE, TEXT_NODE_TYPE, IS_PARENT_NODE_TYPE, Operation, candidateNodes, candidateElements, candidateElementsWithIds, unmatchedNodes, unmatchedElements, whitespaceNodes, _idArrayMap, _idSetMap, _options, _Morph_instances, morphOneToMany_fn, morphOneToOne_fn, morphMatchingElements_fn, morphNonMatchingElements_fn, morphOtherNode_fn, visitAttributes_fn, visitTextArea_fn, replaceNode_fn, removeNode_fn, mapIdArraysForEach_fn, mapIdArrays_fn, mapIdSets_fn, Morph, morphAdapter, queue, processing, STRATEGY_KEYWORDS, SwapCommand, MorphCommand, createSwapCommand, createMorphCommand, CRITICAL_LAYOUT_ELEMENTS, STRUCTURAL_SEMANTIC_ELEMENTS, WARNING_ELEMENTS, DEFAULT_VALIDATION_CONFIG, globalConfig, STYLES, SEVERITY_ICONS, SEVERITY_LABELS, STRATEGY_MAP, ProcessPartialsCommand, createProcessPartialsCommand, WaitCommand, createWaitCommand, customResponseTypes, DEFAULT_FETCH_THROWS_ON, fetchThrowsOnPatterns, FetchCommand, createFetchCommand, SET_RUNGS, SetCommand, createSetCommand, GetCommand, createGetCommand, NumericModifyCommand, createIncrementCommand, ClearCommand, createClearCommand, LogCommand, createLogCommand, EventDispatchCommand, createTriggerCommand, GoCommand, createGoCommand, HistoryCommand, createPushUrlCommand, ScrollCommand, createScrollCommand, ConditionalCommand, createConditionalCommand, createIfCommand, RepeatCommand, createRepeatCommand, ControlFlowSignalBase, BreakCommand, createBreakCommand, ContinueCommand, createContinueCommand, HaltCommand, createHaltCommand, ReturnCommand, createReturnCommand, ExitCommand, createExitCommand, ThrowCommand, createThrowCommand, CallCommand, createCallCommand, FocusCommand, createFocusCommand, BlurCommand, createBlurCommand, PseudoCommand, ContentInsertionCommand, AppendCommand, createAppendCommand, PrependCommand, createPrependCommand, RESTORE_KEYWORDS, TransitionCommand, createTransitionCommand, MeasureCommand, createMeasureCommand, SettleCommand, createSettleCommand, StartViewTransitionCommand, createStartViewTransitionCommand, TakeCommand, createTakeCommand, JsCommand, createJsCommand, DefaultCommand, createDefaultCommand, TellCommand, createTellCommand, CopyCommand, createCopyCommand, PickCommand, createPickCommand, BeepCommand, createBeepCommand, BreakpointCommand, createBreakpointCommand, InstallCommand, RenderCommand, COMMAND_FACTORIES, Runtime, DEFAULT_CONFIDENCE_THRESHOLD, debugEnabled, debugEventTarget, stats, VERSION, HistorySwapBehavior, BoostedBehavior, DEFAULT_LANGUAGE$1, injectedHost, LAZY_HEADER, IMMEDIATE_EVENTS, POWERED_ATTRIBUTE, SCRIPT_SELECTOR, processedElements, lazyStubs, AttributeProcessor, defaultAttributeProcessor, RingBuffer, HISTORY_CAPACITY, DebugController, DEFAULT_LANGUAGE, ASTCache, astCache, registeredFrontEnd, defaultFrontEnd, config, _defaultRuntime, _debugController, hyperscript, lokascript, POS, DiagnosticSeverity, SymbolKind, CompletionItemKind, _semanticModule, SemanticGrammarBridge, bridge, _fw, index;
10262
10727
  var init_dist = __esm({
10263
10728
  "../core/dist/index.mjs"() {
10264
10729
  "use strict";
@@ -11339,6 +11804,8 @@ var init_dist = __esm({
11339
11804
  ["!", prefix(80)],
11340
11805
  ["no", prefix(27)]
11341
11806
  ]);
11807
+ OF_OPERAND_BP = 70;
11808
+ THE_OF_TARGET_BP = OF_OPERAND_BP + 1;
11342
11809
  PARSER_COMPARISON_FRAGMENT = /* @__PURE__ */ new Map([
11343
11810
  ["===", leftAssoc(30)],
11344
11811
  ["!==", leftAssoc(30)],
@@ -11383,7 +11850,7 @@ var init_dist = __esm({
11383
11850
  ["is greater than or equal to", leftAssoc(30)],
11384
11851
  ["is less than or equal to", leftAssoc(30)],
11385
11852
  ["in", leftAssoc(30)],
11386
- ["of", rightAssoc(30)],
11853
+ ["of", { infix: { bp: [90, OF_OPERAND_BP], handler: ofHandler } }],
11387
11854
  ["really", leftAssoc(30)],
11388
11855
  [
11389
11856
  "exists",
@@ -11707,7 +12174,7 @@ var init_dist = __esm({
11707
12174
  "/",
11708
12175
  "mod"
11709
12176
  ]);
11710
- CONTEXT_POSSESSIVES = /* @__PURE__ */ new Set(["my", "its", "your"]);
12177
+ CONTEXT_REFS = /* @__PURE__ */ new Set(["me", "it", "you", "result"]);
11711
12178
  SWAP_STRATEGY_KEYWORDS = [
11712
12179
  "afterbegin",
11713
12180
  "afterend",
@@ -11781,7 +12248,6 @@ var init_dist = __esm({
11781
12248
  markers: [],
11782
12249
  syntax
11783
12250
  });
11784
- BEEP = ONE_EXPR("beep! [<expression>, \u2026]");
11785
12251
  COMMAND_GRAMMAR = {
11786
12252
  append: { positional: "expression", markers: ["to"], syntax: "append <value> [to <target>]" },
11787
12253
  prepend: { positional: "expression", markers: ["to"], syntax: "prepend <value> [to <target>]" },
@@ -11792,8 +12258,7 @@ var init_dist = __esm({
11792
12258
  syntax: "make (a|an) <type> [from <args>] [called <name>]"
11793
12259
  },
11794
12260
  throw: { positional: "primary", markers: [], syntax: "throw <error>" },
11795
- "beep!": BEEP,
11796
- beep: BEEP,
12261
+ beep: ONE_EXPR("beep! [<expression>, \u2026]"),
11797
12262
  blur: ONE_EXPR("blur [<target>]"),
11798
12263
  break: NONE,
11799
12264
  breakpoint: NONE,
@@ -11817,7 +12282,8 @@ var init_dist = __esm({
11817
12282
  render: {
11818
12283
  positional: "expression",
11819
12284
  markers: ["with"],
11820
- syntax: "render <template> [with <variables>]"
12285
+ namedArgs: ["with"],
12286
+ syntax: "render <template> [with <name>: <value>, \u2026]"
11821
12287
  },
11822
12288
  reset: ONE_EXPR("reset [<form>]"),
11823
12289
  return: ONE_EXPR("return [<value>]"),
@@ -12138,7 +12604,9 @@ var init_dist = __esm({
12138
12604
  const firstToken = this.peek();
12139
12605
  const prefixEntry = _Parser.PRATT_TABLE.get(firstToken.value);
12140
12606
  let left;
12141
- if (prefixEntry?.prefix) {
12607
+ if (isGluedBeepBang(firstToken, this.tokens[this.current + 1])) {
12608
+ left = this.parseBeepExpression();
12609
+ } else if (prefixEntry?.prefix) {
12142
12610
  const token = this.advance();
12143
12611
  if (this.isAtEnd()) {
12144
12612
  this.addError(`Expected expression after '${token.value}' operator`);
@@ -12157,6 +12625,9 @@ var init_dist = __esm({
12157
12625
  continue;
12158
12626
  }
12159
12627
  }
12628
+ if (nextToken.value === "*" && this.isSpacedStyleRef(this.current)) {
12629
+ break;
12630
+ }
12160
12631
  const infixEntry = _Parser.PRATT_TABLE.get(nextToken.value);
12161
12632
  if (!infixEntry?.infix) {
12162
12633
  if (STOP_TOKENS.has(nextToken.value) || STOP_DELIMITERS.has(nextToken.value)) {
@@ -12210,6 +12681,35 @@ var init_dist = __esm({
12210
12681
  this.prattCache.set(cacheKey, { node: left, endPos: this.current });
12211
12682
  return left;
12212
12683
  }
12684
+ parseBeepExpression() {
12685
+ const beepToken = this.advance();
12686
+ this.advance();
12687
+ if (this.isAtEnd()) {
12688
+ this.addError("Expected expression after 'beep!'");
12689
+ return this.createErrorNode();
12690
+ }
12691
+ const operand = this.parseExpressionPratt(_Parser.BEEP_OPERAND_BP);
12692
+ return {
12693
+ type: "unaryExpression",
12694
+ operator: "beep!",
12695
+ operand,
12696
+ argument: operand,
12697
+ prefix: true,
12698
+ start: beepToken.start,
12699
+ end: this.previous().end,
12700
+ line: beepToken.line,
12701
+ column: beepToken.column
12702
+ };
12703
+ }
12704
+ isStyleRefStar(i) {
12705
+ const star = this.tokens[i];
12706
+ const name = this.tokens[i + 1];
12707
+ return star?.value === "*" && !!name && name.start === star.end && isIdentifierLike(name);
12708
+ }
12709
+ isSpacedStyleRef(i) {
12710
+ const prev = this.tokens[i - 1];
12711
+ return this.isStyleRefStar(i) && !!prev && prev.end < this.tokens[i].start;
12712
+ }
12213
12713
  tryParseStringPostfix(left, nextToken) {
12214
12714
  const v2 = nextToken.value;
12215
12715
  let unit = null;
@@ -12379,6 +12879,10 @@ var init_dist = __esm({
12379
12879
  const isStop = () => this.check("end") || extraStops.some((s) => this.check(s));
12380
12880
  debug.parse("\u{1F504} parseCommandListUntilTerminator: Starting (extraStops:", extraStops.join(","), ")");
12381
12881
  while (!this.isAtEnd() && !isStop()) {
12882
+ if (this.checkComment()) {
12883
+ this.advance();
12884
+ continue;
12885
+ }
12382
12886
  debug.parse("\u{1F4CD} Loop iteration, current token:", this.peek().value, "kind:", this.peek().kind);
12383
12887
  let parsedCommand = false;
12384
12888
  const isCommandToken = this.checkIsCommand();
@@ -12450,7 +12954,7 @@ var init_dist = __esm({
12450
12954
  if (this.check("end")) {
12451
12955
  debug.parse('\u2705 Found "end", consuming it');
12452
12956
  this.advance();
12453
- } else {
12957
+ } else if (!this.atEndOfInput()) {
12454
12958
  debug.parse('\u274C ERROR: Expected "end" but got:', this.peek().value, "at position:", this.peek().start);
12455
12959
  throw new Error(`Expected "end" to close ${construct} block`);
12456
12960
  }
@@ -12462,7 +12966,7 @@ var init_dist = __esm({
12462
12966
  if (!hasElse) {
12463
12967
  if (this.check("end")) {
12464
12968
  this.advance();
12465
- } else {
12969
+ } else if (!this.atEndOfInput()) {
12466
12970
  throw new Error('Expected "end" to close repeat block');
12467
12971
  }
12468
12972
  }
@@ -12477,7 +12981,10 @@ var init_dist = __esm({
12477
12981
  if (terminator === "end") {
12478
12982
  this.advance();
12479
12983
  } else if (terminator === "") {
12480
- throw new Error('Expected "end", "else", "until", or "while" to close repeat block');
12984
+ if (!this.atEndOfInput()) {
12985
+ throw new Error('Expected "end", "else", "until", or "while" to close repeat block');
12986
+ }
12987
+ return { commands, terminator: "end" };
12481
12988
  }
12482
12989
  return { commands, terminator };
12483
12990
  }
@@ -13017,7 +13524,9 @@ var init_dist = __esm({
13017
13524
  parseTopLevelInitBlock() {
13018
13525
  const pos2 = this.getPosition();
13019
13526
  const initCommands = this.parseCommandBlock(["end"]);
13020
- this.consume("end", "Expected 'end' after init block");
13527
+ if (!this.match("end") && !this.atEndOfInput() && !this.checkFeatureStart()) {
13528
+ this.consume("end", "Expected 'end' after init block");
13529
+ }
13021
13530
  return {
13022
13531
  type: "initBlock",
13023
13532
  commands: initCommands,
@@ -13186,10 +13695,27 @@ var init_dist = __esm({
13186
13695
  ...sourceExpr.column !== void 0 && { column: sourceExpr.column }
13187
13696
  };
13188
13697
  }
13698
+ isArgumentListOnly(open) {
13699
+ let depth = 0;
13700
+ for (let i = open; i < this.tokens.length; i++) {
13701
+ const token = this.tokens[i];
13702
+ if (token.kind !== "operator")
13703
+ continue;
13704
+ if (token.value === "(" || token.value === "[" || token.value === "{")
13705
+ depth++;
13706
+ else if (token.value === ")" || token.value === "]" || token.value === "}") {
13707
+ if (--depth === 0)
13708
+ return i === open + 1;
13709
+ } else if (token.value === "," && depth === 1)
13710
+ return true;
13711
+ }
13712
+ return false;
13713
+ }
13189
13714
  tryParsePseudoCommand(callExpr, sourceExpr) {
13190
13715
  const methodName = callExpr.callee.name;
13191
13716
  const nextToken = this.peek();
13192
- const hasPseudoCommandPattern = _Parser.PSEUDO_COMMAND_PREPOSITIONS.includes(nextToken.value.toLowerCase()) || isIdentifier(nextToken) && !this.isCommand(nextToken.value) || isContextVar(nextToken);
13717
+ const nextIsArticle = nextToken.value.toLowerCase() === "the";
13718
+ const hasPseudoCommandPattern = nextIsArticle || _Parser.PSEUDO_COMMAND_PREPOSITIONS.includes(nextToken.value.toLowerCase()) || isIdentifier(nextToken) && !this.isCommand(nextToken.value) || isContextVar(nextToken);
13193
13719
  if (!hasPseudoCommandPattern) {
13194
13720
  return {
13195
13721
  node: this.callExprToCommandNode(callExpr, sourceExpr),
@@ -13213,7 +13739,9 @@ var init_dist = __esm({
13213
13739
  });
13214
13740
  }
13215
13741
  let preposition;
13216
- if (_Parser.PSEUDO_COMMAND_PREPOSITIONS.includes(this.peek().value.toLowerCase())) {
13742
+ if (nextIsArticle) {
13743
+ this.advance();
13744
+ } else if (_Parser.PSEUDO_COMMAND_PREPOSITIONS.includes(this.peek().value.toLowerCase())) {
13217
13745
  preposition = this.advance().value.toLowerCase();
13218
13746
  }
13219
13747
  let targetExpr;
@@ -13366,10 +13894,18 @@ var init_dist = __esm({
13366
13894
  this.consume("]", "Expected ']' after event condition");
13367
13895
  }
13368
13896
  let target;
13897
+ let targetExpression;
13369
13898
  if (this.match("from")) {
13370
- const targetToken = this.advance();
13371
- target = targetToken.value;
13372
- debug.parse(`\u{1F527} parseEventHandler: Parsed 'from' target: ${target} (kind: ${targetToken.kind})`);
13899
+ if (this.check("the"))
13900
+ this.advance();
13901
+ if (_Parser.POSITIONAL_SOURCE_HEADS.has(this.peek().value.toLowerCase())) {
13902
+ const startIndex = this.current;
13903
+ targetExpression = this.parseExpression();
13904
+ target = this.tokens.slice(startIndex, this.current).map((t) => t.value).join(" ");
13905
+ } else {
13906
+ target = this.advance().value;
13907
+ }
13908
+ debug.parse(`\u{1F527} parseEventHandler: Parsed 'from' target: ${target}`);
13373
13909
  this.parseTemporalKeywordModifiers(modifiers);
13374
13910
  }
13375
13911
  let attributeName;
@@ -13414,7 +13950,7 @@ var init_dist = __esm({
13414
13950
  const nextIsOpenParen = this.tokens[this.current + 1]?.value === "(";
13415
13951
  const commandName = this.peek().value.toLowerCase();
13416
13952
  const isSpecialBodyCommand = commandName === "js" || commandName === "tell";
13417
- if (nextIsOpenParen && !isSpecialBodyCommand) {
13953
+ if (nextIsOpenParen && !isSpecialBodyCommand && this.isArgumentListOnly(this.current + 1)) {
13418
13954
  let expr;
13419
13955
  const savedError = this.error;
13420
13956
  try {
@@ -13442,7 +13978,7 @@ var init_dist = __esm({
13442
13978
  this.recordDropped(`Command '${cmdToken}' failed to parse and was discarded`);
13443
13979
  }
13444
13980
  }
13445
- } else if (this.checkIdentifier()) {
13981
+ } else if (this.checkIdentifier() || this.checkEventNamedCall()) {
13446
13982
  const token = this.peek();
13447
13983
  if (this.isCommand(token.value)) {
13448
13984
  const cmd = this.parseCommandWithErrorRecovery();
@@ -13528,6 +14064,7 @@ var init_dist = __esm({
13528
14064
  ...eventParams.length > 0 && { args: eventParams },
13529
14065
  ...condition && { condition: fromLegacyExpression(condition) },
13530
14066
  ...target && { target },
14067
+ ...targetExpression && { targetExpression: fromLegacyExpression(targetExpression) },
13531
14068
  ...attributeName && { attributeName },
13532
14069
  ...watchTarget && { watchTarget: fromLegacyExpression(watchTarget) },
13533
14070
  ...customEventSource && { customEventSource },
@@ -13764,10 +14301,9 @@ var init_dist = __esm({
13764
14301
  }
13765
14302
  parseCommandCore() {
13766
14303
  const commandToken = this.previous();
13767
- let commandName = commandToken.value;
14304
+ const commandName = commandToken.value;
13768
14305
  if (commandName === "beep" && this.check("!")) {
13769
14306
  this.advance();
13770
- commandName = "beep!";
13771
14307
  }
13772
14308
  if (commandName === "fetch") {
13773
14309
  return parseFetchCommand(this.getContext(), commandToken);
@@ -13860,6 +14396,9 @@ var init_dist = __esm({
13860
14396
  args.push(this.parsePrimary());
13861
14397
  }
13862
14398
  const callNode = this.createCallExpression(createIdentifier(funcName, namePos), args);
14399
+ if (funcName === "closest" && args.length > 0 && this.match("to")) {
14400
+ callNode.closestTo = this.parseExpression();
14401
+ }
13863
14402
  if (funcName === "next" || funcName === "previous") {
13864
14403
  const relativePositional = this.parseRelativePositionalModifiers();
13865
14404
  if (relativePositional) {
@@ -13898,6 +14437,9 @@ var init_dist = __esm({
13898
14437
  }
13899
14438
  parseTheXofY() {
13900
14439
  const positionalKeywords = ["first", "last", "next", "previous", "random", "closest"];
14440
+ if (this.isStyleRefStar(this.current)) {
14441
+ return this.parsePrimary();
14442
+ }
13901
14443
  if (!this.checkIdentifier()) {
13902
14444
  return this.createIdentifier("the");
13903
14445
  }
@@ -13913,7 +14455,7 @@ var init_dist = __esm({
13913
14455
  return this.createIdentifier(propertyName);
13914
14456
  }
13915
14457
  this.advance();
13916
- const target = this.parseCall();
14458
+ const target = parseOfOperand(this.makePrattContext(), THE_OF_TARGET_BP);
13917
14459
  return {
13918
14460
  type: "propertyOfExpression",
13919
14461
  property: { type: "identifier", name: propertyName },
@@ -14108,6 +14650,13 @@ var init_dist = __esm({
14108
14650
  checkLiteral() {
14109
14651
  return this.checkPredicate(isLiteral);
14110
14652
  }
14653
+ checkEventNamedCall() {
14654
+ if (this.isAtEnd() || !this.checkIdentifierLike() || this.checkIsCommand())
14655
+ return false;
14656
+ const token = this.peek();
14657
+ const next = this.tokens[this.current + 1];
14658
+ return next?.value === "(" && next.start === token.end;
14659
+ }
14111
14660
  checkIdentifierLike() {
14112
14661
  return this.checkPredicate(isIdentifierLike);
14113
14662
  }
@@ -14216,6 +14765,19 @@ var init_dist = __esm({
14216
14765
  isAtEnd() {
14217
14766
  return this.current >= this.tokens.length;
14218
14767
  }
14768
+ atEndOfInput() {
14769
+ for (let i = this.current; i < this.tokens.length; i++) {
14770
+ if (!isComment(this.tokens[i]))
14771
+ return false;
14772
+ }
14773
+ return true;
14774
+ }
14775
+ checkFeatureStart() {
14776
+ if (this.isAtEnd())
14777
+ return false;
14778
+ const value = this.peek().value;
14779
+ return value === "init" || value === "on" || value === "def" || getRegisteredFeature(value) !== void 0;
14780
+ }
14219
14781
  peek() {
14220
14782
  if (this.isAtEnd()) {
14221
14783
  return { kind: "unknown", value: "", start: 0, end: 0, line: 1, column: 1 };
@@ -14420,6 +14982,7 @@ var init_dist = __esm({
14420
14982
  match: this.match.bind(this),
14421
14983
  matchOperator: this.matchOperator.bind(this),
14422
14984
  isAtEnd: this.isAtEnd.bind(this),
14985
+ atEndOfInput: this.atEndOfInput.bind(this),
14423
14986
  createIdentifier: this.createIdentifier.bind(this),
14424
14987
  parseExpression: this.parseExpression.bind(this),
14425
14988
  parsePrimary: this.parsePrimary.bind(this),
@@ -14468,7 +15031,15 @@ var init_dist = __esm({
14468
15031
  "ignoring case"
14469
15032
  ]);
14470
15033
  Parser.PRATT_TABLE = PARSER_TABLE;
15034
+ Parser.BEEP_OPERAND_BP = 80;
14471
15035
  Parser.PSEUDO_COMMAND_PREPOSITIONS = ["from", "on", "with", "into", "at", "to"];
15036
+ Parser.POSITIONAL_SOURCE_HEADS = /* @__PURE__ */ new Set([
15037
+ "closest",
15038
+ "next",
15039
+ "previous",
15040
+ "first",
15041
+ "last"
15042
+ ]);
14472
15043
  HookRegistry = class {
14473
15044
  constructor() {
14474
15045
  this.hooks = /* @__PURE__ */ new Map();
@@ -14918,7 +15489,7 @@ var init_dist = __esm({
14918
15489
  innertext: (el) => el.textContent?.trim(),
14919
15490
  innerHTML: (el) => el.innerHTML,
14920
15491
  outerhtml: (el) => el.outerHTML,
14921
- value: (el) => isFormElement$1(el) ? el.value : void 0,
15492
+ value: (el) => "value" in el ? el.value : void 0,
14922
15493
  checked: (el) => isInputElement$1(el) ? el.checked : void 0,
14923
15494
  disabled: (el) => "disabled" in el ? el.disabled : void 0,
14924
15495
  selected: (el) => isOptionElement$1(el) ? el.selected : void 0,
@@ -15360,14 +15931,15 @@ var init_dist = __esm({
15360
15931
  if (typeof selector !== "string") {
15361
15932
  throw new Error("closest requires a string selector");
15362
15933
  }
15363
- if (!context.me) {
15934
+ const start = args.length > 1 ? args[1] : context.me;
15935
+ if (!(start instanceof Element)) {
15364
15936
  return null;
15365
15937
  }
15366
- return context.me.closest(selector);
15938
+ return start.closest(selector);
15367
15939
  },
15368
15940
  validate(args) {
15369
- if (args.length !== 1) {
15370
- return "closest requires exactly 1 argument (selector)";
15941
+ if (args.length !== 1 && args.length !== 2) {
15942
+ return "closest requires a selector and, optionally, the element to start from";
15371
15943
  }
15372
15944
  const arg = args[0];
15373
15945
  if (typeof arg === "string")
@@ -16220,7 +16792,7 @@ var init_dist = __esm({
16220
16792
  else if (value instanceof Node || value instanceof Element)
16221
16793
  result = false;
16222
16794
  else if (isObject(value))
16223
- result = Object.keys(value).length === 0;
16795
+ result = value.length === 0;
16224
16796
  else
16225
16797
  result = false;
16226
16798
  return result;
@@ -17501,8 +18073,8 @@ var init_dist = __esm({
17501
18073
  }
17502
18074
  case "initBlock":
17503
18075
  case "block": {
17504
- const block = node;
17505
- const ops = (Array.isArray(block.commands) ? block.commands : []).map((c) => this.compile(c));
18076
+ const block2 = node;
18077
+ const ops = (Array.isArray(block2.commands) ? block2.commands : []).map((c) => this.compile(c));
17506
18078
  const consumesHalt = node.type === "initBlock";
17507
18079
  return async (context) => {
17508
18080
  for (const op of ops) {
@@ -17882,12 +18454,22 @@ var init_dist = __esm({
17882
18454
  debug.runtime(`BEHAVIOR: Finished installing ${behaviorName}`);
17883
18455
  }
17884
18456
  async executeEventHandler(node, context) {
17885
- const { event, events, commands, target, args, selector, condition, attributeName, watchTarget, modifiers, errorSymbol, errorHandler, finallyHandler } = node;
18457
+ const { event, events, commands, target, targetExpression, args, selector, condition, attributeName, watchTarget, modifiers, errorSymbol, errorHandler, finallyHandler } = node;
17886
18458
  const eventNames = events && events.length > 0 ? events : [event];
17887
18459
  debug.runtime(`BEHAVIOR: executeEventHandler: event='${event}', target='${target}'`);
17888
18460
  let targets = [];
17889
18461
  let globalTarget = null;
17890
- if (target) {
18462
+ if (targetExpression) {
18463
+ const resolved = await this.execute(targetExpression, context);
18464
+ if (resolved === window)
18465
+ globalTarget = window;
18466
+ else if (resolved === document)
18467
+ globalTarget = document;
18468
+ else if (this.isElement(resolved))
18469
+ targets = [resolved];
18470
+ else if (Array.isArray(resolved))
18471
+ targets = resolved.filter((el) => this.isElement(el));
18472
+ } else if (target) {
17891
18473
  const targetLower = typeof target === "string" ? target.toLowerCase() : "";
17892
18474
  if (targetLower === "window" || targetLower === "the window") {
17893
18475
  globalTarget = window;
@@ -19667,9 +20249,9 @@ var init_dist = __esm({
19667
20249
  if (!raw.args?.length)
19668
20250
  throw new Error("toggle command requires an argument");
19669
20251
  const firstArg = raw.args[0];
19670
- const propertyTarget = await resolveAnyPropertyTarget(firstArg, evaluator, context);
19671
- if (propertyTarget) {
19672
- return { type: "property", target: propertyTarget };
20252
+ const propertyTarget2 = await resolveAnyPropertyTarget(firstArg, evaluator, context);
20253
+ if (propertyTarget2) {
20254
+ return { type: "property", target: propertyTarget2 };
19673
20255
  }
19674
20256
  if (firstArg.type === "selector" && typeof firstArg.value === "string" && firstArg.value.startsWith("*")) {
19675
20257
  let expression2 = firstArg.value;
@@ -19903,13 +20485,13 @@ var init_dist = __esm({
19903
20485
  let targetSelector = null, memberPath, variableName;
19904
20486
  if (targetArg) {
19905
20487
  const tt = nodeType(targetArg);
19906
- const propertyTarget = await resolveAnyPropertyTarget(targetArg, evaluator, context);
19907
- if (propertyTarget) {
20488
+ const propertyTarget2 = await resolveAnyPropertyTarget(targetArg, evaluator, context);
20489
+ if (propertyTarget2) {
19908
20490
  return {
19909
20491
  value,
19910
- targets: [propertyTarget.element],
20492
+ targets: [propertyTarget2.element],
19911
20493
  position: "into",
19912
- memberPath: propertyTarget.property
20494
+ memberPath: propertyTarget2.property
19913
20495
  };
19914
20496
  }
19915
20497
  if (tt === "memberExpression") {
@@ -20049,7 +20631,11 @@ var init_dist = __esm({
20049
20631
  }
20050
20632
  if (isInsertableNode(v2))
20051
20633
  return v2;
20052
- return v2 == null ? "" : String(v2);
20634
+ if (v2 == null)
20635
+ return "null";
20636
+ if (Array.isArray(v2))
20637
+ return arrayFragment(v2);
20638
+ return String(v2);
20053
20639
  }
20054
20640
  looksLikeCss(s) {
20055
20641
  if (!s)
@@ -21132,7 +21718,7 @@ var init_dist = __esm({
21132
21718
  if (args.length === 0 && modifiers.with === void 0) {
21133
21719
  throw new Error("[HyperFixi] morph: command requires arguments");
21134
21720
  }
21135
- const strategy = literalText(modifiers.strategy)?.toLowerCase() === "over" ? "morphOuter" : "morph";
21721
+ const strategy = literalText(modifiers.strategy)?.toLowerCase() === "over" ? "morphOuter" : "morphMerge";
21136
21722
  const useViewTransition = modifiers.viewTransition !== void 0;
21137
21723
  const targetNode = modifiers.on !== void 0 ? modifiers.on : args[0];
21138
21724
  const contentNode = modifiers.with !== void 0 ? modifiers.with : modifiers.on !== void 0 ? args[0] : args[1];
@@ -21768,6 +22354,7 @@ var init_dist = __esm({
21768
22354
  return FetchCommand2 = _classThis;
21769
22355
  })();
21770
22356
  createFetchCommand = createFactory(FetchCommand);
22357
+ SET_RUNGS = { nodeWriters: true, styleSplit: true, ofAndObjectProperty: true };
21771
22358
  SetCommand = (() => {
21772
22359
  let _classDecorators = [command({ name: "set" })];
21773
22360
  let _classDescriptor;
@@ -21786,8 +22373,7 @@ var init_dist = __esm({
21786
22373
  const argScope = firstArg?.scope;
21787
22374
  const writeTarget = await resolveWriteTarget(firstArg, evaluator, context, {
21788
22375
  scopeElements: () => this.resolveTargets(raw.modifiers.on, evaluator, context),
21789
- nodeWriters: true,
21790
- styleSplit: true
22376
+ ...SET_RUNGS
21791
22377
  });
21792
22378
  if (writeTarget) {
21793
22379
  return toSetInput(writeTarget, await this.extractValue(raw, evaluator, context));
@@ -21911,13 +22497,13 @@ var init_dist = __esm({
21911
22497
  }
21912
22498
  async tryParseMemberExpression(firstArg, raw, evaluator, context) {
21913
22499
  const objectNode = firstArg.object;
21914
- const propertyNode = firstArg.property;
21915
- if (objectNode?.name && propertyNode?.name) {
22500
+ const propertyNode2 = firstArg.property;
22501
+ if (objectNode?.name && propertyNode2?.name) {
21916
22502
  const objectName = objectNode.name.toLowerCase();
21917
22503
  if (["me", "my", "it", "its", "you", "your"].includes(objectName)) {
21918
22504
  const element = resolvePossessive(objectName, context);
21919
22505
  const value = await this.extractValue(raw, evaluator, context);
21920
- return { type: "property", element, property: propertyNode.name, value };
22506
+ return { type: "property", element, property: propertyNode2.name, value };
21921
22507
  }
21922
22508
  }
21923
22509
  const objectAst = firstArg.object;
@@ -21933,7 +22519,7 @@ var init_dist = __esm({
21933
22519
  container = await evaluator.evaluate(objectAst, context);
21934
22520
  }
21935
22521
  if (container != null && typeof container === "object") {
21936
- const property = computed ? String(await evaluator.evaluate(firstArg.property, context)) : propertyNode?.name || "";
22522
+ const property = computed ? String(await evaluator.evaluate(firstArg.property, context)) : propertyNode2?.name || "";
21937
22523
  if (property) {
21938
22524
  const value = await this.extractValue(raw, evaluator, context);
21939
22525
  return {
@@ -22100,7 +22686,7 @@ var init_dist = __esm({
22100
22686
  async execute(input, context) {
22101
22687
  const { target, property, scope, amount = 1, operation = "increment", customWrite } = input;
22102
22688
  if (customWrite) {
22103
- const delta = isFinite(amount) ? amount : 1;
22689
+ const delta = amount;
22104
22690
  const newValue2 = operation === "increment" ? customWrite.currentValue + delta : customWrite.currentValue - delta;
22105
22691
  await customWrite.writer(customWrite.node, newValue2, context);
22106
22692
  Object.assign(context, { it: newValue2 });
@@ -22111,8 +22697,7 @@ var init_dist = __esm({
22111
22697
  if (isNaN(currentValue)) {
22112
22698
  newValue = NaN;
22113
22699
  } else {
22114
- const delta = isFinite(amount) ? amount : 1;
22115
- newValue = operation === "increment" ? currentValue + delta : currentValue - delta;
22700
+ newValue = operation === "increment" ? currentValue + amount : currentValue - amount;
22116
22701
  }
22117
22702
  setTargetValue(target, property, scope, newValue, context);
22118
22703
  Object.assign(context, { it: newValue });
@@ -22516,9 +23101,9 @@ var init_dist = __esm({
22516
23101
  }
22517
23102
  const { target, position, offset, behavior } = input;
22518
23103
  if (typeof window !== "undefined") {
22519
- const block = this.mapVerticalPosition(position.vertical);
23104
+ const block2 = this.mapVerticalPosition(position.vertical);
22520
23105
  const inline = this.mapHorizontalPosition(position.horizontal);
22521
- target.scrollIntoView?.({ behavior, block, inline });
23106
+ target.scrollIntoView?.({ behavior, block: block2, inline });
22522
23107
  if (offset !== 0) {
22523
23108
  const { x, y } = this.calculateScrollPosition(target, position, offset);
22524
23109
  window.scrollTo?.({ left: x, top: y, behavior });
@@ -23009,7 +23594,7 @@ var init_dist = __esm({
23009
23594
  }
23010
23595
  }
23011
23596
  const m = raw.modifiers ?? {};
23012
- const text = (node) => slotText(node, evaluator, context);
23597
+ const text = (node) => slotText$1(node, evaluator, context);
23013
23598
  const loopType = await text(m.loopType) ?? null;
23014
23599
  const bottomTested = m.bottomTested ? await evaluator.evaluate(m.bottomTested, context) : false;
23015
23600
  if (loopType === "for" || m.for) {
@@ -23027,17 +23612,24 @@ var init_dist = __esm({
23027
23612
  };
23028
23613
  }
23029
23614
  if (loopType === "times" || m.times) {
23030
- const countValue = m.times && await evaluator.evaluate(m.times, context);
23031
- const count = typeof countValue === "number" ? countValue : parseInt(String(countValue), 10);
23032
- if (isNaN(count))
23615
+ if (!m.times)
23033
23616
  throw new Error("times loops require a count number");
23617
+ const count = loopCount(await evaluator.evaluate(m.times, context));
23034
23618
  return { type: "times", count, indexVariable, commands, elseCommands };
23035
23619
  }
23036
23620
  if (loopType === "while" || m.while) {
23037
23621
  const condition = m.while;
23038
23622
  if (!condition)
23039
23623
  throw new Error("while loops require a condition");
23040
- return { type: "while", condition, indexVariable, commands, elseCommands, bottomTested };
23624
+ const check = () => evaluator.evaluate(condition, context);
23625
+ return {
23626
+ type: "while",
23627
+ condition: check,
23628
+ indexVariable,
23629
+ commands,
23630
+ elseCommands,
23631
+ bottomTested
23632
+ };
23041
23633
  }
23042
23634
  if (loopType === "until-event" || m.event) {
23043
23635
  const eventName = await text(m.event);
@@ -23053,7 +23645,7 @@ var init_dist = __esm({
23053
23645
  eventTarget = window;
23054
23646
  } else {
23055
23647
  const target = await evaluator.evaluate(m.from, context);
23056
- if (target instanceof EventTarget)
23648
+ if (isEventTarget(target))
23057
23649
  eventTarget = target;
23058
23650
  else if (target === "document")
23059
23651
  eventTarget = document;
@@ -23074,7 +23666,15 @@ var init_dist = __esm({
23074
23666
  const condition = m.until;
23075
23667
  if (!condition)
23076
23668
  throw new Error("until loops require a condition");
23077
- return { type: "until", condition, indexVariable, commands, elseCommands, bottomTested };
23669
+ const check = () => evaluator.evaluate(condition, context);
23670
+ return {
23671
+ type: "until",
23672
+ condition: check,
23673
+ indexVariable,
23674
+ commands,
23675
+ elseCommands,
23676
+ bottomTested
23677
+ };
23078
23678
  }
23079
23679
  if (loopType === "forever") {
23080
23680
  return { type: "forever", indexVariable, commands, elseCommands };
@@ -23092,10 +23692,10 @@ var init_dist = __esm({
23092
23692
  ({ config: config2, iterCtx } = createTimesLoopConfig(count, indexVariable));
23093
23693
  break;
23094
23694
  case "while":
23095
- ({ config: config2, iterCtx } = createWhileLoopConfig(condition, evaluateCondition, context, indexVariable));
23695
+ ({ config: config2, iterCtx } = createWhileLoopConfig(condition, evaluateLoopCondition, context, indexVariable));
23096
23696
  break;
23097
23697
  case "until":
23098
- ({ config: config2, iterCtx } = createUntilLoopConfig(condition, evaluateCondition, context, indexVariable));
23698
+ ({ config: config2, iterCtx } = createUntilLoopConfig(condition, evaluateLoopCondition, context, indexVariable));
23099
23699
  break;
23100
23700
  case "until-event":
23101
23701
  ({ config: config2, iterCtx } = createUntilEventLoopConfig(eventName, eventTarget, indexVariable));
@@ -24042,6 +24642,7 @@ var init_dist = __esm({
24042
24642
  return PrependCommand2 = _classThis;
24043
24643
  })();
24044
24644
  createPrependCommand = createFactory(PrependCommand);
24645
+ RESTORE_KEYWORDS = /* @__PURE__ */ new Set(["initial", "inherit", "unset", "revert"]);
24045
24646
  TransitionCommand = (() => {
24046
24647
  let _classDecorators = [command({ name: "transition" })];
24047
24648
  let _classDescriptor;
@@ -24057,10 +24658,9 @@ var init_dist = __esm({
24057
24658
  let property;
24058
24659
  let target;
24059
24660
  const firstArg = await evaluator.evaluate(raw.args[0], context);
24060
- const asElement = (value2) => Array.isArray(value2) || isNodeList(value2) ? value2[0] : value2;
24061
- const firstAsElement = asElement(firstArg);
24062
- if (isHTMLElement$2(firstAsElement) || typeof firstArg === "string" && /^[#.]|^(?:me|it|you)$/.test(firstArg)) {
24063
- target = firstAsElement;
24661
+ const owners = elementsOf(firstArg);
24662
+ if (owners.length > 0 || typeof firstArg === "string" && /^[#.]|^(?:me|it|you)$/.test(firstArg)) {
24663
+ target = owners.length === 1 ? owners[0] : owners.length > 1 ? owners : firstArg;
24064
24664
  property = String(await evaluator.evaluate(raw.args[1], context));
24065
24665
  } else if (raw.args.length >= 2) {
24066
24666
  throw new Error("transition: target element not found");
@@ -24070,9 +24670,9 @@ var init_dist = __esm({
24070
24670
  throw new Error("transition requires a CSS property");
24071
24671
  property = named.property;
24072
24672
  if (named.objectNode !== void 0) {
24073
- const owner = asElement(await evaluator.evaluate(named.objectNode, context));
24074
- if (isHTMLElement$2(owner))
24075
- target = owner;
24673
+ const owner = elementsOf(await evaluator.evaluate(named.objectNode, context));
24674
+ if (owner.length > 0)
24675
+ target = owner.length === 1 ? owner[0] : owner;
24076
24676
  }
24077
24677
  } else {
24078
24678
  property = String(firstArg);
@@ -24082,55 +24682,126 @@ var init_dist = __esm({
24082
24682
  }
24083
24683
  if (!raw.modifiers?.to)
24084
24684
  throw new Error('transition requires "to <value>"');
24085
- let value = await evaluator.evaluate(raw.modifiers.to, context);
24086
- if (value === void 0 && raw.modifiers.to.type === "identifier") {
24087
- value = raw.modifiers.to.name;
24088
- }
24089
- const result = { property, value };
24685
+ const result = {
24686
+ property,
24687
+ value: await valueOf(raw.modifiers.to, evaluator, context)
24688
+ };
24090
24689
  if (target !== void 0)
24091
24690
  result.target = target;
24691
+ if (raw.modifiers?.from)
24692
+ result.from = await valueOf(raw.modifiers.from, evaluator, context);
24693
+ if (raw.modifiers?.pairs) {
24694
+ result.pairs = await this.laterPairs(raw.modifiers.pairs, evaluator, context);
24695
+ }
24092
24696
  if (raw.modifiers?.over)
24093
24697
  result.duration = await evaluator.evaluate(raw.modifiers.over, context);
24094
24698
  if (raw.modifiers?.with)
24095
24699
  result.timingFunction = String(await evaluator.evaluate(raw.modifiers.with, context));
24700
+ if (raw.modifiers?.using)
24701
+ result.using = String(await evaluator.evaluate(raw.modifiers.using, context));
24096
24702
  return result;
24097
24703
  }
24704
+ async laterPairs(node, evaluator, context) {
24705
+ const pairs = [];
24706
+ for (const pairNode of node.elements ?? []) {
24707
+ const fields = /* @__PURE__ */ new Map();
24708
+ const properties = pairNode.properties ?? [];
24709
+ for (const { key, value } of properties) {
24710
+ if (typeof key.name === "string")
24711
+ fields.set(key.name, value);
24712
+ }
24713
+ const propertyNode2 = fields.get("property");
24714
+ const toNode = fields.get("to");
24715
+ if (!propertyNode2 || !toNode)
24716
+ throw new Error("transition requires property and value");
24717
+ const pair = {
24718
+ property: String(await evaluator.evaluate(propertyNode2, context)),
24719
+ value: await valueOf(toNode, evaluator, context)
24720
+ };
24721
+ const ownerNode = fields.get("owner");
24722
+ if (ownerNode) {
24723
+ const owners = elementsOf(await evaluator.evaluate(ownerNode, context));
24724
+ if (owners.length === 0)
24725
+ throw new Error("transition: target element not found");
24726
+ pair.target = owners.length === 1 ? owners[0] : owners;
24727
+ }
24728
+ const fromNode = fields.get("from");
24729
+ if (fromNode)
24730
+ pair.from = await valueOf(fromNode, evaluator, context);
24731
+ pairs.push(pair);
24732
+ }
24733
+ return pairs;
24734
+ }
24098
24735
  async execute(input, context) {
24099
- let { property } = input;
24100
- const { target, value, duration: durationInput, timingFunction } = input;
24101
- if (property.startsWith("*"))
24102
- property = property.substring(1);
24103
- property = camelToKebab(property);
24104
- const targetElement = resolveElement(target, context, "transition");
24105
- const duration = parseDuration(durationInput, 300);
24106
- const fromValue = getComputedStyle(targetElement).getPropertyValue(property);
24107
- const originalTransition = targetElement.style.transition;
24108
- const transitionProp = `${property} ${duration}ms ${timingFunction || "ease"}`;
24109
- targetElement.style.transition = originalTransition ? `${originalTransition}, ${transitionProp}` : transitionProp;
24110
- let toValue = String(value);
24111
- let removeInlineAfter = false;
24112
- if (toValue === "initial" || toValue === "inherit" || toValue === "unset" || toValue === "revert") {
24113
- const currentInline = targetElement.style.getPropertyValue(property);
24114
- targetElement.style.removeProperty(property);
24115
- toValue = getComputedStyle(targetElement).getPropertyValue(property);
24116
- if (currentInline) {
24117
- targetElement.style.setProperty(property, currentInline);
24118
- }
24119
- removeInlineAfter = true;
24120
- }
24121
- targetElement.style.setProperty(property, toValue);
24122
- const result = await waitForTransitionEnd(targetElement, property, duration);
24123
- targetElement.style.transition = originalTransition;
24124
- if (removeInlineAfter) {
24125
- targetElement.style.removeProperty(property);
24736
+ const duration = parseDuration(input.duration, input.using !== void 0 && longestTransitionMs(input.using) || 300);
24737
+ const timing = input.timingFunction || "ease";
24738
+ const jobs = [];
24739
+ for (const pair of [input, ...input.pairs ?? []]) {
24740
+ let property = pair.property;
24741
+ if (property.startsWith("*"))
24742
+ property = property.substring(1);
24743
+ property = camelToKebab(property);
24744
+ const elements = Array.isArray(pair.target) ? pair.target.filter(isHTMLElement$2) : [resolveElement(pair.target, context, "transition")];
24745
+ for (const element of elements) {
24746
+ jobs.push({
24747
+ element,
24748
+ property,
24749
+ pair,
24750
+ fromValue: getComputedStyle(element).getPropertyValue(property),
24751
+ toValue: "",
24752
+ removeInlineAfter: false
24753
+ });
24754
+ }
24755
+ }
24756
+ if (jobs.length === 0)
24757
+ throw new Error("transition: target element not found");
24758
+ for (const job of jobs) {
24759
+ if (job.pair.from !== void 0) {
24760
+ job.element.style.setProperty(job.property, String(job.pair.from));
24761
+ }
24762
+ }
24763
+ for (const job of jobs)
24764
+ if (job.pair.from !== void 0)
24765
+ void job.element.offsetWidth;
24766
+ const originals = /* @__PURE__ */ new Map();
24767
+ for (const job of jobs) {
24768
+ if (!originals.has(job.element))
24769
+ originals.set(job.element, job.element.style.transition);
24770
+ }
24771
+ for (const [element, original] of originals) {
24772
+ const own = jobs.filter((job) => job.element === element).map((job) => `${job.property} ${duration}ms ${timing}`);
24773
+ element.style.transition = input.using ?? [original, ...own].filter(Boolean).join(", ");
24774
+ }
24775
+ for (const job of jobs) {
24776
+ const { element, property } = job;
24777
+ let toValue = String(job.pair.value);
24778
+ if (RESTORE_KEYWORDS.has(toValue)) {
24779
+ const currentInline = element.style.getPropertyValue(property);
24780
+ element.style.removeProperty(property);
24781
+ toValue = getComputedStyle(element).getPropertyValue(property);
24782
+ if (currentInline) {
24783
+ element.style.setProperty(property, currentInline);
24784
+ }
24785
+ job.removeInlineAfter = true;
24786
+ }
24787
+ job.toValue = toValue;
24788
+ element.style.setProperty(property, toValue);
24126
24789
  }
24790
+ const results = await Promise.all(jobs.map((job) => waitForTransitionEnd(job.element, job.property, duration)));
24791
+ for (const [element, original] of originals)
24792
+ element.style.transition = original;
24793
+ for (const job of jobs) {
24794
+ if (job.removeInlineAfter)
24795
+ job.element.style.removeProperty(job.property);
24796
+ }
24797
+ const first = jobs[0];
24127
24798
  return {
24128
- element: targetElement,
24129
- property,
24130
- fromValue,
24131
- toValue,
24799
+ element: first.element,
24800
+ property: first.property,
24801
+ fromValue: first.fromValue,
24802
+ toValue: first.toValue,
24132
24803
  duration,
24133
- completed: result.completed
24804
+ completed: results.every((result) => result.completed)
24134
24805
  };
24135
24806
  }
24136
24807
  };
@@ -24143,13 +24814,16 @@ var init_dist = __esm({
24143
24814
  })();
24144
24815
  _classThis.metadata = commandMeta({
24145
24816
  description: "Animate CSS properties using CSS transitions",
24146
- syntax: "transition [<target>] <property> to <value> [over <duration>] [with <timing>]",
24817
+ syntax: "transition <property> [from <value>] to <value> [<property> \u2026 to <value>]\u2026 [over <duration>] [using <css> | with <timing>]",
24147
24818
  examples: [
24148
24819
  "transition opacity to 0.5",
24149
24820
  "transition my *opacity to 0 over 200ms",
24150
24821
  "transition #box's *opacity to 0 over 200ms",
24151
24822
  "transition left to 100px over 500ms",
24152
- "transition background-color to red over 1s with ease-in-out"
24823
+ "transition background-color to red over 1s with ease-in-out",
24824
+ "transition *width to 100px *height to 50px over 300ms",
24825
+ "transition *opacity of #panel from 0 to 1 over 200ms",
24826
+ 'transition my *opacity to 0 using "opacity 1s ease-in"'
24153
24827
  ],
24154
24828
  sideEffects: ["style-change", "timing"],
24155
24829
  category: "animation",
@@ -25309,17 +25983,7 @@ var init_dist = __esm({
25309
25983
  this.debugContext(context);
25310
25984
  return { expressionCount: 0, debugged: true, outputs: [] };
25311
25985
  }
25312
- const outputs = [];
25313
- console.group("\u{1F514} beep! Debug Output");
25314
- for (const expression of expressions) {
25315
- const output = this.debugExpression(expression);
25316
- outputs.push(output);
25317
- console.log(`Value:`, expression);
25318
- console.log(`Type:`, output.type);
25319
- console.log(`Representation:`, output.representation);
25320
- console.log("---");
25321
- }
25322
- console.groupEnd();
25986
+ const outputs = beepValues(context.me, expressions);
25323
25987
  return { expressionCount: expressions.length, debugged: true, outputs };
25324
25988
  }
25325
25989
  debugContext(context) {
@@ -25332,63 +25996,6 @@ var init_dist = __esm({
25332
25996
  console.log("variables:", context.variables);
25333
25997
  console.groupEnd();
25334
25998
  }
25335
- debugExpression(expression) {
25336
- return {
25337
- value: expression,
25338
- type: this.getType(expression),
25339
- representation: this.getRepresentation(expression)
25340
- };
25341
- }
25342
- getType(value) {
25343
- if (value === null)
25344
- return "null";
25345
- if (value === void 0)
25346
- return "undefined";
25347
- if (Array.isArray(value))
25348
- return "array";
25349
- if (isHTMLElement$2(value))
25350
- return "HTMLElement";
25351
- if (value instanceof Element)
25352
- return "Element";
25353
- if (value instanceof Node)
25354
- return "Node";
25355
- if (value instanceof Error)
25356
- return "Error";
25357
- if (value instanceof Date)
25358
- return "Date";
25359
- if (value instanceof RegExp)
25360
- return "RegExp";
25361
- return typeof value;
25362
- }
25363
- getRepresentation(value) {
25364
- if (value === null)
25365
- return "null";
25366
- if (value === void 0)
25367
- return "undefined";
25368
- if (Array.isArray(value)) {
25369
- return `Array(${value.length}) [${value.slice(0, 3).map((v2) => this.getRepresentation(v2)).join(", ")}${value.length > 3 ? "..." : ""}]`;
25370
- }
25371
- if (isHTMLElement$2(value)) {
25372
- const el = value;
25373
- const tag = el.tagName.toLowerCase();
25374
- const id = el.id ? `#${el.id}` : "";
25375
- const classes = el.className ? `.${el.className.split(" ").join(".")}` : "";
25376
- return `<${tag}${id}${classes}/>`;
25377
- }
25378
- if (value instanceof Error)
25379
- return `Error: ${value.message}`;
25380
- if (typeof value === "string")
25381
- return value.length > 50 ? `"${value.substring(0, 47)}..."` : `"${value}"`;
25382
- if (typeof value === "object") {
25383
- try {
25384
- const keys = Object.keys(value);
25385
- return `Object {${keys.slice(0, 3).join(", ")}${keys.length > 3 ? "..." : ""}}`;
25386
- } catch {
25387
- return "[Object]";
25388
- }
25389
- }
25390
- return String(value);
25391
- }
25392
25999
  };
25393
26000
  __setFunctionName(_classThis, "BeepCommand");
25394
26001
  (() => {
@@ -25909,16 +26516,12 @@ var init_dist = __esm({
25909
26516
  };
25910
26517
  RenderCommand.metadata = commandMeta({
25911
26518
  description: "Render templates with @if, @else, and @repeat directives",
25912
- syntax: [
25913
- "render <template>",
25914
- "render <template> with <variables>",
25915
- "render <template> with (key: value, ...)"
25916
- ],
26519
+ syntax: ["render <template>", "render <template> with <name>: <value>, ..."],
25917
26520
  examples: [
25918
26521
  "render myTemplate",
25919
- 'render myTemplate with (name: "Alice")',
25920
- 'render "<template>Hello ${name}!</template>" with (name: "World")',
25921
- "render template with (items: data)"
26522
+ 'render myTemplate with name: "Alice"',
26523
+ 'render "<template>Hello ${name}!</template>" with name: "World"',
26524
+ "render template with items: data"
25922
26525
  ],
25923
26526
  category: "templates",
25924
26527
  compatibility: "standard",
@@ -26030,7 +26633,7 @@ var init_dist = __esm({
26030
26633
  averageConfidence: 0,
26031
26634
  confidenceHistory: []
26032
26635
  };
26033
- VERSION = "3.1.0";
26636
+ VERSION = "3.2.0";
26034
26637
  HistorySwapBehavior = {
26035
26638
  name: "HistorySwap",
26036
26639
  init(element, params = {}) {
@@ -27482,8 +28085,8 @@ var init_multilingual = __esm({
27482
28085
  if (declared && modifiers) {
27483
28086
  for (const [key, role] of Object.entries(declared)) {
27484
28087
  const value = modifiers[key];
27485
- const isNode2 = !!value && typeof value === "object" && "type" in value;
27486
- if (isNode2 && inferred[role] === void 0) {
28088
+ const isNode3 = !!value && typeof value === "object" && "type" in value;
28089
+ if (isNode3 && inferred[role] === void 0) {
27487
28090
  inferred[role] = value;
27488
28091
  }
27489
28092
  }
@@ -27870,7 +28473,7 @@ function analyze(ast) {
27870
28473
  function extractClassOp2(node) {
27871
28474
  if (node.type !== "command") return null;
27872
28475
  const cmd = node;
27873
- if (!CLASS_COMMANDS.has(cmd.name)) return null;
28476
+ if (!TARGET_SLOTS.has(cmd.name)) return null;
27874
28477
  const args = cmd.args ?? [];
27875
28478
  if (args.length === 0) return null;
27876
28479
  const arg = args[0];
@@ -27879,48 +28482,32 @@ function extractClassOp2(node) {
27879
28482
  if (!selector.startsWith(".")) return null;
27880
28483
  const className = selector.slice(1);
27881
28484
  if (!className) return null;
27882
- return {
27883
- command: cmd.name,
27884
- className,
27885
- hasExplicitTarget: !!cmd.target
27886
- };
28485
+ return { command: cmd.name, className };
27887
28486
  }
27888
- function isSameTarget(a, b) {
27889
- if (a.type !== "command" || b.type !== "command") return false;
27890
- const cmdA = a;
27891
- const cmdB = b;
27892
- if (!cmdA.target && !cmdB.target) return true;
27893
- if (cmdA.target && cmdB.target) {
27894
- return getTargetKey(cmdA.target) === getTargetKey(cmdB.target);
27895
- }
27896
- return false;
28487
+ function classTarget(cmd) {
28488
+ if (cmd.target) return cmd.target;
28489
+ const slot = cmd.modifiers?.[TARGET_SLOTS.get(cmd.name)];
28490
+ return slot && typeof slot === "object" && "type" in slot ? slot : void 0;
27897
28491
  }
27898
- function getTargetKey(target) {
27899
- if (target.type === "selector") {
27900
- return `selector:${target.value}`;
27901
- }
27902
- if (target.type === "identifier") {
27903
- return `identifier:${target.value}`;
28492
+ function batchTargetKey(cmd) {
28493
+ const target = classTarget(cmd);
28494
+ if (!target) return "me";
28495
+ if (target.type === "identifier" && target.value === "me") return "me";
28496
+ if (target.type === "selector" && /^#[\w-]+$/.test(target.value)) {
28497
+ return target.value;
27904
28498
  }
27905
28499
  return null;
27906
28500
  }
27907
- function resolveTarget2(cmd) {
27908
- if (!cmd.target) return "_ctx.me";
27909
- if (cmd.target.type === "selector") {
27910
- const sel = cmd.target.value;
27911
- if (sel.startsWith("#") && !sel.includes(" ") && !sel.includes(".")) {
27912
- return `document.getElementById('${sel.slice(1)}')`;
27913
- }
27914
- return `document.querySelector('${sel}')`;
27915
- }
27916
- if (cmd.target.type === "identifier") {
27917
- const val = cmd.target.value;
27918
- if (val === "me") return "_ctx.me";
27919
- return val;
27920
- }
27921
- return "_ctx.me";
28501
+ function resolveTarget2(key) {
28502
+ return key === "me" ? "_ctx.me" : `document.getElementById('${key.slice(1)}')`;
28503
+ }
28504
+ function reordersClass(run, op) {
28505
+ return run.some((node) => {
28506
+ const other = extractClassOp2(node);
28507
+ return other.command !== op.command && other.className === op.className;
28508
+ });
27922
28509
  }
27923
- function createBatchNode(run) {
28510
+ function createBatchNode(run, targetKey2) {
27924
28511
  const adds = [];
27925
28512
  const removes = [];
27926
28513
  const toggles = [];
@@ -27938,10 +28525,9 @@ function createBatchNode(run) {
27938
28525
  break;
27939
28526
  }
27940
28527
  }
27941
- const target = resolveTarget2(run[0]);
27942
28528
  return {
27943
28529
  type: "batchedClassOps",
27944
- target,
28530
+ target: resolveTarget2(targetKey2),
27945
28531
  adds,
27946
28532
  removes,
27947
28533
  toggles
@@ -27950,9 +28536,10 @@ function createBatchNode(run) {
27950
28536
  function batchBody(nodes) {
27951
28537
  const result = [];
27952
28538
  let currentRun = [];
28539
+ let runKey = "";
27953
28540
  function flushRun() {
27954
28541
  if (currentRun.length >= 2) {
27955
- result.push(createBatchNode(currentRun));
28542
+ result.push(createBatchNode(currentRun, runKey));
27956
28543
  } else if (currentRun.length === 1) {
27957
28544
  result.push(currentRun[0]);
27958
28545
  }
@@ -27960,12 +28547,14 @@ function batchBody(nodes) {
27960
28547
  }
27961
28548
  for (const node of nodes) {
27962
28549
  const op = extractClassOp2(node);
27963
- if (op) {
27964
- if (currentRun.length > 0 && isSameTarget(currentRun[0], node)) {
28550
+ const key = op && batchTargetKey(node);
28551
+ if (op && key) {
28552
+ if (currentRun.length > 0 && key === runKey && !reordersClass(currentRun, op)) {
27965
28553
  currentRun.push(node);
27966
28554
  } else {
27967
28555
  flushRun();
27968
28556
  currentRun = [node];
28557
+ runKey = key;
27969
28558
  }
27970
28559
  } else {
27971
28560
  flushRun();
@@ -28210,6 +28799,70 @@ function generateExpression(node, ctx) {
28210
28799
  const codegen = new ExpressionCodegen(ctx);
28211
28800
  return codegen.generate(node);
28212
28801
  }
28802
+ function commandTarget(node, ...slots) {
28803
+ if (node.target) return node.target;
28804
+ for (const slot of slots) {
28805
+ const found = slot === "arg" ? node.args?.[0] : node.modifiers?.[slot];
28806
+ if (isNode2(found)) return found;
28807
+ }
28808
+ return void 0;
28809
+ }
28810
+ function isScopedQuery(node) {
28811
+ const binary = node;
28812
+ return binary?.type === "binary" && binary.operator === "in" && binary.left.type === "selector";
28813
+ }
28814
+ function allMatches(node, ctx) {
28815
+ if (isScopedQuery(node)) return ctx.generateExpression(node);
28816
+ if (node?.type !== "selector") return null;
28817
+ const selector = node.value;
28818
+ return /^#[\w-]+$/.test(selector) ? null : `document.querySelectorAll('${sanitizeSelector(selector)}')`;
28819
+ }
28820
+ function forEachTarget(node, ctx, op) {
28821
+ const all = allMatches(node, ctx);
28822
+ if (all) return `${all}.forEach(el => ${op("el")})`;
28823
+ return op(node ? ctx.generateExpression(node) : "_ctx.me");
28824
+ }
28825
+ function variableStore(node, ctx) {
28826
+ let name;
28827
+ let scope;
28828
+ if (node.type === "variable") {
28829
+ name = node.name;
28830
+ scope = node.scope;
28831
+ } else if (node.type === "identifier") {
28832
+ name = node.value ?? node.name ?? "";
28833
+ if (/^(\$|::)/.test(name)) scope = "global";
28834
+ else if (/^[A-Za-z_]\w*$/.test(name)) scope = "local";
28835
+ else return null;
28836
+ } else {
28837
+ return null;
28838
+ }
28839
+ const key = sanitizeIdentifier(name.replace(/^(\$|::|:)/, ""));
28840
+ if (scope === "global") {
28841
+ ctx.requireHelper("globals");
28842
+ return { read: `_rt.globals.get('${key}')`, write: (v2) => `_rt.globals.set('${key}', ${v2})` };
28843
+ }
28844
+ if (scope === "element") {
28845
+ ctx.requireHelper("elementVars");
28846
+ const store = `_rt.elementVars(_ctx.me)`;
28847
+ return { read: `${store}.get('${key}')`, write: (v2) => `${store}.set('${key}', ${v2})` };
28848
+ }
28849
+ return { read: `_ctx.locals.get('${key}')`, write: (v2) => `_ctx.locals.set('${key}', ${v2})` };
28850
+ }
28851
+ function scrollOptions(modifiers) {
28852
+ const word = (slot) => {
28853
+ const value = modifiers?.[slot]?.value;
28854
+ return typeof value === "string" ? value : void 0;
28855
+ };
28856
+ const position = word("position") ?? "";
28857
+ const options = [];
28858
+ if (SCROLL_BLOCK[position]) options.push(`block: '${SCROLL_BLOCK[position]}'`);
28859
+ if (SCROLL_INLINE[position]) options.push(`inline: '${SCROLL_INLINE[position]}'`);
28860
+ const behavior = modifiers?.smooth ? "smooth" : word("behavior");
28861
+ options.push(
28862
+ `behavior: '${behavior === "smooth" || behavior === "instant" ? behavior : "auto"}'`
28863
+ );
28864
+ return `{ ${options.join(", ")} }`;
28865
+ }
28213
28866
  function generateIf(node, ctx, generateBody) {
28214
28867
  const exprCodegen = new ExpressionCodegen(ctx);
28215
28868
  const condition = exprCodegen.generate(node.condition);
@@ -28234,49 +28887,85 @@ ${elseBody}
28234
28887
  }
28235
28888
  return code;
28236
28889
  }
28890
+ function emitLoop(parts) {
28891
+ const pass = [
28892
+ ...parts.elseBody !== void 0 ? ["_ran = true;"] : [],
28893
+ ...parts.enter ? [parts.enter] : [],
28894
+ ...parts.indexName ? [`_ctx.locals.set('${sanitizeIdentifier(parts.indexName)}', _i);`] : [],
28895
+ parts.body
28896
+ ].join("\n");
28897
+ const step = parts.step ? `_i++, ${parts.step}` : "_i++";
28898
+ const loop = parts.bottomTested ? (
28899
+ // A `continue` jumps straight to the test, so the counter moves there.
28900
+ `let _i = 0;
28901
+ do {
28902
+ ${pass}
28903
+ } while ((${step}, ${parts.test}));`
28904
+ ) : `for (let _i = 0; ${parts.test}; ${step}) {
28905
+ ${pass}
28906
+ }`;
28907
+ if (parts.elseBody === void 0) return parts.bottomTested ? `{
28908
+ ${loop}
28909
+ }` : loop;
28910
+ return `{
28911
+ let _ran = false;
28912
+ ${loop}
28913
+ if (!_ran) {
28914
+ ${parts.elseBody}
28915
+ }
28916
+ }`;
28917
+ }
28237
28918
  function generateRepeat(node, ctx, generateBody) {
28238
28919
  const exprCodegen = new ExpressionCodegen(ctx);
28239
- const body = generateBody(node.body);
28920
+ const shared = {
28921
+ indexName: node.indexName,
28922
+ body: generateBody(node.body),
28923
+ elseBody: node.elseBody ? generateBody(node.elseBody) : void 0
28924
+ };
28240
28925
  if (node.count !== void 0) {
28241
28926
  const count = typeof node.count === "number" ? String(node.count) : exprCodegen.generate(node.count);
28242
- return `for (let _i = 0; _i < ${count}; _i++) {
28243
- _ctx.locals.set('index', _i);
28244
- ${body}
28245
- }`;
28927
+ return emitLoop({ ...shared, test: `_i < ${count}`, indexName: node.indexName ?? "index" });
28246
28928
  }
28247
28929
  if (node.whileCondition) {
28248
- const condition = exprCodegen.generate(node.whileCondition);
28249
- return `while (${condition}) {
28250
- ${body}
28930
+ return emitLoop({ ...shared, test: exprCodegen.generate(node.whileCondition) });
28931
+ }
28932
+ if (node.untilEvent !== void 0) {
28933
+ ctx.requireHelper("wait");
28934
+ const target = node.untilEventTarget ? exprCodegen.generate(node.untilEventTarget) : "_ctx.me";
28935
+ return `{
28936
+ let _fired = false;
28937
+ ${target}.addEventListener(${JSON.stringify(node.untilEvent)}, () => { _fired = true; }, { once: true });
28938
+ ${emitLoop({ ...shared, test: "!_fired", step: "await _rt.wait(0)" })}
28251
28939
  }`;
28252
28940
  }
28253
- return `while (true) {
28254
- ${body}
28255
- }`;
28941
+ return emitLoop({ ...shared, test: "true" });
28256
28942
  }
28257
28943
  function generateForEach(node, ctx, generateBody) {
28258
28944
  const exprCodegen = new ExpressionCodegen(ctx);
28259
- const collection = exprCodegen.generate(node.collection);
28945
+ const source = node.collection;
28946
+ const collection = source.type === "selector" && !String(source.value).startsWith("#") ? `document.querySelectorAll('${sanitizeSelector(String(source.value))}')` : exprCodegen.generate(source);
28260
28947
  const itemName = sanitizeIdentifier(node.itemName);
28261
- const indexName = node.indexName ? sanitizeIdentifier(node.indexName) : "index";
28262
- const body = generateBody(node.body);
28263
28948
  return `{
28264
28949
  const _collection = ${collection};
28265
- const _arr = Array.isArray(_collection) ? _collection : Array.from(_collection);
28266
- for (let _i = 0; _i < _arr.length; _i++) {
28267
- _ctx.locals.set('${itemName}', _arr[_i]);
28268
- _ctx.locals.set('${indexName}', _i);
28269
- ${body}
28270
- }
28950
+ const _arr = _collection == null ? [] : Array.isArray(_collection) ? _collection : Array.from(_collection);
28951
+ ${emitLoop({
28952
+ test: "_i < _arr.length",
28953
+ enter: `_ctx.locals.set('${itemName}', _arr[_i]);`,
28954
+ indexName: node.indexName ?? "index",
28955
+ body: generateBody(node.body),
28956
+ elseBody: node.elseBody ? generateBody(node.elseBody) : void 0
28957
+ })}
28271
28958
  }`;
28272
28959
  }
28273
28960
  function generateWhile(node, ctx, generateBody) {
28274
28961
  const exprCodegen = new ExpressionCodegen(ctx);
28275
- const condition = exprCodegen.generate(node.condition);
28276
- const body = generateBody(node.body);
28277
- return `while (${condition}) {
28278
- ${body}
28279
- }`;
28962
+ return emitLoop({
28963
+ test: exprCodegen.generate(node.condition),
28964
+ bottomTested: node.bottomTested,
28965
+ indexName: node.indexName,
28966
+ body: generateBody(node.body),
28967
+ elseBody: node.elseBody ? generateBody(node.elseBody) : void 0
28968
+ });
28280
28969
  }
28281
28970
  function generateCommand(node, ctx) {
28282
28971
  const codegen = commandCodegens.get(node.name);
@@ -28351,7 +29040,7 @@ async function createMultilingualCompiler() {
28351
29040
  }
28352
29041
  return compiler;
28353
29042
  }
28354
- var import_framework2, DEFAULT_OPTIONS, ELEMENT_REGEX, SCRIPT_TAG_REGEX, ID_REGEX, CLASS_REGEX, LANG_REGEX, HTMLScanner, VueScanner, SvelteScanner, JSXScanner, Analyzer, AnalysisVisitor, CLASS_COMMANDS, ClassBatchingPass, ConstantFoldingPass, SelectorCachingPass, DeadCodeEliminationPass, LoopUnrollingPass, OptimizationPipeline, ExpressionCodegen, ToggleCodegen, AddCodegen, RemoveCodegen, SetCodegen, PutCodegen, ShowCodegen, HideCodegen, FocusCodegen, BlurCodegen, LogCodegen, WaitCodegen, FetchCodegen, SendCodegen, IncrementCodegen, DecrementCodegen, HaltCodegen, ExitCodegen, ReturnCodegen, CallCodegen, ScrollCodegen, TakeCodegen, UnlessCodegen, ThrowCodegen, DefaultCodegen, GoCodegen, AppendCodegen, PickCodegen, PushUrlCodegen, ReplaceUrlCodegen, GetCodegen, BreakCodegen, ContinueCodegen, BeepCodegen, JsCodegen, CopyCodegen, MakeCodegen, SwapCodegen, MorphCodegen, TransitionCodegen, MeasureCodegen, SettleCodegen, TellCodegen, AsyncCodegen, InstallCodegen, RenderCodegen, commandCodegens, EventHandlerCodegen, DEFAULT_COMPILE_OPTIONS, DEFAULT_CODEGEN_OPTIONS, AOTCompiler, VERSION2;
29043
+ var import_framework2, DEFAULT_OPTIONS, ELEMENT_REGEX, SCRIPT_TAG_REGEX, ID_REGEX, CLASS_REGEX, LANG_REGEX, HTMLScanner, VueScanner, SvelteScanner, JSXScanner, Analyzer, AnalysisVisitor, TARGET_SLOTS, ClassBatchingPass, ConstantFoldingPass, SelectorCachingPass, DeadCodeEliminationPass, LoopUnrollingPass, OptimizationPipeline, ExpressionCodegen, isNode2, ToggleCodegen, AddCodegen, RemoveCodegen, SetCodegen, PutCodegen, ShowCodegen, HideCodegen, EmptyCodegen, FocusCodegen, BlurCodegen, LogCodegen, WaitCodegen, FetchCodegen, SendCodegen, IncrementCodegen, DecrementCodegen, HaltCodegen, ExitCodegen, ReturnCodegen, CallCodegen, SCROLL_BLOCK, SCROLL_INLINE, ScrollCodegen, TakeCodegen, UnlessCodegen, ThrowCodegen, DefaultCodegen, GoCodegen, AppendCodegen, PickCodegen, PushUrlCodegen, ReplaceUrlCodegen, GetCodegen, BreakCodegen, ContinueCodegen, BeepCodegen, JsCodegen, CopyCodegen, MakeCodegen, SwapCodegen, MorphCodegen, TransitionCodegen, MeasureCodegen, SettleCodegen, TellCodegen, AsyncCodegen, InstallCodegen, RenderCodegen, commandCodegens, EventHandlerCodegen, DEFAULT_COMPILE_OPTIONS, DEFAULT_CODEGEN_OPTIONS, AOTCompiler, VERSION2;
28355
29044
  var init_dist3 = __esm({
28356
29045
  "../aot-compiler/dist/index.js"() {
28357
29046
  "use strict";
@@ -28720,6 +29409,13 @@ var init_dist3 = __esm({
28720
29409
  }
28721
29410
  visitCommand(node) {
28722
29411
  this.commandsUsed.add(node.name);
29412
+ if (["set", "increment", "decrement", "default"].includes(node.name)) {
29413
+ const target = node.roles?.destination ?? node.roles?.patient ?? node.args?.[0];
29414
+ const name = target?.type === "identifier" ? target.value : void 0;
29415
+ if (name && /^[A-Za-z_]\w*$/.test(name)) {
29416
+ this.registerVariable(name, "local", "write");
29417
+ }
29418
+ }
28723
29419
  switch (node.name) {
28724
29420
  case "fetch":
28725
29421
  this.hasAsync = true;
@@ -28801,32 +29497,37 @@ var init_dist3 = __esm({
28801
29497
  if (node.whileCondition) {
28802
29498
  this.visit(node.whileCondition);
28803
29499
  }
28804
- for (const child of node.body) {
28805
- this.visit(child);
29500
+ if (node.untilEvent !== void 0) {
29501
+ this.hasAsync = true;
29502
+ this.runtimeHelpers.add("wait");
29503
+ if (node.untilEventTarget) this.visit(node.untilEventTarget);
28806
29504
  }
29505
+ this.visitLoopBody(node);
28807
29506
  this.exitNesting();
28808
29507
  }
28809
29508
  visitForEach(node) {
28810
29509
  this.hasLoops = true;
28811
29510
  this.enterNesting();
28812
29511
  this.registerVariable(node.itemName, "local", "write");
28813
- if (node.indexName) {
28814
- this.registerVariable(node.indexName, "local", "write");
28815
- }
28816
29512
  this.visit(node.collection);
28817
- for (const child of node.body) {
28818
- this.visit(child);
28819
- }
29513
+ this.visitLoopBody(node);
28820
29514
  this.exitNesting();
28821
29515
  }
28822
29516
  visitWhile(node) {
28823
29517
  this.hasLoops = true;
28824
29518
  this.enterNesting();
28825
29519
  this.visit(node.condition);
28826
- for (const child of node.body) {
29520
+ this.visitLoopBody(node);
29521
+ this.exitNesting();
29522
+ }
29523
+ /** The parts every loop form shares: its `index` local, body, and `else`. */
29524
+ visitLoopBody(node) {
29525
+ if (node.indexName) {
29526
+ this.registerVariable(node.indexName, "local", "write");
29527
+ }
29528
+ for (const child of [...node.body, ...node.elseBody ?? []]) {
28827
29529
  this.visit(child);
28828
29530
  }
28829
- this.exitNesting();
28830
29531
  }
28831
29532
  visitVariable(node, access) {
28832
29533
  const name = node.name.startsWith(":") || node.name.startsWith("$") ? node.name.slice(1) : node.name;
@@ -28975,7 +29676,11 @@ var init_dist3 = __esm({
28975
29676
  }
28976
29677
  }
28977
29678
  };
28978
- CLASS_COMMANDS = /* @__PURE__ */ new Set(["add", "remove", "toggle"]);
29679
+ TARGET_SLOTS = /* @__PURE__ */ new Map([
29680
+ ["add", "to"],
29681
+ ["remove", "from"],
29682
+ ["toggle", "on"]
29683
+ ]);
28979
29684
  ClassBatchingPass = class {
28980
29685
  constructor() {
28981
29686
  this.name = "class-batching";
@@ -29253,6 +29958,9 @@ var init_dist3 = __esm({
29253
29958
  if (this.usesIndex(body)) {
29254
29959
  return null;
29255
29960
  }
29961
+ if (repeatNode.indexName !== void 0 || repeatNode.elseBody !== void 0) {
29962
+ return null;
29963
+ }
29256
29964
  const unrolled = [];
29257
29965
  for (let i = 0; i < count; i++) {
29258
29966
  unrolled.push(...this.cloneBody(body));
@@ -29381,6 +30089,8 @@ var init_dist3 = __esm({
29381
30089
  return this.generateTemplate(node);
29382
30090
  case "unary":
29383
30091
  return this.generateUnary(node);
30092
+ case "typeCheck":
30093
+ return this.generateTypeCheck(node);
29384
30094
  case "conditional":
29385
30095
  return this.generateConditional(node);
29386
30096
  case "sequence":
@@ -29455,6 +30165,9 @@ var init_dist3 = __esm({
29455
30165
  this.ctx.requireHelper("globals");
29456
30166
  return `_rt.globals.get('${varName}')`;
29457
30167
  }
30168
+ if (this.ctx.analysis?.variables?.locals?.get(name)?.scope === "local") {
30169
+ return `_ctx.locals.get('${sanitizeIdentifier(name)}')`;
30170
+ }
29458
30171
  return sanitizeIdentifier(name);
29459
30172
  }
29460
30173
  }
@@ -29488,7 +30201,8 @@ var init_dist3 = __esm({
29488
30201
  this.ctx.requireHelper("globals");
29489
30202
  return `_rt.globals.get('${varName}')`;
29490
30203
  case "element":
29491
- return `_ctx.me.${varName}`;
30204
+ this.ctx.requireHelper("elementVars");
30205
+ return `_rt.elementVars(_ctx.me).get('${varName}')`;
29492
30206
  default:
29493
30207
  return `_ctx.locals.get('${varName}')`;
29494
30208
  }
@@ -29501,19 +30215,40 @@ var init_dist3 = __esm({
29501
30215
  const right = this.generate(node.right);
29502
30216
  const op = node.operator;
29503
30217
  switch (op) {
29504
- // Equality
30218
+ // Equality, with core's phrase forms of each (`n is equal to 3`).
29505
30219
  case "is":
29506
30220
  case "==":
30221
+ case "am":
30222
+ case "equals":
30223
+ case "is equal":
30224
+ case "is equal to":
29507
30225
  return `(${left} === ${right})`;
29508
30226
  case "is not":
29509
30227
  case "!=":
30228
+ case "is not equal":
30229
+ case "is not equal to":
29510
30230
  return `(${left} !== ${right})`;
29511
- // Comparison
30231
+ case "really equals":
30232
+ case "is really":
30233
+ case "is really equal to":
30234
+ return `(${left} === ${right})`;
30235
+ case "is not really":
30236
+ case "is not really equal to":
30237
+ return `(${left} !== ${right})`;
30238
+ // Comparison. Left as is, `(n is greater than 2)` is not JavaScript.
29512
30239
  case "<":
29513
30240
  case ">":
29514
30241
  case "<=":
29515
30242
  case ">=":
29516
30243
  return `(${left} ${op} ${right})`;
30244
+ case "is less than":
30245
+ return `(${left} < ${right})`;
30246
+ case "is greater than":
30247
+ return `(${left} > ${right})`;
30248
+ case "is less than or equal to":
30249
+ return `(${left} <= ${right})`;
30250
+ case "is greater than or equal to":
30251
+ return `(${left} >= ${right})`;
29517
30252
  // Arithmetic
29518
30253
  case "+":
29519
30254
  case "-":
@@ -29521,6 +30256,9 @@ var init_dist3 = __esm({
29521
30256
  case "/":
29522
30257
  case "%":
29523
30258
  return `(${left} ${op} ${right})`;
30259
+ // Hyperscript's word for `%`; left as is, `(n mod 2)` is not JavaScript.
30260
+ case "mod":
30261
+ return `(${left} % ${right})`;
29524
30262
  // Logical
29525
30263
  case "and":
29526
30264
  case "&&":
@@ -29530,15 +30268,49 @@ var init_dist3 = __esm({
29530
30268
  return `(${left} || ${right})`;
29531
30269
  // String/collection operators
29532
30270
  case "contains":
30271
+ case "contain":
30272
+ case "includes":
30273
+ case "include":
29533
30274
  this.ctx.requireHelper("contains");
29534
30275
  return `_rt.contains(${left}, ${right})`;
30276
+ case "does not contain":
30277
+ case "do not contain":
30278
+ case "does not contains":
30279
+ case "does not include":
30280
+ this.ctx.requireHelper("contains");
30281
+ return `!_rt.contains(${left}, ${right})`;
30282
+ // `matches` tests a selector, so a selector operand stays a string: the
30283
+ // element it would query is not a selector (`_rt.matches(el, el)`).
29535
30284
  case "matches":
30285
+ case "match":
30286
+ this.ctx.requireHelper("matches");
30287
+ return `_rt.matches(${left}, ${this.selectorText(node.right, right)})`;
30288
+ case "does not match":
30289
+ case "do not match":
29536
30290
  this.ctx.requireHelper("matches");
29537
- return `_rt.matches(${left}, ${right})`;
30291
+ return `!_rt.matches(${left}, ${this.selectorText(node.right, right)})`;
30292
+ // Membership, `n is in [1, 3]`: the right side contains the left.
30293
+ case "is in":
30294
+ case "am in":
30295
+ this.ctx.requireHelper("contains");
30296
+ return `_rt.contains(${right}, ${left})`;
30297
+ case "is not in":
30298
+ case "am not in":
30299
+ this.ctx.requireHelper("contains");
30300
+ return `!_rt.contains(${right}, ${left})`;
29538
30301
  case "starts with":
29539
30302
  return `${left}.startsWith(${right})`;
29540
30303
  case "ends with":
29541
30304
  return `${left}.endsWith(${right})`;
30305
+ // A scoped query, `<li/> in #list`: the matches inside the scope, as
30306
+ // both engines read it. JavaScript's `in` (the default below) tests a
30307
+ // property KEY, so `(document.querySelector('li') in …)` matched nothing.
30308
+ case "in":
30309
+ if (node.left.type === "selector") {
30310
+ const query = sanitizeSelector(node.left.value);
30311
+ return `${right}.querySelectorAll('${query}')`;
30312
+ }
30313
+ return `(${left} ${op} ${right})`;
29542
30314
  // Class check
29543
30315
  case "has":
29544
30316
  if (node.right.type === "selector") {
@@ -29566,13 +30338,18 @@ var init_dist3 = __esm({
29566
30338
  return `(${left} ${op} ${right})`;
29567
30339
  }
29568
30340
  }
30341
+ /** A selector operand as its quoted text; any other operand as generated. */
30342
+ selectorText(node, generated) {
30343
+ return node.type === "selector" ? `'${sanitizeSelector(node.value)}'` : generated;
30344
+ }
29569
30345
  // ===========================================================================
29570
30346
  // MEMBER EXPRESSIONS
29571
30347
  // ===========================================================================
29572
30348
  generateMember(node) {
29573
30349
  const object = this.generate(node.object);
29574
- if (typeof node.property === "string") {
29575
- const prop = node.property;
30350
+ const property = node.property;
30351
+ const prop = typeof property === "string" ? property : !node.computed && property.type === "identifier" ? property.value ?? property.name : void 0;
30352
+ if (prop !== void 0) {
29576
30353
  if (prop.startsWith("*")) {
29577
30354
  const styleProp = prop.slice(1);
29578
30355
  return `${object}.style.${styleProp}`;
@@ -29754,6 +30531,12 @@ var init_dist3 = __esm({
29754
30531
  // ===========================================================================
29755
30532
  // UNARY EXPRESSIONS
29756
30533
  // ===========================================================================
30534
+ /** `x is a Number`, as both engines test it: see the runtime's `typeCheck`. */
30535
+ generateTypeCheck(node) {
30536
+ this.ctx.requireHelper("typeCheck");
30537
+ const test = `_rt.typeCheck(${this.generate(node.value)}, ${JSON.stringify(node.typeName)}, ${node.nullOk})`;
30538
+ return node.negated ? `!${test}` : test;
30539
+ }
29757
30540
  generateUnary(node) {
29758
30541
  const operand = this.generate(node.operand);
29759
30542
  switch (node.operator) {
@@ -29766,6 +30549,22 @@ var init_dist3 = __esm({
29766
30549
  return `+${operand}`;
29767
30550
  case "no":
29768
30551
  return `!${operand}`;
30552
+ // Core's postfix tests. Left as is, `exists#d1` is not JavaScript.
30553
+ case "exists":
30554
+ case "some":
30555
+ this.ctx.requireHelper("exists");
30556
+ return `_rt.exists(${operand})`;
30557
+ case "does not exist":
30558
+ this.ctx.requireHelper("exists");
30559
+ return `!_rt.exists(${operand})`;
30560
+ case "is empty":
30561
+ this.ctx.requireHelper("isEmpty");
30562
+ return `_rt.isEmpty(${operand})`;
30563
+ case "is not empty":
30564
+ this.ctx.requireHelper("isEmpty");
30565
+ return `!_rt.isEmpty(${operand})`;
30566
+ case "beep!":
30567
+ return `((v) => (console.log('%c[beep]', 'color: orange; font-weight: bold', v), v))(${operand})`;
29769
30568
  default:
29770
30569
  return `${node.operator}${operand}`;
29771
30570
  }
@@ -29780,6 +30579,7 @@ var init_dist3 = __esm({
29780
30579
  return `(${condition} ? ${consequent} : ${alternate})`;
29781
30580
  }
29782
30581
  };
30582
+ isNode2 = (value) => typeof value === "object" && value !== null && "type" in value;
29783
30583
  ToggleCodegen = class {
29784
30584
  constructor() {
29785
30585
  this.command = "toggle";
@@ -29787,22 +30587,15 @@ var init_dist3 = __esm({
29787
30587
  generate(node, ctx) {
29788
30588
  const args = node.args ?? [];
29789
30589
  if (args.length === 0) return null;
29790
- const target = node.target ? ctx.generateExpression(node.target) : "_ctx.me";
30590
+ const targetNode = commandTarget(node, "on");
29791
30591
  const arg = args[0];
29792
30592
  if (arg.type === "selector") {
29793
30593
  const selector = arg.value;
29794
30594
  if (selector.startsWith(".")) {
29795
30595
  const className = sanitizeClassName(selector.slice(1));
29796
30596
  if (!className) return null;
29797
- if (target === "_ctx.me") {
29798
- return {
29799
- code: `_ctx.me.classList.toggle('${className}')`,
29800
- async: false,
29801
- sideEffects: true
29802
- };
29803
- }
29804
30597
  return {
29805
- code: `Array.from(document.querySelectorAll('${sanitizeSelector(selector.slice(1))}')).forEach(el => el.classList.toggle('${className}'))`,
30598
+ code: forEachTarget(targetNode, ctx, (el) => `${el}.classList.toggle('${className}')`),
29806
30599
  async: false,
29807
30600
  sideEffects: true
29808
30601
  };
@@ -29814,7 +30607,11 @@ var init_dist3 = __esm({
29814
30607
  const attrName = value.slice(1);
29815
30608
  ctx.requireHelper("toggleAttr");
29816
30609
  return {
29817
- code: `_rt.toggleAttr(${target}, '${sanitizeSelector(attrName)}')`,
30610
+ code: forEachTarget(
30611
+ targetNode,
30612
+ ctx,
30613
+ (el) => `_rt.toggleAttr(${el}, '${sanitizeSelector(attrName)}')`
30614
+ ),
29818
30615
  async: false,
29819
30616
  sideEffects: true
29820
30617
  };
@@ -29822,7 +30619,11 @@ var init_dist3 = __esm({
29822
30619
  }
29823
30620
  ctx.requireHelper("toggle");
29824
30621
  return {
29825
- code: `_rt.toggle(${ctx.generateExpression(arg)}, ${target})`,
30622
+ code: forEachTarget(
30623
+ targetNode,
30624
+ ctx,
30625
+ (el) => `_rt.toggle(${ctx.generateExpression(arg)}, ${el})`
30626
+ ),
29826
30627
  async: false,
29827
30628
  sideEffects: true
29828
30629
  };
@@ -29843,15 +30644,8 @@ var init_dist3 = __esm({
29843
30644
  if (selector.startsWith(".")) {
29844
30645
  const className = sanitizeClassName(selector.slice(1));
29845
30646
  if (!className) return null;
29846
- if (target === "_ctx.me") {
29847
- return {
29848
- code: `_ctx.me.classList.add('${className}')`,
29849
- async: false,
29850
- sideEffects: true
29851
- };
29852
- }
29853
30647
  return {
29854
- code: `${target}.classList.add('${className}')`,
30648
+ code: forEachTarget(targetSlot, ctx, (el) => `${el}.classList.add('${className}')`),
29855
30649
  async: false,
29856
30650
  sideEffects: true
29857
30651
  };
@@ -29874,7 +30668,11 @@ var init_dist3 = __esm({
29874
30668
  }
29875
30669
  ctx.requireHelper("addClass");
29876
30670
  return {
29877
- code: `_rt.addClass(${target}, ${ctx.generateExpression(arg)})`,
30671
+ code: forEachTarget(
30672
+ targetSlot,
30673
+ ctx,
30674
+ (el) => `_rt.addClass(${el}, ${ctx.generateExpression(arg)})`
30675
+ ),
29878
30676
  async: false,
29879
30677
  sideEffects: true
29880
30678
  };
@@ -29888,23 +30686,29 @@ var init_dist3 = __esm({
29888
30686
  const args = node.args ?? [];
29889
30687
  if (args.length === 0) {
29890
30688
  const targetSlot2 = node.target ?? node.modifiers?.from;
29891
- const target2 = targetSlot2 ? ctx.generateExpression(targetSlot2) : "_ctx.me";
29892
30689
  return {
29893
- code: `${target2}.remove()`,
30690
+ code: forEachTarget(targetSlot2, ctx, (el) => `${el}.remove()`),
29894
30691
  async: false,
29895
30692
  sideEffects: true
29896
30693
  };
29897
30694
  }
29898
30695
  const targetSlot = node.target ?? node.modifiers?.from;
29899
- const target = targetSlot ? ctx.generateExpression(targetSlot) : "_ctx.me";
29900
30696
  const arg = args[0];
30697
+ const isElement2 = arg.type === "selector" ? !/^[.@]/.test(arg.value) : arg.type === "identifier" && !String(arg.value ?? "").startsWith("@") || arg.type === "variable" || isScopedQuery(arg);
30698
+ if (!targetSlot && isElement2) {
30699
+ return {
30700
+ code: forEachTarget(arg, ctx, (el) => `${el}.remove()`),
30701
+ async: false,
30702
+ sideEffects: true
30703
+ };
30704
+ }
29901
30705
  if (arg.type === "selector") {
29902
30706
  const selector = arg.value;
29903
30707
  if (selector.startsWith(".")) {
29904
30708
  const className = sanitizeClassName(selector.slice(1));
29905
30709
  if (!className) return null;
29906
30710
  return {
29907
- code: `${target}.classList.remove('${className}')`,
30711
+ code: forEachTarget(targetSlot, ctx, (el) => `${el}.classList.remove('${className}')`),
29908
30712
  async: false,
29909
30713
  sideEffects: true
29910
30714
  };
@@ -29912,7 +30716,11 @@ var init_dist3 = __esm({
29912
30716
  }
29913
30717
  ctx.requireHelper("removeClass");
29914
30718
  return {
29915
- code: `_rt.removeClass(${target}, ${ctx.generateExpression(arg)})`,
30719
+ code: forEachTarget(
30720
+ targetSlot,
30721
+ ctx,
30722
+ (el) => `_rt.removeClass(${el}, ${ctx.generateExpression(arg)})`
30723
+ ),
29916
30724
  async: false,
29917
30725
  sideEffects: true
29918
30726
  };
@@ -29930,26 +30738,8 @@ var init_dist3 = __esm({
29930
30738
  const valueNode = roles?.patient ?? modifiers?.to ?? args[1];
29931
30739
  if (!targetNode || !valueNode) return null;
29932
30740
  const value = ctx.generateExpression(valueNode);
29933
- if (targetNode.type === "variable") {
29934
- const varNode = targetNode;
29935
- const name = varNode.name.startsWith(":") ? varNode.name.slice(1) : varNode.name;
29936
- const safeName = sanitizeIdentifier(name);
29937
- if (varNode.scope === "local") {
29938
- return {
29939
- code: `_ctx.locals.set('${safeName}', ${value})`,
29940
- async: false,
29941
- sideEffects: true
29942
- };
29943
- }
29944
- if (varNode.scope === "global") {
29945
- ctx.requireHelper("globals");
29946
- return {
29947
- code: `_rt.globals.set('${safeName}', ${value})`,
29948
- async: false,
29949
- sideEffects: true
29950
- };
29951
- }
29952
- }
30741
+ const store = variableStore(targetNode, ctx);
30742
+ if (store) return { code: store.write(value), async: false, sideEffects: true };
29953
30743
  if (targetNode.type === "possessive") {
29954
30744
  const possessive = targetNode;
29955
30745
  const obj = ctx.generateExpression(possessive.object);
@@ -29998,20 +30788,17 @@ var init_dist3 = __esm({
29998
30788
  const contentNode = roles?.patient ?? args[0];
29999
30789
  if (!contentNode) return null;
30000
30790
  const content = ctx.generateExpression(contentNode);
30001
- let target = "_ctx.me";
30002
- if (roles?.destination) {
30003
- target = ctx.generateExpression(roles.destination);
30004
- } else if (modifiers) {
30791
+ let targetNode = roles?.destination;
30792
+ if (!targetNode && modifiers) {
30005
30793
  for (const key of ["into", "before", "after"]) {
30006
30794
  if (modifiers[key] && typeof modifiers[key] === "object") {
30007
- target = ctx.generateExpression(modifiers[key]);
30795
+ targetNode = modifiers[key];
30008
30796
  break;
30009
30797
  }
30010
30798
  }
30011
30799
  }
30012
- if (target === "_ctx.me" && node.target) {
30013
- target = ctx.generateExpression(node.target);
30014
- }
30800
+ targetNode ?? (targetNode = node.target);
30801
+ const into = (op) => forEachTarget(targetNode, ctx, op);
30015
30802
  let modifier = "into";
30016
30803
  if (roles?.method && roles.method.value) {
30017
30804
  modifier = String(roles.method.value);
@@ -30023,39 +30810,39 @@ var init_dist3 = __esm({
30023
30810
  switch (modifier) {
30024
30811
  case "into":
30025
30812
  return {
30026
- code: `${target}.innerHTML = ${content}`,
30813
+ code: into((el) => `${el}.innerHTML = ${content}`),
30027
30814
  async: false,
30028
30815
  sideEffects: true
30029
30816
  };
30030
30817
  case "before":
30031
30818
  return {
30032
- code: `${target}.insertAdjacentHTML('beforebegin', ${content})`,
30819
+ code: into((el) => `${el}.insertAdjacentHTML('beforebegin', ${content})`),
30033
30820
  async: false,
30034
30821
  sideEffects: true
30035
30822
  };
30036
30823
  case "after":
30037
30824
  return {
30038
- code: `${target}.insertAdjacentHTML('afterend', ${content})`,
30825
+ code: into((el) => `${el}.insertAdjacentHTML('afterend', ${content})`),
30039
30826
  async: false,
30040
30827
  sideEffects: true
30041
30828
  };
30042
30829
  case "at start of":
30043
30830
  case "start":
30044
30831
  return {
30045
- code: `${target}.insertAdjacentHTML('afterbegin', ${content})`,
30832
+ code: into((el) => `${el}.insertAdjacentHTML('afterbegin', ${content})`),
30046
30833
  async: false,
30047
30834
  sideEffects: true
30048
30835
  };
30049
30836
  case "at end of":
30050
30837
  case "end":
30051
30838
  return {
30052
- code: `${target}.insertAdjacentHTML('beforeend', ${content})`,
30839
+ code: into((el) => `${el}.insertAdjacentHTML('beforeend', ${content})`),
30053
30840
  async: false,
30054
30841
  sideEffects: true
30055
30842
  };
30056
30843
  default:
30057
30844
  return {
30058
- code: `${target}.innerHTML = ${content}`,
30845
+ code: into((el) => `${el}.innerHTML = ${content}`),
30059
30846
  async: false,
30060
30847
  sideEffects: true
30061
30848
  };
@@ -30067,9 +30854,9 @@ var init_dist3 = __esm({
30067
30854
  this.command = "show";
30068
30855
  }
30069
30856
  generate(node, ctx) {
30070
- const target = node.target ? ctx.generateExpression(node.target) : "_ctx.me";
30857
+ const targetNode = commandTarget(node, "arg");
30071
30858
  return {
30072
- code: `${target}.style.display = ''`,
30859
+ code: forEachTarget(targetNode, ctx, (el) => `${el}.style.display = ''`),
30073
30860
  async: false,
30074
30861
  sideEffects: true
30075
30862
  };
@@ -30080,9 +30867,22 @@ var init_dist3 = __esm({
30080
30867
  this.command = "hide";
30081
30868
  }
30082
30869
  generate(node, ctx) {
30083
- const target = node.target ? ctx.generateExpression(node.target) : "_ctx.me";
30870
+ const targetNode = commandTarget(node, "arg");
30871
+ return {
30872
+ code: forEachTarget(targetNode, ctx, (el) => `${el}.style.display = 'none'`),
30873
+ async: false,
30874
+ sideEffects: true
30875
+ };
30876
+ }
30877
+ };
30878
+ EmptyCodegen = class {
30879
+ constructor() {
30880
+ this.command = "empty";
30881
+ }
30882
+ generate(node, ctx) {
30883
+ const targetNode = commandTarget(node, "arg");
30084
30884
  return {
30085
- code: `${target}.style.display = 'none'`,
30885
+ code: forEachTarget(targetNode, ctx, (el) => `${el}.replaceChildren()`),
30086
30886
  async: false,
30087
30887
  sideEffects: true
30088
30888
  };
@@ -30093,7 +30893,8 @@ var init_dist3 = __esm({
30093
30893
  this.command = "focus";
30094
30894
  }
30095
30895
  generate(node, ctx) {
30096
- const target = node.target ? ctx.generateExpression(node.target) : "_ctx.me";
30896
+ const targetNode = commandTarget(node, "arg");
30897
+ const target = targetNode ? ctx.generateExpression(targetNode) : "_ctx.me";
30097
30898
  return {
30098
30899
  code: `${target}.focus()`,
30099
30900
  async: false,
@@ -30106,7 +30907,8 @@ var init_dist3 = __esm({
30106
30907
  this.command = "blur";
30107
30908
  }
30108
30909
  generate(node, ctx) {
30109
- const target = node.target ? ctx.generateExpression(node.target) : "_ctx.me";
30910
+ const targetNode = commandTarget(node, "arg");
30911
+ const target = targetNode ? ctx.generateExpression(targetNode) : "_ctx.me";
30110
30912
  return {
30111
30913
  code: `${target}.blur()`,
30112
30914
  async: false,
@@ -30227,11 +31029,11 @@ var init_dist3 = __esm({
30227
31029
  const args = node.args ?? [];
30228
31030
  if (args.length === 0) return null;
30229
31031
  const eventName = ctx.generateExpression(args[0]);
30230
- const target = node.target ? ctx.generateExpression(node.target) : "_ctx.me";
31032
+ const targetNode = commandTarget(node, "on", "to");
30231
31033
  const detail = args.length > 1 ? ctx.generateExpression(args[1]) : "undefined";
30232
31034
  ctx.requireHelper("send");
30233
31035
  return {
30234
- code: `_rt.send(${target}, ${eventName}, ${detail})`,
31036
+ code: forEachTarget(targetNode, ctx, (el) => `_rt.send(${el}, ${eventName}, ${detail})`),
30235
31037
  async: false,
30236
31038
  sideEffects: true
30237
31039
  };
@@ -30249,25 +31051,10 @@ var init_dist3 = __esm({
30249
31051
  if (!target) return null;
30250
31052
  const amountNode = roles?.quantity ?? modifiers?.by ?? args[1];
30251
31053
  const amount = amountNode ? ctx.generateExpression(amountNode) : "1";
30252
- if (target.type === "variable") {
30253
- const varNode = target;
30254
- const name = varNode.name.startsWith(":") ? varNode.name.slice(1) : varNode.name;
30255
- const safeName = sanitizeIdentifier(name);
30256
- if (varNode.scope === "local") {
30257
- return {
30258
- code: `_ctx.locals.set('${safeName}', (_ctx.locals.get('${safeName}') || 0) + ${amount})`,
30259
- async: false,
30260
- sideEffects: true
30261
- };
30262
- }
30263
- if (varNode.scope === "global") {
30264
- ctx.requireHelper("globals");
30265
- return {
30266
- code: `_rt.globals.set('${safeName}', (_rt.globals.get('${safeName}') || 0) + ${amount})`,
30267
- async: false,
30268
- sideEffects: true
30269
- };
30270
- }
31054
+ const store = variableStore(target, ctx);
31055
+ if (store) {
31056
+ const code = store.write(`(Number(${store.read}) || 0) + ${amount}`);
31057
+ return { code, async: false, sideEffects: true };
30271
31058
  }
30272
31059
  const targetCode = ctx.generateExpression(target);
30273
31060
  return {
@@ -30289,25 +31076,10 @@ var init_dist3 = __esm({
30289
31076
  if (!target) return null;
30290
31077
  const amountNode = roles?.quantity ?? modifiers?.by ?? args[1];
30291
31078
  const amount = amountNode ? ctx.generateExpression(amountNode) : "1";
30292
- if (target.type === "variable") {
30293
- const varNode = target;
30294
- const name = varNode.name.startsWith(":") ? varNode.name.slice(1) : varNode.name;
30295
- const safeName = sanitizeIdentifier(name);
30296
- if (varNode.scope === "local") {
30297
- return {
30298
- code: `_ctx.locals.set('${safeName}', (_ctx.locals.get('${safeName}') || 0) - ${amount})`,
30299
- async: false,
30300
- sideEffects: true
30301
- };
30302
- }
30303
- if (varNode.scope === "global") {
30304
- ctx.requireHelper("globals");
30305
- return {
30306
- code: `_rt.globals.set('${safeName}', (_rt.globals.get('${safeName}') || 0) - ${amount})`,
30307
- async: false,
30308
- sideEffects: true
30309
- };
30310
- }
31079
+ const store = variableStore(target, ctx);
31080
+ if (store) {
31081
+ const code = store.write(`(Number(${store.read}) || 0) - ${amount}`);
31082
+ return { code, async: false, sideEffects: true };
30311
31083
  }
30312
31084
  const targetCode = ctx.generateExpression(target);
30313
31085
  return {
@@ -30372,17 +31144,22 @@ var init_dist3 = __esm({
30372
31144
  };
30373
31145
  }
30374
31146
  };
31147
+ SCROLL_BLOCK = {
31148
+ top: "start",
31149
+ middle: "center",
31150
+ bottom: "end",
31151
+ nearest: "nearest"
31152
+ };
31153
+ SCROLL_INLINE = { left: "start", center: "center", right: "end" };
30375
31154
  ScrollCodegen = class {
30376
31155
  constructor() {
30377
31156
  this.command = "scroll";
30378
31157
  }
30379
31158
  generate(node, ctx) {
30380
- const target = node.target ? ctx.generateExpression(node.target) : "_ctx.me";
30381
- const mods = node.modifiers;
30382
- const smooth = mods?.smooth || mods?.behavior?.value === "smooth";
30383
- const behavior = smooth ? "'smooth'" : "'auto'";
31159
+ const targetNode = commandTarget(node, "arg", "of");
31160
+ const target = targetNode ? ctx.generateExpression(targetNode) : "_ctx.me";
30384
31161
  return {
30385
- code: `${target}.scrollIntoView({ behavior: ${behavior} })`,
31162
+ code: `${target}.scrollIntoView(${scrollOptions(node.modifiers)})`,
30386
31163
  async: false,
30387
31164
  sideEffects: true
30388
31165
  };
@@ -30392,7 +31169,7 @@ var init_dist3 = __esm({
30392
31169
  constructor() {
30393
31170
  this.command = "take";
30394
31171
  }
30395
- generate(node, _ctx) {
31172
+ generate(node, ctx) {
30396
31173
  const args = node.args ?? [];
30397
31174
  if (args.length === 0) return null;
30398
31175
  const arg = args[0];
@@ -30401,6 +31178,19 @@ var init_dist3 = __esm({
30401
31178
  if (!selector.startsWith(".")) return null;
30402
31179
  const className = sanitizeClassName(selector.slice(1));
30403
31180
  if (!className) return null;
31181
+ const source = commandTarget(node, "from");
31182
+ if (source) {
31183
+ let from = allMatches(source, ctx);
31184
+ if (!from) {
31185
+ ctx.requireHelper("toArray");
31186
+ from = `_rt.toArray(${ctx.generateExpression(source)})`;
31187
+ }
31188
+ return {
31189
+ code: `(() => { ${from}.forEach(el => el.classList.remove('${className}')); _ctx.me.classList.add('${className}'); })()`,
31190
+ async: false,
31191
+ sideEffects: true
31192
+ };
31193
+ }
30404
31194
  return {
30405
31195
  code: `(() => { const _me = _ctx.me; _me.parentElement?.querySelectorAll('.${className}').forEach(el => el.classList.remove('${className}')); _me.classList.add('${className}'); })()`,
30406
31196
  async: false,
@@ -30449,25 +31239,10 @@ var init_dist3 = __esm({
30449
31239
  const valueNode = roles?.patient ?? modifiers?.to ?? args[1];
30450
31240
  if (!targetNode || !valueNode) return null;
30451
31241
  const value = ctx.generateExpression(valueNode);
30452
- if (targetNode.type === "variable") {
30453
- const varNode = targetNode;
30454
- const name = varNode.name.startsWith(":") ? varNode.name.slice(1) : varNode.name;
30455
- const safeName = sanitizeIdentifier(name);
30456
- if (varNode.scope === "local") {
30457
- return {
30458
- code: `if (_ctx.locals.get('${safeName}') == null) _ctx.locals.set('${safeName}', ${value})`,
30459
- async: false,
30460
- sideEffects: true
30461
- };
30462
- }
30463
- if (varNode.scope === "global") {
30464
- ctx.requireHelper("globals");
30465
- return {
30466
- code: `if (_rt.globals.get('${safeName}') == null) _rt.globals.set('${safeName}', ${value})`,
30467
- async: false,
30468
- sideEffects: true
30469
- };
30470
- }
31242
+ const store = variableStore(targetNode, ctx);
31243
+ if (store) {
31244
+ const code = `if (${store.read} == null) ${store.write(value)}`;
31245
+ return { code, async: false, sideEffects: true };
30471
31246
  }
30472
31247
  return null;
30473
31248
  }
@@ -30483,6 +31258,15 @@ var init_dist3 = __esm({
30483
31258
  if (mods?.back) {
30484
31259
  return { code: "history.back()", async: false, sideEffects: true };
30485
31260
  }
31261
+ if (mods?.position || mods?.behavior) {
31262
+ const element = roles?.destination ?? args[0];
31263
+ if (!element) return null;
31264
+ return {
31265
+ code: `${ctx.generateExpression(element)}.scrollIntoView(${scrollOptions(mods)})`,
31266
+ async: false,
31267
+ sideEffects: true
31268
+ };
31269
+ }
30486
31270
  const target = mods?.url ?? roles?.destination ?? args[0];
30487
31271
  if (!target) return null;
30488
31272
  if (target.type === "identifier") {
@@ -30494,9 +31278,10 @@ var init_dist3 = __esm({
30494
31278
  return { code: "history.forward()", async: false, sideEffects: true };
30495
31279
  }
30496
31280
  }
30497
- const url = ctx.generateExpression(target);
31281
+ ctx.requireHelper("go");
31282
+ const newWindow = mods?.in !== void 0 ? ", true" : "";
30498
31283
  return {
30499
- code: `window.location.href = ${url}`,
31284
+ code: `_rt.go(${ctx.generateExpression(target)}${newWindow})`,
30500
31285
  async: false,
30501
31286
  sideEffects: true
30502
31287
  };
@@ -30512,7 +31297,8 @@ var init_dist3 = __esm({
30512
31297
  const contentNode = roles?.patient ?? args[0];
30513
31298
  if (!contentNode) return null;
30514
31299
  const content = ctx.generateExpression(contentNode);
30515
- const target = node.target ? ctx.generateExpression(node.target) : "_ctx.me";
31300
+ const targetNode = commandTarget(node, "to");
31301
+ const target = targetNode ? ctx.generateExpression(targetNode) : "_ctx.me";
30516
31302
  return {
30517
31303
  code: `${target}.insertAdjacentHTML('beforeend', ${content})`,
30518
31304
  async: false,
@@ -30831,9 +31617,9 @@ var init_dist3 = __esm({
30831
31617
  const args = node.args ?? [];
30832
31618
  const roles = node.roles;
30833
31619
  const mods = node.modifiers;
30834
- const propertyNode = roles?.patient ?? args[0];
30835
- if (!propertyNode) return null;
30836
- const property = ctx.generateExpression(propertyNode);
31620
+ const propertyNode2 = roles?.patient ?? args[0];
31621
+ if (!propertyNode2) return null;
31622
+ const property = ctx.generateExpression(propertyNode2);
30837
31623
  const valueNode = mods?.to ?? args[1];
30838
31624
  if (!valueNode) return null;
30839
31625
  const value = ctx.generateExpression(valueNode);
@@ -30891,7 +31677,8 @@ var init_dist3 = __esm({
30891
31677
  this.command = "settle";
30892
31678
  }
30893
31679
  generate(node, ctx) {
30894
- const target = node.target ? ctx.generateExpression(node.target) : "_ctx.me";
31680
+ const targetNode = commandTarget(node, "arg");
31681
+ const target = targetNode ? ctx.generateExpression(targetNode) : "_ctx.me";
30895
31682
  const mods = node.modifiers;
30896
31683
  const timeoutNode = mods?.for;
30897
31684
  const timeout = timeoutNode ? ctx.generateExpression(timeoutNode) : "5000";
@@ -31011,6 +31798,7 @@ var init_dist3 = __esm({
31011
31798
  ["put", new PutCodegen()],
31012
31799
  ["show", new ShowCodegen()],
31013
31800
  ["hide", new HideCodegen()],
31801
+ ["empty", new EmptyCodegen()],
31014
31802
  ["focus", new FocusCodegen()],
31015
31803
  ["blur", new BlurCodegen()],
31016
31804
  ["log", new LogCodegen()],
@@ -31112,6 +31900,8 @@ var init_dist3 = __esm({
31112
31900
  return generateForEach(node, this.ctx, (nodes) => this.generateBody(nodes));
31113
31901
  case "while":
31114
31902
  return generateWhile(node, this.ctx, (nodes) => this.generateBody(nodes));
31903
+ case "sequence":
31904
+ return this.generateBody(node.commands);
31115
31905
  case "event": {
31116
31906
  const nested = node;
31117
31907
  return this.generateBody(nested.body ?? []);
@@ -31961,15 +32751,8 @@ var init_dist3 = __esm({
31961
32751
  * Create a codegen context.
31962
32752
  */
31963
32753
  createCodegenContext(handlerId, analysis, options) {
31964
- const selectorCache = /* @__PURE__ */ new Map();
31965
32754
  const requiredHelpers = /* @__PURE__ */ new Set();
31966
32755
  let idCounter = 0;
31967
- for (const info of analysis.expressions.selectors) {
31968
- if (info.canCache && info.usages.length > 1) {
31969
- const cacheKey = "_sel_" + sanitizeIdentifier(info.selector).slice(0, 20) + "_" + idCounter++;
31970
- selectorCache.set(info.selector, cacheKey);
31971
- }
31972
- }
31973
32756
  const exprCodegenRef = { current: null };
31974
32757
  const ctx = {
31975
32758
  handlerId,
@@ -31982,8 +32765,12 @@ var init_dist3 = __esm({
31982
32765
  },
31983
32766
  implicitTarget: "_ctx.me",
31984
32767
  localVarDeclarations: "",
31985
- canCacheSelector: (selector) => selectorCache.has(selector),
31986
- getCachedSelector: (selector) => selectorCache.get(selector) ?? `document.querySelector('${selector}')`,
32768
+ // A selector is queried each time it is evaluated, as on both engines. The
32769
+ // cache named a variable nothing declared (`_sel__d1_0`: a handler naming
32770
+ // #d1 twice threw), and a declared one goes stale when the handler
32771
+ // replaces the element.
32772
+ canCacheSelector: () => false,
32773
+ getCachedSelector: (selector) => `document.querySelector('${selector}')`,
31987
32774
  requireHelper: (name) => {
31988
32775
  requiredHelpers.add(name);
31989
32776
  },
@@ -32471,6 +33258,7 @@ function normalizeJSON(input) {
32471
33258
 
32472
33259
  // src/validation/inert-shapes.ts
32473
33260
  var QUANTIFIER = /^(all|every|each)\.(\S+)$/;
33261
+ var BARE_QUANTIFIER = /^(all|every|each)$/;
32474
33262
  var PREDICATE_AS_CLASS = /(^|\s)\.(has|is|contains|matches|includes)(\s|$)/;
32475
33263
  function valueString(v2) {
32476
33264
  return String(v2.value ?? v2.raw ?? "");
@@ -32497,6 +33285,15 @@ function walk(node, out) {
32497
33285
  });
32498
33286
  continue;
32499
33287
  }
33288
+ if (v2.type === "expression" && BARE_QUANTIFIER.test(s) && (role === "destination" || role === "source")) {
33289
+ out.push({
33290
+ severity: "warning",
33291
+ code: "INERT_QUANTIFIER_TARGET",
33292
+ message: `${role} parsed as the identifier "${s}", which is undefined at runtime, so this ${role} will not resolve; the selector after it is not read.`,
33293
+ suggestion: `Selectors already address every match: write the selector alone, without "${s}".`
33294
+ });
33295
+ continue;
33296
+ }
32500
33297
  if (role === "condition" && v2.type === "expression" && PREDICATE_AS_CLASS.test(s)) {
32501
33298
  const word = PREDICATE_AS_CLASS.exec(s)?.[2];
32502
33299
  out.push({
@@ -32769,7 +33566,8 @@ function extractOperations(node) {
32769
33566
  }
32770
33567
  function extractEventHandler(n) {
32771
33568
  const eventRole = n.roles?.get("event");
32772
- const eventName = String(eventRole?.value ?? "click");
33569
+ const named = eventRole?.value ?? eventRole?.raw;
33570
+ const eventName = typeof named === "string" && named.length > 0 ? named : "click";
32773
33571
  const operations = [];
32774
33572
  if (n.body && Array.isArray(n.body)) {
32775
33573
  for (const child of n.body) {
@@ -34871,6 +35669,7 @@ function renderSvelteElement(el, analysis) {
34871
35669
  var CompilationService = class _CompilationService {
34872
35670
  constructor(options = {}) {
34873
35671
  this.translateFn = null;
35672
+ this.translationCollisionsFn = null;
34874
35673
  this.confidenceThreshold = options.confidenceThreshold ?? 0.7;
34875
35674
  this.cache = new SemanticCache(options.cacheSize ?? 500);
34876
35675
  this.testRenderers = new Map(
@@ -34925,6 +35724,7 @@ var CompilationService = class _CompilationService {
34925
35724
  compiler
34926
35725
  );
34927
35726
  service.translateFn = semantic.translate;
35727
+ service.translationCollisionsFn = semantic.findTranslationCollisions;
34928
35728
  return service;
34929
35729
  }
34930
35730
  // ===========================================================================
@@ -35049,7 +35849,7 @@ var CompilationService = class _CompilationService {
35049
35849
  ok: true,
35050
35850
  code: result,
35051
35851
  ...verification !== void 0 ? { verification } : {},
35052
- diagnostics: []
35852
+ diagnostics: this.translationCollisions(request)
35053
35853
  };
35054
35854
  } catch (error) {
35055
35855
  return {
@@ -35064,6 +35864,20 @@ var CompilationService = class _CompilationService {
35064
35864
  };
35065
35865
  }
35066
35866
  }
35867
+ /**
35868
+ * A variable of the source that the target language reads as a value word
35869
+ * (tl `ako` is `me`): the translation reads the value, and no spelling of
35870
+ * the name tells them apart, so the fix is a rename before translating.
35871
+ */
35872
+ translationCollisions(request) {
35873
+ const findings = this.translationCollisionsFn?.(request.code, request.from, request.to) ?? [];
35874
+ return findings.map((finding) => ({
35875
+ severity: "warning",
35876
+ code: "NAME_COLLISION",
35877
+ message: finding.message,
35878
+ suggestion: finding.rename ? `Rename \`${finding.name}\` to \`${finding.rename}\` in the source (${finding.occurrences.length} place(s)), then translate.` : `Rename \`${finding.name}\` in the source, then translate.`
35879
+ }));
35880
+ }
35067
35881
  /**
35068
35882
  * Generate behavior-level tests from hyperscript.
35069
35883
  *
@@ -35305,7 +36119,27 @@ var CompilationService = class _CompilationService {
35305
36119
  if (!gateRef.pass || !gateCand.pass) {
35306
36120
  return { ok: false, diagnostics };
35307
36121
  }
35308
- return { ok: true, diagnostics, ...(0, import_fidelity.scoreNodes)(normRef.node, normCand.node) };
36122
+ const truncated = (...lists) => lists.some((ds) => ds.some((d) => d.code === "UNCONSUMED_INPUT"));
36123
+ const referenceComplete = !truncated(normRef.diagnostics, gateRef.diagnostics);
36124
+ const candidateComplete = !truncated(normCand.diagnostics, gateCand.diagnostics);
36125
+ if (!referenceComplete || !candidateComplete) {
36126
+ const sides = [!referenceComplete && "reference", !candidateComplete && "candidate"].filter(Boolean).join(" and ");
36127
+ diagnostics.push({
36128
+ severity: "warning",
36129
+ code: "INCOMPLETE_PARSE",
36130
+ message: `The ${sides} parse left tokens unconsumed, so the scores compare only what parsed; not faithful.`,
36131
+ suggestion: "The unconsumed tokens are named in the UNCONSUMED_INPUT diagnostics above. If the reference is truncated, a translation of it drops the same clause."
36132
+ });
36133
+ }
36134
+ const report2 = (0, import_fidelity.scoreNodes)(normRef.node, normCand.node);
36135
+ return {
36136
+ ok: true,
36137
+ diagnostics,
36138
+ ...report2,
36139
+ faithful: report2.faithful && referenceComplete && candidateComplete,
36140
+ referenceComplete,
36141
+ candidateComplete
36142
+ };
35309
36143
  }
35310
36144
  /**
35311
36145
  * Generate a target-specific output from LSE (bracket syntax or protocol JSON).
@@ -35485,7 +36319,7 @@ function nodeToSemanticJSON(node) {
35485
36319
  const bodyJSON = nodeToSemanticJSON(n.body[0]);
35486
36320
  if (bodyJSON) {
35487
36321
  bodyJSON.trigger = {
35488
- event: eventRole?.value ?? "click",
36322
+ event: eventNameOf(eventRole),
35489
36323
  modifiers: n.eventModifiers
35490
36324
  };
35491
36325
  return bodyJSON;
@@ -35495,13 +36329,17 @@ function nodeToSemanticJSON(node) {
35495
36329
  action: n.action,
35496
36330
  roles,
35497
36331
  trigger: {
35498
- event: eventRole?.value ?? "click",
36332
+ event: eventNameOf(eventRole),
35499
36333
  modifiers: n.eventModifiers
35500
36334
  }
35501
36335
  };
35502
36336
  }
35503
36337
  return { action: n.action, roles };
35504
36338
  }
36339
+ function eventNameOf(eventRole) {
36340
+ const name = eventRole?.value ?? eventRole?.raw;
36341
+ return typeof name === "string" && name.length > 0 ? name : "click";
36342
+ }
35505
36343
 
35506
36344
  // src/input/json-schema.ts
35507
36345
  var import_ir = require("@lokascript/framework/ir");