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