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.cjs CHANGED
@@ -22,16 +22,21 @@ var index_exports = {};
22
22
  __export(index_exports, {
23
23
  ArrayExpression: () => ArrayExpression,
24
24
  ArrayPattern: () => ArrayPattern,
25
+ AssertError: () => AssertError,
26
+ AssertNode: () => AssertNode,
25
27
  AssignmentNode: () => AssignmentNode,
26
28
  AwaitExpression: () => AwaitExpression,
27
29
  BaseNode: () => BaseNode,
28
30
  BehaviorNode: () => BehaviorNode,
29
31
  BinaryExpression: () => BinaryExpression,
30
32
  BlockNode: () => BlockNode,
33
+ BreakNode: () => BreakNode,
31
34
  CallExpression: () => CallExpression,
35
+ ContinueNode: () => ContinueNode,
32
36
  DeclarationNode: () => DeclarationNode,
33
37
  DirectiveExpression: () => DirectiveExpression,
34
38
  Engine: () => Engine,
39
+ ForEachNode: () => ForEachNode,
35
40
  ForNode: () => ForNode,
36
41
  FunctionDeclarationNode: () => FunctionDeclarationNode,
37
42
  FunctionExpression: () => FunctionExpression,
@@ -51,8 +56,6 @@ __export(index_exports, {
51
56
  ReturnNode: () => ReturnNode,
52
57
  SelectorNode: () => SelectorNode,
53
58
  SpreadElement: () => SpreadElement,
54
- StateBlockNode: () => StateBlockNode,
55
- StateEntryNode: () => StateEntryNode,
56
59
  TemplateExpression: () => TemplateExpression,
57
60
  TernaryExpression: () => TernaryExpression,
58
61
  TokenType: () => TokenType,
@@ -88,6 +91,9 @@ var TokenType = /* @__PURE__ */ ((TokenType2) => {
88
91
  TokenType2["While"] = "While";
89
92
  TokenType2["Try"] = "Try";
90
93
  TokenType2["Catch"] = "Catch";
94
+ TokenType2["Assert"] = "Assert";
95
+ TokenType2["Break"] = "Break";
96
+ TokenType2["Continue"] = "Continue";
91
97
  TokenType2["LBrace"] = "LBrace";
92
98
  TokenType2["RBrace"] = "RBrace";
93
99
  TokenType2["LParen"] = "LParen";
@@ -103,7 +109,9 @@ var TokenType = /* @__PURE__ */ ((TokenType2) => {
103
109
  TokenType2["Greater"] = "Greater";
104
110
  TokenType2["Less"] = "Less";
105
111
  TokenType2["Plus"] = "Plus";
112
+ TokenType2["PlusPlus"] = "PlusPlus";
106
113
  TokenType2["Minus"] = "Minus";
114
+ TokenType2["MinusMinus"] = "MinusMinus";
107
115
  TokenType2["Tilde"] = "Tilde";
108
116
  TokenType2["Star"] = "Star";
109
117
  TokenType2["Slash"] = "Slash";
@@ -143,6 +151,9 @@ var KEYWORDS = {
143
151
  while: "While" /* While */,
144
152
  try: "Try" /* Try */,
145
153
  catch: "Catch" /* Catch */,
154
+ assert: "Assert" /* Assert */,
155
+ break: "Break" /* Break */,
156
+ continue: "Continue" /* Continue */,
146
157
  true: "Boolean" /* Boolean */,
147
158
  false: "Boolean" /* Boolean */,
148
159
  null: "Null" /* Null */
@@ -244,8 +255,20 @@ var Lexer = class {
244
255
  readIdentifier() {
245
256
  const start = this.position();
246
257
  let value = "";
247
- while (!this.eof() && (this.isAlphaNumeric(this.peek()) || this.peek() === "_" || this.peek() === "-")) {
248
- value += this.next();
258
+ while (!this.eof()) {
259
+ const ch = this.peek();
260
+ if (this.isAlphaNumeric(ch) || ch === "_") {
261
+ value += this.next();
262
+ continue;
263
+ }
264
+ if (ch === "-") {
265
+ if (this.peek(1) === "-") {
266
+ break;
267
+ }
268
+ value += this.next();
269
+ continue;
270
+ }
271
+ break;
249
272
  }
250
273
  const keywordType = KEYWORDS[value];
251
274
  if (keywordType) {
@@ -386,6 +409,16 @@ var Lexer = class {
386
409
  this.next();
387
410
  return this.token("Pipe" /* Pipe */, "|>", start);
388
411
  }
412
+ if (ch === "+" && next === "+") {
413
+ this.next();
414
+ this.next();
415
+ return this.token("PlusPlus" /* PlusPlus */, "++", start);
416
+ }
417
+ if (ch === "-" && next === "-") {
418
+ this.next();
419
+ this.next();
420
+ return this.token("MinusMinus" /* MinusMinus */, "--", start);
421
+ }
389
422
  if (ch === "." && next === "." && this.peek(2) === ".") {
390
423
  this.next();
391
424
  this.next();
@@ -488,11 +521,20 @@ var BaseNode = class {
488
521
  async prepare(_context) {
489
522
  return;
490
523
  }
491
- async evaluate(_context) {
524
+ evaluate(_context) {
492
525
  return void 0;
493
526
  }
494
527
  };
495
- async function evaluateWithChildScope(context, block) {
528
+ function isPromiseLike(value) {
529
+ return Boolean(value) && typeof value.then === "function";
530
+ }
531
+ function resolveMaybe(value, next) {
532
+ if (isPromiseLike(value)) {
533
+ return value.then(next);
534
+ }
535
+ return next(value);
536
+ }
537
+ function evaluateWithChildScope(context, block) {
496
538
  const scope = context.scope;
497
539
  if (!scope || !scope.createChild) {
498
540
  return block.evaluate(context);
@@ -500,7 +542,7 @@ async function evaluateWithChildScope(context, block) {
500
542
  const previousScope = context.scope;
501
543
  context.scope = scope.createChild();
502
544
  try {
503
- return await block.evaluate(context);
545
+ return block.evaluate(context);
504
546
  } finally {
505
547
  context.scope = previousScope;
506
548
  }
@@ -526,15 +568,25 @@ var BlockNode = class extends BaseNode {
526
568
  super("Block");
527
569
  this.statements = statements;
528
570
  }
529
- async evaluate(context) {
530
- for (const statement of this.statements) {
531
- if (context.returning) {
532
- break;
533
- }
534
- if (statement && typeof statement.evaluate === "function") {
535
- await statement.evaluate(context);
571
+ evaluate(context) {
572
+ let index = 0;
573
+ const run = () => {
574
+ while (index < this.statements.length) {
575
+ if (context.returning || context.breaking || context.continuing) {
576
+ break;
577
+ }
578
+ const statement = this.statements[index];
579
+ index += 1;
580
+ if (statement && typeof statement.evaluate === "function") {
581
+ const result = statement.evaluate(context);
582
+ if (isPromiseLike(result)) {
583
+ return result.then(() => run());
584
+ }
585
+ }
536
586
  }
537
- }
587
+ return void 0;
588
+ };
589
+ return run();
538
590
  }
539
591
  };
540
592
  var SelectorNode = class extends BaseNode {
@@ -544,87 +596,205 @@ var SelectorNode = class extends BaseNode {
544
596
  }
545
597
  };
546
598
  var BehaviorNode = class extends BaseNode {
547
- constructor(selector, body) {
599
+ constructor(selector, body, flags = {}, flagArgs = {}) {
548
600
  super("Behavior");
549
601
  this.selector = selector;
550
602
  this.body = body;
551
- }
552
- };
553
- var StateEntryNode = class extends BaseNode {
554
- constructor(name, value, important) {
555
- super("StateEntry");
556
- this.name = name;
557
- this.value = value;
558
- this.important = important;
559
- }
560
- };
561
- var StateBlockNode = class extends BaseNode {
562
- constructor(entries) {
563
- super("StateBlock");
564
- this.entries = entries;
603
+ this.flags = flags;
604
+ this.flagArgs = flagArgs;
565
605
  }
566
606
  };
567
607
  var OnBlockNode = class extends BaseNode {
568
- constructor(eventName, args, body, modifiers = []) {
608
+ constructor(eventName, args, body, flags = {}, flagArgs = {}) {
569
609
  super("OnBlock");
570
610
  this.eventName = eventName;
571
611
  this.args = args;
572
612
  this.body = body;
573
- this.modifiers = modifiers;
613
+ this.flags = flags;
614
+ this.flagArgs = flagArgs;
574
615
  }
575
616
  };
576
617
  var AssignmentNode = class extends BaseNode {
577
- constructor(target, value, operator = "=") {
618
+ constructor(target, value, operator = "=", prefix = false) {
578
619
  super("Assignment");
579
620
  this.target = target;
580
621
  this.value = value;
581
622
  this.operator = operator;
623
+ this.prefix = prefix;
582
624
  }
583
- async evaluate(context) {
625
+ evaluate(context) {
584
626
  if (!context.scope || !context.scope.setPath) {
585
627
  return void 0;
586
628
  }
587
- const value = await this.value.evaluate(context);
588
- if (this.operator !== "=") {
589
- return this.applyCompoundAssignment(context, value);
590
- }
591
- if (this.target instanceof IdentifierExpression && this.target.name.startsWith("root.") && context.rootScope) {
592
- const path = this.target.name.slice("root.".length);
593
- context.rootScope.setPath?.(`self.${path}`, value);
594
- return value;
595
- }
596
- this.assignTarget(context, this.target, value);
597
- return value;
629
+ if (this.operator === "++" || this.operator === "--") {
630
+ return this.applyIncrement(context);
631
+ }
632
+ const value = this.value.evaluate(context);
633
+ return resolveMaybe(value, (resolvedValue) => {
634
+ if (this.operator !== "=") {
635
+ return this.applyCompoundAssignment(context, resolvedValue);
636
+ }
637
+ if (this.target instanceof IdentifierExpression && this.target.name.startsWith("root.") && context.rootScope) {
638
+ const path = this.target.name.slice("root.".length);
639
+ context.rootScope.setPath?.(`self.${path}`, resolvedValue);
640
+ return resolvedValue;
641
+ }
642
+ if (this.target instanceof MemberExpression || this.target instanceof IndexExpression) {
643
+ const resolved = this.resolveAssignmentTarget(context);
644
+ return resolveMaybe(resolved, (resolvedTarget) => {
645
+ if (resolvedTarget?.scope?.setPath) {
646
+ resolvedTarget.scope.setPath(resolvedTarget.path, resolvedValue);
647
+ return resolvedValue;
648
+ }
649
+ this.assignTarget(context, this.target, resolvedValue);
650
+ return resolvedValue;
651
+ });
652
+ }
653
+ this.assignTarget(context, this.target, resolvedValue);
654
+ return resolvedValue;
655
+ });
598
656
  }
599
657
  applyCompoundAssignment(context, value) {
600
658
  if (!context.scope || !context.scope.setPath) {
601
659
  return void 0;
602
660
  }
603
- if (!(this.target instanceof IdentifierExpression)) {
604
- throw new Error("Compound assignment requires a simple identifier");
605
- }
606
- const isRoot = this.target.name.startsWith("root.");
607
- const scope = isRoot && context.rootScope ? context.rootScope : context.scope;
608
- const rawPath = isRoot ? this.target.name.slice("root.".length) : this.target.name;
609
- const path = isRoot ? `self.${rawPath}` : rawPath;
610
- const current = scope?.getPath ? scope.getPath(path) : void 0;
611
- let result;
612
- if (this.operator === "+=") {
613
- result = current + value;
614
- } else if (this.operator === "-=") {
615
- result = current - value;
616
- } else if (this.operator === "*=") {
617
- result = current * value;
618
- } else {
619
- result = current / value;
661
+ const resolved = this.resolveAssignmentTarget(context);
662
+ return resolveMaybe(resolved, (resolvedTarget) => {
663
+ if (!resolvedTarget) {
664
+ throw new Error("Compound assignment requires a simple identifier or member path");
665
+ }
666
+ const { scope, path } = resolvedTarget;
667
+ const current = scope?.getPath ? scope.getPath(path) : void 0;
668
+ let result;
669
+ if (this.operator === "+=") {
670
+ result = current + value;
671
+ } else if (this.operator === "-=") {
672
+ result = current - value;
673
+ } else if (this.operator === "*=") {
674
+ result = current * value;
675
+ } else {
676
+ result = current / value;
677
+ }
678
+ scope?.setPath?.(path, result);
679
+ return result;
680
+ });
681
+ }
682
+ applyIncrement(context) {
683
+ if (!context.scope || !context.scope.setPath) {
684
+ return void 0;
620
685
  }
621
- scope?.setPath?.(path, result);
622
- return result;
686
+ const resolved = this.resolveAssignmentTarget(context);
687
+ return resolveMaybe(resolved, (resolvedTarget) => {
688
+ if (!resolvedTarget) {
689
+ throw new Error("Increment/decrement requires a simple identifier or member path");
690
+ }
691
+ const { scope, path } = resolvedTarget;
692
+ const current = scope?.getPath ? scope.getPath(path) : void 0;
693
+ const numeric = typeof current === "number" ? current : Number(current);
694
+ const delta = this.operator === "++" ? 1 : -1;
695
+ const next = (Number.isNaN(numeric) ? 0 : numeric) + delta;
696
+ scope?.setPath?.(path, next);
697
+ return this.prefix ? next : numeric;
698
+ });
699
+ }
700
+ resolveAssignmentTarget(context) {
701
+ if (this.target instanceof IdentifierExpression) {
702
+ const isRoot = this.target.name.startsWith("root.");
703
+ const rawPath = isRoot ? this.target.name.slice("root.".length) : this.target.name;
704
+ if (isRoot) {
705
+ if (context.rootScope) {
706
+ return { scope: context.rootScope, path: `self.${rawPath}` };
707
+ }
708
+ return { scope: context.scope, path: `root.${rawPath}` };
709
+ }
710
+ return { scope: context.scope, path: rawPath };
711
+ }
712
+ if (this.target instanceof MemberExpression) {
713
+ const resolvedPath = this.target.getIdentifierPath();
714
+ if (resolvedPath) {
715
+ const path = resolvedPath.path;
716
+ const isRoot = path.startsWith("root.");
717
+ const rawPath = isRoot ? path.slice("root.".length) : path;
718
+ if (isRoot) {
719
+ if (context.rootScope) {
720
+ return { scope: context.rootScope, path: `self.${rawPath}` };
721
+ }
722
+ return { scope: context.scope, path: `root.${rawPath}` };
723
+ }
724
+ return { scope: context.scope, path: rawPath };
725
+ }
726
+ const targetExpr = this.target;
727
+ const basePath = this.resolveTargetPath(context, targetExpr.target);
728
+ return resolveMaybe(basePath, (resolvedBase) => {
729
+ if (!resolvedBase) {
730
+ return null;
731
+ }
732
+ const path = `${resolvedBase}.${targetExpr.property}`;
733
+ const isRoot = path.startsWith("root.");
734
+ const rawPath = isRoot ? path.slice("root.".length) : path;
735
+ if (isRoot) {
736
+ if (context.rootScope) {
737
+ return { scope: context.rootScope, path: `self.${rawPath}` };
738
+ }
739
+ return { scope: context.scope, path: `root.${rawPath}` };
740
+ }
741
+ return { scope: context.scope, path: rawPath };
742
+ });
743
+ }
744
+ if (this.target instanceof IndexExpression) {
745
+ const path = this.resolveIndexPath(context, this.target);
746
+ return resolveMaybe(path, (resolvedPath) => {
747
+ if (!resolvedPath) {
748
+ return null;
749
+ }
750
+ const isRoot = resolvedPath.startsWith("root.");
751
+ const rawPath = isRoot ? resolvedPath.slice("root.".length) : resolvedPath;
752
+ if (isRoot) {
753
+ if (context.rootScope) {
754
+ return { scope: context.rootScope, path: `self.${rawPath}` };
755
+ }
756
+ return { scope: context.scope, path: `root.${rawPath}` };
757
+ }
758
+ return { scope: context.scope, path: rawPath };
759
+ });
760
+ }
761
+ return null;
762
+ }
763
+ resolveIndexPath(context, expr) {
764
+ const base = this.resolveTargetPath(context, expr.target);
765
+ return resolveMaybe(base, (resolvedBase) => {
766
+ if (!resolvedBase) {
767
+ return null;
768
+ }
769
+ const indexValue = expr.index.evaluate(context);
770
+ return resolveMaybe(indexValue, (resolvedIndex) => {
771
+ if (resolvedIndex == null) {
772
+ return null;
773
+ }
774
+ return `${resolvedBase}.${resolvedIndex}`;
775
+ });
776
+ });
777
+ }
778
+ resolveTargetPath(context, target) {
779
+ if (target instanceof IdentifierExpression) {
780
+ return target.name;
781
+ }
782
+ if (target instanceof MemberExpression) {
783
+ return target.getIdentifierPath()?.path ?? null;
784
+ }
785
+ if (target instanceof IndexExpression) {
786
+ return this.resolveIndexPath(context, target);
787
+ }
788
+ return null;
623
789
  }
624
790
  assignTarget(context, target, value) {
625
791
  if (!context.scope || !context.scope.setPath) {
626
792
  return;
627
793
  }
794
+ if (target instanceof DirectiveExpression) {
795
+ this.assignDirectiveTarget(context, target, value);
796
+ return;
797
+ }
628
798
  if (target instanceof IdentifierExpression) {
629
799
  context.scope.setPath(target.name, value);
630
800
  return;
@@ -666,19 +836,99 @@ var AssignmentNode = class extends BaseNode {
666
836
  return;
667
837
  }
668
838
  }
839
+ assignDirectiveTarget(context, target, value) {
840
+ const element = context.element;
841
+ if (!element) {
842
+ return;
843
+ }
844
+ if (target.kind === "attr") {
845
+ if (target.name === "value") {
846
+ if (element instanceof HTMLInputElement || element instanceof HTMLTextAreaElement) {
847
+ element.value = value == null ? "" : String(value);
848
+ element.setAttribute("value", element.value);
849
+ return;
850
+ }
851
+ if (element instanceof HTMLSelectElement) {
852
+ element.value = value == null ? "" : String(value);
853
+ return;
854
+ }
855
+ }
856
+ if (target.name === "checked" && element instanceof HTMLInputElement) {
857
+ const checked = value === true || value === "true" || value === 1 || value === "1";
858
+ element.checked = checked;
859
+ if (checked) {
860
+ element.setAttribute("checked", "");
861
+ } else {
862
+ element.removeAttribute("checked");
863
+ }
864
+ return;
865
+ }
866
+ if (target.name === "html" && element instanceof HTMLElement) {
867
+ element.innerHTML = value == null ? "" : String(value);
868
+ return;
869
+ }
870
+ element.setAttribute(target.name, value == null ? "" : String(value));
871
+ return;
872
+ }
873
+ if (target.kind === "style" && element instanceof HTMLElement) {
874
+ element.style.setProperty(target.name, value == null ? "" : String(value));
875
+ }
876
+ }
669
877
  };
670
878
  var ReturnNode = class extends BaseNode {
671
879
  constructor(value) {
672
880
  super("Return");
673
881
  this.value = value;
674
882
  }
675
- async evaluate(context) {
883
+ evaluate(context) {
676
884
  if (context.returning) {
677
885
  return context.returnValue;
678
886
  }
679
- context.returnValue = this.value ? await this.value.evaluate(context) : void 0;
680
- context.returning = true;
681
- return context.returnValue;
887
+ const nextValue = this.value ? this.value.evaluate(context) : void 0;
888
+ return resolveMaybe(nextValue, (resolved) => {
889
+ context.returnValue = resolved;
890
+ context.returning = true;
891
+ return context.returnValue;
892
+ });
893
+ }
894
+ };
895
+ var BreakNode = class extends BaseNode {
896
+ constructor() {
897
+ super("Break");
898
+ }
899
+ evaluate(context) {
900
+ context.breaking = true;
901
+ return void 0;
902
+ }
903
+ };
904
+ var ContinueNode = class extends BaseNode {
905
+ constructor() {
906
+ super("Continue");
907
+ }
908
+ evaluate(context) {
909
+ context.continuing = true;
910
+ return void 0;
911
+ }
912
+ };
913
+ var AssertError = class extends Error {
914
+ constructor(message = "Assertion failed") {
915
+ super(message);
916
+ this.name = "AssertError";
917
+ }
918
+ };
919
+ var AssertNode = class extends BaseNode {
920
+ constructor(test) {
921
+ super("Assert");
922
+ this.test = test;
923
+ }
924
+ evaluate(context) {
925
+ const value = this.test.evaluate(context);
926
+ return resolveMaybe(value, (resolved) => {
927
+ if (!resolved) {
928
+ throw new AssertError();
929
+ }
930
+ return resolved;
931
+ });
682
932
  }
683
933
  };
684
934
  var IfNode = class extends BaseNode {
@@ -688,14 +938,17 @@ var IfNode = class extends BaseNode {
688
938
  this.consequent = consequent;
689
939
  this.alternate = alternate;
690
940
  }
691
- async evaluate(context) {
692
- const condition = await this.test.evaluate(context);
693
- if (condition) {
694
- return evaluateWithChildScope(context, this.consequent);
695
- }
696
- if (this.alternate) {
697
- return evaluateWithChildScope(context, this.alternate);
698
- }
941
+ evaluate(context) {
942
+ const condition = this.test.evaluate(context);
943
+ return resolveMaybe(condition, (resolved) => {
944
+ if (resolved) {
945
+ return evaluateWithChildScope(context, this.consequent);
946
+ }
947
+ if (this.alternate) {
948
+ return evaluateWithChildScope(context, this.alternate);
949
+ }
950
+ return void 0;
951
+ });
699
952
  }
700
953
  };
701
954
  var WhileNode = class extends BaseNode {
@@ -704,21 +957,104 @@ var WhileNode = class extends BaseNode {
704
957
  this.test = test;
705
958
  this.body = body;
706
959
  }
707
- async evaluate(context) {
960
+ evaluate(context) {
708
961
  const previousScope = context.scope;
709
962
  if (context.scope?.createChild) {
710
963
  context.scope = context.scope.createChild();
711
964
  }
712
- try {
713
- while (await this.test.evaluate(context)) {
714
- await this.body.evaluate(context);
715
- if (context.returning) {
716
- break;
965
+ const run = () => {
966
+ const condition = this.test.evaluate(context);
967
+ return resolveMaybe(condition, (resolved) => {
968
+ if (!resolved || context.returning) {
969
+ return void 0;
717
970
  }
971
+ const bodyResult = this.body.evaluate(context);
972
+ return resolveMaybe(bodyResult, () => {
973
+ if (context.breaking) {
974
+ context.breaking = false;
975
+ return void 0;
976
+ }
977
+ if (context.continuing) {
978
+ context.continuing = false;
979
+ }
980
+ return run();
981
+ });
982
+ });
983
+ };
984
+ const result = run();
985
+ if (isPromiseLike(result)) {
986
+ return result.finally(() => {
987
+ context.scope = previousScope;
988
+ });
989
+ }
990
+ context.scope = previousScope;
991
+ return result;
992
+ }
993
+ };
994
+ var ForEachNode = class extends BaseNode {
995
+ constructor(target, iterable, kind, body) {
996
+ super("ForEach");
997
+ this.target = target;
998
+ this.iterable = iterable;
999
+ this.kind = kind;
1000
+ this.body = body;
1001
+ }
1002
+ evaluate(context) {
1003
+ const iterableValue = this.iterable.evaluate(context);
1004
+ return resolveMaybe(iterableValue, (resolved) => {
1005
+ const entries = this.getEntries(resolved);
1006
+ const previousScope = context.scope;
1007
+ let bodyScope = context.scope;
1008
+ if (context.scope?.createChild) {
1009
+ bodyScope = context.scope.createChild();
718
1010
  }
719
- } finally {
720
- context.scope = previousScope;
1011
+ let index = 0;
1012
+ const loop = () => {
1013
+ if (index >= entries.length || context.returning) {
1014
+ context.scope = previousScope;
1015
+ return void 0;
1016
+ }
1017
+ const value = entries[index];
1018
+ index += 1;
1019
+ context.scope = bodyScope;
1020
+ context.scope?.setPath?.(this.target.name, value);
1021
+ const bodyResult = this.body.evaluate(context);
1022
+ return resolveMaybe(bodyResult, () => {
1023
+ if (context.breaking) {
1024
+ context.breaking = false;
1025
+ context.scope = previousScope;
1026
+ return void 0;
1027
+ }
1028
+ if (context.continuing) {
1029
+ context.continuing = false;
1030
+ }
1031
+ context.scope = previousScope;
1032
+ return loop();
1033
+ });
1034
+ };
1035
+ return loop();
1036
+ });
1037
+ }
1038
+ getEntries(value) {
1039
+ if (value == null) {
1040
+ return [];
1041
+ }
1042
+ if (this.kind === "in") {
1043
+ if (typeof value === "object") {
1044
+ return Object.keys(value);
1045
+ }
1046
+ return [];
1047
+ }
1048
+ if (typeof value === "string") {
1049
+ return Array.from(value);
1050
+ }
1051
+ if (typeof value[Symbol.iterator] === "function") {
1052
+ return Array.from(value);
1053
+ }
1054
+ if (typeof value === "object") {
1055
+ return Object.values(value);
721
1056
  }
1057
+ return [];
722
1058
  }
723
1059
  };
724
1060
  var ForNode = class extends BaseNode {
@@ -729,27 +1065,45 @@ var ForNode = class extends BaseNode {
729
1065
  this.update = update;
730
1066
  this.body = body;
731
1067
  }
732
- async evaluate(context) {
733
- if (this.init) {
734
- await this.init.evaluate(context);
735
- }
736
- const previousScope = context.scope;
737
- let bodyScope = context.scope;
738
- if (context.scope?.createChild) {
739
- bodyScope = context.scope.createChild();
740
- }
741
- while (this.test ? await this.test.evaluate(context) : true) {
742
- context.scope = bodyScope;
743
- await this.body.evaluate(context);
744
- if (context.returning) {
745
- break;
746
- }
747
- context.scope = previousScope;
748
- if (this.update) {
749
- await this.update.evaluate(context);
750
- }
751
- }
752
- context.scope = previousScope;
1068
+ evaluate(context) {
1069
+ const initResult = this.init ? this.init.evaluate(context) : void 0;
1070
+ const run = () => {
1071
+ const previousScope = context.scope;
1072
+ let bodyScope = context.scope;
1073
+ if (context.scope?.createChild) {
1074
+ bodyScope = context.scope.createChild();
1075
+ }
1076
+ const loop = () => {
1077
+ const testResult = this.test ? this.test.evaluate(context) : true;
1078
+ return resolveMaybe(testResult, (passed) => {
1079
+ if (!passed || context.returning) {
1080
+ context.scope = previousScope;
1081
+ return void 0;
1082
+ }
1083
+ context.scope = bodyScope;
1084
+ const bodyResult = this.body.evaluate(context);
1085
+ return resolveMaybe(bodyResult, () => {
1086
+ if (context.returning) {
1087
+ context.scope = previousScope;
1088
+ return void 0;
1089
+ }
1090
+ if (context.breaking) {
1091
+ context.breaking = false;
1092
+ context.scope = previousScope;
1093
+ return void 0;
1094
+ }
1095
+ context.scope = previousScope;
1096
+ if (context.continuing) {
1097
+ context.continuing = false;
1098
+ }
1099
+ const updateResult = this.update ? this.update.evaluate(context) : void 0;
1100
+ return resolveMaybe(updateResult, () => loop());
1101
+ });
1102
+ });
1103
+ };
1104
+ return loop();
1105
+ };
1106
+ return resolveMaybe(initResult, () => run());
753
1107
  }
754
1108
  };
755
1109
  var TryNode = class extends BaseNode {
@@ -759,10 +1113,8 @@ var TryNode = class extends BaseNode {
759
1113
  this.errorName = errorName;
760
1114
  this.handler = handler;
761
1115
  }
762
- async evaluate(context) {
763
- try {
764
- return await evaluateWithChildScope(context, this.body);
765
- } catch (error) {
1116
+ evaluate(context) {
1117
+ const handleError = (error) => {
766
1118
  if (context.returning) {
767
1119
  return context.returnValue;
768
1120
  }
@@ -780,11 +1132,23 @@ var TryNode = class extends BaseNode {
780
1132
  scope.setPath(`self.${this.errorName}`, error);
781
1133
  }
782
1134
  }
783
- await this.handler.evaluate(context);
784
- if (scope && scope.setPath && handlerScope === previousScope) {
785
- scope.setPath(this.errorName, previous);
1135
+ const handlerResult = this.handler.evaluate(context);
1136
+ return resolveMaybe(handlerResult, () => {
1137
+ if (scope && scope.setPath && handlerScope === previousScope) {
1138
+ scope.setPath(this.errorName, previous);
1139
+ }
1140
+ context.scope = previousScope;
1141
+ return void 0;
1142
+ });
1143
+ };
1144
+ try {
1145
+ const bodyResult = evaluateWithChildScope(context, this.body);
1146
+ if (isPromiseLike(bodyResult)) {
1147
+ return bodyResult.catch((error) => handleError(error));
786
1148
  }
787
- context.scope = previousScope;
1149
+ return bodyResult;
1150
+ } catch (error) {
1151
+ return handleError(error);
788
1152
  }
789
1153
  }
790
1154
  };
@@ -804,11 +1168,35 @@ var FunctionExpression = class extends BaseNode {
804
1168
  this.body = body;
805
1169
  this.isAsync = isAsync;
806
1170
  }
807
- async evaluate(context) {
1171
+ evaluate(context) {
808
1172
  const scope = context.scope;
809
1173
  const globals = context.globals;
810
1174
  const element = context.element;
811
- return async (...args) => {
1175
+ if (this.isAsync) {
1176
+ return (...args) => {
1177
+ const activeScope = scope?.createChild ? scope.createChild() : scope;
1178
+ const inner = {
1179
+ scope: activeScope,
1180
+ rootScope: context.rootScope,
1181
+ ...globals ? { globals } : {},
1182
+ ...element ? { element } : {},
1183
+ returnValue: void 0,
1184
+ returning: false,
1185
+ breaking: false,
1186
+ continuing: false
1187
+ };
1188
+ const previousValues = /* @__PURE__ */ new Map();
1189
+ const applyResult = activeScope ? this.applyParams(activeScope, previousValues, inner, args) : void 0;
1190
+ const bodyResult = resolveMaybe(applyResult, () => this.body.evaluate(inner));
1191
+ const finalResult = resolveMaybe(bodyResult, () => inner.returnValue);
1192
+ return Promise.resolve(finalResult).finally(() => {
1193
+ if (activeScope && activeScope === scope) {
1194
+ this.restoreParams(activeScope, previousValues);
1195
+ }
1196
+ });
1197
+ };
1198
+ }
1199
+ return (...args) => {
812
1200
  const activeScope = scope?.createChild ? scope.createChild() : scope;
813
1201
  const inner = {
814
1202
  scope: activeScope,
@@ -816,47 +1204,69 @@ var FunctionExpression = class extends BaseNode {
816
1204
  ...globals ? { globals } : {},
817
1205
  ...element ? { element } : {},
818
1206
  returnValue: void 0,
819
- returning: false
1207
+ returning: false,
1208
+ breaking: false,
1209
+ continuing: false
820
1210
  };
821
- if (activeScope) {
822
- const previousValues = /* @__PURE__ */ new Map();
823
- await this.applyParams(activeScope, previousValues, inner, args);
824
- await this.body.evaluate(inner);
825
- if (activeScope === scope) {
826
- this.restoreParams(activeScope, previousValues);
827
- }
828
- } else {
829
- await this.body.evaluate(inner);
1211
+ const previousValues = /* @__PURE__ */ new Map();
1212
+ const applyResult = activeScope ? this.applyParams(activeScope, previousValues, inner, args) : void 0;
1213
+ const bodyResult = resolveMaybe(applyResult, () => this.body.evaluate(inner));
1214
+ const finalResult = resolveMaybe(bodyResult, () => inner.returnValue);
1215
+ if (isPromiseLike(finalResult)) {
1216
+ return finalResult.finally(() => {
1217
+ if (activeScope && activeScope === scope) {
1218
+ this.restoreParams(activeScope, previousValues);
1219
+ }
1220
+ });
1221
+ }
1222
+ if (activeScope && activeScope === scope) {
1223
+ this.restoreParams(activeScope, previousValues);
830
1224
  }
831
- return inner.returnValue;
1225
+ return finalResult;
832
1226
  };
833
1227
  }
834
- async applyParams(scope, previousValues, context, args) {
835
- if (!scope || !scope.setPath) {
1228
+ applyParams(scope, previousValues, context, args) {
1229
+ if (!scope) {
836
1230
  return;
837
1231
  }
838
- let argIndex = 0;
839
- for (const param of this.params) {
840
- const name = param.name;
841
- if (!name) {
842
- continue;
843
- }
844
- previousValues.set(name, scope.getPath(name));
845
- if (param.rest) {
846
- scope.setPath(`self.${name}`, args.slice(argIndex));
847
- argIndex = args.length;
848
- continue;
849
- }
850
- let value = args[argIndex];
851
- if (value === void 0 && param.defaultValue) {
852
- value = await param.defaultValue.evaluate(context);
853
- }
854
- scope.setPath(`self.${name}`, value);
855
- argIndex += 1;
1232
+ const setPath = scope.setPath?.bind(scope);
1233
+ if (!setPath) {
1234
+ return;
856
1235
  }
1236
+ const params = this.params;
1237
+ const applyAt = (paramIndex, argIndex) => {
1238
+ for (let i = paramIndex; i < params.length; i += 1) {
1239
+ const param = params[i];
1240
+ const name = param.name;
1241
+ if (!name) {
1242
+ continue;
1243
+ }
1244
+ previousValues.set(name, scope.getPath(name));
1245
+ if (param.rest) {
1246
+ setPath(`self.${name}`, args.slice(argIndex));
1247
+ return;
1248
+ }
1249
+ let value = args[argIndex];
1250
+ if (value === void 0 && param.defaultValue) {
1251
+ const defaultValue = param.defaultValue.evaluate(context);
1252
+ return resolveMaybe(defaultValue, (resolvedDefault) => {
1253
+ setPath(`self.${name}`, resolvedDefault);
1254
+ return applyAt(i + 1, argIndex + 1);
1255
+ });
1256
+ }
1257
+ setPath(`self.${name}`, value);
1258
+ argIndex += 1;
1259
+ }
1260
+ return;
1261
+ };
1262
+ return applyAt(0, 0);
857
1263
  }
858
1264
  restoreParams(scope, previousValues) {
859
- if (!scope || !scope.setPath) {
1265
+ if (!scope) {
1266
+ return;
1267
+ }
1268
+ const setPath = scope.setPath?.bind(scope);
1269
+ if (!setPath) {
860
1270
  return;
861
1271
  }
862
1272
  for (const param of this.params) {
@@ -864,7 +1274,7 @@ var FunctionExpression = class extends BaseNode {
864
1274
  if (!name) {
865
1275
  continue;
866
1276
  }
867
- scope.setPath(name, previousValues.get(name));
1277
+ setPath(name, previousValues.get(name));
868
1278
  }
869
1279
  }
870
1280
  };
@@ -883,7 +1293,7 @@ var IdentifierExpression = class extends BaseNode {
883
1293
  super("Identifier");
884
1294
  this.name = name;
885
1295
  }
886
- async evaluate(context) {
1296
+ evaluate(context) {
887
1297
  if (this.name.startsWith("root.") && context.rootScope) {
888
1298
  const path = this.name.slice("root.".length);
889
1299
  return context.rootScope.getPath(`self.${path}`);
@@ -928,7 +1338,7 @@ var LiteralExpression = class extends BaseNode {
928
1338
  super("Literal");
929
1339
  this.value = value;
930
1340
  }
931
- async evaluate() {
1341
+ evaluate() {
932
1342
  return this.value;
933
1343
  }
934
1344
  };
@@ -937,13 +1347,22 @@ var TemplateExpression = class extends BaseNode {
937
1347
  super("TemplateExpression");
938
1348
  this.parts = parts;
939
1349
  }
940
- async evaluate(context) {
1350
+ evaluate(context) {
941
1351
  let result = "";
942
- for (const part of this.parts) {
943
- const value = await part.evaluate(context);
944
- result += value == null ? "" : String(value);
945
- }
946
- return result;
1352
+ let index = 0;
1353
+ const run = () => {
1354
+ while (index < this.parts.length) {
1355
+ const part = this.parts[index];
1356
+ index += 1;
1357
+ const value = part.evaluate(context);
1358
+ return resolveMaybe(value, (resolved) => {
1359
+ result += resolved == null ? "" : String(resolved);
1360
+ return run();
1361
+ });
1362
+ }
1363
+ return result;
1364
+ };
1365
+ return run();
947
1366
  }
948
1367
  };
949
1368
  var UnaryExpression = class extends BaseNode {
@@ -952,15 +1371,17 @@ var UnaryExpression = class extends BaseNode {
952
1371
  this.operator = operator;
953
1372
  this.argument = argument;
954
1373
  }
955
- async evaluate(context) {
956
- const value = await this.argument.evaluate(context);
957
- if (this.operator === "!") {
958
- return !value;
959
- }
960
- if (this.operator === "-") {
961
- return -value;
962
- }
963
- return value;
1374
+ evaluate(context) {
1375
+ const value = this.argument.evaluate(context);
1376
+ return resolveMaybe(value, (resolved) => {
1377
+ if (this.operator === "!") {
1378
+ return !resolved;
1379
+ }
1380
+ if (this.operator === "-") {
1381
+ return -resolved;
1382
+ }
1383
+ return resolved;
1384
+ });
964
1385
  }
965
1386
  };
966
1387
  var BinaryExpression = class extends BaseNode {
@@ -970,61 +1391,71 @@ var BinaryExpression = class extends BaseNode {
970
1391
  this.left = left;
971
1392
  this.right = right;
972
1393
  }
973
- async evaluate(context) {
974
- if (this.operator === "&&") {
975
- const leftValue = await this.left.evaluate(context);
976
- return leftValue && await this.right.evaluate(context);
977
- }
978
- if (this.operator === "||") {
979
- const leftValue = await this.left.evaluate(context);
980
- return leftValue || await this.right.evaluate(context);
981
- }
982
- if (this.operator === "??") {
983
- const leftValue = await this.left.evaluate(context);
984
- return leftValue ?? await this.right.evaluate(context);
985
- }
986
- const left = await this.left.evaluate(context);
987
- const right = await this.right.evaluate(context);
988
- if (this.operator === "+") {
989
- return left + right;
990
- }
991
- if (this.operator === "-") {
992
- return left - right;
993
- }
994
- if (this.operator === "*") {
995
- return left * right;
996
- }
997
- if (this.operator === "/") {
998
- return left / right;
999
- }
1000
- if (this.operator === "%") {
1001
- return left % right;
1002
- }
1003
- if (this.operator === "==") {
1004
- return left == right;
1005
- }
1006
- if (this.operator === "!=") {
1007
- return left != right;
1008
- }
1009
- if (this.operator === "===") {
1010
- return left === right;
1011
- }
1012
- if (this.operator === "!==") {
1013
- return left !== right;
1014
- }
1015
- if (this.operator === "<") {
1016
- return left < right;
1017
- }
1018
- if (this.operator === ">") {
1019
- return left > right;
1020
- }
1021
- if (this.operator === "<=") {
1022
- return left <= right;
1023
- }
1024
- if (this.operator === ">=") {
1025
- return left >= right;
1026
- }
1027
- return void 0;
1394
+ evaluate(context) {
1395
+ const leftValue = this.left.evaluate(context);
1396
+ return resolveMaybe(leftValue, (resolvedLeft) => {
1397
+ if (this.operator === "&&") {
1398
+ if (!resolvedLeft) {
1399
+ return resolvedLeft;
1400
+ }
1401
+ return this.right.evaluate(context);
1402
+ }
1403
+ if (this.operator === "||") {
1404
+ if (resolvedLeft) {
1405
+ return resolvedLeft;
1406
+ }
1407
+ return this.right.evaluate(context);
1408
+ }
1409
+ if (this.operator === "??") {
1410
+ if (resolvedLeft !== null && resolvedLeft !== void 0) {
1411
+ return resolvedLeft;
1412
+ }
1413
+ return this.right.evaluate(context);
1414
+ }
1415
+ const rightValue = this.right.evaluate(context);
1416
+ return resolveMaybe(rightValue, (resolvedRight) => {
1417
+ if (this.operator === "+") {
1418
+ return resolvedLeft + resolvedRight;
1419
+ }
1420
+ if (this.operator === "-") {
1421
+ return resolvedLeft - resolvedRight;
1422
+ }
1423
+ if (this.operator === "*") {
1424
+ return resolvedLeft * resolvedRight;
1425
+ }
1426
+ if (this.operator === "/") {
1427
+ return resolvedLeft / resolvedRight;
1428
+ }
1429
+ if (this.operator === "%") {
1430
+ return resolvedLeft % resolvedRight;
1431
+ }
1432
+ if (this.operator === "==") {
1433
+ return resolvedLeft == resolvedRight;
1434
+ }
1435
+ if (this.operator === "!=") {
1436
+ return resolvedLeft != resolvedRight;
1437
+ }
1438
+ if (this.operator === "===") {
1439
+ return resolvedLeft === resolvedRight;
1440
+ }
1441
+ if (this.operator === "!==") {
1442
+ return resolvedLeft !== resolvedRight;
1443
+ }
1444
+ if (this.operator === "<") {
1445
+ return resolvedLeft < resolvedRight;
1446
+ }
1447
+ if (this.operator === ">") {
1448
+ return resolvedLeft > resolvedRight;
1449
+ }
1450
+ if (this.operator === "<=") {
1451
+ return resolvedLeft <= resolvedRight;
1452
+ }
1453
+ if (this.operator === ">=") {
1454
+ return resolvedLeft >= resolvedRight;
1455
+ }
1456
+ return void 0;
1457
+ });
1458
+ });
1028
1459
  }
1029
1460
  };
1030
1461
  var TernaryExpression = class extends BaseNode {
@@ -1034,12 +1465,14 @@ var TernaryExpression = class extends BaseNode {
1034
1465
  this.consequent = consequent;
1035
1466
  this.alternate = alternate;
1036
1467
  }
1037
- async evaluate(context) {
1038
- const condition = await this.test.evaluate(context);
1039
- if (condition) {
1040
- return this.consequent.evaluate(context);
1041
- }
1042
- return this.alternate.evaluate(context);
1468
+ evaluate(context) {
1469
+ const condition = this.test.evaluate(context);
1470
+ return resolveMaybe(condition, (resolved) => {
1471
+ if (resolved) {
1472
+ return this.consequent.evaluate(context);
1473
+ }
1474
+ return this.alternate.evaluate(context);
1475
+ });
1043
1476
  }
1044
1477
  };
1045
1478
  var MemberExpression = class _MemberExpression extends BaseNode {
@@ -1049,11 +1482,11 @@ var MemberExpression = class _MemberExpression extends BaseNode {
1049
1482
  this.property = property;
1050
1483
  this.optional = optional;
1051
1484
  }
1052
- async evaluate(context) {
1053
- const resolved = await this.resolve(context);
1054
- return resolved?.value;
1485
+ evaluate(context) {
1486
+ const resolved = this.resolve(context);
1487
+ return resolveMaybe(resolved, (resolvedValue) => resolvedValue?.value);
1055
1488
  }
1056
- async resolve(context) {
1489
+ resolve(context) {
1057
1490
  const path = this.getIdentifierPath();
1058
1491
  if (path) {
1059
1492
  const resolved = this.resolveFromScope(context, path);
@@ -1065,11 +1498,13 @@ var MemberExpression = class _MemberExpression extends BaseNode {
1065
1498
  return resolvedGlobal;
1066
1499
  }
1067
1500
  }
1068
- const target = await this.target.evaluate(context);
1069
- if (target == null) {
1070
- return { value: void 0, target, optional: this.optional };
1071
- }
1072
- return { value: target[this.property], target, optional: this.optional };
1501
+ const target = this.target.evaluate(context);
1502
+ return resolveMaybe(target, (resolvedTarget) => {
1503
+ if (resolvedTarget == null) {
1504
+ return { value: void 0, target: resolvedTarget, optional: this.optional };
1505
+ }
1506
+ return { value: resolvedTarget[this.property], target: resolvedTarget, optional: this.optional };
1507
+ });
1073
1508
  }
1074
1509
  getIdentifierPath() {
1075
1510
  const targetPath = this.getTargetIdentifierPath();
@@ -1145,25 +1580,39 @@ var CallExpression = class extends BaseNode {
1145
1580
  this.callee = callee;
1146
1581
  this.args = args;
1147
1582
  }
1148
- async evaluate(context) {
1149
- const resolved = await this.resolveCallee(context);
1150
- const fn = resolved?.fn ?? await this.callee.evaluate(context);
1151
- if (typeof fn !== "function") {
1152
- return void 0;
1153
- }
1154
- const values = [];
1155
- for (const arg of this.args) {
1156
- values.push(await arg.evaluate(context));
1157
- }
1158
- return fn.apply(resolved?.thisArg, values);
1583
+ evaluate(context) {
1584
+ const resolved = this.resolveCallee(context);
1585
+ return resolveMaybe(resolved, (resolvedCallee) => {
1586
+ const fnValue = resolvedCallee?.fn ?? this.callee.evaluate(context);
1587
+ return resolveMaybe(fnValue, (resolvedFn) => {
1588
+ if (typeof resolvedFn !== "function") {
1589
+ return void 0;
1590
+ }
1591
+ const values = [];
1592
+ const evalArgs = (index) => {
1593
+ for (let i = index; i < this.args.length; i += 1) {
1594
+ const arg = this.args[i];
1595
+ const argValue = arg.evaluate(context);
1596
+ return resolveMaybe(argValue, (resolvedArg) => {
1597
+ values.push(resolvedArg);
1598
+ return evalArgs(i + 1);
1599
+ });
1600
+ }
1601
+ return resolvedFn.apply(resolvedCallee?.thisArg, values);
1602
+ };
1603
+ return evalArgs(0);
1604
+ });
1605
+ });
1159
1606
  }
1160
- async resolveCallee(context) {
1607
+ resolveCallee(context) {
1161
1608
  if (this.callee instanceof MemberExpression) {
1162
- const resolved = await this.callee.resolve(context);
1163
- if (!resolved) {
1164
- return void 0;
1165
- }
1166
- return { fn: resolved.value, thisArg: resolved.target };
1609
+ const resolved = this.callee.resolve(context);
1610
+ return resolveMaybe(resolved, (resolvedValue) => {
1611
+ if (!resolvedValue) {
1612
+ return void 0;
1613
+ }
1614
+ return { fn: resolvedValue.value, thisArg: resolvedValue.target };
1615
+ });
1167
1616
  }
1168
1617
  if (!(this.callee instanceof IdentifierExpression)) {
1169
1618
  return void 0;
@@ -1205,27 +1654,40 @@ var ArrayExpression = class extends BaseNode {
1205
1654
  super("ArrayExpression");
1206
1655
  this.elements = elements;
1207
1656
  }
1208
- async evaluate(context) {
1657
+ evaluate(context) {
1209
1658
  const values = [];
1210
- for (const element of this.elements) {
1211
- if (element instanceof SpreadElement) {
1212
- const spreadValue = await element.value.evaluate(context);
1213
- if (spreadValue == null) {
1214
- continue;
1659
+ const pushElements = (value) => {
1660
+ if (value == null) {
1661
+ return;
1662
+ }
1663
+ const iterator = value[Symbol.iterator];
1664
+ if (typeof iterator === "function") {
1665
+ for (const entry of value) {
1666
+ values.push(entry);
1215
1667
  }
1216
- const iterator = spreadValue[Symbol.iterator];
1217
- if (typeof iterator === "function") {
1218
- for (const entry of spreadValue) {
1219
- values.push(entry);
1220
- }
1221
- } else {
1222
- values.push(spreadValue);
1668
+ } else {
1669
+ values.push(value);
1670
+ }
1671
+ };
1672
+ const evalAt = (index) => {
1673
+ for (let i = index; i < this.elements.length; i += 1) {
1674
+ const element = this.elements[i];
1675
+ if (element instanceof SpreadElement) {
1676
+ const spreadValue = element.value.evaluate(context);
1677
+ return resolveMaybe(spreadValue, (resolvedSpread) => {
1678
+ pushElements(resolvedSpread);
1679
+ return evalAt(i + 1);
1680
+ });
1223
1681
  }
1224
- continue;
1682
+ const value = element.evaluate(context);
1683
+ return resolveMaybe(value, (resolvedValue) => {
1684
+ values.push(resolvedValue);
1685
+ return evalAt(i + 1);
1686
+ });
1225
1687
  }
1226
- values.push(await element.evaluate(context));
1227
- }
1228
- return values;
1688
+ return values;
1689
+ };
1690
+ return evalAt(0);
1229
1691
  }
1230
1692
  };
1231
1693
  var ObjectExpression = class extends BaseNode {
@@ -1233,24 +1695,39 @@ var ObjectExpression = class extends BaseNode {
1233
1695
  super("ObjectExpression");
1234
1696
  this.entries = entries;
1235
1697
  }
1236
- async evaluate(context) {
1698
+ evaluate(context) {
1237
1699
  const result = {};
1238
- for (const entry of this.entries) {
1239
- if ("spread" in entry) {
1240
- const spreadValue = await entry.spread.evaluate(context);
1241
- if (spreadValue != null) {
1242
- Object.assign(result, spreadValue);
1700
+ const evalAt = (index) => {
1701
+ for (let i = index; i < this.entries.length; i += 1) {
1702
+ const entry = this.entries[i];
1703
+ if ("spread" in entry) {
1704
+ const spreadValue = entry.spread.evaluate(context);
1705
+ return resolveMaybe(spreadValue, (resolvedSpread) => {
1706
+ if (resolvedSpread != null) {
1707
+ Object.assign(result, resolvedSpread);
1708
+ }
1709
+ return evalAt(i + 1);
1710
+ });
1243
1711
  }
1244
- continue;
1245
- }
1246
- if ("computed" in entry && entry.computed) {
1247
- const keyValue = await entry.keyExpr.evaluate(context);
1248
- result[String(keyValue)] = await entry.value.evaluate(context);
1249
- } else {
1250
- result[entry.key] = await entry.value.evaluate(context);
1712
+ if ("computed" in entry && entry.computed) {
1713
+ const keyValue = entry.keyExpr.evaluate(context);
1714
+ return resolveMaybe(keyValue, (resolvedKey) => {
1715
+ const entryValue = entry.value.evaluate(context);
1716
+ return resolveMaybe(entryValue, (resolvedValue) => {
1717
+ result[String(resolvedKey)] = resolvedValue;
1718
+ return evalAt(i + 1);
1719
+ });
1720
+ });
1721
+ }
1722
+ const value = entry.value.evaluate(context);
1723
+ return resolveMaybe(value, (resolvedValue) => {
1724
+ result[entry.key] = resolvedValue;
1725
+ return evalAt(i + 1);
1726
+ });
1251
1727
  }
1252
- }
1253
- return result;
1728
+ return result;
1729
+ };
1730
+ return evalAt(0);
1254
1731
  }
1255
1732
  };
1256
1733
  var IndexExpression = class extends BaseNode {
@@ -1259,16 +1736,30 @@ var IndexExpression = class extends BaseNode {
1259
1736
  this.target = target;
1260
1737
  this.index = index;
1261
1738
  }
1262
- async evaluate(context) {
1263
- const target = await this.target.evaluate(context);
1264
- if (target == null) {
1265
- return void 0;
1266
- }
1267
- const index = await this.index.evaluate(context);
1268
- if (index == null) {
1269
- return void 0;
1739
+ evaluate(context) {
1740
+ const target = this.target.evaluate(context);
1741
+ return resolveMaybe(target, (resolvedTarget) => {
1742
+ if (resolvedTarget == null) {
1743
+ return void 0;
1744
+ }
1745
+ const index = this.index.evaluate(context);
1746
+ return resolveMaybe(index, (resolvedIndex) => {
1747
+ if (resolvedIndex == null) {
1748
+ return void 0;
1749
+ }
1750
+ const key = this.normalizeIndexKey(resolvedTarget, resolvedIndex);
1751
+ return resolvedTarget[key];
1752
+ });
1753
+ });
1754
+ }
1755
+ normalizeIndexKey(target, index) {
1756
+ if (Array.isArray(target) && typeof index === "string" && index.trim() !== "") {
1757
+ const numeric = Number(index);
1758
+ if (!Number.isNaN(numeric)) {
1759
+ return numeric;
1760
+ }
1270
1761
  }
1271
- return target[index];
1762
+ return index;
1272
1763
  }
1273
1764
  };
1274
1765
  var DirectiveExpression = class extends BaseNode {
@@ -1277,7 +1768,7 @@ var DirectiveExpression = class extends BaseNode {
1277
1768
  this.kind = kind;
1278
1769
  this.name = name;
1279
1770
  }
1280
- async evaluate(context) {
1771
+ evaluate(context) {
1281
1772
  const element = context.element;
1282
1773
  if (!element) {
1283
1774
  return `${this.kind}:${this.name}`;
@@ -1291,6 +1782,12 @@ var DirectiveExpression = class extends BaseNode {
1291
1782
  return element.value;
1292
1783
  }
1293
1784
  }
1785
+ if (this.name === "text" && element instanceof HTMLElement) {
1786
+ return element.innerText;
1787
+ }
1788
+ if (this.name === "content" && element instanceof HTMLElement) {
1789
+ return element.textContent ?? "";
1790
+ }
1294
1791
  if (this.name === "checked" && element instanceof HTMLInputElement) {
1295
1792
  return element.checked;
1296
1793
  }
@@ -1310,9 +1807,9 @@ var AwaitExpression = class extends BaseNode {
1310
1807
  super("AwaitExpression");
1311
1808
  this.argument = argument;
1312
1809
  }
1313
- async evaluate(context) {
1314
- const value = await this.argument.evaluate(context);
1315
- return await value;
1810
+ evaluate(context) {
1811
+ const value = this.argument.evaluate(context);
1812
+ return Promise.resolve(value);
1316
1813
  }
1317
1814
  };
1318
1815
  var QueryExpression = class extends BaseNode {
@@ -1321,7 +1818,7 @@ var QueryExpression = class extends BaseNode {
1321
1818
  this.direction = direction;
1322
1819
  this.selector = selector;
1323
1820
  }
1324
- async evaluate(context) {
1821
+ evaluate(context) {
1325
1822
  const selector = this.selector.trim();
1326
1823
  if (!selector) {
1327
1824
  return [];
@@ -1387,6 +1884,9 @@ var TokenStream = class {
1387
1884
  let count = 0;
1388
1885
  for (let i = this.index; i < this.tokens.length; i++) {
1389
1886
  const token = this.tokens[i];
1887
+ if (!token) {
1888
+ continue;
1889
+ }
1390
1890
  if (token.type === "Whitespace" /* Whitespace */) {
1391
1891
  continue;
1392
1892
  }
@@ -1397,6 +1897,29 @@ var TokenStream = class {
1397
1897
  }
1398
1898
  return null;
1399
1899
  }
1900
+ indexAfterDelimited(openType, closeType, offset = 0) {
1901
+ const first = this.peekNonWhitespace(offset);
1902
+ if (!first || first.type !== openType) {
1903
+ return null;
1904
+ }
1905
+ let index = offset + 1;
1906
+ let depth = 1;
1907
+ while (true) {
1908
+ const token = this.peekNonWhitespace(index);
1909
+ if (!token) {
1910
+ return null;
1911
+ }
1912
+ if (token.type === openType) {
1913
+ depth += 1;
1914
+ } else if (token.type === closeType) {
1915
+ depth -= 1;
1916
+ if (depth === 0) {
1917
+ return index + 1;
1918
+ }
1919
+ }
1920
+ index += 1;
1921
+ }
1922
+ }
1400
1923
  };
1401
1924
 
1402
1925
  // src/parser/parser.ts
@@ -1404,11 +1927,14 @@ var Parser = class _Parser {
1404
1927
  stream;
1405
1928
  source;
1406
1929
  customFlags;
1930
+ behaviorFlags;
1407
1931
  allowImplicitSemicolon = false;
1408
1932
  awaitStack = [];
1933
+ functionDepth = 0;
1409
1934
  constructor(input, options) {
1410
1935
  this.source = input;
1411
- this.customFlags = options?.customFlags ?? /* @__PURE__ */ new Set();
1936
+ this.customFlags = options?.customFlags ?? /* @__PURE__ */ new Set(["important", "trusted", "debounce"]);
1937
+ this.behaviorFlags = options?.behaviorFlags ?? /* @__PURE__ */ new Set();
1412
1938
  const lexer = new Lexer(input);
1413
1939
  this.stream = new TokenStream(lexer.tokenize());
1414
1940
  }
@@ -1448,8 +1974,9 @@ var Parser = class _Parser {
1448
1974
  this.stream.skipWhitespace();
1449
1975
  this.stream.expect("Behavior" /* Behavior */);
1450
1976
  const selector = this.parseSelector();
1977
+ const { flags, flagArgs } = this.parseBehaviorFlags();
1451
1978
  const body = this.parseBlock({ allowDeclarations: true });
1452
- return new BehaviorNode(selector, body);
1979
+ return new BehaviorNode(selector, body, flags, flagArgs);
1453
1980
  });
1454
1981
  }
1455
1982
  parseSelector() {
@@ -1463,6 +1990,9 @@ var Parser = class _Parser {
1463
1990
  if (token.type === "LBrace" /* LBrace */) {
1464
1991
  break;
1465
1992
  }
1993
+ if (token.type === "Bang" /* Bang */) {
1994
+ break;
1995
+ }
1466
1996
  if (token.type === "Whitespace" /* Whitespace */) {
1467
1997
  this.stream.next();
1468
1998
  if (sawNonWhitespace && selectorText[selectorText.length - 1] !== " ") {
@@ -1478,6 +2008,10 @@ var Parser = class _Parser {
1478
2008
  }
1479
2009
  return new SelectorNode(selectorText.trim());
1480
2010
  }
2011
+ parseBehaviorFlags() {
2012
+ const result = this.parseFlags(this.behaviorFlags, "behavior modifier");
2013
+ return { flags: result.flags, flagArgs: result.flagArgs };
2014
+ }
1481
2015
  parseUseStatement() {
1482
2016
  return this.wrapErrors(() => {
1483
2017
  this.stream.expect("Use" /* Use */);
@@ -1561,6 +2095,7 @@ ${caret}`;
1561
2095
  }
1562
2096
  parseBlock(options) {
1563
2097
  const allowDeclarations = options?.allowDeclarations ?? false;
2098
+ const allowReturn = options?.allowReturn ?? this.functionDepth > 0;
1564
2099
  this.stream.skipWhitespace();
1565
2100
  this.stream.expect("LBrace" /* LBrace */);
1566
2101
  const statements = [];
@@ -1622,7 +2157,7 @@ ${caret}`;
1622
2157
  }
1623
2158
  sawConstruct = true;
1624
2159
  }
1625
- statements.push(this.parseStatement());
2160
+ statements.push(this.parseStatement({ allowReturn }));
1626
2161
  }
1627
2162
  }
1628
2163
  return new BlockNode(statements);
@@ -1641,6 +2176,15 @@ ${caret}`;
1641
2176
  }
1642
2177
  return this.parseReturnStatement();
1643
2178
  }
2179
+ if (next.type === "Assert" /* Assert */) {
2180
+ return this.parseAssertStatement();
2181
+ }
2182
+ if (next.type === "Break" /* Break */) {
2183
+ return this.parseBreakStatement();
2184
+ }
2185
+ if (next.type === "Continue" /* Continue */) {
2186
+ return this.parseContinueStatement();
2187
+ }
1644
2188
  if (allowBlocks && next.type === "On" /* On */) {
1645
2189
  return this.parseOnBlock();
1646
2190
  }
@@ -1676,44 +2220,6 @@ ${caret}`;
1676
2220
  }
1677
2221
  throw new Error(`Unexpected token ${next.type}`);
1678
2222
  }
1679
- parseStateBlock() {
1680
- this.stream.expect("State" /* State */);
1681
- this.stream.skipWhitespace();
1682
- this.stream.expect("LBrace" /* LBrace */);
1683
- const entries = [];
1684
- while (true) {
1685
- this.stream.skipWhitespace();
1686
- const next = this.stream.peek();
1687
- if (!next) {
1688
- throw new Error("Unterminated state block");
1689
- }
1690
- if (next.type === "RBrace" /* RBrace */) {
1691
- this.stream.next();
1692
- break;
1693
- }
1694
- const nameToken = this.stream.expect("Identifier" /* Identifier */);
1695
- this.stream.skipWhitespace();
1696
- this.stream.expect("Colon" /* Colon */);
1697
- this.stream.skipWhitespace();
1698
- const value = this.parseExpression();
1699
- this.stream.skipWhitespace();
1700
- let important = false;
1701
- if (this.stream.peek()?.type === "Bang" /* Bang */) {
1702
- this.stream.next();
1703
- this.stream.skipWhitespace();
1704
- const importantToken = this.stream.next();
1705
- if (importantToken.type === "Identifier" /* Identifier */ && importantToken.value === "important") {
1706
- important = true;
1707
- } else {
1708
- throw new Error("Expected 'important' after '!'");
1709
- }
1710
- }
1711
- this.stream.skipWhitespace();
1712
- this.stream.expect("Semicolon" /* Semicolon */);
1713
- entries.push(new StateEntryNode(nameToken.value, value, important));
1714
- }
1715
- return new StateBlockNode(entries);
1716
- }
1717
2223
  parseOnBlock() {
1718
2224
  this.stream.expect("On" /* On */);
1719
2225
  this.stream.skipWhitespace();
@@ -1741,22 +2247,9 @@ ${caret}`;
1741
2247
  }
1742
2248
  throw new Error(`Unexpected token in on() args: ${next.type}`);
1743
2249
  }
1744
- const modifiers = this.parseOnModifiers();
2250
+ const { flags, flagArgs } = this.parseFlags(this.customFlags, "flag");
1745
2251
  const body = this.parseBlock({ allowDeclarations: false });
1746
- return new OnBlockNode(event, args, body, modifiers);
1747
- }
1748
- parseOnModifiers() {
1749
- const modifiers = [];
1750
- while (true) {
1751
- this.stream.skipWhitespace();
1752
- if (this.stream.peek()?.type !== "Bang" /* Bang */) {
1753
- break;
1754
- }
1755
- this.stream.next();
1756
- const name = this.stream.expect("Identifier" /* Identifier */).value;
1757
- modifiers.push(name);
1758
- }
1759
- return modifiers;
2252
+ return new OnBlockNode(event, args, body, flags, flagArgs);
1760
2253
  }
1761
2254
  parseAssignment() {
1762
2255
  const target = this.parseAssignmentTarget();
@@ -1966,6 +2459,11 @@ ${caret}`;
1966
2459
  if (!token) {
1967
2460
  throw new Error("Expected expression");
1968
2461
  }
2462
+ if (token.type === "PlusPlus" /* PlusPlus */ || token.type === "MinusMinus" /* MinusMinus */) {
2463
+ this.stream.next();
2464
+ const argument = this.parseUnaryExpression();
2465
+ return this.createIncrementNode(token, argument, true);
2466
+ }
1969
2467
  if (token.type === "Bang" /* Bang */) {
1970
2468
  this.stream.next();
1971
2469
  const argument = this.parseUnaryExpression();
@@ -1981,7 +2479,31 @@ ${caret}`;
1981
2479
  const argument = this.parseUnaryExpression();
1982
2480
  return new AwaitExpression(argument);
1983
2481
  }
1984
- return this.parseCallExpression();
2482
+ return this.parsePostfixExpression();
2483
+ }
2484
+ parsePostfixExpression() {
2485
+ let expr = this.parseCallExpression();
2486
+ while (true) {
2487
+ this.stream.skipWhitespace();
2488
+ const token = this.stream.peek();
2489
+ if (!token) {
2490
+ break;
2491
+ }
2492
+ if (token.type === "PlusPlus" /* PlusPlus */ || token.type === "MinusMinus" /* MinusMinus */) {
2493
+ this.stream.next();
2494
+ expr = this.createIncrementNode(token, expr, false);
2495
+ continue;
2496
+ }
2497
+ break;
2498
+ }
2499
+ return expr;
2500
+ }
2501
+ createIncrementNode(token, argument, prefix) {
2502
+ if (!(argument instanceof IdentifierExpression) && !(argument instanceof MemberExpression) && !(argument instanceof IndexExpression) && !(argument instanceof DirectiveExpression)) {
2503
+ throw new Error("Increment/decrement requires a mutable target");
2504
+ }
2505
+ const operator = token.type === "PlusPlus" /* PlusPlus */ ? "++" : "--";
2506
+ return new AssignmentNode(argument, new LiteralExpression(1), operator, prefix);
1985
2507
  }
1986
2508
  parseCallExpression() {
1987
2509
  let expr = this.parsePrimaryExpression();
@@ -2293,6 +2815,7 @@ ${caret}`;
2293
2815
  this.stream.expect("LBrace" /* LBrace */);
2294
2816
  const statements = [];
2295
2817
  this.awaitStack.push(allowAwait);
2818
+ this.functionDepth += 1;
2296
2819
  try {
2297
2820
  while (true) {
2298
2821
  this.stream.skipWhitespace();
@@ -2304,9 +2827,10 @@ ${caret}`;
2304
2827
  this.stream.next();
2305
2828
  break;
2306
2829
  }
2307
- statements.push(this.parseStatement({ allowBlocks: false, allowReturn: true }));
2830
+ statements.push(this.parseStatement({ allowBlocks: true, allowReturn: true }));
2308
2831
  }
2309
2832
  } finally {
2833
+ this.functionDepth -= 1;
2310
2834
  this.awaitStack.pop();
2311
2835
  }
2312
2836
  return new BlockNode(statements);
@@ -2347,7 +2871,14 @@ ${caret}`;
2347
2871
  return this.parseObjectPattern();
2348
2872
  }
2349
2873
  if (token.type === "Identifier" /* Identifier */) {
2350
- return new IdentifierExpression(this.parseIdentifierPath());
2874
+ const expr = this.parseCallExpression();
2875
+ if (expr instanceof CallExpression) {
2876
+ throw new Error("Invalid assignment target CallExpression");
2877
+ }
2878
+ if (expr instanceof IdentifierExpression || expr instanceof MemberExpression || expr instanceof IndexExpression) {
2879
+ return expr;
2880
+ }
2881
+ throw new Error("Invalid assignment target");
2351
2882
  }
2352
2883
  throw new Error(`Invalid assignment target ${token.type}`);
2353
2884
  }
@@ -2495,7 +3026,7 @@ ${caret}`;
2495
3026
  const operator = this.parseDeclarationOperator();
2496
3027
  this.stream.skipWhitespace();
2497
3028
  const value = this.parseExpression();
2498
- const { flags, flagArgs } = this.parseFlags();
3029
+ const { flags, flagArgs } = this.parseFlags(this.customFlags, "flag");
2499
3030
  this.stream.skipWhitespace();
2500
3031
  this.stream.expect("Semicolon" /* Semicolon */);
2501
3032
  return new DeclarationNode(target, operator, value, flags, flagArgs);
@@ -2536,7 +3067,7 @@ ${caret}`;
2536
3067
  }
2537
3068
  return ":";
2538
3069
  }
2539
- parseFlags() {
3070
+ parseFlags(allowed, errorLabel) {
2540
3071
  const flags = {};
2541
3072
  const flagArgs = {};
2542
3073
  while (true) {
@@ -2546,59 +3077,107 @@ ${caret}`;
2546
3077
  }
2547
3078
  this.stream.next();
2548
3079
  const name = this.stream.expect("Identifier" /* Identifier */).value;
2549
- if (name === "important") {
2550
- flags.important = true;
2551
- } else if (name === "trusted") {
2552
- flags.trusted = true;
2553
- } else if (name === "debounce") {
2554
- flags.debounce = true;
2555
- if (this.stream.peek()?.type === "LParen" /* LParen */) {
2556
- this.stream.next();
2557
- this.stream.skipWhitespace();
2558
- const numberToken = this.stream.expect("Number" /* Number */);
2559
- flagArgs.debounce = Number(numberToken.value);
2560
- this.stream.skipWhitespace();
2561
- this.stream.expect("RParen" /* RParen */);
2562
- } else {
2563
- flagArgs.debounce = 200;
2564
- }
2565
- } else if (this.customFlags.has(name)) {
2566
- flags[name] = true;
2567
- const customArg = this.parseCustomFlagArg();
2568
- if (customArg !== void 0) {
2569
- flagArgs[name] = customArg;
2570
- }
3080
+ if (allowed && !allowed.has(name)) {
3081
+ throw new Error(`Unknown ${errorLabel} ${name}`);
3082
+ }
3083
+ flags[name] = true;
3084
+ const customArg = this.parseCustomFlagArg();
3085
+ if (customArg !== void 0) {
3086
+ flagArgs[name] = customArg;
3087
+ }
3088
+ }
3089
+ return { flags, flagArgs };
3090
+ }
3091
+ parseCustomFlagArg() {
3092
+ if (this.stream.peek()?.type !== "LParen" /* LParen */) {
3093
+ return void 0;
3094
+ }
3095
+ this.stream.next();
3096
+ this.stream.skipWhitespace();
3097
+ const token = this.stream.peek();
3098
+ if (!token) {
3099
+ throw new Error("Unterminated flag arguments");
3100
+ }
3101
+ const value = this.parseCustomFlagLiteral();
3102
+ this.stream.skipWhitespace();
3103
+ this.stream.expect("RParen" /* RParen */);
3104
+ return value;
3105
+ }
3106
+ parseCustomFlagLiteral() {
3107
+ const token = this.stream.peek();
3108
+ if (!token) {
3109
+ throw new Error("Unterminated flag arguments");
3110
+ }
3111
+ if (token.type === "Number" /* Number */) {
3112
+ return Number(this.stream.next().value);
3113
+ }
3114
+ if (token.type === "String" /* String */) {
3115
+ return this.stream.next().value;
3116
+ }
3117
+ if (token.type === "Boolean" /* Boolean */) {
3118
+ return this.stream.next().value === "true";
3119
+ }
3120
+ if (token.type === "Identifier" /* Identifier */) {
3121
+ return this.stream.next().value;
3122
+ }
3123
+ if (token.type === "LBracket" /* LBracket */) {
3124
+ return this.parseCustomFlagArray();
3125
+ }
3126
+ if (token.type === "LBrace" /* LBrace */) {
3127
+ return this.parseCustomFlagObject();
3128
+ }
3129
+ throw new Error(`Unsupported flag argument ${token.type}`);
3130
+ }
3131
+ parseCustomFlagArray() {
3132
+ this.stream.expect("LBracket" /* LBracket */);
3133
+ const items = [];
3134
+ while (true) {
3135
+ this.stream.skipWhitespace();
3136
+ const next = this.stream.peek();
3137
+ if (!next) {
3138
+ throw new Error("Unterminated flag array");
3139
+ }
3140
+ if (next.type === "RBracket" /* RBracket */) {
3141
+ this.stream.next();
3142
+ break;
3143
+ }
3144
+ items.push(this.parseCustomFlagLiteral());
3145
+ this.stream.skipWhitespace();
3146
+ if (this.stream.peek()?.type === "Comma" /* Comma */) {
3147
+ this.stream.next();
3148
+ }
3149
+ }
3150
+ return items;
3151
+ }
3152
+ parseCustomFlagObject() {
3153
+ this.stream.expect("LBrace" /* LBrace */);
3154
+ const obj = {};
3155
+ while (true) {
3156
+ this.stream.skipWhitespace();
3157
+ const next = this.stream.peek();
3158
+ if (!next) {
3159
+ throw new Error("Unterminated flag object");
3160
+ }
3161
+ if (next.type === "RBrace" /* RBrace */) {
3162
+ this.stream.next();
3163
+ break;
3164
+ }
3165
+ let key;
3166
+ if (next.type === "Identifier" /* Identifier */ || next.type === "String" /* String */) {
3167
+ key = this.stream.next().value;
2571
3168
  } else {
2572
- throw new Error(`Unknown flag ${name}`);
3169
+ throw new Error(`Unsupported flag object key ${next.type}`);
3170
+ }
3171
+ this.stream.skipWhitespace();
3172
+ this.stream.expect("Colon" /* Colon */);
3173
+ this.stream.skipWhitespace();
3174
+ obj[key] = this.parseCustomFlagLiteral();
3175
+ this.stream.skipWhitespace();
3176
+ if (this.stream.peek()?.type === "Comma" /* Comma */) {
3177
+ this.stream.next();
2573
3178
  }
2574
3179
  }
2575
- return { flags, flagArgs };
2576
- }
2577
- parseCustomFlagArg() {
2578
- if (this.stream.peek()?.type !== "LParen" /* LParen */) {
2579
- return void 0;
2580
- }
2581
- this.stream.next();
2582
- this.stream.skipWhitespace();
2583
- const token = this.stream.peek();
2584
- if (!token) {
2585
- throw new Error("Unterminated flag arguments");
2586
- }
2587
- let value;
2588
- if (token.type === "Number" /* Number */) {
2589
- value = Number(this.stream.next().value);
2590
- } else if (token.type === "String" /* String */) {
2591
- value = this.stream.next().value;
2592
- } else if (token.type === "Boolean" /* Boolean */) {
2593
- value = this.stream.next().value === "true";
2594
- } else if (token.type === "Identifier" /* Identifier */) {
2595
- value = this.stream.next().value;
2596
- } else {
2597
- throw new Error(`Unsupported flag argument ${token.type}`);
2598
- }
2599
- this.stream.skipWhitespace();
2600
- this.stream.expect("RParen" /* RParen */);
2601
- return value;
3180
+ return obj;
2602
3181
  }
2603
3182
  isDeclarationStart() {
2604
3183
  const first = this.stream.peekNonWhitespace(0);
@@ -2623,14 +3202,29 @@ ${caret}`;
2623
3202
  }
2624
3203
  if (first.type === "Identifier" /* Identifier */) {
2625
3204
  let index = 1;
2626
- while (this.stream.peekNonWhitespace(index)?.type === "Dot" /* Dot */ && this.stream.peekNonWhitespace(index + 1)?.type === "Identifier" /* Identifier */) {
2627
- index += 2;
3205
+ while (true) {
3206
+ const token = this.stream.peekNonWhitespace(index);
3207
+ if (!token) {
3208
+ return false;
3209
+ }
3210
+ if (token.type === "Dot" /* Dot */ && this.stream.peekNonWhitespace(index + 1)?.type === "Identifier" /* Identifier */) {
3211
+ index += 2;
3212
+ continue;
3213
+ }
3214
+ if (token.type === "LBracket" /* LBracket */) {
3215
+ const indexAfter = this.stream.indexAfterDelimited("LBracket" /* LBracket */, "RBracket" /* RBracket */, index);
3216
+ if (indexAfter === null) {
3217
+ return false;
3218
+ }
3219
+ index = indexAfter;
3220
+ continue;
3221
+ }
3222
+ break;
2628
3223
  }
2629
3224
  return this.isAssignmentOperatorStart(index);
2630
3225
  }
2631
3226
  if (first.type === "At" /* At */ || first.type === "Dollar" /* Dollar */) {
2632
3227
  const second = this.stream.peekNonWhitespace(1);
2633
- const third = this.stream.peekNonWhitespace(2);
2634
3228
  return second?.type === "Identifier" /* Identifier */ && this.isAssignmentOperatorStart(2);
2635
3229
  }
2636
3230
  if (first.type === "LBrace" /* LBrace */ || first.type === "LBracket" /* LBracket */) {
@@ -2668,17 +3262,6 @@ ${caret}`;
2668
3262
  }
2669
3263
  return false;
2670
3264
  }
2671
- isCallStart() {
2672
- const first = this.stream.peekNonWhitespace(0);
2673
- if (!first || first.type !== "Identifier" /* Identifier */) {
2674
- return false;
2675
- }
2676
- let index = 1;
2677
- while (this.stream.peekNonWhitespace(index)?.type === "Dot" /* Dot */ && this.stream.peekNonWhitespace(index + 1)?.type === "Identifier" /* Identifier */) {
2678
- index += 2;
2679
- }
2680
- return this.stream.peekNonWhitespace(index)?.type === "LParen" /* LParen */;
2681
- }
2682
3265
  isExpressionStatementStart() {
2683
3266
  const first = this.stream.peekNonWhitespace(0);
2684
3267
  if (!first) {
@@ -2708,50 +3291,22 @@ ${caret}`;
2708
3291
  if (this.stream.peekNonWhitespace(index)?.type !== "LParen" /* LParen */) {
2709
3292
  return false;
2710
3293
  }
2711
- index += 1;
2712
- let depth = 1;
2713
- while (true) {
2714
- const token = this.stream.peekNonWhitespace(index);
2715
- if (!token) {
2716
- return false;
2717
- }
2718
- if (token.type === "LParen" /* LParen */) {
2719
- depth += 1;
2720
- } else if (token.type === "RParen" /* RParen */) {
2721
- depth -= 1;
2722
- if (depth === 0) {
2723
- index += 1;
2724
- break;
2725
- }
2726
- }
2727
- index += 1;
3294
+ const indexAfterParams = this.stream.indexAfterDelimited("LParen" /* LParen */, "RParen" /* RParen */, index);
3295
+ if (indexAfterParams === null) {
3296
+ return false;
2728
3297
  }
2729
- return this.stream.peekNonWhitespace(index)?.type === "LBrace" /* LBrace */;
3298
+ return this.stream.peekNonWhitespace(indexAfterParams)?.type === "LBrace" /* LBrace */;
2730
3299
  }
2731
3300
  isArrowFunctionStart() {
2732
3301
  const first = this.stream.peekNonWhitespace(0);
2733
3302
  if (!first || first.type !== "LParen" /* LParen */) {
2734
3303
  return false;
2735
3304
  }
2736
- let index = 1;
2737
- let depth = 1;
2738
- while (true) {
2739
- const token = this.stream.peekNonWhitespace(index);
2740
- if (!token) {
2741
- return false;
2742
- }
2743
- if (token.type === "LParen" /* LParen */) {
2744
- depth += 1;
2745
- } else if (token.type === "RParen" /* RParen */) {
2746
- depth -= 1;
2747
- if (depth === 0) {
2748
- index += 1;
2749
- break;
2750
- }
2751
- }
2752
- index += 1;
3305
+ const indexAfterParams = this.stream.indexAfterDelimited("LParen" /* LParen */, "RParen" /* RParen */, 0);
3306
+ if (indexAfterParams === null) {
3307
+ return false;
2753
3308
  }
2754
- return this.stream.peekNonWhitespace(index)?.type === "Arrow" /* Arrow */;
3309
+ return this.stream.peekNonWhitespace(indexAfterParams)?.type === "Arrow" /* Arrow */;
2755
3310
  }
2756
3311
  isAsyncArrowFunctionStart() {
2757
3312
  const first = this.stream.peekNonWhitespace(0);
@@ -2761,25 +3316,11 @@ ${caret}`;
2761
3316
  if (this.stream.peekNonWhitespace(1)?.type !== "LParen" /* LParen */) {
2762
3317
  return false;
2763
3318
  }
2764
- let index = 2;
2765
- let depth = 1;
2766
- while (true) {
2767
- const token = this.stream.peekNonWhitespace(index);
2768
- if (!token) {
2769
- return false;
2770
- }
2771
- if (token.type === "LParen" /* LParen */) {
2772
- depth += 1;
2773
- } else if (token.type === "RParen" /* RParen */) {
2774
- depth -= 1;
2775
- if (depth === 0) {
2776
- index += 1;
2777
- break;
2778
- }
2779
- }
2780
- index += 1;
3319
+ const indexAfterParams = this.stream.indexAfterDelimited("LParen" /* LParen */, "RParen" /* RParen */, 1);
3320
+ if (indexAfterParams === null) {
3321
+ return false;
2781
3322
  }
2782
- return this.stream.peekNonWhitespace(index)?.type === "Arrow" /* Arrow */;
3323
+ return this.stream.peekNonWhitespace(indexAfterParams)?.type === "Arrow" /* Arrow */;
2783
3324
  }
2784
3325
  isFunctionExpressionAssignmentStart() {
2785
3326
  const first = this.stream.peekNonWhitespace(0);
@@ -2796,25 +3337,11 @@ ${caret}`;
2796
3337
  if (this.stream.peekNonWhitespace(index)?.type !== "LParen" /* LParen */) {
2797
3338
  return false;
2798
3339
  }
2799
- index += 1;
2800
- let depth = 1;
2801
- while (true) {
2802
- const token = this.stream.peekNonWhitespace(index);
2803
- if (!token) {
2804
- return false;
2805
- }
2806
- if (token.type === "LParen" /* LParen */) {
2807
- depth += 1;
2808
- } else if (token.type === "RParen" /* RParen */) {
2809
- depth -= 1;
2810
- if (depth === 0) {
2811
- index += 1;
2812
- break;
2813
- }
2814
- }
2815
- index += 1;
3340
+ const indexAfterParams = this.stream.indexAfterDelimited("LParen" /* LParen */, "RParen" /* RParen */, index);
3341
+ if (indexAfterParams === null) {
3342
+ return false;
2816
3343
  }
2817
- return this.stream.peekNonWhitespace(index)?.type === "Arrow" /* Arrow */;
3344
+ return this.stream.peekNonWhitespace(indexAfterParams)?.type === "Arrow" /* Arrow */;
2818
3345
  }
2819
3346
  parseExpressionStatement() {
2820
3347
  const expr = this.parseExpression();
@@ -2829,7 +3356,7 @@ ${caret}`;
2829
3356
  const test = this.parseExpression();
2830
3357
  this.stream.skipWhitespace();
2831
3358
  this.stream.expect("RParen" /* RParen */);
2832
- const consequent = this.parseBlock({ allowDeclarations: false });
3359
+ const consequent = this.parseConditionalBody();
2833
3360
  this.stream.skipWhitespace();
2834
3361
  let alternate;
2835
3362
  if (this.stream.peek()?.type === "Else" /* Else */) {
@@ -2839,11 +3366,19 @@ ${caret}`;
2839
3366
  const nested = this.parseIfBlock();
2840
3367
  alternate = new BlockNode([nested]);
2841
3368
  } else {
2842
- alternate = this.parseBlock({ allowDeclarations: false });
3369
+ alternate = this.parseConditionalBody();
2843
3370
  }
2844
3371
  }
2845
3372
  return new IfNode(test, consequent, alternate);
2846
3373
  }
3374
+ parseConditionalBody() {
3375
+ this.stream.skipWhitespace();
3376
+ if (this.stream.peek()?.type === "LBrace" /* LBrace */) {
3377
+ return this.parseBlock({ allowDeclarations: false });
3378
+ }
3379
+ const statement = this.parseStatement({ allowBlocks: false, allowReturn: this.functionDepth > 0 });
3380
+ return new BlockNode([statement]);
3381
+ }
2847
3382
  parseWhileBlock() {
2848
3383
  this.stream.expect("While" /* While */);
2849
3384
  this.stream.skipWhitespace();
@@ -2860,6 +3395,21 @@ ${caret}`;
2860
3395
  this.stream.skipWhitespace();
2861
3396
  this.stream.expect("LParen" /* LParen */);
2862
3397
  this.stream.skipWhitespace();
3398
+ const eachKind = this.detectForEachKind();
3399
+ if (eachKind) {
3400
+ const target = this.parseForEachTarget();
3401
+ this.stream.skipWhitespace();
3402
+ const keyword = this.stream.expect("Identifier" /* Identifier */);
3403
+ if (keyword.value !== eachKind) {
3404
+ throw new Error(`Expected '${eachKind}' but got '${keyword.value}'`);
3405
+ }
3406
+ this.stream.skipWhitespace();
3407
+ const iterable = this.parseExpression();
3408
+ this.stream.skipWhitespace();
3409
+ this.stream.expect("RParen" /* RParen */);
3410
+ const body2 = this.parseBlock({ allowDeclarations: false });
3411
+ return new ForEachNode(target, iterable, eachKind, body2);
3412
+ }
2863
3413
  let init;
2864
3414
  if (this.stream.peek()?.type !== "Semicolon" /* Semicolon */) {
2865
3415
  init = this.parseForClause();
@@ -2883,6 +3433,43 @@ ${caret}`;
2883
3433
  const body = this.parseBlock({ allowDeclarations: false });
2884
3434
  return new ForNode(init, test, update, body);
2885
3435
  }
3436
+ detectForEachKind() {
3437
+ let offset = 0;
3438
+ let depth = 0;
3439
+ while (true) {
3440
+ const token = this.stream.peekNonWhitespace(offset);
3441
+ if (!token) {
3442
+ return null;
3443
+ }
3444
+ if (token.type === "LParen" /* LParen */ || token.type === "LBracket" /* LBracket */ || token.type === "LBrace" /* LBrace */) {
3445
+ depth += 1;
3446
+ } else if (token.type === "RParen" /* RParen */ || token.type === "RBracket" /* RBracket */ || token.type === "RBrace" /* RBrace */) {
3447
+ if (depth === 0) {
3448
+ return null;
3449
+ }
3450
+ depth -= 1;
3451
+ }
3452
+ if (depth === 0) {
3453
+ if (token.type === "Semicolon" /* Semicolon */) {
3454
+ return null;
3455
+ }
3456
+ if (token.type === "Identifier" /* Identifier */ && (token.value === "in" || token.value === "of")) {
3457
+ return token.value;
3458
+ }
3459
+ }
3460
+ offset += 1;
3461
+ }
3462
+ }
3463
+ parseForEachTarget() {
3464
+ const token = this.stream.peek();
3465
+ if (!token) {
3466
+ throw new Error("Expected for-each target");
3467
+ }
3468
+ if (token.type !== "Identifier" /* Identifier */) {
3469
+ throw new Error("for-in/of target must be an identifier");
3470
+ }
3471
+ return new IdentifierExpression(this.stream.next().value);
3472
+ }
2886
3473
  parseForClause() {
2887
3474
  if (this.isAssignmentStart()) {
2888
3475
  return this.parseAssignmentExpression();
@@ -2978,9 +3565,6 @@ ${caret}`;
2978
3565
  const body = this.parseFunctionBlockWithAwait(isAsync);
2979
3566
  return new FunctionDeclarationNode(name, params, body, isAsync);
2980
3567
  }
2981
- parseFunctionBlock() {
2982
- return this.parseFunctionBlockWithAwait(false);
2983
- }
2984
3568
  parseReturnStatement() {
2985
3569
  this.stream.expect("Return" /* Return */);
2986
3570
  this.stream.skipWhitespace();
@@ -2993,6 +3577,23 @@ ${caret}`;
2993
3577
  this.stream.expect("Semicolon" /* Semicolon */);
2994
3578
  return new ReturnNode(value);
2995
3579
  }
3580
+ parseAssertStatement() {
3581
+ this.stream.expect("Assert" /* Assert */);
3582
+ this.stream.skipWhitespace();
3583
+ const test = this.parseExpression();
3584
+ this.consumeStatementTerminator();
3585
+ return new AssertNode(test);
3586
+ }
3587
+ parseBreakStatement() {
3588
+ this.stream.expect("Break" /* Break */);
3589
+ this.consumeStatementTerminator();
3590
+ return new BreakNode();
3591
+ }
3592
+ parseContinueStatement() {
3593
+ this.stream.expect("Continue" /* Continue */);
3594
+ this.consumeStatementTerminator();
3595
+ return new ContinueNode();
3596
+ }
2996
3597
  parseArrowFunctionExpression(isAsync = false) {
2997
3598
  const params = this.parseFunctionParams();
2998
3599
  this.stream.skipWhitespace();
@@ -3102,6 +3703,7 @@ var Scope = class _Scope {
3102
3703
  root;
3103
3704
  listeners = /* @__PURE__ */ new Map();
3104
3705
  anyListeners = /* @__PURE__ */ new Set();
3706
+ isEachItem = false;
3105
3707
  createChild() {
3106
3708
  return new _Scope(this);
3107
3709
  }
@@ -3404,18 +4006,19 @@ var Engine = class _Engine {
3404
4006
  eachBindings = /* @__PURE__ */ new WeakMap();
3405
4007
  lifecycleBindings = /* @__PURE__ */ new WeakMap();
3406
4008
  behaviorRegistry = [];
4009
+ behaviorRegistryHashes = /* @__PURE__ */ new Set();
3407
4010
  behaviorBindings = /* @__PURE__ */ new WeakMap();
3408
4011
  behaviorListeners = /* @__PURE__ */ new WeakMap();
3409
4012
  behaviorId = 0;
3410
4013
  codeCache = /* @__PURE__ */ new Map();
3411
4014
  behaviorCache = /* @__PURE__ */ new Map();
3412
4015
  observer;
3413
- observerRoot;
3414
4016
  attributeHandlers = [];
3415
4017
  globals = {};
3416
4018
  importantFlags = /* @__PURE__ */ new WeakMap();
3417
4019
  inlineDeclarations = /* @__PURE__ */ new WeakMap();
3418
4020
  flagHandlers = /* @__PURE__ */ new Map();
4021
+ behaviorModifiers = /* @__PURE__ */ new Map();
3419
4022
  pendingAdded = /* @__PURE__ */ new Set();
3420
4023
  pendingRemoved = /* @__PURE__ */ new Set();
3421
4024
  pendingUpdated = /* @__PURE__ */ new Set();
@@ -3424,10 +4027,119 @@ var Engine = class _Engine {
3424
4027
  diagnostics;
3425
4028
  logger;
3426
4029
  pendingUses = [];
4030
+ pendingAutoBindToScope = [];
4031
+ scopeWatchers = /* @__PURE__ */ new WeakMap();
4032
+ executionStack = [];
4033
+ groupProxyCache = /* @__PURE__ */ new WeakMap();
3427
4034
  constructor(options = {}) {
3428
4035
  this.diagnostics = options.diagnostics ?? false;
3429
4036
  this.logger = options.logger ?? console;
3430
4037
  this.registerGlobal("console", console);
4038
+ this.registerFlag("important");
4039
+ this.registerFlag("trusted");
4040
+ this.registerFlag("debounce", {
4041
+ onEventBind: ({ args }) => ({
4042
+ debounceMs: typeof args === "number" ? args : 200
4043
+ })
4044
+ });
4045
+ this.registerFlag("prevent", {
4046
+ onEventBefore: ({ event }) => {
4047
+ event?.preventDefault();
4048
+ }
4049
+ });
4050
+ this.registerFlag("stop", {
4051
+ onEventBefore: ({ event }) => {
4052
+ event?.stopPropagation();
4053
+ }
4054
+ });
4055
+ this.registerFlag("self", {
4056
+ onEventBefore: ({ event, element }) => {
4057
+ const target = event?.target;
4058
+ if (!(target instanceof Node)) {
4059
+ return false;
4060
+ }
4061
+ return target === element;
4062
+ }
4063
+ });
4064
+ this.registerFlag("outside", {
4065
+ onEventBind: ({ element }) => ({ listenerTarget: element.ownerDocument }),
4066
+ onEventBefore: ({ event, element }) => {
4067
+ const target = event?.target;
4068
+ if (!(target instanceof Node)) {
4069
+ return false;
4070
+ }
4071
+ return !element.contains(target);
4072
+ }
4073
+ });
4074
+ this.registerFlag("once", {
4075
+ onEventBind: () => ({ options: { once: true } })
4076
+ });
4077
+ this.registerFlag("passive", {
4078
+ onEventBind: () => ({ options: { passive: true } })
4079
+ });
4080
+ this.registerFlag("capture", {
4081
+ onEventBind: () => ({ options: { capture: true } })
4082
+ });
4083
+ this.registerFlag("shift", {
4084
+ onEventBefore: ({ event }) => this.matchesKeyFlag(event, "shift")
4085
+ });
4086
+ this.registerFlag("ctrl", {
4087
+ onEventBefore: ({ event }) => this.matchesKeyFlag(event, "ctrl")
4088
+ });
4089
+ this.registerFlag("control", {
4090
+ onEventBefore: ({ event }) => this.matchesKeyFlag(event, "ctrl")
4091
+ });
4092
+ this.registerFlag("alt", {
4093
+ onEventBefore: ({ event }) => this.matchesKeyFlag(event, "alt")
4094
+ });
4095
+ this.registerFlag("meta", {
4096
+ onEventBefore: ({ event }) => this.matchesKeyFlag(event, "meta")
4097
+ });
4098
+ this.registerFlag("enter", {
4099
+ onEventBefore: ({ event }) => this.matchesKeyFlag(event, "enter")
4100
+ });
4101
+ this.registerFlag("escape", {
4102
+ onEventBefore: ({ event }) => this.matchesKeyFlag(event, "escape")
4103
+ });
4104
+ this.registerFlag("esc", {
4105
+ onEventBefore: ({ event }) => this.matchesKeyFlag(event, "escape")
4106
+ });
4107
+ this.registerFlag("tab", {
4108
+ onEventBefore: ({ event }) => this.matchesKeyFlag(event, "tab")
4109
+ });
4110
+ this.registerFlag("space", {
4111
+ onEventBefore: ({ event }) => this.matchesKeyFlag(event, "space")
4112
+ });
4113
+ this.registerFlag("up", {
4114
+ onEventBefore: ({ event }) => this.matchesKeyFlag(event, "arrowup")
4115
+ });
4116
+ this.registerFlag("down", {
4117
+ onEventBefore: ({ event }) => this.matchesKeyFlag(event, "arrowdown")
4118
+ });
4119
+ this.registerFlag("left", {
4120
+ onEventBefore: ({ event }) => this.matchesKeyFlag(event, "arrowleft")
4121
+ });
4122
+ this.registerFlag("right", {
4123
+ onEventBefore: ({ event }) => this.matchesKeyFlag(event, "arrowright")
4124
+ });
4125
+ this.registerFlag("arrowup", {
4126
+ onEventBefore: ({ event }) => this.matchesKeyFlag(event, "arrowup")
4127
+ });
4128
+ this.registerFlag("arrowdown", {
4129
+ onEventBefore: ({ event }) => this.matchesKeyFlag(event, "arrowdown")
4130
+ });
4131
+ this.registerFlag("arrowleft", {
4132
+ onEventBefore: ({ event }) => this.matchesKeyFlag(event, "arrowleft")
4133
+ });
4134
+ this.registerFlag("arrowright", {
4135
+ onEventBefore: ({ event }) => this.matchesKeyFlag(event, "arrowright")
4136
+ });
4137
+ this.registerFlag("delete", {
4138
+ onEventBefore: ({ event }) => this.matchesKeyFlag(event, "delete")
4139
+ });
4140
+ this.registerFlag("backspace", {
4141
+ onEventBefore: ({ event }) => this.matchesKeyFlag(event, "backspace")
4142
+ });
3431
4143
  this.registerGlobal("list", {
3432
4144
  async map(items, fn) {
3433
4145
  if (!Array.isArray(items) || typeof fn !== "function") {
@@ -3465,6 +4177,96 @@ var Engine = class _Engine {
3465
4177
  }
3466
4178
  });
3467
4179
  this.registerDefaultAttributeHandlers();
4180
+ this.registerFlag("int", {
4181
+ transformValue: (_context, value) => this.coerceInt(value)
4182
+ });
4183
+ this.registerFlag("float", {
4184
+ transformValue: (_context, value) => this.coerceFloat(value)
4185
+ });
4186
+ this.registerBehaviorModifier("group", {
4187
+ onConstruct: ({ args, scope, rootScope, behavior, element }) => {
4188
+ const key = typeof args === "string" ? args : void 0;
4189
+ if (!key) {
4190
+ return;
4191
+ }
4192
+ const targetScope = this.getGroupTargetScope(element, behavior, scope, rootScope);
4193
+ const existing = targetScope.getPath?.(key);
4194
+ const list = Array.isArray(existing) ? existing : [];
4195
+ const proxy = this.getGroupProxy(scope);
4196
+ if (!list.includes(proxy)) {
4197
+ list.push(proxy);
4198
+ targetScope.setPath?.(key, list);
4199
+ } else if (!Array.isArray(existing)) {
4200
+ targetScope.setPath?.(key, list);
4201
+ }
4202
+ },
4203
+ onUnbind: ({ args, scope, rootScope, behavior, element }) => {
4204
+ const key = typeof args === "string" ? args : void 0;
4205
+ if (!key) {
4206
+ return;
4207
+ }
4208
+ const targetScope = this.getGroupTargetScope(element, behavior, scope, rootScope);
4209
+ const existing = targetScope.getPath?.(key);
4210
+ if (!Array.isArray(existing)) {
4211
+ return;
4212
+ }
4213
+ const proxy = this.getGroupProxy(scope);
4214
+ const next = existing.filter((entry) => entry !== proxy);
4215
+ if (next.length !== existing.length) {
4216
+ targetScope.setPath?.(key, next);
4217
+ }
4218
+ }
4219
+ });
4220
+ }
4221
+ getGroupTargetScope(element, behavior, scope, rootScope) {
4222
+ let targetScope = rootScope ?? scope;
4223
+ if (behavior.parentSelector) {
4224
+ const parentElement = element.closest(behavior.parentSelector);
4225
+ if (parentElement) {
4226
+ targetScope = this.getScope(parentElement);
4227
+ }
4228
+ }
4229
+ return targetScope;
4230
+ }
4231
+ getGroupProxy(scope) {
4232
+ const cached = this.groupProxyCache.get(scope);
4233
+ if (cached) {
4234
+ return cached;
4235
+ }
4236
+ const proxy = new Proxy(
4237
+ {},
4238
+ {
4239
+ get: (_target, prop) => {
4240
+ if (typeof prop === "symbol") {
4241
+ return void 0;
4242
+ }
4243
+ if (prop === "__scope") {
4244
+ return scope;
4245
+ }
4246
+ return scope.getPath(String(prop));
4247
+ },
4248
+ set: (_target, prop, value) => {
4249
+ if (typeof prop === "symbol") {
4250
+ return false;
4251
+ }
4252
+ scope.setPath(String(prop), value);
4253
+ return true;
4254
+ },
4255
+ has: (_target, prop) => {
4256
+ if (typeof prop === "symbol") {
4257
+ return false;
4258
+ }
4259
+ return scope.getPath(String(prop)) !== void 0;
4260
+ },
4261
+ getOwnPropertyDescriptor: () => ({
4262
+ enumerable: true,
4263
+ configurable: true
4264
+ }),
4265
+ ownKeys: () => []
4266
+ }
4267
+ );
4268
+ this.groupProxyCache.set(scope, proxy);
4269
+ return proxy;
3468
4270
  }
3469
4271
  async mount(root) {
3470
4272
  const documentRoot = root.ownerDocument;
@@ -3491,7 +4293,10 @@ var Engine = class _Engine {
3491
4293
  this.disconnectObserver();
3492
4294
  }
3493
4295
  registerBehaviors(source) {
3494
- const program = new Parser(source, { customFlags: new Set(this.flagHandlers.keys()) }).parseProgram();
4296
+ const program = new Parser(source, {
4297
+ customFlags: new Set(this.flagHandlers.keys()),
4298
+ behaviorFlags: new Set(this.behaviorModifiers.keys())
4299
+ }).parseProgram();
3495
4300
  for (const use of program.uses) {
3496
4301
  if (use.flags?.wait) {
3497
4302
  this.pendingUses.push(this.waitForUseGlobal(use));
@@ -3515,11 +4320,14 @@ var Engine = class _Engine {
3515
4320
  Object.assign(this.globals, values);
3516
4321
  }
3517
4322
  registerFlag(name, handler = {}) {
4323
+ this.flagHandlers.set(name, handler);
4324
+ }
4325
+ registerBehaviorModifier(name, handler = {}) {
3518
4326
  const reserved = /* @__PURE__ */ new Set(["important", "trusted", "debounce"]);
3519
4327
  if (reserved.has(name)) {
3520
- throw new Error(`Flag '${name}' is reserved`);
4328
+ throw new Error(`Behavior modifier '${name}' is reserved`);
3521
4329
  }
3522
- this.flagHandlers.set(name, handler);
4330
+ this.behaviorModifiers.set(name, handler);
3523
4331
  }
3524
4332
  getRegistryStats() {
3525
4333
  return {
@@ -3627,7 +4435,6 @@ var Engine = class _Engine {
3627
4435
  if (this.observer) {
3628
4436
  return;
3629
4437
  }
3630
- this.observerRoot = root;
3631
4438
  this.observerFlush = debounce(() => this.flushObserverQueue(), 10);
3632
4439
  this.observer = new MutationObserver((mutations) => {
3633
4440
  for (const mutation of mutations) {
@@ -3657,7 +4464,6 @@ var Engine = class _Engine {
3657
4464
  disconnectObserver() {
3658
4465
  this.observer?.disconnect();
3659
4466
  this.observer = void 0;
3660
- this.observerRoot = void 0;
3661
4467
  this.pendingAdded.clear();
3662
4468
  this.pendingRemoved.clear();
3663
4469
  this.pendingUpdated.clear();
@@ -3686,6 +4492,8 @@ var Engine = class _Engine {
3686
4492
  if (this.behaviorBindings.has(node)) {
3687
4493
  this.runBehaviorDestruct(node);
3688
4494
  }
4495
+ this.cleanupScopeWatchers(node);
4496
+ this.cleanupBehaviorListeners(node);
3689
4497
  for (const child of Array.from(node.querySelectorAll("*"))) {
3690
4498
  if (this.lifecycleBindings.has(child)) {
3691
4499
  this.runDestruct(child);
@@ -3693,6 +4501,8 @@ var Engine = class _Engine {
3693
4501
  if (this.behaviorBindings.has(child)) {
3694
4502
  this.runBehaviorDestruct(child);
3695
4503
  }
4504
+ this.cleanupScopeWatchers(child);
4505
+ this.cleanupBehaviorListeners(child);
3696
4506
  }
3697
4507
  }
3698
4508
  handleAddedNode(node) {
@@ -3716,13 +4526,13 @@ var Engine = class _Engine {
3716
4526
  }
3717
4527
  async applyBehaviors(root) {
3718
4528
  await this.waitForUses();
3719
- if (this.behaviorRegistry.length === 0) {
3720
- return;
3721
- }
3722
- const elements = [root, ...Array.from(root.querySelectorAll("*"))];
3723
- for (const element of elements) {
3724
- await this.reapplyBehaviorsForElement(element);
4529
+ if (this.behaviorRegistry.length > 0) {
4530
+ const elements = [root, ...Array.from(root.querySelectorAll("*"))];
4531
+ for (const element of elements) {
4532
+ await this.reapplyBehaviorsForElement(element);
4533
+ }
3725
4534
  }
4535
+ this.flushAutoBindQueue();
3726
4536
  }
3727
4537
  async reapplyBehaviorsForElement(element) {
3728
4538
  if (this.behaviorRegistry.length === 0) {
@@ -3754,15 +4564,18 @@ var Engine = class _Engine {
3754
4564
  const rootScope = this.getBehaviorRootScope(element, behavior);
3755
4565
  this.applyBehaviorFunctions(element, scope, behavior.functions, rootScope);
3756
4566
  await this.applyBehaviorDeclarations(element, scope, behavior.declarations, rootScope);
4567
+ await this.applyBehaviorModifierHook("onBind", behavior, element, scope, rootScope);
3757
4568
  if (behavior.construct) {
3758
4569
  await this.safeExecuteBlock(behavior.construct, scope, element, rootScope);
3759
4570
  }
4571
+ await this.applyBehaviorModifierHook("onConstruct", behavior, element, scope, rootScope);
3760
4572
  for (const onBlock of behavior.onBlocks) {
3761
4573
  this.attachBehaviorOnHandler(
3762
4574
  element,
3763
4575
  onBlock.event,
3764
4576
  onBlock.body,
3765
- onBlock.modifiers,
4577
+ onBlock.flags,
4578
+ onBlock.flagArgs,
3766
4579
  onBlock.args,
3767
4580
  behavior.id,
3768
4581
  rootScope
@@ -3772,10 +4585,11 @@ var Engine = class _Engine {
3772
4585
  }
3773
4586
  unbindBehaviorForElement(behavior, element, scope, bound) {
3774
4587
  bound.delete(behavior.id);
4588
+ const rootScope = this.getBehaviorRootScope(element, behavior);
3775
4589
  if (behavior.destruct) {
3776
- const rootScope = this.getBehaviorRootScope(element, behavior);
3777
4590
  void this.safeExecuteBlock(behavior.destruct, scope, element, rootScope);
3778
4591
  }
4592
+ void this.applyBehaviorModifierHook("onDestruct", behavior, element, scope, rootScope);
3779
4593
  const listenerMap = this.behaviorListeners.get(element);
3780
4594
  const listeners = listenerMap?.get(behavior.id);
3781
4595
  if (listeners) {
@@ -3784,6 +4598,7 @@ var Engine = class _Engine {
3784
4598
  }
3785
4599
  listenerMap?.delete(behavior.id);
3786
4600
  }
4601
+ void this.applyBehaviorModifierHook("onUnbind", behavior, element, scope, rootScope);
3787
4602
  this.logDiagnostic("unbind", element, behavior);
3788
4603
  }
3789
4604
  runBehaviorDestruct(element) {
@@ -3793,11 +4608,15 @@ var Engine = class _Engine {
3793
4608
  }
3794
4609
  const scope = this.getScope(element);
3795
4610
  for (const behavior of this.behaviorRegistry) {
3796
- if (!bound.has(behavior.id) || !behavior.destruct) {
4611
+ if (!bound.has(behavior.id) || !behavior.destruct && !this.behaviorHasModifierHooks(behavior)) {
3797
4612
  continue;
3798
4613
  }
3799
4614
  const rootScope = this.getBehaviorRootScope(element, behavior);
3800
- void this.safeExecuteBlock(behavior.destruct, scope, element, rootScope);
4615
+ if (behavior.destruct) {
4616
+ void this.safeExecuteBlock(behavior.destruct, scope, element, rootScope);
4617
+ }
4618
+ void this.applyBehaviorModifierHook("onDestruct", behavior, element, scope, rootScope);
4619
+ void this.applyBehaviorModifierHook("onUnbind", behavior, element, scope, rootScope);
3801
4620
  }
3802
4621
  }
3803
4622
  attachAttributes(element) {
@@ -3881,6 +4700,7 @@ var Engine = class _Engine {
3881
4700
  const fragment = element.content.cloneNode(true);
3882
4701
  const roots = Array.from(fragment.children);
3883
4702
  const itemScope = new Scope(scope);
4703
+ itemScope.isEachItem = true;
3884
4704
  itemScope.setPath(`self.${binding.itemName}`, item);
3885
4705
  if (binding.indexName) {
3886
4706
  itemScope.setPath(`self.${binding.indexName}`, index);
@@ -3919,7 +4739,93 @@ var Engine = class _Engine {
3919
4739
  if (name.includes(":to")) {
3920
4740
  return "to";
3921
4741
  }
3922
- return "both";
4742
+ return "auto";
4743
+ }
4744
+ resolveBindConfig(element, expr, scope, direction) {
4745
+ if (direction !== "auto") {
4746
+ return {
4747
+ direction,
4748
+ seedFromScope: false,
4749
+ syncToScope: direction === "to" || direction === "both",
4750
+ deferToScope: false
4751
+ };
4752
+ }
4753
+ if (this.isInEachScope(scope)) {
4754
+ return { direction: "both", seedFromScope: false, syncToScope: false, deferToScope: false };
4755
+ }
4756
+ if (this.isFormControl(element)) {
4757
+ if (this.hasScopeValue(scope, expr)) {
4758
+ return { direction: "both", seedFromScope: true, syncToScope: false, deferToScope: false };
4759
+ }
4760
+ return { direction: "both", seedFromScope: false, syncToScope: false, deferToScope: true };
4761
+ }
4762
+ if (this.hasScopeValue(scope, expr)) {
4763
+ return { direction: "both", seedFromScope: false, syncToScope: false, deferToScope: false };
4764
+ }
4765
+ if (this.hasElementValue(element)) {
4766
+ return { direction: "both", seedFromScope: false, syncToScope: false, deferToScope: true };
4767
+ }
4768
+ return { direction: "both", seedFromScope: false, syncToScope: false, deferToScope: false };
4769
+ }
4770
+ isFormControl(element) {
4771
+ return element instanceof HTMLInputElement || element instanceof HTMLTextAreaElement || element instanceof HTMLSelectElement;
4772
+ }
4773
+ hasScopeValue(scope, expr) {
4774
+ const key = expr.trim();
4775
+ if (!key) {
4776
+ return false;
4777
+ }
4778
+ const value = scope.get(key);
4779
+ return value !== void 0 && value !== null;
4780
+ }
4781
+ hasElementValue(element) {
4782
+ if (element instanceof HTMLInputElement || element instanceof HTMLTextAreaElement || element instanceof HTMLSelectElement) {
4783
+ return element.value.length > 0;
4784
+ }
4785
+ return (element.textContent ?? "").trim().length > 0;
4786
+ }
4787
+ coerceInt(value) {
4788
+ if (value == null || value === "") {
4789
+ return value;
4790
+ }
4791
+ const num = typeof value === "number" ? value : Number.parseInt(String(value), 10);
4792
+ return Number.isNaN(num) ? value : num;
4793
+ }
4794
+ coerceFloat(value) {
4795
+ if (value == null || value === "") {
4796
+ return value;
4797
+ }
4798
+ const num = typeof value === "number" ? value : Number.parseFloat(String(value));
4799
+ return Number.isNaN(num) ? value : num;
4800
+ }
4801
+ isInEachScope(scope) {
4802
+ let cursor = scope;
4803
+ while (cursor) {
4804
+ if (cursor.isEachItem) {
4805
+ return true;
4806
+ }
4807
+ cursor = cursor.parent;
4808
+ }
4809
+ return false;
4810
+ }
4811
+ flushAutoBindQueue() {
4812
+ if (this.pendingAutoBindToScope.length === 0) {
4813
+ return;
4814
+ }
4815
+ const pending = this.pendingAutoBindToScope;
4816
+ this.pendingAutoBindToScope = [];
4817
+ for (const entry of pending) {
4818
+ if (!entry.element.isConnected) {
4819
+ continue;
4820
+ }
4821
+ if (this.hasScopeValue(entry.scope, entry.expr)) {
4822
+ continue;
4823
+ }
4824
+ if (!this.hasElementValue(entry.element)) {
4825
+ continue;
4826
+ }
4827
+ applyBindToScope(entry.element, entry.expr, entry.scope);
4828
+ }
3923
4829
  }
3924
4830
  hasVsnAttributes(element) {
3925
4831
  return element.getAttributeNames().some((name) => name.startsWith("vsn-"));
@@ -3944,7 +4850,7 @@ var Engine = class _Engine {
3944
4850
  }
3945
4851
  return void 0;
3946
4852
  }
3947
- watch(scope, expr, handler) {
4853
+ watch(scope, expr, handler, element) {
3948
4854
  const key = expr.trim();
3949
4855
  if (!key) {
3950
4856
  return;
@@ -3959,29 +4865,70 @@ var Engine = class _Engine {
3959
4865
  }
3960
4866
  if (target) {
3961
4867
  target.on(key, handler);
4868
+ if (element) {
4869
+ this.trackScopeWatcher(element, target, "path", handler, key);
4870
+ }
3962
4871
  return;
3963
4872
  }
3964
4873
  let cursor = scope;
3965
4874
  while (cursor) {
3966
4875
  cursor.on(key, handler);
4876
+ if (element) {
4877
+ this.trackScopeWatcher(element, cursor, "path", handler, key);
4878
+ }
3967
4879
  cursor = cursor.parent;
3968
4880
  }
3969
4881
  }
3970
- watchWithDebounce(scope, expr, handler, debounceMs) {
3971
- if (debounceMs) {
3972
- this.watch(scope, expr, debounce(handler, debounceMs));
3973
- } else {
3974
- this.watch(scope, expr, handler);
3975
- }
4882
+ watchWithDebounce(scope, expr, handler, debounceMs, element) {
4883
+ const effectiveHandler = debounceMs ? debounce(handler, debounceMs) : handler;
4884
+ this.watch(scope, expr, effectiveHandler, element);
3976
4885
  }
3977
- watchAllScopes(scope, handler, debounceMs) {
4886
+ watchAllScopes(scope, handler, debounceMs, element) {
3978
4887
  const effectiveHandler = debounceMs ? debounce(handler, debounceMs) : handler;
3979
4888
  let cursor = scope;
3980
4889
  while (cursor) {
3981
4890
  cursor.onAny(effectiveHandler);
4891
+ if (element) {
4892
+ this.trackScopeWatcher(element, cursor, "any", effectiveHandler);
4893
+ }
3982
4894
  cursor = cursor.parent;
3983
4895
  }
3984
4896
  }
4897
+ trackScopeWatcher(element, scope, kind, handler, key) {
4898
+ const watchers = this.scopeWatchers.get(element) ?? [];
4899
+ watchers.push({ scope, kind, handler, ...key ? { key } : {} });
4900
+ this.scopeWatchers.set(element, watchers);
4901
+ }
4902
+ cleanupScopeWatchers(element) {
4903
+ const watchers = this.scopeWatchers.get(element);
4904
+ if (!watchers) {
4905
+ return;
4906
+ }
4907
+ for (const watcher of watchers) {
4908
+ if (watcher.kind === "any") {
4909
+ watcher.scope.offAny(watcher.handler);
4910
+ continue;
4911
+ }
4912
+ if (watcher.key) {
4913
+ watcher.scope.off(watcher.key, watcher.handler);
4914
+ }
4915
+ }
4916
+ this.scopeWatchers.delete(element);
4917
+ }
4918
+ cleanupBehaviorListeners(element) {
4919
+ const listenerMap = this.behaviorListeners.get(element);
4920
+ if (!listenerMap) {
4921
+ return;
4922
+ }
4923
+ for (const listeners of listenerMap.values()) {
4924
+ for (const listener of listeners) {
4925
+ listener.target.removeEventListener(listener.event, listener.handler, listener.options);
4926
+ }
4927
+ }
4928
+ listenerMap.clear();
4929
+ this.behaviorListeners.delete(element);
4930
+ this.behaviorBindings.delete(element);
4931
+ }
3985
4932
  parseOnAttribute(name, value) {
3986
4933
  if (!name.startsWith("vsn-on:")) {
3987
4934
  return null;
@@ -3991,111 +4938,56 @@ var Engine = class _Engine {
3991
4938
  if (!event) {
3992
4939
  return null;
3993
4940
  }
3994
- const descriptor = this.parseEventDescriptor(event);
3995
- if (!descriptor.event) {
3996
- return null;
3997
- }
3998
- let debounceMs;
3999
- const modifiers = [];
4000
- for (const flag of flags) {
4001
- if (flag.startsWith("debounce")) {
4002
- const match = flag.match(/debounce\((\d+)\)/);
4003
- debounceMs = match ? Number(match[1]) : 200;
4004
- continue;
4005
- }
4006
- modifiers.push(flag);
4941
+ if (event.includes(".")) {
4942
+ throw new Error("vsn:on does not support dot modifiers; use !flags instead");
4007
4943
  }
4008
- const combinedModifiers = [...modifiers, ...descriptor.modifiers];
4944
+ const { flagMap, flagArgs } = this.parseInlineFlags(flags);
4009
4945
  const config = {
4010
- event: descriptor.event,
4946
+ event,
4011
4947
  code: value,
4012
- ...debounceMs !== void 0 ? { debounceMs } : {},
4013
- ...combinedModifiers.length > 0 ? { modifiers: combinedModifiers } : {},
4014
- ...descriptor.keyModifiers.length > 0 ? { keyModifiers: descriptor.keyModifiers } : {}
4948
+ flags: flagMap,
4949
+ flagArgs
4015
4950
  };
4016
4951
  return config;
4017
4952
  }
4018
- parseEventDescriptor(raw) {
4019
- const parts = raw.split(".").map((part) => part.trim()).filter(Boolean);
4020
- const event = parts.shift() ?? "";
4021
- const modifiers = [];
4022
- const keyModifiers = [];
4023
- const modifierSet = /* @__PURE__ */ new Set(["outside", "self"]);
4024
- for (const part of parts) {
4025
- if (modifierSet.has(part)) {
4026
- modifiers.push(part);
4027
- } else {
4028
- keyModifiers.push(part);
4953
+ parseInlineFlags(parts) {
4954
+ const flagMap = {};
4955
+ const flagArgs = {};
4956
+ for (const raw of parts) {
4957
+ const trimmed = raw.trim();
4958
+ if (!trimmed) {
4959
+ continue;
4029
4960
  }
4030
- }
4031
- return { event, keyModifiers, modifiers };
4032
- }
4033
- matchesKeyModifiers(event, keyModifiers) {
4034
- if (!keyModifiers || keyModifiers.length === 0) {
4035
- return true;
4036
- }
4037
- if (!(event instanceof KeyboardEvent)) {
4038
- return false;
4039
- }
4040
- const modifierChecks = {
4041
- shift: event.shiftKey,
4042
- ctrl: event.ctrlKey,
4043
- control: event.ctrlKey,
4044
- alt: event.altKey,
4045
- meta: event.metaKey
4046
- };
4047
- const keyAliases = {
4048
- esc: "escape",
4049
- escape: "escape",
4050
- enter: "enter",
4051
- tab: "tab",
4052
- space: "space",
4053
- spacebar: "space",
4054
- up: "arrowup",
4055
- down: "arrowdown",
4056
- left: "arrowleft",
4057
- right: "arrowright",
4058
- arrowup: "arrowup",
4059
- arrowdown: "arrowdown",
4060
- arrowleft: "arrowleft",
4061
- arrowright: "arrowright",
4062
- delete: "delete",
4063
- backspace: "backspace"
4064
- };
4065
- let key = event.key?.toLowerCase() ?? "";
4066
- if (key === " ") {
4067
- key = "space";
4068
- }
4069
- for (const rawModifier of keyModifiers) {
4070
- const modifier = rawModifier.toLowerCase();
4071
- if (modifier in modifierChecks) {
4072
- if (!modifierChecks[modifier]) {
4073
- return false;
4074
- }
4961
+ const match = trimmed.match(/^([a-zA-Z][\w-]*)(?:\((.+)\))?$/);
4962
+ if (!match) {
4075
4963
  continue;
4076
4964
  }
4077
- const expectedKey = keyAliases[modifier] ?? modifier;
4078
- if (key !== expectedKey) {
4079
- return false;
4965
+ const name = match[1] ?? "";
4966
+ if (!name) {
4967
+ continue;
4968
+ }
4969
+ if (!this.flagHandlers.has(name)) {
4970
+ throw new Error(`Unknown flag ${name}`);
4971
+ }
4972
+ flagMap[name] = true;
4973
+ if (match[2] !== void 0) {
4974
+ flagArgs[name] = this.parseInlineFlagArg(match[2]);
4080
4975
  }
4081
4976
  }
4082
- return true;
4977
+ return { flagMap, flagArgs };
4083
4978
  }
4084
- matchesTargetModifiers(element, event, modifiers) {
4085
- if (!modifiers || modifiers.length === 0) {
4979
+ parseInlineFlagArg(raw) {
4980
+ const trimmed = raw.trim();
4981
+ if (trimmed === "true") {
4086
4982
  return true;
4087
4983
  }
4088
- const target = event?.target;
4089
- if (!target || !(target instanceof Node)) {
4090
- return !modifiers.includes("self") && !modifiers.includes("outside");
4091
- }
4092
- if (modifiers.includes("self") && target !== element) {
4984
+ if (trimmed === "false") {
4093
4985
  return false;
4094
4986
  }
4095
- if (modifiers.includes("outside") && element.contains(target)) {
4096
- return false;
4987
+ if (/^-?\d+(\.\d+)?$/.test(trimmed)) {
4988
+ return Number(trimmed);
4097
4989
  }
4098
- return true;
4990
+ return trimmed;
4099
4991
  }
4100
4992
  describeElement(element) {
4101
4993
  const tag = element.tagName.toLowerCase();
@@ -4137,51 +5029,50 @@ var Engine = class _Engine {
4137
5029
  }
4138
5030
  }
4139
5031
  attachOnHandler(element, config) {
4140
- const options = this.getListenerOptions(config.modifiers);
4141
- const listenerTarget = config.modifiers?.includes("outside") ? element.ownerDocument : element;
5032
+ const { listenerTarget, options, debounceMs } = this.getEventBindingConfig(
5033
+ element,
5034
+ config.flags,
5035
+ config.flagArgs
5036
+ );
4142
5037
  let effectiveHandler;
4143
5038
  const handler = async (event) => {
4144
5039
  if (!element.isConnected) {
4145
5040
  listenerTarget.removeEventListener(config.event, effectiveHandler, options);
4146
5041
  return;
4147
5042
  }
4148
- if (!this.matchesKeyModifiers(event, config.keyModifiers)) {
4149
- return;
4150
- }
4151
- if (!this.matchesTargetModifiers(element, event, config.modifiers)) {
5043
+ const scope = this.getScope(element);
5044
+ if (!this.applyEventFlagBefore(element, scope, config.flags, config.flagArgs, event)) {
4152
5045
  return;
4153
5046
  }
4154
- this.applyEventModifiers(event, config.modifiers);
4155
- const scope = this.getScope(element);
4156
5047
  try {
4157
5048
  await this.execute(config.code, scope, element);
4158
5049
  this.evaluate(element);
4159
5050
  } catch (error) {
4160
5051
  this.emitError(element, error);
5052
+ } finally {
5053
+ this.applyEventFlagAfter(element, scope, config.flags, config.flagArgs, event);
4161
5054
  }
4162
5055
  };
4163
- effectiveHandler = config.debounceMs ? debounce(handler, config.debounceMs) : handler;
5056
+ effectiveHandler = debounceMs ? debounce(handler, debounceMs) : handler;
4164
5057
  listenerTarget.addEventListener(config.event, effectiveHandler, options);
4165
5058
  }
4166
- attachBehaviorOnHandler(element, event, body, modifiers, args, behaviorId, rootScope) {
4167
- const descriptor = this.parseEventDescriptor(event);
4168
- const combinedModifiers = modifiers ? [...modifiers, ...descriptor.modifiers] : descriptor.modifiers.length > 0 ? [...descriptor.modifiers] : void 0;
4169
- const listenerTarget = combinedModifiers?.includes("outside") ? element.ownerDocument : element;
5059
+ attachBehaviorOnHandler(element, event, body, flags, flagArgs, args, behaviorId, rootScope) {
5060
+ if (event.includes(".")) {
5061
+ throw new Error("vsn:on does not support dot modifiers; use !flags instead");
5062
+ }
5063
+ const { listenerTarget, options, debounceMs } = this.getEventBindingConfig(element, flags, flagArgs);
4170
5064
  const handler = async (evt) => {
4171
- if (!this.matchesKeyModifiers(evt, descriptor.keyModifiers)) {
4172
- return;
4173
- }
4174
- if (!this.matchesTargetModifiers(element, evt, combinedModifiers)) {
5065
+ const scope = this.getScope(element);
5066
+ if (!this.applyEventFlagBefore(element, scope, flags, flagArgs, evt)) {
4175
5067
  return;
4176
5068
  }
4177
- this.applyEventModifiers(evt, combinedModifiers);
4178
- const scope = this.getScope(element);
4179
5069
  const previousValues = /* @__PURE__ */ new Map();
4180
5070
  if (args && args.length > 0) {
4181
5071
  const argName = args[0];
4182
5072
  if (argName) {
4183
5073
  previousValues.set(argName, scope.getPath(argName));
4184
- scope.setPath(argName, evt);
5074
+ const [nextArg] = this.applyEventFlagArgTransforms(element, scope, flags, flagArgs, evt);
5075
+ scope.setPath(argName, nextArg);
4185
5076
  }
4186
5077
  }
4187
5078
  let failed = false;
@@ -4194,16 +5085,17 @@ var Engine = class _Engine {
4194
5085
  for (const [name, value] of previousValues.entries()) {
4195
5086
  scope.setPath(name, value);
4196
5087
  }
5088
+ this.applyEventFlagAfter(element, scope, flags, flagArgs, evt);
4197
5089
  }
4198
5090
  if (!failed) {
4199
5091
  this.evaluate(element);
4200
5092
  }
4201
5093
  };
4202
- const options = this.getListenerOptions(combinedModifiers);
4203
- listenerTarget.addEventListener(descriptor.event, handler, options);
5094
+ const effectiveHandler = debounceMs ? debounce(handler, debounceMs) : handler;
5095
+ listenerTarget.addEventListener(event, effectiveHandler, options);
4204
5096
  const listenerMap = this.behaviorListeners.get(element) ?? /* @__PURE__ */ new Map();
4205
5097
  const listeners = listenerMap.get(behaviorId) ?? [];
4206
- listeners.push({ target: listenerTarget, event: descriptor.event, handler, options });
5098
+ listeners.push({ target: listenerTarget, event, handler: effectiveHandler, options });
4207
5099
  listenerMap.set(behaviorId, listeners);
4208
5100
  this.behaviorListeners.set(element, listenerMap);
4209
5101
  }
@@ -4234,33 +5126,158 @@ var Engine = class _Engine {
4234
5126
  Promise.resolve().then(handler);
4235
5127
  }
4236
5128
  }
4237
- applyEventModifiers(event, modifiers) {
4238
- if (!event || !modifiers || modifiers.length === 0) {
4239
- return;
5129
+ getEventBindingConfig(element, flags, flagArgs) {
5130
+ let listenerTarget = element;
5131
+ let options = {};
5132
+ let debounceMs;
5133
+ for (const name of Object.keys(flags)) {
5134
+ const handler = this.flagHandlers.get(name);
5135
+ if (!handler?.onEventBind) {
5136
+ continue;
5137
+ }
5138
+ const patch = handler.onEventBind({
5139
+ name,
5140
+ args: flagArgs[name],
5141
+ element,
5142
+ scope: this.getScope(element),
5143
+ rootScope: void 0,
5144
+ event: void 0,
5145
+ engine: this
5146
+ });
5147
+ if (!patch) {
5148
+ continue;
5149
+ }
5150
+ if (patch.listenerTarget) {
5151
+ listenerTarget = patch.listenerTarget;
5152
+ }
5153
+ if (patch.options) {
5154
+ options = { ...options, ...patch.options };
5155
+ }
5156
+ if (patch.debounceMs !== void 0) {
5157
+ debounceMs = patch.debounceMs;
5158
+ }
5159
+ }
5160
+ return {
5161
+ listenerTarget,
5162
+ ...Object.keys(options).length > 0 ? { options } : {},
5163
+ ...debounceMs !== void 0 ? { debounceMs } : {}
5164
+ };
5165
+ }
5166
+ applyEventFlagBefore(element, scope, flags, flagArgs, event) {
5167
+ for (const name of Object.keys(flags)) {
5168
+ const handler = this.flagHandlers.get(name);
5169
+ if (!handler?.onEventBefore) {
5170
+ continue;
5171
+ }
5172
+ const result = handler.onEventBefore({
5173
+ name,
5174
+ args: flagArgs[name],
5175
+ element,
5176
+ scope,
5177
+ rootScope: void 0,
5178
+ event,
5179
+ engine: this
5180
+ });
5181
+ if (result === false) {
5182
+ return false;
5183
+ }
4240
5184
  }
4241
- for (const modifier of modifiers) {
4242
- if (modifier === "prevent") {
4243
- event.preventDefault();
4244
- } else if (modifier === "stop") {
4245
- event.stopPropagation();
5185
+ return true;
5186
+ }
5187
+ applyEventFlagAfter(element, scope, flags, flagArgs, event) {
5188
+ for (const name of Object.keys(flags)) {
5189
+ const handler = this.flagHandlers.get(name);
5190
+ if (!handler?.onEventAfter) {
5191
+ continue;
4246
5192
  }
5193
+ handler.onEventAfter({
5194
+ name,
5195
+ args: flagArgs[name],
5196
+ element,
5197
+ scope,
5198
+ rootScope: void 0,
5199
+ event,
5200
+ engine: this
5201
+ });
4247
5202
  }
4248
5203
  }
4249
- getListenerOptions(modifiers) {
4250
- if (!modifiers || modifiers.length === 0) {
4251
- return void 0;
5204
+ applyEventFlagArgTransforms(element, scope, flags, flagArgs, event) {
5205
+ let args = [event];
5206
+ for (const name of Object.keys(flags)) {
5207
+ const handler = this.flagHandlers.get(name);
5208
+ if (!handler?.transformEventArgs) {
5209
+ continue;
5210
+ }
5211
+ const nextArgs = handler.transformEventArgs(
5212
+ {
5213
+ name,
5214
+ args: flagArgs[name],
5215
+ element,
5216
+ scope,
5217
+ rootScope: void 0,
5218
+ event,
5219
+ engine: this
5220
+ },
5221
+ args
5222
+ );
5223
+ if (Array.isArray(nextArgs)) {
5224
+ args = nextArgs;
5225
+ }
5226
+ }
5227
+ return args;
5228
+ }
5229
+ matchesKeyFlag(event, flag) {
5230
+ if (!(event instanceof KeyboardEvent)) {
5231
+ return false;
5232
+ }
5233
+ const modifierChecks = {
5234
+ shift: event.shiftKey,
5235
+ ctrl: event.ctrlKey,
5236
+ alt: event.altKey,
5237
+ meta: event.metaKey
5238
+ };
5239
+ if (flag in modifierChecks) {
5240
+ return modifierChecks[flag] ?? false;
4252
5241
  }
4253
- const options = {};
4254
- if (modifiers.includes("once")) {
4255
- options.once = true;
5242
+ const keyAliases = {
5243
+ escape: "escape",
5244
+ esc: "escape",
5245
+ enter: "enter",
5246
+ tab: "tab",
5247
+ space: "space",
5248
+ spacebar: "space",
5249
+ up: "arrowup",
5250
+ down: "arrowdown",
5251
+ left: "arrowleft",
5252
+ right: "arrowright",
5253
+ arrowup: "arrowup",
5254
+ arrowdown: "arrowdown",
5255
+ arrowleft: "arrowleft",
5256
+ arrowright: "arrowright",
5257
+ delete: "delete",
5258
+ backspace: "backspace"
5259
+ };
5260
+ let key = event.key?.toLowerCase() ?? "";
5261
+ if (key === " ") {
5262
+ key = "space";
4256
5263
  }
4257
- if (modifiers.includes("passive")) {
4258
- options.passive = true;
5264
+ const expectedKey = keyAliases[flag] ?? flag;
5265
+ return key === expectedKey;
5266
+ }
5267
+ async withExecutionElement(element, fn) {
5268
+ if (!element) {
5269
+ await fn();
5270
+ return;
4259
5271
  }
4260
- if (modifiers.includes("capture")) {
4261
- options.capture = true;
5272
+ this.executionStack.push(element);
5273
+ try {
5274
+ await fn();
5275
+ } finally {
5276
+ this.executionStack.pop();
4262
5277
  }
4263
- return Object.keys(options).length > 0 ? options : void 0;
5278
+ }
5279
+ getCurrentElement() {
5280
+ return this.executionStack[this.executionStack.length - 1];
4264
5281
  }
4265
5282
  async execute(code, scope, element, rootScope) {
4266
5283
  let block = this.codeCache.get(code);
@@ -4268,22 +5285,26 @@ var Engine = class _Engine {
4268
5285
  block = Parser.parseInline(code);
4269
5286
  this.codeCache.set(code, block);
4270
5287
  }
4271
- const context = {
4272
- scope,
4273
- rootScope,
4274
- globals: this.globals,
4275
- ...element ? { element } : {}
4276
- };
4277
- await block.evaluate(context);
5288
+ await this.withExecutionElement(element, async () => {
5289
+ const context = {
5290
+ scope,
5291
+ rootScope,
5292
+ globals: this.globals,
5293
+ ...element ? { element } : {}
5294
+ };
5295
+ await block.evaluate(context);
5296
+ });
4278
5297
  }
4279
5298
  async executeBlock(block, scope, element, rootScope) {
4280
- const context = {
4281
- scope,
4282
- rootScope,
4283
- globals: this.globals,
4284
- ...element ? { element } : {}
4285
- };
4286
- await block.evaluate(context);
5299
+ await this.withExecutionElement(element, async () => {
5300
+ const context = {
5301
+ scope,
5302
+ rootScope,
5303
+ globals: this.globals,
5304
+ ...element ? { element } : {}
5305
+ };
5306
+ await block.evaluate(context);
5307
+ });
4287
5308
  }
4288
5309
  async safeExecute(code, scope, element, rootScope) {
4289
5310
  try {
@@ -4306,15 +5327,26 @@ var Engine = class _Engine {
4306
5327
  collectBehavior(behavior, parentSelector, rootSelectorOverride) {
4307
5328
  const selector = parentSelector ? `${parentSelector} ${behavior.selector.selectorText}` : behavior.selector.selectorText;
4308
5329
  const rootSelector = rootSelectorOverride ?? (parentSelector ?? behavior.selector.selectorText);
5330
+ const behaviorHash = this.hashBehavior(behavior);
5331
+ const hash = `${selector}::${rootSelector}::${behaviorHash}`;
5332
+ if (this.behaviorRegistryHashes.has(hash)) {
5333
+ return;
5334
+ }
4309
5335
  const cached = this.getCachedBehavior(behavior);
4310
- this.behaviorRegistry.push({
5336
+ const entry = {
4311
5337
  id: this.behaviorId += 1,
5338
+ hash,
4312
5339
  selector,
4313
5340
  rootSelector,
4314
5341
  specificity: this.computeSpecificity(selector),
4315
5342
  order: this.behaviorRegistry.length,
4316
- ...cached
4317
- });
5343
+ flags: behavior.flags ?? {},
5344
+ flagArgs: behavior.flagArgs ?? {},
5345
+ ...cached,
5346
+ ...parentSelector ? { parentSelector } : {}
5347
+ };
5348
+ this.behaviorRegistry.push(entry);
5349
+ this.behaviorRegistryHashes.add(hash);
4318
5350
  this.collectNestedBehaviors(behavior.body, selector, rootSelector);
4319
5351
  }
4320
5352
  collectNestedBehaviors(block, parentSelector, rootSelector) {
@@ -4387,7 +5419,8 @@ var Engine = class _Engine {
4387
5419
  blocks.push({
4388
5420
  event: statement.eventName,
4389
5421
  body: statement.body,
4390
- modifiers: statement.modifiers,
5422
+ flags: statement.flags,
5423
+ flagArgs: statement.flagArgs,
4391
5424
  args: statement.args
4392
5425
  });
4393
5426
  }
@@ -4452,6 +5485,8 @@ var Engine = class _Engine {
4452
5485
  return {
4453
5486
  type,
4454
5487
  selector: node.selector?.selectorText ?? "",
5488
+ flags: node.flags ?? {},
5489
+ flagArgs: node.flagArgs ?? {},
4455
5490
  body: this.normalizeNode(node.body)
4456
5491
  };
4457
5492
  }
@@ -4469,6 +5504,8 @@ var Engine = class _Engine {
4469
5504
  type,
4470
5505
  eventName: node.eventName ?? "",
4471
5506
  args: Array.isArray(node.args) ? node.args : [],
5507
+ flags: node.flags ?? {},
5508
+ flagArgs: node.flagArgs ?? {},
4472
5509
  body: this.normalizeNode(node.body)
4473
5510
  };
4474
5511
  }
@@ -4486,21 +5523,9 @@ var Engine = class _Engine {
4486
5523
  return {
4487
5524
  type,
4488
5525
  target: this.normalizeNode(node.target),
4489
- value: this.normalizeNode(node.value)
4490
- };
4491
- }
4492
- if (type === "StateBlock") {
4493
- return {
4494
- type,
4495
- entries: Array.isArray(node.entries) ? node.entries.map((entry) => this.normalizeNode(entry)) : []
4496
- };
4497
- }
4498
- if (type === "StateEntry") {
4499
- return {
4500
- type,
4501
- name: node.name ?? "",
4502
5526
  value: this.normalizeNode(node.value),
4503
- important: Boolean(node.important)
5527
+ operator: node.operator ?? "",
5528
+ prefix: Boolean(node.prefix)
4504
5529
  };
4505
5530
  }
4506
5531
  if (type === "FunctionDeclaration") {
@@ -4534,6 +5559,15 @@ var Engine = class _Engine {
4534
5559
  value: this.normalizeNode(node.value ?? null)
4535
5560
  };
4536
5561
  }
5562
+ if (type === "Assert") {
5563
+ return {
5564
+ type,
5565
+ test: this.normalizeNode(node.test)
5566
+ };
5567
+ }
5568
+ if (type === "Break" || type === "Continue") {
5569
+ return { type };
5570
+ }
4537
5571
  if (type === "If") {
4538
5572
  return {
4539
5573
  type,
@@ -4558,6 +5592,15 @@ var Engine = class _Engine {
4558
5592
  body: this.normalizeNode(node.body)
4559
5593
  };
4560
5594
  }
5595
+ if (type === "ForEach") {
5596
+ return {
5597
+ type,
5598
+ kind: node.kind ?? "of",
5599
+ target: this.normalizeNode(node.target),
5600
+ iterable: this.normalizeNode(node.iterable),
5601
+ body: this.normalizeNode(node.body)
5602
+ };
5603
+ }
4561
5604
  if (type === "Try") {
4562
5605
  return {
4563
5606
  type,
@@ -4680,7 +5723,9 @@ var Engine = class _Engine {
4680
5723
  globals: this.globals,
4681
5724
  element,
4682
5725
  returnValue: void 0,
4683
- returning: false
5726
+ returning: false,
5727
+ breaking: false,
5728
+ continuing: false
4684
5729
  };
4685
5730
  const previousValues = /* @__PURE__ */ new Map();
4686
5731
  await this.applyFunctionParams(callScope, declaration.params, previousValues, context, args);
@@ -4731,6 +5776,7 @@ var Engine = class _Engine {
4731
5776
  const context = { scope, rootScope, element };
4732
5777
  const operator = declaration.operator;
4733
5778
  const debounceMs = declaration.flags.debounce ? declaration.flagArgs.debounce ?? 200 : void 0;
5779
+ const transform = (value) => this.applyCustomFlagTransforms(value, element, scope, declaration);
4734
5780
  const importantKey = this.getImportantKey(declaration);
4735
5781
  if (!declaration.flags.important && importantKey && this.isImportant(element, importantKey)) {
4736
5782
  return;
@@ -4741,7 +5787,8 @@ var Engine = class _Engine {
4741
5787
  this.applyCustomFlags(element, scope, declaration);
4742
5788
  if (declaration.target instanceof IdentifierExpression) {
4743
5789
  const value = await declaration.value.evaluate(context);
4744
- scope.setPath(declaration.target.name, value);
5790
+ const transformed = this.applyCustomFlagTransforms(value, element, scope, declaration);
5791
+ scope.setPath(declaration.target.name, transformed);
4745
5792
  if (declaration.flags.important && importantKey) {
4746
5793
  this.markImportant(element, importantKey);
4747
5794
  }
@@ -4754,7 +5801,7 @@ var Engine = class _Engine {
4754
5801
  const exprIdentifier = declaration.value instanceof IdentifierExpression ? declaration.value.name : void 0;
4755
5802
  if (operator === ":>") {
4756
5803
  if (exprIdentifier) {
4757
- this.applyDirectiveToScope(element, target, exprIdentifier, scope, debounceMs, rootScope);
5804
+ this.applyDirectiveToScope(element, target, exprIdentifier, scope, debounceMs, rootScope, transform);
4758
5805
  }
4759
5806
  if (declaration.flags.important && importantKey) {
4760
5807
  this.markImportant(element, importantKey);
@@ -4762,11 +5809,12 @@ var Engine = class _Engine {
4762
5809
  return;
4763
5810
  }
4764
5811
  if (operator === ":=" && exprIdentifier) {
4765
- this.applyDirectiveToScope(element, target, exprIdentifier, scope, debounceMs, rootScope);
5812
+ this.applyDirectiveToScope(element, target, exprIdentifier, scope, debounceMs, rootScope, transform);
4766
5813
  }
4767
5814
  if (!exprIdentifier) {
4768
5815
  const value = await declaration.value.evaluate(context);
4769
- this.setDirectiveValue(element, target, value, declaration.flags.trusted);
5816
+ const transformed = this.applyCustomFlagTransforms(value, element, scope, declaration);
5817
+ this.setDirectiveValue(element, target, transformed, declaration.flags.trusted);
4770
5818
  const shouldWatch2 = operator === ":<" || operator === ":=";
4771
5819
  if (shouldWatch2) {
4772
5820
  this.applyDirectiveFromExpression(
@@ -4816,6 +5864,63 @@ var Engine = class _Engine {
4816
5864
  });
4817
5865
  }
4818
5866
  }
5867
+ applyCustomFlagTransforms(value, element, scope, declaration) {
5868
+ if (this.flagHandlers.size === 0) {
5869
+ return value;
5870
+ }
5871
+ let nextValue = value;
5872
+ for (const [name, handler] of this.flagHandlers) {
5873
+ if (!declaration.flags[name] || !handler.transformValue) {
5874
+ continue;
5875
+ }
5876
+ nextValue = handler.transformValue(
5877
+ {
5878
+ name,
5879
+ args: declaration.flagArgs[name],
5880
+ element,
5881
+ scope,
5882
+ declaration
5883
+ },
5884
+ nextValue
5885
+ );
5886
+ }
5887
+ return nextValue;
5888
+ }
5889
+ async applyBehaviorModifierHook(hook, behavior, element, scope, rootScope) {
5890
+ if (this.behaviorModifiers.size === 0) {
5891
+ return;
5892
+ }
5893
+ for (const [name, handler] of this.behaviorModifiers) {
5894
+ if (!behavior.flags?.[name]) {
5895
+ continue;
5896
+ }
5897
+ const callback = handler[hook];
5898
+ if (!callback) {
5899
+ continue;
5900
+ }
5901
+ await callback({
5902
+ name,
5903
+ args: behavior.flagArgs?.[name],
5904
+ element,
5905
+ scope,
5906
+ rootScope,
5907
+ behavior,
5908
+ engine: this
5909
+ });
5910
+ }
5911
+ }
5912
+ behaviorHasModifierHooks(behavior) {
5913
+ if (this.behaviorModifiers.size === 0) {
5914
+ return false;
5915
+ }
5916
+ const flags = behavior.flags ?? {};
5917
+ for (const name of Object.keys(flags)) {
5918
+ if (flags[name] && this.behaviorModifiers.has(name)) {
5919
+ return true;
5920
+ }
5921
+ }
5922
+ return false;
5923
+ }
4819
5924
  applyDirectiveFromScope(element, target, expr, scope, trusted, debounceMs, watch = true, rootScope) {
4820
5925
  if (target.kind === "attr" && target.name === "html" && element instanceof HTMLElement) {
4821
5926
  const handler2 = () => {
@@ -4832,7 +5937,7 @@ var Engine = class _Engine {
4832
5937
  const useRoot = expr.startsWith("root.") && rootScope;
4833
5938
  const sourceScope = useRoot ? rootScope : scope;
4834
5939
  const watchExpr = useRoot ? expr.slice("root.".length) : expr;
4835
- this.watchWithDebounce(sourceScope, watchExpr, handler2, debounceMs);
5940
+ this.watchWithDebounce(sourceScope, watchExpr, handler2, debounceMs, element);
4836
5941
  }
4837
5942
  return;
4838
5943
  }
@@ -4851,7 +5956,7 @@ var Engine = class _Engine {
4851
5956
  const useRoot = expr.startsWith("root.") && rootScope;
4852
5957
  const sourceScope = useRoot ? rootScope : scope;
4853
5958
  const watchExpr = useRoot ? expr.slice("root.".length) : expr;
4854
- this.watchWithDebounce(sourceScope, watchExpr, handler, debounceMs);
5959
+ this.watchWithDebounce(sourceScope, watchExpr, handler, debounceMs, element);
4855
5960
  }
4856
5961
  }
4857
5962
  applyDirectiveFromExpression(element, target, expr, scope, trusted, debounceMs, rootScope) {
@@ -4863,45 +5968,49 @@ var Engine = class _Engine {
4863
5968
  void handler();
4864
5969
  this.watchAllScopes(scope, () => {
4865
5970
  void handler();
4866
- }, debounceMs);
5971
+ }, debounceMs, element);
4867
5972
  }
4868
- applyDirectiveToScope(element, target, expr, scope, debounceMs, rootScope) {
5973
+ applyDirectiveToScope(element, target, expr, scope, debounceMs, rootScope, transform) {
4869
5974
  const useRoot = expr.startsWith("root.") && rootScope;
4870
5975
  const targetScope = useRoot ? rootScope : scope;
4871
5976
  const targetExpr = useRoot ? `self.${expr.slice("root.".length)}` : expr;
4872
5977
  if (target.kind === "attr" && target.name === "value") {
4873
- this.applyValueBindingToScope(element, targetExpr, debounceMs, targetScope);
5978
+ this.applyValueBindingToScope(element, targetExpr, debounceMs, targetScope, transform);
4874
5979
  return;
4875
5980
  }
4876
5981
  if (target.kind === "attr" && target.name === "checked") {
4877
- this.applyCheckedBindingToScope(element, targetExpr, debounceMs, targetScope);
5982
+ this.applyCheckedBindingToScope(element, targetExpr, debounceMs, targetScope, transform);
4878
5983
  return;
4879
5984
  }
4880
5985
  const value = this.getDirectiveValue(element, target);
4881
5986
  if (value != null) {
4882
- targetScope.set(targetExpr, value);
5987
+ const nextValue = transform ? transform(value) : value;
5988
+ targetScope.set(targetExpr, nextValue);
4883
5989
  }
4884
5990
  }
4885
- applyCheckedBindingToScope(element, expr, debounceMs, scope) {
5991
+ applyCheckedBindingToScope(element, expr, debounceMs, scope, transform) {
4886
5992
  if (!(element instanceof HTMLInputElement)) {
4887
5993
  return;
4888
5994
  }
4889
5995
  const handler = () => {
4890
5996
  const targetScope = scope ?? this.getScope(element);
4891
- targetScope.set(expr, element.checked);
5997
+ const value = transform ? transform(element.checked) : element.checked;
5998
+ targetScope.set(expr, value);
4892
5999
  };
4893
6000
  const effectiveHandler = debounceMs ? debounce(handler, debounceMs) : handler;
4894
6001
  effectiveHandler();
4895
6002
  element.addEventListener("change", effectiveHandler);
4896
6003
  element.addEventListener("input", effectiveHandler);
4897
6004
  }
4898
- applyValueBindingToScope(element, expr, debounceMs, scope) {
6005
+ applyValueBindingToScope(element, expr, debounceMs, scope, transform) {
4899
6006
  if (!(element instanceof HTMLInputElement || element instanceof HTMLTextAreaElement || element instanceof HTMLSelectElement)) {
4900
6007
  return;
4901
6008
  }
4902
6009
  const handler = () => {
4903
6010
  const targetScope = scope ?? this.getScope(element);
4904
- applyBindToScope(element, expr, targetScope);
6011
+ const value = element.value;
6012
+ const nextValue = transform ? transform(value) : value;
6013
+ targetScope.set(expr, nextValue);
4905
6014
  };
4906
6015
  const effectiveHandler = debounceMs ? debounce(handler, debounceMs) : handler;
4907
6016
  effectiveHandler();
@@ -4918,6 +6027,14 @@ var Engine = class _Engine {
4918
6027
  return;
4919
6028
  }
4920
6029
  if (target.kind === "attr") {
6030
+ if (target.name === "text" && element instanceof HTMLElement) {
6031
+ element.innerText = value == null ? "" : String(value);
6032
+ return;
6033
+ }
6034
+ if (target.name === "content" && element instanceof HTMLElement) {
6035
+ element.textContent = value == null ? "" : String(value);
6036
+ return;
6037
+ }
4921
6038
  if (target.name === "value") {
4922
6039
  if (element instanceof HTMLInputElement || element instanceof HTMLTextAreaElement) {
4923
6040
  element.value = value == null ? "" : String(value);
@@ -4948,6 +6065,12 @@ var Engine = class _Engine {
4948
6065
  }
4949
6066
  getDirectiveValue(element, target) {
4950
6067
  if (target.kind === "attr") {
6068
+ if (target.name === "text" && element instanceof HTMLElement) {
6069
+ return element.innerText;
6070
+ }
6071
+ if (target.name === "content" && element instanceof HTMLElement) {
6072
+ return element.textContent ?? "";
6073
+ }
4951
6074
  if (target.name === "value") {
4952
6075
  if (element instanceof HTMLInputElement || element instanceof HTMLTextAreaElement) {
4953
6076
  return element.value;
@@ -4983,17 +6106,27 @@ var Engine = class _Engine {
4983
6106
  id: "vsn-bind",
4984
6107
  match: (name) => name.startsWith("vsn-bind"),
4985
6108
  handle: (element, name, value, scope) => {
4986
- const direction = this.parseBindDirection(name);
4987
- this.bindBindings.set(element, { expr: value, direction });
4988
- if (direction === "to" || direction === "both") {
6109
+ const parsedDirection = this.parseBindDirection(name);
6110
+ const config = this.resolveBindConfig(element, value, scope, parsedDirection);
6111
+ const direction = config.direction;
6112
+ const auto = parsedDirection === "auto";
6113
+ this.bindBindings.set(element, { expr: value, direction, auto });
6114
+ if (!auto && (direction === "to" || direction === "both")) {
4989
6115
  this.markInlineDeclaration(element, `state:${value}`);
4990
6116
  }
4991
- if (direction === "to" || direction === "both") {
6117
+ if (config.seedFromScope) {
6118
+ applyBindToElement(element, value, scope);
6119
+ }
6120
+ if (config.deferToScope) {
6121
+ this.pendingAutoBindToScope.push({ element, expr: value, scope });
6122
+ } else if (config.syncToScope) {
4992
6123
  applyBindToScope(element, value, scope);
6124
+ }
6125
+ if (direction === "to" || direction === "both") {
4993
6126
  this.attachBindInputHandler(element, value);
4994
6127
  }
4995
6128
  if (direction === "from" || direction === "both") {
4996
- this.watch(scope, value, () => applyBindToElement(element, value, scope));
6129
+ this.watch(scope, value, () => applyBindToElement(element, value, scope), element);
4997
6130
  }
4998
6131
  }
4999
6132
  });
@@ -5005,7 +6138,7 @@ var Engine = class _Engine {
5005
6138
  if (element instanceof HTMLElement) {
5006
6139
  applyIf(element, value, scope);
5007
6140
  }
5008
- this.watch(scope, value, () => this.evaluate(element));
6141
+ this.watch(scope, value, () => this.evaluate(element), element);
5009
6142
  }
5010
6143
  });
5011
6144
  this.registerAttributeHandler({
@@ -5016,7 +6149,7 @@ var Engine = class _Engine {
5016
6149
  if (element instanceof HTMLElement) {
5017
6150
  applyShow(element, value, scope);
5018
6151
  }
5019
- this.watch(scope, value, () => this.evaluate(element));
6152
+ this.watch(scope, value, () => this.evaluate(element), element);
5020
6153
  }
5021
6154
  });
5022
6155
  this.registerAttributeHandler({
@@ -5032,7 +6165,7 @@ var Engine = class _Engine {
5032
6165
  this.handleTrustedHtml(element);
5033
6166
  }
5034
6167
  }
5035
- this.watch(scope, value, () => this.evaluate(element));
6168
+ this.watch(scope, value, () => this.evaluate(element), element);
5036
6169
  }
5037
6170
  });
5038
6171
  this.registerAttributeHandler({
@@ -5045,7 +6178,7 @@ var Engine = class _Engine {
5045
6178
  }
5046
6179
  this.eachBindings.set(element, { ...config, rendered: [] });
5047
6180
  this.renderEach(element);
5048
- this.watch(scope, config.listExpr, () => this.renderEach(element));
6181
+ this.watch(scope, config.listExpr, () => this.renderEach(element), element);
5049
6182
  }
5050
6183
  });
5051
6184
  this.registerAttributeHandler({
@@ -5100,15 +6233,27 @@ function parseCFS(source) {
5100
6233
  const parser = new Parser(source);
5101
6234
  return parser.parseProgram();
5102
6235
  }
6236
+ if (typeof window !== "undefined") {
6237
+ window["parseCFS"] = parseCFS;
6238
+ }
5103
6239
  function autoMount(root = document) {
5104
6240
  if (typeof document === "undefined") {
5105
6241
  return null;
5106
6242
  }
5107
6243
  const engine = new Engine();
6244
+ globalThis.VSNEngine = engine;
5108
6245
  const startTime = typeof performance !== "undefined" && performance.now ? performance.now() : Date.now();
5109
6246
  const mount = () => {
5110
6247
  const target = root instanceof Document ? root.body : root;
5111
6248
  if (target) {
6249
+ const plugins = globalThis.VSNPlugins;
6250
+ if (plugins && typeof plugins === "object") {
6251
+ for (const plugin of Object.values(plugins)) {
6252
+ if (typeof plugin === "function") {
6253
+ plugin(engine);
6254
+ }
6255
+ }
6256
+ }
5112
6257
  const sources = Array.from(document.querySelectorAll('script[type="text/vsn"]')).map((script) => script.textContent ?? "").join("\n");
5113
6258
  if (sources.trim()) {
5114
6259
  engine.registerBehaviors(sources);
@@ -5120,9 +6265,9 @@ function autoMount(root = document) {
5120
6265
  }
5121
6266
  };
5122
6267
  if (document.readyState === "loading") {
5123
- document.addEventListener("DOMContentLoaded", mount, { once: true });
6268
+ document.addEventListener("DOMContentLoaded", () => setTimeout(mount, 0), { once: true });
5124
6269
  } else {
5125
- mount();
6270
+ setTimeout(mount, 0);
5126
6271
  }
5127
6272
  return engine;
5128
6273
  }
@@ -5136,16 +6281,21 @@ if (typeof document !== "undefined") {
5136
6281
  0 && (module.exports = {
5137
6282
  ArrayExpression,
5138
6283
  ArrayPattern,
6284
+ AssertError,
6285
+ AssertNode,
5139
6286
  AssignmentNode,
5140
6287
  AwaitExpression,
5141
6288
  BaseNode,
5142
6289
  BehaviorNode,
5143
6290
  BinaryExpression,
5144
6291
  BlockNode,
6292
+ BreakNode,
5145
6293
  CallExpression,
6294
+ ContinueNode,
5146
6295
  DeclarationNode,
5147
6296
  DirectiveExpression,
5148
6297
  Engine,
6298
+ ForEachNode,
5149
6299
  ForNode,
5150
6300
  FunctionDeclarationNode,
5151
6301
  FunctionExpression,
@@ -5165,8 +6315,6 @@ if (typeof document !== "undefined") {
5165
6315
  ReturnNode,
5166
6316
  SelectorNode,
5167
6317
  SpreadElement,
5168
- StateBlockNode,
5169
- StateEntryNode,
5170
6318
  TemplateExpression,
5171
6319
  TernaryExpression,
5172
6320
  TokenType,