@hyperfixi/core 2.8.0 → 2.9.1

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 (50) hide show
  1. package/CHANGELOG.md +52 -0
  2. package/dist/ast-utils/index.js +1201 -891
  3. package/dist/ast-utils/index.mjs +1201 -891
  4. package/dist/chunks/{bridge-DHj-SYm2.js → bridge-BYhqTOnL.js} +2 -2
  5. package/dist/chunks/browser-modular-D2BHzZFB.js +2 -0
  6. package/dist/chunks/feature-eventsource-Bxg_UNZo.js +2 -0
  7. package/dist/chunks/feature-sockets-CcTxD5sT.js +2 -0
  8. package/dist/chunks/feature-webworker-DoqZ0Grg.js +2 -0
  9. package/dist/chunks/{index-CuPeasRm.js → index-DXgJaDj-.js} +2 -2
  10. package/dist/commands/index.js +168 -112
  11. package/dist/commands/index.mjs +168 -112
  12. package/dist/hyperfixi-browser-classic-i18n.js +1 -1
  13. package/dist/hyperfixi-browser-minimal.js +1 -1
  14. package/dist/hyperfixi-browser-standard.js +1 -1
  15. package/dist/hyperfixi-browser.js +1 -1
  16. package/dist/hyperfixi-classic-i18n.js +1 -1
  17. package/dist/hyperfixi-hx-v4.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 +2119 -1538
  24. package/dist/index.min.js +1 -1
  25. package/dist/index.mjs +2119 -1538
  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-multilingual.js +1 -1
  31. package/dist/lsp-metadata.js +5 -5
  32. package/dist/lsp-metadata.mjs +5 -5
  33. package/dist/metadata.js +25 -25
  34. package/dist/metadata.mjs +25 -25
  35. package/dist/multilingual/index.js +17 -17
  36. package/dist/multilingual/index.mjs +17 -17
  37. package/dist/parser/command-parsers/utility-commands.d.ts +1 -0
  38. package/dist/parser/full-parser.js +155 -106
  39. package/dist/parser/full-parser.mjs +155 -106
  40. package/dist/parser/parser.d.ts +1 -0
  41. package/dist/reference/index.js +1 -0
  42. package/dist/reference/index.mjs +1 -0
  43. package/dist/registry/index.js +155 -106
  44. package/dist/registry/index.mjs +155 -106
  45. package/dist/types/base-types.d.ts +3 -0
  46. package/package.json +7 -6
  47. package/dist/chunks/browser-modular-D1m0Eikh.js +0 -2
  48. package/dist/chunks/feature-eventsource-BpZvPy_K.js +0 -2
  49. package/dist/chunks/feature-sockets-CrYvjZ4j.js +0 -2
  50. package/dist/chunks/feature-webworker-BSYguEIW.js +0 -2
@@ -153,7 +153,7 @@ function resolveElement(target, context, commandName) {
153
153
  if (!target) {
154
154
  const me = context.me;
155
155
  if (!me) {
156
- throw new Error(withCmd(commandName, 'No target element — provide explicit target or ensure context.me is set'));
156
+ throw new Error(withCmd(commandName, 'No target element \u2014 provide explicit target or ensure context.me is set'));
157
157
  }
158
158
  return asHTMLElement(me);
159
159
  }
@@ -179,7 +179,7 @@ function resolveElement(target, context, commandName) {
179
179
  }
180
180
  const doc = context.me?.ownerDocument ?? (typeof document !== 'undefined' ? document : null);
181
181
  if (!doc) {
182
- throw new Error(withCmd(commandName, 'DOM not available — cannot resolve element selector'));
182
+ throw new Error(withCmd(commandName, 'DOM not available \u2014 cannot resolve element selector'));
183
183
  }
184
184
  const element = doc.querySelector(trimmed);
185
185
  if (!element) {
@@ -635,39 +635,39 @@ const DEBUG = {
635
635
  const debug = {
636
636
  command: (...args) => {
637
637
  if (DEBUG.commands)
638
- console.log('🔧', ...args);
638
+ console.log('\ud83d\udd27', ...args);
639
639
  },
640
640
  event: (...args) => {
641
641
  if (DEBUG.events)
642
- console.log('🎯', ...args);
642
+ console.log('\ud83c\udfaf', ...args);
643
643
  },
644
644
  parse: (...args) => {
645
645
  if (DEBUG.parsing)
646
- console.log('📝', ...args);
646
+ console.log('\ud83d\udcdd', ...args);
647
647
  },
648
648
  expr: (...args) => {
649
649
  if (DEBUG.expressions)
650
- console.log('🔍', ...args);
650
+ console.log('\ud83d\udd0d', ...args);
651
651
  },
652
652
  expressions: (...args) => {
653
653
  if (DEBUG.expressions)
654
- console.log('🔍', ...args);
654
+ console.log('\ud83d\udd0d', ...args);
655
655
  },
656
656
  style: (...args) => {
657
657
  if (DEBUG.styles)
658
- console.log('🎨', ...args);
658
+ console.log('\ud83c\udfa8', ...args);
659
659
  },
660
660
  runtime: (...args) => {
661
661
  if (DEBUG.runtime)
662
- console.log('🚀', ...args);
662
+ console.log('\ud83d\ude80', ...args);
663
663
  },
664
664
  loop: (...args) => {
665
665
  if (DEBUG.loops)
666
- console.log('🔁', ...args);
666
+ console.log('\ud83d\udd01', ...args);
667
667
  },
668
668
  async: (...args) => {
669
669
  if (DEBUG.async)
670
- console.log('⏳', ...args);
670
+ console.log('\u23f3', ...args);
671
671
  },
672
672
  };
673
673
 
@@ -6220,6 +6220,7 @@ function tokenize(input) {
6220
6220
  const prevToken = tokenizer.tokens[tokenizer.tokens.length - 1];
6221
6221
  const isPossessive = nextChar === 's' &&
6222
6222
  prevToken &&
6223
+ !/\s/.test(tokenizer.input[tokenizer.position - 1] ?? ' ') &&
6223
6224
  (prevToken.kind === TokenKind.IDENTIFIER ||
6224
6225
  prevToken.kind === TokenKind.SELECTOR ||
6225
6226
  prevToken.value === ']' ||
@@ -7944,9 +7945,9 @@ function createPartialCommandNode(name, pos) {
7944
7945
  };
7945
7946
  }
7946
7947
  function createProgramNode(statements) {
7947
- debug.parse(`✅ createProgramNode: Called with ${statements.length} statements`);
7948
+ debug.parse(`\u2705 createProgramNode: Called with ${statements.length} statements`);
7948
7949
  if (statements.length === 0) {
7949
- debug.parse(`✅ createProgramNode: Returning error node (0 statements)`);
7950
+ debug.parse(`\u2705 createProgramNode: Returning error node (0 statements)`);
7950
7951
  return {
7951
7952
  type: 'identifier',
7952
7953
  name: '__ERROR__',
@@ -7957,7 +7958,7 @@ function createProgramNode(statements) {
7957
7958
  };
7958
7959
  }
7959
7960
  if (statements.length === 1) {
7960
- debug.parse(`✅ createProgramNode: Returning single statement (type=${statements[0].type})`);
7961
+ debug.parse(`\u2705 createProgramNode: Returning single statement (type=${statements[0].type})`);
7961
7962
  return statements[0];
7962
7963
  }
7963
7964
  const programNode = {
@@ -7968,7 +7969,7 @@ function createProgramNode(statements) {
7968
7969
  line: statements[0]?.line || 1,
7969
7970
  column: statements[0]?.column || 1,
7970
7971
  };
7971
- debug.parse(`✅ createProgramNode: Returning Program node with ${statements.length} statements, type=${programNode.type}`);
7972
+ debug.parse(`\u2705 createProgramNode: Returning Program node with ${statements.length} statements, type=${programNode.type}`);
7972
7973
  return programNode;
7973
7974
  }
7974
7975
 
@@ -8357,37 +8358,37 @@ function parseRepeatCommand(ctx, commandToken) {
8357
8358
  ctx.advance();
8358
8359
  loopType = 'until-event';
8359
8360
  const eventToken = ctx.peek();
8360
- debug.parse('📍 Parsing event name, current token:', {
8361
+ debug.parse('\ud83d\udccd Parsing event name, current token:', {
8361
8362
  value: eventToken.value,
8362
8363
  kind: eventToken.kind,
8363
8364
  });
8364
8365
  if (isIdentifierLike(eventToken) || isEvent(eventToken)) {
8365
8366
  eventName = eventToken.value;
8366
8367
  ctx.advance();
8367
- debug.parse('✅ Got event name:', eventName, 'Next token:', ctx.peek().value);
8368
+ debug.parse('\u2705 Got event name:', eventName, 'Next token:', ctx.peek().value);
8368
8369
  }
8369
8370
  else {
8370
8371
  throw new Error('Expected event name after "event"');
8371
8372
  }
8372
- debug.parse('🔍 Checking for "from", current token:', ctx.peek().value);
8373
+ debug.parse('\ud83d\udd0d Checking for "from", current token:', ctx.peek().value);
8373
8374
  if (ctx.check(KEYWORDS$1.FROM)) {
8374
- debug.parse('✅ Found "from", advancing...');
8375
+ debug.parse('\u2705 Found "from", advancing...');
8375
8376
  ctx.advance();
8376
- debug.parse('📍 After consuming "from", current token:', ctx.peek().value);
8377
+ debug.parse('\ud83d\udccd After consuming "from", current token:', ctx.peek().value);
8377
8378
  if (consumeOptionalKeyword(ctx, KEYWORDS$1.THE)) {
8378
- debug.parse('✅ Found "the", advancing...');
8379
+ debug.parse('\u2705 Found "the", advancing...');
8379
8380
  }
8380
8381
  const beforePrimary = ctx.peek();
8381
- debug.parse('🔍 Before parsePrimary for event target:', {
8382
+ debug.parse('\ud83d\udd0d Before parsePrimary for event target:', {
8382
8383
  value: beforePrimary.value,
8383
8384
  kind: beforePrimary.kind,
8384
8385
  position: beforePrimary.start,
8385
8386
  });
8386
8387
  eventTarget = ctx.parsePrimary();
8387
- debug.parse('✅ After parsePrimary, eventTarget:', eventTarget);
8388
+ debug.parse('\u2705 After parsePrimary, eventTarget:', eventTarget);
8388
8389
  }
8389
8390
  else {
8390
- debug.parse('❌ No "from" found, skipping target parsing');
8391
+ debug.parse('\u274c No "from" found, skipping target parsing');
8391
8392
  }
8392
8393
  }
8393
8394
  else {
@@ -9488,16 +9489,6 @@ function stringNode(value, tok) {
9488
9489
  column: tok.column,
9489
9490
  };
9490
9491
  }
9491
- function isNakedURLStart(ctx) {
9492
- const tok = ctx.peek();
9493
- if (tok.value === '/')
9494
- return true;
9495
- if (ctx.checkIdentifierLike()) {
9496
- const next = ctx.peekAt(1);
9497
- return !!next && next.value === ':' && next.start === tok.end;
9498
- }
9499
- return false;
9500
- }
9501
9492
  function parseGoCommand(ctx, identifierNode) {
9502
9493
  const args = [];
9503
9494
  while (!isCommandBoundary(ctx, ['when', 'where', 'catch', 'finally'])) {
@@ -9656,6 +9647,16 @@ function isFetchURLTerminator(ctx) {
9656
9647
  return (['as', 'with', '{', 'then', 'end', 'else', 'otherwise', ')'].includes(val) ||
9657
9648
  isCommandBoundary(ctx));
9658
9649
  }
9650
+ function isNakedURLStart(ctx) {
9651
+ const tok = ctx.peek();
9652
+ if (tok.value === '/')
9653
+ return true;
9654
+ if (ctx.checkIdentifierLike()) {
9655
+ const next = ctx.peekAt(1);
9656
+ return !!next && next.value === ':' && next.start === tok.end;
9657
+ }
9658
+ return false;
9659
+ }
9659
9660
  function parseBareURLPath(ctx, isTerminator = isFetchURLTerminator) {
9660
9661
  const startPos = ctx.savePosition();
9661
9662
  let path = '';
@@ -9677,7 +9678,7 @@ function parseBareURLPath(ctx, isTerminator = isFetchURLTerminator) {
9677
9678
  function parseFetchCommand(ctx, commandToken) {
9678
9679
  const modifiers = {};
9679
9680
  let url = null;
9680
- if (!ctx.isAtEnd() && ctx.check('/')) {
9681
+ if (!ctx.isAtEnd() && isNakedURLStart(ctx)) {
9681
9682
  url = parseBareURLPath(ctx);
9682
9683
  }
9683
9684
  if (!url) {
@@ -10243,26 +10244,26 @@ class Parser {
10243
10244
  const commandSequence = this.parseCommandSequence();
10244
10245
  if (commandSequence) {
10245
10246
  if (!this.isAtEnd() && this.check('on')) {
10246
- debug.parse('✅ PARSER: Found event handlers after command sequence, parsing as program');
10247
+ debug.parse('\u2705 PARSER: Found event handlers after command sequence, parsing as program');
10247
10248
  const statements = [commandSequence];
10248
- debug.parse(`✅ PARSER: Starting with ${statements.length} statement(s) from command sequence`);
10249
+ debug.parse(`\u2705 PARSER: Starting with ${statements.length} statement(s) from command sequence`);
10249
10250
  let eventHandlerCount = 0;
10250
10251
  while (!this.isAtEnd() && this.check('on')) {
10251
- debug.parse(`✅ PARSER: Parsing event handler #${eventHandlerCount + 1}, current token: ${this.peek().value}`);
10252
+ debug.parse(`\u2705 PARSER: Parsing event handler #${eventHandlerCount + 1}, current token: ${this.peek().value}`);
10252
10253
  this.advance();
10253
10254
  const eventHandler = this.parseEventHandler();
10254
- debug.parse(`✅ PARSER: parseEventHandler returned:`, eventHandler
10255
+ debug.parse(`\u2705 PARSER: parseEventHandler returned:`, eventHandler
10255
10256
  ? `type=${eventHandler.type}, event=${eventHandler.event}`
10256
10257
  : 'null');
10257
10258
  if (eventHandler) {
10258
10259
  statements.push(eventHandler);
10259
10260
  eventHandlerCount++;
10260
- debug.parse(`✅ PARSER: Added event handler, now have ${statements.length} statements total`);
10261
+ debug.parse(`\u2705 PARSER: Added event handler, now have ${statements.length} statements total`);
10261
10262
  }
10262
- debug.parse(`✅ PARSER: After parsing event handler, current token: ${this.isAtEnd() ? 'END' : this.peek().value}, isAtEnd=${this.isAtEnd()}, check('on')=${this.check('on')}`);
10263
+ debug.parse(`\u2705 PARSER: After parsing event handler, current token: ${this.isAtEnd() ? 'END' : this.peek().value}, isAtEnd=${this.isAtEnd()}, check('on')=${this.check('on')}`);
10263
10264
  if (!this.isAtEnd() && !this.check('on')) {
10264
10265
  if (!this.isAtEnd()) {
10265
- debug.parse(`⚠️ PARSER: Unexpected token after event handlers: ${this.peek().value}`);
10266
+ debug.parse(`\u26a0\ufe0f PARSER: Unexpected token after event handlers: ${this.peek().value}`);
10266
10267
  this.addError(`Unexpected token after event handlers: ${this.peek().value}`);
10267
10268
  return {
10268
10269
  success: false,
@@ -10271,11 +10272,11 @@ class Parser {
10271
10272
  error: this.error,
10272
10273
  };
10273
10274
  }
10274
- debug.parse(`✅ PARSER: No more event handlers, at end of input`);
10275
+ debug.parse(`\u2705 PARSER: No more event handlers, at end of input`);
10275
10276
  break;
10276
10277
  }
10277
10278
  }
10278
- debug.parse(`✅ PARSER: Finished parsing, creating Program node with ${statements.length} statements`);
10279
+ debug.parse(`\u2705 PARSER: Finished parsing, creating Program node with ${statements.length} statements`);
10279
10280
  if (this.error) {
10280
10281
  return {
10281
10282
  success: false,
@@ -10670,41 +10671,41 @@ class Parser {
10670
10671
  parseCommandListUntilTerminator(extraStops) {
10671
10672
  const commands = [];
10672
10673
  const isStop = () => this.check('end') || extraStops.some(s => this.check(s));
10673
- debug.parse('🔄 parseCommandListUntilTerminator: Starting (extraStops:', extraStops.join(','), ')');
10674
+ debug.parse('\ud83d\udd04 parseCommandListUntilTerminator: Starting (extraStops:', extraStops.join(','), ')');
10674
10675
  while (!this.isAtEnd() && !isStop()) {
10675
- debug.parse('📍 Loop iteration, current token:', this.peek().value, 'kind:', this.peek().kind);
10676
+ debug.parse('\ud83d\udccd Loop iteration, current token:', this.peek().value, 'kind:', this.peek().kind);
10676
10677
  let parsedCommand = false;
10677
10678
  const isCommandToken = this.checkIsCommand();
10678
10679
  const isCommandIdentifier = !isCommandToken && this.checkIdentifier() && this.isCommand(this.peek().value);
10679
10680
  if (isCommandToken || isCommandIdentifier) {
10680
- debug.parse(isCommandToken ? '✅ Found COMMAND token:' : '✅ Found IDENTIFIER that is a command:', this.peek().value);
10681
+ debug.parse(isCommandToken ? '\u2705 Found COMMAND token:' : '\u2705 Found IDENTIFIER that is a command:', this.peek().value);
10681
10682
  this.advance();
10682
10683
  const savedError = this.error;
10683
10684
  try {
10684
10685
  const cmd = this.parseCommand();
10685
10686
  if (this.error && this.error !== savedError) {
10686
- debug.parse('⚠️ parseCommandListUntilTerminator: Command parsing added error, restoring. Error was:', this.error.message);
10687
+ debug.parse('\u26a0\ufe0f parseCommandListUntilTerminator: Command parsing added error, restoring. Error was:', this.error.message);
10687
10688
  this.error = savedError;
10688
10689
  }
10689
10690
  if (cmd) {
10690
- debug.parse('✅ Parsed command:', cmd.name);
10691
+ debug.parse('\u2705 Parsed command:', cmd.name);
10691
10692
  commands.push(cmd);
10692
10693
  parsedCommand = true;
10693
10694
  }
10694
10695
  }
10695
10696
  catch (error) {
10696
- debug.parse('⚠️ parseCommandListUntilTerminator: Command parsing threw, restoring:', error instanceof Error ? error.message : String(error));
10697
+ debug.parse('\u26a0\ufe0f parseCommandListUntilTerminator: Command parsing threw, restoring:', error instanceof Error ? error.message : String(error));
10697
10698
  this.error = savedError;
10698
10699
  }
10699
10700
  }
10700
10701
  else if (this.checkIdentifier()) {
10701
- debug.parse('❌ IDENTIFIER is not a command:', this.peek().value);
10702
+ debug.parse('\u274c IDENTIFIER is not a command:', this.peek().value);
10702
10703
  }
10703
10704
  if (!parsedCommand) {
10704
- debug.parse('❌ No command parsed, breaking. Current token:', this.peek().value);
10705
+ debug.parse('\u274c No command parsed, breaking. Current token:', this.peek().value);
10705
10706
  break;
10706
10707
  }
10707
- debug.parse('📍 After parsing command, current token:', this.peek().value);
10708
+ debug.parse('\ud83d\udccd After parsing command, current token:', this.peek().value);
10708
10709
  while (!this.isAtEnd() &&
10709
10710
  !isStop() &&
10710
10711
  !this.checkIsCommand() &&
@@ -10712,19 +10713,19 @@ class Parser {
10712
10713
  !this.check('then') &&
10713
10714
  !this.check('and') &&
10714
10715
  !this.check(',')) {
10715
- debug.parse('⚠️ Skipping unexpected token:', this.peek().value);
10716
+ debug.parse('\u26a0\ufe0f Skipping unexpected token:', this.peek().value);
10716
10717
  this.advance();
10717
10718
  }
10718
10719
  if (this.match('then', 'and', ',')) {
10719
- debug.parse('✅ Found separator, continuing');
10720
+ debug.parse('\u2705 Found separator, continuing');
10720
10721
  continue;
10721
10722
  }
10722
10723
  else if (this.checkIsCommand() || this.isCommand(this.peek().value)) {
10723
- debug.parse('✅ Next token is a command, continuing without separator');
10724
+ debug.parse('\u2705 Next token is a command, continuing without separator');
10724
10725
  continue;
10725
10726
  }
10726
10727
  else {
10727
- debug.parse('📍 No separator and no command, breaking. Current token:', this.peek().value);
10728
+ debug.parse('\ud83d\udccd No separator and no command, breaking. Current token:', this.peek().value);
10728
10729
  break;
10729
10730
  }
10730
10731
  }
@@ -10738,18 +10739,18 @@ class Parser {
10738
10739
  if (!this.check('end') && !extraStops.some(s => this.check(s))) {
10739
10740
  terminator = '';
10740
10741
  }
10741
- debug.parse('✅ parseCommandListUntilTerminator: parsed', commands.length, 'commands (terminator:', terminator, ')');
10742
+ debug.parse('\u2705 parseCommandListUntilTerminator: parsed', commands.length, 'commands (terminator:', terminator, ')');
10742
10743
  return { commands, terminator };
10743
10744
  }
10744
10745
  parseCommandListUntilEnd() {
10745
10746
  const { commands } = this.parseCommandListUntilTerminator([]);
10746
- debug.parse('🔍 After loop, checking for "end". Current token:', this.peek().value);
10747
+ debug.parse('\ud83d\udd0d After loop, checking for "end". Current token:', this.peek().value);
10747
10748
  if (this.check('end')) {
10748
- debug.parse('✅ Found "end", consuming it');
10749
+ debug.parse('\u2705 Found "end", consuming it');
10749
10750
  this.advance();
10750
10751
  }
10751
10752
  else {
10752
- debug.parse('❌ ERROR: Expected "end" but got:', this.peek().value, 'at position:', this.peek().start);
10753
+ debug.parse('\u274c ERROR: Expected "end" but got:', this.peek().value, 'at position:', this.peek().start);
10753
10754
  throw new Error('Expected "end" to close repeat block');
10754
10755
  }
10755
10756
  return commands;
@@ -11422,6 +11423,23 @@ class Parser {
11422
11423
  this.consume(')', "Expected ')' after parameter list");
11423
11424
  }
11424
11425
  const bodyCommands = this.parseCommandBlock(['end', 'catch', 'finally']);
11426
+ const { errorSymbol, errorHandler, finallyHandler } = this.parseErrorAndFinallyBlocks();
11427
+ this.consume('end', "Expected 'end' after function definition");
11428
+ return {
11429
+ type: 'def',
11430
+ name: funcName,
11431
+ params,
11432
+ body: bodyCommands,
11433
+ ...(errorSymbol !== undefined && { errorSymbol }),
11434
+ ...(errorHandler !== undefined && { errorHandler }),
11435
+ ...(finallyHandler !== undefined && { finallyHandler }),
11436
+ start: pos.start,
11437
+ end: this.getPosition().end,
11438
+ line: pos.line,
11439
+ column: pos.column,
11440
+ };
11441
+ }
11442
+ parseErrorAndFinallyBlocks() {
11425
11443
  let errorSymbol;
11426
11444
  let errorHandler;
11427
11445
  if (this.check('catch')) {
@@ -11436,19 +11454,10 @@ class Parser {
11436
11454
  this.advance();
11437
11455
  finallyHandler = this.parseCommandBlock(['end']);
11438
11456
  }
11439
- this.consume('end', "Expected 'end' after function definition");
11440
11457
  return {
11441
- type: 'def',
11442
- name: funcName,
11443
- params,
11444
- body: bodyCommands,
11445
11458
  ...(errorSymbol !== undefined && { errorSymbol }),
11446
11459
  ...(errorHandler !== undefined && { errorHandler }),
11447
11460
  ...(finallyHandler !== undefined && { finallyHandler }),
11448
- start: pos.start,
11449
- end: this.getPosition().end,
11450
- line: pos.line,
11451
- column: pos.column,
11452
11461
  };
11453
11462
  }
11454
11463
  parseEventNameWithNamespace(errorMessage) {
@@ -11593,13 +11602,13 @@ class Parser {
11593
11602
  try {
11594
11603
  const cmd = this.parseCommand();
11595
11604
  if (this.error && this.error !== savedError) {
11596
- debug.parse('⚠️ Command parsing added error, restoring error state. Error was:', this.error.message);
11605
+ debug.parse('\u26a0\ufe0f Command parsing added error, restoring error state. Error was:', this.error.message);
11597
11606
  this.error = savedError;
11598
11607
  }
11599
11608
  return cmd;
11600
11609
  }
11601
11610
  catch (error) {
11602
- debug.parse('⚠️ Command parsing threw exception, restoring error state:', error instanceof Error ? error.message : String(error));
11611
+ debug.parse('\u26a0\ufe0f Command parsing threw exception, restoring error state:', error instanceof Error ? error.message : String(error));
11603
11612
  this.error = savedError;
11604
11613
  return null;
11605
11614
  }
@@ -11616,12 +11625,12 @@ class Parser {
11616
11625
  const timeToken = this.advance();
11617
11626
  const delayMs = parseTimeToMs(timeToken.value);
11618
11627
  modifiers[modifierKey] = delayMs;
11619
- debug.parse(`🔧 parseEventHandler: Parsed '${modName} at ${timeToken.value}' (${delayMs}ms)`);
11628
+ debug.parse(`\ud83d\udd27 parseEventHandler: Parsed '${modName} at ${timeToken.value}' (${delayMs}ms)`);
11620
11629
  }
11621
11630
  else if (this.matchNumber()) {
11622
11631
  const delayMs = parseInt(this.previous().value, 10);
11623
11632
  modifiers[modifierKey] = delayMs;
11624
- debug.parse(`🔧 parseEventHandler: Parsed '${modName} at ${delayMs}'`);
11633
+ debug.parse(`\ud83d\udd27 parseEventHandler: Parsed '${modName} at ${delayMs}'`);
11625
11634
  }
11626
11635
  else {
11627
11636
  this.addError(`Expected time expression after '${modName} at', got: ${this.peek().value}`);
@@ -11632,7 +11641,7 @@ class Parser {
11632
11641
  }
11633
11642
  }
11634
11643
  parseEventHandler() {
11635
- debug.parse(`🔧 parseEventHandler: ENTRY - parsing event handler`);
11644
+ debug.parse(`\ud83d\udd27 parseEventHandler: ENTRY - parsing event handler`);
11636
11645
  const eventNames = [];
11637
11646
  let firstOnceAlias = false;
11638
11647
  if (this.check('first') && !this.checkIsCommand() && this.current + 1 < this.tokens.length) {
@@ -11641,27 +11650,27 @@ class Parser {
11641
11650
  if (peek2Value && peek2Value !== 'of' && peek2Value !== 'in' && peek2Value !== 'from') {
11642
11651
  this.advance();
11643
11652
  firstOnceAlias = true;
11644
- debug.parse(`🔧 parseEventHandler: Parsed 'first' as .once alias`);
11653
+ debug.parse(`\ud83d\udd27 parseEventHandler: Parsed 'first' as .once alias`);
11645
11654
  }
11646
11655
  }
11647
11656
  const event = this.parseEventNameWithNamespace("Expected event name after 'on'");
11648
11657
  eventNames.push(event);
11649
- debug.parse(`🔧 parseEventHandler: Parsed first event name: ${event}`);
11658
+ debug.parse(`\ud83d\udd27 parseEventHandler: Parsed first event name: ${event}`);
11650
11659
  while (this.check('or')) {
11651
11660
  this.advance();
11652
- debug.parse(`🔧 parseEventHandler: Found 'or', parsing additional event name`);
11661
+ debug.parse(`\ud83d\udd27 parseEventHandler: Found 'or', parsing additional event name`);
11653
11662
  const additionalEvent = this.parseEventNameWithNamespace("Expected event name after 'or'");
11654
11663
  eventNames.push(additionalEvent);
11655
- debug.parse(`🔧 parseEventHandler: Parsed additional event name: ${additionalEvent}`);
11664
+ debug.parse(`\ud83d\udd27 parseEventHandler: Parsed additional event name: ${additionalEvent}`);
11656
11665
  }
11657
- debug.parse(`🔧 parseEventHandler: Total events parsed: ${eventNames.join(', ')}`);
11666
+ debug.parse(`\ud83d\udd27 parseEventHandler: Total events parsed: ${eventNames.join(', ')}`);
11658
11667
  let customEventSource;
11659
11668
  if (this.registryIntegration) {
11660
11669
  const firstEvent = eventNames[0];
11661
11670
  if (this.registryIntegration.hasEventSource(firstEvent)) {
11662
11671
  const source = this.registryIntegration.getEventSource(firstEvent);
11663
11672
  customEventSource = source?.name;
11664
- debug.parse(`🔧 parseEventHandler: Detected custom event source '${customEventSource}' for event '${firstEvent}'`);
11673
+ debug.parse(`\ud83d\udd27 parseEventHandler: Detected custom event source '${customEventSource}' for event '${firstEvent}'`);
11665
11674
  }
11666
11675
  }
11667
11676
  const eventParams = [];
@@ -11673,7 +11682,7 @@ class Parser {
11673
11682
  } while (this.match(','));
11674
11683
  }
11675
11684
  this.consume(')', "Expected ')' after event parameters");
11676
- debug.parse(`🔧 parseEventHandler: Parsed event parameters: ${eventParams.join(', ')}`);
11685
+ debug.parse(`\ud83d\udd27 parseEventHandler: Parsed event parameters: ${eventParams.join(', ')}`);
11677
11686
  }
11678
11687
  const modifiers = {};
11679
11688
  while (this.check('.')) {
@@ -11682,15 +11691,15 @@ class Parser {
11682
11691
  const modName = modToken.value.toLowerCase();
11683
11692
  if (modName === 'once') {
11684
11693
  modifiers.once = true;
11685
- debug.parse(`🔧 parseEventHandler: Parsed modifier '.once'`);
11694
+ debug.parse(`\ud83d\udd27 parseEventHandler: Parsed modifier '.once'`);
11686
11695
  }
11687
11696
  else if (modName === 'prevent') {
11688
11697
  modifiers.prevent = true;
11689
- debug.parse(`🔧 parseEventHandler: Parsed modifier '.prevent'`);
11698
+ debug.parse(`\ud83d\udd27 parseEventHandler: Parsed modifier '.prevent'`);
11690
11699
  }
11691
11700
  else if (modName === 'stop') {
11692
11701
  modifiers.stop = true;
11693
- debug.parse(`🔧 parseEventHandler: Parsed modifier '.stop'`);
11702
+ debug.parse(`\ud83d\udd27 parseEventHandler: Parsed modifier '.stop'`);
11694
11703
  }
11695
11704
  else if (modName === 'debounce' || modName === 'throttle') {
11696
11705
  if (this.check('(')) {
@@ -11702,11 +11711,11 @@ class Parser {
11702
11711
  }
11703
11712
  if (modName === 'debounce') {
11704
11713
  modifiers.debounce = delayMs;
11705
- debug.parse(`🔧 parseEventHandler: Parsed modifier '.debounce(${delayMs})'`);
11714
+ debug.parse(`\ud83d\udd27 parseEventHandler: Parsed modifier '.debounce(${delayMs})'`);
11706
11715
  }
11707
11716
  else {
11708
11717
  modifiers.throttle = delayMs;
11709
- debug.parse(`🔧 parseEventHandler: Parsed modifier '.throttle(${delayMs})'`);
11718
+ debug.parse(`\ud83d\udd27 parseEventHandler: Parsed modifier '.throttle(${delayMs})'`);
11710
11719
  }
11711
11720
  this.consume(')', `Expected ')' after ${modName} delay`);
11712
11721
  }
@@ -11715,7 +11724,7 @@ class Parser {
11715
11724
  }
11716
11725
  }
11717
11726
  else {
11718
- debug.parse(`🔧 parseEventHandler: Warning - unknown modifier '.${modName}'`);
11727
+ debug.parse(`\ud83d\udd27 parseEventHandler: Warning - unknown modifier '.${modName}'`);
11719
11728
  }
11720
11729
  }
11721
11730
  this.parseTemporalKeywordModifiers(modifiers);
@@ -11723,7 +11732,7 @@ class Parser {
11723
11732
  modifiers.once = true;
11724
11733
  }
11725
11734
  if (Object.keys(modifiers).length > 0) {
11726
- debug.parse(`🔧 parseEventHandler: Parsed modifiers:`, modifiers);
11735
+ debug.parse(`\ud83d\udd27 parseEventHandler: Parsed modifiers:`, modifiers);
11727
11736
  }
11728
11737
  let condition;
11729
11738
  if (this.match('[')) {
@@ -11734,7 +11743,7 @@ class Parser {
11734
11743
  if (this.match('from')) {
11735
11744
  const targetToken = this.advance();
11736
11745
  target = targetToken.value;
11737
- debug.parse(`🔧 parseEventHandler: Parsed 'from' target: ${target} (kind: ${targetToken.kind})`);
11746
+ debug.parse(`\ud83d\udd27 parseEventHandler: Parsed 'from' target: ${target} (kind: ${targetToken.kind})`);
11738
11747
  this.parseTemporalKeywordModifiers(modifiers);
11739
11748
  }
11740
11749
  let attributeName;
@@ -11746,32 +11755,36 @@ class Parser {
11746
11755
  else {
11747
11756
  attributeName = attrToken.value;
11748
11757
  }
11749
- debug.parse(`🔧 parseEventHandler: Parsed attribute name: ${attributeName}`);
11758
+ debug.parse(`\ud83d\udd27 parseEventHandler: Parsed attribute name: ${attributeName}`);
11750
11759
  }
11751
11760
  let watchTarget;
11752
11761
  if (this.match('in')) {
11753
- debug.parse(`🔧 parseEventHandler: Found 'in' keyword, parsing watch target`);
11762
+ debug.parse(`\ud83d\udd27 parseEventHandler: Found 'in' keyword, parsing watch target`);
11754
11763
  watchTarget = this.parseExpression();
11755
- debug.parse(`🔧 parseEventHandler: Parsed watch target expression`);
11764
+ debug.parse(`\ud83d\udd27 parseEventHandler: Parsed watch target expression`);
11756
11765
  }
11757
11766
  const commands = [];
11758
- debug.parse(`✅ parseEventHandler: About to parse commands, current token: ${this.isAtEnd() ? 'END' : this.peek().value}, isAtEnd: ${this.isAtEnd()}`);
11767
+ debug.parse(`\u2705 parseEventHandler: About to parse commands, current token: ${this.isAtEnd() ? 'END' : this.peek().value}, isAtEnd: ${this.isAtEnd()}`);
11759
11768
  while (!this.isAtEnd()) {
11760
- debug.parse(`✅ parseEventHandler: Loop iteration, current token: ${this.peek().value}, kind: ${this.peek().kind}`);
11769
+ debug.parse(`\u2705 parseEventHandler: Loop iteration, current token: ${this.peek().value}, kind: ${this.peek().kind}`);
11761
11770
  if (this.check('on')) {
11762
- debug.parse(`✅ parseEventHandler: Stopping command parsing, found next event handler 'on'`);
11771
+ debug.parse(`\u2705 parseEventHandler: Stopping command parsing, found next event handler 'on'`);
11763
11772
  break;
11764
11773
  }
11765
11774
  if (this.checkComment()) {
11766
- debug.parse(`✅ parseEventHandler: Skipping comment token: ${this.peek().value}`);
11775
+ debug.parse(`\u2705 parseEventHandler: Skipping comment token: ${this.peek().value}`);
11767
11776
  this.advance();
11768
11777
  continue;
11769
11778
  }
11770
11779
  if (this.check('end')) {
11771
- debug.parse(`✅ parseEventHandler: Stopping command parsing, found 'end' keyword`);
11780
+ debug.parse(`\u2705 parseEventHandler: Stopping command parsing, found 'end' keyword`);
11772
11781
  this.advance();
11773
11782
  break;
11774
11783
  }
11784
+ if (this.check('catch') || this.check('finally')) {
11785
+ debug.parse(`\u2705 parseEventHandler: Stopping command parsing, found '${this.peek().value}'`);
11786
+ break;
11787
+ }
11775
11788
  if (this.checkIsCommand()) {
11776
11789
  const nextIsOpenParen = this.tokens[this.current + 1]?.value === '(';
11777
11790
  const commandName = this.peek().value.toLowerCase();
@@ -11800,7 +11813,7 @@ class Parser {
11800
11813
  const cmd = this.parseCommandWithErrorRecovery();
11801
11814
  if (cmd) {
11802
11815
  commands.push(cmd);
11803
- debug.parse(`✅ parseEventHandler: Parsed command, next token: ${this.isAtEnd() ? 'END' : this.peek().value}`);
11816
+ debug.parse(`\u2705 parseEventHandler: Parsed command, next token: ${this.isAtEnd() ? 'END' : this.peek().value}`);
11804
11817
  }
11805
11818
  }
11806
11819
  }
@@ -11818,7 +11831,7 @@ class Parser {
11818
11831
  expr = this.parseExpression();
11819
11832
  }
11820
11833
  catch (error) {
11821
- debug.parse('⚠️ Expression parsing error, restoring error state:', error instanceof Error ? error.message : String(error));
11834
+ debug.parse('\u26a0\ufe0f Expression parsing error, restoring error state:', error instanceof Error ? error.message : String(error));
11822
11835
  this.error = savedError;
11823
11836
  break;
11824
11837
  }
@@ -11865,7 +11878,9 @@ class Parser {
11865
11878
  !this.check('then') &&
11866
11879
  !this.check('and') &&
11867
11880
  !this.check(',') &&
11868
- !this.check('on')) {
11881
+ !this.check('on') &&
11882
+ !this.check('catch') &&
11883
+ !this.check('finally')) {
11869
11884
  this.advance();
11870
11885
  }
11871
11886
  if (this.match('then', 'and', ',')) {
@@ -11878,12 +11893,24 @@ class Parser {
11878
11893
  break;
11879
11894
  }
11880
11895
  }
11896
+ let errorSymbol;
11897
+ let errorHandler;
11898
+ let finallyHandler;
11899
+ if (!this.isAtEnd() && (this.check('catch') || this.check('finally'))) {
11900
+ ({ errorSymbol, errorHandler, finallyHandler } = this.parseErrorAndFinallyBlocks());
11901
+ if (this.check('end')) {
11902
+ this.advance();
11903
+ }
11904
+ }
11881
11905
  const pos = this.getPosition();
11882
11906
  const node = {
11883
11907
  type: 'eventHandler',
11884
11908
  event: eventNames.length === 1 ? eventNames[0] : eventNames.join('|'),
11885
11909
  events: eventNames,
11886
11910
  commands,
11911
+ ...(errorSymbol !== undefined && { errorSymbol }),
11912
+ ...(errorHandler !== undefined && { errorHandler }),
11913
+ ...(finallyHandler !== undefined && { finallyHandler }),
11887
11914
  ...(eventParams.length > 0 && { args: eventParams }),
11888
11915
  ...(condition && { condition }),
11889
11916
  ...(target && { target }),
@@ -11896,7 +11923,7 @@ class Parser {
11896
11923
  line: pos.line,
11897
11924
  column: pos.column,
11898
11925
  };
11899
- debug.parse(`🔧 parseEventHandler: Created node with events: ${eventNames.join(', ')}, attributeName: ${attributeName || 'none'}, watchTarget: ${watchTarget ? 'yes' : 'none'}`);
11926
+ debug.parse(`\ud83d\udd27 parseEventHandler: Created node with events: ${eventNames.join(', ')}, attributeName: ${attributeName || 'none'}, watchTarget: ${watchTarget ? 'yes' : 'none'}`);
11900
11927
  return node;
11901
11928
  }
11902
11929
  parseBehaviorDefinition() {
@@ -11961,11 +11988,33 @@ class Parser {
11961
11988
  }
11962
11989
  }
11963
11990
  }
11964
- const handlerCommands = this.parseCommandListUntilEnd();
11991
+ const { commands: handlerCommands, terminator } = this.parseCommandListUntilTerminator([
11992
+ 'catch',
11993
+ 'finally',
11994
+ ]);
11995
+ let handlerErrorSymbol;
11996
+ let handlerErrorHandler;
11997
+ let handlerFinallyHandler;
11998
+ if (terminator === 'catch' || terminator === 'finally') {
11999
+ ({
12000
+ errorSymbol: handlerErrorSymbol,
12001
+ errorHandler: handlerErrorHandler,
12002
+ finallyHandler: handlerFinallyHandler,
12003
+ } = this.parseErrorAndFinallyBlocks());
12004
+ }
12005
+ if (this.check('end')) {
12006
+ this.advance();
12007
+ }
12008
+ else {
12009
+ throw new Error('Expected "end" to close behavior event handler');
12010
+ }
11965
12011
  const handlerNode = {
11966
12012
  type: 'eventHandler',
11967
12013
  event: eventName,
11968
12014
  commands: handlerCommands,
12015
+ ...(handlerErrorSymbol !== undefined && { errorSymbol: handlerErrorSymbol }),
12016
+ ...(handlerErrorHandler !== undefined && { errorHandler: handlerErrorHandler }),
12017
+ ...(handlerFinallyHandler !== undefined && { finallyHandler: handlerFinallyHandler }),
11969
12018
  ...(eventSource !== undefined && { target: eventSource }),
11970
12019
  ...(eventArgs.length > 0 && { args: eventArgs }),
11971
12020
  start: handlerPos.start,
@@ -12020,7 +12069,7 @@ class Parser {
12020
12069
  this.error.message.includes('unclosed parenthes') ||
12021
12070
  this.error.message.includes('Unclosed parenthes');
12022
12071
  if (!isCriticalError) {
12023
- debug.parse('⚠️ parseCommandSequence: Command parsing added non-critical error, restoring error state. Error was:', this.error.message);
12072
+ debug.parse('\u26a0\ufe0f parseCommandSequence: Command parsing added non-critical error, restoring error state. Error was:', this.error.message);
12024
12073
  this.error = savedError;
12025
12074
  }
12026
12075
  }
@@ -12030,11 +12079,11 @@ class Parser {
12030
12079
  !this.isCommand(this.peek().value) &&
12031
12080
  !this.check('then') &&
12032
12081
  !this.check('on')) {
12033
- debug.parse('⚠️ parseCommandSequence: Skipping unexpected token:', this.peek().value);
12082
+ debug.parse('\u26a0\ufe0f parseCommandSequence: Skipping unexpected token:', this.peek().value);
12034
12083
  this.advance();
12035
12084
  }
12036
12085
  if (this.check('on')) {
12037
- debug.parse('✅ parseCommandSequence: Found "on" token, stopping command sequence to allow event handler parsing');
12086
+ debug.parse('\u2705 parseCommandSequence: Found "on" token, stopping command sequence to allow event handler parsing');
12038
12087
  break;
12039
12088
  }
12040
12089
  if (this.match('then')) {
@@ -12478,7 +12527,7 @@ class Parser {
12478
12527
  if (!this.check(')') && callee.type === 'identifier') {
12479
12528
  const funcName = callee.name;
12480
12529
  if (CommandClassification.isCSSFunction(funcName)) {
12481
- debug.parse(`💡 Tip: CSS functions like ${funcName}() should be quoted for clean parsing. ` +
12530
+ debug.parse(`\ud83d\udca1 Tip: CSS functions like ${funcName}() should be quoted for clean parsing. ` +
12482
12531
  `Use '${funcName}(...)' or \`${funcName}(...)\` instead.`);
12483
12532
  }
12484
12533
  }
@@ -12780,7 +12829,7 @@ class Parser {
12780
12829
  if (this.checkIsCommand() || (this.isCommand(token.value) && !this.isKeyword(token.value))) {
12781
12830
  return;
12782
12831
  }
12783
- debug.parse('⚠️ synchronize: Skipping token:', token.value);
12832
+ debug.parse('\u26a0\ufe0f synchronize: Skipping token:', token.value);
12784
12833
  this.advance();
12785
12834
  }
12786
12835
  }
@@ -16767,8 +16816,15 @@ let FetchCommand = (() => {
16767
16816
  }
16768
16817
  async parseURL(arg, evaluator, context) {
16769
16818
  const v = await evaluator.evaluate(arg, context);
16770
- if (typeof v !== 'string' || !v)
16819
+ if (typeof v !== 'string' || !v) {
16820
+ const node = arg;
16821
+ if ((v === undefined || v === null) && node?.type === 'identifier' && node.name) {
16822
+ throw new Error(`fetch: URL expression "${node.name}" evaluated to ${String(v)} \u2014 ` +
16823
+ `check the variable is set, or quote it if you meant a literal URL ` +
16824
+ `(fetch 'https://example.com/path')`);
16825
+ }
16771
16826
  throw new Error('fetch: URL must be a non-empty string');
16827
+ }
16772
16828
  return v;
16773
16829
  }
16774
16830
  parseResponseType(asNode) {
@@ -16987,7 +17043,7 @@ let SetCommand = (() => {
16987
17043
  if (typeof firstValue !== 'string') {
16988
17044
  const isMember = firstArg?.type === 'memberExpression' || firstArg?.type === 'propertyAccess';
16989
17045
  const hint = isMember
16990
- ? ` (a property chain evaluated to ${firstValue === null ? 'null' : typeof firstValue} — check that all intermediate objects exist)`
17046
+ ? ` (a property chain evaluated to ${firstValue === null ? 'null' : typeof firstValue} \u2014 check that all intermediate objects exist)`
16991
17047
  : '';
16992
17048
  throw new Error(`set command target must be a string or object literal${hint}`);
16993
17049
  }
@@ -18065,7 +18121,7 @@ let BeepCommand = (() => {
18065
18121
  return { expressionCount: 0, debugged: true, outputs: [] };
18066
18122
  }
18067
18123
  const outputs = [];
18068
- console.group('🔔 beep! Debug Output');
18124
+ console.group('\ud83d\udd14 beep! Debug Output');
18069
18125
  for (const expression of expressions) {
18070
18126
  const output = this.debugExpression(expression);
18071
18127
  outputs.push(output);
@@ -18078,7 +18134,7 @@ let BeepCommand = (() => {
18078
18134
  return { expressionCount: expressions.length, debugged: true, outputs };
18079
18135
  }
18080
18136
  debugContext(context) {
18081
- console.group('🔔 beep! Context Debug');
18137
+ console.group('\ud83d\udd14 beep! Context Debug');
18082
18138
  console.log('me:', context.me);
18083
18139
  console.log('it:', context.it);
18084
18140
  console.log('you:', context.you);