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