vsn 1.0.1 → 1.0.3

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.
package/dist/index.js CHANGED
@@ -20,6 +20,9 @@ var TokenType = /* @__PURE__ */ ((TokenType2) => {
20
20
  TokenType2["While"] = "While";
21
21
  TokenType2["Try"] = "Try";
22
22
  TokenType2["Catch"] = "Catch";
23
+ TokenType2["Assert"] = "Assert";
24
+ TokenType2["Break"] = "Break";
25
+ TokenType2["Continue"] = "Continue";
23
26
  TokenType2["LBrace"] = "LBrace";
24
27
  TokenType2["RBrace"] = "RBrace";
25
28
  TokenType2["LParen"] = "LParen";
@@ -35,7 +38,9 @@ var TokenType = /* @__PURE__ */ ((TokenType2) => {
35
38
  TokenType2["Greater"] = "Greater";
36
39
  TokenType2["Less"] = "Less";
37
40
  TokenType2["Plus"] = "Plus";
41
+ TokenType2["PlusPlus"] = "PlusPlus";
38
42
  TokenType2["Minus"] = "Minus";
43
+ TokenType2["MinusMinus"] = "MinusMinus";
39
44
  TokenType2["Tilde"] = "Tilde";
40
45
  TokenType2["Star"] = "Star";
41
46
  TokenType2["Slash"] = "Slash";
@@ -75,6 +80,9 @@ var KEYWORDS = {
75
80
  while: "While" /* While */,
76
81
  try: "Try" /* Try */,
77
82
  catch: "Catch" /* Catch */,
83
+ assert: "Assert" /* Assert */,
84
+ break: "Break" /* Break */,
85
+ continue: "Continue" /* Continue */,
78
86
  true: "Boolean" /* Boolean */,
79
87
  false: "Boolean" /* Boolean */,
80
88
  null: "Null" /* Null */
@@ -176,8 +184,20 @@ var Lexer = class {
176
184
  readIdentifier() {
177
185
  const start = this.position();
178
186
  let value = "";
179
- while (!this.eof() && (this.isAlphaNumeric(this.peek()) || this.peek() === "_" || this.peek() === "-")) {
180
- value += this.next();
187
+ while (!this.eof()) {
188
+ const ch = this.peek();
189
+ if (this.isAlphaNumeric(ch) || ch === "_") {
190
+ value += this.next();
191
+ continue;
192
+ }
193
+ if (ch === "-") {
194
+ if (this.peek(1) === "-") {
195
+ break;
196
+ }
197
+ value += this.next();
198
+ continue;
199
+ }
200
+ break;
181
201
  }
182
202
  const keywordType = KEYWORDS[value];
183
203
  if (keywordType) {
@@ -318,6 +338,16 @@ var Lexer = class {
318
338
  this.next();
319
339
  return this.token("Pipe" /* Pipe */, "|>", start);
320
340
  }
341
+ if (ch === "+" && next === "+") {
342
+ this.next();
343
+ this.next();
344
+ return this.token("PlusPlus" /* PlusPlus */, "++", start);
345
+ }
346
+ if (ch === "-" && next === "-") {
347
+ this.next();
348
+ this.next();
349
+ return this.token("MinusMinus" /* MinusMinus */, "--", start);
350
+ }
321
351
  if (ch === "." && next === "." && this.peek(2) === ".") {
322
352
  this.next();
323
353
  this.next();
@@ -420,11 +450,20 @@ var BaseNode = class {
420
450
  async prepare(_context) {
421
451
  return;
422
452
  }
423
- async evaluate(_context) {
453
+ evaluate(_context) {
424
454
  return void 0;
425
455
  }
426
456
  };
427
- async function evaluateWithChildScope(context, block) {
457
+ function isPromiseLike(value) {
458
+ return Boolean(value) && typeof value.then === "function";
459
+ }
460
+ function resolveMaybe(value, next) {
461
+ if (isPromiseLike(value)) {
462
+ return value.then(next);
463
+ }
464
+ return next(value);
465
+ }
466
+ function evaluateWithChildScope(context, block) {
428
467
  const scope = context.scope;
429
468
  if (!scope || !scope.createChild) {
430
469
  return block.evaluate(context);
@@ -432,7 +471,7 @@ async function evaluateWithChildScope(context, block) {
432
471
  const previousScope = context.scope;
433
472
  context.scope = scope.createChild();
434
473
  try {
435
- return await block.evaluate(context);
474
+ return block.evaluate(context);
436
475
  } finally {
437
476
  context.scope = previousScope;
438
477
  }
@@ -458,15 +497,25 @@ var BlockNode = class extends BaseNode {
458
497
  super("Block");
459
498
  this.statements = statements;
460
499
  }
461
- async evaluate(context) {
462
- for (const statement of this.statements) {
463
- if (context.returning) {
464
- break;
465
- }
466
- if (statement && typeof statement.evaluate === "function") {
467
- await statement.evaluate(context);
500
+ evaluate(context) {
501
+ let index = 0;
502
+ const run = () => {
503
+ while (index < this.statements.length) {
504
+ if (context.returning || context.breaking || context.continuing) {
505
+ break;
506
+ }
507
+ const statement = this.statements[index];
508
+ index += 1;
509
+ if (statement && typeof statement.evaluate === "function") {
510
+ const result = statement.evaluate(context);
511
+ if (isPromiseLike(result)) {
512
+ return result.then(() => run());
513
+ }
514
+ }
468
515
  }
469
- }
516
+ return void 0;
517
+ };
518
+ return run();
470
519
  }
471
520
  };
472
521
  var SelectorNode = class extends BaseNode {
@@ -476,87 +525,205 @@ var SelectorNode = class extends BaseNode {
476
525
  }
477
526
  };
478
527
  var BehaviorNode = class extends BaseNode {
479
- constructor(selector, body) {
528
+ constructor(selector, body, flags = {}, flagArgs = {}) {
480
529
  super("Behavior");
481
530
  this.selector = selector;
482
531
  this.body = body;
483
- }
484
- };
485
- var StateEntryNode = class extends BaseNode {
486
- constructor(name, value, important) {
487
- super("StateEntry");
488
- this.name = name;
489
- this.value = value;
490
- this.important = important;
491
- }
492
- };
493
- var StateBlockNode = class extends BaseNode {
494
- constructor(entries) {
495
- super("StateBlock");
496
- this.entries = entries;
532
+ this.flags = flags;
533
+ this.flagArgs = flagArgs;
497
534
  }
498
535
  };
499
536
  var OnBlockNode = class extends BaseNode {
500
- constructor(eventName, args, body, modifiers = []) {
537
+ constructor(eventName, args, body, flags = {}, flagArgs = {}) {
501
538
  super("OnBlock");
502
539
  this.eventName = eventName;
503
540
  this.args = args;
504
541
  this.body = body;
505
- this.modifiers = modifiers;
542
+ this.flags = flags;
543
+ this.flagArgs = flagArgs;
506
544
  }
507
545
  };
508
546
  var AssignmentNode = class extends BaseNode {
509
- constructor(target, value, operator = "=") {
547
+ constructor(target, value, operator = "=", prefix = false) {
510
548
  super("Assignment");
511
549
  this.target = target;
512
550
  this.value = value;
513
551
  this.operator = operator;
552
+ this.prefix = prefix;
514
553
  }
515
- async evaluate(context) {
554
+ evaluate(context) {
516
555
  if (!context.scope || !context.scope.setPath) {
517
556
  return void 0;
518
557
  }
519
- const value = await this.value.evaluate(context);
520
- if (this.operator !== "=") {
521
- return this.applyCompoundAssignment(context, value);
522
- }
523
- if (this.target instanceof IdentifierExpression && this.target.name.startsWith("root.") && context.rootScope) {
524
- const path = this.target.name.slice("root.".length);
525
- context.rootScope.setPath?.(`self.${path}`, value);
526
- return value;
527
- }
528
- this.assignTarget(context, this.target, value);
529
- return value;
558
+ if (this.operator === "++" || this.operator === "--") {
559
+ return this.applyIncrement(context);
560
+ }
561
+ const value = this.value.evaluate(context);
562
+ return resolveMaybe(value, (resolvedValue) => {
563
+ if (this.operator !== "=") {
564
+ return this.applyCompoundAssignment(context, resolvedValue);
565
+ }
566
+ if (this.target instanceof IdentifierExpression && this.target.name.startsWith("root.") && context.rootScope) {
567
+ const path = this.target.name.slice("root.".length);
568
+ context.rootScope.setPath?.(`self.${path}`, resolvedValue);
569
+ return resolvedValue;
570
+ }
571
+ if (this.target instanceof MemberExpression || this.target instanceof IndexExpression) {
572
+ const resolved = this.resolveAssignmentTarget(context);
573
+ return resolveMaybe(resolved, (resolvedTarget) => {
574
+ if (resolvedTarget?.scope?.setPath) {
575
+ resolvedTarget.scope.setPath(resolvedTarget.path, resolvedValue);
576
+ return resolvedValue;
577
+ }
578
+ this.assignTarget(context, this.target, resolvedValue);
579
+ return resolvedValue;
580
+ });
581
+ }
582
+ this.assignTarget(context, this.target, resolvedValue);
583
+ return resolvedValue;
584
+ });
530
585
  }
531
586
  applyCompoundAssignment(context, value) {
532
587
  if (!context.scope || !context.scope.setPath) {
533
588
  return void 0;
534
589
  }
535
- if (!(this.target instanceof IdentifierExpression)) {
536
- throw new Error("Compound assignment requires a simple identifier");
537
- }
538
- const isRoot = this.target.name.startsWith("root.");
539
- const scope = isRoot && context.rootScope ? context.rootScope : context.scope;
540
- const rawPath = isRoot ? this.target.name.slice("root.".length) : this.target.name;
541
- const path = isRoot ? `self.${rawPath}` : rawPath;
542
- const current = scope?.getPath ? scope.getPath(path) : void 0;
543
- let result;
544
- if (this.operator === "+=") {
545
- result = current + value;
546
- } else if (this.operator === "-=") {
547
- result = current - value;
548
- } else if (this.operator === "*=") {
549
- result = current * value;
550
- } else {
551
- result = current / value;
590
+ const resolved = this.resolveAssignmentTarget(context);
591
+ return resolveMaybe(resolved, (resolvedTarget) => {
592
+ if (!resolvedTarget) {
593
+ throw new Error("Compound assignment requires a simple identifier or member path");
594
+ }
595
+ const { scope, path } = resolvedTarget;
596
+ const current = scope?.getPath ? scope.getPath(path) : void 0;
597
+ let result;
598
+ if (this.operator === "+=") {
599
+ result = current + value;
600
+ } else if (this.operator === "-=") {
601
+ result = current - value;
602
+ } else if (this.operator === "*=") {
603
+ result = current * value;
604
+ } else {
605
+ result = current / value;
606
+ }
607
+ scope?.setPath?.(path, result);
608
+ return result;
609
+ });
610
+ }
611
+ applyIncrement(context) {
612
+ if (!context.scope || !context.scope.setPath) {
613
+ return void 0;
552
614
  }
553
- scope?.setPath?.(path, result);
554
- return result;
615
+ const resolved = this.resolveAssignmentTarget(context);
616
+ return resolveMaybe(resolved, (resolvedTarget) => {
617
+ if (!resolvedTarget) {
618
+ throw new Error("Increment/decrement requires a simple identifier or member path");
619
+ }
620
+ const { scope, path } = resolvedTarget;
621
+ const current = scope?.getPath ? scope.getPath(path) : void 0;
622
+ const numeric = typeof current === "number" ? current : Number(current);
623
+ const delta = this.operator === "++" ? 1 : -1;
624
+ const next = (Number.isNaN(numeric) ? 0 : numeric) + delta;
625
+ scope?.setPath?.(path, next);
626
+ return this.prefix ? next : numeric;
627
+ });
628
+ }
629
+ resolveAssignmentTarget(context) {
630
+ if (this.target instanceof IdentifierExpression) {
631
+ const isRoot = this.target.name.startsWith("root.");
632
+ const rawPath = isRoot ? this.target.name.slice("root.".length) : this.target.name;
633
+ if (isRoot) {
634
+ if (context.rootScope) {
635
+ return { scope: context.rootScope, path: `self.${rawPath}` };
636
+ }
637
+ return { scope: context.scope, path: `root.${rawPath}` };
638
+ }
639
+ return { scope: context.scope, path: rawPath };
640
+ }
641
+ if (this.target instanceof MemberExpression) {
642
+ const resolvedPath = this.target.getIdentifierPath();
643
+ if (resolvedPath) {
644
+ const path = resolvedPath.path;
645
+ const isRoot = path.startsWith("root.");
646
+ const rawPath = isRoot ? path.slice("root.".length) : path;
647
+ if (isRoot) {
648
+ if (context.rootScope) {
649
+ return { scope: context.rootScope, path: `self.${rawPath}` };
650
+ }
651
+ return { scope: context.scope, path: `root.${rawPath}` };
652
+ }
653
+ return { scope: context.scope, path: rawPath };
654
+ }
655
+ const targetExpr = this.target;
656
+ const basePath = this.resolveTargetPath(context, targetExpr.target);
657
+ return resolveMaybe(basePath, (resolvedBase) => {
658
+ if (!resolvedBase) {
659
+ return null;
660
+ }
661
+ const path = `${resolvedBase}.${targetExpr.property}`;
662
+ const isRoot = path.startsWith("root.");
663
+ const rawPath = isRoot ? path.slice("root.".length) : path;
664
+ if (isRoot) {
665
+ if (context.rootScope) {
666
+ return { scope: context.rootScope, path: `self.${rawPath}` };
667
+ }
668
+ return { scope: context.scope, path: `root.${rawPath}` };
669
+ }
670
+ return { scope: context.scope, path: rawPath };
671
+ });
672
+ }
673
+ if (this.target instanceof IndexExpression) {
674
+ const path = this.resolveIndexPath(context, this.target);
675
+ return resolveMaybe(path, (resolvedPath) => {
676
+ if (!resolvedPath) {
677
+ return null;
678
+ }
679
+ const isRoot = resolvedPath.startsWith("root.");
680
+ const rawPath = isRoot ? resolvedPath.slice("root.".length) : resolvedPath;
681
+ if (isRoot) {
682
+ if (context.rootScope) {
683
+ return { scope: context.rootScope, path: `self.${rawPath}` };
684
+ }
685
+ return { scope: context.scope, path: `root.${rawPath}` };
686
+ }
687
+ return { scope: context.scope, path: rawPath };
688
+ });
689
+ }
690
+ return null;
691
+ }
692
+ resolveIndexPath(context, expr) {
693
+ const base = this.resolveTargetPath(context, expr.target);
694
+ return resolveMaybe(base, (resolvedBase) => {
695
+ if (!resolvedBase) {
696
+ return null;
697
+ }
698
+ const indexValue = expr.index.evaluate(context);
699
+ return resolveMaybe(indexValue, (resolvedIndex) => {
700
+ if (resolvedIndex == null) {
701
+ return null;
702
+ }
703
+ return `${resolvedBase}.${resolvedIndex}`;
704
+ });
705
+ });
706
+ }
707
+ resolveTargetPath(context, target) {
708
+ if (target instanceof IdentifierExpression) {
709
+ return target.name;
710
+ }
711
+ if (target instanceof MemberExpression) {
712
+ return target.getIdentifierPath()?.path ?? null;
713
+ }
714
+ if (target instanceof IndexExpression) {
715
+ return this.resolveIndexPath(context, target);
716
+ }
717
+ return null;
555
718
  }
556
719
  assignTarget(context, target, value) {
557
720
  if (!context.scope || !context.scope.setPath) {
558
721
  return;
559
722
  }
723
+ if (target instanceof DirectiveExpression) {
724
+ this.assignDirectiveTarget(context, target, value);
725
+ return;
726
+ }
560
727
  if (target instanceof IdentifierExpression) {
561
728
  context.scope.setPath(target.name, value);
562
729
  return;
@@ -598,19 +765,99 @@ var AssignmentNode = class extends BaseNode {
598
765
  return;
599
766
  }
600
767
  }
768
+ assignDirectiveTarget(context, target, value) {
769
+ const element = context.element;
770
+ if (!element) {
771
+ return;
772
+ }
773
+ if (target.kind === "attr") {
774
+ if (target.name === "value") {
775
+ if (element instanceof HTMLInputElement || element instanceof HTMLTextAreaElement) {
776
+ element.value = value == null ? "" : String(value);
777
+ element.setAttribute("value", element.value);
778
+ return;
779
+ }
780
+ if (element instanceof HTMLSelectElement) {
781
+ element.value = value == null ? "" : String(value);
782
+ return;
783
+ }
784
+ }
785
+ if (target.name === "checked" && element instanceof HTMLInputElement) {
786
+ const checked = value === true || value === "true" || value === 1 || value === "1";
787
+ element.checked = checked;
788
+ if (checked) {
789
+ element.setAttribute("checked", "");
790
+ } else {
791
+ element.removeAttribute("checked");
792
+ }
793
+ return;
794
+ }
795
+ if (target.name === "html" && element instanceof HTMLElement) {
796
+ element.innerHTML = value == null ? "" : String(value);
797
+ return;
798
+ }
799
+ element.setAttribute(target.name, value == null ? "" : String(value));
800
+ return;
801
+ }
802
+ if (target.kind === "style" && element instanceof HTMLElement) {
803
+ element.style.setProperty(target.name, value == null ? "" : String(value));
804
+ }
805
+ }
601
806
  };
602
807
  var ReturnNode = class extends BaseNode {
603
808
  constructor(value) {
604
809
  super("Return");
605
810
  this.value = value;
606
811
  }
607
- async evaluate(context) {
812
+ evaluate(context) {
608
813
  if (context.returning) {
609
814
  return context.returnValue;
610
815
  }
611
- context.returnValue = this.value ? await this.value.evaluate(context) : void 0;
612
- context.returning = true;
613
- return context.returnValue;
816
+ const nextValue = this.value ? this.value.evaluate(context) : void 0;
817
+ return resolveMaybe(nextValue, (resolved) => {
818
+ context.returnValue = resolved;
819
+ context.returning = true;
820
+ return context.returnValue;
821
+ });
822
+ }
823
+ };
824
+ var BreakNode = class extends BaseNode {
825
+ constructor() {
826
+ super("Break");
827
+ }
828
+ evaluate(context) {
829
+ context.breaking = true;
830
+ return void 0;
831
+ }
832
+ };
833
+ var ContinueNode = class extends BaseNode {
834
+ constructor() {
835
+ super("Continue");
836
+ }
837
+ evaluate(context) {
838
+ context.continuing = true;
839
+ return void 0;
840
+ }
841
+ };
842
+ var AssertError = class extends Error {
843
+ constructor(message = "Assertion failed") {
844
+ super(message);
845
+ this.name = "AssertError";
846
+ }
847
+ };
848
+ var AssertNode = class extends BaseNode {
849
+ constructor(test) {
850
+ super("Assert");
851
+ this.test = test;
852
+ }
853
+ evaluate(context) {
854
+ const value = this.test.evaluate(context);
855
+ return resolveMaybe(value, (resolved) => {
856
+ if (!resolved) {
857
+ throw new AssertError();
858
+ }
859
+ return resolved;
860
+ });
614
861
  }
615
862
  };
616
863
  var IfNode = class extends BaseNode {
@@ -620,14 +867,17 @@ var IfNode = class extends BaseNode {
620
867
  this.consequent = consequent;
621
868
  this.alternate = alternate;
622
869
  }
623
- async evaluate(context) {
624
- const condition = await this.test.evaluate(context);
625
- if (condition) {
626
- return evaluateWithChildScope(context, this.consequent);
627
- }
628
- if (this.alternate) {
629
- return evaluateWithChildScope(context, this.alternate);
630
- }
870
+ evaluate(context) {
871
+ const condition = this.test.evaluate(context);
872
+ return resolveMaybe(condition, (resolved) => {
873
+ if (resolved) {
874
+ return evaluateWithChildScope(context, this.consequent);
875
+ }
876
+ if (this.alternate) {
877
+ return evaluateWithChildScope(context, this.alternate);
878
+ }
879
+ return void 0;
880
+ });
631
881
  }
632
882
  };
633
883
  var WhileNode = class extends BaseNode {
@@ -636,21 +886,104 @@ var WhileNode = class extends BaseNode {
636
886
  this.test = test;
637
887
  this.body = body;
638
888
  }
639
- async evaluate(context) {
889
+ evaluate(context) {
640
890
  const previousScope = context.scope;
641
891
  if (context.scope?.createChild) {
642
892
  context.scope = context.scope.createChild();
643
893
  }
644
- try {
645
- while (await this.test.evaluate(context)) {
646
- await this.body.evaluate(context);
647
- if (context.returning) {
648
- break;
894
+ const run = () => {
895
+ const condition = this.test.evaluate(context);
896
+ return resolveMaybe(condition, (resolved) => {
897
+ if (!resolved || context.returning) {
898
+ return void 0;
649
899
  }
900
+ const bodyResult = this.body.evaluate(context);
901
+ return resolveMaybe(bodyResult, () => {
902
+ if (context.breaking) {
903
+ context.breaking = false;
904
+ return void 0;
905
+ }
906
+ if (context.continuing) {
907
+ context.continuing = false;
908
+ }
909
+ return run();
910
+ });
911
+ });
912
+ };
913
+ const result = run();
914
+ if (isPromiseLike(result)) {
915
+ return result.finally(() => {
916
+ context.scope = previousScope;
917
+ });
918
+ }
919
+ context.scope = previousScope;
920
+ return result;
921
+ }
922
+ };
923
+ var ForEachNode = class extends BaseNode {
924
+ constructor(target, iterable, kind, body) {
925
+ super("ForEach");
926
+ this.target = target;
927
+ this.iterable = iterable;
928
+ this.kind = kind;
929
+ this.body = body;
930
+ }
931
+ evaluate(context) {
932
+ const iterableValue = this.iterable.evaluate(context);
933
+ return resolveMaybe(iterableValue, (resolved) => {
934
+ const entries = this.getEntries(resolved);
935
+ const previousScope = context.scope;
936
+ let bodyScope = context.scope;
937
+ if (context.scope?.createChild) {
938
+ bodyScope = context.scope.createChild();
650
939
  }
651
- } finally {
652
- context.scope = previousScope;
940
+ let index = 0;
941
+ const loop = () => {
942
+ if (index >= entries.length || context.returning) {
943
+ context.scope = previousScope;
944
+ return void 0;
945
+ }
946
+ const value = entries[index];
947
+ index += 1;
948
+ context.scope = bodyScope;
949
+ context.scope?.setPath?.(this.target.name, value);
950
+ const bodyResult = this.body.evaluate(context);
951
+ return resolveMaybe(bodyResult, () => {
952
+ if (context.breaking) {
953
+ context.breaking = false;
954
+ context.scope = previousScope;
955
+ return void 0;
956
+ }
957
+ if (context.continuing) {
958
+ context.continuing = false;
959
+ }
960
+ context.scope = previousScope;
961
+ return loop();
962
+ });
963
+ };
964
+ return loop();
965
+ });
966
+ }
967
+ getEntries(value) {
968
+ if (value == null) {
969
+ return [];
970
+ }
971
+ if (this.kind === "in") {
972
+ if (typeof value === "object") {
973
+ return Object.keys(value);
974
+ }
975
+ return [];
976
+ }
977
+ if (typeof value === "string") {
978
+ return Array.from(value);
979
+ }
980
+ if (typeof value[Symbol.iterator] === "function") {
981
+ return Array.from(value);
982
+ }
983
+ if (typeof value === "object") {
984
+ return Object.values(value);
653
985
  }
986
+ return [];
654
987
  }
655
988
  };
656
989
  var ForNode = class extends BaseNode {
@@ -661,27 +994,45 @@ var ForNode = class extends BaseNode {
661
994
  this.update = update;
662
995
  this.body = body;
663
996
  }
664
- async evaluate(context) {
665
- if (this.init) {
666
- await this.init.evaluate(context);
667
- }
668
- const previousScope = context.scope;
669
- let bodyScope = context.scope;
670
- if (context.scope?.createChild) {
671
- bodyScope = context.scope.createChild();
672
- }
673
- while (this.test ? await this.test.evaluate(context) : true) {
674
- context.scope = bodyScope;
675
- await this.body.evaluate(context);
676
- if (context.returning) {
677
- break;
678
- }
679
- context.scope = previousScope;
680
- if (this.update) {
681
- await this.update.evaluate(context);
682
- }
683
- }
684
- context.scope = previousScope;
997
+ evaluate(context) {
998
+ const initResult = this.init ? this.init.evaluate(context) : void 0;
999
+ const run = () => {
1000
+ const previousScope = context.scope;
1001
+ let bodyScope = context.scope;
1002
+ if (context.scope?.createChild) {
1003
+ bodyScope = context.scope.createChild();
1004
+ }
1005
+ const loop = () => {
1006
+ const testResult = this.test ? this.test.evaluate(context) : true;
1007
+ return resolveMaybe(testResult, (passed) => {
1008
+ if (!passed || context.returning) {
1009
+ context.scope = previousScope;
1010
+ return void 0;
1011
+ }
1012
+ context.scope = bodyScope;
1013
+ const bodyResult = this.body.evaluate(context);
1014
+ return resolveMaybe(bodyResult, () => {
1015
+ if (context.returning) {
1016
+ context.scope = previousScope;
1017
+ return void 0;
1018
+ }
1019
+ if (context.breaking) {
1020
+ context.breaking = false;
1021
+ context.scope = previousScope;
1022
+ return void 0;
1023
+ }
1024
+ context.scope = previousScope;
1025
+ if (context.continuing) {
1026
+ context.continuing = false;
1027
+ }
1028
+ const updateResult = this.update ? this.update.evaluate(context) : void 0;
1029
+ return resolveMaybe(updateResult, () => loop());
1030
+ });
1031
+ });
1032
+ };
1033
+ return loop();
1034
+ };
1035
+ return resolveMaybe(initResult, () => run());
685
1036
  }
686
1037
  };
687
1038
  var TryNode = class extends BaseNode {
@@ -691,10 +1042,8 @@ var TryNode = class extends BaseNode {
691
1042
  this.errorName = errorName;
692
1043
  this.handler = handler;
693
1044
  }
694
- async evaluate(context) {
695
- try {
696
- return await evaluateWithChildScope(context, this.body);
697
- } catch (error) {
1045
+ evaluate(context) {
1046
+ const handleError = (error) => {
698
1047
  if (context.returning) {
699
1048
  return context.returnValue;
700
1049
  }
@@ -712,11 +1061,23 @@ var TryNode = class extends BaseNode {
712
1061
  scope.setPath(`self.${this.errorName}`, error);
713
1062
  }
714
1063
  }
715
- await this.handler.evaluate(context);
716
- if (scope && scope.setPath && handlerScope === previousScope) {
717
- scope.setPath(this.errorName, previous);
1064
+ const handlerResult = this.handler.evaluate(context);
1065
+ return resolveMaybe(handlerResult, () => {
1066
+ if (scope && scope.setPath && handlerScope === previousScope) {
1067
+ scope.setPath(this.errorName, previous);
1068
+ }
1069
+ context.scope = previousScope;
1070
+ return void 0;
1071
+ });
1072
+ };
1073
+ try {
1074
+ const bodyResult = evaluateWithChildScope(context, this.body);
1075
+ if (isPromiseLike(bodyResult)) {
1076
+ return bodyResult.catch((error) => handleError(error));
718
1077
  }
719
- context.scope = previousScope;
1078
+ return bodyResult;
1079
+ } catch (error) {
1080
+ return handleError(error);
720
1081
  }
721
1082
  }
722
1083
  };
@@ -736,11 +1097,35 @@ var FunctionExpression = class extends BaseNode {
736
1097
  this.body = body;
737
1098
  this.isAsync = isAsync;
738
1099
  }
739
- async evaluate(context) {
1100
+ evaluate(context) {
740
1101
  const scope = context.scope;
741
1102
  const globals = context.globals;
742
1103
  const element = context.element;
743
- return async (...args) => {
1104
+ if (this.isAsync) {
1105
+ return (...args) => {
1106
+ const activeScope = scope?.createChild ? scope.createChild() : scope;
1107
+ const inner = {
1108
+ scope: activeScope,
1109
+ rootScope: context.rootScope,
1110
+ ...globals ? { globals } : {},
1111
+ ...element ? { element } : {},
1112
+ returnValue: void 0,
1113
+ returning: false,
1114
+ breaking: false,
1115
+ continuing: false
1116
+ };
1117
+ const previousValues = /* @__PURE__ */ new Map();
1118
+ const applyResult = activeScope ? this.applyParams(activeScope, previousValues, inner, args) : void 0;
1119
+ const bodyResult = resolveMaybe(applyResult, () => this.body.evaluate(inner));
1120
+ const finalResult = resolveMaybe(bodyResult, () => inner.returnValue);
1121
+ return Promise.resolve(finalResult).finally(() => {
1122
+ if (activeScope && activeScope === scope) {
1123
+ this.restoreParams(activeScope, previousValues);
1124
+ }
1125
+ });
1126
+ };
1127
+ }
1128
+ return (...args) => {
744
1129
  const activeScope = scope?.createChild ? scope.createChild() : scope;
745
1130
  const inner = {
746
1131
  scope: activeScope,
@@ -748,47 +1133,69 @@ var FunctionExpression = class extends BaseNode {
748
1133
  ...globals ? { globals } : {},
749
1134
  ...element ? { element } : {},
750
1135
  returnValue: void 0,
751
- returning: false
1136
+ returning: false,
1137
+ breaking: false,
1138
+ continuing: false
752
1139
  };
753
- if (activeScope) {
754
- const previousValues = /* @__PURE__ */ new Map();
755
- await this.applyParams(activeScope, previousValues, inner, args);
756
- await this.body.evaluate(inner);
757
- if (activeScope === scope) {
758
- this.restoreParams(activeScope, previousValues);
759
- }
760
- } else {
761
- await this.body.evaluate(inner);
1140
+ const previousValues = /* @__PURE__ */ new Map();
1141
+ const applyResult = activeScope ? this.applyParams(activeScope, previousValues, inner, args) : void 0;
1142
+ const bodyResult = resolveMaybe(applyResult, () => this.body.evaluate(inner));
1143
+ const finalResult = resolveMaybe(bodyResult, () => inner.returnValue);
1144
+ if (isPromiseLike(finalResult)) {
1145
+ return finalResult.finally(() => {
1146
+ if (activeScope && activeScope === scope) {
1147
+ this.restoreParams(activeScope, previousValues);
1148
+ }
1149
+ });
1150
+ }
1151
+ if (activeScope && activeScope === scope) {
1152
+ this.restoreParams(activeScope, previousValues);
762
1153
  }
763
- return inner.returnValue;
1154
+ return finalResult;
764
1155
  };
765
1156
  }
766
- async applyParams(scope, previousValues, context, args) {
767
- if (!scope || !scope.setPath) {
1157
+ applyParams(scope, previousValues, context, args) {
1158
+ if (!scope) {
768
1159
  return;
769
1160
  }
770
- let argIndex = 0;
771
- for (const param of this.params) {
772
- const name = param.name;
773
- if (!name) {
774
- continue;
775
- }
776
- previousValues.set(name, scope.getPath(name));
777
- if (param.rest) {
778
- scope.setPath(`self.${name}`, args.slice(argIndex));
779
- argIndex = args.length;
780
- continue;
781
- }
782
- let value = args[argIndex];
783
- if (value === void 0 && param.defaultValue) {
784
- value = await param.defaultValue.evaluate(context);
785
- }
786
- scope.setPath(`self.${name}`, value);
787
- argIndex += 1;
1161
+ const setPath = scope.setPath?.bind(scope);
1162
+ if (!setPath) {
1163
+ return;
788
1164
  }
1165
+ const params = this.params;
1166
+ const applyAt = (paramIndex, argIndex) => {
1167
+ for (let i = paramIndex; i < params.length; i += 1) {
1168
+ const param = params[i];
1169
+ const name = param.name;
1170
+ if (!name) {
1171
+ continue;
1172
+ }
1173
+ previousValues.set(name, scope.getPath(name));
1174
+ if (param.rest) {
1175
+ setPath(`self.${name}`, args.slice(argIndex));
1176
+ return;
1177
+ }
1178
+ let value = args[argIndex];
1179
+ if (value === void 0 && param.defaultValue) {
1180
+ const defaultValue = param.defaultValue.evaluate(context);
1181
+ return resolveMaybe(defaultValue, (resolvedDefault) => {
1182
+ setPath(`self.${name}`, resolvedDefault);
1183
+ return applyAt(i + 1, argIndex + 1);
1184
+ });
1185
+ }
1186
+ setPath(`self.${name}`, value);
1187
+ argIndex += 1;
1188
+ }
1189
+ return;
1190
+ };
1191
+ return applyAt(0, 0);
789
1192
  }
790
1193
  restoreParams(scope, previousValues) {
791
- if (!scope || !scope.setPath) {
1194
+ if (!scope) {
1195
+ return;
1196
+ }
1197
+ const setPath = scope.setPath?.bind(scope);
1198
+ if (!setPath) {
792
1199
  return;
793
1200
  }
794
1201
  for (const param of this.params) {
@@ -796,7 +1203,7 @@ var FunctionExpression = class extends BaseNode {
796
1203
  if (!name) {
797
1204
  continue;
798
1205
  }
799
- scope.setPath(name, previousValues.get(name));
1206
+ setPath(name, previousValues.get(name));
800
1207
  }
801
1208
  }
802
1209
  };
@@ -815,7 +1222,7 @@ var IdentifierExpression = class extends BaseNode {
815
1222
  super("Identifier");
816
1223
  this.name = name;
817
1224
  }
818
- async evaluate(context) {
1225
+ evaluate(context) {
819
1226
  if (this.name.startsWith("root.") && context.rootScope) {
820
1227
  const path = this.name.slice("root.".length);
821
1228
  return context.rootScope.getPath(`self.${path}`);
@@ -860,7 +1267,7 @@ var LiteralExpression = class extends BaseNode {
860
1267
  super("Literal");
861
1268
  this.value = value;
862
1269
  }
863
- async evaluate() {
1270
+ evaluate() {
864
1271
  return this.value;
865
1272
  }
866
1273
  };
@@ -869,13 +1276,22 @@ var TemplateExpression = class extends BaseNode {
869
1276
  super("TemplateExpression");
870
1277
  this.parts = parts;
871
1278
  }
872
- async evaluate(context) {
1279
+ evaluate(context) {
873
1280
  let result = "";
874
- for (const part of this.parts) {
875
- const value = await part.evaluate(context);
876
- result += value == null ? "" : String(value);
877
- }
878
- return result;
1281
+ let index = 0;
1282
+ const run = () => {
1283
+ while (index < this.parts.length) {
1284
+ const part = this.parts[index];
1285
+ index += 1;
1286
+ const value = part.evaluate(context);
1287
+ return resolveMaybe(value, (resolved) => {
1288
+ result += resolved == null ? "" : String(resolved);
1289
+ return run();
1290
+ });
1291
+ }
1292
+ return result;
1293
+ };
1294
+ return run();
879
1295
  }
880
1296
  };
881
1297
  var UnaryExpression = class extends BaseNode {
@@ -884,15 +1300,17 @@ var UnaryExpression = class extends BaseNode {
884
1300
  this.operator = operator;
885
1301
  this.argument = argument;
886
1302
  }
887
- async evaluate(context) {
888
- const value = await this.argument.evaluate(context);
889
- if (this.operator === "!") {
890
- return !value;
891
- }
892
- if (this.operator === "-") {
893
- return -value;
894
- }
895
- return value;
1303
+ evaluate(context) {
1304
+ const value = this.argument.evaluate(context);
1305
+ return resolveMaybe(value, (resolved) => {
1306
+ if (this.operator === "!") {
1307
+ return !resolved;
1308
+ }
1309
+ if (this.operator === "-") {
1310
+ return -resolved;
1311
+ }
1312
+ return resolved;
1313
+ });
896
1314
  }
897
1315
  };
898
1316
  var BinaryExpression = class extends BaseNode {
@@ -902,61 +1320,71 @@ var BinaryExpression = class extends BaseNode {
902
1320
  this.left = left;
903
1321
  this.right = right;
904
1322
  }
905
- async evaluate(context) {
906
- if (this.operator === "&&") {
907
- const leftValue = await this.left.evaluate(context);
908
- return leftValue && await this.right.evaluate(context);
909
- }
910
- if (this.operator === "||") {
911
- const leftValue = await this.left.evaluate(context);
912
- return leftValue || await this.right.evaluate(context);
913
- }
914
- if (this.operator === "??") {
915
- const leftValue = await this.left.evaluate(context);
916
- return leftValue ?? await this.right.evaluate(context);
917
- }
918
- const left = await this.left.evaluate(context);
919
- const right = await this.right.evaluate(context);
920
- if (this.operator === "+") {
921
- return left + right;
922
- }
923
- if (this.operator === "-") {
924
- return left - right;
925
- }
926
- if (this.operator === "*") {
927
- return left * right;
928
- }
929
- if (this.operator === "/") {
930
- return left / right;
931
- }
932
- if (this.operator === "%") {
933
- return left % right;
934
- }
935
- if (this.operator === "==") {
936
- return left == right;
937
- }
938
- if (this.operator === "!=") {
939
- return left != right;
940
- }
941
- if (this.operator === "===") {
942
- return left === right;
943
- }
944
- if (this.operator === "!==") {
945
- return left !== right;
946
- }
947
- if (this.operator === "<") {
948
- return left < right;
949
- }
950
- if (this.operator === ">") {
951
- return left > right;
952
- }
953
- if (this.operator === "<=") {
954
- return left <= right;
955
- }
956
- if (this.operator === ">=") {
957
- return left >= right;
958
- }
959
- return void 0;
1323
+ evaluate(context) {
1324
+ const leftValue = this.left.evaluate(context);
1325
+ return resolveMaybe(leftValue, (resolvedLeft) => {
1326
+ if (this.operator === "&&") {
1327
+ if (!resolvedLeft) {
1328
+ return resolvedLeft;
1329
+ }
1330
+ return this.right.evaluate(context);
1331
+ }
1332
+ if (this.operator === "||") {
1333
+ if (resolvedLeft) {
1334
+ return resolvedLeft;
1335
+ }
1336
+ return this.right.evaluate(context);
1337
+ }
1338
+ if (this.operator === "??") {
1339
+ if (resolvedLeft !== null && resolvedLeft !== void 0) {
1340
+ return resolvedLeft;
1341
+ }
1342
+ return this.right.evaluate(context);
1343
+ }
1344
+ const rightValue = this.right.evaluate(context);
1345
+ return resolveMaybe(rightValue, (resolvedRight) => {
1346
+ if (this.operator === "+") {
1347
+ return resolvedLeft + resolvedRight;
1348
+ }
1349
+ if (this.operator === "-") {
1350
+ return resolvedLeft - resolvedRight;
1351
+ }
1352
+ if (this.operator === "*") {
1353
+ return resolvedLeft * resolvedRight;
1354
+ }
1355
+ if (this.operator === "/") {
1356
+ return resolvedLeft / resolvedRight;
1357
+ }
1358
+ if (this.operator === "%") {
1359
+ return resolvedLeft % resolvedRight;
1360
+ }
1361
+ if (this.operator === "==") {
1362
+ return resolvedLeft == resolvedRight;
1363
+ }
1364
+ if (this.operator === "!=") {
1365
+ return resolvedLeft != resolvedRight;
1366
+ }
1367
+ if (this.operator === "===") {
1368
+ return resolvedLeft === resolvedRight;
1369
+ }
1370
+ if (this.operator === "!==") {
1371
+ return resolvedLeft !== resolvedRight;
1372
+ }
1373
+ if (this.operator === "<") {
1374
+ return resolvedLeft < resolvedRight;
1375
+ }
1376
+ if (this.operator === ">") {
1377
+ return resolvedLeft > resolvedRight;
1378
+ }
1379
+ if (this.operator === "<=") {
1380
+ return resolvedLeft <= resolvedRight;
1381
+ }
1382
+ if (this.operator === ">=") {
1383
+ return resolvedLeft >= resolvedRight;
1384
+ }
1385
+ return void 0;
1386
+ });
1387
+ });
960
1388
  }
961
1389
  };
962
1390
  var TernaryExpression = class extends BaseNode {
@@ -966,12 +1394,14 @@ var TernaryExpression = class extends BaseNode {
966
1394
  this.consequent = consequent;
967
1395
  this.alternate = alternate;
968
1396
  }
969
- async evaluate(context) {
970
- const condition = await this.test.evaluate(context);
971
- if (condition) {
972
- return this.consequent.evaluate(context);
973
- }
974
- return this.alternate.evaluate(context);
1397
+ evaluate(context) {
1398
+ const condition = this.test.evaluate(context);
1399
+ return resolveMaybe(condition, (resolved) => {
1400
+ if (resolved) {
1401
+ return this.consequent.evaluate(context);
1402
+ }
1403
+ return this.alternate.evaluate(context);
1404
+ });
975
1405
  }
976
1406
  };
977
1407
  var MemberExpression = class _MemberExpression extends BaseNode {
@@ -981,11 +1411,11 @@ var MemberExpression = class _MemberExpression extends BaseNode {
981
1411
  this.property = property;
982
1412
  this.optional = optional;
983
1413
  }
984
- async evaluate(context) {
985
- const resolved = await this.resolve(context);
986
- return resolved?.value;
1414
+ evaluate(context) {
1415
+ const resolved = this.resolve(context);
1416
+ return resolveMaybe(resolved, (resolvedValue) => resolvedValue?.value);
987
1417
  }
988
- async resolve(context) {
1418
+ resolve(context) {
989
1419
  const path = this.getIdentifierPath();
990
1420
  if (path) {
991
1421
  const resolved = this.resolveFromScope(context, path);
@@ -997,11 +1427,13 @@ var MemberExpression = class _MemberExpression extends BaseNode {
997
1427
  return resolvedGlobal;
998
1428
  }
999
1429
  }
1000
- const target = await this.target.evaluate(context);
1001
- if (target == null) {
1002
- return { value: void 0, target, optional: this.optional };
1003
- }
1004
- return { value: target[this.property], target, optional: this.optional };
1430
+ const target = this.target.evaluate(context);
1431
+ return resolveMaybe(target, (resolvedTarget) => {
1432
+ if (resolvedTarget == null) {
1433
+ return { value: void 0, target: resolvedTarget, optional: this.optional };
1434
+ }
1435
+ return { value: resolvedTarget[this.property], target: resolvedTarget, optional: this.optional };
1436
+ });
1005
1437
  }
1006
1438
  getIdentifierPath() {
1007
1439
  const targetPath = this.getTargetIdentifierPath();
@@ -1077,25 +1509,39 @@ var CallExpression = class extends BaseNode {
1077
1509
  this.callee = callee;
1078
1510
  this.args = args;
1079
1511
  }
1080
- async evaluate(context) {
1081
- const resolved = await this.resolveCallee(context);
1082
- const fn = resolved?.fn ?? await this.callee.evaluate(context);
1083
- if (typeof fn !== "function") {
1084
- return void 0;
1085
- }
1086
- const values = [];
1087
- for (const arg of this.args) {
1088
- values.push(await arg.evaluate(context));
1089
- }
1090
- return fn.apply(resolved?.thisArg, values);
1512
+ evaluate(context) {
1513
+ const resolved = this.resolveCallee(context);
1514
+ return resolveMaybe(resolved, (resolvedCallee) => {
1515
+ const fnValue = resolvedCallee?.fn ?? this.callee.evaluate(context);
1516
+ return resolveMaybe(fnValue, (resolvedFn) => {
1517
+ if (typeof resolvedFn !== "function") {
1518
+ return void 0;
1519
+ }
1520
+ const values = [];
1521
+ const evalArgs = (index) => {
1522
+ for (let i = index; i < this.args.length; i += 1) {
1523
+ const arg = this.args[i];
1524
+ const argValue = arg.evaluate(context);
1525
+ return resolveMaybe(argValue, (resolvedArg) => {
1526
+ values.push(resolvedArg);
1527
+ return evalArgs(i + 1);
1528
+ });
1529
+ }
1530
+ return resolvedFn.apply(resolvedCallee?.thisArg, values);
1531
+ };
1532
+ return evalArgs(0);
1533
+ });
1534
+ });
1091
1535
  }
1092
- async resolveCallee(context) {
1536
+ resolveCallee(context) {
1093
1537
  if (this.callee instanceof MemberExpression) {
1094
- const resolved = await this.callee.resolve(context);
1095
- if (!resolved) {
1096
- return void 0;
1097
- }
1098
- return { fn: resolved.value, thisArg: resolved.target };
1538
+ const resolved = this.callee.resolve(context);
1539
+ return resolveMaybe(resolved, (resolvedValue) => {
1540
+ if (!resolvedValue) {
1541
+ return void 0;
1542
+ }
1543
+ return { fn: resolvedValue.value, thisArg: resolvedValue.target };
1544
+ });
1099
1545
  }
1100
1546
  if (!(this.callee instanceof IdentifierExpression)) {
1101
1547
  return void 0;
@@ -1137,27 +1583,40 @@ var ArrayExpression = class extends BaseNode {
1137
1583
  super("ArrayExpression");
1138
1584
  this.elements = elements;
1139
1585
  }
1140
- async evaluate(context) {
1586
+ evaluate(context) {
1141
1587
  const values = [];
1142
- for (const element of this.elements) {
1143
- if (element instanceof SpreadElement) {
1144
- const spreadValue = await element.value.evaluate(context);
1145
- if (spreadValue == null) {
1146
- continue;
1588
+ const pushElements = (value) => {
1589
+ if (value == null) {
1590
+ return;
1591
+ }
1592
+ const iterator = value[Symbol.iterator];
1593
+ if (typeof iterator === "function") {
1594
+ for (const entry of value) {
1595
+ values.push(entry);
1147
1596
  }
1148
- const iterator = spreadValue[Symbol.iterator];
1149
- if (typeof iterator === "function") {
1150
- for (const entry of spreadValue) {
1151
- values.push(entry);
1152
- }
1153
- } else {
1154
- values.push(spreadValue);
1597
+ } else {
1598
+ values.push(value);
1599
+ }
1600
+ };
1601
+ const evalAt = (index) => {
1602
+ for (let i = index; i < this.elements.length; i += 1) {
1603
+ const element = this.elements[i];
1604
+ if (element instanceof SpreadElement) {
1605
+ const spreadValue = element.value.evaluate(context);
1606
+ return resolveMaybe(spreadValue, (resolvedSpread) => {
1607
+ pushElements(resolvedSpread);
1608
+ return evalAt(i + 1);
1609
+ });
1155
1610
  }
1156
- continue;
1611
+ const value = element.evaluate(context);
1612
+ return resolveMaybe(value, (resolvedValue) => {
1613
+ values.push(resolvedValue);
1614
+ return evalAt(i + 1);
1615
+ });
1157
1616
  }
1158
- values.push(await element.evaluate(context));
1159
- }
1160
- return values;
1617
+ return values;
1618
+ };
1619
+ return evalAt(0);
1161
1620
  }
1162
1621
  };
1163
1622
  var ObjectExpression = class extends BaseNode {
@@ -1165,24 +1624,39 @@ var ObjectExpression = class extends BaseNode {
1165
1624
  super("ObjectExpression");
1166
1625
  this.entries = entries;
1167
1626
  }
1168
- async evaluate(context) {
1627
+ evaluate(context) {
1169
1628
  const result = {};
1170
- for (const entry of this.entries) {
1171
- if ("spread" in entry) {
1172
- const spreadValue = await entry.spread.evaluate(context);
1173
- if (spreadValue != null) {
1174
- Object.assign(result, spreadValue);
1629
+ const evalAt = (index) => {
1630
+ for (let i = index; i < this.entries.length; i += 1) {
1631
+ const entry = this.entries[i];
1632
+ if ("spread" in entry) {
1633
+ const spreadValue = entry.spread.evaluate(context);
1634
+ return resolveMaybe(spreadValue, (resolvedSpread) => {
1635
+ if (resolvedSpread != null) {
1636
+ Object.assign(result, resolvedSpread);
1637
+ }
1638
+ return evalAt(i + 1);
1639
+ });
1175
1640
  }
1176
- continue;
1177
- }
1178
- if ("computed" in entry && entry.computed) {
1179
- const keyValue = await entry.keyExpr.evaluate(context);
1180
- result[String(keyValue)] = await entry.value.evaluate(context);
1181
- } else {
1182
- result[entry.key] = await entry.value.evaluate(context);
1641
+ if ("computed" in entry && entry.computed) {
1642
+ const keyValue = entry.keyExpr.evaluate(context);
1643
+ return resolveMaybe(keyValue, (resolvedKey) => {
1644
+ const entryValue = entry.value.evaluate(context);
1645
+ return resolveMaybe(entryValue, (resolvedValue) => {
1646
+ result[String(resolvedKey)] = resolvedValue;
1647
+ return evalAt(i + 1);
1648
+ });
1649
+ });
1650
+ }
1651
+ const value = entry.value.evaluate(context);
1652
+ return resolveMaybe(value, (resolvedValue) => {
1653
+ result[entry.key] = resolvedValue;
1654
+ return evalAt(i + 1);
1655
+ });
1183
1656
  }
1184
- }
1185
- return result;
1657
+ return result;
1658
+ };
1659
+ return evalAt(0);
1186
1660
  }
1187
1661
  };
1188
1662
  var IndexExpression = class extends BaseNode {
@@ -1191,16 +1665,30 @@ var IndexExpression = class extends BaseNode {
1191
1665
  this.target = target;
1192
1666
  this.index = index;
1193
1667
  }
1194
- async evaluate(context) {
1195
- const target = await this.target.evaluate(context);
1196
- if (target == null) {
1197
- return void 0;
1198
- }
1199
- const index = await this.index.evaluate(context);
1200
- if (index == null) {
1201
- return void 0;
1668
+ evaluate(context) {
1669
+ const target = this.target.evaluate(context);
1670
+ return resolveMaybe(target, (resolvedTarget) => {
1671
+ if (resolvedTarget == null) {
1672
+ return void 0;
1673
+ }
1674
+ const index = this.index.evaluate(context);
1675
+ return resolveMaybe(index, (resolvedIndex) => {
1676
+ if (resolvedIndex == null) {
1677
+ return void 0;
1678
+ }
1679
+ const key = this.normalizeIndexKey(resolvedTarget, resolvedIndex);
1680
+ return resolvedTarget[key];
1681
+ });
1682
+ });
1683
+ }
1684
+ normalizeIndexKey(target, index) {
1685
+ if (Array.isArray(target) && typeof index === "string" && index.trim() !== "") {
1686
+ const numeric = Number(index);
1687
+ if (!Number.isNaN(numeric)) {
1688
+ return numeric;
1689
+ }
1202
1690
  }
1203
- return target[index];
1691
+ return index;
1204
1692
  }
1205
1693
  };
1206
1694
  var DirectiveExpression = class extends BaseNode {
@@ -1209,7 +1697,7 @@ var DirectiveExpression = class extends BaseNode {
1209
1697
  this.kind = kind;
1210
1698
  this.name = name;
1211
1699
  }
1212
- async evaluate(context) {
1700
+ evaluate(context) {
1213
1701
  const element = context.element;
1214
1702
  if (!element) {
1215
1703
  return `${this.kind}:${this.name}`;
@@ -1223,6 +1711,12 @@ var DirectiveExpression = class extends BaseNode {
1223
1711
  return element.value;
1224
1712
  }
1225
1713
  }
1714
+ if (this.name === "text" && element instanceof HTMLElement) {
1715
+ return element.innerText;
1716
+ }
1717
+ if (this.name === "content" && element instanceof HTMLElement) {
1718
+ return element.textContent ?? "";
1719
+ }
1226
1720
  if (this.name === "checked" && element instanceof HTMLInputElement) {
1227
1721
  return element.checked;
1228
1722
  }
@@ -1242,9 +1736,9 @@ var AwaitExpression = class extends BaseNode {
1242
1736
  super("AwaitExpression");
1243
1737
  this.argument = argument;
1244
1738
  }
1245
- async evaluate(context) {
1246
- const value = await this.argument.evaluate(context);
1247
- return await value;
1739
+ evaluate(context) {
1740
+ const value = this.argument.evaluate(context);
1741
+ return Promise.resolve(value);
1248
1742
  }
1249
1743
  };
1250
1744
  var QueryExpression = class extends BaseNode {
@@ -1253,7 +1747,7 @@ var QueryExpression = class extends BaseNode {
1253
1747
  this.direction = direction;
1254
1748
  this.selector = selector;
1255
1749
  }
1256
- async evaluate(context) {
1750
+ evaluate(context) {
1257
1751
  const selector = this.selector.trim();
1258
1752
  if (!selector) {
1259
1753
  return [];
@@ -1319,6 +1813,9 @@ var TokenStream = class {
1319
1813
  let count = 0;
1320
1814
  for (let i = this.index; i < this.tokens.length; i++) {
1321
1815
  const token = this.tokens[i];
1816
+ if (!token) {
1817
+ continue;
1818
+ }
1322
1819
  if (token.type === "Whitespace" /* Whitespace */) {
1323
1820
  continue;
1324
1821
  }
@@ -1329,6 +1826,29 @@ var TokenStream = class {
1329
1826
  }
1330
1827
  return null;
1331
1828
  }
1829
+ indexAfterDelimited(openType, closeType, offset = 0) {
1830
+ const first = this.peekNonWhitespace(offset);
1831
+ if (!first || first.type !== openType) {
1832
+ return null;
1833
+ }
1834
+ let index = offset + 1;
1835
+ let depth = 1;
1836
+ while (true) {
1837
+ const token = this.peekNonWhitespace(index);
1838
+ if (!token) {
1839
+ return null;
1840
+ }
1841
+ if (token.type === openType) {
1842
+ depth += 1;
1843
+ } else if (token.type === closeType) {
1844
+ depth -= 1;
1845
+ if (depth === 0) {
1846
+ return index + 1;
1847
+ }
1848
+ }
1849
+ index += 1;
1850
+ }
1851
+ }
1332
1852
  };
1333
1853
 
1334
1854
  // src/parser/parser.ts
@@ -1336,11 +1856,14 @@ var Parser = class _Parser {
1336
1856
  stream;
1337
1857
  source;
1338
1858
  customFlags;
1859
+ behaviorFlags;
1339
1860
  allowImplicitSemicolon = false;
1340
1861
  awaitStack = [];
1862
+ functionDepth = 0;
1341
1863
  constructor(input, options) {
1342
1864
  this.source = input;
1343
- this.customFlags = options?.customFlags ?? /* @__PURE__ */ new Set();
1865
+ this.customFlags = options?.customFlags ?? /* @__PURE__ */ new Set(["important", "trusted", "debounce"]);
1866
+ this.behaviorFlags = options?.behaviorFlags ?? /* @__PURE__ */ new Set();
1344
1867
  const lexer = new Lexer(input);
1345
1868
  this.stream = new TokenStream(lexer.tokenize());
1346
1869
  }
@@ -1380,8 +1903,9 @@ var Parser = class _Parser {
1380
1903
  this.stream.skipWhitespace();
1381
1904
  this.stream.expect("Behavior" /* Behavior */);
1382
1905
  const selector = this.parseSelector();
1906
+ const { flags, flagArgs } = this.parseBehaviorFlags();
1383
1907
  const body = this.parseBlock({ allowDeclarations: true });
1384
- return new BehaviorNode(selector, body);
1908
+ return new BehaviorNode(selector, body, flags, flagArgs);
1385
1909
  });
1386
1910
  }
1387
1911
  parseSelector() {
@@ -1395,6 +1919,9 @@ var Parser = class _Parser {
1395
1919
  if (token.type === "LBrace" /* LBrace */) {
1396
1920
  break;
1397
1921
  }
1922
+ if (token.type === "Bang" /* Bang */) {
1923
+ break;
1924
+ }
1398
1925
  if (token.type === "Whitespace" /* Whitespace */) {
1399
1926
  this.stream.next();
1400
1927
  if (sawNonWhitespace && selectorText[selectorText.length - 1] !== " ") {
@@ -1410,6 +1937,10 @@ var Parser = class _Parser {
1410
1937
  }
1411
1938
  return new SelectorNode(selectorText.trim());
1412
1939
  }
1940
+ parseBehaviorFlags() {
1941
+ const result = this.parseFlags(this.behaviorFlags, "behavior modifier");
1942
+ return { flags: result.flags, flagArgs: result.flagArgs };
1943
+ }
1413
1944
  parseUseStatement() {
1414
1945
  return this.wrapErrors(() => {
1415
1946
  this.stream.expect("Use" /* Use */);
@@ -1493,6 +2024,7 @@ ${caret}`;
1493
2024
  }
1494
2025
  parseBlock(options) {
1495
2026
  const allowDeclarations = options?.allowDeclarations ?? false;
2027
+ const allowReturn = options?.allowReturn ?? this.functionDepth > 0;
1496
2028
  this.stream.skipWhitespace();
1497
2029
  this.stream.expect("LBrace" /* LBrace */);
1498
2030
  const statements = [];
@@ -1554,7 +2086,7 @@ ${caret}`;
1554
2086
  }
1555
2087
  sawConstruct = true;
1556
2088
  }
1557
- statements.push(this.parseStatement());
2089
+ statements.push(this.parseStatement({ allowReturn }));
1558
2090
  }
1559
2091
  }
1560
2092
  return new BlockNode(statements);
@@ -1573,6 +2105,15 @@ ${caret}`;
1573
2105
  }
1574
2106
  return this.parseReturnStatement();
1575
2107
  }
2108
+ if (next.type === "Assert" /* Assert */) {
2109
+ return this.parseAssertStatement();
2110
+ }
2111
+ if (next.type === "Break" /* Break */) {
2112
+ return this.parseBreakStatement();
2113
+ }
2114
+ if (next.type === "Continue" /* Continue */) {
2115
+ return this.parseContinueStatement();
2116
+ }
1576
2117
  if (allowBlocks && next.type === "On" /* On */) {
1577
2118
  return this.parseOnBlock();
1578
2119
  }
@@ -1608,44 +2149,6 @@ ${caret}`;
1608
2149
  }
1609
2150
  throw new Error(`Unexpected token ${next.type}`);
1610
2151
  }
1611
- parseStateBlock() {
1612
- this.stream.expect("State" /* State */);
1613
- this.stream.skipWhitespace();
1614
- this.stream.expect("LBrace" /* LBrace */);
1615
- const entries = [];
1616
- while (true) {
1617
- this.stream.skipWhitespace();
1618
- const next = this.stream.peek();
1619
- if (!next) {
1620
- throw new Error("Unterminated state block");
1621
- }
1622
- if (next.type === "RBrace" /* RBrace */) {
1623
- this.stream.next();
1624
- break;
1625
- }
1626
- const nameToken = this.stream.expect("Identifier" /* Identifier */);
1627
- this.stream.skipWhitespace();
1628
- this.stream.expect("Colon" /* Colon */);
1629
- this.stream.skipWhitespace();
1630
- const value = this.parseExpression();
1631
- this.stream.skipWhitespace();
1632
- let important = false;
1633
- if (this.stream.peek()?.type === "Bang" /* Bang */) {
1634
- this.stream.next();
1635
- this.stream.skipWhitespace();
1636
- const importantToken = this.stream.next();
1637
- if (importantToken.type === "Identifier" /* Identifier */ && importantToken.value === "important") {
1638
- important = true;
1639
- } else {
1640
- throw new Error("Expected 'important' after '!'");
1641
- }
1642
- }
1643
- this.stream.skipWhitespace();
1644
- this.stream.expect("Semicolon" /* Semicolon */);
1645
- entries.push(new StateEntryNode(nameToken.value, value, important));
1646
- }
1647
- return new StateBlockNode(entries);
1648
- }
1649
2152
  parseOnBlock() {
1650
2153
  this.stream.expect("On" /* On */);
1651
2154
  this.stream.skipWhitespace();
@@ -1673,22 +2176,9 @@ ${caret}`;
1673
2176
  }
1674
2177
  throw new Error(`Unexpected token in on() args: ${next.type}`);
1675
2178
  }
1676
- const modifiers = this.parseOnModifiers();
2179
+ const { flags, flagArgs } = this.parseFlags(this.customFlags, "flag");
1677
2180
  const body = this.parseBlock({ allowDeclarations: false });
1678
- return new OnBlockNode(event, args, body, modifiers);
1679
- }
1680
- parseOnModifiers() {
1681
- const modifiers = [];
1682
- while (true) {
1683
- this.stream.skipWhitespace();
1684
- if (this.stream.peek()?.type !== "Bang" /* Bang */) {
1685
- break;
1686
- }
1687
- this.stream.next();
1688
- const name = this.stream.expect("Identifier" /* Identifier */).value;
1689
- modifiers.push(name);
1690
- }
1691
- return modifiers;
2181
+ return new OnBlockNode(event, args, body, flags, flagArgs);
1692
2182
  }
1693
2183
  parseAssignment() {
1694
2184
  const target = this.parseAssignmentTarget();
@@ -1898,6 +2388,11 @@ ${caret}`;
1898
2388
  if (!token) {
1899
2389
  throw new Error("Expected expression");
1900
2390
  }
2391
+ if (token.type === "PlusPlus" /* PlusPlus */ || token.type === "MinusMinus" /* MinusMinus */) {
2392
+ this.stream.next();
2393
+ const argument = this.parseUnaryExpression();
2394
+ return this.createIncrementNode(token, argument, true);
2395
+ }
1901
2396
  if (token.type === "Bang" /* Bang */) {
1902
2397
  this.stream.next();
1903
2398
  const argument = this.parseUnaryExpression();
@@ -1913,7 +2408,31 @@ ${caret}`;
1913
2408
  const argument = this.parseUnaryExpression();
1914
2409
  return new AwaitExpression(argument);
1915
2410
  }
1916
- return this.parseCallExpression();
2411
+ return this.parsePostfixExpression();
2412
+ }
2413
+ parsePostfixExpression() {
2414
+ let expr = this.parseCallExpression();
2415
+ while (true) {
2416
+ this.stream.skipWhitespace();
2417
+ const token = this.stream.peek();
2418
+ if (!token) {
2419
+ break;
2420
+ }
2421
+ if (token.type === "PlusPlus" /* PlusPlus */ || token.type === "MinusMinus" /* MinusMinus */) {
2422
+ this.stream.next();
2423
+ expr = this.createIncrementNode(token, expr, false);
2424
+ continue;
2425
+ }
2426
+ break;
2427
+ }
2428
+ return expr;
2429
+ }
2430
+ createIncrementNode(token, argument, prefix) {
2431
+ if (!(argument instanceof IdentifierExpression) && !(argument instanceof MemberExpression) && !(argument instanceof IndexExpression) && !(argument instanceof DirectiveExpression)) {
2432
+ throw new Error("Increment/decrement requires a mutable target");
2433
+ }
2434
+ const operator = token.type === "PlusPlus" /* PlusPlus */ ? "++" : "--";
2435
+ return new AssignmentNode(argument, new LiteralExpression(1), operator, prefix);
1917
2436
  }
1918
2437
  parseCallExpression() {
1919
2438
  let expr = this.parsePrimaryExpression();
@@ -2225,6 +2744,7 @@ ${caret}`;
2225
2744
  this.stream.expect("LBrace" /* LBrace */);
2226
2745
  const statements = [];
2227
2746
  this.awaitStack.push(allowAwait);
2747
+ this.functionDepth += 1;
2228
2748
  try {
2229
2749
  while (true) {
2230
2750
  this.stream.skipWhitespace();
@@ -2236,9 +2756,10 @@ ${caret}`;
2236
2756
  this.stream.next();
2237
2757
  break;
2238
2758
  }
2239
- statements.push(this.parseStatement({ allowBlocks: false, allowReturn: true }));
2759
+ statements.push(this.parseStatement({ allowBlocks: true, allowReturn: true }));
2240
2760
  }
2241
2761
  } finally {
2762
+ this.functionDepth -= 1;
2242
2763
  this.awaitStack.pop();
2243
2764
  }
2244
2765
  return new BlockNode(statements);
@@ -2279,7 +2800,14 @@ ${caret}`;
2279
2800
  return this.parseObjectPattern();
2280
2801
  }
2281
2802
  if (token.type === "Identifier" /* Identifier */) {
2282
- return new IdentifierExpression(this.parseIdentifierPath());
2803
+ const expr = this.parseCallExpression();
2804
+ if (expr instanceof CallExpression) {
2805
+ throw new Error("Invalid assignment target CallExpression");
2806
+ }
2807
+ if (expr instanceof IdentifierExpression || expr instanceof MemberExpression || expr instanceof IndexExpression) {
2808
+ return expr;
2809
+ }
2810
+ throw new Error("Invalid assignment target");
2283
2811
  }
2284
2812
  throw new Error(`Invalid assignment target ${token.type}`);
2285
2813
  }
@@ -2427,7 +2955,7 @@ ${caret}`;
2427
2955
  const operator = this.parseDeclarationOperator();
2428
2956
  this.stream.skipWhitespace();
2429
2957
  const value = this.parseExpression();
2430
- const { flags, flagArgs } = this.parseFlags();
2958
+ const { flags, flagArgs } = this.parseFlags(this.customFlags, "flag");
2431
2959
  this.stream.skipWhitespace();
2432
2960
  this.stream.expect("Semicolon" /* Semicolon */);
2433
2961
  return new DeclarationNode(target, operator, value, flags, flagArgs);
@@ -2468,7 +2996,7 @@ ${caret}`;
2468
2996
  }
2469
2997
  return ":";
2470
2998
  }
2471
- parseFlags() {
2999
+ parseFlags(allowed, errorLabel) {
2472
3000
  const flags = {};
2473
3001
  const flagArgs = {};
2474
3002
  while (true) {
@@ -2478,59 +3006,107 @@ ${caret}`;
2478
3006
  }
2479
3007
  this.stream.next();
2480
3008
  const name = this.stream.expect("Identifier" /* Identifier */).value;
2481
- if (name === "important") {
2482
- flags.important = true;
2483
- } else if (name === "trusted") {
2484
- flags.trusted = true;
2485
- } else if (name === "debounce") {
2486
- flags.debounce = true;
2487
- if (this.stream.peek()?.type === "LParen" /* LParen */) {
2488
- this.stream.next();
2489
- this.stream.skipWhitespace();
2490
- const numberToken = this.stream.expect("Number" /* Number */);
2491
- flagArgs.debounce = Number(numberToken.value);
2492
- this.stream.skipWhitespace();
2493
- this.stream.expect("RParen" /* RParen */);
2494
- } else {
2495
- flagArgs.debounce = 200;
2496
- }
2497
- } else if (this.customFlags.has(name)) {
2498
- flags[name] = true;
2499
- const customArg = this.parseCustomFlagArg();
2500
- if (customArg !== void 0) {
2501
- flagArgs[name] = customArg;
2502
- }
3009
+ if (allowed && !allowed.has(name)) {
3010
+ throw new Error(`Unknown ${errorLabel} ${name}`);
3011
+ }
3012
+ flags[name] = true;
3013
+ const customArg = this.parseCustomFlagArg();
3014
+ if (customArg !== void 0) {
3015
+ flagArgs[name] = customArg;
3016
+ }
3017
+ }
3018
+ return { flags, flagArgs };
3019
+ }
3020
+ parseCustomFlagArg() {
3021
+ if (this.stream.peek()?.type !== "LParen" /* LParen */) {
3022
+ return void 0;
3023
+ }
3024
+ this.stream.next();
3025
+ this.stream.skipWhitespace();
3026
+ const token = this.stream.peek();
3027
+ if (!token) {
3028
+ throw new Error("Unterminated flag arguments");
3029
+ }
3030
+ const value = this.parseCustomFlagLiteral();
3031
+ this.stream.skipWhitespace();
3032
+ this.stream.expect("RParen" /* RParen */);
3033
+ return value;
3034
+ }
3035
+ parseCustomFlagLiteral() {
3036
+ const token = this.stream.peek();
3037
+ if (!token) {
3038
+ throw new Error("Unterminated flag arguments");
3039
+ }
3040
+ if (token.type === "Number" /* Number */) {
3041
+ return Number(this.stream.next().value);
3042
+ }
3043
+ if (token.type === "String" /* String */) {
3044
+ return this.stream.next().value;
3045
+ }
3046
+ if (token.type === "Boolean" /* Boolean */) {
3047
+ return this.stream.next().value === "true";
3048
+ }
3049
+ if (token.type === "Identifier" /* Identifier */) {
3050
+ return this.stream.next().value;
3051
+ }
3052
+ if (token.type === "LBracket" /* LBracket */) {
3053
+ return this.parseCustomFlagArray();
3054
+ }
3055
+ if (token.type === "LBrace" /* LBrace */) {
3056
+ return this.parseCustomFlagObject();
3057
+ }
3058
+ throw new Error(`Unsupported flag argument ${token.type}`);
3059
+ }
3060
+ parseCustomFlagArray() {
3061
+ this.stream.expect("LBracket" /* LBracket */);
3062
+ const items = [];
3063
+ while (true) {
3064
+ this.stream.skipWhitespace();
3065
+ const next = this.stream.peek();
3066
+ if (!next) {
3067
+ throw new Error("Unterminated flag array");
3068
+ }
3069
+ if (next.type === "RBracket" /* RBracket */) {
3070
+ this.stream.next();
3071
+ break;
3072
+ }
3073
+ items.push(this.parseCustomFlagLiteral());
3074
+ this.stream.skipWhitespace();
3075
+ if (this.stream.peek()?.type === "Comma" /* Comma */) {
3076
+ this.stream.next();
3077
+ }
3078
+ }
3079
+ return items;
3080
+ }
3081
+ parseCustomFlagObject() {
3082
+ this.stream.expect("LBrace" /* LBrace */);
3083
+ const obj = {};
3084
+ while (true) {
3085
+ this.stream.skipWhitespace();
3086
+ const next = this.stream.peek();
3087
+ if (!next) {
3088
+ throw new Error("Unterminated flag object");
3089
+ }
3090
+ if (next.type === "RBrace" /* RBrace */) {
3091
+ this.stream.next();
3092
+ break;
3093
+ }
3094
+ let key;
3095
+ if (next.type === "Identifier" /* Identifier */ || next.type === "String" /* String */) {
3096
+ key = this.stream.next().value;
2503
3097
  } else {
2504
- throw new Error(`Unknown flag ${name}`);
3098
+ throw new Error(`Unsupported flag object key ${next.type}`);
3099
+ }
3100
+ this.stream.skipWhitespace();
3101
+ this.stream.expect("Colon" /* Colon */);
3102
+ this.stream.skipWhitespace();
3103
+ obj[key] = this.parseCustomFlagLiteral();
3104
+ this.stream.skipWhitespace();
3105
+ if (this.stream.peek()?.type === "Comma" /* Comma */) {
3106
+ this.stream.next();
2505
3107
  }
2506
3108
  }
2507
- return { flags, flagArgs };
2508
- }
2509
- parseCustomFlagArg() {
2510
- if (this.stream.peek()?.type !== "LParen" /* LParen */) {
2511
- return void 0;
2512
- }
2513
- this.stream.next();
2514
- this.stream.skipWhitespace();
2515
- const token = this.stream.peek();
2516
- if (!token) {
2517
- throw new Error("Unterminated flag arguments");
2518
- }
2519
- let value;
2520
- if (token.type === "Number" /* Number */) {
2521
- value = Number(this.stream.next().value);
2522
- } else if (token.type === "String" /* String */) {
2523
- value = this.stream.next().value;
2524
- } else if (token.type === "Boolean" /* Boolean */) {
2525
- value = this.stream.next().value === "true";
2526
- } else if (token.type === "Identifier" /* Identifier */) {
2527
- value = this.stream.next().value;
2528
- } else {
2529
- throw new Error(`Unsupported flag argument ${token.type}`);
2530
- }
2531
- this.stream.skipWhitespace();
2532
- this.stream.expect("RParen" /* RParen */);
2533
- return value;
3109
+ return obj;
2534
3110
  }
2535
3111
  isDeclarationStart() {
2536
3112
  const first = this.stream.peekNonWhitespace(0);
@@ -2555,14 +3131,29 @@ ${caret}`;
2555
3131
  }
2556
3132
  if (first.type === "Identifier" /* Identifier */) {
2557
3133
  let index = 1;
2558
- while (this.stream.peekNonWhitespace(index)?.type === "Dot" /* Dot */ && this.stream.peekNonWhitespace(index + 1)?.type === "Identifier" /* Identifier */) {
2559
- index += 2;
3134
+ while (true) {
3135
+ const token = this.stream.peekNonWhitespace(index);
3136
+ if (!token) {
3137
+ return false;
3138
+ }
3139
+ if (token.type === "Dot" /* Dot */ && this.stream.peekNonWhitespace(index + 1)?.type === "Identifier" /* Identifier */) {
3140
+ index += 2;
3141
+ continue;
3142
+ }
3143
+ if (token.type === "LBracket" /* LBracket */) {
3144
+ const indexAfter = this.stream.indexAfterDelimited("LBracket" /* LBracket */, "RBracket" /* RBracket */, index);
3145
+ if (indexAfter === null) {
3146
+ return false;
3147
+ }
3148
+ index = indexAfter;
3149
+ continue;
3150
+ }
3151
+ break;
2560
3152
  }
2561
3153
  return this.isAssignmentOperatorStart(index);
2562
3154
  }
2563
3155
  if (first.type === "At" /* At */ || first.type === "Dollar" /* Dollar */) {
2564
3156
  const second = this.stream.peekNonWhitespace(1);
2565
- const third = this.stream.peekNonWhitespace(2);
2566
3157
  return second?.type === "Identifier" /* Identifier */ && this.isAssignmentOperatorStart(2);
2567
3158
  }
2568
3159
  if (first.type === "LBrace" /* LBrace */ || first.type === "LBracket" /* LBracket */) {
@@ -2600,17 +3191,6 @@ ${caret}`;
2600
3191
  }
2601
3192
  return false;
2602
3193
  }
2603
- isCallStart() {
2604
- const first = this.stream.peekNonWhitespace(0);
2605
- if (!first || first.type !== "Identifier" /* Identifier */) {
2606
- return false;
2607
- }
2608
- let index = 1;
2609
- while (this.stream.peekNonWhitespace(index)?.type === "Dot" /* Dot */ && this.stream.peekNonWhitespace(index + 1)?.type === "Identifier" /* Identifier */) {
2610
- index += 2;
2611
- }
2612
- return this.stream.peekNonWhitespace(index)?.type === "LParen" /* LParen */;
2613
- }
2614
3194
  isExpressionStatementStart() {
2615
3195
  const first = this.stream.peekNonWhitespace(0);
2616
3196
  if (!first) {
@@ -2640,50 +3220,22 @@ ${caret}`;
2640
3220
  if (this.stream.peekNonWhitespace(index)?.type !== "LParen" /* LParen */) {
2641
3221
  return false;
2642
3222
  }
2643
- index += 1;
2644
- let depth = 1;
2645
- while (true) {
2646
- const token = this.stream.peekNonWhitespace(index);
2647
- if (!token) {
2648
- return false;
2649
- }
2650
- if (token.type === "LParen" /* LParen */) {
2651
- depth += 1;
2652
- } else if (token.type === "RParen" /* RParen */) {
2653
- depth -= 1;
2654
- if (depth === 0) {
2655
- index += 1;
2656
- break;
2657
- }
2658
- }
2659
- index += 1;
3223
+ const indexAfterParams = this.stream.indexAfterDelimited("LParen" /* LParen */, "RParen" /* RParen */, index);
3224
+ if (indexAfterParams === null) {
3225
+ return false;
2660
3226
  }
2661
- return this.stream.peekNonWhitespace(index)?.type === "LBrace" /* LBrace */;
3227
+ return this.stream.peekNonWhitespace(indexAfterParams)?.type === "LBrace" /* LBrace */;
2662
3228
  }
2663
3229
  isArrowFunctionStart() {
2664
3230
  const first = this.stream.peekNonWhitespace(0);
2665
3231
  if (!first || first.type !== "LParen" /* LParen */) {
2666
3232
  return false;
2667
3233
  }
2668
- let index = 1;
2669
- let depth = 1;
2670
- while (true) {
2671
- const token = this.stream.peekNonWhitespace(index);
2672
- if (!token) {
2673
- return false;
2674
- }
2675
- if (token.type === "LParen" /* LParen */) {
2676
- depth += 1;
2677
- } else if (token.type === "RParen" /* RParen */) {
2678
- depth -= 1;
2679
- if (depth === 0) {
2680
- index += 1;
2681
- break;
2682
- }
2683
- }
2684
- index += 1;
3234
+ const indexAfterParams = this.stream.indexAfterDelimited("LParen" /* LParen */, "RParen" /* RParen */, 0);
3235
+ if (indexAfterParams === null) {
3236
+ return false;
2685
3237
  }
2686
- return this.stream.peekNonWhitespace(index)?.type === "Arrow" /* Arrow */;
3238
+ return this.stream.peekNonWhitespace(indexAfterParams)?.type === "Arrow" /* Arrow */;
2687
3239
  }
2688
3240
  isAsyncArrowFunctionStart() {
2689
3241
  const first = this.stream.peekNonWhitespace(0);
@@ -2693,25 +3245,11 @@ ${caret}`;
2693
3245
  if (this.stream.peekNonWhitespace(1)?.type !== "LParen" /* LParen */) {
2694
3246
  return false;
2695
3247
  }
2696
- let index = 2;
2697
- let depth = 1;
2698
- while (true) {
2699
- const token = this.stream.peekNonWhitespace(index);
2700
- if (!token) {
2701
- return false;
2702
- }
2703
- if (token.type === "LParen" /* LParen */) {
2704
- depth += 1;
2705
- } else if (token.type === "RParen" /* RParen */) {
2706
- depth -= 1;
2707
- if (depth === 0) {
2708
- index += 1;
2709
- break;
2710
- }
2711
- }
2712
- index += 1;
3248
+ const indexAfterParams = this.stream.indexAfterDelimited("LParen" /* LParen */, "RParen" /* RParen */, 1);
3249
+ if (indexAfterParams === null) {
3250
+ return false;
2713
3251
  }
2714
- return this.stream.peekNonWhitespace(index)?.type === "Arrow" /* Arrow */;
3252
+ return this.stream.peekNonWhitespace(indexAfterParams)?.type === "Arrow" /* Arrow */;
2715
3253
  }
2716
3254
  isFunctionExpressionAssignmentStart() {
2717
3255
  const first = this.stream.peekNonWhitespace(0);
@@ -2728,25 +3266,11 @@ ${caret}`;
2728
3266
  if (this.stream.peekNonWhitespace(index)?.type !== "LParen" /* LParen */) {
2729
3267
  return false;
2730
3268
  }
2731
- index += 1;
2732
- let depth = 1;
2733
- while (true) {
2734
- const token = this.stream.peekNonWhitespace(index);
2735
- if (!token) {
2736
- return false;
2737
- }
2738
- if (token.type === "LParen" /* LParen */) {
2739
- depth += 1;
2740
- } else if (token.type === "RParen" /* RParen */) {
2741
- depth -= 1;
2742
- if (depth === 0) {
2743
- index += 1;
2744
- break;
2745
- }
2746
- }
2747
- index += 1;
3269
+ const indexAfterParams = this.stream.indexAfterDelimited("LParen" /* LParen */, "RParen" /* RParen */, index);
3270
+ if (indexAfterParams === null) {
3271
+ return false;
2748
3272
  }
2749
- return this.stream.peekNonWhitespace(index)?.type === "Arrow" /* Arrow */;
3273
+ return this.stream.peekNonWhitespace(indexAfterParams)?.type === "Arrow" /* Arrow */;
2750
3274
  }
2751
3275
  parseExpressionStatement() {
2752
3276
  const expr = this.parseExpression();
@@ -2761,7 +3285,7 @@ ${caret}`;
2761
3285
  const test = this.parseExpression();
2762
3286
  this.stream.skipWhitespace();
2763
3287
  this.stream.expect("RParen" /* RParen */);
2764
- const consequent = this.parseBlock({ allowDeclarations: false });
3288
+ const consequent = this.parseConditionalBody();
2765
3289
  this.stream.skipWhitespace();
2766
3290
  let alternate;
2767
3291
  if (this.stream.peek()?.type === "Else" /* Else */) {
@@ -2771,11 +3295,19 @@ ${caret}`;
2771
3295
  const nested = this.parseIfBlock();
2772
3296
  alternate = new BlockNode([nested]);
2773
3297
  } else {
2774
- alternate = this.parseBlock({ allowDeclarations: false });
3298
+ alternate = this.parseConditionalBody();
2775
3299
  }
2776
3300
  }
2777
3301
  return new IfNode(test, consequent, alternate);
2778
3302
  }
3303
+ parseConditionalBody() {
3304
+ this.stream.skipWhitespace();
3305
+ if (this.stream.peek()?.type === "LBrace" /* LBrace */) {
3306
+ return this.parseBlock({ allowDeclarations: false });
3307
+ }
3308
+ const statement = this.parseStatement({ allowBlocks: false, allowReturn: this.functionDepth > 0 });
3309
+ return new BlockNode([statement]);
3310
+ }
2779
3311
  parseWhileBlock() {
2780
3312
  this.stream.expect("While" /* While */);
2781
3313
  this.stream.skipWhitespace();
@@ -2792,6 +3324,21 @@ ${caret}`;
2792
3324
  this.stream.skipWhitespace();
2793
3325
  this.stream.expect("LParen" /* LParen */);
2794
3326
  this.stream.skipWhitespace();
3327
+ const eachKind = this.detectForEachKind();
3328
+ if (eachKind) {
3329
+ const target = this.parseForEachTarget();
3330
+ this.stream.skipWhitespace();
3331
+ const keyword = this.stream.expect("Identifier" /* Identifier */);
3332
+ if (keyword.value !== eachKind) {
3333
+ throw new Error(`Expected '${eachKind}' but got '${keyword.value}'`);
3334
+ }
3335
+ this.stream.skipWhitespace();
3336
+ const iterable = this.parseExpression();
3337
+ this.stream.skipWhitespace();
3338
+ this.stream.expect("RParen" /* RParen */);
3339
+ const body2 = this.parseBlock({ allowDeclarations: false });
3340
+ return new ForEachNode(target, iterable, eachKind, body2);
3341
+ }
2795
3342
  let init;
2796
3343
  if (this.stream.peek()?.type !== "Semicolon" /* Semicolon */) {
2797
3344
  init = this.parseForClause();
@@ -2815,6 +3362,43 @@ ${caret}`;
2815
3362
  const body = this.parseBlock({ allowDeclarations: false });
2816
3363
  return new ForNode(init, test, update, body);
2817
3364
  }
3365
+ detectForEachKind() {
3366
+ let offset = 0;
3367
+ let depth = 0;
3368
+ while (true) {
3369
+ const token = this.stream.peekNonWhitespace(offset);
3370
+ if (!token) {
3371
+ return null;
3372
+ }
3373
+ if (token.type === "LParen" /* LParen */ || token.type === "LBracket" /* LBracket */ || token.type === "LBrace" /* LBrace */) {
3374
+ depth += 1;
3375
+ } else if (token.type === "RParen" /* RParen */ || token.type === "RBracket" /* RBracket */ || token.type === "RBrace" /* RBrace */) {
3376
+ if (depth === 0) {
3377
+ return null;
3378
+ }
3379
+ depth -= 1;
3380
+ }
3381
+ if (depth === 0) {
3382
+ if (token.type === "Semicolon" /* Semicolon */) {
3383
+ return null;
3384
+ }
3385
+ if (token.type === "Identifier" /* Identifier */ && (token.value === "in" || token.value === "of")) {
3386
+ return token.value;
3387
+ }
3388
+ }
3389
+ offset += 1;
3390
+ }
3391
+ }
3392
+ parseForEachTarget() {
3393
+ const token = this.stream.peek();
3394
+ if (!token) {
3395
+ throw new Error("Expected for-each target");
3396
+ }
3397
+ if (token.type !== "Identifier" /* Identifier */) {
3398
+ throw new Error("for-in/of target must be an identifier");
3399
+ }
3400
+ return new IdentifierExpression(this.stream.next().value);
3401
+ }
2818
3402
  parseForClause() {
2819
3403
  if (this.isAssignmentStart()) {
2820
3404
  return this.parseAssignmentExpression();
@@ -2910,9 +3494,6 @@ ${caret}`;
2910
3494
  const body = this.parseFunctionBlockWithAwait(isAsync);
2911
3495
  return new FunctionDeclarationNode(name, params, body, isAsync);
2912
3496
  }
2913
- parseFunctionBlock() {
2914
- return this.parseFunctionBlockWithAwait(false);
2915
- }
2916
3497
  parseReturnStatement() {
2917
3498
  this.stream.expect("Return" /* Return */);
2918
3499
  this.stream.skipWhitespace();
@@ -2925,6 +3506,23 @@ ${caret}`;
2925
3506
  this.stream.expect("Semicolon" /* Semicolon */);
2926
3507
  return new ReturnNode(value);
2927
3508
  }
3509
+ parseAssertStatement() {
3510
+ this.stream.expect("Assert" /* Assert */);
3511
+ this.stream.skipWhitespace();
3512
+ const test = this.parseExpression();
3513
+ this.consumeStatementTerminator();
3514
+ return new AssertNode(test);
3515
+ }
3516
+ parseBreakStatement() {
3517
+ this.stream.expect("Break" /* Break */);
3518
+ this.consumeStatementTerminator();
3519
+ return new BreakNode();
3520
+ }
3521
+ parseContinueStatement() {
3522
+ this.stream.expect("Continue" /* Continue */);
3523
+ this.consumeStatementTerminator();
3524
+ return new ContinueNode();
3525
+ }
2928
3526
  parseArrowFunctionExpression(isAsync = false) {
2929
3527
  const params = this.parseFunctionParams();
2930
3528
  this.stream.skipWhitespace();
@@ -3034,6 +3632,7 @@ var Scope = class _Scope {
3034
3632
  root;
3035
3633
  listeners = /* @__PURE__ */ new Map();
3036
3634
  anyListeners = /* @__PURE__ */ new Set();
3635
+ isEachItem = false;
3037
3636
  createChild() {
3038
3637
  return new _Scope(this);
3039
3638
  }
@@ -3336,18 +3935,19 @@ var Engine = class _Engine {
3336
3935
  eachBindings = /* @__PURE__ */ new WeakMap();
3337
3936
  lifecycleBindings = /* @__PURE__ */ new WeakMap();
3338
3937
  behaviorRegistry = [];
3938
+ behaviorRegistryHashes = /* @__PURE__ */ new Set();
3339
3939
  behaviorBindings = /* @__PURE__ */ new WeakMap();
3340
3940
  behaviorListeners = /* @__PURE__ */ new WeakMap();
3341
3941
  behaviorId = 0;
3342
3942
  codeCache = /* @__PURE__ */ new Map();
3343
3943
  behaviorCache = /* @__PURE__ */ new Map();
3344
3944
  observer;
3345
- observerRoot;
3346
3945
  attributeHandlers = [];
3347
3946
  globals = {};
3348
3947
  importantFlags = /* @__PURE__ */ new WeakMap();
3349
3948
  inlineDeclarations = /* @__PURE__ */ new WeakMap();
3350
3949
  flagHandlers = /* @__PURE__ */ new Map();
3950
+ behaviorModifiers = /* @__PURE__ */ new Map();
3351
3951
  pendingAdded = /* @__PURE__ */ new Set();
3352
3952
  pendingRemoved = /* @__PURE__ */ new Set();
3353
3953
  pendingUpdated = /* @__PURE__ */ new Set();
@@ -3356,10 +3956,119 @@ var Engine = class _Engine {
3356
3956
  diagnostics;
3357
3957
  logger;
3358
3958
  pendingUses = [];
3959
+ pendingAutoBindToScope = [];
3960
+ scopeWatchers = /* @__PURE__ */ new WeakMap();
3961
+ executionStack = [];
3962
+ groupProxyCache = /* @__PURE__ */ new WeakMap();
3359
3963
  constructor(options = {}) {
3360
3964
  this.diagnostics = options.diagnostics ?? false;
3361
3965
  this.logger = options.logger ?? console;
3362
3966
  this.registerGlobal("console", console);
3967
+ this.registerFlag("important");
3968
+ this.registerFlag("trusted");
3969
+ this.registerFlag("debounce", {
3970
+ onEventBind: ({ args }) => ({
3971
+ debounceMs: typeof args === "number" ? args : 200
3972
+ })
3973
+ });
3974
+ this.registerFlag("prevent", {
3975
+ onEventBefore: ({ event }) => {
3976
+ event?.preventDefault();
3977
+ }
3978
+ });
3979
+ this.registerFlag("stop", {
3980
+ onEventBefore: ({ event }) => {
3981
+ event?.stopPropagation();
3982
+ }
3983
+ });
3984
+ this.registerFlag("self", {
3985
+ onEventBefore: ({ event, element }) => {
3986
+ const target = event?.target;
3987
+ if (!(target instanceof Node)) {
3988
+ return false;
3989
+ }
3990
+ return target === element;
3991
+ }
3992
+ });
3993
+ this.registerFlag("outside", {
3994
+ onEventBind: ({ element }) => ({ listenerTarget: element.ownerDocument }),
3995
+ onEventBefore: ({ event, element }) => {
3996
+ const target = event?.target;
3997
+ if (!(target instanceof Node)) {
3998
+ return false;
3999
+ }
4000
+ return !element.contains(target);
4001
+ }
4002
+ });
4003
+ this.registerFlag("once", {
4004
+ onEventBind: () => ({ options: { once: true } })
4005
+ });
4006
+ this.registerFlag("passive", {
4007
+ onEventBind: () => ({ options: { passive: true } })
4008
+ });
4009
+ this.registerFlag("capture", {
4010
+ onEventBind: () => ({ options: { capture: true } })
4011
+ });
4012
+ this.registerFlag("shift", {
4013
+ onEventBefore: ({ event }) => this.matchesKeyFlag(event, "shift")
4014
+ });
4015
+ this.registerFlag("ctrl", {
4016
+ onEventBefore: ({ event }) => this.matchesKeyFlag(event, "ctrl")
4017
+ });
4018
+ this.registerFlag("control", {
4019
+ onEventBefore: ({ event }) => this.matchesKeyFlag(event, "ctrl")
4020
+ });
4021
+ this.registerFlag("alt", {
4022
+ onEventBefore: ({ event }) => this.matchesKeyFlag(event, "alt")
4023
+ });
4024
+ this.registerFlag("meta", {
4025
+ onEventBefore: ({ event }) => this.matchesKeyFlag(event, "meta")
4026
+ });
4027
+ this.registerFlag("enter", {
4028
+ onEventBefore: ({ event }) => this.matchesKeyFlag(event, "enter")
4029
+ });
4030
+ this.registerFlag("escape", {
4031
+ onEventBefore: ({ event }) => this.matchesKeyFlag(event, "escape")
4032
+ });
4033
+ this.registerFlag("esc", {
4034
+ onEventBefore: ({ event }) => this.matchesKeyFlag(event, "escape")
4035
+ });
4036
+ this.registerFlag("tab", {
4037
+ onEventBefore: ({ event }) => this.matchesKeyFlag(event, "tab")
4038
+ });
4039
+ this.registerFlag("space", {
4040
+ onEventBefore: ({ event }) => this.matchesKeyFlag(event, "space")
4041
+ });
4042
+ this.registerFlag("up", {
4043
+ onEventBefore: ({ event }) => this.matchesKeyFlag(event, "arrowup")
4044
+ });
4045
+ this.registerFlag("down", {
4046
+ onEventBefore: ({ event }) => this.matchesKeyFlag(event, "arrowdown")
4047
+ });
4048
+ this.registerFlag("left", {
4049
+ onEventBefore: ({ event }) => this.matchesKeyFlag(event, "arrowleft")
4050
+ });
4051
+ this.registerFlag("right", {
4052
+ onEventBefore: ({ event }) => this.matchesKeyFlag(event, "arrowright")
4053
+ });
4054
+ this.registerFlag("arrowup", {
4055
+ onEventBefore: ({ event }) => this.matchesKeyFlag(event, "arrowup")
4056
+ });
4057
+ this.registerFlag("arrowdown", {
4058
+ onEventBefore: ({ event }) => this.matchesKeyFlag(event, "arrowdown")
4059
+ });
4060
+ this.registerFlag("arrowleft", {
4061
+ onEventBefore: ({ event }) => this.matchesKeyFlag(event, "arrowleft")
4062
+ });
4063
+ this.registerFlag("arrowright", {
4064
+ onEventBefore: ({ event }) => this.matchesKeyFlag(event, "arrowright")
4065
+ });
4066
+ this.registerFlag("delete", {
4067
+ onEventBefore: ({ event }) => this.matchesKeyFlag(event, "delete")
4068
+ });
4069
+ this.registerFlag("backspace", {
4070
+ onEventBefore: ({ event }) => this.matchesKeyFlag(event, "backspace")
4071
+ });
3363
4072
  this.registerGlobal("list", {
3364
4073
  async map(items, fn) {
3365
4074
  if (!Array.isArray(items) || typeof fn !== "function") {
@@ -3397,6 +4106,96 @@ var Engine = class _Engine {
3397
4106
  }
3398
4107
  });
3399
4108
  this.registerDefaultAttributeHandlers();
4109
+ this.registerFlag("int", {
4110
+ transformValue: (_context, value) => this.coerceInt(value)
4111
+ });
4112
+ this.registerFlag("float", {
4113
+ transformValue: (_context, value) => this.coerceFloat(value)
4114
+ });
4115
+ this.registerBehaviorModifier("group", {
4116
+ onConstruct: ({ args, scope, rootScope, behavior, element }) => {
4117
+ const key = typeof args === "string" ? args : void 0;
4118
+ if (!key) {
4119
+ return;
4120
+ }
4121
+ const targetScope = this.getGroupTargetScope(element, behavior, scope, rootScope);
4122
+ const existing = targetScope.getPath?.(key);
4123
+ const list = Array.isArray(existing) ? existing : [];
4124
+ const proxy = this.getGroupProxy(scope);
4125
+ if (!list.includes(proxy)) {
4126
+ list.push(proxy);
4127
+ targetScope.setPath?.(key, list);
4128
+ } else if (!Array.isArray(existing)) {
4129
+ targetScope.setPath?.(key, list);
4130
+ }
4131
+ },
4132
+ onUnbind: ({ args, scope, rootScope, behavior, element }) => {
4133
+ const key = typeof args === "string" ? args : void 0;
4134
+ if (!key) {
4135
+ return;
4136
+ }
4137
+ const targetScope = this.getGroupTargetScope(element, behavior, scope, rootScope);
4138
+ const existing = targetScope.getPath?.(key);
4139
+ if (!Array.isArray(existing)) {
4140
+ return;
4141
+ }
4142
+ const proxy = this.getGroupProxy(scope);
4143
+ const next = existing.filter((entry) => entry !== proxy);
4144
+ if (next.length !== existing.length) {
4145
+ targetScope.setPath?.(key, next);
4146
+ }
4147
+ }
4148
+ });
4149
+ }
4150
+ getGroupTargetScope(element, behavior, scope, rootScope) {
4151
+ let targetScope = rootScope ?? scope;
4152
+ if (behavior.parentSelector) {
4153
+ const parentElement = element.closest(behavior.parentSelector);
4154
+ if (parentElement) {
4155
+ targetScope = this.getScope(parentElement);
4156
+ }
4157
+ }
4158
+ return targetScope;
4159
+ }
4160
+ getGroupProxy(scope) {
4161
+ const cached = this.groupProxyCache.get(scope);
4162
+ if (cached) {
4163
+ return cached;
4164
+ }
4165
+ const proxy = new Proxy(
4166
+ {},
4167
+ {
4168
+ get: (_target, prop) => {
4169
+ if (typeof prop === "symbol") {
4170
+ return void 0;
4171
+ }
4172
+ if (prop === "__scope") {
4173
+ return scope;
4174
+ }
4175
+ return scope.getPath(String(prop));
4176
+ },
4177
+ set: (_target, prop, value) => {
4178
+ if (typeof prop === "symbol") {
4179
+ return false;
4180
+ }
4181
+ scope.setPath(String(prop), value);
4182
+ return true;
4183
+ },
4184
+ has: (_target, prop) => {
4185
+ if (typeof prop === "symbol") {
4186
+ return false;
4187
+ }
4188
+ return scope.getPath(String(prop)) !== void 0;
4189
+ },
4190
+ getOwnPropertyDescriptor: () => ({
4191
+ enumerable: true,
4192
+ configurable: true
4193
+ }),
4194
+ ownKeys: () => []
4195
+ }
4196
+ );
4197
+ this.groupProxyCache.set(scope, proxy);
4198
+ return proxy;
3400
4199
  }
3401
4200
  async mount(root) {
3402
4201
  const documentRoot = root.ownerDocument;
@@ -3423,7 +4222,10 @@ var Engine = class _Engine {
3423
4222
  this.disconnectObserver();
3424
4223
  }
3425
4224
  registerBehaviors(source) {
3426
- const program = new Parser(source, { customFlags: new Set(this.flagHandlers.keys()) }).parseProgram();
4225
+ const program = new Parser(source, {
4226
+ customFlags: new Set(this.flagHandlers.keys()),
4227
+ behaviorFlags: new Set(this.behaviorModifiers.keys())
4228
+ }).parseProgram();
3427
4229
  for (const use of program.uses) {
3428
4230
  if (use.flags?.wait) {
3429
4231
  this.pendingUses.push(this.waitForUseGlobal(use));
@@ -3447,11 +4249,14 @@ var Engine = class _Engine {
3447
4249
  Object.assign(this.globals, values);
3448
4250
  }
3449
4251
  registerFlag(name, handler = {}) {
4252
+ this.flagHandlers.set(name, handler);
4253
+ }
4254
+ registerBehaviorModifier(name, handler = {}) {
3450
4255
  const reserved = /* @__PURE__ */ new Set(["important", "trusted", "debounce"]);
3451
4256
  if (reserved.has(name)) {
3452
- throw new Error(`Flag '${name}' is reserved`);
4257
+ throw new Error(`Behavior modifier '${name}' is reserved`);
3453
4258
  }
3454
- this.flagHandlers.set(name, handler);
4259
+ this.behaviorModifiers.set(name, handler);
3455
4260
  }
3456
4261
  getRegistryStats() {
3457
4262
  return {
@@ -3559,7 +4364,6 @@ var Engine = class _Engine {
3559
4364
  if (this.observer) {
3560
4365
  return;
3561
4366
  }
3562
- this.observerRoot = root;
3563
4367
  this.observerFlush = debounce(() => this.flushObserverQueue(), 10);
3564
4368
  this.observer = new MutationObserver((mutations) => {
3565
4369
  for (const mutation of mutations) {
@@ -3589,7 +4393,6 @@ var Engine = class _Engine {
3589
4393
  disconnectObserver() {
3590
4394
  this.observer?.disconnect();
3591
4395
  this.observer = void 0;
3592
- this.observerRoot = void 0;
3593
4396
  this.pendingAdded.clear();
3594
4397
  this.pendingRemoved.clear();
3595
4398
  this.pendingUpdated.clear();
@@ -3618,6 +4421,8 @@ var Engine = class _Engine {
3618
4421
  if (this.behaviorBindings.has(node)) {
3619
4422
  this.runBehaviorDestruct(node);
3620
4423
  }
4424
+ this.cleanupScopeWatchers(node);
4425
+ this.cleanupBehaviorListeners(node);
3621
4426
  for (const child of Array.from(node.querySelectorAll("*"))) {
3622
4427
  if (this.lifecycleBindings.has(child)) {
3623
4428
  this.runDestruct(child);
@@ -3625,6 +4430,8 @@ var Engine = class _Engine {
3625
4430
  if (this.behaviorBindings.has(child)) {
3626
4431
  this.runBehaviorDestruct(child);
3627
4432
  }
4433
+ this.cleanupScopeWatchers(child);
4434
+ this.cleanupBehaviorListeners(child);
3628
4435
  }
3629
4436
  }
3630
4437
  handleAddedNode(node) {
@@ -3648,13 +4455,13 @@ var Engine = class _Engine {
3648
4455
  }
3649
4456
  async applyBehaviors(root) {
3650
4457
  await this.waitForUses();
3651
- if (this.behaviorRegistry.length === 0) {
3652
- return;
3653
- }
3654
- const elements = [root, ...Array.from(root.querySelectorAll("*"))];
3655
- for (const element of elements) {
3656
- await this.reapplyBehaviorsForElement(element);
4458
+ if (this.behaviorRegistry.length > 0) {
4459
+ const elements = [root, ...Array.from(root.querySelectorAll("*"))];
4460
+ for (const element of elements) {
4461
+ await this.reapplyBehaviorsForElement(element);
4462
+ }
3657
4463
  }
4464
+ this.flushAutoBindQueue();
3658
4465
  }
3659
4466
  async reapplyBehaviorsForElement(element) {
3660
4467
  if (this.behaviorRegistry.length === 0) {
@@ -3686,15 +4493,18 @@ var Engine = class _Engine {
3686
4493
  const rootScope = this.getBehaviorRootScope(element, behavior);
3687
4494
  this.applyBehaviorFunctions(element, scope, behavior.functions, rootScope);
3688
4495
  await this.applyBehaviorDeclarations(element, scope, behavior.declarations, rootScope);
4496
+ await this.applyBehaviorModifierHook("onBind", behavior, element, scope, rootScope);
3689
4497
  if (behavior.construct) {
3690
4498
  await this.safeExecuteBlock(behavior.construct, scope, element, rootScope);
3691
4499
  }
4500
+ await this.applyBehaviorModifierHook("onConstruct", behavior, element, scope, rootScope);
3692
4501
  for (const onBlock of behavior.onBlocks) {
3693
4502
  this.attachBehaviorOnHandler(
3694
4503
  element,
3695
4504
  onBlock.event,
3696
4505
  onBlock.body,
3697
- onBlock.modifiers,
4506
+ onBlock.flags,
4507
+ onBlock.flagArgs,
3698
4508
  onBlock.args,
3699
4509
  behavior.id,
3700
4510
  rootScope
@@ -3704,10 +4514,11 @@ var Engine = class _Engine {
3704
4514
  }
3705
4515
  unbindBehaviorForElement(behavior, element, scope, bound) {
3706
4516
  bound.delete(behavior.id);
4517
+ const rootScope = this.getBehaviorRootScope(element, behavior);
3707
4518
  if (behavior.destruct) {
3708
- const rootScope = this.getBehaviorRootScope(element, behavior);
3709
4519
  void this.safeExecuteBlock(behavior.destruct, scope, element, rootScope);
3710
4520
  }
4521
+ void this.applyBehaviorModifierHook("onDestruct", behavior, element, scope, rootScope);
3711
4522
  const listenerMap = this.behaviorListeners.get(element);
3712
4523
  const listeners = listenerMap?.get(behavior.id);
3713
4524
  if (listeners) {
@@ -3716,6 +4527,7 @@ var Engine = class _Engine {
3716
4527
  }
3717
4528
  listenerMap?.delete(behavior.id);
3718
4529
  }
4530
+ void this.applyBehaviorModifierHook("onUnbind", behavior, element, scope, rootScope);
3719
4531
  this.logDiagnostic("unbind", element, behavior);
3720
4532
  }
3721
4533
  runBehaviorDestruct(element) {
@@ -3725,11 +4537,15 @@ var Engine = class _Engine {
3725
4537
  }
3726
4538
  const scope = this.getScope(element);
3727
4539
  for (const behavior of this.behaviorRegistry) {
3728
- if (!bound.has(behavior.id) || !behavior.destruct) {
4540
+ if (!bound.has(behavior.id) || !behavior.destruct && !this.behaviorHasModifierHooks(behavior)) {
3729
4541
  continue;
3730
4542
  }
3731
4543
  const rootScope = this.getBehaviorRootScope(element, behavior);
3732
- void this.safeExecuteBlock(behavior.destruct, scope, element, rootScope);
4544
+ if (behavior.destruct) {
4545
+ void this.safeExecuteBlock(behavior.destruct, scope, element, rootScope);
4546
+ }
4547
+ void this.applyBehaviorModifierHook("onDestruct", behavior, element, scope, rootScope);
4548
+ void this.applyBehaviorModifierHook("onUnbind", behavior, element, scope, rootScope);
3733
4549
  }
3734
4550
  }
3735
4551
  attachAttributes(element) {
@@ -3813,6 +4629,7 @@ var Engine = class _Engine {
3813
4629
  const fragment = element.content.cloneNode(true);
3814
4630
  const roots = Array.from(fragment.children);
3815
4631
  const itemScope = new Scope(scope);
4632
+ itemScope.isEachItem = true;
3816
4633
  itemScope.setPath(`self.${binding.itemName}`, item);
3817
4634
  if (binding.indexName) {
3818
4635
  itemScope.setPath(`self.${binding.indexName}`, index);
@@ -3851,7 +4668,93 @@ var Engine = class _Engine {
3851
4668
  if (name.includes(":to")) {
3852
4669
  return "to";
3853
4670
  }
3854
- return "both";
4671
+ return "auto";
4672
+ }
4673
+ resolveBindConfig(element, expr, scope, direction) {
4674
+ if (direction !== "auto") {
4675
+ return {
4676
+ direction,
4677
+ seedFromScope: false,
4678
+ syncToScope: direction === "to" || direction === "both",
4679
+ deferToScope: false
4680
+ };
4681
+ }
4682
+ if (this.isInEachScope(scope)) {
4683
+ return { direction: "both", seedFromScope: false, syncToScope: false, deferToScope: false };
4684
+ }
4685
+ if (this.isFormControl(element)) {
4686
+ if (this.hasScopeValue(scope, expr)) {
4687
+ return { direction: "both", seedFromScope: true, syncToScope: false, deferToScope: false };
4688
+ }
4689
+ return { direction: "both", seedFromScope: false, syncToScope: false, deferToScope: true };
4690
+ }
4691
+ if (this.hasScopeValue(scope, expr)) {
4692
+ return { direction: "both", seedFromScope: false, syncToScope: false, deferToScope: false };
4693
+ }
4694
+ if (this.hasElementValue(element)) {
4695
+ return { direction: "both", seedFromScope: false, syncToScope: false, deferToScope: true };
4696
+ }
4697
+ return { direction: "both", seedFromScope: false, syncToScope: false, deferToScope: false };
4698
+ }
4699
+ isFormControl(element) {
4700
+ return element instanceof HTMLInputElement || element instanceof HTMLTextAreaElement || element instanceof HTMLSelectElement;
4701
+ }
4702
+ hasScopeValue(scope, expr) {
4703
+ const key = expr.trim();
4704
+ if (!key) {
4705
+ return false;
4706
+ }
4707
+ const value = scope.get(key);
4708
+ return value !== void 0 && value !== null;
4709
+ }
4710
+ hasElementValue(element) {
4711
+ if (element instanceof HTMLInputElement || element instanceof HTMLTextAreaElement || element instanceof HTMLSelectElement) {
4712
+ return element.value.length > 0;
4713
+ }
4714
+ return (element.textContent ?? "").trim().length > 0;
4715
+ }
4716
+ coerceInt(value) {
4717
+ if (value == null || value === "") {
4718
+ return value;
4719
+ }
4720
+ const num = typeof value === "number" ? value : Number.parseInt(String(value), 10);
4721
+ return Number.isNaN(num) ? value : num;
4722
+ }
4723
+ coerceFloat(value) {
4724
+ if (value == null || value === "") {
4725
+ return value;
4726
+ }
4727
+ const num = typeof value === "number" ? value : Number.parseFloat(String(value));
4728
+ return Number.isNaN(num) ? value : num;
4729
+ }
4730
+ isInEachScope(scope) {
4731
+ let cursor = scope;
4732
+ while (cursor) {
4733
+ if (cursor.isEachItem) {
4734
+ return true;
4735
+ }
4736
+ cursor = cursor.parent;
4737
+ }
4738
+ return false;
4739
+ }
4740
+ flushAutoBindQueue() {
4741
+ if (this.pendingAutoBindToScope.length === 0) {
4742
+ return;
4743
+ }
4744
+ const pending = this.pendingAutoBindToScope;
4745
+ this.pendingAutoBindToScope = [];
4746
+ for (const entry of pending) {
4747
+ if (!entry.element.isConnected) {
4748
+ continue;
4749
+ }
4750
+ if (this.hasScopeValue(entry.scope, entry.expr)) {
4751
+ continue;
4752
+ }
4753
+ if (!this.hasElementValue(entry.element)) {
4754
+ continue;
4755
+ }
4756
+ applyBindToScope(entry.element, entry.expr, entry.scope);
4757
+ }
3855
4758
  }
3856
4759
  hasVsnAttributes(element) {
3857
4760
  return element.getAttributeNames().some((name) => name.startsWith("vsn-"));
@@ -3876,7 +4779,7 @@ var Engine = class _Engine {
3876
4779
  }
3877
4780
  return void 0;
3878
4781
  }
3879
- watch(scope, expr, handler) {
4782
+ watch(scope, expr, handler, element) {
3880
4783
  const key = expr.trim();
3881
4784
  if (!key) {
3882
4785
  return;
@@ -3891,29 +4794,70 @@ var Engine = class _Engine {
3891
4794
  }
3892
4795
  if (target) {
3893
4796
  target.on(key, handler);
4797
+ if (element) {
4798
+ this.trackScopeWatcher(element, target, "path", handler, key);
4799
+ }
3894
4800
  return;
3895
4801
  }
3896
4802
  let cursor = scope;
3897
4803
  while (cursor) {
3898
4804
  cursor.on(key, handler);
4805
+ if (element) {
4806
+ this.trackScopeWatcher(element, cursor, "path", handler, key);
4807
+ }
3899
4808
  cursor = cursor.parent;
3900
4809
  }
3901
4810
  }
3902
- watchWithDebounce(scope, expr, handler, debounceMs) {
3903
- if (debounceMs) {
3904
- this.watch(scope, expr, debounce(handler, debounceMs));
3905
- } else {
3906
- this.watch(scope, expr, handler);
3907
- }
4811
+ watchWithDebounce(scope, expr, handler, debounceMs, element) {
4812
+ const effectiveHandler = debounceMs ? debounce(handler, debounceMs) : handler;
4813
+ this.watch(scope, expr, effectiveHandler, element);
3908
4814
  }
3909
- watchAllScopes(scope, handler, debounceMs) {
4815
+ watchAllScopes(scope, handler, debounceMs, element) {
3910
4816
  const effectiveHandler = debounceMs ? debounce(handler, debounceMs) : handler;
3911
4817
  let cursor = scope;
3912
4818
  while (cursor) {
3913
4819
  cursor.onAny(effectiveHandler);
4820
+ if (element) {
4821
+ this.trackScopeWatcher(element, cursor, "any", effectiveHandler);
4822
+ }
3914
4823
  cursor = cursor.parent;
3915
4824
  }
3916
4825
  }
4826
+ trackScopeWatcher(element, scope, kind, handler, key) {
4827
+ const watchers = this.scopeWatchers.get(element) ?? [];
4828
+ watchers.push({ scope, kind, handler, ...key ? { key } : {} });
4829
+ this.scopeWatchers.set(element, watchers);
4830
+ }
4831
+ cleanupScopeWatchers(element) {
4832
+ const watchers = this.scopeWatchers.get(element);
4833
+ if (!watchers) {
4834
+ return;
4835
+ }
4836
+ for (const watcher of watchers) {
4837
+ if (watcher.kind === "any") {
4838
+ watcher.scope.offAny(watcher.handler);
4839
+ continue;
4840
+ }
4841
+ if (watcher.key) {
4842
+ watcher.scope.off(watcher.key, watcher.handler);
4843
+ }
4844
+ }
4845
+ this.scopeWatchers.delete(element);
4846
+ }
4847
+ cleanupBehaviorListeners(element) {
4848
+ const listenerMap = this.behaviorListeners.get(element);
4849
+ if (!listenerMap) {
4850
+ return;
4851
+ }
4852
+ for (const listeners of listenerMap.values()) {
4853
+ for (const listener of listeners) {
4854
+ listener.target.removeEventListener(listener.event, listener.handler, listener.options);
4855
+ }
4856
+ }
4857
+ listenerMap.clear();
4858
+ this.behaviorListeners.delete(element);
4859
+ this.behaviorBindings.delete(element);
4860
+ }
3917
4861
  parseOnAttribute(name, value) {
3918
4862
  if (!name.startsWith("vsn-on:")) {
3919
4863
  return null;
@@ -3923,111 +4867,56 @@ var Engine = class _Engine {
3923
4867
  if (!event) {
3924
4868
  return null;
3925
4869
  }
3926
- const descriptor = this.parseEventDescriptor(event);
3927
- if (!descriptor.event) {
3928
- return null;
3929
- }
3930
- let debounceMs;
3931
- const modifiers = [];
3932
- for (const flag of flags) {
3933
- if (flag.startsWith("debounce")) {
3934
- const match = flag.match(/debounce\((\d+)\)/);
3935
- debounceMs = match ? Number(match[1]) : 200;
3936
- continue;
3937
- }
3938
- modifiers.push(flag);
4870
+ if (event.includes(".")) {
4871
+ throw new Error("vsn:on does not support dot modifiers; use !flags instead");
3939
4872
  }
3940
- const combinedModifiers = [...modifiers, ...descriptor.modifiers];
4873
+ const { flagMap, flagArgs } = this.parseInlineFlags(flags);
3941
4874
  const config = {
3942
- event: descriptor.event,
4875
+ event,
3943
4876
  code: value,
3944
- ...debounceMs !== void 0 ? { debounceMs } : {},
3945
- ...combinedModifiers.length > 0 ? { modifiers: combinedModifiers } : {},
3946
- ...descriptor.keyModifiers.length > 0 ? { keyModifiers: descriptor.keyModifiers } : {}
4877
+ flags: flagMap,
4878
+ flagArgs
3947
4879
  };
3948
4880
  return config;
3949
4881
  }
3950
- parseEventDescriptor(raw) {
3951
- const parts = raw.split(".").map((part) => part.trim()).filter(Boolean);
3952
- const event = parts.shift() ?? "";
3953
- const modifiers = [];
3954
- const keyModifiers = [];
3955
- const modifierSet = /* @__PURE__ */ new Set(["outside", "self"]);
3956
- for (const part of parts) {
3957
- if (modifierSet.has(part)) {
3958
- modifiers.push(part);
3959
- } else {
3960
- keyModifiers.push(part);
4882
+ parseInlineFlags(parts) {
4883
+ const flagMap = {};
4884
+ const flagArgs = {};
4885
+ for (const raw of parts) {
4886
+ const trimmed = raw.trim();
4887
+ if (!trimmed) {
4888
+ continue;
3961
4889
  }
3962
- }
3963
- return { event, keyModifiers, modifiers };
3964
- }
3965
- matchesKeyModifiers(event, keyModifiers) {
3966
- if (!keyModifiers || keyModifiers.length === 0) {
3967
- return true;
3968
- }
3969
- if (!(event instanceof KeyboardEvent)) {
3970
- return false;
3971
- }
3972
- const modifierChecks = {
3973
- shift: event.shiftKey,
3974
- ctrl: event.ctrlKey,
3975
- control: event.ctrlKey,
3976
- alt: event.altKey,
3977
- meta: event.metaKey
3978
- };
3979
- const keyAliases = {
3980
- esc: "escape",
3981
- escape: "escape",
3982
- enter: "enter",
3983
- tab: "tab",
3984
- space: "space",
3985
- spacebar: "space",
3986
- up: "arrowup",
3987
- down: "arrowdown",
3988
- left: "arrowleft",
3989
- right: "arrowright",
3990
- arrowup: "arrowup",
3991
- arrowdown: "arrowdown",
3992
- arrowleft: "arrowleft",
3993
- arrowright: "arrowright",
3994
- delete: "delete",
3995
- backspace: "backspace"
3996
- };
3997
- let key = event.key?.toLowerCase() ?? "";
3998
- if (key === " ") {
3999
- key = "space";
4000
- }
4001
- for (const rawModifier of keyModifiers) {
4002
- const modifier = rawModifier.toLowerCase();
4003
- if (modifier in modifierChecks) {
4004
- if (!modifierChecks[modifier]) {
4005
- return false;
4006
- }
4890
+ const match = trimmed.match(/^([a-zA-Z][\w-]*)(?:\((.+)\))?$/);
4891
+ if (!match) {
4007
4892
  continue;
4008
4893
  }
4009
- const expectedKey = keyAliases[modifier] ?? modifier;
4010
- if (key !== expectedKey) {
4011
- return false;
4894
+ const name = match[1] ?? "";
4895
+ if (!name) {
4896
+ continue;
4897
+ }
4898
+ if (!this.flagHandlers.has(name)) {
4899
+ throw new Error(`Unknown flag ${name}`);
4900
+ }
4901
+ flagMap[name] = true;
4902
+ if (match[2] !== void 0) {
4903
+ flagArgs[name] = this.parseInlineFlagArg(match[2]);
4012
4904
  }
4013
4905
  }
4014
- return true;
4906
+ return { flagMap, flagArgs };
4015
4907
  }
4016
- matchesTargetModifiers(element, event, modifiers) {
4017
- if (!modifiers || modifiers.length === 0) {
4908
+ parseInlineFlagArg(raw) {
4909
+ const trimmed = raw.trim();
4910
+ if (trimmed === "true") {
4018
4911
  return true;
4019
4912
  }
4020
- const target = event?.target;
4021
- if (!target || !(target instanceof Node)) {
4022
- return !modifiers.includes("self") && !modifiers.includes("outside");
4023
- }
4024
- if (modifiers.includes("self") && target !== element) {
4913
+ if (trimmed === "false") {
4025
4914
  return false;
4026
4915
  }
4027
- if (modifiers.includes("outside") && element.contains(target)) {
4028
- return false;
4916
+ if (/^-?\d+(\.\d+)?$/.test(trimmed)) {
4917
+ return Number(trimmed);
4029
4918
  }
4030
- return true;
4919
+ return trimmed;
4031
4920
  }
4032
4921
  describeElement(element) {
4033
4922
  const tag = element.tagName.toLowerCase();
@@ -4069,51 +4958,50 @@ var Engine = class _Engine {
4069
4958
  }
4070
4959
  }
4071
4960
  attachOnHandler(element, config) {
4072
- const options = this.getListenerOptions(config.modifiers);
4073
- const listenerTarget = config.modifiers?.includes("outside") ? element.ownerDocument : element;
4961
+ const { listenerTarget, options, debounceMs } = this.getEventBindingConfig(
4962
+ element,
4963
+ config.flags,
4964
+ config.flagArgs
4965
+ );
4074
4966
  let effectiveHandler;
4075
4967
  const handler = async (event) => {
4076
4968
  if (!element.isConnected) {
4077
4969
  listenerTarget.removeEventListener(config.event, effectiveHandler, options);
4078
4970
  return;
4079
4971
  }
4080
- if (!this.matchesKeyModifiers(event, config.keyModifiers)) {
4081
- return;
4082
- }
4083
- if (!this.matchesTargetModifiers(element, event, config.modifiers)) {
4972
+ const scope = this.getScope(element);
4973
+ if (!this.applyEventFlagBefore(element, scope, config.flags, config.flagArgs, event)) {
4084
4974
  return;
4085
4975
  }
4086
- this.applyEventModifiers(event, config.modifiers);
4087
- const scope = this.getScope(element);
4088
4976
  try {
4089
4977
  await this.execute(config.code, scope, element);
4090
4978
  this.evaluate(element);
4091
4979
  } catch (error) {
4092
4980
  this.emitError(element, error);
4981
+ } finally {
4982
+ this.applyEventFlagAfter(element, scope, config.flags, config.flagArgs, event);
4093
4983
  }
4094
4984
  };
4095
- effectiveHandler = config.debounceMs ? debounce(handler, config.debounceMs) : handler;
4985
+ effectiveHandler = debounceMs ? debounce(handler, debounceMs) : handler;
4096
4986
  listenerTarget.addEventListener(config.event, effectiveHandler, options);
4097
4987
  }
4098
- attachBehaviorOnHandler(element, event, body, modifiers, args, behaviorId, rootScope) {
4099
- const descriptor = this.parseEventDescriptor(event);
4100
- const combinedModifiers = modifiers ? [...modifiers, ...descriptor.modifiers] : descriptor.modifiers.length > 0 ? [...descriptor.modifiers] : void 0;
4101
- const listenerTarget = combinedModifiers?.includes("outside") ? element.ownerDocument : element;
4988
+ attachBehaviorOnHandler(element, event, body, flags, flagArgs, args, behaviorId, rootScope) {
4989
+ if (event.includes(".")) {
4990
+ throw new Error("vsn:on does not support dot modifiers; use !flags instead");
4991
+ }
4992
+ const { listenerTarget, options, debounceMs } = this.getEventBindingConfig(element, flags, flagArgs);
4102
4993
  const handler = async (evt) => {
4103
- if (!this.matchesKeyModifiers(evt, descriptor.keyModifiers)) {
4104
- return;
4105
- }
4106
- if (!this.matchesTargetModifiers(element, evt, combinedModifiers)) {
4994
+ const scope = this.getScope(element);
4995
+ if (!this.applyEventFlagBefore(element, scope, flags, flagArgs, evt)) {
4107
4996
  return;
4108
4997
  }
4109
- this.applyEventModifiers(evt, combinedModifiers);
4110
- const scope = this.getScope(element);
4111
4998
  const previousValues = /* @__PURE__ */ new Map();
4112
4999
  if (args && args.length > 0) {
4113
5000
  const argName = args[0];
4114
5001
  if (argName) {
4115
5002
  previousValues.set(argName, scope.getPath(argName));
4116
- scope.setPath(argName, evt);
5003
+ const [nextArg] = this.applyEventFlagArgTransforms(element, scope, flags, flagArgs, evt);
5004
+ scope.setPath(argName, nextArg);
4117
5005
  }
4118
5006
  }
4119
5007
  let failed = false;
@@ -4126,16 +5014,17 @@ var Engine = class _Engine {
4126
5014
  for (const [name, value] of previousValues.entries()) {
4127
5015
  scope.setPath(name, value);
4128
5016
  }
5017
+ this.applyEventFlagAfter(element, scope, flags, flagArgs, evt);
4129
5018
  }
4130
5019
  if (!failed) {
4131
5020
  this.evaluate(element);
4132
5021
  }
4133
5022
  };
4134
- const options = this.getListenerOptions(combinedModifiers);
4135
- listenerTarget.addEventListener(descriptor.event, handler, options);
5023
+ const effectiveHandler = debounceMs ? debounce(handler, debounceMs) : handler;
5024
+ listenerTarget.addEventListener(event, effectiveHandler, options);
4136
5025
  const listenerMap = this.behaviorListeners.get(element) ?? /* @__PURE__ */ new Map();
4137
5026
  const listeners = listenerMap.get(behaviorId) ?? [];
4138
- listeners.push({ target: listenerTarget, event: descriptor.event, handler, options });
5027
+ listeners.push({ target: listenerTarget, event, handler: effectiveHandler, options });
4139
5028
  listenerMap.set(behaviorId, listeners);
4140
5029
  this.behaviorListeners.set(element, listenerMap);
4141
5030
  }
@@ -4166,33 +5055,158 @@ var Engine = class _Engine {
4166
5055
  Promise.resolve().then(handler);
4167
5056
  }
4168
5057
  }
4169
- applyEventModifiers(event, modifiers) {
4170
- if (!event || !modifiers || modifiers.length === 0) {
4171
- return;
5058
+ getEventBindingConfig(element, flags, flagArgs) {
5059
+ let listenerTarget = element;
5060
+ let options = {};
5061
+ let debounceMs;
5062
+ for (const name of Object.keys(flags)) {
5063
+ const handler = this.flagHandlers.get(name);
5064
+ if (!handler?.onEventBind) {
5065
+ continue;
5066
+ }
5067
+ const patch = handler.onEventBind({
5068
+ name,
5069
+ args: flagArgs[name],
5070
+ element,
5071
+ scope: this.getScope(element),
5072
+ rootScope: void 0,
5073
+ event: void 0,
5074
+ engine: this
5075
+ });
5076
+ if (!patch) {
5077
+ continue;
5078
+ }
5079
+ if (patch.listenerTarget) {
5080
+ listenerTarget = patch.listenerTarget;
5081
+ }
5082
+ if (patch.options) {
5083
+ options = { ...options, ...patch.options };
5084
+ }
5085
+ if (patch.debounceMs !== void 0) {
5086
+ debounceMs = patch.debounceMs;
5087
+ }
5088
+ }
5089
+ return {
5090
+ listenerTarget,
5091
+ ...Object.keys(options).length > 0 ? { options } : {},
5092
+ ...debounceMs !== void 0 ? { debounceMs } : {}
5093
+ };
5094
+ }
5095
+ applyEventFlagBefore(element, scope, flags, flagArgs, event) {
5096
+ for (const name of Object.keys(flags)) {
5097
+ const handler = this.flagHandlers.get(name);
5098
+ if (!handler?.onEventBefore) {
5099
+ continue;
5100
+ }
5101
+ const result = handler.onEventBefore({
5102
+ name,
5103
+ args: flagArgs[name],
5104
+ element,
5105
+ scope,
5106
+ rootScope: void 0,
5107
+ event,
5108
+ engine: this
5109
+ });
5110
+ if (result === false) {
5111
+ return false;
5112
+ }
4172
5113
  }
4173
- for (const modifier of modifiers) {
4174
- if (modifier === "prevent") {
4175
- event.preventDefault();
4176
- } else if (modifier === "stop") {
4177
- event.stopPropagation();
5114
+ return true;
5115
+ }
5116
+ applyEventFlagAfter(element, scope, flags, flagArgs, event) {
5117
+ for (const name of Object.keys(flags)) {
5118
+ const handler = this.flagHandlers.get(name);
5119
+ if (!handler?.onEventAfter) {
5120
+ continue;
4178
5121
  }
5122
+ handler.onEventAfter({
5123
+ name,
5124
+ args: flagArgs[name],
5125
+ element,
5126
+ scope,
5127
+ rootScope: void 0,
5128
+ event,
5129
+ engine: this
5130
+ });
4179
5131
  }
4180
5132
  }
4181
- getListenerOptions(modifiers) {
4182
- if (!modifiers || modifiers.length === 0) {
4183
- return void 0;
5133
+ applyEventFlagArgTransforms(element, scope, flags, flagArgs, event) {
5134
+ let args = [event];
5135
+ for (const name of Object.keys(flags)) {
5136
+ const handler = this.flagHandlers.get(name);
5137
+ if (!handler?.transformEventArgs) {
5138
+ continue;
5139
+ }
5140
+ const nextArgs = handler.transformEventArgs(
5141
+ {
5142
+ name,
5143
+ args: flagArgs[name],
5144
+ element,
5145
+ scope,
5146
+ rootScope: void 0,
5147
+ event,
5148
+ engine: this
5149
+ },
5150
+ args
5151
+ );
5152
+ if (Array.isArray(nextArgs)) {
5153
+ args = nextArgs;
5154
+ }
5155
+ }
5156
+ return args;
5157
+ }
5158
+ matchesKeyFlag(event, flag) {
5159
+ if (!(event instanceof KeyboardEvent)) {
5160
+ return false;
5161
+ }
5162
+ const modifierChecks = {
5163
+ shift: event.shiftKey,
5164
+ ctrl: event.ctrlKey,
5165
+ alt: event.altKey,
5166
+ meta: event.metaKey
5167
+ };
5168
+ if (flag in modifierChecks) {
5169
+ return modifierChecks[flag] ?? false;
4184
5170
  }
4185
- const options = {};
4186
- if (modifiers.includes("once")) {
4187
- options.once = true;
5171
+ const keyAliases = {
5172
+ escape: "escape",
5173
+ esc: "escape",
5174
+ enter: "enter",
5175
+ tab: "tab",
5176
+ space: "space",
5177
+ spacebar: "space",
5178
+ up: "arrowup",
5179
+ down: "arrowdown",
5180
+ left: "arrowleft",
5181
+ right: "arrowright",
5182
+ arrowup: "arrowup",
5183
+ arrowdown: "arrowdown",
5184
+ arrowleft: "arrowleft",
5185
+ arrowright: "arrowright",
5186
+ delete: "delete",
5187
+ backspace: "backspace"
5188
+ };
5189
+ let key = event.key?.toLowerCase() ?? "";
5190
+ if (key === " ") {
5191
+ key = "space";
4188
5192
  }
4189
- if (modifiers.includes("passive")) {
4190
- options.passive = true;
5193
+ const expectedKey = keyAliases[flag] ?? flag;
5194
+ return key === expectedKey;
5195
+ }
5196
+ async withExecutionElement(element, fn) {
5197
+ if (!element) {
5198
+ await fn();
5199
+ return;
4191
5200
  }
4192
- if (modifiers.includes("capture")) {
4193
- options.capture = true;
5201
+ this.executionStack.push(element);
5202
+ try {
5203
+ await fn();
5204
+ } finally {
5205
+ this.executionStack.pop();
4194
5206
  }
4195
- return Object.keys(options).length > 0 ? options : void 0;
5207
+ }
5208
+ getCurrentElement() {
5209
+ return this.executionStack[this.executionStack.length - 1];
4196
5210
  }
4197
5211
  async execute(code, scope, element, rootScope) {
4198
5212
  let block = this.codeCache.get(code);
@@ -4200,22 +5214,26 @@ var Engine = class _Engine {
4200
5214
  block = Parser.parseInline(code);
4201
5215
  this.codeCache.set(code, block);
4202
5216
  }
4203
- const context = {
4204
- scope,
4205
- rootScope,
4206
- globals: this.globals,
4207
- ...element ? { element } : {}
4208
- };
4209
- await block.evaluate(context);
5217
+ await this.withExecutionElement(element, async () => {
5218
+ const context = {
5219
+ scope,
5220
+ rootScope,
5221
+ globals: this.globals,
5222
+ ...element ? { element } : {}
5223
+ };
5224
+ await block.evaluate(context);
5225
+ });
4210
5226
  }
4211
5227
  async executeBlock(block, scope, element, rootScope) {
4212
- const context = {
4213
- scope,
4214
- rootScope,
4215
- globals: this.globals,
4216
- ...element ? { element } : {}
4217
- };
4218
- await block.evaluate(context);
5228
+ await this.withExecutionElement(element, async () => {
5229
+ const context = {
5230
+ scope,
5231
+ rootScope,
5232
+ globals: this.globals,
5233
+ ...element ? { element } : {}
5234
+ };
5235
+ await block.evaluate(context);
5236
+ });
4219
5237
  }
4220
5238
  async safeExecute(code, scope, element, rootScope) {
4221
5239
  try {
@@ -4238,15 +5256,26 @@ var Engine = class _Engine {
4238
5256
  collectBehavior(behavior, parentSelector, rootSelectorOverride) {
4239
5257
  const selector = parentSelector ? `${parentSelector} ${behavior.selector.selectorText}` : behavior.selector.selectorText;
4240
5258
  const rootSelector = rootSelectorOverride ?? (parentSelector ?? behavior.selector.selectorText);
5259
+ const behaviorHash = this.hashBehavior(behavior);
5260
+ const hash = `${selector}::${rootSelector}::${behaviorHash}`;
5261
+ if (this.behaviorRegistryHashes.has(hash)) {
5262
+ return;
5263
+ }
4241
5264
  const cached = this.getCachedBehavior(behavior);
4242
- this.behaviorRegistry.push({
5265
+ const entry = {
4243
5266
  id: this.behaviorId += 1,
5267
+ hash,
4244
5268
  selector,
4245
5269
  rootSelector,
4246
5270
  specificity: this.computeSpecificity(selector),
4247
5271
  order: this.behaviorRegistry.length,
4248
- ...cached
4249
- });
5272
+ flags: behavior.flags ?? {},
5273
+ flagArgs: behavior.flagArgs ?? {},
5274
+ ...cached,
5275
+ ...parentSelector ? { parentSelector } : {}
5276
+ };
5277
+ this.behaviorRegistry.push(entry);
5278
+ this.behaviorRegistryHashes.add(hash);
4250
5279
  this.collectNestedBehaviors(behavior.body, selector, rootSelector);
4251
5280
  }
4252
5281
  collectNestedBehaviors(block, parentSelector, rootSelector) {
@@ -4319,7 +5348,8 @@ var Engine = class _Engine {
4319
5348
  blocks.push({
4320
5349
  event: statement.eventName,
4321
5350
  body: statement.body,
4322
- modifiers: statement.modifiers,
5351
+ flags: statement.flags,
5352
+ flagArgs: statement.flagArgs,
4323
5353
  args: statement.args
4324
5354
  });
4325
5355
  }
@@ -4384,6 +5414,8 @@ var Engine = class _Engine {
4384
5414
  return {
4385
5415
  type,
4386
5416
  selector: node.selector?.selectorText ?? "",
5417
+ flags: node.flags ?? {},
5418
+ flagArgs: node.flagArgs ?? {},
4387
5419
  body: this.normalizeNode(node.body)
4388
5420
  };
4389
5421
  }
@@ -4401,6 +5433,8 @@ var Engine = class _Engine {
4401
5433
  type,
4402
5434
  eventName: node.eventName ?? "",
4403
5435
  args: Array.isArray(node.args) ? node.args : [],
5436
+ flags: node.flags ?? {},
5437
+ flagArgs: node.flagArgs ?? {},
4404
5438
  body: this.normalizeNode(node.body)
4405
5439
  };
4406
5440
  }
@@ -4418,21 +5452,9 @@ var Engine = class _Engine {
4418
5452
  return {
4419
5453
  type,
4420
5454
  target: this.normalizeNode(node.target),
4421
- value: this.normalizeNode(node.value)
4422
- };
4423
- }
4424
- if (type === "StateBlock") {
4425
- return {
4426
- type,
4427
- entries: Array.isArray(node.entries) ? node.entries.map((entry) => this.normalizeNode(entry)) : []
4428
- };
4429
- }
4430
- if (type === "StateEntry") {
4431
- return {
4432
- type,
4433
- name: node.name ?? "",
4434
5455
  value: this.normalizeNode(node.value),
4435
- important: Boolean(node.important)
5456
+ operator: node.operator ?? "",
5457
+ prefix: Boolean(node.prefix)
4436
5458
  };
4437
5459
  }
4438
5460
  if (type === "FunctionDeclaration") {
@@ -4466,6 +5488,15 @@ var Engine = class _Engine {
4466
5488
  value: this.normalizeNode(node.value ?? null)
4467
5489
  };
4468
5490
  }
5491
+ if (type === "Assert") {
5492
+ return {
5493
+ type,
5494
+ test: this.normalizeNode(node.test)
5495
+ };
5496
+ }
5497
+ if (type === "Break" || type === "Continue") {
5498
+ return { type };
5499
+ }
4469
5500
  if (type === "If") {
4470
5501
  return {
4471
5502
  type,
@@ -4490,6 +5521,15 @@ var Engine = class _Engine {
4490
5521
  body: this.normalizeNode(node.body)
4491
5522
  };
4492
5523
  }
5524
+ if (type === "ForEach") {
5525
+ return {
5526
+ type,
5527
+ kind: node.kind ?? "of",
5528
+ target: this.normalizeNode(node.target),
5529
+ iterable: this.normalizeNode(node.iterable),
5530
+ body: this.normalizeNode(node.body)
5531
+ };
5532
+ }
4493
5533
  if (type === "Try") {
4494
5534
  return {
4495
5535
  type,
@@ -4612,7 +5652,9 @@ var Engine = class _Engine {
4612
5652
  globals: this.globals,
4613
5653
  element,
4614
5654
  returnValue: void 0,
4615
- returning: false
5655
+ returning: false,
5656
+ breaking: false,
5657
+ continuing: false
4616
5658
  };
4617
5659
  const previousValues = /* @__PURE__ */ new Map();
4618
5660
  await this.applyFunctionParams(callScope, declaration.params, previousValues, context, args);
@@ -4663,6 +5705,7 @@ var Engine = class _Engine {
4663
5705
  const context = { scope, rootScope, element };
4664
5706
  const operator = declaration.operator;
4665
5707
  const debounceMs = declaration.flags.debounce ? declaration.flagArgs.debounce ?? 200 : void 0;
5708
+ const transform = (value) => this.applyCustomFlagTransforms(value, element, scope, declaration);
4666
5709
  const importantKey = this.getImportantKey(declaration);
4667
5710
  if (!declaration.flags.important && importantKey && this.isImportant(element, importantKey)) {
4668
5711
  return;
@@ -4673,7 +5716,8 @@ var Engine = class _Engine {
4673
5716
  this.applyCustomFlags(element, scope, declaration);
4674
5717
  if (declaration.target instanceof IdentifierExpression) {
4675
5718
  const value = await declaration.value.evaluate(context);
4676
- scope.setPath(declaration.target.name, value);
5719
+ const transformed = this.applyCustomFlagTransforms(value, element, scope, declaration);
5720
+ scope.setPath(declaration.target.name, transformed);
4677
5721
  if (declaration.flags.important && importantKey) {
4678
5722
  this.markImportant(element, importantKey);
4679
5723
  }
@@ -4686,7 +5730,7 @@ var Engine = class _Engine {
4686
5730
  const exprIdentifier = declaration.value instanceof IdentifierExpression ? declaration.value.name : void 0;
4687
5731
  if (operator === ":>") {
4688
5732
  if (exprIdentifier) {
4689
- this.applyDirectiveToScope(element, target, exprIdentifier, scope, debounceMs, rootScope);
5733
+ this.applyDirectiveToScope(element, target, exprIdentifier, scope, debounceMs, rootScope, transform);
4690
5734
  }
4691
5735
  if (declaration.flags.important && importantKey) {
4692
5736
  this.markImportant(element, importantKey);
@@ -4694,11 +5738,12 @@ var Engine = class _Engine {
4694
5738
  return;
4695
5739
  }
4696
5740
  if (operator === ":=" && exprIdentifier) {
4697
- this.applyDirectiveToScope(element, target, exprIdentifier, scope, debounceMs, rootScope);
5741
+ this.applyDirectiveToScope(element, target, exprIdentifier, scope, debounceMs, rootScope, transform);
4698
5742
  }
4699
5743
  if (!exprIdentifier) {
4700
5744
  const value = await declaration.value.evaluate(context);
4701
- this.setDirectiveValue(element, target, value, declaration.flags.trusted);
5745
+ const transformed = this.applyCustomFlagTransforms(value, element, scope, declaration);
5746
+ this.setDirectiveValue(element, target, transformed, declaration.flags.trusted);
4702
5747
  const shouldWatch2 = operator === ":<" || operator === ":=";
4703
5748
  if (shouldWatch2) {
4704
5749
  this.applyDirectiveFromExpression(
@@ -4748,6 +5793,63 @@ var Engine = class _Engine {
4748
5793
  });
4749
5794
  }
4750
5795
  }
5796
+ applyCustomFlagTransforms(value, element, scope, declaration) {
5797
+ if (this.flagHandlers.size === 0) {
5798
+ return value;
5799
+ }
5800
+ let nextValue = value;
5801
+ for (const [name, handler] of this.flagHandlers) {
5802
+ if (!declaration.flags[name] || !handler.transformValue) {
5803
+ continue;
5804
+ }
5805
+ nextValue = handler.transformValue(
5806
+ {
5807
+ name,
5808
+ args: declaration.flagArgs[name],
5809
+ element,
5810
+ scope,
5811
+ declaration
5812
+ },
5813
+ nextValue
5814
+ );
5815
+ }
5816
+ return nextValue;
5817
+ }
5818
+ async applyBehaviorModifierHook(hook, behavior, element, scope, rootScope) {
5819
+ if (this.behaviorModifiers.size === 0) {
5820
+ return;
5821
+ }
5822
+ for (const [name, handler] of this.behaviorModifiers) {
5823
+ if (!behavior.flags?.[name]) {
5824
+ continue;
5825
+ }
5826
+ const callback = handler[hook];
5827
+ if (!callback) {
5828
+ continue;
5829
+ }
5830
+ await callback({
5831
+ name,
5832
+ args: behavior.flagArgs?.[name],
5833
+ element,
5834
+ scope,
5835
+ rootScope,
5836
+ behavior,
5837
+ engine: this
5838
+ });
5839
+ }
5840
+ }
5841
+ behaviorHasModifierHooks(behavior) {
5842
+ if (this.behaviorModifiers.size === 0) {
5843
+ return false;
5844
+ }
5845
+ const flags = behavior.flags ?? {};
5846
+ for (const name of Object.keys(flags)) {
5847
+ if (flags[name] && this.behaviorModifiers.has(name)) {
5848
+ return true;
5849
+ }
5850
+ }
5851
+ return false;
5852
+ }
4751
5853
  applyDirectiveFromScope(element, target, expr, scope, trusted, debounceMs, watch = true, rootScope) {
4752
5854
  if (target.kind === "attr" && target.name === "html" && element instanceof HTMLElement) {
4753
5855
  const handler2 = () => {
@@ -4764,7 +5866,7 @@ var Engine = class _Engine {
4764
5866
  const useRoot = expr.startsWith("root.") && rootScope;
4765
5867
  const sourceScope = useRoot ? rootScope : scope;
4766
5868
  const watchExpr = useRoot ? expr.slice("root.".length) : expr;
4767
- this.watchWithDebounce(sourceScope, watchExpr, handler2, debounceMs);
5869
+ this.watchWithDebounce(sourceScope, watchExpr, handler2, debounceMs, element);
4768
5870
  }
4769
5871
  return;
4770
5872
  }
@@ -4783,7 +5885,7 @@ var Engine = class _Engine {
4783
5885
  const useRoot = expr.startsWith("root.") && rootScope;
4784
5886
  const sourceScope = useRoot ? rootScope : scope;
4785
5887
  const watchExpr = useRoot ? expr.slice("root.".length) : expr;
4786
- this.watchWithDebounce(sourceScope, watchExpr, handler, debounceMs);
5888
+ this.watchWithDebounce(sourceScope, watchExpr, handler, debounceMs, element);
4787
5889
  }
4788
5890
  }
4789
5891
  applyDirectiveFromExpression(element, target, expr, scope, trusted, debounceMs, rootScope) {
@@ -4795,45 +5897,49 @@ var Engine = class _Engine {
4795
5897
  void handler();
4796
5898
  this.watchAllScopes(scope, () => {
4797
5899
  void handler();
4798
- }, debounceMs);
5900
+ }, debounceMs, element);
4799
5901
  }
4800
- applyDirectiveToScope(element, target, expr, scope, debounceMs, rootScope) {
5902
+ applyDirectiveToScope(element, target, expr, scope, debounceMs, rootScope, transform) {
4801
5903
  const useRoot = expr.startsWith("root.") && rootScope;
4802
5904
  const targetScope = useRoot ? rootScope : scope;
4803
5905
  const targetExpr = useRoot ? `self.${expr.slice("root.".length)}` : expr;
4804
5906
  if (target.kind === "attr" && target.name === "value") {
4805
- this.applyValueBindingToScope(element, targetExpr, debounceMs, targetScope);
5907
+ this.applyValueBindingToScope(element, targetExpr, debounceMs, targetScope, transform);
4806
5908
  return;
4807
5909
  }
4808
5910
  if (target.kind === "attr" && target.name === "checked") {
4809
- this.applyCheckedBindingToScope(element, targetExpr, debounceMs, targetScope);
5911
+ this.applyCheckedBindingToScope(element, targetExpr, debounceMs, targetScope, transform);
4810
5912
  return;
4811
5913
  }
4812
5914
  const value = this.getDirectiveValue(element, target);
4813
5915
  if (value != null) {
4814
- targetScope.set(targetExpr, value);
5916
+ const nextValue = transform ? transform(value) : value;
5917
+ targetScope.set(targetExpr, nextValue);
4815
5918
  }
4816
5919
  }
4817
- applyCheckedBindingToScope(element, expr, debounceMs, scope) {
5920
+ applyCheckedBindingToScope(element, expr, debounceMs, scope, transform) {
4818
5921
  if (!(element instanceof HTMLInputElement)) {
4819
5922
  return;
4820
5923
  }
4821
5924
  const handler = () => {
4822
5925
  const targetScope = scope ?? this.getScope(element);
4823
- targetScope.set(expr, element.checked);
5926
+ const value = transform ? transform(element.checked) : element.checked;
5927
+ targetScope.set(expr, value);
4824
5928
  };
4825
5929
  const effectiveHandler = debounceMs ? debounce(handler, debounceMs) : handler;
4826
5930
  effectiveHandler();
4827
5931
  element.addEventListener("change", effectiveHandler);
4828
5932
  element.addEventListener("input", effectiveHandler);
4829
5933
  }
4830
- applyValueBindingToScope(element, expr, debounceMs, scope) {
5934
+ applyValueBindingToScope(element, expr, debounceMs, scope, transform) {
4831
5935
  if (!(element instanceof HTMLInputElement || element instanceof HTMLTextAreaElement || element instanceof HTMLSelectElement)) {
4832
5936
  return;
4833
5937
  }
4834
5938
  const handler = () => {
4835
5939
  const targetScope = scope ?? this.getScope(element);
4836
- applyBindToScope(element, expr, targetScope);
5940
+ const value = element.value;
5941
+ const nextValue = transform ? transform(value) : value;
5942
+ targetScope.set(expr, nextValue);
4837
5943
  };
4838
5944
  const effectiveHandler = debounceMs ? debounce(handler, debounceMs) : handler;
4839
5945
  effectiveHandler();
@@ -4850,6 +5956,14 @@ var Engine = class _Engine {
4850
5956
  return;
4851
5957
  }
4852
5958
  if (target.kind === "attr") {
5959
+ if (target.name === "text" && element instanceof HTMLElement) {
5960
+ element.innerText = value == null ? "" : String(value);
5961
+ return;
5962
+ }
5963
+ if (target.name === "content" && element instanceof HTMLElement) {
5964
+ element.textContent = value == null ? "" : String(value);
5965
+ return;
5966
+ }
4853
5967
  if (target.name === "value") {
4854
5968
  if (element instanceof HTMLInputElement || element instanceof HTMLTextAreaElement) {
4855
5969
  element.value = value == null ? "" : String(value);
@@ -4880,6 +5994,12 @@ var Engine = class _Engine {
4880
5994
  }
4881
5995
  getDirectiveValue(element, target) {
4882
5996
  if (target.kind === "attr") {
5997
+ if (target.name === "text" && element instanceof HTMLElement) {
5998
+ return element.innerText;
5999
+ }
6000
+ if (target.name === "content" && element instanceof HTMLElement) {
6001
+ return element.textContent ?? "";
6002
+ }
4883
6003
  if (target.name === "value") {
4884
6004
  if (element instanceof HTMLInputElement || element instanceof HTMLTextAreaElement) {
4885
6005
  return element.value;
@@ -4915,17 +6035,27 @@ var Engine = class _Engine {
4915
6035
  id: "vsn-bind",
4916
6036
  match: (name) => name.startsWith("vsn-bind"),
4917
6037
  handle: (element, name, value, scope) => {
4918
- const direction = this.parseBindDirection(name);
4919
- this.bindBindings.set(element, { expr: value, direction });
4920
- if (direction === "to" || direction === "both") {
6038
+ const parsedDirection = this.parseBindDirection(name);
6039
+ const config = this.resolveBindConfig(element, value, scope, parsedDirection);
6040
+ const direction = config.direction;
6041
+ const auto = parsedDirection === "auto";
6042
+ this.bindBindings.set(element, { expr: value, direction, auto });
6043
+ if (!auto && (direction === "to" || direction === "both")) {
4921
6044
  this.markInlineDeclaration(element, `state:${value}`);
4922
6045
  }
4923
- if (direction === "to" || direction === "both") {
6046
+ if (config.seedFromScope) {
6047
+ applyBindToElement(element, value, scope);
6048
+ }
6049
+ if (config.deferToScope) {
6050
+ this.pendingAutoBindToScope.push({ element, expr: value, scope });
6051
+ } else if (config.syncToScope) {
4924
6052
  applyBindToScope(element, value, scope);
6053
+ }
6054
+ if (direction === "to" || direction === "both") {
4925
6055
  this.attachBindInputHandler(element, value);
4926
6056
  }
4927
6057
  if (direction === "from" || direction === "both") {
4928
- this.watch(scope, value, () => applyBindToElement(element, value, scope));
6058
+ this.watch(scope, value, () => applyBindToElement(element, value, scope), element);
4929
6059
  }
4930
6060
  }
4931
6061
  });
@@ -4937,7 +6067,7 @@ var Engine = class _Engine {
4937
6067
  if (element instanceof HTMLElement) {
4938
6068
  applyIf(element, value, scope);
4939
6069
  }
4940
- this.watch(scope, value, () => this.evaluate(element));
6070
+ this.watch(scope, value, () => this.evaluate(element), element);
4941
6071
  }
4942
6072
  });
4943
6073
  this.registerAttributeHandler({
@@ -4948,7 +6078,7 @@ var Engine = class _Engine {
4948
6078
  if (element instanceof HTMLElement) {
4949
6079
  applyShow(element, value, scope);
4950
6080
  }
4951
- this.watch(scope, value, () => this.evaluate(element));
6081
+ this.watch(scope, value, () => this.evaluate(element), element);
4952
6082
  }
4953
6083
  });
4954
6084
  this.registerAttributeHandler({
@@ -4964,7 +6094,7 @@ var Engine = class _Engine {
4964
6094
  this.handleTrustedHtml(element);
4965
6095
  }
4966
6096
  }
4967
- this.watch(scope, value, () => this.evaluate(element));
6097
+ this.watch(scope, value, () => this.evaluate(element), element);
4968
6098
  }
4969
6099
  });
4970
6100
  this.registerAttributeHandler({
@@ -4977,7 +6107,7 @@ var Engine = class _Engine {
4977
6107
  }
4978
6108
  this.eachBindings.set(element, { ...config, rendered: [] });
4979
6109
  this.renderEach(element);
4980
- this.watch(scope, config.listExpr, () => this.renderEach(element));
6110
+ this.watch(scope, config.listExpr, () => this.renderEach(element), element);
4981
6111
  }
4982
6112
  });
4983
6113
  this.registerAttributeHandler({
@@ -5032,15 +6162,27 @@ function parseCFS(source) {
5032
6162
  const parser = new Parser(source);
5033
6163
  return parser.parseProgram();
5034
6164
  }
6165
+ if (typeof window !== "undefined") {
6166
+ window["parseCFS"] = parseCFS;
6167
+ }
5035
6168
  function autoMount(root = document) {
5036
6169
  if (typeof document === "undefined") {
5037
6170
  return null;
5038
6171
  }
5039
6172
  const engine = new Engine();
6173
+ globalThis.VSNEngine = engine;
5040
6174
  const startTime = typeof performance !== "undefined" && performance.now ? performance.now() : Date.now();
5041
6175
  const mount = () => {
5042
6176
  const target = root instanceof Document ? root.body : root;
5043
6177
  if (target) {
6178
+ const plugins = globalThis.VSNPlugins;
6179
+ if (plugins && typeof plugins === "object") {
6180
+ for (const plugin of Object.values(plugins)) {
6181
+ if (typeof plugin === "function") {
6182
+ plugin(engine);
6183
+ }
6184
+ }
6185
+ }
5044
6186
  const sources = Array.from(document.querySelectorAll('script[type="text/vsn"]')).map((script) => script.textContent ?? "").join("\n");
5045
6187
  if (sources.trim()) {
5046
6188
  engine.registerBehaviors(sources);
@@ -5052,9 +6194,9 @@ function autoMount(root = document) {
5052
6194
  }
5053
6195
  };
5054
6196
  if (document.readyState === "loading") {
5055
- document.addEventListener("DOMContentLoaded", mount, { once: true });
6197
+ document.addEventListener("DOMContentLoaded", () => setTimeout(mount, 0), { once: true });
5056
6198
  } else {
5057
- mount();
6199
+ setTimeout(mount, 0);
5058
6200
  }
5059
6201
  return engine;
5060
6202
  }
@@ -5067,16 +6209,21 @@ if (typeof document !== "undefined") {
5067
6209
  export {
5068
6210
  ArrayExpression,
5069
6211
  ArrayPattern,
6212
+ AssertError,
6213
+ AssertNode,
5070
6214
  AssignmentNode,
5071
6215
  AwaitExpression,
5072
6216
  BaseNode,
5073
6217
  BehaviorNode,
5074
6218
  BinaryExpression,
5075
6219
  BlockNode,
6220
+ BreakNode,
5076
6221
  CallExpression,
6222
+ ContinueNode,
5077
6223
  DeclarationNode,
5078
6224
  DirectiveExpression,
5079
6225
  Engine,
6226
+ ForEachNode,
5080
6227
  ForNode,
5081
6228
  FunctionDeclarationNode,
5082
6229
  FunctionExpression,
@@ -5096,8 +6243,6 @@ export {
5096
6243
  ReturnNode,
5097
6244
  SelectorNode,
5098
6245
  SpreadElement,
5099
- StateBlockNode,
5100
- StateEntryNode,
5101
6246
  TemplateExpression,
5102
6247
  TernaryExpression,
5103
6248
  TokenType,