@hyperfixi/core 2.9.1 → 2.9.2

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.
Files changed (49) hide show
  1. package/dist/bundle-generator/index.js +6 -0
  2. package/dist/bundle-generator/index.mjs +6 -0
  3. package/dist/bundle-generator/parser-templates.d.ts +1 -1
  4. package/dist/chunks/{bridge-BYhqTOnL.js → bridge-DwtNLKK3.js} +2 -2
  5. package/dist/chunks/browser-modular-6HT1r2AH.js +2 -0
  6. package/dist/chunks/{index-DXgJaDj-.js → index-B8jwvuj_.js} +2 -2
  7. package/dist/commands/index.js +133 -74
  8. package/dist/commands/index.mjs +133 -74
  9. package/dist/hyperfixi-browser-classic-i18n.js +1 -1
  10. package/dist/hyperfixi-browser-minimal.js +1 -1
  11. package/dist/hyperfixi-browser-standard.js +1 -1
  12. package/dist/hyperfixi-browser.js +1 -1
  13. package/dist/hyperfixi-classic-i18n.js +1 -1
  14. package/dist/hyperfixi-hx-v4.js +1 -1
  15. package/dist/hyperfixi-hx.js +1 -1
  16. package/dist/hyperfixi-hybrid-complete.js +1 -1
  17. package/dist/hyperfixi-hybrid-hx.js +1 -1
  18. package/dist/hyperfixi-minimal.js +1 -1
  19. package/dist/hyperfixi-multilingual.js +1 -1
  20. package/dist/hyperfixi-standard.js +1 -1
  21. package/dist/hyperfixi.js +1 -1
  22. package/dist/hyperfixi.mjs +1 -1
  23. package/dist/index.js +233 -79
  24. package/dist/index.min.js +1 -1
  25. package/dist/index.mjs +233 -79
  26. package/dist/lokascript-browser-classic-i18n.js +1 -1
  27. package/dist/lokascript-browser-minimal.js +1 -1
  28. package/dist/lokascript-browser-standard.js +1 -1
  29. package/dist/lokascript-browser.js +1 -1
  30. package/dist/lokascript-hybrid-complete.js +1 -1
  31. package/dist/lokascript-hybrid-hx.js +1 -1
  32. package/dist/lokascript-multilingual.js +1 -1
  33. package/dist/parser/command-parsers/utility-commands.d.ts +1 -1
  34. package/dist/parser/full-parser.js +133 -74
  35. package/dist/parser/full-parser.mjs +133 -74
  36. package/dist/parser/hybrid/index.js +3 -0
  37. package/dist/parser/hybrid/index.mjs +3 -0
  38. package/dist/parser/hybrid/parser-core.js +3 -0
  39. package/dist/parser/hybrid/parser-core.mjs +3 -0
  40. package/dist/parser/hybrid-parser.js +3 -0
  41. package/dist/parser/hybrid-parser.mjs +3 -0
  42. package/dist/reference/index.js +1 -0
  43. package/dist/reference/index.mjs +1 -0
  44. package/dist/registry/index.js +133 -74
  45. package/dist/registry/index.mjs +133 -74
  46. package/dist/runtime/runtime-base.d.ts +2 -1
  47. package/dist/types/core.d.ts +1 -0
  48. package/package.json +6 -6
  49. package/dist/chunks/browser-modular-D2BHzZFB.js +0 -2
@@ -1,2 +1,2 @@
1
- export{O as ObjectPool,P as Parser,R as Runtime,d as debug,h as default,a as defaultAttributeProcessor,b as detectFeatures,e as evalHyperScript,c as evalHyperScriptAsync,f as evalHyperScriptSmart,g as getLoadedFeatures,i as hyperscript,j as isFeatureLoaded,l as loadRequiredFeatures,p as preloadDocumentFeatures,k as preloadFeatures,s as styleBatcher,t as tokenize}from"./chunks/browser-modular-D2BHzZFB.js";import"./chunks/feature-sockets-CcTxD5sT.js";
1
+ export{O as ObjectPool,P as Parser,R as Runtime,d as debug,h as default,a as defaultAttributeProcessor,b as detectFeatures,e as evalHyperScript,c as evalHyperScriptAsync,f as evalHyperScriptSmart,g as getLoadedFeatures,i as hyperscript,j as isFeatureLoaded,l as loadRequiredFeatures,p as preloadDocumentFeatures,k as preloadFeatures,s as styleBatcher,t as tokenize}from"./chunks/browser-modular-6HT1r2AH.js";import"./chunks/feature-sockets-CcTxD5sT.js";
2
2
  //# sourceMappingURL=hyperfixi.mjs.map
package/dist/index.js CHANGED
@@ -516,13 +516,13 @@ function tokenize$1(input) {
516
516
  continue;
517
517
  }
518
518
  if (char === '{') {
519
- addToken(tokenizer, TokenKind.OPERATOR, '{');
520
519
  advance(tokenizer);
520
+ addToken(tokenizer, TokenKind.OPERATOR, '{');
521
521
  continue;
522
522
  }
523
523
  if (char === '}') {
524
- addToken(tokenizer, TokenKind.OPERATOR, '}');
525
524
  advance(tokenizer);
525
+ addToken(tokenizer, TokenKind.OPERATOR, '}');
526
526
  continue;
527
527
  }
528
528
  if (char === '[') {
@@ -538,24 +538,24 @@ function tokenize$1(input) {
538
538
  prevToken.value === ']') &&
539
539
  !isEventCondition;
540
540
  if (isMemberAccess) {
541
- addToken(tokenizer, TokenKind.OPERATOR, '[');
542
541
  advance(tokenizer);
542
+ addToken(tokenizer, TokenKind.OPERATOR, '[');
543
543
  }
544
544
  else {
545
545
  if (isEventCondition) {
546
- addToken(tokenizer, TokenKind.SYMBOL, '[');
547
546
  advance(tokenizer);
547
+ addToken(tokenizer, TokenKind.SYMBOL, '[');
548
548
  }
549
549
  else {
550
- addToken(tokenizer, TokenKind.OPERATOR, '[');
551
550
  advance(tokenizer);
551
+ addToken(tokenizer, TokenKind.OPERATOR, '[');
552
552
  }
553
553
  }
554
554
  continue;
555
555
  }
556
556
  if (char === ']') {
557
- addToken(tokenizer, TokenKind.OPERATOR, ']');
558
557
  advance(tokenizer);
558
+ addToken(tokenizer, TokenKind.OPERATOR, ']');
559
559
  continue;
560
560
  }
561
561
  if (char === '$') {
@@ -2982,7 +2982,6 @@ const MULTI_WORD_PATTERNS = [
2982
2982
  keywords: ['a', 'an', 'from', 'called'],
2983
2983
  syntax: 'make (a|an) <type> [from <args>] [called <name>]',
2984
2984
  },
2985
- { command: 'send', keywords: ['to'], syntax: 'send <event> to <target>' },
2986
2985
  { command: 'throw', keywords: [], syntax: 'throw <error>' },
2987
2986
  ];
2988
2987
  function getMultiWordPattern(commandName) {
@@ -3283,7 +3282,13 @@ function parseTriggerCommand(ctx, identifierNode) {
3283
3282
  finalArgs.push(ctx.createIdentifier(keyword));
3284
3283
  continue;
3285
3284
  }
3286
- finalArgs.push(ctx.parsePrimary());
3285
+ const before = ctx.savePosition();
3286
+ const expr = ctx.parsePrimary();
3287
+ if (expr)
3288
+ finalArgs.push(expr);
3289
+ if (ctx.savePosition() === before) {
3290
+ ctx.advance();
3291
+ }
3287
3292
  }
3288
3293
  return CommandNodeBuilder.fromIdentifier(identifierNode)
3289
3294
  .withArgs(...finalArgs)
@@ -3502,11 +3507,64 @@ function parseRepeatCommand(ctx, commandToken) {
3502
3507
  }
3503
3508
  return builder.endingAt(ctx.getPosition()).build();
3504
3509
  }
3510
+ function parseIfBranchCommands(ctx, stopAtElse) {
3511
+ const commands = [];
3512
+ const atTerminator = () => ctx.check(KEYWORDS$1.END) || (stopAtElse && ctx.check(KEYWORDS$1.ELSE));
3513
+ while (!ctx.isAtEnd() && !atTerminator()) {
3514
+ if (isComment(ctx.peek())) {
3515
+ ctx.advance();
3516
+ continue;
3517
+ }
3518
+ if (!ctx.checkIsCommand() && !ctx.isCommand(ctx.peek().value))
3519
+ break;
3520
+ ctx.advance();
3521
+ const cmd = ctx.parseCommand();
3522
+ if (cmd) {
3523
+ commands.push(cmd);
3524
+ }
3525
+ if (ctx.check(KEYWORDS$1.THEN) || ctx.check(KEYWORDS$1.AND) || ctx.check(',')) {
3526
+ ctx.advance();
3527
+ }
3528
+ }
3529
+ return commands;
3530
+ }
3531
+ const OPERAND_INTRODUCERS = new Set([
3532
+ 'is',
3533
+ 'am',
3534
+ 'are',
3535
+ 'not',
3536
+ 'no',
3537
+ 'and',
3538
+ 'or',
3539
+ '==',
3540
+ '===',
3541
+ '!=',
3542
+ '!==',
3543
+ '<',
3544
+ '>',
3545
+ '<=',
3546
+ '>=',
3547
+ '+',
3548
+ '-',
3549
+ '*',
3550
+ '/',
3551
+ 'mod',
3552
+ ]);
3553
+ function isBodyCommandStart(ctx, prevToken, token) {
3554
+ if (prevToken === null) {
3555
+ return false;
3556
+ }
3557
+ if (OPERAND_INTRODUCERS.has(prevToken.value?.toLowerCase?.() ?? '')) {
3558
+ return false;
3559
+ }
3560
+ return ctx.checkIsCommand() || ctx.isCommand(token.value?.toLowerCase());
3561
+ }
3505
3562
  function parseIfCommand(ctx, commandToken) {
3506
3563
  const args = [];
3507
3564
  let hasThen = false;
3508
3565
  const savedPosForThen = ctx.savePosition();
3509
3566
  const maxThenLookahead = 500;
3567
+ let prevForThen = null;
3510
3568
  for (let i = 0; i < maxThenLookahead && !ctx.isAtEnd(); i++) {
3511
3569
  const token = ctx.peek();
3512
3570
  if (token.value === KEYWORDS$1.THEN) {
@@ -3519,6 +3577,14 @@ function parseIfCommand(ctx, commandToken) {
3519
3577
  token.value === KEYWORDS$1.ON) {
3520
3578
  break;
3521
3579
  }
3580
+ if (token.line !== undefined &&
3581
+ token.line !== commandToken.line &&
3582
+ isBodyCommandStart(ctx, prevForThen, token)) {
3583
+ break;
3584
+ }
3585
+ if (!isComment(token)) {
3586
+ prevForThen = token;
3587
+ }
3522
3588
  ctx.advance();
3523
3589
  }
3524
3590
  ctx.restorePosition(savedPosForThen);
@@ -3527,9 +3593,14 @@ function parseIfCommand(ctx, commandToken) {
3527
3593
  const savedPosition = ctx.savePosition();
3528
3594
  const ifStatementLine = commandToken.line;
3529
3595
  const maxLookahead = 100;
3596
+ let firstCommandOnIfLine = false;
3597
+ let prevToken = null;
3530
3598
  while (!ctx.isAtEnd() && ctx.current - savedPosition < maxLookahead) {
3531
3599
  const token = ctx.peek();
3532
3600
  const tokenValue = token.value?.toLowerCase();
3601
+ if (firstCommandOnIfLine && token.line !== undefined && token.line !== ifStatementLine) {
3602
+ break;
3603
+ }
3533
3604
  if (tokenValue === KEYWORDS$1.BEHAVIOR ||
3534
3605
  tokenValue === KEYWORDS$1.DEF ||
3535
3606
  tokenValue === KEYWORDS$1.ON) {
@@ -3541,11 +3612,15 @@ function parseIfCommand(ctx, commandToken) {
3541
3612
  }
3542
3613
  break;
3543
3614
  }
3544
- if (ctx.checkIsCommand() || ctx.isCommand(tokenValue)) {
3615
+ if (isBodyCommandStart(ctx, prevToken, token)) {
3545
3616
  if (token.line !== undefined && token.line !== ifStatementLine) {
3546
3617
  hasImplicitMultiLineEnd = true;
3547
3618
  break;
3548
3619
  }
3620
+ firstCommandOnIfLine = true;
3621
+ }
3622
+ if (!isComment(token)) {
3623
+ prevToken = token;
3549
3624
  }
3550
3625
  ctx.advance();
3551
3626
  }
@@ -3560,11 +3635,12 @@ function parseIfCommand(ctx, commandToken) {
3560
3635
  const conditionTokens = [];
3561
3636
  const maxIterations = 20;
3562
3637
  let iterations = 0;
3638
+ let firstConditionParse = true;
3563
3639
  while (!ctx.isAtEnd() &&
3564
- !ctx.checkIsCommand() &&
3565
- !ctx.isCommand(ctx.peek().value) &&
3566
3640
  !ctx.check(KEYWORDS$1.THEN) &&
3641
+ (firstConditionParse || (!ctx.checkIsCommand() && !ctx.isCommand(ctx.peek().value))) &&
3567
3642
  iterations < maxIterations) {
3643
+ firstConditionParse = false;
3568
3644
  const beforePos = ctx.savePosition();
3569
3645
  conditionTokens.push(ctx.parseLogicalAnd());
3570
3646
  if (ctx.savePosition() === beforePos) {
@@ -3591,22 +3667,8 @@ function parseIfCommand(ctx, commandToken) {
3591
3667
  }
3592
3668
  args.push(condition);
3593
3669
  if (isMultiLine) {
3594
- if (hasThen) {
3595
- ctx.advance();
3596
- }
3597
- const thenCommands = [];
3598
- while (!ctx.isAtEnd() && !ctx.check(KEYWORDS$1.ELSE) && !ctx.check(KEYWORDS$1.END)) {
3599
- if (ctx.checkIsCommand() || ctx.isCommand(ctx.peek().value)) {
3600
- ctx.advance();
3601
- const cmd = ctx.parseCommand();
3602
- if (cmd) {
3603
- thenCommands.push(cmd);
3604
- }
3605
- }
3606
- else {
3607
- break;
3608
- }
3609
- }
3670
+ consumeOptionalKeyword(ctx, KEYWORDS$1.THEN);
3671
+ const thenCommands = parseIfBranchCommands(ctx, true);
3610
3672
  if (thenCommands.length === 0 && ctx.isAtEnd()) {
3611
3673
  throw new Error("Expected command after 'then' in if statement - incomplete conditional");
3612
3674
  }
@@ -3632,19 +3694,7 @@ function parseIfCommand(ctx, commandToken) {
3632
3694
  consumedElseIf = true;
3633
3695
  }
3634
3696
  else {
3635
- const elseCommands = [];
3636
- while (!ctx.isAtEnd() && !ctx.check(KEYWORDS$1.END)) {
3637
- if (ctx.checkIsCommand() || ctx.isCommand(ctx.peek().value)) {
3638
- ctx.advance();
3639
- const cmd = ctx.parseCommand();
3640
- if (cmd) {
3641
- elseCommands.push(cmd);
3642
- }
3643
- }
3644
- else {
3645
- break;
3646
- }
3647
- }
3697
+ const elseCommands = parseIfBranchCommands(ctx, false);
3648
3698
  args.push(createBlock(elseCommands, {
3649
3699
  start: commandToken.start,
3650
3700
  end: ctx.getPosition().end,
@@ -4466,21 +4516,6 @@ const GO_KEYWORDS = new Set([
4466
4516
  'back',
4467
4517
  'forward',
4468
4518
  ]);
4469
- const GO_URL_STOP = new Set([
4470
- 'in',
4471
- 'then',
4472
- 'and',
4473
- 'else',
4474
- 'end',
4475
- 'otherwise',
4476
- 'when',
4477
- 'where',
4478
- 'catch',
4479
- 'finally',
4480
- ]);
4481
- function isGoURLTerminator(ctx) {
4482
- return ctx.isAtEnd() || GO_URL_STOP.has(ctx.peek().value);
4483
- }
4484
4519
  function stringNode(value, tok) {
4485
4520
  return {
4486
4521
  type: 'string',
@@ -4502,7 +4537,7 @@ function parseGoCommand(ctx, identifierNode) {
4502
4537
  continue;
4503
4538
  }
4504
4539
  if (isNakedURLStart(ctx)) {
4505
- const url = parseBareURLPath(ctx, isGoURLTerminator);
4540
+ const url = parseBareURLPath(ctx);
4506
4541
  if (url) {
4507
4542
  args.push(url);
4508
4543
  continue;
@@ -4642,13 +4677,6 @@ function parseMultiWordCommand(ctx, commandToken, commandName) {
4642
4677
  }
4643
4678
  return builder.build();
4644
4679
  }
4645
- function isFetchURLTerminator(ctx) {
4646
- if (ctx.isAtEnd())
4647
- return true;
4648
- const val = ctx.peek().value;
4649
- return (['as', 'with', '{', 'then', 'end', 'else', 'otherwise', ')'].includes(val) ||
4650
- isCommandBoundary(ctx));
4651
- }
4652
4680
  function isNakedURLStart(ctx) {
4653
4681
  const tok = ctx.peek();
4654
4682
  if (tok.value === '/')
@@ -4659,23 +4687,50 @@ function isNakedURLStart(ctx) {
4659
4687
  }
4660
4688
  return false;
4661
4689
  }
4662
- function parseBareURLPath(ctx, isTerminator = isFetchURLTerminator) {
4690
+ const URL_HARD_STOPS = new Set(['{', ')']);
4691
+ function parseBareURLPath(ctx) {
4663
4692
  const startPos = ctx.savePosition();
4693
+ const firstTok = ctx.peek();
4664
4694
  let path = '';
4665
- while (!ctx.isAtEnd() && !isTerminator(ctx)) {
4666
- path += ctx.advance().value;
4695
+ let prev = null;
4696
+ while (!ctx.isAtEnd()) {
4697
+ const tok = ctx.peek();
4698
+ if (prev && tok.start !== prev.end)
4699
+ break;
4700
+ if (tok.value === '$' && ctx.peekAt(1)?.value === '{') {
4701
+ ctx.advance();
4702
+ let last = ctx.advance();
4703
+ let depth = 1;
4704
+ while (!ctx.isAtEnd() && depth > 0) {
4705
+ last = ctx.advance();
4706
+ if (last.value === '{')
4707
+ depth++;
4708
+ else if (last.value === '}')
4709
+ depth--;
4710
+ }
4711
+ path += ctx.getInputSlice(tok.start, last.end);
4712
+ prev = last;
4713
+ continue;
4714
+ }
4715
+ if (URL_HARD_STOPS.has(tok.value))
4716
+ break;
4717
+ prev = ctx.advance();
4718
+ path += prev.value;
4667
4719
  }
4668
4720
  if (!path || path === '/') {
4669
4721
  ctx.restorePosition(startPos);
4670
4722
  return null;
4671
4723
  }
4672
- return {
4673
- type: 'literal',
4674
- value: path,
4675
- raw: path,
4676
- start: startPos,
4677
- end: ctx.savePosition(),
4724
+ const span = {
4725
+ start: firstTok.start,
4726
+ end: prev ? prev.end : firstTok.end,
4727
+ line: firstTok.line,
4728
+ column: firstTok.column,
4678
4729
  };
4730
+ if (path.includes('${')) {
4731
+ return { type: 'templateLiteral', value: path, ...span };
4732
+ }
4733
+ return { type: 'literal', value: path, raw: path, ...span };
4679
4734
  }
4680
4735
  function parseFetchCommand(ctx, commandToken) {
4681
4736
  const modifiers = {};
@@ -4906,10 +4961,10 @@ function parseTellCommand(ctx, identifierNode) {
4906
4961
  catch {
4907
4962
  break;
4908
4963
  }
4909
- if (ctx.match(KEYWORDS$1.AND)) {
4964
+ if (ctx.match(KEYWORDS$1.AND) || ctx.match(KEYWORDS$1.THEN)) {
4910
4965
  continue;
4911
4966
  }
4912
- if (ctx.check(KEYWORDS$1.THEN) || ctx.check(KEYWORDS$1.ELSE) || ctx.check(KEYWORDS$1.END)) {
4967
+ if (ctx.check(KEYWORDS$1.ELSE) || ctx.check(KEYWORDS$1.END)) {
4913
4968
  break;
4914
4969
  }
4915
4970
  if (ctx.checkIsCommand()) {
@@ -5112,6 +5167,7 @@ class Parser {
5112
5167
  const result = this.parseInternal();
5113
5168
  if (this.errors.length > 0) {
5114
5169
  result.errors = [...this.errors];
5170
+ result.recovered = true;
5115
5171
  }
5116
5172
  return result;
5117
5173
  }
@@ -7143,6 +7199,9 @@ class Parser {
7143
7199
  this.advance();
7144
7200
  const cmd = this.parseCommand();
7145
7201
  commands.push(cmd);
7202
+ if (this.match('then', 'and', ',')) {
7203
+ continue;
7204
+ }
7146
7205
  }
7147
7206
  else {
7148
7207
  break;
@@ -13049,6 +13108,25 @@ function isImplicitCommand(val) {
13049
13108
  return val !== null && typeof val === 'object' && 'command' in val && 'selector' in val;
13050
13109
  }
13051
13110
  const eventRecursionDepth = new WeakMap();
13111
+ function collectIdentifierNames(node, out = new Set()) {
13112
+ if (node === null || typeof node !== 'object')
13113
+ return out;
13114
+ if (Array.isArray(node)) {
13115
+ for (const item of node)
13116
+ collectIdentifierNames(item, out);
13117
+ return out;
13118
+ }
13119
+ const obj = node;
13120
+ if (obj.type === 'identifier' && typeof obj.name === 'string') {
13121
+ out.add(obj.name);
13122
+ }
13123
+ for (const key of Object.keys(obj)) {
13124
+ if (key === 'start' || key === 'end' || key === 'line' || key === 'column')
13125
+ continue;
13126
+ collectIdentifierNames(obj[key], out);
13127
+ }
13128
+ return out;
13129
+ }
13052
13130
  class RuntimeBase {
13053
13131
  constructor(options) {
13054
13132
  this.autoCleanupObserver = null;
@@ -13240,6 +13318,9 @@ class RuntimeBase {
13240
13318
  case 'behavior': {
13241
13319
  return await this.executeBehaviorDefinition(node, context);
13242
13320
  }
13321
+ case 'def': {
13322
+ return this.installFunction(node, context);
13323
+ }
13243
13324
  case 'Program': {
13244
13325
  return await this.executeProgram(node, context);
13245
13326
  }
@@ -13455,17 +13536,64 @@ class RuntimeBase {
13455
13536
  };
13456
13537
  return this.processCommand(commandNode, context);
13457
13538
  }
13539
+ installFunction(node, context) {
13540
+ const runtime = this;
13541
+ const params = node.params ?? [];
13542
+ const body = (node.body ?? []);
13543
+ const errorHandler = node.errorHandler;
13544
+ const finallyHandler = node.finallyHandler;
13545
+ const errorSymbol = node.errorSymbol;
13546
+ const fn = async (...args) => {
13547
+ const fnContext = {
13548
+ ...context,
13549
+ locals: new Map(context.locals),
13550
+ };
13551
+ params.forEach((name, i) => {
13552
+ if (name)
13553
+ fnContext.locals.set(name, args[i]);
13554
+ });
13555
+ const run = async (commands) => {
13556
+ const result = await runtime.executeCommandSequenceWithResult(commands, fnContext);
13557
+ if (!isOk(result))
13558
+ throw asControlFlowError(result.error);
13559
+ return result.value;
13560
+ };
13561
+ if (!errorHandler && !finallyHandler) {
13562
+ return await run(body);
13563
+ }
13564
+ try {
13565
+ return await run(body);
13566
+ }
13567
+ catch (e) {
13568
+ if (!errorHandler || isControlFlowError(e))
13569
+ throw e;
13570
+ if (errorSymbol)
13571
+ fnContext.locals.set(errorSymbol, e);
13572
+ return await run(errorHandler);
13573
+ }
13574
+ finally {
13575
+ if (finallyHandler) {
13576
+ await run(finallyHandler);
13577
+ }
13578
+ }
13579
+ };
13580
+ context.globals.set(node.name, fn);
13581
+ }
13458
13582
  async executeProgram(node, context) {
13459
13583
  if (!node.statements || !Array.isArray(node.statements))
13460
13584
  return;
13461
13585
  let lastResult = undefined;
13462
13586
  const eventHandlers = [];
13587
+ const defs = [];
13463
13588
  const initBlocks = [];
13464
13589
  const otherStatements = [];
13465
13590
  for (const statement of node.statements) {
13466
13591
  if (statement.type === 'eventHandler') {
13467
13592
  eventHandlers.push(statement);
13468
13593
  }
13594
+ else if (statement.type === 'def') {
13595
+ defs.push(statement);
13596
+ }
13469
13597
  else if (statement.type === 'initBlock') {
13470
13598
  initBlocks.push(statement);
13471
13599
  }
@@ -13473,6 +13601,9 @@ class RuntimeBase {
13473
13601
  otherStatements.push(statement);
13474
13602
  }
13475
13603
  }
13604
+ for (const def of defs) {
13605
+ this.execute(def, context);
13606
+ }
13476
13607
  for (const handler of eventHandlers) {
13477
13608
  try {
13478
13609
  await this.execute(handler, context);
@@ -13668,7 +13799,7 @@ class RuntimeBase {
13668
13799
  debug.runtime(`BEHAVIOR: Finished installing ${behaviorName}`);
13669
13800
  }
13670
13801
  async executeEventHandler(node, context) {
13671
- const { event, events, commands, target, args, selector, attributeName, watchTarget, modifiers, errorSymbol, errorHandler, finallyHandler, } = node;
13802
+ const { event, events, commands, target, args, selector, condition, attributeName, watchTarget, modifiers, errorSymbol, errorHandler, finallyHandler, } = node;
13672
13803
  const eventNames = events && events.length > 0 ? events : [event];
13673
13804
  debug.runtime(`BEHAVIOR: executeEventHandler: event='${event}', target='${target}'`);
13674
13805
  let targets = [];
@@ -13751,7 +13882,8 @@ class RuntimeBase {
13751
13882
  }
13752
13883
  return;
13753
13884
  }
13754
- const baseEventHandler = RuntimeBase.createEventHandler(this, commands, context, selector, args, { errorSymbol, errorHandler, finallyHandler });
13885
+ const applicableCondition = eventNames.length === 1 ? condition : undefined;
13886
+ const baseEventHandler = RuntimeBase.createEventHandler(this, commands, context, selector, args, { errorSymbol, errorHandler, finallyHandler }, applicableCondition);
13755
13887
  let eventHandler;
13756
13888
  let debounceCleanup = null;
13757
13889
  if (modifiers) {
@@ -13855,7 +13987,7 @@ class RuntimeBase {
13855
13987
  }
13856
13988
  }
13857
13989
  }
13858
- static createEventHandler(runtime, commands, context, selector, args, errorBlocks) {
13990
+ static createEventHandler(runtime, commands, context, selector, args, errorBlocks, condition) {
13859
13991
  return async (domEvent) => {
13860
13992
  const currentDepth = eventRecursionDepth.get(domEvent) ?? 0;
13861
13993
  if (currentDepth >= 100) {
@@ -13891,6 +14023,25 @@ class RuntimeBase {
13891
14023
  eventContext.locals.set(argName, value);
13892
14024
  }
13893
14025
  }
14026
+ if (condition) {
14027
+ const eventProps = domEvent;
14028
+ for (const name of collectIdentifierNames(condition)) {
14029
+ if (!eventContext.locals.has(name) && name in eventProps) {
14030
+ eventContext.locals.set(name, eventProps[name]);
14031
+ }
14032
+ }
14033
+ let filterResult;
14034
+ try {
14035
+ filterResult = await runtime.evaluateExpression(condition, eventContext);
14036
+ }
14037
+ catch (e) {
14038
+ debug.runtime(`Event filter threw; skipping handler body:`, e);
14039
+ return;
14040
+ }
14041
+ if (!filterResult) {
14042
+ return;
14043
+ }
14044
+ }
13894
14045
  const runCommands = async (toRun) => {
13895
14046
  for (const command of toRun) {
13896
14047
  try {
@@ -80809,9 +80960,11 @@ function compileSync(code, options) {
80809
80960
  const parseResult = parse$1(code, parserOptions);
80810
80961
  const timeMs = performance.now() - startTime;
80811
80962
  if (parseResult.success && parseResult.node) {
80963
+ const recovered = parseResult.errors ?? [];
80812
80964
  const result = {
80813
80965
  ok: true,
80814
80966
  ast: parseResult.node,
80967
+ ...(recovered.length > 0 ? { errors: recovered.map(toCompileError) } : {}),
80815
80968
  meta: {
80816
80969
  parser: usesSemanticParser ? 'semantic' : 'traditional',
80817
80970
  language: lang,
@@ -80937,9 +81090,10 @@ async function evalCode(code, context, options) {
80937
81090
  }
80938
81091
  async function validate(code, options) {
80939
81092
  const result = await compileAsync(code, options);
81093
+ const errors = result.errors ?? [];
80940
81094
  return {
80941
- valid: result.ok,
80942
- errors: result.errors,
81095
+ valid: result.ok && errors.length === 0,
81096
+ errors: errors.length > 0 ? errors : undefined,
80943
81097
  };
80944
81098
  }
80945
81099
  function createContextWithParent(element, parent) {