@lokascript/compilation-service 3.1.1 → 3.3.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/http.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")) {
@@ -2213,6 +2349,9 @@ function parseToggleCommand(ctx, identifierNode) {
2213
2349
  if (classArg && mode !== void 0 && classArg.expression) {
2214
2350
  args.push(classArg.expression);
2215
2351
  modifiers["as"] = literalModifier(mode);
2352
+ } else if (classArg?.type === "binaryExpression" && classArg.operator === "of" && classArg.left?.type === "selector" && typeof classArg.left.value === "string" && classArg.left.value.startsWith("*") && classArg.right) {
2353
+ args.push(classArg.left);
2354
+ modifiers["on"] = classArg.right;
2216
2355
  } else if (classArg) {
2217
2356
  args.push(classArg);
2218
2357
  }
@@ -2354,11 +2493,15 @@ function parseSwapCommand(ctx, identifierNode) {
2354
2493
  const targetExpr = isDelete ? ctx.parseExpression() : parseOneArgument(ctx, [KEYWORDS.WITH]);
2355
2494
  if (targetExpr)
2356
2495
  args.push(targetExpr);
2357
- if (!isDelete && consumeOptionalKeyword(ctx, KEYWORDS.WITH)) {
2496
+ const isMorph = identifierNode.name === "morph";
2497
+ if (!isDelete && (consumeOptionalKeyword(ctx, KEYWORDS.WITH) || isMorph && consumeOptionalKeyword(ctx, KEYWORDS.TO))) {
2358
2498
  const contentExpr = parseOneArgument(ctx, ["using"]);
2359
2499
  if (contentExpr)
2360
2500
  modifiers["with"] = contentExpr;
2361
2501
  }
2502
+ if (isMorph && modifiers["with"] === void 0) {
2503
+ ctx.addError("Expected 'to' and the content to morph into");
2504
+ }
2362
2505
  return CommandNodeBuilder.fromIdentifier(identifierNode).withArgs(...args).withModifiers({ ...modifiers, ...parseViewTransitionTail(ctx) }).endingAt(ctx.getPosition()).build();
2363
2506
  }
2364
2507
  function parseShowHideCommand(ctx, identifierNode) {
@@ -3033,6 +3176,8 @@ function atArgumentBoundary(ctx, grammar) {
3033
3176
  const next = ctx.peek();
3034
3177
  if (ctx.checkIsCommand() && ctx.peekAt(1)?.value === "(")
3035
3178
  return false;
3179
+ if (isGluedBeepBang(next, ctx.peekAt(1)))
3180
+ return false;
3036
3181
  if (isCommandBoundary(ctx, ["catch", "finally", "on"]))
3037
3182
  return true;
3038
3183
  return isKeyword(next, [...grammar.markers]);
@@ -3064,10 +3209,11 @@ function parseDeclaredCommand(ctx, commandToken, name, grammar) {
3064
3209
  }
3065
3210
  }
3066
3211
  const commaList = new Set(grammar.commaList ?? []);
3212
+ const namedArgs = new Set(grammar.namedArgs ?? []);
3067
3213
  while (!ctx.isAtEnd() && isKeyword(ctx.peek(), [...grammar.markers])) {
3068
3214
  const keyword = ctx.advance().value;
3069
3215
  const key = ctx.resolveKeyword(keyword).toLowerCase();
3070
- let value = ctx.parseExpression();
3216
+ let value = namedArgs.has(key) && isNakedNamedArgStart(ctx) ? parseNakedNamedArgs(ctx, () => ctx.parseExpression(), name) : ctx.parseExpression();
3071
3217
  if (value && commaList.has(key) && ctx.check(",")) {
3072
3218
  const elements = [value];
3073
3219
  while (ctx.match(",")) {
@@ -3193,8 +3339,8 @@ function parseFetchCommand(ctx, commandToken) {
3193
3339
  }
3194
3340
  if (ctx.check("with") && !modifiers["with"]) {
3195
3341
  ctx.advance();
3196
- if (isFetchNakedNamedArgStart(ctx)) {
3197
- modifiers["with"] = parseFetchNakedNamedArgs(ctx);
3342
+ if (isNakedNamedArgStart(ctx)) {
3343
+ modifiers["with"] = parseNakedNamedArgs(ctx, () => ctx.parsePrimary(), "fetch");
3198
3344
  } else {
3199
3345
  modifiers["with"] = ctx.parsePrimary();
3200
3346
  }
@@ -3226,45 +3372,6 @@ function parseFetchCommand(ctx, commandToken) {
3226
3372
  }
3227
3373
  return builder.build();
3228
3374
  }
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
3375
  function findJsEndBoundary(ctx, startPos) {
3269
3376
  const input = ctx.getInputSlice(startPos);
3270
3377
  if (!input) {
@@ -3378,6 +3485,7 @@ function parseTellCommand(ctx, identifierNode) {
3378
3485
  const commands = [];
3379
3486
  while (!ctx.isAtEnd()) {
3380
3487
  if (ctx.checkIsCommand()) {
3488
+ const word = ctx.peek().value;
3381
3489
  try {
3382
3490
  ctx.advance();
3383
3491
  const cmd = ctx.parseCommand();
@@ -3386,7 +3494,8 @@ function parseTellCommand(ctx, identifierNode) {
3386
3494
  } else {
3387
3495
  break;
3388
3496
  }
3389
- } catch {
3497
+ } catch (error) {
3498
+ ctx.addError(`'${word}' in a tell body failed to parse: ${error instanceof Error ? error.message : String(error)}`);
3390
3499
  break;
3391
3500
  }
3392
3501
  if (ctx.match(KEYWORDS.AND) || ctx.match(KEYWORDS.THEN)) {
@@ -3606,6 +3715,84 @@ function isNodeList(value) {
3606
3715
  function isDocumentFragment(value) {
3607
3716
  return value !== null && typeof value === "object" && "nodeType" in value && value.nodeType === 11 && "childNodes" in value;
3608
3717
  }
3718
+ function beepValues(me, values) {
3719
+ const outputs = values.map(describeBeepValue);
3720
+ const shown = outputs.filter((output) => announce(me, output.value));
3721
+ if (shown.length === 0)
3722
+ return outputs;
3723
+ console.group("\u{1F514} beep! Debug Output");
3724
+ for (const output of shown) {
3725
+ console.log(`Value:`, output.value);
3726
+ console.log(`Type:`, output.type);
3727
+ console.log(`Representation:`, output.representation);
3728
+ console.log("---");
3729
+ }
3730
+ console.groupEnd();
3731
+ return outputs;
3732
+ }
3733
+ function announce(me, value) {
3734
+ const target = me;
3735
+ if (typeof CustomEvent !== "function" || typeof target?.dispatchEvent !== "function")
3736
+ return true;
3737
+ return target.dispatchEvent(new CustomEvent("hyperscript:beep", {
3738
+ bubbles: true,
3739
+ cancelable: true,
3740
+ detail: { element: me, value }
3741
+ }));
3742
+ }
3743
+ function describeBeepValue(value) {
3744
+ return { value, type: typeOf(value), representation: representationOf(value) };
3745
+ }
3746
+ function typeOf(value) {
3747
+ if (value === null)
3748
+ return "null";
3749
+ if (value === void 0)
3750
+ return "undefined";
3751
+ if (Array.isArray(value))
3752
+ return "array";
3753
+ if (isHTMLElement$2(value))
3754
+ return "HTMLElement";
3755
+ if (typeof Element !== "undefined" && value instanceof Element)
3756
+ return "Element";
3757
+ if (typeof Node !== "undefined" && value instanceof Node)
3758
+ return "Node";
3759
+ if (value instanceof Error)
3760
+ return "Error";
3761
+ if (value instanceof Date)
3762
+ return "Date";
3763
+ if (value instanceof RegExp)
3764
+ return "RegExp";
3765
+ return typeof value;
3766
+ }
3767
+ function representationOf(value) {
3768
+ if (value === null)
3769
+ return "null";
3770
+ if (value === void 0)
3771
+ return "undefined";
3772
+ if (Array.isArray(value)) {
3773
+ return `Array(${value.length}) [${value.slice(0, 3).map((v2) => representationOf(v2)).join(", ")}${value.length > 3 ? "..." : ""}]`;
3774
+ }
3775
+ if (isHTMLElement$2(value)) {
3776
+ const el = value;
3777
+ const tag = el.tagName.toLowerCase();
3778
+ const id = el.id ? `#${el.id}` : "";
3779
+ const classes = el.className ? `.${el.className.split(" ").join(".")}` : "";
3780
+ return `<${tag}${id}${classes}/>`;
3781
+ }
3782
+ if (value instanceof Error)
3783
+ return `Error: ${value.message}`;
3784
+ if (typeof value === "string")
3785
+ return value.length > 50 ? `"${value.substring(0, 47)}..."` : `"${value}"`;
3786
+ if (typeof value === "object") {
3787
+ try {
3788
+ const keys = Object.keys(value);
3789
+ return `Object {${keys.slice(0, 3).join(", ")}${keys.length > 3 ? "..." : ""}}`;
3790
+ } catch {
3791
+ return "[Object]";
3792
+ }
3793
+ }
3794
+ return String(value);
3795
+ }
3609
3796
  function convertToNumber(value) {
3610
3797
  if (value === null || value === void 0) {
3611
3798
  return 0;
@@ -3637,6 +3824,16 @@ function convertToNumber(value) {
3637
3824
  }
3638
3825
  return 0;
3639
3826
  }
3827
+ function counterAmount(value) {
3828
+ return typeof value === "number" ? value : parseFloat(String(value));
3829
+ }
3830
+ function counterValue(value) {
3831
+ if (isHTMLElement$2(value)) {
3832
+ const element = value;
3833
+ return counterValue(element.value || element.textContent);
3834
+ }
3835
+ return value ? counterAmount(value) : 0;
3836
+ }
3640
3837
  function getVariableValue(name, context, preferredScope) {
3641
3838
  if (preferredScope === "element") {
3642
3839
  return getElementVar(context, name);
@@ -3738,32 +3935,36 @@ function setElementPropertyValue(propertyPath, value, context) {
3738
3935
  element[property] = value;
3739
3936
  }
3740
3937
  }
3938
+ function isObjectOwner(value) {
3939
+ return typeof value === "object" && value !== null && !Array.isArray(value) && !isHTMLElement$2(value);
3940
+ }
3741
3941
  function getCurrentNumericValue(target, property, scope, context) {
3742
3942
  if (typeof target === "number") {
3743
3943
  return target;
3744
3944
  }
3745
3945
  if (isHTMLElement$2(target)) {
3746
3946
  const element = target;
3947
+ if (property?.startsWith("@"))
3948
+ return counterValue(element.getAttribute(property.slice(1)));
3747
3949
  if (property) {
3748
3950
  if (property.startsWith("data-") || ["id", "class", "title", "alt", "src", "href"].includes(property)) {
3749
- const value = element.getAttribute(property);
3750
- return convertToNumber(value);
3951
+ return counterValue(element.getAttribute(property));
3751
3952
  } else {
3752
- const value = element[property];
3753
- return convertToNumber(value);
3953
+ return counterValue(element[property]);
3754
3954
  }
3755
3955
  } else {
3756
- const value = element.value || element.textContent;
3757
- return convertToNumber(value);
3956
+ return counterValue(element);
3758
3957
  }
3759
3958
  }
3959
+ if (property && isObjectOwner(target)) {
3960
+ return counterValue(target[property]);
3961
+ }
3760
3962
  if (typeof target === "string") {
3761
3963
  if (scope === "element") {
3762
- return convertToNumber(getVariableValue(target, context, "element"));
3964
+ return counterValue(getVariableValue(target, context, "element"));
3763
3965
  }
3764
3966
  if (scope === "global") {
3765
- const value2 = getVariableValue(target, context, "global");
3766
- return convertToNumber(value2);
3967
+ return counterValue(getVariableValue(target, context, "global"));
3767
3968
  }
3768
3969
  if (target.includes(".")) {
3769
3970
  return getElementPropertyValue(target, context);
@@ -3775,14 +3976,17 @@ function getCurrentNumericValue(target, property, scope, context) {
3775
3976
  } else if (target === "you" && context.you) {
3776
3977
  return convertToNumber(context.you.value || 0);
3777
3978
  }
3778
- const value = getVariableValue(target, context);
3779
- return convertToNumber(value);
3979
+ return counterValue(getVariableValue(target, context));
3780
3980
  }
3781
- return convertToNumber(target);
3981
+ return counterValue(target);
3782
3982
  }
3783
3983
  function setTargetValue(target, property, scope, newValue, context) {
3784
3984
  if (isHTMLElement$2(target)) {
3785
3985
  const element = target;
3986
+ if (property?.startsWith("@")) {
3987
+ element.setAttribute(property.slice(1), String(newValue));
3988
+ return;
3989
+ }
3786
3990
  if (property) {
3787
3991
  if (property.startsWith("data-") || ["id", "class", "title", "alt", "src", "href"].includes(property)) {
3788
3992
  element.setAttribute(property, String(newValue));
@@ -3798,6 +4002,10 @@ function setTargetValue(target, property, scope, newValue, context) {
3798
4002
  }
3799
4003
  return;
3800
4004
  }
4005
+ if (property && isObjectOwner(target)) {
4006
+ target[property] = newValue;
4007
+ return;
4008
+ }
3801
4009
  if (typeof target === "string") {
3802
4010
  if (scope === "element") {
3803
4011
  setVariableValue(target, newValue, context, "element");
@@ -3886,13 +4094,6 @@ function isFunction(value) {
3886
4094
  function isInputElement$1(el) {
3887
4095
  return el !== null && typeof el === "object" && "tagName" in el && el.tagName === "INPUT";
3888
4096
  }
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
4097
  function isHTMLElement$1(el) {
3897
4098
  return el !== null && typeof el === "object" && "nodeType" in el && el.nodeType === 1 && "style" in el;
3898
4099
  }
@@ -5208,11 +5409,12 @@ async function evaluateBinaryExpression(node, context) {
5208
5409
  }
5209
5410
  }
5210
5411
  }
5211
- if (leftNode?.type === "selector" && leftNode.fromQuery && typeof leftNode.value === "string") {
5412
+ if (leftNode?.type === "selector" && typeof leftNode.value === "string" && (leftNode.fromQuery || operator === "in" && leftNode.value.startsWith("."))) {
5212
5413
  const root = await evaluateAST(node.right, context);
5213
5414
  const scope = root && typeof root.querySelectorAll === "function" ? root : null;
5214
5415
  if (scope) {
5215
- return Array.from(scope.querySelectorAll(leftNode.value));
5416
+ const query = leftNode.fromQuery ? leftNode.value : escapeSelectorForQuery(leftNode, leftNode.value);
5417
+ return Array.from(scope.querySelectorAll(query));
5216
5418
  }
5217
5419
  }
5218
5420
  }
@@ -5221,6 +5423,12 @@ async function evaluateBinaryExpression(node, context) {
5221
5423
  return getExpr(context, "styleRef").evaluate(context, leftNode.value.slice(1), el);
5222
5424
  }
5223
5425
  if (operator === "of") {
5426
+ if (leftNode?.type === "attributeAccess") {
5427
+ const target = await evaluateAST(node.right, context);
5428
+ if (target == null)
5429
+ return void 0;
5430
+ return getExpr(context, "possessive").evaluate(context, target, `@${leftNode.attributeName}`);
5431
+ }
5224
5432
  const path = propertyPathOf(node.left);
5225
5433
  if (path) {
5226
5434
  const target = await evaluateAST(node.right, context);
@@ -5228,6 +5436,10 @@ async function evaluateBinaryExpression(node, context) {
5228
5436
  for (const seg of path) {
5229
5437
  if (cur == null)
5230
5438
  return void 0;
5439
+ if (isMappableCollection(cur) && !(seg in cur)) {
5440
+ cur = await getExpr(context, "possessive").evaluate(context, cur, seg);
5441
+ continue;
5442
+ }
5231
5443
  cur = isElement(cur) ? getElementProperty$1(cur, seg) : cur[seg];
5232
5444
  }
5233
5445
  return typeof cur === "function" ? cur.bind(target) : cur;
@@ -5252,26 +5464,21 @@ async function evaluateBinaryExpression(node, context) {
5252
5464
  const R = node.ignoringCase ? applyCI(right) : right;
5253
5465
  switch (operator) {
5254
5466
  case "+": {
5255
- let addLeft = left;
5256
- let addRight = right;
5257
5467
  if (node.coerceNumeric) {
5258
- if (typeof addLeft === "string")
5259
- addLeft = convertToNumber(addLeft);
5260
- if (typeof addRight === "string")
5261
- addRight = convertToNumber(addRight);
5468
+ return counterValue(left) + counterAmount(right);
5262
5469
  }
5263
- if (typeof addLeft === "string" || typeof addRight === "string") {
5264
- return String(addLeft ?? "") + String(addRight ?? "");
5470
+ if (typeof left === "string" || typeof right === "string") {
5471
+ return String(left ?? "") + String(right ?? "");
5265
5472
  }
5266
- if (Array.isArray(addLeft)) {
5267
- return Array.isArray(addRight) ? [...addLeft, ...addRight] : [...addLeft, addRight];
5473
+ if (Array.isArray(left)) {
5474
+ return Array.isArray(right) ? [...left, ...right] : [...left, right];
5268
5475
  }
5269
- return unwrapTypedResult(await getExpr(context, "addition").evaluate(context, {
5270
- left: addLeft,
5271
- right: addRight
5272
- }));
5476
+ return unwrapTypedResult(await getExpr(context, "addition").evaluate(context, { left, right }));
5273
5477
  }
5274
5478
  case "-":
5479
+ if (node.coerceNumeric) {
5480
+ return counterValue(left) - counterAmount(right);
5481
+ }
5275
5482
  return unwrapTypedResult(await getExpr(context, "subtraction").evaluate(context, { left, right }));
5276
5483
  case "*":
5277
5484
  return unwrapTypedResult(await getExpr(context, "multiplication").evaluate(context, { left, right }));
@@ -5516,15 +5723,14 @@ async function evaluateAttributeAccessNode(node, context) {
5516
5723
  return `@${attributeName}`;
5517
5724
  }
5518
5725
  async function evaluatePropertyOfExpressionNode(node, context) {
5519
- const propertyNode = node.property;
5520
- if (propertyNode?.type !== "identifier") {
5726
+ const propertyNode2 = node.property;
5727
+ if (propertyNode2?.type !== "identifier") {
5521
5728
  throw new Error('Property name must be an identifier in "the X of Y" pattern');
5522
5729
  }
5523
- const propertyName = propertyNode.name;
5730
+ const propertyName = propertyNode2.name;
5524
5731
  const target = await evaluateAST(node.target, context);
5525
- if (target == null) {
5526
- throw new Error(`Cannot access property "${propertyName}" of ${target}`);
5527
- }
5732
+ if (target == null)
5733
+ return void 0;
5528
5734
  return getExpr(context, "possessive").evaluate(context, target, propertyName);
5529
5735
  }
5530
5736
  async function evaluateTemplateLiteralNode(node, context) {
@@ -5640,6 +5846,9 @@ async function evaluateUnaryExpression(node, context) {
5640
5846
  case "not":
5641
5847
  case "!":
5642
5848
  return logicalExpressions.not.evaluate(context, value);
5849
+ case "beep!":
5850
+ beepValues(context.me, [value]);
5851
+ return value;
5643
5852
  case "no":
5644
5853
  return logicalExpressions.no.evaluate(context, value);
5645
5854
  case "-":
@@ -5727,6 +5936,10 @@ async function evaluateCallExpression(node, context) {
5727
5936
  const args = await resolveCallArgs(node.arguments, funcName, context);
5728
5937
  switch (funcName) {
5729
5938
  case "closest":
5939
+ if (node.closestTo) {
5940
+ const start = unwrapElement(await evaluateAST(node.closestTo, context)) ?? null;
5941
+ return referencesExpressions.closest.evaluate(context, args[0], start);
5942
+ }
5730
5943
  return referencesExpressions.closest.evaluate(context, ...args);
5731
5944
  case "previous":
5732
5945
  case "next": {
@@ -6452,6 +6665,73 @@ function parseAttributeWithValue(expression) {
6452
6665
  const result = parseAttribute(expression);
6453
6666
  return { name: result.name, value: result.value ?? "" };
6454
6667
  }
6668
+ function _parseDurationComponents(value) {
6669
+ const trimmed = value.trim();
6670
+ const match = trimmed.match(/^(\d+(?:\.\d+)?)\s*(s|ms|sec|seconds?|milliseconds?)?$/i);
6671
+ if (!match) {
6672
+ return null;
6673
+ }
6674
+ const number = parseFloat(match[1]);
6675
+ const unit = (match[2] || "ms").toLowerCase();
6676
+ if (unit === "ms" || unit === "millisecond" || unit === "milliseconds") {
6677
+ return { number, milliseconds: Math.floor(number) };
6678
+ } else if (unit === "s" || unit === "sec" || unit === "second" || unit === "seconds") {
6679
+ return { number, milliseconds: Math.floor(number * 1e3) };
6680
+ }
6681
+ return null;
6682
+ }
6683
+ function parseDuration(value, defaultMs = 300) {
6684
+ if (typeof value === "number") {
6685
+ return Math.max(0, Math.floor(value));
6686
+ }
6687
+ if (typeof value === "string") {
6688
+ const result = _parseDurationComponents(value);
6689
+ return result ? result.milliseconds : defaultMs;
6690
+ }
6691
+ return defaultMs;
6692
+ }
6693
+ function parseDurationStrict(value) {
6694
+ if (typeof value === "number") {
6695
+ if (value < 0)
6696
+ throw new Error("Duration must be >= 0");
6697
+ return Math.floor(value);
6698
+ }
6699
+ if (typeof value === "string") {
6700
+ const result = _parseDurationComponents(value);
6701
+ if (!result) {
6702
+ throw new Error(`Invalid duration format: "${value}"`);
6703
+ }
6704
+ return result.milliseconds;
6705
+ }
6706
+ throw new Error(`Invalid duration type: ${typeof value}`);
6707
+ }
6708
+ function parseCSSDurations(durationString) {
6709
+ if (!durationString || durationString === "none") {
6710
+ return [0];
6711
+ }
6712
+ return durationString.split(",").map((duration) => {
6713
+ const value = parseFloat(duration.trim());
6714
+ if (isNaN(value))
6715
+ return 0;
6716
+ if (duration.includes("s") && !duration.includes("ms")) {
6717
+ return value * 1e3;
6718
+ }
6719
+ return value;
6720
+ });
6721
+ }
6722
+ function calculateMaxAnimationTime(durations, delays) {
6723
+ let maxTime = 0;
6724
+ for (let i = 0; i < durations.length; i++) {
6725
+ const duration = durations[i] || 0;
6726
+ const delay = delays[i] || 0;
6727
+ const totalTime = duration + delay;
6728
+ maxTime = Math.max(maxTime, totalTime);
6729
+ }
6730
+ return maxTime;
6731
+ }
6732
+ function camelToKebab(str) {
6733
+ return str.replace(/([A-Z])/g, "-$1").toLowerCase();
6734
+ }
6455
6735
  function batchApply(targets, operation) {
6456
6736
  for (const element of targets) {
6457
6737
  operation(element);
@@ -6514,15 +6794,18 @@ function toggleAttribute(element, name, value) {
6514
6794
  }
6515
6795
  }
6516
6796
  }
6797
+ function toCssPropertyName(property) {
6798
+ return property.startsWith("--") ? property : camelToKebab(property);
6799
+ }
6517
6800
  function batchSetStyles(targets, styles) {
6518
6801
  const entries = Object.entries(styles);
6519
6802
  return batchApplyItems(targets, entries, (element, [property, value]) => {
6520
- element.style.setProperty(property, value);
6803
+ element.style.setProperty(toCssPropertyName(property), value);
6521
6804
  });
6522
6805
  }
6523
6806
  function batchRemoveStyles(targets, properties) {
6524
6807
  return batchApplyItems(targets, properties, (element, property) => {
6525
- element.style.removeProperty(property);
6808
+ element.style.removeProperty(toCssPropertyName(property));
6526
6809
  });
6527
6810
  }
6528
6811
  function parseClasses(classValue) {
@@ -6870,73 +7153,6 @@ function removeElement(element) {
6870
7153
  }
6871
7154
  return false;
6872
7155
  }
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
7156
  function detectSmartElementType(elements) {
6941
7157
  if (elements.length === 0)
6942
7158
  return null;
@@ -7221,13 +7437,13 @@ async function resolveAnyPropertyTarget(node, evaluator, context) {
7221
7437
  const anyNode = node;
7222
7438
  if (anyNode?.type === "possessiveExpression" || anyNode?.type === "memberExpression") {
7223
7439
  const objectNode = anyNode.object;
7224
- const propertyNode = anyNode.property;
7440
+ const propertyNode2 = anyNode.property;
7225
7441
  let element = await evaluator.evaluate(objectNode, context);
7226
7442
  if (Array.isArray(element))
7227
7443
  element = element[0];
7228
7444
  if (!isHTMLElement$2(element))
7229
7445
  return null;
7230
- const propertyName = propertyNode?.name || propertyNode?.value;
7446
+ const propertyName = propertyNode2?.name || propertyNode2?.value;
7231
7447
  if (!propertyName)
7232
7448
  return null;
7233
7449
  return { element, property: propertyName };
@@ -7323,6 +7539,19 @@ function detectExpressionType(firstValue, firstArg) {
7323
7539
  }
7324
7540
  return { type: "class", expression: "" };
7325
7541
  }
7542
+ function arrayFragment(items) {
7543
+ const fragment = document.createDocumentFragment();
7544
+ for (const item of items) {
7545
+ if (isInsertableNode(item)) {
7546
+ fragment.append(item);
7547
+ continue;
7548
+ }
7549
+ const template = document.createElement("template");
7550
+ template.innerHTML = item === null ? "" : String(item);
7551
+ fragment.append(template.content);
7552
+ }
7553
+ return fragment;
7554
+ }
7326
7555
  function escapeHtml(value) {
7327
7556
  return value.replace(/[&<>"']/g, (c) => HTML_ESCAPE[c]);
7328
7557
  }
@@ -7653,10 +7882,28 @@ function withViewTransition(callback, options) {
7653
7882
  drain().catch(reject);
7654
7883
  });
7655
7884
  }
7885
+ function loneRootWithTag(content, tagName) {
7886
+ let root;
7887
+ if (typeof content === "string") {
7888
+ const template = document.createElement("template");
7889
+ template.innerHTML = content;
7890
+ root = template.content.firstElementChild;
7891
+ if (!root || root.nextElementSibling)
7892
+ return null;
7893
+ } else {
7894
+ root = content.cloneNode(true);
7895
+ }
7896
+ return root.tagName === tagName && isHTMLElement$2(root) ? root : null;
7897
+ }
7656
7898
  function executeSwap(target, content, strategy, morphOptions) {
7657
7899
  const contentStr = content !== null && !isHTMLElement$2(content) ? content : "";
7658
7900
  const contentEl = isHTMLElement$2(content) ? content : null;
7659
7901
  switch (strategy) {
7902
+ case "morphMerge": {
7903
+ const root = content !== null ? loneRootWithTag(content, target.tagName) : null;
7904
+ executeSwap(target, root ?? content, root ? "morphOuter" : "morph", morphOptions);
7905
+ break;
7906
+ }
7660
7907
  case "morph":
7661
7908
  if (content !== null) {
7662
7909
  try {
@@ -8415,6 +8662,19 @@ async function resolveWriteTarget(node, evaluator, context, options) {
8415
8662
  }
8416
8663
  return { kind: "property", target: property };
8417
8664
  }
8665
+ if (options.ofAndObjectProperty) {
8666
+ const access = ofOrPossessiveAccess(n);
8667
+ if (access) {
8668
+ const owner = await evaluator.evaluate(access.owner, context);
8669
+ const element = Array.isArray(owner) ? owner[0] : owner;
8670
+ if (access.isOf && isHTMLElement$2(element)) {
8671
+ return { kind: "property", target: { element, property: access.name } };
8672
+ }
8673
+ if (isObjectOwner(owner)) {
8674
+ return { kind: "member", container: owner, property: access.name };
8675
+ }
8676
+ }
8677
+ }
8418
8678
  if (options.bareReference && (nodeType === "identifier" || nodeType === "variable" || nodeType === "symbol") && typeof n.name === "string") {
8419
8679
  const rawScope = n.scope;
8420
8680
  const scope = rawScope === "element" || rawScope === "global" ? rawScope : void 0;
@@ -8422,6 +8682,13 @@ async function resolveWriteTarget(node, evaluator, context, options) {
8422
8682
  }
8423
8683
  return null;
8424
8684
  }
8685
+ function ofOrPossessiveAccess(n) {
8686
+ 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];
8687
+ const name = property?.name;
8688
+ if (!owner || typeof name !== "string" || !/^[A-Za-z_$][\w$]*$/.test(name))
8689
+ return null;
8690
+ return { owner, name, isOf };
8691
+ }
8425
8692
  function toSetInput(target, value) {
8426
8693
  switch (target.kind) {
8427
8694
  case "node-write":
@@ -8442,6 +8709,13 @@ function toSetInput(target, value) {
8442
8709
  property: target.target.property,
8443
8710
  value
8444
8711
  };
8712
+ case "member":
8713
+ return {
8714
+ type: "member-assignment",
8715
+ container: target.container,
8716
+ property: target.property,
8717
+ value
8718
+ };
8445
8719
  default:
8446
8720
  throw new Error(`set: unrequested write-target rung '${target.kind}'`);
8447
8721
  }
@@ -8451,12 +8725,24 @@ function getNodeType(node) {
8451
8725
  return "unknown";
8452
8726
  return node.type || "unknown";
8453
8727
  }
8728
+ function propertyTarget(node) {
8729
+ const n = node;
8730
+ const owner = n.type === "propertyOfExpression" ? n.target : n.type === "possessiveExpression" || n.type === "memberExpression" || n.type === "propertyAccess" ? n.object : void 0;
8731
+ if (!owner || n.computed)
8732
+ return null;
8733
+ const p = n.property;
8734
+ const name = typeof p === "string" ? p : typeof p?.name === "string" ? p.name : void 0;
8735
+ if (!name || !/^[A-Za-z_$][\w$-]*$/.test(name))
8736
+ return null;
8737
+ return { owner, name };
8738
+ }
8454
8739
  async function parseNumericTargetInput(raw, evaluator, context, commandName) {
8455
8740
  if (!raw.args || raw.args.length === 0) {
8456
8741
  throw new Error(`${commandName} command requires a target`);
8457
8742
  }
8458
8743
  const targetArg = raw.args[0];
8459
8744
  let target;
8745
+ let property;
8460
8746
  let extractedScope;
8461
8747
  let customWrite;
8462
8748
  const nodeType = getNodeType(targetArg);
@@ -8469,7 +8755,20 @@ async function parseNumericTargetInput(raw, evaluator, context, commandName) {
8469
8755
  target = targetArg.value;
8470
8756
  } else {
8471
8757
  const writer = getRegisteredNodeWriter(nodeType);
8472
- if (writer) {
8758
+ const attribute = writer ? null : await resolveAttributeWriteTarget(targetArg, evaluator, context, async () => context.me ? [context.me] : []);
8759
+ const prop = writer || attribute ? null : propertyTarget(targetArg);
8760
+ const owner = prop ? await evaluator.evaluate(prop.owner, context) : void 0;
8761
+ const ownerElement = Array.isArray(owner) ? owner[0] : owner;
8762
+ if (attribute?.elements[0]) {
8763
+ target = attribute.elements[0];
8764
+ property = `@${attribute.name}`;
8765
+ } else if (prop && isHTMLElement$2(ownerElement)) {
8766
+ target = ownerElement;
8767
+ property = prop.name;
8768
+ } else if (prop && isObjectOwner(owner)) {
8769
+ target = owner;
8770
+ property = prop.name;
8771
+ } else if (writer) {
8473
8772
  const currentRaw = await evaluator.evaluate(targetArg, context);
8474
8773
  const currentValue = typeof currentRaw === "number" ? currentRaw : currentRaw == null ? 0 : Number(currentRaw) || 0;
8475
8774
  target = currentValue;
@@ -8498,24 +8797,20 @@ async function parseNumericTargetInput(raw, evaluator, context, commandName) {
8498
8797
  const evaluated = await evaluator.evaluate(arg, context);
8499
8798
  if (typeof evaluated === "number") {
8500
8799
  amount = evaluated;
8800
+ } else if (evaluated == null) {
8801
+ amount = NaN;
8501
8802
  }
8502
8803
  }
8503
8804
  }
8504
8805
  const byModifier = raw.modifiers?.by;
8505
8806
  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
- }
8807
+ const value = getNodeType(byModifier) === "literal" ? byModifier.value : await evaluator.evaluate(byModifier, context);
8808
+ amount = counterAmount(value);
8515
8809
  }
8516
8810
  return {
8517
8811
  target,
8518
8812
  amount,
8813
+ ...property && { property },
8519
8814
  ...scope && { scope },
8520
8815
  ...customWrite && { customWrite }
8521
8816
  };
@@ -8722,7 +9017,10 @@ function createUntilLoopConfig(conditionExpr, evaluateCondition2, context, index
8722
9017
  };
8723
9018
  const config2 = {
8724
9019
  type: "until",
8725
- shouldContinue: () => !evaluateCondition2(conditionExpr, context)
9020
+ shouldContinue: () => {
9021
+ const met = evaluateCondition2(conditionExpr, context);
9022
+ return met instanceof Promise ? met.then((value) => !value) : !met;
9023
+ }
8726
9024
  };
8727
9025
  return { config: config2, iterCtx };
8728
9026
  }
@@ -8757,7 +9055,7 @@ function createForeverLoopConfig(indexVariable, maxIterations = 1e4) {
8757
9055
  };
8758
9056
  return { config: config2, iterCtx };
8759
9057
  }
8760
- async function slotText(node, evaluator, context) {
9058
+ async function slotText$1(node, evaluator, context) {
8761
9059
  if (!node)
8762
9060
  return void 0;
8763
9061
  const n = node;
@@ -8770,6 +9068,18 @@ async function slotText(node, evaluator, context) {
8770
9068
  const v2 = await evaluator.evaluate(node, context);
8771
9069
  return typeof v2 === "string" ? v2 : void 0;
8772
9070
  }
9071
+ function isEventTarget(value) {
9072
+ return typeof value?.addEventListener === "function";
9073
+ }
9074
+ async function evaluateLoopCondition(condition, context) {
9075
+ if (typeof condition === "function")
9076
+ return Boolean(await condition());
9077
+ return evaluateCondition(condition, context);
9078
+ }
9079
+ function loopCount(value) {
9080
+ const count = Number(value);
9081
+ return Number.isNaN(count) ? 0 : count;
9082
+ }
8773
9083
  function createPseudoCommand() {
8774
9084
  return new PseudoCommand();
8775
9085
  }
@@ -8805,6 +9115,28 @@ function namedPropertyOf(node) {
8805
9115
  }
8806
9116
  return null;
8807
9117
  }
9118
+ function elementsOf(value) {
9119
+ if (Array.isArray(value) || isNodeList(value)) {
9120
+ return Array.from(value).filter(isHTMLElement$2);
9121
+ }
9122
+ return isHTMLElement$2(value) ? [value] : [];
9123
+ }
9124
+ async function valueOf(node, evaluator, context) {
9125
+ const value = await evaluator.evaluate(node, context);
9126
+ const name = node.name;
9127
+ if (value === void 0 && node.type === "identifier" && typeof name === "string")
9128
+ return name;
9129
+ return value;
9130
+ }
9131
+ function longestTransitionMs(css) {
9132
+ let longest;
9133
+ for (const item of css.split(",")) {
9134
+ const times = [...item.matchAll(/(\d*\.?\d+)(ms|s)\b/g)].map((m) => parseFloat(m[1]) * (m[2] === "s" ? 1e3 : 1));
9135
+ if (times.length > 0)
9136
+ longest = Math.max(longest ?? 0, times[0] + (times[1] ?? 0));
9137
+ }
9138
+ return longest;
9139
+ }
8808
9140
  function scopedVariableTarget(node) {
8809
9141
  if (node?.["type"] !== "contextReference")
8810
9142
  return null;
@@ -9603,6 +9935,14 @@ function convertNode(node, infer) {
9603
9935
  ...pos(node)
9604
9936
  };
9605
9937
  case "identifier":
9938
+ if (node.scope === "element" || node.scope === "local" || node.scope === "global") {
9939
+ return {
9940
+ type: "variable",
9941
+ name: node.name ?? "",
9942
+ scope: node.scope,
9943
+ ...pos(node)
9944
+ };
9945
+ }
9606
9946
  return {
9607
9947
  type: "identifier",
9608
9948
  value: node.name ?? node.value ?? "",
@@ -9625,6 +9965,15 @@ function convertNode(node, infer) {
9625
9965
  operand: convertNode(node.argument ?? node.operand, infer),
9626
9966
  ...pos(node)
9627
9967
  };
9968
+ case "typeCheckExpression":
9969
+ return {
9970
+ type: "typeCheck",
9971
+ value: convertNode(node.value, infer),
9972
+ typeName: String(node.typeName),
9973
+ nullOk: node.nullOk !== false,
9974
+ negated: node.negated === true,
9975
+ ...pos(node)
9976
+ };
9628
9977
  case "timeExpression":
9629
9978
  return { type: "literal", value: node.value, ...pos(node) };
9630
9979
  case "templateLiteral":
@@ -9636,6 +9985,28 @@ function convertNode(node, infer) {
9636
9985
  scope: node.scope ?? "local",
9637
9986
  ...pos(node)
9638
9987
  };
9988
+ case "arrayLiteral":
9989
+ return {
9990
+ type: "array",
9991
+ elements: (node.elements ?? []).map((element) => convertNode(element, infer)),
9992
+ ...pos(node)
9993
+ };
9994
+ case "attributeAccess":
9995
+ return {
9996
+ type: "possessive",
9997
+ object: { type: "identifier", value: "me" },
9998
+ property: `@${node.attributeName}`,
9999
+ ...pos(node)
10000
+ };
10001
+ case "propertyOfExpression": {
10002
+ const property = node.property;
10003
+ return {
10004
+ type: "possessive",
10005
+ object: convertNode(node.target, infer),
10006
+ property: property?.name ?? property?.value ?? "",
10007
+ ...pos(node)
10008
+ };
10009
+ }
9639
10010
  case "htmlSelector":
9640
10011
  return {
9641
10012
  type: "selector",
@@ -9686,6 +10057,18 @@ function convertCommand$1(node, infer) {
9686
10057
  if (name === "repeat") {
9687
10058
  return convertRepeatCommand(node, infer);
9688
10059
  }
10060
+ const original = node.originalCommand;
10061
+ const sum = node.modifiers?.to;
10062
+ const counted = node.args?.[0];
10063
+ if (name === "set" && (original === "increment" || original === "decrement") && sum?.type === "binaryExpression" && counted) {
10064
+ return convertCommand$1({
10065
+ ...node,
10066
+ name: original,
10067
+ args: [counted],
10068
+ modifiers: { by: sum.right },
10069
+ originalCommand: void 0
10070
+ }, infer);
10071
+ }
9689
10072
  const args = (node.args ?? []).map((arg) => convertNode(arg, infer));
9690
10073
  const target = node.target ? convertNode(node.target, infer) : void 0;
9691
10074
  const modifiers = node.modifiers ? convertModifiers(node.modifiers, infer) : void 0;
@@ -9727,6 +10110,64 @@ function convertIfCommand(node, infer) {
9727
10110
  };
9728
10111
  }
9729
10112
  function convertRepeatCommand(node, infer) {
10113
+ const slots = node.modifiers ?? {};
10114
+ const form = slotText(slots.loopType);
10115
+ if (form === void 0)
10116
+ return convertPositionalRepeat(node, infer);
10117
+ const [bodyBlock, elseBlock] = node.args ?? [];
10118
+ const body = extractBlockCommands(bodyBlock, infer);
10119
+ const indexName = slotText(slots.index);
10120
+ const shared = {
10121
+ ...indexName ? { indexName } : {},
10122
+ ...elseBlock ? { elseBody: extractBlockCommands(elseBlock, infer) } : {},
10123
+ ...pos(node)
10124
+ };
10125
+ const operand = (slot) => slot ? convertNode(slot, infer) : void 0;
10126
+ switch (form) {
10127
+ case "for":
10128
+ return {
10129
+ type: "foreach",
10130
+ itemName: slotText(slots.for) ?? "it",
10131
+ collection: operand(slots.in) ?? { type: "literal", value: null },
10132
+ body,
10133
+ ...shared
10134
+ };
10135
+ case "times":
10136
+ return { type: "repeat", count: operand(slots.times), body, ...shared };
10137
+ case "while":
10138
+ case "until": {
10139
+ const test = operand(slots[form]) ?? { type: "literal", value: form === "until" };
10140
+ return {
10141
+ type: "while",
10142
+ condition: form === "until" ? { type: "unary", operator: "not", operand: test } : test,
10143
+ ...slots.bottomTested?.value === true ? { bottomTested: true } : {},
10144
+ body,
10145
+ ...shared
10146
+ };
10147
+ }
10148
+ case "until-event": {
10149
+ const event = slotText(slots.event);
10150
+ if (!event) {
10151
+ return { type: "error", message: "'repeat until event' has no event name", ...pos(node) };
10152
+ }
10153
+ const target = operand(slots.from);
10154
+ return {
10155
+ type: "repeat",
10156
+ untilEvent: event,
10157
+ ...target ? { untilEventTarget: target } : {},
10158
+ body,
10159
+ ...shared
10160
+ };
10161
+ }
10162
+ default:
10163
+ return { type: "repeat", body, ...shared };
10164
+ }
10165
+ }
10166
+ function slotText(slot) {
10167
+ const text = slot?.value ?? slot?.name;
10168
+ return typeof text === "string" ? text : void 0;
10169
+ }
10170
+ function convertPositionalRepeat(node, infer) {
9730
10171
  const args = node.args ?? [];
9731
10172
  if (args.length === 0) {
9732
10173
  return { type: "repeat", body: [], ...pos(node) };
@@ -9846,13 +10287,13 @@ function buildEventModifiers(node) {
9846
10287
  ...node.selector && !node.from ? { from: node.selector } : {}
9847
10288
  };
9848
10289
  }
9849
- function extractBlockCommands(block, infer) {
9850
- if (!block)
10290
+ function extractBlockCommands(block2, infer) {
10291
+ if (!block2)
9851
10292
  return [];
9852
- if (block.type === "block") {
9853
- return (block.commands ?? []).map((cmd) => convertNode(cmd, infer));
10293
+ if (block2.type === "block") {
10294
+ return (block2.commands ?? []).map((cmd) => convertNode(cmd, infer));
9854
10295
  }
9855
- return [convertNode(block, infer)];
10296
+ return [convertNode(block2, infer)];
9856
10297
  }
9857
10298
  function inferRoles(name, args, modifiers, target, infer) {
9858
10299
  const roles = {};
@@ -10012,6 +10453,15 @@ function toCoreAST(node) {
10012
10453
  prefix: true,
10013
10454
  ...nodePos(node)
10014
10455
  };
10456
+ case "typeCheck":
10457
+ return {
10458
+ type: "typeCheckExpression",
10459
+ value: toCoreAST(node.value),
10460
+ typeName: node.typeName,
10461
+ nullOk: node.nullOk,
10462
+ negated: node.negated,
10463
+ ...nodePos(node)
10464
+ };
10015
10465
  case "member":
10016
10466
  return {
10017
10467
  type: "memberExpression",
@@ -10020,6 +10470,12 @@ function toCoreAST(node) {
10020
10470
  computed: node.computed ?? false,
10021
10471
  ...nodePos(node)
10022
10472
  };
10473
+ case "array":
10474
+ return {
10475
+ type: "arrayLiteral",
10476
+ elements: node.elements.map((element) => toCoreAST(element)),
10477
+ ...nodePos(node)
10478
+ };
10023
10479
  case "possessive":
10024
10480
  return {
10025
10481
  type: "possessiveExpression",
@@ -10079,67 +10535,79 @@ function convertCommand(node) {
10079
10535
  }
10080
10536
  return result;
10081
10537
  }
10538
+ function block(nodes) {
10539
+ return { type: "block", commands: nodes.map((n) => toCoreAST(n)), ...POS };
10540
+ }
10082
10541
  function convertIf(node) {
10083
- const result = {
10542
+ const [elseIf, ...laterElseIfs] = node.elseIfBranches ?? [];
10543
+ const elseBranch = elseIf ? [
10544
+ {
10545
+ type: "if",
10546
+ condition: elseIf.condition,
10547
+ thenBranch: elseIf.body,
10548
+ elseIfBranches: laterElseIfs,
10549
+ ...node.elseBranch ? { elseBranch: node.elseBranch } : {}
10550
+ }
10551
+ ] : node.elseBranch;
10552
+ return {
10084
10553
  type: "command",
10085
10554
  name: "if",
10086
10555
  isBlocking: true,
10087
- condition: toCoreAST(node.condition),
10088
- thenBranch: node.thenBranch.map((n) => toCoreAST(n)),
10089
- args: [],
10556
+ args: [
10557
+ toCoreAST(node.condition),
10558
+ block(node.thenBranch),
10559
+ ...elseBranch ? [block(elseBranch)] : []
10560
+ ],
10090
10561
  ...nodePos(node)
10091
10562
  };
10092
- if (node.elseBranch) {
10093
- result.elseBranch = node.elseBranch.map((n) => toCoreAST(n));
10094
- }
10095
- return result;
10096
10563
  }
10097
- function convertRepeat(node) {
10098
- const result = {
10564
+ function loopCommand(node, form, operands) {
10565
+ const modifiers = { loopType: slotString(form) };
10566
+ for (const [slot, value] of Object.entries(operands)) {
10567
+ if (value)
10568
+ modifiers[slot] = value;
10569
+ }
10570
+ if (node.indexName)
10571
+ modifiers.index = slotString(node.indexName);
10572
+ return {
10099
10573
  type: "command",
10100
10574
  name: "repeat",
10101
10575
  isBlocking: true,
10102
- body: node.body.map((n) => toCoreAST(n)),
10103
- args: [],
10576
+ args: [block(node.body), ...node.elseBody ? [block(node.elseBody)] : []],
10577
+ modifiers,
10104
10578
  ...nodePos(node)
10105
10579
  };
10580
+ }
10581
+ function slotString(value) {
10582
+ return { type: "string", value, ...POS };
10583
+ }
10584
+ function convertRepeat(node) {
10106
10585
  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";
10586
+ const times = typeof node.count === "number" ? { type: "literal", value: node.count, raw: String(node.count), ...POS } : toCoreAST(node.count);
10587
+ return loopCommand(node, "times", { times });
10115
10588
  }
10116
- return result;
10589
+ if (node.whileCondition !== void 0) {
10590
+ return loopCommand(node, "while", { while: toCoreAST(node.whileCondition) });
10591
+ }
10592
+ if (node.untilEvent !== void 0) {
10593
+ return loopCommand(node, "until-event", {
10594
+ event: slotString(node.untilEvent),
10595
+ from: node.untilEventTarget ? toCoreAST(node.untilEventTarget) : void 0
10596
+ });
10597
+ }
10598
+ return loopCommand(node, "forever", {});
10117
10599
  }
10118
10600
  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
- };
10601
+ return loopCommand(node, "for", {
10602
+ for: slotString(node.itemName),
10603
+ in: toCoreAST(node.collection)
10604
+ });
10131
10605
  }
10132
10606
  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
- };
10607
+ return loopCommand(node, "while", {
10608
+ while: toCoreAST(node.condition),
10609
+ bottomTested: node.bottomTested ? { type: "literal", value: true, raw: "true", ...POS } : void 0
10610
+ });
10143
10611
  }
10144
10612
  function flattenEventModifiers(modifiers) {
10145
10613
  if (!modifiers)
@@ -10258,7 +10726,7 @@ async function semanticNodeToRuntimeAST(node) {
10258
10726
  const fw = await getFramework();
10259
10727
  return fw.semanticNodeToRuntimeAST(node);
10260
10728
  }
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;
10729
+ 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
10730
  var init_dist = __esm({
10263
10731
  "../core/dist/index.mjs"() {
10264
10732
  "use strict";
@@ -11339,6 +11807,8 @@ var init_dist = __esm({
11339
11807
  ["!", prefix(80)],
11340
11808
  ["no", prefix(27)]
11341
11809
  ]);
11810
+ OF_OPERAND_BP = 70;
11811
+ THE_OF_TARGET_BP = OF_OPERAND_BP + 1;
11342
11812
  PARSER_COMPARISON_FRAGMENT = /* @__PURE__ */ new Map([
11343
11813
  ["===", leftAssoc(30)],
11344
11814
  ["!==", leftAssoc(30)],
@@ -11383,7 +11853,7 @@ var init_dist = __esm({
11383
11853
  ["is greater than or equal to", leftAssoc(30)],
11384
11854
  ["is less than or equal to", leftAssoc(30)],
11385
11855
  ["in", leftAssoc(30)],
11386
- ["of", rightAssoc(30)],
11856
+ ["of", { infix: { bp: [90, OF_OPERAND_BP], handler: ofHandler } }],
11387
11857
  ["really", leftAssoc(30)],
11388
11858
  [
11389
11859
  "exists",
@@ -11707,7 +12177,7 @@ var init_dist = __esm({
11707
12177
  "/",
11708
12178
  "mod"
11709
12179
  ]);
11710
- CONTEXT_POSSESSIVES = /* @__PURE__ */ new Set(["my", "its", "your"]);
12180
+ CONTEXT_REFS = /* @__PURE__ */ new Set(["me", "it", "you", "result"]);
11711
12181
  SWAP_STRATEGY_KEYWORDS = [
11712
12182
  "afterbegin",
11713
12183
  "afterend",
@@ -11781,7 +12251,6 @@ var init_dist = __esm({
11781
12251
  markers: [],
11782
12252
  syntax
11783
12253
  });
11784
- BEEP = ONE_EXPR("beep! [<expression>, \u2026]");
11785
12254
  COMMAND_GRAMMAR = {
11786
12255
  append: { positional: "expression", markers: ["to"], syntax: "append <value> [to <target>]" },
11787
12256
  prepend: { positional: "expression", markers: ["to"], syntax: "prepend <value> [to <target>]" },
@@ -11792,8 +12261,7 @@ var init_dist = __esm({
11792
12261
  syntax: "make (a|an) <type> [from <args>] [called <name>]"
11793
12262
  },
11794
12263
  throw: { positional: "primary", markers: [], syntax: "throw <error>" },
11795
- "beep!": BEEP,
11796
- beep: BEEP,
12264
+ beep: ONE_EXPR("beep! [<expression>, \u2026]"),
11797
12265
  blur: ONE_EXPR("blur [<target>]"),
11798
12266
  break: NONE,
11799
12267
  breakpoint: NONE,
@@ -11817,7 +12285,8 @@ var init_dist = __esm({
11817
12285
  render: {
11818
12286
  positional: "expression",
11819
12287
  markers: ["with"],
11820
- syntax: "render <template> [with <variables>]"
12288
+ namedArgs: ["with"],
12289
+ syntax: "render <template> [with <name>: <value>, \u2026]"
11821
12290
  },
11822
12291
  reset: ONE_EXPR("reset [<form>]"),
11823
12292
  return: ONE_EXPR("return [<value>]"),
@@ -12138,7 +12607,9 @@ var init_dist = __esm({
12138
12607
  const firstToken = this.peek();
12139
12608
  const prefixEntry = _Parser.PRATT_TABLE.get(firstToken.value);
12140
12609
  let left;
12141
- if (prefixEntry?.prefix) {
12610
+ if (isGluedBeepBang(firstToken, this.tokens[this.current + 1])) {
12611
+ left = this.parseBeepExpression();
12612
+ } else if (prefixEntry?.prefix) {
12142
12613
  const token = this.advance();
12143
12614
  if (this.isAtEnd()) {
12144
12615
  this.addError(`Expected expression after '${token.value}' operator`);
@@ -12157,6 +12628,9 @@ var init_dist = __esm({
12157
12628
  continue;
12158
12629
  }
12159
12630
  }
12631
+ if (nextToken.value === "*" && this.isSpacedStyleRef(this.current)) {
12632
+ break;
12633
+ }
12160
12634
  const infixEntry = _Parser.PRATT_TABLE.get(nextToken.value);
12161
12635
  if (!infixEntry?.infix) {
12162
12636
  if (STOP_TOKENS.has(nextToken.value) || STOP_DELIMITERS.has(nextToken.value)) {
@@ -12210,6 +12684,35 @@ var init_dist = __esm({
12210
12684
  this.prattCache.set(cacheKey, { node: left, endPos: this.current });
12211
12685
  return left;
12212
12686
  }
12687
+ parseBeepExpression() {
12688
+ const beepToken = this.advance();
12689
+ this.advance();
12690
+ if (this.isAtEnd()) {
12691
+ this.addError("Expected expression after 'beep!'");
12692
+ return this.createErrorNode();
12693
+ }
12694
+ const operand = this.parseExpressionPratt(_Parser.BEEP_OPERAND_BP);
12695
+ return {
12696
+ type: "unaryExpression",
12697
+ operator: "beep!",
12698
+ operand,
12699
+ argument: operand,
12700
+ prefix: true,
12701
+ start: beepToken.start,
12702
+ end: this.previous().end,
12703
+ line: beepToken.line,
12704
+ column: beepToken.column
12705
+ };
12706
+ }
12707
+ isStyleRefStar(i) {
12708
+ const star = this.tokens[i];
12709
+ const name = this.tokens[i + 1];
12710
+ return star?.value === "*" && !!name && name.start === star.end && isIdentifierLike(name);
12711
+ }
12712
+ isSpacedStyleRef(i) {
12713
+ const prev = this.tokens[i - 1];
12714
+ return this.isStyleRefStar(i) && !!prev && prev.end < this.tokens[i].start;
12715
+ }
12213
12716
  tryParseStringPostfix(left, nextToken) {
12214
12717
  const v2 = nextToken.value;
12215
12718
  let unit = null;
@@ -12379,6 +12882,10 @@ var init_dist = __esm({
12379
12882
  const isStop = () => this.check("end") || extraStops.some((s) => this.check(s));
12380
12883
  debug.parse("\u{1F504} parseCommandListUntilTerminator: Starting (extraStops:", extraStops.join(","), ")");
12381
12884
  while (!this.isAtEnd() && !isStop()) {
12885
+ if (this.checkComment()) {
12886
+ this.advance();
12887
+ continue;
12888
+ }
12382
12889
  debug.parse("\u{1F4CD} Loop iteration, current token:", this.peek().value, "kind:", this.peek().kind);
12383
12890
  let parsedCommand = false;
12384
12891
  const isCommandToken = this.checkIsCommand();
@@ -12450,7 +12957,7 @@ var init_dist = __esm({
12450
12957
  if (this.check("end")) {
12451
12958
  debug.parse('\u2705 Found "end", consuming it');
12452
12959
  this.advance();
12453
- } else {
12960
+ } else if (!this.atEndOfInput()) {
12454
12961
  debug.parse('\u274C ERROR: Expected "end" but got:', this.peek().value, "at position:", this.peek().start);
12455
12962
  throw new Error(`Expected "end" to close ${construct} block`);
12456
12963
  }
@@ -12462,7 +12969,7 @@ var init_dist = __esm({
12462
12969
  if (!hasElse) {
12463
12970
  if (this.check("end")) {
12464
12971
  this.advance();
12465
- } else {
12972
+ } else if (!this.atEndOfInput()) {
12466
12973
  throw new Error('Expected "end" to close repeat block');
12467
12974
  }
12468
12975
  }
@@ -12477,7 +12984,10 @@ var init_dist = __esm({
12477
12984
  if (terminator === "end") {
12478
12985
  this.advance();
12479
12986
  } else if (terminator === "") {
12480
- throw new Error('Expected "end", "else", "until", or "while" to close repeat block');
12987
+ if (!this.atEndOfInput()) {
12988
+ throw new Error('Expected "end", "else", "until", or "while" to close repeat block');
12989
+ }
12990
+ return { commands, terminator: "end" };
12481
12991
  }
12482
12992
  return { commands, terminator };
12483
12993
  }
@@ -13017,7 +13527,9 @@ var init_dist = __esm({
13017
13527
  parseTopLevelInitBlock() {
13018
13528
  const pos2 = this.getPosition();
13019
13529
  const initCommands = this.parseCommandBlock(["end"]);
13020
- this.consume("end", "Expected 'end' after init block");
13530
+ if (!this.match("end") && !this.atEndOfInput() && !this.checkFeatureStart()) {
13531
+ this.consume("end", "Expected 'end' after init block");
13532
+ }
13021
13533
  return {
13022
13534
  type: "initBlock",
13023
13535
  commands: initCommands,
@@ -13186,10 +13698,27 @@ var init_dist = __esm({
13186
13698
  ...sourceExpr.column !== void 0 && { column: sourceExpr.column }
13187
13699
  };
13188
13700
  }
13701
+ isArgumentListOnly(open) {
13702
+ let depth = 0;
13703
+ for (let i = open; i < this.tokens.length; i++) {
13704
+ const token = this.tokens[i];
13705
+ if (token.kind !== "operator")
13706
+ continue;
13707
+ if (token.value === "(" || token.value === "[" || token.value === "{")
13708
+ depth++;
13709
+ else if (token.value === ")" || token.value === "]" || token.value === "}") {
13710
+ if (--depth === 0)
13711
+ return i === open + 1;
13712
+ } else if (token.value === "," && depth === 1)
13713
+ return true;
13714
+ }
13715
+ return false;
13716
+ }
13189
13717
  tryParsePseudoCommand(callExpr, sourceExpr) {
13190
13718
  const methodName = callExpr.callee.name;
13191
13719
  const nextToken = this.peek();
13192
- const hasPseudoCommandPattern = _Parser.PSEUDO_COMMAND_PREPOSITIONS.includes(nextToken.value.toLowerCase()) || isIdentifier(nextToken) && !this.isCommand(nextToken.value) || isContextVar(nextToken);
13720
+ const nextIsArticle = nextToken.value.toLowerCase() === "the";
13721
+ const hasPseudoCommandPattern = nextIsArticle || _Parser.PSEUDO_COMMAND_PREPOSITIONS.includes(nextToken.value.toLowerCase()) || isIdentifier(nextToken) && !this.isCommand(nextToken.value) || isContextVar(nextToken);
13193
13722
  if (!hasPseudoCommandPattern) {
13194
13723
  return {
13195
13724
  node: this.callExprToCommandNode(callExpr, sourceExpr),
@@ -13213,7 +13742,9 @@ var init_dist = __esm({
13213
13742
  });
13214
13743
  }
13215
13744
  let preposition;
13216
- if (_Parser.PSEUDO_COMMAND_PREPOSITIONS.includes(this.peek().value.toLowerCase())) {
13745
+ if (nextIsArticle) {
13746
+ this.advance();
13747
+ } else if (_Parser.PSEUDO_COMMAND_PREPOSITIONS.includes(this.peek().value.toLowerCase())) {
13217
13748
  preposition = this.advance().value.toLowerCase();
13218
13749
  }
13219
13750
  let targetExpr;
@@ -13366,10 +13897,18 @@ var init_dist = __esm({
13366
13897
  this.consume("]", "Expected ']' after event condition");
13367
13898
  }
13368
13899
  let target;
13900
+ let targetExpression;
13369
13901
  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})`);
13902
+ if (this.check("the"))
13903
+ this.advance();
13904
+ if (_Parser.POSITIONAL_SOURCE_HEADS.has(this.peek().value.toLowerCase())) {
13905
+ const startIndex = this.current;
13906
+ targetExpression = this.parseExpression();
13907
+ target = this.tokens.slice(startIndex, this.current).map((t) => t.value).join(" ");
13908
+ } else {
13909
+ target = this.advance().value;
13910
+ }
13911
+ debug.parse(`\u{1F527} parseEventHandler: Parsed 'from' target: ${target}`);
13373
13912
  this.parseTemporalKeywordModifiers(modifiers);
13374
13913
  }
13375
13914
  let attributeName;
@@ -13414,7 +13953,7 @@ var init_dist = __esm({
13414
13953
  const nextIsOpenParen = this.tokens[this.current + 1]?.value === "(";
13415
13954
  const commandName = this.peek().value.toLowerCase();
13416
13955
  const isSpecialBodyCommand = commandName === "js" || commandName === "tell";
13417
- if (nextIsOpenParen && !isSpecialBodyCommand) {
13956
+ if (nextIsOpenParen && !isSpecialBodyCommand && this.isArgumentListOnly(this.current + 1)) {
13418
13957
  let expr;
13419
13958
  const savedError = this.error;
13420
13959
  try {
@@ -13442,7 +13981,7 @@ var init_dist = __esm({
13442
13981
  this.recordDropped(`Command '${cmdToken}' failed to parse and was discarded`);
13443
13982
  }
13444
13983
  }
13445
- } else if (this.checkIdentifier()) {
13984
+ } else if (this.checkIdentifier() || this.checkEventNamedCall()) {
13446
13985
  const token = this.peek();
13447
13986
  if (this.isCommand(token.value)) {
13448
13987
  const cmd = this.parseCommandWithErrorRecovery();
@@ -13528,6 +14067,7 @@ var init_dist = __esm({
13528
14067
  ...eventParams.length > 0 && { args: eventParams },
13529
14068
  ...condition && { condition: fromLegacyExpression(condition) },
13530
14069
  ...target && { target },
14070
+ ...targetExpression && { targetExpression: fromLegacyExpression(targetExpression) },
13531
14071
  ...attributeName && { attributeName },
13532
14072
  ...watchTarget && { watchTarget: fromLegacyExpression(watchTarget) },
13533
14073
  ...customEventSource && { customEventSource },
@@ -13764,10 +14304,9 @@ var init_dist = __esm({
13764
14304
  }
13765
14305
  parseCommandCore() {
13766
14306
  const commandToken = this.previous();
13767
- let commandName = commandToken.value;
14307
+ const commandName = commandToken.value;
13768
14308
  if (commandName === "beep" && this.check("!")) {
13769
14309
  this.advance();
13770
- commandName = "beep!";
13771
14310
  }
13772
14311
  if (commandName === "fetch") {
13773
14312
  return parseFetchCommand(this.getContext(), commandToken);
@@ -13860,6 +14399,9 @@ var init_dist = __esm({
13860
14399
  args.push(this.parsePrimary());
13861
14400
  }
13862
14401
  const callNode = this.createCallExpression(createIdentifier(funcName, namePos), args);
14402
+ if (funcName === "closest" && args.length > 0 && this.match("to")) {
14403
+ callNode.closestTo = this.parseExpression();
14404
+ }
13863
14405
  if (funcName === "next" || funcName === "previous") {
13864
14406
  const relativePositional = this.parseRelativePositionalModifiers();
13865
14407
  if (relativePositional) {
@@ -13898,6 +14440,9 @@ var init_dist = __esm({
13898
14440
  }
13899
14441
  parseTheXofY() {
13900
14442
  const positionalKeywords = ["first", "last", "next", "previous", "random", "closest"];
14443
+ if (this.isStyleRefStar(this.current)) {
14444
+ return this.parsePrimary();
14445
+ }
13901
14446
  if (!this.checkIdentifier()) {
13902
14447
  return this.createIdentifier("the");
13903
14448
  }
@@ -13913,7 +14458,7 @@ var init_dist = __esm({
13913
14458
  return this.createIdentifier(propertyName);
13914
14459
  }
13915
14460
  this.advance();
13916
- const target = this.parseCall();
14461
+ const target = parseOfOperand(this.makePrattContext(), THE_OF_TARGET_BP);
13917
14462
  return {
13918
14463
  type: "propertyOfExpression",
13919
14464
  property: { type: "identifier", name: propertyName },
@@ -14108,6 +14653,13 @@ var init_dist = __esm({
14108
14653
  checkLiteral() {
14109
14654
  return this.checkPredicate(isLiteral);
14110
14655
  }
14656
+ checkEventNamedCall() {
14657
+ if (this.isAtEnd() || !this.checkIdentifierLike() || this.checkIsCommand())
14658
+ return false;
14659
+ const token = this.peek();
14660
+ const next = this.tokens[this.current + 1];
14661
+ return next?.value === "(" && next.start === token.end;
14662
+ }
14111
14663
  checkIdentifierLike() {
14112
14664
  return this.checkPredicate(isIdentifierLike);
14113
14665
  }
@@ -14216,6 +14768,19 @@ var init_dist = __esm({
14216
14768
  isAtEnd() {
14217
14769
  return this.current >= this.tokens.length;
14218
14770
  }
14771
+ atEndOfInput() {
14772
+ for (let i = this.current; i < this.tokens.length; i++) {
14773
+ if (!isComment(this.tokens[i]))
14774
+ return false;
14775
+ }
14776
+ return true;
14777
+ }
14778
+ checkFeatureStart() {
14779
+ if (this.isAtEnd())
14780
+ return false;
14781
+ const value = this.peek().value;
14782
+ return value === "init" || value === "on" || value === "def" || getRegisteredFeature(value) !== void 0;
14783
+ }
14219
14784
  peek() {
14220
14785
  if (this.isAtEnd()) {
14221
14786
  return { kind: "unknown", value: "", start: 0, end: 0, line: 1, column: 1 };
@@ -14420,6 +14985,7 @@ var init_dist = __esm({
14420
14985
  match: this.match.bind(this),
14421
14986
  matchOperator: this.matchOperator.bind(this),
14422
14987
  isAtEnd: this.isAtEnd.bind(this),
14988
+ atEndOfInput: this.atEndOfInput.bind(this),
14423
14989
  createIdentifier: this.createIdentifier.bind(this),
14424
14990
  parseExpression: this.parseExpression.bind(this),
14425
14991
  parsePrimary: this.parsePrimary.bind(this),
@@ -14468,7 +15034,15 @@ var init_dist = __esm({
14468
15034
  "ignoring case"
14469
15035
  ]);
14470
15036
  Parser.PRATT_TABLE = PARSER_TABLE;
15037
+ Parser.BEEP_OPERAND_BP = 80;
14471
15038
  Parser.PSEUDO_COMMAND_PREPOSITIONS = ["from", "on", "with", "into", "at", "to"];
15039
+ Parser.POSITIONAL_SOURCE_HEADS = /* @__PURE__ */ new Set([
15040
+ "closest",
15041
+ "next",
15042
+ "previous",
15043
+ "first",
15044
+ "last"
15045
+ ]);
14472
15046
  HookRegistry = class {
14473
15047
  constructor() {
14474
15048
  this.hooks = /* @__PURE__ */ new Map();
@@ -14918,7 +15492,7 @@ var init_dist = __esm({
14918
15492
  innertext: (el) => el.textContent?.trim(),
14919
15493
  innerHTML: (el) => el.innerHTML,
14920
15494
  outerhtml: (el) => el.outerHTML,
14921
- value: (el) => isFormElement$1(el) ? el.value : void 0,
15495
+ value: (el) => "value" in el ? el.value : void 0,
14922
15496
  checked: (el) => isInputElement$1(el) ? el.checked : void 0,
14923
15497
  disabled: (el) => "disabled" in el ? el.disabled : void 0,
14924
15498
  selected: (el) => isOptionElement$1(el) ? el.selected : void 0,
@@ -15360,14 +15934,15 @@ var init_dist = __esm({
15360
15934
  if (typeof selector !== "string") {
15361
15935
  throw new Error("closest requires a string selector");
15362
15936
  }
15363
- if (!context.me) {
15937
+ const start = args.length > 1 ? args[1] : context.me;
15938
+ if (!(start instanceof Element)) {
15364
15939
  return null;
15365
15940
  }
15366
- return context.me.closest(selector);
15941
+ return start.closest(selector);
15367
15942
  },
15368
15943
  validate(args) {
15369
- if (args.length !== 1) {
15370
- return "closest requires exactly 1 argument (selector)";
15944
+ if (args.length !== 1 && args.length !== 2) {
15945
+ return "closest requires a selector and, optionally, the element to start from";
15371
15946
  }
15372
15947
  const arg = args[0];
15373
15948
  if (typeof arg === "string")
@@ -16220,7 +16795,7 @@ var init_dist = __esm({
16220
16795
  else if (value instanceof Node || value instanceof Element)
16221
16796
  result = false;
16222
16797
  else if (isObject(value))
16223
- result = Object.keys(value).length === 0;
16798
+ result = value.length === 0;
16224
16799
  else
16225
16800
  result = false;
16226
16801
  return result;
@@ -17501,8 +18076,8 @@ var init_dist = __esm({
17501
18076
  }
17502
18077
  case "initBlock":
17503
18078
  case "block": {
17504
- const block = node;
17505
- const ops = (Array.isArray(block.commands) ? block.commands : []).map((c) => this.compile(c));
18079
+ const block2 = node;
18080
+ const ops = (Array.isArray(block2.commands) ? block2.commands : []).map((c) => this.compile(c));
17506
18081
  const consumesHalt = node.type === "initBlock";
17507
18082
  return async (context) => {
17508
18083
  for (const op of ops) {
@@ -17882,12 +18457,22 @@ var init_dist = __esm({
17882
18457
  debug.runtime(`BEHAVIOR: Finished installing ${behaviorName}`);
17883
18458
  }
17884
18459
  async executeEventHandler(node, context) {
17885
- const { event, events, commands, target, args, selector, condition, attributeName, watchTarget, modifiers, errorSymbol, errorHandler, finallyHandler } = node;
18460
+ const { event, events, commands, target, targetExpression, args, selector, condition, attributeName, watchTarget, modifiers, errorSymbol, errorHandler, finallyHandler } = node;
17886
18461
  const eventNames = events && events.length > 0 ? events : [event];
17887
18462
  debug.runtime(`BEHAVIOR: executeEventHandler: event='${event}', target='${target}'`);
17888
18463
  let targets = [];
17889
18464
  let globalTarget = null;
17890
- if (target) {
18465
+ if (targetExpression) {
18466
+ const resolved = await this.execute(targetExpression, context);
18467
+ if (resolved === window)
18468
+ globalTarget = window;
18469
+ else if (resolved === document)
18470
+ globalTarget = document;
18471
+ else if (this.isElement(resolved))
18472
+ targets = [resolved];
18473
+ else if (Array.isArray(resolved))
18474
+ targets = resolved.filter((el) => this.isElement(el));
18475
+ } else if (target) {
17891
18476
  const targetLower = typeof target === "string" ? target.toLowerCase() : "";
17892
18477
  if (targetLower === "window" || targetLower === "the window") {
17893
18478
  globalTarget = window;
@@ -19667,9 +20252,9 @@ var init_dist = __esm({
19667
20252
  if (!raw.args?.length)
19668
20253
  throw new Error("toggle command requires an argument");
19669
20254
  const firstArg = raw.args[0];
19670
- const propertyTarget = await resolveAnyPropertyTarget(firstArg, evaluator, context);
19671
- if (propertyTarget) {
19672
- return { type: "property", target: propertyTarget };
20255
+ const propertyTarget2 = await resolveAnyPropertyTarget(firstArg, evaluator, context);
20256
+ if (propertyTarget2) {
20257
+ return { type: "property", target: propertyTarget2 };
19673
20258
  }
19674
20259
  if (firstArg.type === "selector" && typeof firstArg.value === "string" && firstArg.value.startsWith("*")) {
19675
20260
  let expression2 = firstArg.value;
@@ -19903,13 +20488,13 @@ var init_dist = __esm({
19903
20488
  let targetSelector = null, memberPath, variableName;
19904
20489
  if (targetArg) {
19905
20490
  const tt = nodeType(targetArg);
19906
- const propertyTarget = await resolveAnyPropertyTarget(targetArg, evaluator, context);
19907
- if (propertyTarget) {
20491
+ const propertyTarget2 = await resolveAnyPropertyTarget(targetArg, evaluator, context);
20492
+ if (propertyTarget2) {
19908
20493
  return {
19909
20494
  value,
19910
- targets: [propertyTarget.element],
20495
+ targets: [propertyTarget2.element],
19911
20496
  position: "into",
19912
- memberPath: propertyTarget.property
20497
+ memberPath: propertyTarget2.property
19913
20498
  };
19914
20499
  }
19915
20500
  if (tt === "memberExpression") {
@@ -20049,7 +20634,11 @@ var init_dist = __esm({
20049
20634
  }
20050
20635
  if (isInsertableNode(v2))
20051
20636
  return v2;
20052
- return v2 == null ? "" : String(v2);
20637
+ if (v2 == null)
20638
+ return "null";
20639
+ if (Array.isArray(v2))
20640
+ return arrayFragment(v2);
20641
+ return String(v2);
20053
20642
  }
20054
20643
  looksLikeCss(s) {
20055
20644
  if (!s)
@@ -21132,7 +21721,7 @@ var init_dist = __esm({
21132
21721
  if (args.length === 0 && modifiers.with === void 0) {
21133
21722
  throw new Error("[HyperFixi] morph: command requires arguments");
21134
21723
  }
21135
- const strategy = literalText(modifiers.strategy)?.toLowerCase() === "over" ? "morphOuter" : "morph";
21724
+ const strategy = literalText(modifiers.strategy)?.toLowerCase() === "over" ? "morphOuter" : "morphMerge";
21136
21725
  const useViewTransition = modifiers.viewTransition !== void 0;
21137
21726
  const targetNode = modifiers.on !== void 0 ? modifiers.on : args[0];
21138
21727
  const contentNode = modifiers.with !== void 0 ? modifiers.with : modifiers.on !== void 0 ? args[0] : args[1];
@@ -21768,6 +22357,7 @@ var init_dist = __esm({
21768
22357
  return FetchCommand2 = _classThis;
21769
22358
  })();
21770
22359
  createFetchCommand = createFactory(FetchCommand);
22360
+ SET_RUNGS = { nodeWriters: true, styleSplit: true, ofAndObjectProperty: true };
21771
22361
  SetCommand = (() => {
21772
22362
  let _classDecorators = [command({ name: "set" })];
21773
22363
  let _classDescriptor;
@@ -21786,8 +22376,7 @@ var init_dist = __esm({
21786
22376
  const argScope = firstArg?.scope;
21787
22377
  const writeTarget = await resolveWriteTarget(firstArg, evaluator, context, {
21788
22378
  scopeElements: () => this.resolveTargets(raw.modifiers.on, evaluator, context),
21789
- nodeWriters: true,
21790
- styleSplit: true
22379
+ ...SET_RUNGS
21791
22380
  });
21792
22381
  if (writeTarget) {
21793
22382
  return toSetInput(writeTarget, await this.extractValue(raw, evaluator, context));
@@ -21911,13 +22500,13 @@ var init_dist = __esm({
21911
22500
  }
21912
22501
  async tryParseMemberExpression(firstArg, raw, evaluator, context) {
21913
22502
  const objectNode = firstArg.object;
21914
- const propertyNode = firstArg.property;
21915
- if (objectNode?.name && propertyNode?.name) {
22503
+ const propertyNode2 = firstArg.property;
22504
+ if (objectNode?.name && propertyNode2?.name) {
21916
22505
  const objectName = objectNode.name.toLowerCase();
21917
22506
  if (["me", "my", "it", "its", "you", "your"].includes(objectName)) {
21918
22507
  const element = resolvePossessive(objectName, context);
21919
22508
  const value = await this.extractValue(raw, evaluator, context);
21920
- return { type: "property", element, property: propertyNode.name, value };
22509
+ return { type: "property", element, property: propertyNode2.name, value };
21921
22510
  }
21922
22511
  }
21923
22512
  const objectAst = firstArg.object;
@@ -21933,7 +22522,7 @@ var init_dist = __esm({
21933
22522
  container = await evaluator.evaluate(objectAst, context);
21934
22523
  }
21935
22524
  if (container != null && typeof container === "object") {
21936
- const property = computed ? String(await evaluator.evaluate(firstArg.property, context)) : propertyNode?.name || "";
22525
+ const property = computed ? String(await evaluator.evaluate(firstArg.property, context)) : propertyNode2?.name || "";
21937
22526
  if (property) {
21938
22527
  const value = await this.extractValue(raw, evaluator, context);
21939
22528
  return {
@@ -22100,7 +22689,7 @@ var init_dist = __esm({
22100
22689
  async execute(input, context) {
22101
22690
  const { target, property, scope, amount = 1, operation = "increment", customWrite } = input;
22102
22691
  if (customWrite) {
22103
- const delta = isFinite(amount) ? amount : 1;
22692
+ const delta = amount;
22104
22693
  const newValue2 = operation === "increment" ? customWrite.currentValue + delta : customWrite.currentValue - delta;
22105
22694
  await customWrite.writer(customWrite.node, newValue2, context);
22106
22695
  Object.assign(context, { it: newValue2 });
@@ -22111,8 +22700,7 @@ var init_dist = __esm({
22111
22700
  if (isNaN(currentValue)) {
22112
22701
  newValue = NaN;
22113
22702
  } else {
22114
- const delta = isFinite(amount) ? amount : 1;
22115
- newValue = operation === "increment" ? currentValue + delta : currentValue - delta;
22703
+ newValue = operation === "increment" ? currentValue + amount : currentValue - amount;
22116
22704
  }
22117
22705
  setTargetValue(target, property, scope, newValue, context);
22118
22706
  Object.assign(context, { it: newValue });
@@ -22516,9 +23104,9 @@ var init_dist = __esm({
22516
23104
  }
22517
23105
  const { target, position, offset, behavior } = input;
22518
23106
  if (typeof window !== "undefined") {
22519
- const block = this.mapVerticalPosition(position.vertical);
23107
+ const block2 = this.mapVerticalPosition(position.vertical);
22520
23108
  const inline = this.mapHorizontalPosition(position.horizontal);
22521
- target.scrollIntoView?.({ behavior, block, inline });
23109
+ target.scrollIntoView?.({ behavior, block: block2, inline });
22522
23110
  if (offset !== 0) {
22523
23111
  const { x, y } = this.calculateScrollPosition(target, position, offset);
22524
23112
  window.scrollTo?.({ left: x, top: y, behavior });
@@ -23009,7 +23597,7 @@ var init_dist = __esm({
23009
23597
  }
23010
23598
  }
23011
23599
  const m = raw.modifiers ?? {};
23012
- const text = (node) => slotText(node, evaluator, context);
23600
+ const text = (node) => slotText$1(node, evaluator, context);
23013
23601
  const loopType = await text(m.loopType) ?? null;
23014
23602
  const bottomTested = m.bottomTested ? await evaluator.evaluate(m.bottomTested, context) : false;
23015
23603
  if (loopType === "for" || m.for) {
@@ -23027,17 +23615,24 @@ var init_dist = __esm({
23027
23615
  };
23028
23616
  }
23029
23617
  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))
23618
+ if (!m.times)
23033
23619
  throw new Error("times loops require a count number");
23620
+ const count = loopCount(await evaluator.evaluate(m.times, context));
23034
23621
  return { type: "times", count, indexVariable, commands, elseCommands };
23035
23622
  }
23036
23623
  if (loopType === "while" || m.while) {
23037
23624
  const condition = m.while;
23038
23625
  if (!condition)
23039
23626
  throw new Error("while loops require a condition");
23040
- return { type: "while", condition, indexVariable, commands, elseCommands, bottomTested };
23627
+ const check = () => evaluator.evaluate(condition, context);
23628
+ return {
23629
+ type: "while",
23630
+ condition: check,
23631
+ indexVariable,
23632
+ commands,
23633
+ elseCommands,
23634
+ bottomTested
23635
+ };
23041
23636
  }
23042
23637
  if (loopType === "until-event" || m.event) {
23043
23638
  const eventName = await text(m.event);
@@ -23053,7 +23648,7 @@ var init_dist = __esm({
23053
23648
  eventTarget = window;
23054
23649
  } else {
23055
23650
  const target = await evaluator.evaluate(m.from, context);
23056
- if (target instanceof EventTarget)
23651
+ if (isEventTarget(target))
23057
23652
  eventTarget = target;
23058
23653
  else if (target === "document")
23059
23654
  eventTarget = document;
@@ -23074,7 +23669,15 @@ var init_dist = __esm({
23074
23669
  const condition = m.until;
23075
23670
  if (!condition)
23076
23671
  throw new Error("until loops require a condition");
23077
- return { type: "until", condition, indexVariable, commands, elseCommands, bottomTested };
23672
+ const check = () => evaluator.evaluate(condition, context);
23673
+ return {
23674
+ type: "until",
23675
+ condition: check,
23676
+ indexVariable,
23677
+ commands,
23678
+ elseCommands,
23679
+ bottomTested
23680
+ };
23078
23681
  }
23079
23682
  if (loopType === "forever") {
23080
23683
  return { type: "forever", indexVariable, commands, elseCommands };
@@ -23092,10 +23695,10 @@ var init_dist = __esm({
23092
23695
  ({ config: config2, iterCtx } = createTimesLoopConfig(count, indexVariable));
23093
23696
  break;
23094
23697
  case "while":
23095
- ({ config: config2, iterCtx } = createWhileLoopConfig(condition, evaluateCondition, context, indexVariable));
23698
+ ({ config: config2, iterCtx } = createWhileLoopConfig(condition, evaluateLoopCondition, context, indexVariable));
23096
23699
  break;
23097
23700
  case "until":
23098
- ({ config: config2, iterCtx } = createUntilLoopConfig(condition, evaluateCondition, context, indexVariable));
23701
+ ({ config: config2, iterCtx } = createUntilLoopConfig(condition, evaluateLoopCondition, context, indexVariable));
23099
23702
  break;
23100
23703
  case "until-event":
23101
23704
  ({ config: config2, iterCtx } = createUntilEventLoopConfig(eventName, eventTarget, indexVariable));
@@ -24042,6 +24645,7 @@ var init_dist = __esm({
24042
24645
  return PrependCommand2 = _classThis;
24043
24646
  })();
24044
24647
  createPrependCommand = createFactory(PrependCommand);
24648
+ RESTORE_KEYWORDS = /* @__PURE__ */ new Set(["initial", "inherit", "unset", "revert"]);
24045
24649
  TransitionCommand = (() => {
24046
24650
  let _classDecorators = [command({ name: "transition" })];
24047
24651
  let _classDescriptor;
@@ -24057,10 +24661,9 @@ var init_dist = __esm({
24057
24661
  let property;
24058
24662
  let target;
24059
24663
  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;
24664
+ const owners = elementsOf(firstArg);
24665
+ if (owners.length > 0 || typeof firstArg === "string" && /^[#.]|^(?:me|it|you)$/.test(firstArg)) {
24666
+ target = owners.length === 1 ? owners[0] : owners.length > 1 ? owners : firstArg;
24064
24667
  property = String(await evaluator.evaluate(raw.args[1], context));
24065
24668
  } else if (raw.args.length >= 2) {
24066
24669
  throw new Error("transition: target element not found");
@@ -24070,9 +24673,9 @@ var init_dist = __esm({
24070
24673
  throw new Error("transition requires a CSS property");
24071
24674
  property = named.property;
24072
24675
  if (named.objectNode !== void 0) {
24073
- const owner = asElement(await evaluator.evaluate(named.objectNode, context));
24074
- if (isHTMLElement$2(owner))
24075
- target = owner;
24676
+ const owner = elementsOf(await evaluator.evaluate(named.objectNode, context));
24677
+ if (owner.length > 0)
24678
+ target = owner.length === 1 ? owner[0] : owner;
24076
24679
  }
24077
24680
  } else {
24078
24681
  property = String(firstArg);
@@ -24082,55 +24685,126 @@ var init_dist = __esm({
24082
24685
  }
24083
24686
  if (!raw.modifiers?.to)
24084
24687
  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 };
24688
+ const result = {
24689
+ property,
24690
+ value: await valueOf(raw.modifiers.to, evaluator, context)
24691
+ };
24090
24692
  if (target !== void 0)
24091
24693
  result.target = target;
24694
+ if (raw.modifiers?.from)
24695
+ result.from = await valueOf(raw.modifiers.from, evaluator, context);
24696
+ if (raw.modifiers?.pairs) {
24697
+ result.pairs = await this.laterPairs(raw.modifiers.pairs, evaluator, context);
24698
+ }
24092
24699
  if (raw.modifiers?.over)
24093
24700
  result.duration = await evaluator.evaluate(raw.modifiers.over, context);
24094
24701
  if (raw.modifiers?.with)
24095
24702
  result.timingFunction = String(await evaluator.evaluate(raw.modifiers.with, context));
24703
+ if (raw.modifiers?.using)
24704
+ result.using = String(await evaluator.evaluate(raw.modifiers.using, context));
24096
24705
  return result;
24097
24706
  }
24707
+ async laterPairs(node, evaluator, context) {
24708
+ const pairs = [];
24709
+ for (const pairNode of node.elements ?? []) {
24710
+ const fields = /* @__PURE__ */ new Map();
24711
+ const properties = pairNode.properties ?? [];
24712
+ for (const { key, value } of properties) {
24713
+ if (typeof key.name === "string")
24714
+ fields.set(key.name, value);
24715
+ }
24716
+ const propertyNode2 = fields.get("property");
24717
+ const toNode = fields.get("to");
24718
+ if (!propertyNode2 || !toNode)
24719
+ throw new Error("transition requires property and value");
24720
+ const pair = {
24721
+ property: String(await evaluator.evaluate(propertyNode2, context)),
24722
+ value: await valueOf(toNode, evaluator, context)
24723
+ };
24724
+ const ownerNode = fields.get("owner");
24725
+ if (ownerNode) {
24726
+ const owners = elementsOf(await evaluator.evaluate(ownerNode, context));
24727
+ if (owners.length === 0)
24728
+ throw new Error("transition: target element not found");
24729
+ pair.target = owners.length === 1 ? owners[0] : owners;
24730
+ }
24731
+ const fromNode = fields.get("from");
24732
+ if (fromNode)
24733
+ pair.from = await valueOf(fromNode, evaluator, context);
24734
+ pairs.push(pair);
24735
+ }
24736
+ return pairs;
24737
+ }
24098
24738
  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);
24739
+ const duration = parseDuration(input.duration, input.using !== void 0 && longestTransitionMs(input.using) || 300);
24740
+ const timing = input.timingFunction || "ease";
24741
+ const jobs = [];
24742
+ for (const pair of [input, ...input.pairs ?? []]) {
24743
+ let property = pair.property;
24744
+ if (property.startsWith("*"))
24745
+ property = property.substring(1);
24746
+ property = camelToKebab(property);
24747
+ const elements = Array.isArray(pair.target) ? pair.target.filter(isHTMLElement$2) : [resolveElement(pair.target, context, "transition")];
24748
+ for (const element of elements) {
24749
+ jobs.push({
24750
+ element,
24751
+ property,
24752
+ pair,
24753
+ fromValue: getComputedStyle(element).getPropertyValue(property),
24754
+ toValue: "",
24755
+ removeInlineAfter: false
24756
+ });
24757
+ }
24758
+ }
24759
+ if (jobs.length === 0)
24760
+ throw new Error("transition: target element not found");
24761
+ for (const job of jobs) {
24762
+ if (job.pair.from !== void 0) {
24763
+ job.element.style.setProperty(job.property, String(job.pair.from));
24764
+ }
24765
+ }
24766
+ for (const job of jobs)
24767
+ if (job.pair.from !== void 0)
24768
+ void job.element.offsetWidth;
24769
+ const originals = /* @__PURE__ */ new Map();
24770
+ for (const job of jobs) {
24771
+ if (!originals.has(job.element))
24772
+ originals.set(job.element, job.element.style.transition);
24773
+ }
24774
+ for (const [element, original] of originals) {
24775
+ const own = jobs.filter((job) => job.element === element).map((job) => `${job.property} ${duration}ms ${timing}`);
24776
+ element.style.transition = input.using ?? [original, ...own].filter(Boolean).join(", ");
24777
+ }
24778
+ for (const job of jobs) {
24779
+ const { element, property } = job;
24780
+ let toValue = String(job.pair.value);
24781
+ if (RESTORE_KEYWORDS.has(toValue)) {
24782
+ const currentInline = element.style.getPropertyValue(property);
24783
+ element.style.removeProperty(property);
24784
+ toValue = getComputedStyle(element).getPropertyValue(property);
24785
+ if (currentInline) {
24786
+ element.style.setProperty(property, currentInline);
24787
+ }
24788
+ job.removeInlineAfter = true;
24789
+ }
24790
+ job.toValue = toValue;
24791
+ element.style.setProperty(property, toValue);
24126
24792
  }
24793
+ const results = await Promise.all(jobs.map((job) => waitForTransitionEnd(job.element, job.property, duration)));
24794
+ for (const [element, original] of originals)
24795
+ element.style.transition = original;
24796
+ for (const job of jobs) {
24797
+ if (job.removeInlineAfter)
24798
+ job.element.style.removeProperty(job.property);
24799
+ }
24800
+ const first = jobs[0];
24127
24801
  return {
24128
- element: targetElement,
24129
- property,
24130
- fromValue,
24131
- toValue,
24802
+ element: first.element,
24803
+ property: first.property,
24804
+ fromValue: first.fromValue,
24805
+ toValue: first.toValue,
24132
24806
  duration,
24133
- completed: result.completed
24807
+ completed: results.every((result) => result.completed)
24134
24808
  };
24135
24809
  }
24136
24810
  };
@@ -24143,13 +24817,16 @@ var init_dist = __esm({
24143
24817
  })();
24144
24818
  _classThis.metadata = commandMeta({
24145
24819
  description: "Animate CSS properties using CSS transitions",
24146
- syntax: "transition [<target>] <property> to <value> [over <duration>] [with <timing>]",
24820
+ syntax: "transition <property> [from <value>] to <value> [<property> \u2026 to <value>]\u2026 [over <duration>] [using <css> | with <timing>]",
24147
24821
  examples: [
24148
24822
  "transition opacity to 0.5",
24149
24823
  "transition my *opacity to 0 over 200ms",
24150
24824
  "transition #box's *opacity to 0 over 200ms",
24151
24825
  "transition left to 100px over 500ms",
24152
- "transition background-color to red over 1s with ease-in-out"
24826
+ "transition background-color to red over 1s with ease-in-out",
24827
+ "transition *width to 100px *height to 50px over 300ms",
24828
+ "transition *opacity of #panel from 0 to 1 over 200ms",
24829
+ 'transition my *opacity to 0 using "opacity 1s ease-in"'
24153
24830
  ],
24154
24831
  sideEffects: ["style-change", "timing"],
24155
24832
  category: "animation",
@@ -25309,17 +25986,7 @@ var init_dist = __esm({
25309
25986
  this.debugContext(context);
25310
25987
  return { expressionCount: 0, debugged: true, outputs: [] };
25311
25988
  }
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();
25989
+ const outputs = beepValues(context.me, expressions);
25323
25990
  return { expressionCount: expressions.length, debugged: true, outputs };
25324
25991
  }
25325
25992
  debugContext(context) {
@@ -25332,63 +25999,6 @@ var init_dist = __esm({
25332
25999
  console.log("variables:", context.variables);
25333
26000
  console.groupEnd();
25334
26001
  }
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
26002
  };
25393
26003
  __setFunctionName(_classThis, "BeepCommand");
25394
26004
  (() => {
@@ -25909,16 +26519,12 @@ var init_dist = __esm({
25909
26519
  };
25910
26520
  RenderCommand.metadata = commandMeta({
25911
26521
  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
- ],
26522
+ syntax: ["render <template>", "render <template> with <name>: <value>, ..."],
25917
26523
  examples: [
25918
26524
  "render myTemplate",
25919
- 'render myTemplate with (name: "Alice")',
25920
- 'render "<template>Hello ${name}!</template>" with (name: "World")',
25921
- "render template with (items: data)"
26525
+ 'render myTemplate with name: "Alice"',
26526
+ 'render "<template>Hello ${name}!</template>" with name: "World"',
26527
+ "render template with items: data"
25922
26528
  ],
25923
26529
  category: "templates",
25924
26530
  compatibility: "standard",
@@ -26030,7 +26636,7 @@ var init_dist = __esm({
26030
26636
  averageConfidence: 0,
26031
26637
  confidenceHistory: []
26032
26638
  };
26033
- VERSION = "3.1.1";
26639
+ VERSION = "3.3.0";
26034
26640
  HistorySwapBehavior = {
26035
26641
  name: "HistorySwap",
26036
26642
  init(element, params = {}) {
@@ -27482,8 +28088,8 @@ var init_multilingual = __esm({
27482
28088
  if (declared && modifiers) {
27483
28089
  for (const [key, role] of Object.entries(declared)) {
27484
28090
  const value = modifiers[key];
27485
- const isNode2 = !!value && typeof value === "object" && "type" in value;
27486
- if (isNode2 && inferred[role] === void 0) {
28091
+ const isNode3 = !!value && typeof value === "object" && "type" in value;
28092
+ if (isNode3 && inferred[role] === void 0) {
27487
28093
  inferred[role] = value;
27488
28094
  }
27489
28095
  }
@@ -27870,7 +28476,7 @@ function analyze(ast) {
27870
28476
  function extractClassOp2(node) {
27871
28477
  if (node.type !== "command") return null;
27872
28478
  const cmd = node;
27873
- if (!CLASS_COMMANDS.has(cmd.name)) return null;
28479
+ if (!TARGET_SLOTS.has(cmd.name)) return null;
27874
28480
  const args = cmd.args ?? [];
27875
28481
  if (args.length === 0) return null;
27876
28482
  const arg = args[0];
@@ -27879,48 +28485,32 @@ function extractClassOp2(node) {
27879
28485
  if (!selector.startsWith(".")) return null;
27880
28486
  const className = selector.slice(1);
27881
28487
  if (!className) return null;
27882
- return {
27883
- command: cmd.name,
27884
- className,
27885
- hasExplicitTarget: !!cmd.target
27886
- };
28488
+ return { command: cmd.name, className };
27887
28489
  }
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;
28490
+ function classTarget(cmd) {
28491
+ if (cmd.target) return cmd.target;
28492
+ const slot = cmd.modifiers?.[TARGET_SLOTS.get(cmd.name)];
28493
+ return slot && typeof slot === "object" && "type" in slot ? slot : void 0;
27897
28494
  }
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}`;
28495
+ function batchTargetKey(cmd) {
28496
+ const target = classTarget(cmd);
28497
+ if (!target) return "me";
28498
+ if (target.type === "identifier" && target.value === "me") return "me";
28499
+ if (target.type === "selector" && /^#[\w-]+$/.test(target.value)) {
28500
+ return target.value;
27904
28501
  }
27905
28502
  return null;
27906
28503
  }
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";
28504
+ function resolveTarget2(key) {
28505
+ return key === "me" ? "_ctx.me" : `document.getElementById('${key.slice(1)}')`;
28506
+ }
28507
+ function reordersClass(run, op) {
28508
+ return run.some((node) => {
28509
+ const other = extractClassOp2(node);
28510
+ return other.command !== op.command && other.className === op.className;
28511
+ });
27922
28512
  }
27923
- function createBatchNode(run) {
28513
+ function createBatchNode(run, targetKey2) {
27924
28514
  const adds = [];
27925
28515
  const removes = [];
27926
28516
  const toggles = [];
@@ -27938,10 +28528,9 @@ function createBatchNode(run) {
27938
28528
  break;
27939
28529
  }
27940
28530
  }
27941
- const target = resolveTarget2(run[0]);
27942
28531
  return {
27943
28532
  type: "batchedClassOps",
27944
- target,
28533
+ target: resolveTarget2(targetKey2),
27945
28534
  adds,
27946
28535
  removes,
27947
28536
  toggles
@@ -27950,9 +28539,10 @@ function createBatchNode(run) {
27950
28539
  function batchBody(nodes) {
27951
28540
  const result = [];
27952
28541
  let currentRun = [];
28542
+ let runKey = "";
27953
28543
  function flushRun() {
27954
28544
  if (currentRun.length >= 2) {
27955
- result.push(createBatchNode(currentRun));
28545
+ result.push(createBatchNode(currentRun, runKey));
27956
28546
  } else if (currentRun.length === 1) {
27957
28547
  result.push(currentRun[0]);
27958
28548
  }
@@ -27960,12 +28550,14 @@ function batchBody(nodes) {
27960
28550
  }
27961
28551
  for (const node of nodes) {
27962
28552
  const op = extractClassOp2(node);
27963
- if (op) {
27964
- if (currentRun.length > 0 && isSameTarget(currentRun[0], node)) {
28553
+ const key = op && batchTargetKey(node);
28554
+ if (op && key) {
28555
+ if (currentRun.length > 0 && key === runKey && !reordersClass(currentRun, op)) {
27965
28556
  currentRun.push(node);
27966
28557
  } else {
27967
28558
  flushRun();
27968
28559
  currentRun = [node];
28560
+ runKey = key;
27969
28561
  }
27970
28562
  } else {
27971
28563
  flushRun();
@@ -28210,6 +28802,70 @@ function generateExpression(node, ctx) {
28210
28802
  const codegen = new ExpressionCodegen(ctx);
28211
28803
  return codegen.generate(node);
28212
28804
  }
28805
+ function commandTarget(node, ...slots) {
28806
+ if (node.target) return node.target;
28807
+ for (const slot of slots) {
28808
+ const found = slot === "arg" ? node.args?.[0] : node.modifiers?.[slot];
28809
+ if (isNode2(found)) return found;
28810
+ }
28811
+ return void 0;
28812
+ }
28813
+ function isScopedQuery(node) {
28814
+ const binary = node;
28815
+ return binary?.type === "binary" && binary.operator === "in" && binary.left.type === "selector";
28816
+ }
28817
+ function allMatches(node, ctx) {
28818
+ if (isScopedQuery(node)) return ctx.generateExpression(node);
28819
+ if (node?.type !== "selector") return null;
28820
+ const selector = node.value;
28821
+ return /^#[\w-]+$/.test(selector) ? null : `document.querySelectorAll('${sanitizeSelector(selector)}')`;
28822
+ }
28823
+ function forEachTarget(node, ctx, op) {
28824
+ const all = allMatches(node, ctx);
28825
+ if (all) return `${all}.forEach(el => ${op("el")})`;
28826
+ return op(node ? ctx.generateExpression(node) : "_ctx.me");
28827
+ }
28828
+ function variableStore(node, ctx) {
28829
+ let name;
28830
+ let scope;
28831
+ if (node.type === "variable") {
28832
+ name = node.name;
28833
+ scope = node.scope;
28834
+ } else if (node.type === "identifier") {
28835
+ name = node.value ?? node.name ?? "";
28836
+ if (/^(\$|::)/.test(name)) scope = "global";
28837
+ else if (/^[A-Za-z_]\w*$/.test(name)) scope = "local";
28838
+ else return null;
28839
+ } else {
28840
+ return null;
28841
+ }
28842
+ const key = sanitizeIdentifier(name.replace(/^(\$|::|:)/, ""));
28843
+ if (scope === "global") {
28844
+ ctx.requireHelper("globals");
28845
+ return { read: `_rt.globals.get('${key}')`, write: (v2) => `_rt.globals.set('${key}', ${v2})` };
28846
+ }
28847
+ if (scope === "element") {
28848
+ ctx.requireHelper("elementVars");
28849
+ const store = `_rt.elementVars(_ctx.me)`;
28850
+ return { read: `${store}.get('${key}')`, write: (v2) => `${store}.set('${key}', ${v2})` };
28851
+ }
28852
+ return { read: `_ctx.locals.get('${key}')`, write: (v2) => `_ctx.locals.set('${key}', ${v2})` };
28853
+ }
28854
+ function scrollOptions(modifiers) {
28855
+ const word = (slot) => {
28856
+ const value = modifiers?.[slot]?.value;
28857
+ return typeof value === "string" ? value : void 0;
28858
+ };
28859
+ const position = word("position") ?? "";
28860
+ const options = [];
28861
+ if (SCROLL_BLOCK[position]) options.push(`block: '${SCROLL_BLOCK[position]}'`);
28862
+ if (SCROLL_INLINE[position]) options.push(`inline: '${SCROLL_INLINE[position]}'`);
28863
+ const behavior = modifiers?.smooth ? "smooth" : word("behavior");
28864
+ options.push(
28865
+ `behavior: '${behavior === "smooth" || behavior === "instant" ? behavior : "auto"}'`
28866
+ );
28867
+ return `{ ${options.join(", ")} }`;
28868
+ }
28213
28869
  function generateIf(node, ctx, generateBody) {
28214
28870
  const exprCodegen = new ExpressionCodegen(ctx);
28215
28871
  const condition = exprCodegen.generate(node.condition);
@@ -28234,49 +28890,85 @@ ${elseBody}
28234
28890
  }
28235
28891
  return code;
28236
28892
  }
28893
+ function emitLoop(parts) {
28894
+ const pass = [
28895
+ ...parts.elseBody !== void 0 ? ["_ran = true;"] : [],
28896
+ ...parts.enter ? [parts.enter] : [],
28897
+ ...parts.indexName ? [`_ctx.locals.set('${sanitizeIdentifier(parts.indexName)}', _i);`] : [],
28898
+ parts.body
28899
+ ].join("\n");
28900
+ const step = parts.step ? `_i++, ${parts.step}` : "_i++";
28901
+ const loop = parts.bottomTested ? (
28902
+ // A `continue` jumps straight to the test, so the counter moves there.
28903
+ `let _i = 0;
28904
+ do {
28905
+ ${pass}
28906
+ } while ((${step}, ${parts.test}));`
28907
+ ) : `for (let _i = 0; ${parts.test}; ${step}) {
28908
+ ${pass}
28909
+ }`;
28910
+ if (parts.elseBody === void 0) return parts.bottomTested ? `{
28911
+ ${loop}
28912
+ }` : loop;
28913
+ return `{
28914
+ let _ran = false;
28915
+ ${loop}
28916
+ if (!_ran) {
28917
+ ${parts.elseBody}
28918
+ }
28919
+ }`;
28920
+ }
28237
28921
  function generateRepeat(node, ctx, generateBody) {
28238
28922
  const exprCodegen = new ExpressionCodegen(ctx);
28239
- const body = generateBody(node.body);
28923
+ const shared = {
28924
+ indexName: node.indexName,
28925
+ body: generateBody(node.body),
28926
+ elseBody: node.elseBody ? generateBody(node.elseBody) : void 0
28927
+ };
28240
28928
  if (node.count !== void 0) {
28241
28929
  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
- }`;
28930
+ return emitLoop({ ...shared, test: `_i < ${count}`, indexName: node.indexName ?? "index" });
28246
28931
  }
28247
28932
  if (node.whileCondition) {
28248
- const condition = exprCodegen.generate(node.whileCondition);
28249
- return `while (${condition}) {
28250
- ${body}
28933
+ return emitLoop({ ...shared, test: exprCodegen.generate(node.whileCondition) });
28934
+ }
28935
+ if (node.untilEvent !== void 0) {
28936
+ ctx.requireHelper("wait");
28937
+ const target = node.untilEventTarget ? exprCodegen.generate(node.untilEventTarget) : "_ctx.me";
28938
+ return `{
28939
+ let _fired = false;
28940
+ ${target}.addEventListener(${JSON.stringify(node.untilEvent)}, () => { _fired = true; }, { once: true });
28941
+ ${emitLoop({ ...shared, test: "!_fired", step: "await _rt.wait(0)" })}
28251
28942
  }`;
28252
28943
  }
28253
- return `while (true) {
28254
- ${body}
28255
- }`;
28944
+ return emitLoop({ ...shared, test: "true" });
28256
28945
  }
28257
28946
  function generateForEach(node, ctx, generateBody) {
28258
28947
  const exprCodegen = new ExpressionCodegen(ctx);
28259
- const collection = exprCodegen.generate(node.collection);
28948
+ const source = node.collection;
28949
+ const collection = source.type === "selector" && !String(source.value).startsWith("#") ? `document.querySelectorAll('${sanitizeSelector(String(source.value))}')` : exprCodegen.generate(source);
28260
28950
  const itemName = sanitizeIdentifier(node.itemName);
28261
- const indexName = node.indexName ? sanitizeIdentifier(node.indexName) : "index";
28262
- const body = generateBody(node.body);
28263
28951
  return `{
28264
28952
  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
- }
28953
+ const _arr = _collection == null ? [] : Array.isArray(_collection) ? _collection : Array.from(_collection);
28954
+ ${emitLoop({
28955
+ test: "_i < _arr.length",
28956
+ enter: `_ctx.locals.set('${itemName}', _arr[_i]);`,
28957
+ indexName: node.indexName ?? "index",
28958
+ body: generateBody(node.body),
28959
+ elseBody: node.elseBody ? generateBody(node.elseBody) : void 0
28960
+ })}
28271
28961
  }`;
28272
28962
  }
28273
28963
  function generateWhile(node, ctx, generateBody) {
28274
28964
  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
- }`;
28965
+ return emitLoop({
28966
+ test: exprCodegen.generate(node.condition),
28967
+ bottomTested: node.bottomTested,
28968
+ indexName: node.indexName,
28969
+ body: generateBody(node.body),
28970
+ elseBody: node.elseBody ? generateBody(node.elseBody) : void 0
28971
+ });
28280
28972
  }
28281
28973
  function generateCommand(node, ctx) {
28282
28974
  const codegen = commandCodegens.get(node.name);
@@ -28351,7 +29043,7 @@ async function createMultilingualCompiler() {
28351
29043
  }
28352
29044
  return compiler;
28353
29045
  }
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;
29046
+ 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
29047
  var init_dist3 = __esm({
28356
29048
  "../aot-compiler/dist/index.js"() {
28357
29049
  "use strict";
@@ -28720,6 +29412,13 @@ var init_dist3 = __esm({
28720
29412
  }
28721
29413
  visitCommand(node) {
28722
29414
  this.commandsUsed.add(node.name);
29415
+ if (["set", "increment", "decrement", "default"].includes(node.name)) {
29416
+ const target = node.roles?.destination ?? node.roles?.patient ?? node.args?.[0];
29417
+ const name = target?.type === "identifier" ? target.value : void 0;
29418
+ if (name && /^[A-Za-z_]\w*$/.test(name)) {
29419
+ this.registerVariable(name, "local", "write");
29420
+ }
29421
+ }
28723
29422
  switch (node.name) {
28724
29423
  case "fetch":
28725
29424
  this.hasAsync = true;
@@ -28801,32 +29500,37 @@ var init_dist3 = __esm({
28801
29500
  if (node.whileCondition) {
28802
29501
  this.visit(node.whileCondition);
28803
29502
  }
28804
- for (const child of node.body) {
28805
- this.visit(child);
29503
+ if (node.untilEvent !== void 0) {
29504
+ this.hasAsync = true;
29505
+ this.runtimeHelpers.add("wait");
29506
+ if (node.untilEventTarget) this.visit(node.untilEventTarget);
28806
29507
  }
29508
+ this.visitLoopBody(node);
28807
29509
  this.exitNesting();
28808
29510
  }
28809
29511
  visitForEach(node) {
28810
29512
  this.hasLoops = true;
28811
29513
  this.enterNesting();
28812
29514
  this.registerVariable(node.itemName, "local", "write");
28813
- if (node.indexName) {
28814
- this.registerVariable(node.indexName, "local", "write");
28815
- }
28816
29515
  this.visit(node.collection);
28817
- for (const child of node.body) {
28818
- this.visit(child);
28819
- }
29516
+ this.visitLoopBody(node);
28820
29517
  this.exitNesting();
28821
29518
  }
28822
29519
  visitWhile(node) {
28823
29520
  this.hasLoops = true;
28824
29521
  this.enterNesting();
28825
29522
  this.visit(node.condition);
28826
- for (const child of node.body) {
29523
+ this.visitLoopBody(node);
29524
+ this.exitNesting();
29525
+ }
29526
+ /** The parts every loop form shares: its `index` local, body, and `else`. */
29527
+ visitLoopBody(node) {
29528
+ if (node.indexName) {
29529
+ this.registerVariable(node.indexName, "local", "write");
29530
+ }
29531
+ for (const child of [...node.body, ...node.elseBody ?? []]) {
28827
29532
  this.visit(child);
28828
29533
  }
28829
- this.exitNesting();
28830
29534
  }
28831
29535
  visitVariable(node, access) {
28832
29536
  const name = node.name.startsWith(":") || node.name.startsWith("$") ? node.name.slice(1) : node.name;
@@ -28975,7 +29679,11 @@ var init_dist3 = __esm({
28975
29679
  }
28976
29680
  }
28977
29681
  };
28978
- CLASS_COMMANDS = /* @__PURE__ */ new Set(["add", "remove", "toggle"]);
29682
+ TARGET_SLOTS = /* @__PURE__ */ new Map([
29683
+ ["add", "to"],
29684
+ ["remove", "from"],
29685
+ ["toggle", "on"]
29686
+ ]);
28979
29687
  ClassBatchingPass = class {
28980
29688
  constructor() {
28981
29689
  this.name = "class-batching";
@@ -29253,6 +29961,9 @@ var init_dist3 = __esm({
29253
29961
  if (this.usesIndex(body)) {
29254
29962
  return null;
29255
29963
  }
29964
+ if (repeatNode.indexName !== void 0 || repeatNode.elseBody !== void 0) {
29965
+ return null;
29966
+ }
29256
29967
  const unrolled = [];
29257
29968
  for (let i = 0; i < count; i++) {
29258
29969
  unrolled.push(...this.cloneBody(body));
@@ -29381,6 +30092,8 @@ var init_dist3 = __esm({
29381
30092
  return this.generateTemplate(node);
29382
30093
  case "unary":
29383
30094
  return this.generateUnary(node);
30095
+ case "typeCheck":
30096
+ return this.generateTypeCheck(node);
29384
30097
  case "conditional":
29385
30098
  return this.generateConditional(node);
29386
30099
  case "sequence":
@@ -29455,6 +30168,9 @@ var init_dist3 = __esm({
29455
30168
  this.ctx.requireHelper("globals");
29456
30169
  return `_rt.globals.get('${varName}')`;
29457
30170
  }
30171
+ if (this.ctx.analysis?.variables?.locals?.get(name)?.scope === "local") {
30172
+ return `_ctx.locals.get('${sanitizeIdentifier(name)}')`;
30173
+ }
29458
30174
  return sanitizeIdentifier(name);
29459
30175
  }
29460
30176
  }
@@ -29488,7 +30204,8 @@ var init_dist3 = __esm({
29488
30204
  this.ctx.requireHelper("globals");
29489
30205
  return `_rt.globals.get('${varName}')`;
29490
30206
  case "element":
29491
- return `_ctx.me.${varName}`;
30207
+ this.ctx.requireHelper("elementVars");
30208
+ return `_rt.elementVars(_ctx.me).get('${varName}')`;
29492
30209
  default:
29493
30210
  return `_ctx.locals.get('${varName}')`;
29494
30211
  }
@@ -29501,19 +30218,40 @@ var init_dist3 = __esm({
29501
30218
  const right = this.generate(node.right);
29502
30219
  const op = node.operator;
29503
30220
  switch (op) {
29504
- // Equality
30221
+ // Equality, with core's phrase forms of each (`n is equal to 3`).
29505
30222
  case "is":
29506
30223
  case "==":
30224
+ case "am":
30225
+ case "equals":
30226
+ case "is equal":
30227
+ case "is equal to":
29507
30228
  return `(${left} === ${right})`;
29508
30229
  case "is not":
29509
30230
  case "!=":
30231
+ case "is not equal":
30232
+ case "is not equal to":
29510
30233
  return `(${left} !== ${right})`;
29511
- // Comparison
30234
+ case "really equals":
30235
+ case "is really":
30236
+ case "is really equal to":
30237
+ return `(${left} === ${right})`;
30238
+ case "is not really":
30239
+ case "is not really equal to":
30240
+ return `(${left} !== ${right})`;
30241
+ // Comparison. Left as is, `(n is greater than 2)` is not JavaScript.
29512
30242
  case "<":
29513
30243
  case ">":
29514
30244
  case "<=":
29515
30245
  case ">=":
29516
30246
  return `(${left} ${op} ${right})`;
30247
+ case "is less than":
30248
+ return `(${left} < ${right})`;
30249
+ case "is greater than":
30250
+ return `(${left} > ${right})`;
30251
+ case "is less than or equal to":
30252
+ return `(${left} <= ${right})`;
30253
+ case "is greater than or equal to":
30254
+ return `(${left} >= ${right})`;
29517
30255
  // Arithmetic
29518
30256
  case "+":
29519
30257
  case "-":
@@ -29521,6 +30259,9 @@ var init_dist3 = __esm({
29521
30259
  case "/":
29522
30260
  case "%":
29523
30261
  return `(${left} ${op} ${right})`;
30262
+ // Hyperscript's word for `%`; left as is, `(n mod 2)` is not JavaScript.
30263
+ case "mod":
30264
+ return `(${left} % ${right})`;
29524
30265
  // Logical
29525
30266
  case "and":
29526
30267
  case "&&":
@@ -29530,15 +30271,49 @@ var init_dist3 = __esm({
29530
30271
  return `(${left} || ${right})`;
29531
30272
  // String/collection operators
29532
30273
  case "contains":
30274
+ case "contain":
30275
+ case "includes":
30276
+ case "include":
29533
30277
  this.ctx.requireHelper("contains");
29534
30278
  return `_rt.contains(${left}, ${right})`;
30279
+ case "does not contain":
30280
+ case "do not contain":
30281
+ case "does not contains":
30282
+ case "does not include":
30283
+ this.ctx.requireHelper("contains");
30284
+ return `!_rt.contains(${left}, ${right})`;
30285
+ // `matches` tests a selector, so a selector operand stays a string: the
30286
+ // element it would query is not a selector (`_rt.matches(el, el)`).
29535
30287
  case "matches":
30288
+ case "match":
30289
+ this.ctx.requireHelper("matches");
30290
+ return `_rt.matches(${left}, ${this.selectorText(node.right, right)})`;
30291
+ case "does not match":
30292
+ case "do not match":
29536
30293
  this.ctx.requireHelper("matches");
29537
- return `_rt.matches(${left}, ${right})`;
30294
+ return `!_rt.matches(${left}, ${this.selectorText(node.right, right)})`;
30295
+ // Membership, `n is in [1, 3]`: the right side contains the left.
30296
+ case "is in":
30297
+ case "am in":
30298
+ this.ctx.requireHelper("contains");
30299
+ return `_rt.contains(${right}, ${left})`;
30300
+ case "is not in":
30301
+ case "am not in":
30302
+ this.ctx.requireHelper("contains");
30303
+ return `!_rt.contains(${right}, ${left})`;
29538
30304
  case "starts with":
29539
30305
  return `${left}.startsWith(${right})`;
29540
30306
  case "ends with":
29541
30307
  return `${left}.endsWith(${right})`;
30308
+ // A scoped query, `<li/> in #list`: the matches inside the scope, as
30309
+ // both engines read it. JavaScript's `in` (the default below) tests a
30310
+ // property KEY, so `(document.querySelector('li') in …)` matched nothing.
30311
+ case "in":
30312
+ if (node.left.type === "selector") {
30313
+ const query = sanitizeSelector(node.left.value);
30314
+ return `${right}.querySelectorAll('${query}')`;
30315
+ }
30316
+ return `(${left} ${op} ${right})`;
29542
30317
  // Class check
29543
30318
  case "has":
29544
30319
  if (node.right.type === "selector") {
@@ -29566,13 +30341,18 @@ var init_dist3 = __esm({
29566
30341
  return `(${left} ${op} ${right})`;
29567
30342
  }
29568
30343
  }
30344
+ /** A selector operand as its quoted text; any other operand as generated. */
30345
+ selectorText(node, generated) {
30346
+ return node.type === "selector" ? `'${sanitizeSelector(node.value)}'` : generated;
30347
+ }
29569
30348
  // ===========================================================================
29570
30349
  // MEMBER EXPRESSIONS
29571
30350
  // ===========================================================================
29572
30351
  generateMember(node) {
29573
30352
  const object = this.generate(node.object);
29574
- if (typeof node.property === "string") {
29575
- const prop = node.property;
30353
+ const property = node.property;
30354
+ const prop = typeof property === "string" ? property : !node.computed && property.type === "identifier" ? property.value ?? property.name : void 0;
30355
+ if (prop !== void 0) {
29576
30356
  if (prop.startsWith("*")) {
29577
30357
  const styleProp = prop.slice(1);
29578
30358
  return `${object}.style.${styleProp}`;
@@ -29754,6 +30534,12 @@ var init_dist3 = __esm({
29754
30534
  // ===========================================================================
29755
30535
  // UNARY EXPRESSIONS
29756
30536
  // ===========================================================================
30537
+ /** `x is a Number`, as both engines test it: see the runtime's `typeCheck`. */
30538
+ generateTypeCheck(node) {
30539
+ this.ctx.requireHelper("typeCheck");
30540
+ const test = `_rt.typeCheck(${this.generate(node.value)}, ${JSON.stringify(node.typeName)}, ${node.nullOk})`;
30541
+ return node.negated ? `!${test}` : test;
30542
+ }
29757
30543
  generateUnary(node) {
29758
30544
  const operand = this.generate(node.operand);
29759
30545
  switch (node.operator) {
@@ -29766,6 +30552,22 @@ var init_dist3 = __esm({
29766
30552
  return `+${operand}`;
29767
30553
  case "no":
29768
30554
  return `!${operand}`;
30555
+ // Core's postfix tests. Left as is, `exists#d1` is not JavaScript.
30556
+ case "exists":
30557
+ case "some":
30558
+ this.ctx.requireHelper("exists");
30559
+ return `_rt.exists(${operand})`;
30560
+ case "does not exist":
30561
+ this.ctx.requireHelper("exists");
30562
+ return `!_rt.exists(${operand})`;
30563
+ case "is empty":
30564
+ this.ctx.requireHelper("isEmpty");
30565
+ return `_rt.isEmpty(${operand})`;
30566
+ case "is not empty":
30567
+ this.ctx.requireHelper("isEmpty");
30568
+ return `!_rt.isEmpty(${operand})`;
30569
+ case "beep!":
30570
+ return `((v) => (console.log('%c[beep]', 'color: orange; font-weight: bold', v), v))(${operand})`;
29769
30571
  default:
29770
30572
  return `${node.operator}${operand}`;
29771
30573
  }
@@ -29780,6 +30582,7 @@ var init_dist3 = __esm({
29780
30582
  return `(${condition} ? ${consequent} : ${alternate})`;
29781
30583
  }
29782
30584
  };
30585
+ isNode2 = (value) => typeof value === "object" && value !== null && "type" in value;
29783
30586
  ToggleCodegen = class {
29784
30587
  constructor() {
29785
30588
  this.command = "toggle";
@@ -29787,22 +30590,15 @@ var init_dist3 = __esm({
29787
30590
  generate(node, ctx) {
29788
30591
  const args = node.args ?? [];
29789
30592
  if (args.length === 0) return null;
29790
- const target = node.target ? ctx.generateExpression(node.target) : "_ctx.me";
30593
+ const targetNode = commandTarget(node, "on");
29791
30594
  const arg = args[0];
29792
30595
  if (arg.type === "selector") {
29793
30596
  const selector = arg.value;
29794
30597
  if (selector.startsWith(".")) {
29795
30598
  const className = sanitizeClassName(selector.slice(1));
29796
30599
  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
30600
  return {
29805
- code: `Array.from(document.querySelectorAll('${sanitizeSelector(selector.slice(1))}')).forEach(el => el.classList.toggle('${className}'))`,
30601
+ code: forEachTarget(targetNode, ctx, (el) => `${el}.classList.toggle('${className}')`),
29806
30602
  async: false,
29807
30603
  sideEffects: true
29808
30604
  };
@@ -29814,7 +30610,11 @@ var init_dist3 = __esm({
29814
30610
  const attrName = value.slice(1);
29815
30611
  ctx.requireHelper("toggleAttr");
29816
30612
  return {
29817
- code: `_rt.toggleAttr(${target}, '${sanitizeSelector(attrName)}')`,
30613
+ code: forEachTarget(
30614
+ targetNode,
30615
+ ctx,
30616
+ (el) => `_rt.toggleAttr(${el}, '${sanitizeSelector(attrName)}')`
30617
+ ),
29818
30618
  async: false,
29819
30619
  sideEffects: true
29820
30620
  };
@@ -29822,7 +30622,11 @@ var init_dist3 = __esm({
29822
30622
  }
29823
30623
  ctx.requireHelper("toggle");
29824
30624
  return {
29825
- code: `_rt.toggle(${ctx.generateExpression(arg)}, ${target})`,
30625
+ code: forEachTarget(
30626
+ targetNode,
30627
+ ctx,
30628
+ (el) => `_rt.toggle(${ctx.generateExpression(arg)}, ${el})`
30629
+ ),
29826
30630
  async: false,
29827
30631
  sideEffects: true
29828
30632
  };
@@ -29843,15 +30647,8 @@ var init_dist3 = __esm({
29843
30647
  if (selector.startsWith(".")) {
29844
30648
  const className = sanitizeClassName(selector.slice(1));
29845
30649
  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
30650
  return {
29854
- code: `${target}.classList.add('${className}')`,
30651
+ code: forEachTarget(targetSlot, ctx, (el) => `${el}.classList.add('${className}')`),
29855
30652
  async: false,
29856
30653
  sideEffects: true
29857
30654
  };
@@ -29874,7 +30671,11 @@ var init_dist3 = __esm({
29874
30671
  }
29875
30672
  ctx.requireHelper("addClass");
29876
30673
  return {
29877
- code: `_rt.addClass(${target}, ${ctx.generateExpression(arg)})`,
30674
+ code: forEachTarget(
30675
+ targetSlot,
30676
+ ctx,
30677
+ (el) => `_rt.addClass(${el}, ${ctx.generateExpression(arg)})`
30678
+ ),
29878
30679
  async: false,
29879
30680
  sideEffects: true
29880
30681
  };
@@ -29888,23 +30689,29 @@ var init_dist3 = __esm({
29888
30689
  const args = node.args ?? [];
29889
30690
  if (args.length === 0) {
29890
30691
  const targetSlot2 = node.target ?? node.modifiers?.from;
29891
- const target2 = targetSlot2 ? ctx.generateExpression(targetSlot2) : "_ctx.me";
29892
30692
  return {
29893
- code: `${target2}.remove()`,
30693
+ code: forEachTarget(targetSlot2, ctx, (el) => `${el}.remove()`),
29894
30694
  async: false,
29895
30695
  sideEffects: true
29896
30696
  };
29897
30697
  }
29898
30698
  const targetSlot = node.target ?? node.modifiers?.from;
29899
- const target = targetSlot ? ctx.generateExpression(targetSlot) : "_ctx.me";
29900
30699
  const arg = args[0];
30700
+ const isElement2 = arg.type === "selector" ? !/^[.@]/.test(arg.value) : arg.type === "identifier" && !String(arg.value ?? "").startsWith("@") || arg.type === "variable" || isScopedQuery(arg);
30701
+ if (!targetSlot && isElement2) {
30702
+ return {
30703
+ code: forEachTarget(arg, ctx, (el) => `${el}.remove()`),
30704
+ async: false,
30705
+ sideEffects: true
30706
+ };
30707
+ }
29901
30708
  if (arg.type === "selector") {
29902
30709
  const selector = arg.value;
29903
30710
  if (selector.startsWith(".")) {
29904
30711
  const className = sanitizeClassName(selector.slice(1));
29905
30712
  if (!className) return null;
29906
30713
  return {
29907
- code: `${target}.classList.remove('${className}')`,
30714
+ code: forEachTarget(targetSlot, ctx, (el) => `${el}.classList.remove('${className}')`),
29908
30715
  async: false,
29909
30716
  sideEffects: true
29910
30717
  };
@@ -29912,7 +30719,11 @@ var init_dist3 = __esm({
29912
30719
  }
29913
30720
  ctx.requireHelper("removeClass");
29914
30721
  return {
29915
- code: `_rt.removeClass(${target}, ${ctx.generateExpression(arg)})`,
30722
+ code: forEachTarget(
30723
+ targetSlot,
30724
+ ctx,
30725
+ (el) => `_rt.removeClass(${el}, ${ctx.generateExpression(arg)})`
30726
+ ),
29916
30727
  async: false,
29917
30728
  sideEffects: true
29918
30729
  };
@@ -29930,26 +30741,8 @@ var init_dist3 = __esm({
29930
30741
  const valueNode = roles?.patient ?? modifiers?.to ?? args[1];
29931
30742
  if (!targetNode || !valueNode) return null;
29932
30743
  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
- }
30744
+ const store = variableStore(targetNode, ctx);
30745
+ if (store) return { code: store.write(value), async: false, sideEffects: true };
29953
30746
  if (targetNode.type === "possessive") {
29954
30747
  const possessive = targetNode;
29955
30748
  const obj = ctx.generateExpression(possessive.object);
@@ -29998,20 +30791,17 @@ var init_dist3 = __esm({
29998
30791
  const contentNode = roles?.patient ?? args[0];
29999
30792
  if (!contentNode) return null;
30000
30793
  const content = ctx.generateExpression(contentNode);
30001
- let target = "_ctx.me";
30002
- if (roles?.destination) {
30003
- target = ctx.generateExpression(roles.destination);
30004
- } else if (modifiers) {
30794
+ let targetNode = roles?.destination;
30795
+ if (!targetNode && modifiers) {
30005
30796
  for (const key of ["into", "before", "after"]) {
30006
30797
  if (modifiers[key] && typeof modifiers[key] === "object") {
30007
- target = ctx.generateExpression(modifiers[key]);
30798
+ targetNode = modifiers[key];
30008
30799
  break;
30009
30800
  }
30010
30801
  }
30011
30802
  }
30012
- if (target === "_ctx.me" && node.target) {
30013
- target = ctx.generateExpression(node.target);
30014
- }
30803
+ targetNode ?? (targetNode = node.target);
30804
+ const into = (op) => forEachTarget(targetNode, ctx, op);
30015
30805
  let modifier = "into";
30016
30806
  if (roles?.method && roles.method.value) {
30017
30807
  modifier = String(roles.method.value);
@@ -30023,39 +30813,39 @@ var init_dist3 = __esm({
30023
30813
  switch (modifier) {
30024
30814
  case "into":
30025
30815
  return {
30026
- code: `${target}.innerHTML = ${content}`,
30816
+ code: into((el) => `${el}.innerHTML = ${content}`),
30027
30817
  async: false,
30028
30818
  sideEffects: true
30029
30819
  };
30030
30820
  case "before":
30031
30821
  return {
30032
- code: `${target}.insertAdjacentHTML('beforebegin', ${content})`,
30822
+ code: into((el) => `${el}.insertAdjacentHTML('beforebegin', ${content})`),
30033
30823
  async: false,
30034
30824
  sideEffects: true
30035
30825
  };
30036
30826
  case "after":
30037
30827
  return {
30038
- code: `${target}.insertAdjacentHTML('afterend', ${content})`,
30828
+ code: into((el) => `${el}.insertAdjacentHTML('afterend', ${content})`),
30039
30829
  async: false,
30040
30830
  sideEffects: true
30041
30831
  };
30042
30832
  case "at start of":
30043
30833
  case "start":
30044
30834
  return {
30045
- code: `${target}.insertAdjacentHTML('afterbegin', ${content})`,
30835
+ code: into((el) => `${el}.insertAdjacentHTML('afterbegin', ${content})`),
30046
30836
  async: false,
30047
30837
  sideEffects: true
30048
30838
  };
30049
30839
  case "at end of":
30050
30840
  case "end":
30051
30841
  return {
30052
- code: `${target}.insertAdjacentHTML('beforeend', ${content})`,
30842
+ code: into((el) => `${el}.insertAdjacentHTML('beforeend', ${content})`),
30053
30843
  async: false,
30054
30844
  sideEffects: true
30055
30845
  };
30056
30846
  default:
30057
30847
  return {
30058
- code: `${target}.innerHTML = ${content}`,
30848
+ code: into((el) => `${el}.innerHTML = ${content}`),
30059
30849
  async: false,
30060
30850
  sideEffects: true
30061
30851
  };
@@ -30067,9 +30857,9 @@ var init_dist3 = __esm({
30067
30857
  this.command = "show";
30068
30858
  }
30069
30859
  generate(node, ctx) {
30070
- const target = node.target ? ctx.generateExpression(node.target) : "_ctx.me";
30860
+ const targetNode = commandTarget(node, "arg");
30071
30861
  return {
30072
- code: `${target}.style.display = ''`,
30862
+ code: forEachTarget(targetNode, ctx, (el) => `${el}.style.display = ''`),
30073
30863
  async: false,
30074
30864
  sideEffects: true
30075
30865
  };
@@ -30080,9 +30870,22 @@ var init_dist3 = __esm({
30080
30870
  this.command = "hide";
30081
30871
  }
30082
30872
  generate(node, ctx) {
30083
- const target = node.target ? ctx.generateExpression(node.target) : "_ctx.me";
30873
+ const targetNode = commandTarget(node, "arg");
30874
+ return {
30875
+ code: forEachTarget(targetNode, ctx, (el) => `${el}.style.display = 'none'`),
30876
+ async: false,
30877
+ sideEffects: true
30878
+ };
30879
+ }
30880
+ };
30881
+ EmptyCodegen = class {
30882
+ constructor() {
30883
+ this.command = "empty";
30884
+ }
30885
+ generate(node, ctx) {
30886
+ const targetNode = commandTarget(node, "arg");
30084
30887
  return {
30085
- code: `${target}.style.display = 'none'`,
30888
+ code: forEachTarget(targetNode, ctx, (el) => `${el}.replaceChildren()`),
30086
30889
  async: false,
30087
30890
  sideEffects: true
30088
30891
  };
@@ -30093,7 +30896,8 @@ var init_dist3 = __esm({
30093
30896
  this.command = "focus";
30094
30897
  }
30095
30898
  generate(node, ctx) {
30096
- const target = node.target ? ctx.generateExpression(node.target) : "_ctx.me";
30899
+ const targetNode = commandTarget(node, "arg");
30900
+ const target = targetNode ? ctx.generateExpression(targetNode) : "_ctx.me";
30097
30901
  return {
30098
30902
  code: `${target}.focus()`,
30099
30903
  async: false,
@@ -30106,7 +30910,8 @@ var init_dist3 = __esm({
30106
30910
  this.command = "blur";
30107
30911
  }
30108
30912
  generate(node, ctx) {
30109
- const target = node.target ? ctx.generateExpression(node.target) : "_ctx.me";
30913
+ const targetNode = commandTarget(node, "arg");
30914
+ const target = targetNode ? ctx.generateExpression(targetNode) : "_ctx.me";
30110
30915
  return {
30111
30916
  code: `${target}.blur()`,
30112
30917
  async: false,
@@ -30227,11 +31032,11 @@ var init_dist3 = __esm({
30227
31032
  const args = node.args ?? [];
30228
31033
  if (args.length === 0) return null;
30229
31034
  const eventName = ctx.generateExpression(args[0]);
30230
- const target = node.target ? ctx.generateExpression(node.target) : "_ctx.me";
31035
+ const targetNode = commandTarget(node, "on", "to");
30231
31036
  const detail = args.length > 1 ? ctx.generateExpression(args[1]) : "undefined";
30232
31037
  ctx.requireHelper("send");
30233
31038
  return {
30234
- code: `_rt.send(${target}, ${eventName}, ${detail})`,
31039
+ code: forEachTarget(targetNode, ctx, (el) => `_rt.send(${el}, ${eventName}, ${detail})`),
30235
31040
  async: false,
30236
31041
  sideEffects: true
30237
31042
  };
@@ -30249,25 +31054,10 @@ var init_dist3 = __esm({
30249
31054
  if (!target) return null;
30250
31055
  const amountNode = roles?.quantity ?? modifiers?.by ?? args[1];
30251
31056
  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
- }
31057
+ const store = variableStore(target, ctx);
31058
+ if (store) {
31059
+ const code = store.write(`(Number(${store.read}) || 0) + ${amount}`);
31060
+ return { code, async: false, sideEffects: true };
30271
31061
  }
30272
31062
  const targetCode = ctx.generateExpression(target);
30273
31063
  return {
@@ -30289,25 +31079,10 @@ var init_dist3 = __esm({
30289
31079
  if (!target) return null;
30290
31080
  const amountNode = roles?.quantity ?? modifiers?.by ?? args[1];
30291
31081
  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
- }
31082
+ const store = variableStore(target, ctx);
31083
+ if (store) {
31084
+ const code = store.write(`(Number(${store.read}) || 0) - ${amount}`);
31085
+ return { code, async: false, sideEffects: true };
30311
31086
  }
30312
31087
  const targetCode = ctx.generateExpression(target);
30313
31088
  return {
@@ -30372,17 +31147,22 @@ var init_dist3 = __esm({
30372
31147
  };
30373
31148
  }
30374
31149
  };
31150
+ SCROLL_BLOCK = {
31151
+ top: "start",
31152
+ middle: "center",
31153
+ bottom: "end",
31154
+ nearest: "nearest"
31155
+ };
31156
+ SCROLL_INLINE = { left: "start", center: "center", right: "end" };
30375
31157
  ScrollCodegen = class {
30376
31158
  constructor() {
30377
31159
  this.command = "scroll";
30378
31160
  }
30379
31161
  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'";
31162
+ const targetNode = commandTarget(node, "arg", "of");
31163
+ const target = targetNode ? ctx.generateExpression(targetNode) : "_ctx.me";
30384
31164
  return {
30385
- code: `${target}.scrollIntoView({ behavior: ${behavior} })`,
31165
+ code: `${target}.scrollIntoView(${scrollOptions(node.modifiers)})`,
30386
31166
  async: false,
30387
31167
  sideEffects: true
30388
31168
  };
@@ -30392,7 +31172,7 @@ var init_dist3 = __esm({
30392
31172
  constructor() {
30393
31173
  this.command = "take";
30394
31174
  }
30395
- generate(node, _ctx) {
31175
+ generate(node, ctx) {
30396
31176
  const args = node.args ?? [];
30397
31177
  if (args.length === 0) return null;
30398
31178
  const arg = args[0];
@@ -30401,6 +31181,19 @@ var init_dist3 = __esm({
30401
31181
  if (!selector.startsWith(".")) return null;
30402
31182
  const className = sanitizeClassName(selector.slice(1));
30403
31183
  if (!className) return null;
31184
+ const source = commandTarget(node, "from");
31185
+ if (source) {
31186
+ let from = allMatches(source, ctx);
31187
+ if (!from) {
31188
+ ctx.requireHelper("toArray");
31189
+ from = `_rt.toArray(${ctx.generateExpression(source)})`;
31190
+ }
31191
+ return {
31192
+ code: `(() => { ${from}.forEach(el => el.classList.remove('${className}')); _ctx.me.classList.add('${className}'); })()`,
31193
+ async: false,
31194
+ sideEffects: true
31195
+ };
31196
+ }
30404
31197
  return {
30405
31198
  code: `(() => { const _me = _ctx.me; _me.parentElement?.querySelectorAll('.${className}').forEach(el => el.classList.remove('${className}')); _me.classList.add('${className}'); })()`,
30406
31199
  async: false,
@@ -30449,25 +31242,10 @@ var init_dist3 = __esm({
30449
31242
  const valueNode = roles?.patient ?? modifiers?.to ?? args[1];
30450
31243
  if (!targetNode || !valueNode) return null;
30451
31244
  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
- }
31245
+ const store = variableStore(targetNode, ctx);
31246
+ if (store) {
31247
+ const code = `if (${store.read} == null) ${store.write(value)}`;
31248
+ return { code, async: false, sideEffects: true };
30471
31249
  }
30472
31250
  return null;
30473
31251
  }
@@ -30483,6 +31261,15 @@ var init_dist3 = __esm({
30483
31261
  if (mods?.back) {
30484
31262
  return { code: "history.back()", async: false, sideEffects: true };
30485
31263
  }
31264
+ if (mods?.position || mods?.behavior) {
31265
+ const element = roles?.destination ?? args[0];
31266
+ if (!element) return null;
31267
+ return {
31268
+ code: `${ctx.generateExpression(element)}.scrollIntoView(${scrollOptions(mods)})`,
31269
+ async: false,
31270
+ sideEffects: true
31271
+ };
31272
+ }
30486
31273
  const target = mods?.url ?? roles?.destination ?? args[0];
30487
31274
  if (!target) return null;
30488
31275
  if (target.type === "identifier") {
@@ -30494,9 +31281,10 @@ var init_dist3 = __esm({
30494
31281
  return { code: "history.forward()", async: false, sideEffects: true };
30495
31282
  }
30496
31283
  }
30497
- const url = ctx.generateExpression(target);
31284
+ ctx.requireHelper("go");
31285
+ const newWindow = mods?.in !== void 0 ? ", true" : "";
30498
31286
  return {
30499
- code: `window.location.href = ${url}`,
31287
+ code: `_rt.go(${ctx.generateExpression(target)}${newWindow})`,
30500
31288
  async: false,
30501
31289
  sideEffects: true
30502
31290
  };
@@ -30512,7 +31300,8 @@ var init_dist3 = __esm({
30512
31300
  const contentNode = roles?.patient ?? args[0];
30513
31301
  if (!contentNode) return null;
30514
31302
  const content = ctx.generateExpression(contentNode);
30515
- const target = node.target ? ctx.generateExpression(node.target) : "_ctx.me";
31303
+ const targetNode = commandTarget(node, "to");
31304
+ const target = targetNode ? ctx.generateExpression(targetNode) : "_ctx.me";
30516
31305
  return {
30517
31306
  code: `${target}.insertAdjacentHTML('beforeend', ${content})`,
30518
31307
  async: false,
@@ -30831,9 +31620,9 @@ var init_dist3 = __esm({
30831
31620
  const args = node.args ?? [];
30832
31621
  const roles = node.roles;
30833
31622
  const mods = node.modifiers;
30834
- const propertyNode = roles?.patient ?? args[0];
30835
- if (!propertyNode) return null;
30836
- const property = ctx.generateExpression(propertyNode);
31623
+ const propertyNode2 = roles?.patient ?? args[0];
31624
+ if (!propertyNode2) return null;
31625
+ const property = ctx.generateExpression(propertyNode2);
30837
31626
  const valueNode = mods?.to ?? args[1];
30838
31627
  if (!valueNode) return null;
30839
31628
  const value = ctx.generateExpression(valueNode);
@@ -30891,7 +31680,8 @@ var init_dist3 = __esm({
30891
31680
  this.command = "settle";
30892
31681
  }
30893
31682
  generate(node, ctx) {
30894
- const target = node.target ? ctx.generateExpression(node.target) : "_ctx.me";
31683
+ const targetNode = commandTarget(node, "arg");
31684
+ const target = targetNode ? ctx.generateExpression(targetNode) : "_ctx.me";
30895
31685
  const mods = node.modifiers;
30896
31686
  const timeoutNode = mods?.for;
30897
31687
  const timeout2 = timeoutNode ? ctx.generateExpression(timeoutNode) : "5000";
@@ -31011,6 +31801,7 @@ var init_dist3 = __esm({
31011
31801
  ["put", new PutCodegen()],
31012
31802
  ["show", new ShowCodegen()],
31013
31803
  ["hide", new HideCodegen()],
31804
+ ["empty", new EmptyCodegen()],
31014
31805
  ["focus", new FocusCodegen()],
31015
31806
  ["blur", new BlurCodegen()],
31016
31807
  ["log", new LogCodegen()],
@@ -31112,6 +31903,8 @@ var init_dist3 = __esm({
31112
31903
  return generateForEach(node, this.ctx, (nodes) => this.generateBody(nodes));
31113
31904
  case "while":
31114
31905
  return generateWhile(node, this.ctx, (nodes) => this.generateBody(nodes));
31906
+ case "sequence":
31907
+ return this.generateBody(node.commands);
31115
31908
  case "event": {
31116
31909
  const nested = node;
31117
31910
  return this.generateBody(nested.body ?? []);
@@ -31961,15 +32754,8 @@ var init_dist3 = __esm({
31961
32754
  * Create a codegen context.
31962
32755
  */
31963
32756
  createCodegenContext(handlerId, analysis, options) {
31964
- const selectorCache = /* @__PURE__ */ new Map();
31965
32757
  const requiredHelpers = /* @__PURE__ */ new Set();
31966
32758
  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
32759
  const exprCodegenRef = { current: null };
31974
32760
  const ctx = {
31975
32761
  handlerId,
@@ -31982,8 +32768,12 @@ var init_dist3 = __esm({
31982
32768
  },
31983
32769
  implicitTarget: "_ctx.me",
31984
32770
  localVarDeclarations: "",
31985
- canCacheSelector: (selector) => selectorCache.has(selector),
31986
- getCachedSelector: (selector) => selectorCache.get(selector) ?? `document.querySelector('${selector}')`,
32771
+ // A selector is queried each time it is evaluated, as on both engines. The
32772
+ // cache named a variable nothing declared (`_sel__d1_0`: a handler naming
32773
+ // #d1 twice threw), and a declared one goes stale when the handler
32774
+ // replaces the element.
32775
+ canCacheSelector: () => false,
32776
+ getCachedSelector: (selector) => `document.querySelector('${selector}')`,
31987
32777
  requireHelper: (name) => {
31988
32778
  requiredHelpers.add(name);
31989
32779
  },
@@ -32461,6 +33251,7 @@ function normalizeJSON(input) {
32461
33251
 
32462
33252
  // src/validation/inert-shapes.ts
32463
33253
  var QUANTIFIER = /^(all|every|each)\.(\S+)$/;
33254
+ var BARE_QUANTIFIER = /^(all|every|each)$/;
32464
33255
  var PREDICATE_AS_CLASS = /(^|\s)\.(has|is|contains|matches|includes)(\s|$)/;
32465
33256
  function valueString(v2) {
32466
33257
  return String(v2.value ?? v2.raw ?? "");
@@ -32487,6 +33278,15 @@ function walk(node, out) {
32487
33278
  });
32488
33279
  continue;
32489
33280
  }
33281
+ if (v2.type === "expression" && BARE_QUANTIFIER.test(s) && (role === "destination" || role === "source")) {
33282
+ out.push({
33283
+ severity: "warning",
33284
+ code: "INERT_QUANTIFIER_TARGET",
33285
+ 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.`,
33286
+ suggestion: `Selectors already address every match: write the selector alone, without "${s}".`
33287
+ });
33288
+ continue;
33289
+ }
32490
33290
  if (role === "condition" && v2.type === "expression" && PREDICATE_AS_CLASS.test(s)) {
32491
33291
  const word = PREDICATE_AS_CLASS.exec(s)?.[2];
32492
33292
  out.push({
@@ -32759,7 +33559,8 @@ function extractOperations(node) {
32759
33559
  }
32760
33560
  function extractEventHandler(n) {
32761
33561
  const eventRole = n.roles?.get("event");
32762
- const eventName = String(eventRole?.value ?? "click");
33562
+ const named = eventRole?.value ?? eventRole?.raw;
33563
+ const eventName = typeof named === "string" && named.length > 0 ? named : "click";
32763
33564
  const operations = [];
32764
33565
  if (n.body && Array.isArray(n.body)) {
32765
33566
  for (const child of n.body) {
@@ -34861,6 +35662,7 @@ function renderSvelteElement(el, analysis) {
34861
35662
  var CompilationService = class _CompilationService {
34862
35663
  constructor(options = {}) {
34863
35664
  this.translateFn = null;
35665
+ this.translationCollisionsFn = null;
34864
35666
  this.confidenceThreshold = options.confidenceThreshold ?? 0.7;
34865
35667
  this.cache = new SemanticCache(options.cacheSize ?? 500);
34866
35668
  this.testRenderers = new Map(
@@ -34915,6 +35717,7 @@ var CompilationService = class _CompilationService {
34915
35717
  compiler
34916
35718
  );
34917
35719
  service.translateFn = semantic.translate;
35720
+ service.translationCollisionsFn = semantic.findTranslationCollisions;
34918
35721
  return service;
34919
35722
  }
34920
35723
  // ===========================================================================
@@ -35039,7 +35842,7 @@ var CompilationService = class _CompilationService {
35039
35842
  ok: true,
35040
35843
  code: result,
35041
35844
  ...verification !== void 0 ? { verification } : {},
35042
- diagnostics: []
35845
+ diagnostics: this.translationCollisions(request)
35043
35846
  };
35044
35847
  } catch (error) {
35045
35848
  return {
@@ -35054,6 +35857,20 @@ var CompilationService = class _CompilationService {
35054
35857
  };
35055
35858
  }
35056
35859
  }
35860
+ /**
35861
+ * A variable of the source that the target language reads as a value word
35862
+ * (tl `ako` is `me`): the translation reads the value, and no spelling of
35863
+ * the name tells them apart, so the fix is a rename before translating.
35864
+ */
35865
+ translationCollisions(request) {
35866
+ const findings = this.translationCollisionsFn?.(request.code, request.from, request.to) ?? [];
35867
+ return findings.map((finding) => ({
35868
+ severity: "warning",
35869
+ code: "NAME_COLLISION",
35870
+ message: finding.message,
35871
+ 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.`
35872
+ }));
35873
+ }
35057
35874
  /**
35058
35875
  * Generate behavior-level tests from hyperscript.
35059
35876
  *
@@ -35295,7 +36112,27 @@ var CompilationService = class _CompilationService {
35295
36112
  if (!gateRef.pass || !gateCand.pass) {
35296
36113
  return { ok: false, diagnostics };
35297
36114
  }
35298
- return { ok: true, diagnostics, ...(0, import_fidelity.scoreNodes)(normRef.node, normCand.node) };
36115
+ const truncated = (...lists) => lists.some((ds) => ds.some((d) => d.code === "UNCONSUMED_INPUT"));
36116
+ const referenceComplete = !truncated(normRef.diagnostics, gateRef.diagnostics);
36117
+ const candidateComplete = !truncated(normCand.diagnostics, gateCand.diagnostics);
36118
+ if (!referenceComplete || !candidateComplete) {
36119
+ const sides = [!referenceComplete && "reference", !candidateComplete && "candidate"].filter(Boolean).join(" and ");
36120
+ diagnostics.push({
36121
+ severity: "warning",
36122
+ code: "INCOMPLETE_PARSE",
36123
+ message: `The ${sides} parse left tokens unconsumed, so the scores compare only what parsed; not faithful.`,
36124
+ 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."
36125
+ });
36126
+ }
36127
+ const report2 = (0, import_fidelity.scoreNodes)(normRef.node, normCand.node);
36128
+ return {
36129
+ ok: true,
36130
+ diagnostics,
36131
+ ...report2,
36132
+ faithful: report2.faithful && referenceComplete && candidateComplete,
36133
+ referenceComplete,
36134
+ candidateComplete
36135
+ };
35299
36136
  }
35300
36137
  /**
35301
36138
  * Generate a target-specific output from LSE (bracket syntax or protocol JSON).
@@ -35475,7 +36312,7 @@ function nodeToSemanticJSON(node) {
35475
36312
  const bodyJSON = nodeToSemanticJSON(n.body[0]);
35476
36313
  if (bodyJSON) {
35477
36314
  bodyJSON.trigger = {
35478
- event: eventRole?.value ?? "click",
36315
+ event: eventNameOf(eventRole),
35479
36316
  modifiers: n.eventModifiers
35480
36317
  };
35481
36318
  return bodyJSON;
@@ -35485,13 +36322,17 @@ function nodeToSemanticJSON(node) {
35485
36322
  action: n.action,
35486
36323
  roles,
35487
36324
  trigger: {
35488
- event: eventRole?.value ?? "click",
36325
+ event: eventNameOf(eventRole),
35489
36326
  modifiers: n.eventModifiers
35490
36327
  }
35491
36328
  };
35492
36329
  }
35493
36330
  return { action: n.action, roles };
35494
36331
  }
36332
+ function eventNameOf(eventRole) {
36333
+ const name = eventRole?.value ?? eventRole?.raw;
36334
+ return typeof name === "string" && name.length > 0 ? name : "click";
36335
+ }
35495
36336
 
35496
36337
  // src/middleware/body-limit.ts
35497
36338
  function bodyLimit(maxBytes = 50 * 1024) {