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