@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
@@ -424,6 +424,7 @@ function tokenize(input) {
424
424
  const prevToken = tokenizer.tokens[tokenizer.tokens.length - 1];
425
425
  const isPossessive = nextChar === 's' &&
426
426
  prevToken &&
427
+ !/\s/.test(tokenizer.input[tokenizer.position - 1] ?? ' ') &&
427
428
  (prevToken.kind === TokenKind.IDENTIFIER ||
428
429
  prevToken.kind === TokenKind.SELECTOR ||
429
430
  prevToken.value === ']' ||
@@ -1296,39 +1297,39 @@ const DEBUG = {
1296
1297
  const debug = {
1297
1298
  command: (...args) => {
1298
1299
  if (DEBUG.commands)
1299
- console.log('🔧', ...args);
1300
+ console.log('\ud83d\udd27', ...args);
1300
1301
  },
1301
1302
  event: (...args) => {
1302
1303
  if (DEBUG.events)
1303
- console.log('🎯', ...args);
1304
+ console.log('\ud83c\udfaf', ...args);
1304
1305
  },
1305
1306
  parse: (...args) => {
1306
1307
  if (DEBUG.parsing)
1307
- console.log('📝', ...args);
1308
+ console.log('\ud83d\udcdd', ...args);
1308
1309
  },
1309
1310
  expr: (...args) => {
1310
1311
  if (DEBUG.expressions)
1311
- console.log('🔍', ...args);
1312
+ console.log('\ud83d\udd0d', ...args);
1312
1313
  },
1313
1314
  expressions: (...args) => {
1314
1315
  if (DEBUG.expressions)
1315
- console.log('🔍', ...args);
1316
+ console.log('\ud83d\udd0d', ...args);
1316
1317
  },
1317
1318
  style: (...args) => {
1318
1319
  if (DEBUG.styles)
1319
- console.log('🎨', ...args);
1320
+ console.log('\ud83c\udfa8', ...args);
1320
1321
  },
1321
1322
  runtime: (...args) => {
1322
1323
  if (DEBUG.runtime)
1323
- console.log('🚀', ...args);
1324
+ console.log('\ud83d\ude80', ...args);
1324
1325
  },
1325
1326
  loop: (...args) => {
1326
1327
  if (DEBUG.loops)
1327
- console.log('🔁', ...args);
1328
+ console.log('\ud83d\udd01', ...args);
1328
1329
  },
1329
1330
  async: (...args) => {
1330
1331
  if (DEBUG.async)
1331
- console.log('⏳', ...args);
1332
+ console.log('\u23f3', ...args);
1332
1333
  },
1333
1334
  };
1334
1335
 
@@ -2726,9 +2727,9 @@ function createPartialCommandNode(name, pos) {
2726
2727
  };
2727
2728
  }
2728
2729
  function createProgramNode(statements) {
2729
- debug.parse(`✅ createProgramNode: Called with ${statements.length} statements`);
2730
+ debug.parse(`\u2705 createProgramNode: Called with ${statements.length} statements`);
2730
2731
  if (statements.length === 0) {
2731
- debug.parse(`✅ createProgramNode: Returning error node (0 statements)`);
2732
+ debug.parse(`\u2705 createProgramNode: Returning error node (0 statements)`);
2732
2733
  return {
2733
2734
  type: 'identifier',
2734
2735
  name: '__ERROR__',
@@ -2739,7 +2740,7 @@ function createProgramNode(statements) {
2739
2740
  };
2740
2741
  }
2741
2742
  if (statements.length === 1) {
2742
- debug.parse(`✅ createProgramNode: Returning single statement (type=${statements[0].type})`);
2743
+ debug.parse(`\u2705 createProgramNode: Returning single statement (type=${statements[0].type})`);
2743
2744
  return statements[0];
2744
2745
  }
2745
2746
  const programNode = {
@@ -2750,7 +2751,7 @@ function createProgramNode(statements) {
2750
2751
  line: statements[0]?.line || 1,
2751
2752
  column: statements[0]?.column || 1,
2752
2753
  };
2753
- debug.parse(`✅ createProgramNode: Returning Program node with ${statements.length} statements, type=${programNode.type}`);
2754
+ debug.parse(`\u2705 createProgramNode: Returning Program node with ${statements.length} statements, type=${programNode.type}`);
2754
2755
  return programNode;
2755
2756
  }
2756
2757
 
@@ -3139,37 +3140,37 @@ function parseRepeatCommand(ctx, commandToken) {
3139
3140
  ctx.advance();
3140
3141
  loopType = 'until-event';
3141
3142
  const eventToken = ctx.peek();
3142
- debug.parse('📍 Parsing event name, current token:', {
3143
+ debug.parse('\ud83d\udccd Parsing event name, current token:', {
3143
3144
  value: eventToken.value,
3144
3145
  kind: eventToken.kind,
3145
3146
  });
3146
3147
  if (isIdentifierLike(eventToken) || isEvent(eventToken)) {
3147
3148
  eventName = eventToken.value;
3148
3149
  ctx.advance();
3149
- debug.parse('✅ Got event name:', eventName, 'Next token:', ctx.peek().value);
3150
+ debug.parse('\u2705 Got event name:', eventName, 'Next token:', ctx.peek().value);
3150
3151
  }
3151
3152
  else {
3152
3153
  throw new Error('Expected event name after "event"');
3153
3154
  }
3154
- debug.parse('🔍 Checking for "from", current token:', ctx.peek().value);
3155
+ debug.parse('\ud83d\udd0d Checking for "from", current token:', ctx.peek().value);
3155
3156
  if (ctx.check(KEYWORDS.FROM)) {
3156
- debug.parse('✅ Found "from", advancing...');
3157
+ debug.parse('\u2705 Found "from", advancing...');
3157
3158
  ctx.advance();
3158
- debug.parse('📍 After consuming "from", current token:', ctx.peek().value);
3159
+ debug.parse('\ud83d\udccd After consuming "from", current token:', ctx.peek().value);
3159
3160
  if (consumeOptionalKeyword(ctx, KEYWORDS.THE)) {
3160
- debug.parse('✅ Found "the", advancing...');
3161
+ debug.parse('\u2705 Found "the", advancing...');
3161
3162
  }
3162
3163
  const beforePrimary = ctx.peek();
3163
- debug.parse('🔍 Before parsePrimary for event target:', {
3164
+ debug.parse('\ud83d\udd0d Before parsePrimary for event target:', {
3164
3165
  value: beforePrimary.value,
3165
3166
  kind: beforePrimary.kind,
3166
3167
  position: beforePrimary.start,
3167
3168
  });
3168
3169
  eventTarget = ctx.parsePrimary();
3169
- debug.parse('✅ After parsePrimary, eventTarget:', eventTarget);
3170
+ debug.parse('\u2705 After parsePrimary, eventTarget:', eventTarget);
3170
3171
  }
3171
3172
  else {
3172
- debug.parse('❌ No "from" found, skipping target parsing');
3173
+ debug.parse('\u274c No "from" found, skipping target parsing');
3173
3174
  }
3174
3175
  }
3175
3176
  else {
@@ -4270,16 +4271,6 @@ function stringNode(value, tok) {
4270
4271
  column: tok.column,
4271
4272
  };
4272
4273
  }
4273
- function isNakedURLStart(ctx) {
4274
- const tok = ctx.peek();
4275
- if (tok.value === '/')
4276
- return true;
4277
- if (ctx.checkIdentifierLike()) {
4278
- const next = ctx.peekAt(1);
4279
- return !!next && next.value === ':' && next.start === tok.end;
4280
- }
4281
- return false;
4282
- }
4283
4274
  function parseGoCommand(ctx, identifierNode) {
4284
4275
  const args = [];
4285
4276
  while (!isCommandBoundary(ctx, ['when', 'where', 'catch', 'finally'])) {
@@ -4438,6 +4429,16 @@ function isFetchURLTerminator(ctx) {
4438
4429
  return (['as', 'with', '{', 'then', 'end', 'else', 'otherwise', ')'].includes(val) ||
4439
4430
  isCommandBoundary(ctx));
4440
4431
  }
4432
+ function isNakedURLStart(ctx) {
4433
+ const tok = ctx.peek();
4434
+ if (tok.value === '/')
4435
+ return true;
4436
+ if (ctx.checkIdentifierLike()) {
4437
+ const next = ctx.peekAt(1);
4438
+ return !!next && next.value === ':' && next.start === tok.end;
4439
+ }
4440
+ return false;
4441
+ }
4441
4442
  function parseBareURLPath(ctx, isTerminator = isFetchURLTerminator) {
4442
4443
  const startPos = ctx.savePosition();
4443
4444
  let path = '';
@@ -4459,7 +4460,7 @@ function parseBareURLPath(ctx, isTerminator = isFetchURLTerminator) {
4459
4460
  function parseFetchCommand(ctx, commandToken) {
4460
4461
  const modifiers = {};
4461
4462
  let url = null;
4462
- if (!ctx.isAtEnd() && ctx.check('/')) {
4463
+ if (!ctx.isAtEnd() && isNakedURLStart(ctx)) {
4463
4464
  url = parseBareURLPath(ctx);
4464
4465
  }
4465
4466
  if (!url) {
@@ -5025,26 +5026,26 @@ class Parser {
5025
5026
  const commandSequence = this.parseCommandSequence();
5026
5027
  if (commandSequence) {
5027
5028
  if (!this.isAtEnd() && this.check('on')) {
5028
- debug.parse('✅ PARSER: Found event handlers after command sequence, parsing as program');
5029
+ debug.parse('\u2705 PARSER: Found event handlers after command sequence, parsing as program');
5029
5030
  const statements = [commandSequence];
5030
- debug.parse(`✅ PARSER: Starting with ${statements.length} statement(s) from command sequence`);
5031
+ debug.parse(`\u2705 PARSER: Starting with ${statements.length} statement(s) from command sequence`);
5031
5032
  let eventHandlerCount = 0;
5032
5033
  while (!this.isAtEnd() && this.check('on')) {
5033
- debug.parse(`✅ PARSER: Parsing event handler #${eventHandlerCount + 1}, current token: ${this.peek().value}`);
5034
+ debug.parse(`\u2705 PARSER: Parsing event handler #${eventHandlerCount + 1}, current token: ${this.peek().value}`);
5034
5035
  this.advance();
5035
5036
  const eventHandler = this.parseEventHandler();
5036
- debug.parse(`✅ PARSER: parseEventHandler returned:`, eventHandler
5037
+ debug.parse(`\u2705 PARSER: parseEventHandler returned:`, eventHandler
5037
5038
  ? `type=${eventHandler.type}, event=${eventHandler.event}`
5038
5039
  : 'null');
5039
5040
  if (eventHandler) {
5040
5041
  statements.push(eventHandler);
5041
5042
  eventHandlerCount++;
5042
- debug.parse(`✅ PARSER: Added event handler, now have ${statements.length} statements total`);
5043
+ debug.parse(`\u2705 PARSER: Added event handler, now have ${statements.length} statements total`);
5043
5044
  }
5044
- debug.parse(`✅ PARSER: After parsing event handler, current token: ${this.isAtEnd() ? 'END' : this.peek().value}, isAtEnd=${this.isAtEnd()}, check('on')=${this.check('on')}`);
5045
+ debug.parse(`\u2705 PARSER: After parsing event handler, current token: ${this.isAtEnd() ? 'END' : this.peek().value}, isAtEnd=${this.isAtEnd()}, check('on')=${this.check('on')}`);
5045
5046
  if (!this.isAtEnd() && !this.check('on')) {
5046
5047
  if (!this.isAtEnd()) {
5047
- debug.parse(`⚠️ PARSER: Unexpected token after event handlers: ${this.peek().value}`);
5048
+ debug.parse(`\u26a0\ufe0f PARSER: Unexpected token after event handlers: ${this.peek().value}`);
5048
5049
  this.addError(`Unexpected token after event handlers: ${this.peek().value}`);
5049
5050
  return {
5050
5051
  success: false,
@@ -5053,11 +5054,11 @@ class Parser {
5053
5054
  error: this.error,
5054
5055
  };
5055
5056
  }
5056
- debug.parse(`✅ PARSER: No more event handlers, at end of input`);
5057
+ debug.parse(`\u2705 PARSER: No more event handlers, at end of input`);
5057
5058
  break;
5058
5059
  }
5059
5060
  }
5060
- debug.parse(`✅ PARSER: Finished parsing, creating Program node with ${statements.length} statements`);
5061
+ debug.parse(`\u2705 PARSER: Finished parsing, creating Program node with ${statements.length} statements`);
5061
5062
  if (this.error) {
5062
5063
  return {
5063
5064
  success: false,
@@ -5452,41 +5453,41 @@ class Parser {
5452
5453
  parseCommandListUntilTerminator(extraStops) {
5453
5454
  const commands = [];
5454
5455
  const isStop = () => this.check('end') || extraStops.some(s => this.check(s));
5455
- debug.parse('🔄 parseCommandListUntilTerminator: Starting (extraStops:', extraStops.join(','), ')');
5456
+ debug.parse('\ud83d\udd04 parseCommandListUntilTerminator: Starting (extraStops:', extraStops.join(','), ')');
5456
5457
  while (!this.isAtEnd() && !isStop()) {
5457
- debug.parse('📍 Loop iteration, current token:', this.peek().value, 'kind:', this.peek().kind);
5458
+ debug.parse('\ud83d\udccd Loop iteration, current token:', this.peek().value, 'kind:', this.peek().kind);
5458
5459
  let parsedCommand = false;
5459
5460
  const isCommandToken = this.checkIsCommand();
5460
5461
  const isCommandIdentifier = !isCommandToken && this.checkIdentifier() && this.isCommand(this.peek().value);
5461
5462
  if (isCommandToken || isCommandIdentifier) {
5462
- debug.parse(isCommandToken ? '✅ Found COMMAND token:' : '✅ Found IDENTIFIER that is a command:', this.peek().value);
5463
+ debug.parse(isCommandToken ? '\u2705 Found COMMAND token:' : '\u2705 Found IDENTIFIER that is a command:', this.peek().value);
5463
5464
  this.advance();
5464
5465
  const savedError = this.error;
5465
5466
  try {
5466
5467
  const cmd = this.parseCommand();
5467
5468
  if (this.error && this.error !== savedError) {
5468
- debug.parse('⚠️ parseCommandListUntilTerminator: Command parsing added error, restoring. Error was:', this.error.message);
5469
+ debug.parse('\u26a0\ufe0f parseCommandListUntilTerminator: Command parsing added error, restoring. Error was:', this.error.message);
5469
5470
  this.error = savedError;
5470
5471
  }
5471
5472
  if (cmd) {
5472
- debug.parse('✅ Parsed command:', cmd.name);
5473
+ debug.parse('\u2705 Parsed command:', cmd.name);
5473
5474
  commands.push(cmd);
5474
5475
  parsedCommand = true;
5475
5476
  }
5476
5477
  }
5477
5478
  catch (error) {
5478
- debug.parse('⚠️ parseCommandListUntilTerminator: Command parsing threw, restoring:', error instanceof Error ? error.message : String(error));
5479
+ debug.parse('\u26a0\ufe0f parseCommandListUntilTerminator: Command parsing threw, restoring:', error instanceof Error ? error.message : String(error));
5479
5480
  this.error = savedError;
5480
5481
  }
5481
5482
  }
5482
5483
  else if (this.checkIdentifier()) {
5483
- debug.parse('❌ IDENTIFIER is not a command:', this.peek().value);
5484
+ debug.parse('\u274c IDENTIFIER is not a command:', this.peek().value);
5484
5485
  }
5485
5486
  if (!parsedCommand) {
5486
- debug.parse('❌ No command parsed, breaking. Current token:', this.peek().value);
5487
+ debug.parse('\u274c No command parsed, breaking. Current token:', this.peek().value);
5487
5488
  break;
5488
5489
  }
5489
- debug.parse('📍 After parsing command, current token:', this.peek().value);
5490
+ debug.parse('\ud83d\udccd After parsing command, current token:', this.peek().value);
5490
5491
  while (!this.isAtEnd() &&
5491
5492
  !isStop() &&
5492
5493
  !this.checkIsCommand() &&
@@ -5494,19 +5495,19 @@ class Parser {
5494
5495
  !this.check('then') &&
5495
5496
  !this.check('and') &&
5496
5497
  !this.check(',')) {
5497
- debug.parse('⚠️ Skipping unexpected token:', this.peek().value);
5498
+ debug.parse('\u26a0\ufe0f Skipping unexpected token:', this.peek().value);
5498
5499
  this.advance();
5499
5500
  }
5500
5501
  if (this.match('then', 'and', ',')) {
5501
- debug.parse('✅ Found separator, continuing');
5502
+ debug.parse('\u2705 Found separator, continuing');
5502
5503
  continue;
5503
5504
  }
5504
5505
  else if (this.checkIsCommand() || this.isCommand(this.peek().value)) {
5505
- debug.parse('✅ Next token is a command, continuing without separator');
5506
+ debug.parse('\u2705 Next token is a command, continuing without separator');
5506
5507
  continue;
5507
5508
  }
5508
5509
  else {
5509
- debug.parse('📍 No separator and no command, breaking. Current token:', this.peek().value);
5510
+ debug.parse('\ud83d\udccd No separator and no command, breaking. Current token:', this.peek().value);
5510
5511
  break;
5511
5512
  }
5512
5513
  }
@@ -5520,18 +5521,18 @@ class Parser {
5520
5521
  if (!this.check('end') && !extraStops.some(s => this.check(s))) {
5521
5522
  terminator = '';
5522
5523
  }
5523
- debug.parse('✅ parseCommandListUntilTerminator: parsed', commands.length, 'commands (terminator:', terminator, ')');
5524
+ debug.parse('\u2705 parseCommandListUntilTerminator: parsed', commands.length, 'commands (terminator:', terminator, ')');
5524
5525
  return { commands, terminator };
5525
5526
  }
5526
5527
  parseCommandListUntilEnd() {
5527
5528
  const { commands } = this.parseCommandListUntilTerminator([]);
5528
- debug.parse('🔍 After loop, checking for "end". Current token:', this.peek().value);
5529
+ debug.parse('\ud83d\udd0d After loop, checking for "end". Current token:', this.peek().value);
5529
5530
  if (this.check('end')) {
5530
- debug.parse('✅ Found "end", consuming it');
5531
+ debug.parse('\u2705 Found "end", consuming it');
5531
5532
  this.advance();
5532
5533
  }
5533
5534
  else {
5534
- debug.parse('❌ ERROR: Expected "end" but got:', this.peek().value, 'at position:', this.peek().start);
5535
+ debug.parse('\u274c ERROR: Expected "end" but got:', this.peek().value, 'at position:', this.peek().start);
5535
5536
  throw new Error('Expected "end" to close repeat block');
5536
5537
  }
5537
5538
  return commands;
@@ -6204,6 +6205,23 @@ class Parser {
6204
6205
  this.consume(')', "Expected ')' after parameter list");
6205
6206
  }
6206
6207
  const bodyCommands = this.parseCommandBlock(['end', 'catch', 'finally']);
6208
+ const { errorSymbol, errorHandler, finallyHandler } = this.parseErrorAndFinallyBlocks();
6209
+ this.consume('end', "Expected 'end' after function definition");
6210
+ return {
6211
+ type: 'def',
6212
+ name: funcName,
6213
+ params,
6214
+ body: bodyCommands,
6215
+ ...(errorSymbol !== undefined && { errorSymbol }),
6216
+ ...(errorHandler !== undefined && { errorHandler }),
6217
+ ...(finallyHandler !== undefined && { finallyHandler }),
6218
+ start: pos.start,
6219
+ end: this.getPosition().end,
6220
+ line: pos.line,
6221
+ column: pos.column,
6222
+ };
6223
+ }
6224
+ parseErrorAndFinallyBlocks() {
6207
6225
  let errorSymbol;
6208
6226
  let errorHandler;
6209
6227
  if (this.check('catch')) {
@@ -6218,19 +6236,10 @@ class Parser {
6218
6236
  this.advance();
6219
6237
  finallyHandler = this.parseCommandBlock(['end']);
6220
6238
  }
6221
- this.consume('end', "Expected 'end' after function definition");
6222
6239
  return {
6223
- type: 'def',
6224
- name: funcName,
6225
- params,
6226
- body: bodyCommands,
6227
6240
  ...(errorSymbol !== undefined && { errorSymbol }),
6228
6241
  ...(errorHandler !== undefined && { errorHandler }),
6229
6242
  ...(finallyHandler !== undefined && { finallyHandler }),
6230
- start: pos.start,
6231
- end: this.getPosition().end,
6232
- line: pos.line,
6233
- column: pos.column,
6234
6243
  };
6235
6244
  }
6236
6245
  parseEventNameWithNamespace(errorMessage) {
@@ -6375,13 +6384,13 @@ class Parser {
6375
6384
  try {
6376
6385
  const cmd = this.parseCommand();
6377
6386
  if (this.error && this.error !== savedError) {
6378
- debug.parse('⚠️ Command parsing added error, restoring error state. Error was:', this.error.message);
6387
+ debug.parse('\u26a0\ufe0f Command parsing added error, restoring error state. Error was:', this.error.message);
6379
6388
  this.error = savedError;
6380
6389
  }
6381
6390
  return cmd;
6382
6391
  }
6383
6392
  catch (error) {
6384
- debug.parse('⚠️ Command parsing threw exception, restoring error state:', error instanceof Error ? error.message : String(error));
6393
+ debug.parse('\u26a0\ufe0f Command parsing threw exception, restoring error state:', error instanceof Error ? error.message : String(error));
6385
6394
  this.error = savedError;
6386
6395
  return null;
6387
6396
  }
@@ -6398,12 +6407,12 @@ class Parser {
6398
6407
  const timeToken = this.advance();
6399
6408
  const delayMs = parseTimeToMs(timeToken.value);
6400
6409
  modifiers[modifierKey] = delayMs;
6401
- debug.parse(`🔧 parseEventHandler: Parsed '${modName} at ${timeToken.value}' (${delayMs}ms)`);
6410
+ debug.parse(`\ud83d\udd27 parseEventHandler: Parsed '${modName} at ${timeToken.value}' (${delayMs}ms)`);
6402
6411
  }
6403
6412
  else if (this.matchNumber()) {
6404
6413
  const delayMs = parseInt(this.previous().value, 10);
6405
6414
  modifiers[modifierKey] = delayMs;
6406
- debug.parse(`🔧 parseEventHandler: Parsed '${modName} at ${delayMs}'`);
6415
+ debug.parse(`\ud83d\udd27 parseEventHandler: Parsed '${modName} at ${delayMs}'`);
6407
6416
  }
6408
6417
  else {
6409
6418
  this.addError(`Expected time expression after '${modName} at', got: ${this.peek().value}`);
@@ -6414,7 +6423,7 @@ class Parser {
6414
6423
  }
6415
6424
  }
6416
6425
  parseEventHandler() {
6417
- debug.parse(`🔧 parseEventHandler: ENTRY - parsing event handler`);
6426
+ debug.parse(`\ud83d\udd27 parseEventHandler: ENTRY - parsing event handler`);
6418
6427
  const eventNames = [];
6419
6428
  let firstOnceAlias = false;
6420
6429
  if (this.check('first') && !this.checkIsCommand() && this.current + 1 < this.tokens.length) {
@@ -6423,27 +6432,27 @@ class Parser {
6423
6432
  if (peek2Value && peek2Value !== 'of' && peek2Value !== 'in' && peek2Value !== 'from') {
6424
6433
  this.advance();
6425
6434
  firstOnceAlias = true;
6426
- debug.parse(`🔧 parseEventHandler: Parsed 'first' as .once alias`);
6435
+ debug.parse(`\ud83d\udd27 parseEventHandler: Parsed 'first' as .once alias`);
6427
6436
  }
6428
6437
  }
6429
6438
  const event = this.parseEventNameWithNamespace("Expected event name after 'on'");
6430
6439
  eventNames.push(event);
6431
- debug.parse(`🔧 parseEventHandler: Parsed first event name: ${event}`);
6440
+ debug.parse(`\ud83d\udd27 parseEventHandler: Parsed first event name: ${event}`);
6432
6441
  while (this.check('or')) {
6433
6442
  this.advance();
6434
- debug.parse(`🔧 parseEventHandler: Found 'or', parsing additional event name`);
6443
+ debug.parse(`\ud83d\udd27 parseEventHandler: Found 'or', parsing additional event name`);
6435
6444
  const additionalEvent = this.parseEventNameWithNamespace("Expected event name after 'or'");
6436
6445
  eventNames.push(additionalEvent);
6437
- debug.parse(`🔧 parseEventHandler: Parsed additional event name: ${additionalEvent}`);
6446
+ debug.parse(`\ud83d\udd27 parseEventHandler: Parsed additional event name: ${additionalEvent}`);
6438
6447
  }
6439
- debug.parse(`🔧 parseEventHandler: Total events parsed: ${eventNames.join(', ')}`);
6448
+ debug.parse(`\ud83d\udd27 parseEventHandler: Total events parsed: ${eventNames.join(', ')}`);
6440
6449
  let customEventSource;
6441
6450
  if (this.registryIntegration) {
6442
6451
  const firstEvent = eventNames[0];
6443
6452
  if (this.registryIntegration.hasEventSource(firstEvent)) {
6444
6453
  const source = this.registryIntegration.getEventSource(firstEvent);
6445
6454
  customEventSource = source?.name;
6446
- debug.parse(`🔧 parseEventHandler: Detected custom event source '${customEventSource}' for event '${firstEvent}'`);
6455
+ debug.parse(`\ud83d\udd27 parseEventHandler: Detected custom event source '${customEventSource}' for event '${firstEvent}'`);
6447
6456
  }
6448
6457
  }
6449
6458
  const eventParams = [];
@@ -6455,7 +6464,7 @@ class Parser {
6455
6464
  } while (this.match(','));
6456
6465
  }
6457
6466
  this.consume(')', "Expected ')' after event parameters");
6458
- debug.parse(`🔧 parseEventHandler: Parsed event parameters: ${eventParams.join(', ')}`);
6467
+ debug.parse(`\ud83d\udd27 parseEventHandler: Parsed event parameters: ${eventParams.join(', ')}`);
6459
6468
  }
6460
6469
  const modifiers = {};
6461
6470
  while (this.check('.')) {
@@ -6464,15 +6473,15 @@ class Parser {
6464
6473
  const modName = modToken.value.toLowerCase();
6465
6474
  if (modName === 'once') {
6466
6475
  modifiers.once = true;
6467
- debug.parse(`🔧 parseEventHandler: Parsed modifier '.once'`);
6476
+ debug.parse(`\ud83d\udd27 parseEventHandler: Parsed modifier '.once'`);
6468
6477
  }
6469
6478
  else if (modName === 'prevent') {
6470
6479
  modifiers.prevent = true;
6471
- debug.parse(`🔧 parseEventHandler: Parsed modifier '.prevent'`);
6480
+ debug.parse(`\ud83d\udd27 parseEventHandler: Parsed modifier '.prevent'`);
6472
6481
  }
6473
6482
  else if (modName === 'stop') {
6474
6483
  modifiers.stop = true;
6475
- debug.parse(`🔧 parseEventHandler: Parsed modifier '.stop'`);
6484
+ debug.parse(`\ud83d\udd27 parseEventHandler: Parsed modifier '.stop'`);
6476
6485
  }
6477
6486
  else if (modName === 'debounce' || modName === 'throttle') {
6478
6487
  if (this.check('(')) {
@@ -6484,11 +6493,11 @@ class Parser {
6484
6493
  }
6485
6494
  if (modName === 'debounce') {
6486
6495
  modifiers.debounce = delayMs;
6487
- debug.parse(`🔧 parseEventHandler: Parsed modifier '.debounce(${delayMs})'`);
6496
+ debug.parse(`\ud83d\udd27 parseEventHandler: Parsed modifier '.debounce(${delayMs})'`);
6488
6497
  }
6489
6498
  else {
6490
6499
  modifiers.throttle = delayMs;
6491
- debug.parse(`🔧 parseEventHandler: Parsed modifier '.throttle(${delayMs})'`);
6500
+ debug.parse(`\ud83d\udd27 parseEventHandler: Parsed modifier '.throttle(${delayMs})'`);
6492
6501
  }
6493
6502
  this.consume(')', `Expected ')' after ${modName} delay`);
6494
6503
  }
@@ -6497,7 +6506,7 @@ class Parser {
6497
6506
  }
6498
6507
  }
6499
6508
  else {
6500
- debug.parse(`🔧 parseEventHandler: Warning - unknown modifier '.${modName}'`);
6509
+ debug.parse(`\ud83d\udd27 parseEventHandler: Warning - unknown modifier '.${modName}'`);
6501
6510
  }
6502
6511
  }
6503
6512
  this.parseTemporalKeywordModifiers(modifiers);
@@ -6505,7 +6514,7 @@ class Parser {
6505
6514
  modifiers.once = true;
6506
6515
  }
6507
6516
  if (Object.keys(modifiers).length > 0) {
6508
- debug.parse(`🔧 parseEventHandler: Parsed modifiers:`, modifiers);
6517
+ debug.parse(`\ud83d\udd27 parseEventHandler: Parsed modifiers:`, modifiers);
6509
6518
  }
6510
6519
  let condition;
6511
6520
  if (this.match('[')) {
@@ -6516,7 +6525,7 @@ class Parser {
6516
6525
  if (this.match('from')) {
6517
6526
  const targetToken = this.advance();
6518
6527
  target = targetToken.value;
6519
- debug.parse(`🔧 parseEventHandler: Parsed 'from' target: ${target} (kind: ${targetToken.kind})`);
6528
+ debug.parse(`\ud83d\udd27 parseEventHandler: Parsed 'from' target: ${target} (kind: ${targetToken.kind})`);
6520
6529
  this.parseTemporalKeywordModifiers(modifiers);
6521
6530
  }
6522
6531
  let attributeName;
@@ -6528,32 +6537,36 @@ class Parser {
6528
6537
  else {
6529
6538
  attributeName = attrToken.value;
6530
6539
  }
6531
- debug.parse(`🔧 parseEventHandler: Parsed attribute name: ${attributeName}`);
6540
+ debug.parse(`\ud83d\udd27 parseEventHandler: Parsed attribute name: ${attributeName}`);
6532
6541
  }
6533
6542
  let watchTarget;
6534
6543
  if (this.match('in')) {
6535
- debug.parse(`🔧 parseEventHandler: Found 'in' keyword, parsing watch target`);
6544
+ debug.parse(`\ud83d\udd27 parseEventHandler: Found 'in' keyword, parsing watch target`);
6536
6545
  watchTarget = this.parseExpression();
6537
- debug.parse(`🔧 parseEventHandler: Parsed watch target expression`);
6546
+ debug.parse(`\ud83d\udd27 parseEventHandler: Parsed watch target expression`);
6538
6547
  }
6539
6548
  const commands = [];
6540
- debug.parse(`✅ parseEventHandler: About to parse commands, current token: ${this.isAtEnd() ? 'END' : this.peek().value}, isAtEnd: ${this.isAtEnd()}`);
6549
+ debug.parse(`\u2705 parseEventHandler: About to parse commands, current token: ${this.isAtEnd() ? 'END' : this.peek().value}, isAtEnd: ${this.isAtEnd()}`);
6541
6550
  while (!this.isAtEnd()) {
6542
- debug.parse(`✅ parseEventHandler: Loop iteration, current token: ${this.peek().value}, kind: ${this.peek().kind}`);
6551
+ debug.parse(`\u2705 parseEventHandler: Loop iteration, current token: ${this.peek().value}, kind: ${this.peek().kind}`);
6543
6552
  if (this.check('on')) {
6544
- debug.parse(`✅ parseEventHandler: Stopping command parsing, found next event handler 'on'`);
6553
+ debug.parse(`\u2705 parseEventHandler: Stopping command parsing, found next event handler 'on'`);
6545
6554
  break;
6546
6555
  }
6547
6556
  if (this.checkComment()) {
6548
- debug.parse(`✅ parseEventHandler: Skipping comment token: ${this.peek().value}`);
6557
+ debug.parse(`\u2705 parseEventHandler: Skipping comment token: ${this.peek().value}`);
6549
6558
  this.advance();
6550
6559
  continue;
6551
6560
  }
6552
6561
  if (this.check('end')) {
6553
- debug.parse(`✅ parseEventHandler: Stopping command parsing, found 'end' keyword`);
6562
+ debug.parse(`\u2705 parseEventHandler: Stopping command parsing, found 'end' keyword`);
6554
6563
  this.advance();
6555
6564
  break;
6556
6565
  }
6566
+ if (this.check('catch') || this.check('finally')) {
6567
+ debug.parse(`\u2705 parseEventHandler: Stopping command parsing, found '${this.peek().value}'`);
6568
+ break;
6569
+ }
6557
6570
  if (this.checkIsCommand()) {
6558
6571
  const nextIsOpenParen = this.tokens[this.current + 1]?.value === '(';
6559
6572
  const commandName = this.peek().value.toLowerCase();
@@ -6582,7 +6595,7 @@ class Parser {
6582
6595
  const cmd = this.parseCommandWithErrorRecovery();
6583
6596
  if (cmd) {
6584
6597
  commands.push(cmd);
6585
- debug.parse(`✅ parseEventHandler: Parsed command, next token: ${this.isAtEnd() ? 'END' : this.peek().value}`);
6598
+ debug.parse(`\u2705 parseEventHandler: Parsed command, next token: ${this.isAtEnd() ? 'END' : this.peek().value}`);
6586
6599
  }
6587
6600
  }
6588
6601
  }
@@ -6600,7 +6613,7 @@ class Parser {
6600
6613
  expr = this.parseExpression();
6601
6614
  }
6602
6615
  catch (error) {
6603
- debug.parse('⚠️ Expression parsing error, restoring error state:', error instanceof Error ? error.message : String(error));
6616
+ debug.parse('\u26a0\ufe0f Expression parsing error, restoring error state:', error instanceof Error ? error.message : String(error));
6604
6617
  this.error = savedError;
6605
6618
  break;
6606
6619
  }
@@ -6647,7 +6660,9 @@ class Parser {
6647
6660
  !this.check('then') &&
6648
6661
  !this.check('and') &&
6649
6662
  !this.check(',') &&
6650
- !this.check('on')) {
6663
+ !this.check('on') &&
6664
+ !this.check('catch') &&
6665
+ !this.check('finally')) {
6651
6666
  this.advance();
6652
6667
  }
6653
6668
  if (this.match('then', 'and', ',')) {
@@ -6660,12 +6675,24 @@ class Parser {
6660
6675
  break;
6661
6676
  }
6662
6677
  }
6678
+ let errorSymbol;
6679
+ let errorHandler;
6680
+ let finallyHandler;
6681
+ if (!this.isAtEnd() && (this.check('catch') || this.check('finally'))) {
6682
+ ({ errorSymbol, errorHandler, finallyHandler } = this.parseErrorAndFinallyBlocks());
6683
+ if (this.check('end')) {
6684
+ this.advance();
6685
+ }
6686
+ }
6663
6687
  const pos = this.getPosition();
6664
6688
  const node = {
6665
6689
  type: 'eventHandler',
6666
6690
  event: eventNames.length === 1 ? eventNames[0] : eventNames.join('|'),
6667
6691
  events: eventNames,
6668
6692
  commands,
6693
+ ...(errorSymbol !== undefined && { errorSymbol }),
6694
+ ...(errorHandler !== undefined && { errorHandler }),
6695
+ ...(finallyHandler !== undefined && { finallyHandler }),
6669
6696
  ...(eventParams.length > 0 && { args: eventParams }),
6670
6697
  ...(condition && { condition }),
6671
6698
  ...(target && { target }),
@@ -6678,7 +6705,7 @@ class Parser {
6678
6705
  line: pos.line,
6679
6706
  column: pos.column,
6680
6707
  };
6681
- debug.parse(`🔧 parseEventHandler: Created node with events: ${eventNames.join(', ')}, attributeName: ${attributeName || 'none'}, watchTarget: ${watchTarget ? 'yes' : 'none'}`);
6708
+ debug.parse(`\ud83d\udd27 parseEventHandler: Created node with events: ${eventNames.join(', ')}, attributeName: ${attributeName || 'none'}, watchTarget: ${watchTarget ? 'yes' : 'none'}`);
6682
6709
  return node;
6683
6710
  }
6684
6711
  parseBehaviorDefinition() {
@@ -6743,11 +6770,33 @@ class Parser {
6743
6770
  }
6744
6771
  }
6745
6772
  }
6746
- const handlerCommands = this.parseCommandListUntilEnd();
6773
+ const { commands: handlerCommands, terminator } = this.parseCommandListUntilTerminator([
6774
+ 'catch',
6775
+ 'finally',
6776
+ ]);
6777
+ let handlerErrorSymbol;
6778
+ let handlerErrorHandler;
6779
+ let handlerFinallyHandler;
6780
+ if (terminator === 'catch' || terminator === 'finally') {
6781
+ ({
6782
+ errorSymbol: handlerErrorSymbol,
6783
+ errorHandler: handlerErrorHandler,
6784
+ finallyHandler: handlerFinallyHandler,
6785
+ } = this.parseErrorAndFinallyBlocks());
6786
+ }
6787
+ if (this.check('end')) {
6788
+ this.advance();
6789
+ }
6790
+ else {
6791
+ throw new Error('Expected "end" to close behavior event handler');
6792
+ }
6747
6793
  const handlerNode = {
6748
6794
  type: 'eventHandler',
6749
6795
  event: eventName,
6750
6796
  commands: handlerCommands,
6797
+ ...(handlerErrorSymbol !== undefined && { errorSymbol: handlerErrorSymbol }),
6798
+ ...(handlerErrorHandler !== undefined && { errorHandler: handlerErrorHandler }),
6799
+ ...(handlerFinallyHandler !== undefined && { finallyHandler: handlerFinallyHandler }),
6751
6800
  ...(eventSource !== undefined && { target: eventSource }),
6752
6801
  ...(eventArgs.length > 0 && { args: eventArgs }),
6753
6802
  start: handlerPos.start,
@@ -6802,7 +6851,7 @@ class Parser {
6802
6851
  this.error.message.includes('unclosed parenthes') ||
6803
6852
  this.error.message.includes('Unclosed parenthes');
6804
6853
  if (!isCriticalError) {
6805
- debug.parse('⚠️ parseCommandSequence: Command parsing added non-critical error, restoring error state. Error was:', this.error.message);
6854
+ debug.parse('\u26a0\ufe0f parseCommandSequence: Command parsing added non-critical error, restoring error state. Error was:', this.error.message);
6806
6855
  this.error = savedError;
6807
6856
  }
6808
6857
  }
@@ -6812,11 +6861,11 @@ class Parser {
6812
6861
  !this.isCommand(this.peek().value) &&
6813
6862
  !this.check('then') &&
6814
6863
  !this.check('on')) {
6815
- debug.parse('⚠️ parseCommandSequence: Skipping unexpected token:', this.peek().value);
6864
+ debug.parse('\u26a0\ufe0f parseCommandSequence: Skipping unexpected token:', this.peek().value);
6816
6865
  this.advance();
6817
6866
  }
6818
6867
  if (this.check('on')) {
6819
- debug.parse('✅ parseCommandSequence: Found "on" token, stopping command sequence to allow event handler parsing');
6868
+ debug.parse('\u2705 parseCommandSequence: Found "on" token, stopping command sequence to allow event handler parsing');
6820
6869
  break;
6821
6870
  }
6822
6871
  if (this.match('then')) {
@@ -7260,7 +7309,7 @@ class Parser {
7260
7309
  if (!this.check(')') && callee.type === 'identifier') {
7261
7310
  const funcName = callee.name;
7262
7311
  if (CommandClassification.isCSSFunction(funcName)) {
7263
- debug.parse(`💡 Tip: CSS functions like ${funcName}() should be quoted for clean parsing. ` +
7312
+ debug.parse(`\ud83d\udca1 Tip: CSS functions like ${funcName}() should be quoted for clean parsing. ` +
7264
7313
  `Use '${funcName}(...)' or \`${funcName}(...)\` instead.`);
7265
7314
  }
7266
7315
  }
@@ -7562,7 +7611,7 @@ class Parser {
7562
7611
  if (this.checkIsCommand() || (this.isCommand(token.value) && !this.isKeyword(token.value))) {
7563
7612
  return;
7564
7613
  }
7565
- debug.parse('⚠️ synchronize: Skipping token:', token.value);
7614
+ debug.parse('\u26a0\ufe0f synchronize: Skipping token:', token.value);
7566
7615
  this.advance();
7567
7616
  }
7568
7617
  }