@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
@@ -33,39 +33,39 @@ const DEBUG = {
33
33
  const debug = {
34
34
  command: (...args) => {
35
35
  if (DEBUG.commands)
36
- console.log('🔧', ...args);
36
+ console.log('\ud83d\udd27', ...args);
37
37
  },
38
38
  event: (...args) => {
39
39
  if (DEBUG.events)
40
- console.log('🎯', ...args);
40
+ console.log('\ud83c\udfaf', ...args);
41
41
  },
42
42
  parse: (...args) => {
43
43
  if (DEBUG.parsing)
44
- console.log('📝', ...args);
44
+ console.log('\ud83d\udcdd', ...args);
45
45
  },
46
46
  expr: (...args) => {
47
47
  if (DEBUG.expressions)
48
- console.log('🔍', ...args);
48
+ console.log('\ud83d\udd0d', ...args);
49
49
  },
50
50
  expressions: (...args) => {
51
51
  if (DEBUG.expressions)
52
- console.log('🔍', ...args);
52
+ console.log('\ud83d\udd0d', ...args);
53
53
  },
54
54
  style: (...args) => {
55
55
  if (DEBUG.styles)
56
- console.log('🎨', ...args);
56
+ console.log('\ud83c\udfa8', ...args);
57
57
  },
58
58
  runtime: (...args) => {
59
59
  if (DEBUG.runtime)
60
- console.log('🚀', ...args);
60
+ console.log('\ud83d\ude80', ...args);
61
61
  },
62
62
  loop: (...args) => {
63
63
  if (DEBUG.loops)
64
- console.log('🔁', ...args);
64
+ console.log('\ud83d\udd01', ...args);
65
65
  },
66
66
  async: (...args) => {
67
67
  if (DEBUG.async)
68
- console.log('⏳', ...args);
68
+ console.log('\u23f3', ...args);
69
69
  },
70
70
  };
71
71
 
@@ -4048,6 +4048,7 @@ function tokenize(input) {
4048
4048
  const prevToken = tokenizer.tokens[tokenizer.tokens.length - 1];
4049
4049
  const isPossessive = nextChar === 's' &&
4050
4050
  prevToken &&
4051
+ !/\s/.test(tokenizer.input[tokenizer.position - 1] ?? ' ') &&
4051
4052
  (prevToken.kind === TokenKind.IDENTIFIER ||
4052
4053
  prevToken.kind === TokenKind.SELECTOR ||
4053
4054
  prevToken.value === ']' ||
@@ -5772,9 +5773,9 @@ function createPartialCommandNode(name, pos) {
5772
5773
  };
5773
5774
  }
5774
5775
  function createProgramNode(statements) {
5775
- debug.parse(`✅ createProgramNode: Called with ${statements.length} statements`);
5776
+ debug.parse(`\u2705 createProgramNode: Called with ${statements.length} statements`);
5776
5777
  if (statements.length === 0) {
5777
- debug.parse(`✅ createProgramNode: Returning error node (0 statements)`);
5778
+ debug.parse(`\u2705 createProgramNode: Returning error node (0 statements)`);
5778
5779
  return {
5779
5780
  type: 'identifier',
5780
5781
  name: '__ERROR__',
@@ -5785,7 +5786,7 @@ function createProgramNode(statements) {
5785
5786
  };
5786
5787
  }
5787
5788
  if (statements.length === 1) {
5788
- debug.parse(`✅ createProgramNode: Returning single statement (type=${statements[0].type})`);
5789
+ debug.parse(`\u2705 createProgramNode: Returning single statement (type=${statements[0].type})`);
5789
5790
  return statements[0];
5790
5791
  }
5791
5792
  const programNode = {
@@ -5796,7 +5797,7 @@ function createProgramNode(statements) {
5796
5797
  line: statements[0]?.line || 1,
5797
5798
  column: statements[0]?.column || 1,
5798
5799
  };
5799
- debug.parse(`✅ createProgramNode: Returning Program node with ${statements.length} statements, type=${programNode.type}`);
5800
+ debug.parse(`\u2705 createProgramNode: Returning Program node with ${statements.length} statements, type=${programNode.type}`);
5800
5801
  return programNode;
5801
5802
  }
5802
5803
 
@@ -6185,37 +6186,37 @@ function parseRepeatCommand(ctx, commandToken) {
6185
6186
  ctx.advance();
6186
6187
  loopType = 'until-event';
6187
6188
  const eventToken = ctx.peek();
6188
- debug.parse('📍 Parsing event name, current token:', {
6189
+ debug.parse('\ud83d\udccd Parsing event name, current token:', {
6189
6190
  value: eventToken.value,
6190
6191
  kind: eventToken.kind,
6191
6192
  });
6192
6193
  if (isIdentifierLike(eventToken) || isEvent(eventToken)) {
6193
6194
  eventName = eventToken.value;
6194
6195
  ctx.advance();
6195
- debug.parse('✅ Got event name:', eventName, 'Next token:', ctx.peek().value);
6196
+ debug.parse('\u2705 Got event name:', eventName, 'Next token:', ctx.peek().value);
6196
6197
  }
6197
6198
  else {
6198
6199
  throw new Error('Expected event name after "event"');
6199
6200
  }
6200
- debug.parse('🔍 Checking for "from", current token:', ctx.peek().value);
6201
+ debug.parse('\ud83d\udd0d Checking for "from", current token:', ctx.peek().value);
6201
6202
  if (ctx.check(KEYWORDS.FROM)) {
6202
- debug.parse('✅ Found "from", advancing...');
6203
+ debug.parse('\u2705 Found "from", advancing...');
6203
6204
  ctx.advance();
6204
- debug.parse('📍 After consuming "from", current token:', ctx.peek().value);
6205
+ debug.parse('\ud83d\udccd After consuming "from", current token:', ctx.peek().value);
6205
6206
  if (consumeOptionalKeyword(ctx, KEYWORDS.THE)) {
6206
- debug.parse('✅ Found "the", advancing...');
6207
+ debug.parse('\u2705 Found "the", advancing...');
6207
6208
  }
6208
6209
  const beforePrimary = ctx.peek();
6209
- debug.parse('🔍 Before parsePrimary for event target:', {
6210
+ debug.parse('\ud83d\udd0d Before parsePrimary for event target:', {
6210
6211
  value: beforePrimary.value,
6211
6212
  kind: beforePrimary.kind,
6212
6213
  position: beforePrimary.start,
6213
6214
  });
6214
6215
  eventTarget = ctx.parsePrimary();
6215
- debug.parse('✅ After parsePrimary, eventTarget:', eventTarget);
6216
+ debug.parse('\u2705 After parsePrimary, eventTarget:', eventTarget);
6216
6217
  }
6217
6218
  else {
6218
- debug.parse('❌ No "from" found, skipping target parsing');
6219
+ debug.parse('\u274c No "from" found, skipping target parsing');
6219
6220
  }
6220
6221
  }
6221
6222
  else {
@@ -7316,16 +7317,6 @@ function stringNode(value, tok) {
7316
7317
  column: tok.column,
7317
7318
  };
7318
7319
  }
7319
- function isNakedURLStart(ctx) {
7320
- const tok = ctx.peek();
7321
- if (tok.value === '/')
7322
- return true;
7323
- if (ctx.checkIdentifierLike()) {
7324
- const next = ctx.peekAt(1);
7325
- return !!next && next.value === ':' && next.start === tok.end;
7326
- }
7327
- return false;
7328
- }
7329
7320
  function parseGoCommand(ctx, identifierNode) {
7330
7321
  const args = [];
7331
7322
  while (!isCommandBoundary(ctx, ['when', 'where', 'catch', 'finally'])) {
@@ -7484,6 +7475,16 @@ function isFetchURLTerminator(ctx) {
7484
7475
  return (['as', 'with', '{', 'then', 'end', 'else', 'otherwise', ')'].includes(val) ||
7485
7476
  isCommandBoundary(ctx));
7486
7477
  }
7478
+ function isNakedURLStart(ctx) {
7479
+ const tok = ctx.peek();
7480
+ if (tok.value === '/')
7481
+ return true;
7482
+ if (ctx.checkIdentifierLike()) {
7483
+ const next = ctx.peekAt(1);
7484
+ return !!next && next.value === ':' && next.start === tok.end;
7485
+ }
7486
+ return false;
7487
+ }
7487
7488
  function parseBareURLPath(ctx, isTerminator = isFetchURLTerminator) {
7488
7489
  const startPos = ctx.savePosition();
7489
7490
  let path = '';
@@ -7505,7 +7506,7 @@ function parseBareURLPath(ctx, isTerminator = isFetchURLTerminator) {
7505
7506
  function parseFetchCommand(ctx, commandToken) {
7506
7507
  const modifiers = {};
7507
7508
  let url = null;
7508
- if (!ctx.isAtEnd() && ctx.check('/')) {
7509
+ if (!ctx.isAtEnd() && isNakedURLStart(ctx)) {
7509
7510
  url = parseBareURLPath(ctx);
7510
7511
  }
7511
7512
  if (!url) {
@@ -8071,26 +8072,26 @@ class Parser {
8071
8072
  const commandSequence = this.parseCommandSequence();
8072
8073
  if (commandSequence) {
8073
8074
  if (!this.isAtEnd() && this.check('on')) {
8074
- debug.parse('✅ PARSER: Found event handlers after command sequence, parsing as program');
8075
+ debug.parse('\u2705 PARSER: Found event handlers after command sequence, parsing as program');
8075
8076
  const statements = [commandSequence];
8076
- debug.parse(`✅ PARSER: Starting with ${statements.length} statement(s) from command sequence`);
8077
+ debug.parse(`\u2705 PARSER: Starting with ${statements.length} statement(s) from command sequence`);
8077
8078
  let eventHandlerCount = 0;
8078
8079
  while (!this.isAtEnd() && this.check('on')) {
8079
- debug.parse(`✅ PARSER: Parsing event handler #${eventHandlerCount + 1}, current token: ${this.peek().value}`);
8080
+ debug.parse(`\u2705 PARSER: Parsing event handler #${eventHandlerCount + 1}, current token: ${this.peek().value}`);
8080
8081
  this.advance();
8081
8082
  const eventHandler = this.parseEventHandler();
8082
- debug.parse(`✅ PARSER: parseEventHandler returned:`, eventHandler
8083
+ debug.parse(`\u2705 PARSER: parseEventHandler returned:`, eventHandler
8083
8084
  ? `type=${eventHandler.type}, event=${eventHandler.event}`
8084
8085
  : 'null');
8085
8086
  if (eventHandler) {
8086
8087
  statements.push(eventHandler);
8087
8088
  eventHandlerCount++;
8088
- debug.parse(`✅ PARSER: Added event handler, now have ${statements.length} statements total`);
8089
+ debug.parse(`\u2705 PARSER: Added event handler, now have ${statements.length} statements total`);
8089
8090
  }
8090
- debug.parse(`✅ PARSER: After parsing event handler, current token: ${this.isAtEnd() ? 'END' : this.peek().value}, isAtEnd=${this.isAtEnd()}, check('on')=${this.check('on')}`);
8091
+ debug.parse(`\u2705 PARSER: After parsing event handler, current token: ${this.isAtEnd() ? 'END' : this.peek().value}, isAtEnd=${this.isAtEnd()}, check('on')=${this.check('on')}`);
8091
8092
  if (!this.isAtEnd() && !this.check('on')) {
8092
8093
  if (!this.isAtEnd()) {
8093
- debug.parse(`⚠️ PARSER: Unexpected token after event handlers: ${this.peek().value}`);
8094
+ debug.parse(`\u26a0\ufe0f PARSER: Unexpected token after event handlers: ${this.peek().value}`);
8094
8095
  this.addError(`Unexpected token after event handlers: ${this.peek().value}`);
8095
8096
  return {
8096
8097
  success: false,
@@ -8099,11 +8100,11 @@ class Parser {
8099
8100
  error: this.error,
8100
8101
  };
8101
8102
  }
8102
- debug.parse(`✅ PARSER: No more event handlers, at end of input`);
8103
+ debug.parse(`\u2705 PARSER: No more event handlers, at end of input`);
8103
8104
  break;
8104
8105
  }
8105
8106
  }
8106
- debug.parse(`✅ PARSER: Finished parsing, creating Program node with ${statements.length} statements`);
8107
+ debug.parse(`\u2705 PARSER: Finished parsing, creating Program node with ${statements.length} statements`);
8107
8108
  if (this.error) {
8108
8109
  return {
8109
8110
  success: false,
@@ -8498,41 +8499,41 @@ class Parser {
8498
8499
  parseCommandListUntilTerminator(extraStops) {
8499
8500
  const commands = [];
8500
8501
  const isStop = () => this.check('end') || extraStops.some(s => this.check(s));
8501
- debug.parse('🔄 parseCommandListUntilTerminator: Starting (extraStops:', extraStops.join(','), ')');
8502
+ debug.parse('\ud83d\udd04 parseCommandListUntilTerminator: Starting (extraStops:', extraStops.join(','), ')');
8502
8503
  while (!this.isAtEnd() && !isStop()) {
8503
- debug.parse('📍 Loop iteration, current token:', this.peek().value, 'kind:', this.peek().kind);
8504
+ debug.parse('\ud83d\udccd Loop iteration, current token:', this.peek().value, 'kind:', this.peek().kind);
8504
8505
  let parsedCommand = false;
8505
8506
  const isCommandToken = this.checkIsCommand();
8506
8507
  const isCommandIdentifier = !isCommandToken && this.checkIdentifier() && this.isCommand(this.peek().value);
8507
8508
  if (isCommandToken || isCommandIdentifier) {
8508
- debug.parse(isCommandToken ? '✅ Found COMMAND token:' : '✅ Found IDENTIFIER that is a command:', this.peek().value);
8509
+ debug.parse(isCommandToken ? '\u2705 Found COMMAND token:' : '\u2705 Found IDENTIFIER that is a command:', this.peek().value);
8509
8510
  this.advance();
8510
8511
  const savedError = this.error;
8511
8512
  try {
8512
8513
  const cmd = this.parseCommand();
8513
8514
  if (this.error && this.error !== savedError) {
8514
- debug.parse('⚠️ parseCommandListUntilTerminator: Command parsing added error, restoring. Error was:', this.error.message);
8515
+ debug.parse('\u26a0\ufe0f parseCommandListUntilTerminator: Command parsing added error, restoring. Error was:', this.error.message);
8515
8516
  this.error = savedError;
8516
8517
  }
8517
8518
  if (cmd) {
8518
- debug.parse('✅ Parsed command:', cmd.name);
8519
+ debug.parse('\u2705 Parsed command:', cmd.name);
8519
8520
  commands.push(cmd);
8520
8521
  parsedCommand = true;
8521
8522
  }
8522
8523
  }
8523
8524
  catch (error) {
8524
- debug.parse('⚠️ parseCommandListUntilTerminator: Command parsing threw, restoring:', error instanceof Error ? error.message : String(error));
8525
+ debug.parse('\u26a0\ufe0f parseCommandListUntilTerminator: Command parsing threw, restoring:', error instanceof Error ? error.message : String(error));
8525
8526
  this.error = savedError;
8526
8527
  }
8527
8528
  }
8528
8529
  else if (this.checkIdentifier()) {
8529
- debug.parse('❌ IDENTIFIER is not a command:', this.peek().value);
8530
+ debug.parse('\u274c IDENTIFIER is not a command:', this.peek().value);
8530
8531
  }
8531
8532
  if (!parsedCommand) {
8532
- debug.parse('❌ No command parsed, breaking. Current token:', this.peek().value);
8533
+ debug.parse('\u274c No command parsed, breaking. Current token:', this.peek().value);
8533
8534
  break;
8534
8535
  }
8535
- debug.parse('📍 After parsing command, current token:', this.peek().value);
8536
+ debug.parse('\ud83d\udccd After parsing command, current token:', this.peek().value);
8536
8537
  while (!this.isAtEnd() &&
8537
8538
  !isStop() &&
8538
8539
  !this.checkIsCommand() &&
@@ -8540,19 +8541,19 @@ class Parser {
8540
8541
  !this.check('then') &&
8541
8542
  !this.check('and') &&
8542
8543
  !this.check(',')) {
8543
- debug.parse('⚠️ Skipping unexpected token:', this.peek().value);
8544
+ debug.parse('\u26a0\ufe0f Skipping unexpected token:', this.peek().value);
8544
8545
  this.advance();
8545
8546
  }
8546
8547
  if (this.match('then', 'and', ',')) {
8547
- debug.parse('✅ Found separator, continuing');
8548
+ debug.parse('\u2705 Found separator, continuing');
8548
8549
  continue;
8549
8550
  }
8550
8551
  else if (this.checkIsCommand() || this.isCommand(this.peek().value)) {
8551
- debug.parse('✅ Next token is a command, continuing without separator');
8552
+ debug.parse('\u2705 Next token is a command, continuing without separator');
8552
8553
  continue;
8553
8554
  }
8554
8555
  else {
8555
- debug.parse('📍 No separator and no command, breaking. Current token:', this.peek().value);
8556
+ debug.parse('\ud83d\udccd No separator and no command, breaking. Current token:', this.peek().value);
8556
8557
  break;
8557
8558
  }
8558
8559
  }
@@ -8566,18 +8567,18 @@ class Parser {
8566
8567
  if (!this.check('end') && !extraStops.some(s => this.check(s))) {
8567
8568
  terminator = '';
8568
8569
  }
8569
- debug.parse('✅ parseCommandListUntilTerminator: parsed', commands.length, 'commands (terminator:', terminator, ')');
8570
+ debug.parse('\u2705 parseCommandListUntilTerminator: parsed', commands.length, 'commands (terminator:', terminator, ')');
8570
8571
  return { commands, terminator };
8571
8572
  }
8572
8573
  parseCommandListUntilEnd() {
8573
8574
  const { commands } = this.parseCommandListUntilTerminator([]);
8574
- debug.parse('🔍 After loop, checking for "end". Current token:', this.peek().value);
8575
+ debug.parse('\ud83d\udd0d After loop, checking for "end". Current token:', this.peek().value);
8575
8576
  if (this.check('end')) {
8576
- debug.parse('✅ Found "end", consuming it');
8577
+ debug.parse('\u2705 Found "end", consuming it');
8577
8578
  this.advance();
8578
8579
  }
8579
8580
  else {
8580
- debug.parse('❌ ERROR: Expected "end" but got:', this.peek().value, 'at position:', this.peek().start);
8581
+ debug.parse('\u274c ERROR: Expected "end" but got:', this.peek().value, 'at position:', this.peek().start);
8581
8582
  throw new Error('Expected "end" to close repeat block');
8582
8583
  }
8583
8584
  return commands;
@@ -9250,6 +9251,23 @@ class Parser {
9250
9251
  this.consume(')', "Expected ')' after parameter list");
9251
9252
  }
9252
9253
  const bodyCommands = this.parseCommandBlock(['end', 'catch', 'finally']);
9254
+ const { errorSymbol, errorHandler, finallyHandler } = this.parseErrorAndFinallyBlocks();
9255
+ this.consume('end', "Expected 'end' after function definition");
9256
+ return {
9257
+ type: 'def',
9258
+ name: funcName,
9259
+ params,
9260
+ body: bodyCommands,
9261
+ ...(errorSymbol !== undefined && { errorSymbol }),
9262
+ ...(errorHandler !== undefined && { errorHandler }),
9263
+ ...(finallyHandler !== undefined && { finallyHandler }),
9264
+ start: pos.start,
9265
+ end: this.getPosition().end,
9266
+ line: pos.line,
9267
+ column: pos.column,
9268
+ };
9269
+ }
9270
+ parseErrorAndFinallyBlocks() {
9253
9271
  let errorSymbol;
9254
9272
  let errorHandler;
9255
9273
  if (this.check('catch')) {
@@ -9264,19 +9282,10 @@ class Parser {
9264
9282
  this.advance();
9265
9283
  finallyHandler = this.parseCommandBlock(['end']);
9266
9284
  }
9267
- this.consume('end', "Expected 'end' after function definition");
9268
9285
  return {
9269
- type: 'def',
9270
- name: funcName,
9271
- params,
9272
- body: bodyCommands,
9273
9286
  ...(errorSymbol !== undefined && { errorSymbol }),
9274
9287
  ...(errorHandler !== undefined && { errorHandler }),
9275
9288
  ...(finallyHandler !== undefined && { finallyHandler }),
9276
- start: pos.start,
9277
- end: this.getPosition().end,
9278
- line: pos.line,
9279
- column: pos.column,
9280
9289
  };
9281
9290
  }
9282
9291
  parseEventNameWithNamespace(errorMessage) {
@@ -9421,13 +9430,13 @@ class Parser {
9421
9430
  try {
9422
9431
  const cmd = this.parseCommand();
9423
9432
  if (this.error && this.error !== savedError) {
9424
- debug.parse('⚠️ Command parsing added error, restoring error state. Error was:', this.error.message);
9433
+ debug.parse('\u26a0\ufe0f Command parsing added error, restoring error state. Error was:', this.error.message);
9425
9434
  this.error = savedError;
9426
9435
  }
9427
9436
  return cmd;
9428
9437
  }
9429
9438
  catch (error) {
9430
- debug.parse('⚠️ Command parsing threw exception, restoring error state:', error instanceof Error ? error.message : String(error));
9439
+ debug.parse('\u26a0\ufe0f Command parsing threw exception, restoring error state:', error instanceof Error ? error.message : String(error));
9431
9440
  this.error = savedError;
9432
9441
  return null;
9433
9442
  }
@@ -9444,12 +9453,12 @@ class Parser {
9444
9453
  const timeToken = this.advance();
9445
9454
  const delayMs = parseTimeToMs(timeToken.value);
9446
9455
  modifiers[modifierKey] = delayMs;
9447
- debug.parse(`🔧 parseEventHandler: Parsed '${modName} at ${timeToken.value}' (${delayMs}ms)`);
9456
+ debug.parse(`\ud83d\udd27 parseEventHandler: Parsed '${modName} at ${timeToken.value}' (${delayMs}ms)`);
9448
9457
  }
9449
9458
  else if (this.matchNumber()) {
9450
9459
  const delayMs = parseInt(this.previous().value, 10);
9451
9460
  modifiers[modifierKey] = delayMs;
9452
- debug.parse(`🔧 parseEventHandler: Parsed '${modName} at ${delayMs}'`);
9461
+ debug.parse(`\ud83d\udd27 parseEventHandler: Parsed '${modName} at ${delayMs}'`);
9453
9462
  }
9454
9463
  else {
9455
9464
  this.addError(`Expected time expression after '${modName} at', got: ${this.peek().value}`);
@@ -9460,7 +9469,7 @@ class Parser {
9460
9469
  }
9461
9470
  }
9462
9471
  parseEventHandler() {
9463
- debug.parse(`🔧 parseEventHandler: ENTRY - parsing event handler`);
9472
+ debug.parse(`\ud83d\udd27 parseEventHandler: ENTRY - parsing event handler`);
9464
9473
  const eventNames = [];
9465
9474
  let firstOnceAlias = false;
9466
9475
  if (this.check('first') && !this.checkIsCommand() && this.current + 1 < this.tokens.length) {
@@ -9469,27 +9478,27 @@ class Parser {
9469
9478
  if (peek2Value && peek2Value !== 'of' && peek2Value !== 'in' && peek2Value !== 'from') {
9470
9479
  this.advance();
9471
9480
  firstOnceAlias = true;
9472
- debug.parse(`🔧 parseEventHandler: Parsed 'first' as .once alias`);
9481
+ debug.parse(`\ud83d\udd27 parseEventHandler: Parsed 'first' as .once alias`);
9473
9482
  }
9474
9483
  }
9475
9484
  const event = this.parseEventNameWithNamespace("Expected event name after 'on'");
9476
9485
  eventNames.push(event);
9477
- debug.parse(`🔧 parseEventHandler: Parsed first event name: ${event}`);
9486
+ debug.parse(`\ud83d\udd27 parseEventHandler: Parsed first event name: ${event}`);
9478
9487
  while (this.check('or')) {
9479
9488
  this.advance();
9480
- debug.parse(`🔧 parseEventHandler: Found 'or', parsing additional event name`);
9489
+ debug.parse(`\ud83d\udd27 parseEventHandler: Found 'or', parsing additional event name`);
9481
9490
  const additionalEvent = this.parseEventNameWithNamespace("Expected event name after 'or'");
9482
9491
  eventNames.push(additionalEvent);
9483
- debug.parse(`🔧 parseEventHandler: Parsed additional event name: ${additionalEvent}`);
9492
+ debug.parse(`\ud83d\udd27 parseEventHandler: Parsed additional event name: ${additionalEvent}`);
9484
9493
  }
9485
- debug.parse(`🔧 parseEventHandler: Total events parsed: ${eventNames.join(', ')}`);
9494
+ debug.parse(`\ud83d\udd27 parseEventHandler: Total events parsed: ${eventNames.join(', ')}`);
9486
9495
  let customEventSource;
9487
9496
  if (this.registryIntegration) {
9488
9497
  const firstEvent = eventNames[0];
9489
9498
  if (this.registryIntegration.hasEventSource(firstEvent)) {
9490
9499
  const source = this.registryIntegration.getEventSource(firstEvent);
9491
9500
  customEventSource = source?.name;
9492
- debug.parse(`🔧 parseEventHandler: Detected custom event source '${customEventSource}' for event '${firstEvent}'`);
9501
+ debug.parse(`\ud83d\udd27 parseEventHandler: Detected custom event source '${customEventSource}' for event '${firstEvent}'`);
9493
9502
  }
9494
9503
  }
9495
9504
  const eventParams = [];
@@ -9501,7 +9510,7 @@ class Parser {
9501
9510
  } while (this.match(','));
9502
9511
  }
9503
9512
  this.consume(')', "Expected ')' after event parameters");
9504
- debug.parse(`🔧 parseEventHandler: Parsed event parameters: ${eventParams.join(', ')}`);
9513
+ debug.parse(`\ud83d\udd27 parseEventHandler: Parsed event parameters: ${eventParams.join(', ')}`);
9505
9514
  }
9506
9515
  const modifiers = {};
9507
9516
  while (this.check('.')) {
@@ -9510,15 +9519,15 @@ class Parser {
9510
9519
  const modName = modToken.value.toLowerCase();
9511
9520
  if (modName === 'once') {
9512
9521
  modifiers.once = true;
9513
- debug.parse(`🔧 parseEventHandler: Parsed modifier '.once'`);
9522
+ debug.parse(`\ud83d\udd27 parseEventHandler: Parsed modifier '.once'`);
9514
9523
  }
9515
9524
  else if (modName === 'prevent') {
9516
9525
  modifiers.prevent = true;
9517
- debug.parse(`🔧 parseEventHandler: Parsed modifier '.prevent'`);
9526
+ debug.parse(`\ud83d\udd27 parseEventHandler: Parsed modifier '.prevent'`);
9518
9527
  }
9519
9528
  else if (modName === 'stop') {
9520
9529
  modifiers.stop = true;
9521
- debug.parse(`🔧 parseEventHandler: Parsed modifier '.stop'`);
9530
+ debug.parse(`\ud83d\udd27 parseEventHandler: Parsed modifier '.stop'`);
9522
9531
  }
9523
9532
  else if (modName === 'debounce' || modName === 'throttle') {
9524
9533
  if (this.check('(')) {
@@ -9530,11 +9539,11 @@ class Parser {
9530
9539
  }
9531
9540
  if (modName === 'debounce') {
9532
9541
  modifiers.debounce = delayMs;
9533
- debug.parse(`🔧 parseEventHandler: Parsed modifier '.debounce(${delayMs})'`);
9542
+ debug.parse(`\ud83d\udd27 parseEventHandler: Parsed modifier '.debounce(${delayMs})'`);
9534
9543
  }
9535
9544
  else {
9536
9545
  modifiers.throttle = delayMs;
9537
- debug.parse(`🔧 parseEventHandler: Parsed modifier '.throttle(${delayMs})'`);
9546
+ debug.parse(`\ud83d\udd27 parseEventHandler: Parsed modifier '.throttle(${delayMs})'`);
9538
9547
  }
9539
9548
  this.consume(')', `Expected ')' after ${modName} delay`);
9540
9549
  }
@@ -9543,7 +9552,7 @@ class Parser {
9543
9552
  }
9544
9553
  }
9545
9554
  else {
9546
- debug.parse(`🔧 parseEventHandler: Warning - unknown modifier '.${modName}'`);
9555
+ debug.parse(`\ud83d\udd27 parseEventHandler: Warning - unknown modifier '.${modName}'`);
9547
9556
  }
9548
9557
  }
9549
9558
  this.parseTemporalKeywordModifiers(modifiers);
@@ -9551,7 +9560,7 @@ class Parser {
9551
9560
  modifiers.once = true;
9552
9561
  }
9553
9562
  if (Object.keys(modifiers).length > 0) {
9554
- debug.parse(`🔧 parseEventHandler: Parsed modifiers:`, modifiers);
9563
+ debug.parse(`\ud83d\udd27 parseEventHandler: Parsed modifiers:`, modifiers);
9555
9564
  }
9556
9565
  let condition;
9557
9566
  if (this.match('[')) {
@@ -9562,7 +9571,7 @@ class Parser {
9562
9571
  if (this.match('from')) {
9563
9572
  const targetToken = this.advance();
9564
9573
  target = targetToken.value;
9565
- debug.parse(`🔧 parseEventHandler: Parsed 'from' target: ${target} (kind: ${targetToken.kind})`);
9574
+ debug.parse(`\ud83d\udd27 parseEventHandler: Parsed 'from' target: ${target} (kind: ${targetToken.kind})`);
9566
9575
  this.parseTemporalKeywordModifiers(modifiers);
9567
9576
  }
9568
9577
  let attributeName;
@@ -9574,32 +9583,36 @@ class Parser {
9574
9583
  else {
9575
9584
  attributeName = attrToken.value;
9576
9585
  }
9577
- debug.parse(`🔧 parseEventHandler: Parsed attribute name: ${attributeName}`);
9586
+ debug.parse(`\ud83d\udd27 parseEventHandler: Parsed attribute name: ${attributeName}`);
9578
9587
  }
9579
9588
  let watchTarget;
9580
9589
  if (this.match('in')) {
9581
- debug.parse(`🔧 parseEventHandler: Found 'in' keyword, parsing watch target`);
9590
+ debug.parse(`\ud83d\udd27 parseEventHandler: Found 'in' keyword, parsing watch target`);
9582
9591
  watchTarget = this.parseExpression();
9583
- debug.parse(`🔧 parseEventHandler: Parsed watch target expression`);
9592
+ debug.parse(`\ud83d\udd27 parseEventHandler: Parsed watch target expression`);
9584
9593
  }
9585
9594
  const commands = [];
9586
- debug.parse(`✅ parseEventHandler: About to parse commands, current token: ${this.isAtEnd() ? 'END' : this.peek().value}, isAtEnd: ${this.isAtEnd()}`);
9595
+ debug.parse(`\u2705 parseEventHandler: About to parse commands, current token: ${this.isAtEnd() ? 'END' : this.peek().value}, isAtEnd: ${this.isAtEnd()}`);
9587
9596
  while (!this.isAtEnd()) {
9588
- debug.parse(`✅ parseEventHandler: Loop iteration, current token: ${this.peek().value}, kind: ${this.peek().kind}`);
9597
+ debug.parse(`\u2705 parseEventHandler: Loop iteration, current token: ${this.peek().value}, kind: ${this.peek().kind}`);
9589
9598
  if (this.check('on')) {
9590
- debug.parse(`✅ parseEventHandler: Stopping command parsing, found next event handler 'on'`);
9599
+ debug.parse(`\u2705 parseEventHandler: Stopping command parsing, found next event handler 'on'`);
9591
9600
  break;
9592
9601
  }
9593
9602
  if (this.checkComment()) {
9594
- debug.parse(`✅ parseEventHandler: Skipping comment token: ${this.peek().value}`);
9603
+ debug.parse(`\u2705 parseEventHandler: Skipping comment token: ${this.peek().value}`);
9595
9604
  this.advance();
9596
9605
  continue;
9597
9606
  }
9598
9607
  if (this.check('end')) {
9599
- debug.parse(`✅ parseEventHandler: Stopping command parsing, found 'end' keyword`);
9608
+ debug.parse(`\u2705 parseEventHandler: Stopping command parsing, found 'end' keyword`);
9600
9609
  this.advance();
9601
9610
  break;
9602
9611
  }
9612
+ if (this.check('catch') || this.check('finally')) {
9613
+ debug.parse(`\u2705 parseEventHandler: Stopping command parsing, found '${this.peek().value}'`);
9614
+ break;
9615
+ }
9603
9616
  if (this.checkIsCommand()) {
9604
9617
  const nextIsOpenParen = this.tokens[this.current + 1]?.value === '(';
9605
9618
  const commandName = this.peek().value.toLowerCase();
@@ -9628,7 +9641,7 @@ class Parser {
9628
9641
  const cmd = this.parseCommandWithErrorRecovery();
9629
9642
  if (cmd) {
9630
9643
  commands.push(cmd);
9631
- debug.parse(`✅ parseEventHandler: Parsed command, next token: ${this.isAtEnd() ? 'END' : this.peek().value}`);
9644
+ debug.parse(`\u2705 parseEventHandler: Parsed command, next token: ${this.isAtEnd() ? 'END' : this.peek().value}`);
9632
9645
  }
9633
9646
  }
9634
9647
  }
@@ -9646,7 +9659,7 @@ class Parser {
9646
9659
  expr = this.parseExpression();
9647
9660
  }
9648
9661
  catch (error) {
9649
- debug.parse('⚠️ Expression parsing error, restoring error state:', error instanceof Error ? error.message : String(error));
9662
+ debug.parse('\u26a0\ufe0f Expression parsing error, restoring error state:', error instanceof Error ? error.message : String(error));
9650
9663
  this.error = savedError;
9651
9664
  break;
9652
9665
  }
@@ -9693,7 +9706,9 @@ class Parser {
9693
9706
  !this.check('then') &&
9694
9707
  !this.check('and') &&
9695
9708
  !this.check(',') &&
9696
- !this.check('on')) {
9709
+ !this.check('on') &&
9710
+ !this.check('catch') &&
9711
+ !this.check('finally')) {
9697
9712
  this.advance();
9698
9713
  }
9699
9714
  if (this.match('then', 'and', ',')) {
@@ -9706,12 +9721,24 @@ class Parser {
9706
9721
  break;
9707
9722
  }
9708
9723
  }
9724
+ let errorSymbol;
9725
+ let errorHandler;
9726
+ let finallyHandler;
9727
+ if (!this.isAtEnd() && (this.check('catch') || this.check('finally'))) {
9728
+ ({ errorSymbol, errorHandler, finallyHandler } = this.parseErrorAndFinallyBlocks());
9729
+ if (this.check('end')) {
9730
+ this.advance();
9731
+ }
9732
+ }
9709
9733
  const pos = this.getPosition();
9710
9734
  const node = {
9711
9735
  type: 'eventHandler',
9712
9736
  event: eventNames.length === 1 ? eventNames[0] : eventNames.join('|'),
9713
9737
  events: eventNames,
9714
9738
  commands,
9739
+ ...(errorSymbol !== undefined && { errorSymbol }),
9740
+ ...(errorHandler !== undefined && { errorHandler }),
9741
+ ...(finallyHandler !== undefined && { finallyHandler }),
9715
9742
  ...(eventParams.length > 0 && { args: eventParams }),
9716
9743
  ...(condition && { condition }),
9717
9744
  ...(target && { target }),
@@ -9724,7 +9751,7 @@ class Parser {
9724
9751
  line: pos.line,
9725
9752
  column: pos.column,
9726
9753
  };
9727
- debug.parse(`🔧 parseEventHandler: Created node with events: ${eventNames.join(', ')}, attributeName: ${attributeName || 'none'}, watchTarget: ${watchTarget ? 'yes' : 'none'}`);
9754
+ debug.parse(`\ud83d\udd27 parseEventHandler: Created node with events: ${eventNames.join(', ')}, attributeName: ${attributeName || 'none'}, watchTarget: ${watchTarget ? 'yes' : 'none'}`);
9728
9755
  return node;
9729
9756
  }
9730
9757
  parseBehaviorDefinition() {
@@ -9789,11 +9816,33 @@ class Parser {
9789
9816
  }
9790
9817
  }
9791
9818
  }
9792
- const handlerCommands = this.parseCommandListUntilEnd();
9819
+ const { commands: handlerCommands, terminator } = this.parseCommandListUntilTerminator([
9820
+ 'catch',
9821
+ 'finally',
9822
+ ]);
9823
+ let handlerErrorSymbol;
9824
+ let handlerErrorHandler;
9825
+ let handlerFinallyHandler;
9826
+ if (terminator === 'catch' || terminator === 'finally') {
9827
+ ({
9828
+ errorSymbol: handlerErrorSymbol,
9829
+ errorHandler: handlerErrorHandler,
9830
+ finallyHandler: handlerFinallyHandler,
9831
+ } = this.parseErrorAndFinallyBlocks());
9832
+ }
9833
+ if (this.check('end')) {
9834
+ this.advance();
9835
+ }
9836
+ else {
9837
+ throw new Error('Expected "end" to close behavior event handler');
9838
+ }
9793
9839
  const handlerNode = {
9794
9840
  type: 'eventHandler',
9795
9841
  event: eventName,
9796
9842
  commands: handlerCommands,
9843
+ ...(handlerErrorSymbol !== undefined && { errorSymbol: handlerErrorSymbol }),
9844
+ ...(handlerErrorHandler !== undefined && { errorHandler: handlerErrorHandler }),
9845
+ ...(handlerFinallyHandler !== undefined && { finallyHandler: handlerFinallyHandler }),
9797
9846
  ...(eventSource !== undefined && { target: eventSource }),
9798
9847
  ...(eventArgs.length > 0 && { args: eventArgs }),
9799
9848
  start: handlerPos.start,
@@ -9848,7 +9897,7 @@ class Parser {
9848
9897
  this.error.message.includes('unclosed parenthes') ||
9849
9898
  this.error.message.includes('Unclosed parenthes');
9850
9899
  if (!isCriticalError) {
9851
- debug.parse('⚠️ parseCommandSequence: Command parsing added non-critical error, restoring error state. Error was:', this.error.message);
9900
+ debug.parse('\u26a0\ufe0f parseCommandSequence: Command parsing added non-critical error, restoring error state. Error was:', this.error.message);
9852
9901
  this.error = savedError;
9853
9902
  }
9854
9903
  }
@@ -9858,11 +9907,11 @@ class Parser {
9858
9907
  !this.isCommand(this.peek().value) &&
9859
9908
  !this.check('then') &&
9860
9909
  !this.check('on')) {
9861
- debug.parse('⚠️ parseCommandSequence: Skipping unexpected token:', this.peek().value);
9910
+ debug.parse('\u26a0\ufe0f parseCommandSequence: Skipping unexpected token:', this.peek().value);
9862
9911
  this.advance();
9863
9912
  }
9864
9913
  if (this.check('on')) {
9865
- debug.parse('✅ parseCommandSequence: Found "on" token, stopping command sequence to allow event handler parsing');
9914
+ debug.parse('\u2705 parseCommandSequence: Found "on" token, stopping command sequence to allow event handler parsing');
9866
9915
  break;
9867
9916
  }
9868
9917
  if (this.match('then')) {
@@ -10306,7 +10355,7 @@ class Parser {
10306
10355
  if (!this.check(')') && callee.type === 'identifier') {
10307
10356
  const funcName = callee.name;
10308
10357
  if (CommandClassification.isCSSFunction(funcName)) {
10309
- debug.parse(`💡 Tip: CSS functions like ${funcName}() should be quoted for clean parsing. ` +
10358
+ debug.parse(`\ud83d\udca1 Tip: CSS functions like ${funcName}() should be quoted for clean parsing. ` +
10310
10359
  `Use '${funcName}(...)' or \`${funcName}(...)\` instead.`);
10311
10360
  }
10312
10361
  }
@@ -10608,7 +10657,7 @@ class Parser {
10608
10657
  if (this.checkIsCommand() || (this.isCommand(token.value) && !this.isKeyword(token.value))) {
10609
10658
  return;
10610
10659
  }
10611
- debug.parse('⚠️ synchronize: Skipping token:', token.value);
10660
+ debug.parse('\u26a0\ufe0f synchronize: Skipping token:', token.value);
10612
10661
  this.advance();
10613
10662
  }
10614
10663
  }