vsn 1.0.0 → 1.0.2

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.d.cts CHANGED
@@ -129,10 +129,21 @@ declare class ProgramNode extends BaseNode {
129
129
  uses: UseNode[];
130
130
  constructor(behaviors: BehaviorNode[], uses?: UseNode[]);
131
131
  }
132
+ interface UseFlags {
133
+ wait?: boolean;
134
+ }
135
+ interface UseFlagArgs {
136
+ wait?: {
137
+ timeoutMs?: number;
138
+ intervalMs?: number;
139
+ };
140
+ }
132
141
  declare class UseNode extends BaseNode {
133
142
  name: string;
134
143
  alias: string;
135
- constructor(name: string, alias: string);
144
+ flags: UseFlags;
145
+ flagArgs: UseFlagArgs;
146
+ constructor(name: string, alias: string, flags?: UseFlags, flagArgs?: UseFlagArgs);
136
147
  }
137
148
  declare class BlockNode extends BaseNode {
138
149
  statements: CFSNode[];
@@ -168,8 +179,13 @@ declare class OnBlockNode extends BaseNode {
168
179
  declare class AssignmentNode extends BaseNode {
169
180
  target: AssignmentTarget;
170
181
  value: ExpressionNode;
171
- constructor(target: AssignmentTarget, value: ExpressionNode);
182
+ operator: "=" | "+=" | "-=" | "*=" | "/=";
183
+ constructor(target: AssignmentTarget, value: ExpressionNode, operator?: "=" | "+=" | "-=" | "*=" | "/=");
172
184
  evaluate(context: ExecutionContext): Promise<any>;
185
+ private applyCompoundAssignment;
186
+ private resolveAssignmentTarget;
187
+ private resolveIndexPath;
188
+ private resolveTargetPath;
173
189
  private assignTarget;
174
190
  }
175
191
  declare class ReturnNode extends BaseNode {
@@ -241,7 +257,7 @@ declare class DeclarationNode extends BaseNode {
241
257
  }
242
258
  type ExpressionNode = IdentifierExpression | LiteralExpression | TemplateExpression | UnaryExpression | BinaryExpression | MemberExpression | CallExpression | ArrayExpression | ObjectExpression | IndexExpression | FunctionExpression | AwaitExpression | TernaryExpression | DirectiveExpression | QueryExpression;
243
259
  type DeclarationTarget = IdentifierExpression | DirectiveExpression;
244
- type AssignmentTarget = IdentifierExpression | DirectiveExpression | ArrayPattern | ObjectPattern;
260
+ type AssignmentTarget = IdentifierExpression | MemberExpression | IndexExpression | DirectiveExpression | ArrayPattern | ObjectPattern;
245
261
  type FunctionParam = {
246
262
  name: string;
247
263
  defaultValue?: ExpressionNode;
@@ -394,6 +410,7 @@ declare class Parser {
394
410
  private parseBehavior;
395
411
  private parseSelector;
396
412
  private parseUseStatement;
413
+ private parseUseFlags;
397
414
  private wrapErrors;
398
415
  private formatError;
399
416
  private getLineSnippet;
@@ -435,6 +452,7 @@ declare class Parser {
435
452
  private parseCustomFlagArg;
436
453
  private isDeclarationStart;
437
454
  private isAssignmentStart;
455
+ private isAssignmentOperatorStart;
438
456
  private isCallStart;
439
457
  private isExpressionStatementStart;
440
458
  private isFunctionDeclarationStart;
@@ -447,6 +465,7 @@ declare class Parser {
447
465
  private parseForBlock;
448
466
  private parseForClause;
449
467
  private parseAssignmentExpression;
468
+ private parseAssignmentOperator;
450
469
  private parseTryBlock;
451
470
  private parseConstructBlock;
452
471
  private parseDestructBlock;
@@ -532,6 +551,7 @@ declare class Engine {
532
551
  private ignoredAdded;
533
552
  private diagnostics;
534
553
  private logger;
554
+ private pendingUses;
535
555
  constructor(options?: EngineOptions);
536
556
  mount(root: HTMLElement): Promise<void>;
537
557
  unmount(element: Element): void;
@@ -545,6 +565,8 @@ declare class Engine {
545
565
  };
546
566
  registerAttributeHandler(handler: AttributeHandler): void;
547
567
  private resolveGlobalPath;
568
+ private waitForUses;
569
+ private waitForUseGlobal;
548
570
  getScope(element: Element, parentScope?: Scope): Scope;
549
571
  evaluate(element: Element): void;
550
572
  private attachObserver;
@@ -580,6 +602,7 @@ declare class Engine {
580
602
  private describeElement;
581
603
  private logDiagnostic;
582
604
  private emitError;
605
+ private emitUseError;
583
606
  private attachOnHandler;
584
607
  private attachBehaviorOnHandler;
585
608
  private attachGetHandler;
@@ -627,4 +650,4 @@ declare const VERSION = "0.1.0";
627
650
  declare function parseCFS(source: string): ProgramNode;
628
651
  declare function autoMount(root?: HTMLElement | Document): Engine | null;
629
652
 
630
- export { type ArrayElement, ArrayExpression, ArrayPattern, type ArrayPatternElement, AssignmentNode, type AssignmentTarget, AwaitExpression, BaseNode, BehaviorNode, BinaryExpression, BlockNode, type CFSNode, CallExpression, type DeclarationFlagArgs, type DeclarationFlags, DeclarationNode, type DeclarationTarget, DirectiveExpression, Engine, type ExecutionContext, type ExpressionNode, ForNode, FunctionDeclarationNode, FunctionExpression, type FunctionParam, IdentifierExpression, IfNode, IndexExpression, Lexer, LiteralExpression, MemberExpression, type ObjectEntry, ObjectExpression, ObjectPattern, type ObjectPatternEntry, OnBlockNode, Parser, type PatternNode, ProgramNode, QueryExpression, RestElement, ReturnNode, SelectorNode, SpreadElement, StateBlockNode, StateEntryNode, TemplateExpression, TernaryExpression, TokenType, TryNode, UnaryExpression, UseNode, VERSION, WhileNode, autoMount, parseCFS };
653
+ export { type ArrayElement, ArrayExpression, ArrayPattern, type ArrayPatternElement, AssignmentNode, type AssignmentTarget, AwaitExpression, BaseNode, BehaviorNode, BinaryExpression, BlockNode, type CFSNode, CallExpression, type DeclarationFlagArgs, type DeclarationFlags, DeclarationNode, type DeclarationTarget, DirectiveExpression, Engine, type ExecutionContext, type ExpressionNode, ForNode, FunctionDeclarationNode, FunctionExpression, type FunctionParam, IdentifierExpression, IfNode, IndexExpression, Lexer, LiteralExpression, MemberExpression, type ObjectEntry, ObjectExpression, ObjectPattern, type ObjectPatternEntry, OnBlockNode, Parser, type PatternNode, ProgramNode, QueryExpression, RestElement, ReturnNode, SelectorNode, SpreadElement, StateBlockNode, StateEntryNode, TemplateExpression, TernaryExpression, TokenType, TryNode, UnaryExpression, type UseFlagArgs, type UseFlags, UseNode, VERSION, WhileNode, autoMount, parseCFS };
package/dist/index.d.ts CHANGED
@@ -129,10 +129,21 @@ declare class ProgramNode extends BaseNode {
129
129
  uses: UseNode[];
130
130
  constructor(behaviors: BehaviorNode[], uses?: UseNode[]);
131
131
  }
132
+ interface UseFlags {
133
+ wait?: boolean;
134
+ }
135
+ interface UseFlagArgs {
136
+ wait?: {
137
+ timeoutMs?: number;
138
+ intervalMs?: number;
139
+ };
140
+ }
132
141
  declare class UseNode extends BaseNode {
133
142
  name: string;
134
143
  alias: string;
135
- constructor(name: string, alias: string);
144
+ flags: UseFlags;
145
+ flagArgs: UseFlagArgs;
146
+ constructor(name: string, alias: string, flags?: UseFlags, flagArgs?: UseFlagArgs);
136
147
  }
137
148
  declare class BlockNode extends BaseNode {
138
149
  statements: CFSNode[];
@@ -168,8 +179,13 @@ declare class OnBlockNode extends BaseNode {
168
179
  declare class AssignmentNode extends BaseNode {
169
180
  target: AssignmentTarget;
170
181
  value: ExpressionNode;
171
- constructor(target: AssignmentTarget, value: ExpressionNode);
182
+ operator: "=" | "+=" | "-=" | "*=" | "/=";
183
+ constructor(target: AssignmentTarget, value: ExpressionNode, operator?: "=" | "+=" | "-=" | "*=" | "/=");
172
184
  evaluate(context: ExecutionContext): Promise<any>;
185
+ private applyCompoundAssignment;
186
+ private resolveAssignmentTarget;
187
+ private resolveIndexPath;
188
+ private resolveTargetPath;
173
189
  private assignTarget;
174
190
  }
175
191
  declare class ReturnNode extends BaseNode {
@@ -241,7 +257,7 @@ declare class DeclarationNode extends BaseNode {
241
257
  }
242
258
  type ExpressionNode = IdentifierExpression | LiteralExpression | TemplateExpression | UnaryExpression | BinaryExpression | MemberExpression | CallExpression | ArrayExpression | ObjectExpression | IndexExpression | FunctionExpression | AwaitExpression | TernaryExpression | DirectiveExpression | QueryExpression;
243
259
  type DeclarationTarget = IdentifierExpression | DirectiveExpression;
244
- type AssignmentTarget = IdentifierExpression | DirectiveExpression | ArrayPattern | ObjectPattern;
260
+ type AssignmentTarget = IdentifierExpression | MemberExpression | IndexExpression | DirectiveExpression | ArrayPattern | ObjectPattern;
245
261
  type FunctionParam = {
246
262
  name: string;
247
263
  defaultValue?: ExpressionNode;
@@ -394,6 +410,7 @@ declare class Parser {
394
410
  private parseBehavior;
395
411
  private parseSelector;
396
412
  private parseUseStatement;
413
+ private parseUseFlags;
397
414
  private wrapErrors;
398
415
  private formatError;
399
416
  private getLineSnippet;
@@ -435,6 +452,7 @@ declare class Parser {
435
452
  private parseCustomFlagArg;
436
453
  private isDeclarationStart;
437
454
  private isAssignmentStart;
455
+ private isAssignmentOperatorStart;
438
456
  private isCallStart;
439
457
  private isExpressionStatementStart;
440
458
  private isFunctionDeclarationStart;
@@ -447,6 +465,7 @@ declare class Parser {
447
465
  private parseForBlock;
448
466
  private parseForClause;
449
467
  private parseAssignmentExpression;
468
+ private parseAssignmentOperator;
450
469
  private parseTryBlock;
451
470
  private parseConstructBlock;
452
471
  private parseDestructBlock;
@@ -532,6 +551,7 @@ declare class Engine {
532
551
  private ignoredAdded;
533
552
  private diagnostics;
534
553
  private logger;
554
+ private pendingUses;
535
555
  constructor(options?: EngineOptions);
536
556
  mount(root: HTMLElement): Promise<void>;
537
557
  unmount(element: Element): void;
@@ -545,6 +565,8 @@ declare class Engine {
545
565
  };
546
566
  registerAttributeHandler(handler: AttributeHandler): void;
547
567
  private resolveGlobalPath;
568
+ private waitForUses;
569
+ private waitForUseGlobal;
548
570
  getScope(element: Element, parentScope?: Scope): Scope;
549
571
  evaluate(element: Element): void;
550
572
  private attachObserver;
@@ -580,6 +602,7 @@ declare class Engine {
580
602
  private describeElement;
581
603
  private logDiagnostic;
582
604
  private emitError;
605
+ private emitUseError;
583
606
  private attachOnHandler;
584
607
  private attachBehaviorOnHandler;
585
608
  private attachGetHandler;
@@ -627,4 +650,4 @@ declare const VERSION = "0.1.0";
627
650
  declare function parseCFS(source: string): ProgramNode;
628
651
  declare function autoMount(root?: HTMLElement | Document): Engine | null;
629
652
 
630
- export { type ArrayElement, ArrayExpression, ArrayPattern, type ArrayPatternElement, AssignmentNode, type AssignmentTarget, AwaitExpression, BaseNode, BehaviorNode, BinaryExpression, BlockNode, type CFSNode, CallExpression, type DeclarationFlagArgs, type DeclarationFlags, DeclarationNode, type DeclarationTarget, DirectiveExpression, Engine, type ExecutionContext, type ExpressionNode, ForNode, FunctionDeclarationNode, FunctionExpression, type FunctionParam, IdentifierExpression, IfNode, IndexExpression, Lexer, LiteralExpression, MemberExpression, type ObjectEntry, ObjectExpression, ObjectPattern, type ObjectPatternEntry, OnBlockNode, Parser, type PatternNode, ProgramNode, QueryExpression, RestElement, ReturnNode, SelectorNode, SpreadElement, StateBlockNode, StateEntryNode, TemplateExpression, TernaryExpression, TokenType, TryNode, UnaryExpression, UseNode, VERSION, WhileNode, autoMount, parseCFS };
653
+ export { type ArrayElement, ArrayExpression, ArrayPattern, type ArrayPatternElement, AssignmentNode, type AssignmentTarget, AwaitExpression, BaseNode, BehaviorNode, BinaryExpression, BlockNode, type CFSNode, CallExpression, type DeclarationFlagArgs, type DeclarationFlags, DeclarationNode, type DeclarationTarget, DirectiveExpression, Engine, type ExecutionContext, type ExpressionNode, ForNode, FunctionDeclarationNode, FunctionExpression, type FunctionParam, IdentifierExpression, IfNode, IndexExpression, Lexer, LiteralExpression, MemberExpression, type ObjectEntry, ObjectExpression, ObjectPattern, type ObjectPatternEntry, OnBlockNode, Parser, type PatternNode, ProgramNode, QueryExpression, RestElement, ReturnNode, SelectorNode, SpreadElement, StateBlockNode, StateEntryNode, TemplateExpression, TernaryExpression, TokenType, TryNode, UnaryExpression, type UseFlagArgs, type UseFlags, UseNode, VERSION, WhileNode, autoMount, parseCFS };
package/dist/index.js CHANGED
@@ -445,10 +445,12 @@ var ProgramNode = class extends BaseNode {
445
445
  }
446
446
  };
447
447
  var UseNode = class extends BaseNode {
448
- constructor(name, alias) {
448
+ constructor(name, alias, flags = {}, flagArgs = {}) {
449
449
  super("Use");
450
450
  this.name = name;
451
451
  this.alias = alias;
452
+ this.flags = flags;
453
+ this.flagArgs = flagArgs;
452
454
  }
453
455
  };
454
456
  var BlockNode = class extends BaseNode {
@@ -504,24 +506,117 @@ var OnBlockNode = class extends BaseNode {
504
506
  }
505
507
  };
506
508
  var AssignmentNode = class extends BaseNode {
507
- constructor(target, value) {
509
+ constructor(target, value, operator = "=") {
508
510
  super("Assignment");
509
511
  this.target = target;
510
512
  this.value = value;
513
+ this.operator = operator;
511
514
  }
512
515
  async evaluate(context) {
513
516
  if (!context.scope || !context.scope.setPath) {
514
517
  return void 0;
515
518
  }
516
519
  const value = await this.value.evaluate(context);
520
+ if (this.operator !== "=") {
521
+ return await this.applyCompoundAssignment(context, value);
522
+ }
517
523
  if (this.target instanceof IdentifierExpression && this.target.name.startsWith("root.") && context.rootScope) {
518
524
  const path = this.target.name.slice("root.".length);
519
- context.rootScope.setPath?.(`self.${path}`, value);
525
+ context.rootScope.setPath?.(path, value);
520
526
  return value;
521
527
  }
528
+ if (this.target instanceof MemberExpression || this.target instanceof IndexExpression) {
529
+ const resolved = await this.resolveAssignmentTarget(context);
530
+ if (resolved?.scope?.setPath) {
531
+ resolved.scope.setPath(resolved.path, value);
532
+ return value;
533
+ }
534
+ }
522
535
  this.assignTarget(context, this.target, value);
523
536
  return value;
524
537
  }
538
+ async applyCompoundAssignment(context, value) {
539
+ if (!context.scope || !context.scope.setPath) {
540
+ return void 0;
541
+ }
542
+ const resolved = await this.resolveAssignmentTarget(context);
543
+ if (!resolved) {
544
+ throw new Error("Compound assignment requires a simple identifier or member path");
545
+ }
546
+ const { scope, path } = resolved;
547
+ const current = scope?.getPath ? scope.getPath(path) : void 0;
548
+ let result;
549
+ if (this.operator === "+=") {
550
+ result = current + value;
551
+ } else if (this.operator === "-=") {
552
+ result = current - value;
553
+ } else if (this.operator === "*=") {
554
+ result = current * value;
555
+ } else {
556
+ result = current / value;
557
+ }
558
+ scope?.setPath?.(path, result);
559
+ return result;
560
+ }
561
+ async resolveAssignmentTarget(context) {
562
+ if (this.target instanceof IdentifierExpression) {
563
+ const isRoot = this.target.name.startsWith("root.");
564
+ const rawPath = isRoot ? this.target.name.slice("root.".length) : this.target.name;
565
+ if (isRoot) {
566
+ return { scope: context.scope, path: `root.${rawPath}` };
567
+ }
568
+ return { scope: context.scope, path: rawPath };
569
+ }
570
+ if (this.target instanceof MemberExpression) {
571
+ const resolvedPath = this.target.getIdentifierPath();
572
+ if (!resolvedPath) {
573
+ return null;
574
+ }
575
+ const path = resolvedPath.path;
576
+ const isRoot = path.startsWith("root.");
577
+ const rawPath = isRoot ? path.slice("root.".length) : path;
578
+ if (isRoot) {
579
+ return { scope: context.scope, path: `root.${rawPath}` };
580
+ }
581
+ return { scope: context.scope, path: rawPath };
582
+ }
583
+ if (this.target instanceof IndexExpression) {
584
+ const path = await this.resolveIndexPath(context, this.target);
585
+ if (!path) {
586
+ return null;
587
+ }
588
+ const isRoot = path.startsWith("root.");
589
+ const rawPath = isRoot ? path.slice("root.".length) : path;
590
+ if (isRoot) {
591
+ return { scope: context.scope, path: `root.${rawPath}` };
592
+ }
593
+ return { scope: context.scope, path: rawPath };
594
+ }
595
+ return null;
596
+ }
597
+ async resolveIndexPath(context, expr) {
598
+ const base = await this.resolveTargetPath(context, expr.target);
599
+ if (!base) {
600
+ return null;
601
+ }
602
+ const indexValue = await expr.index.evaluate(context);
603
+ if (indexValue == null) {
604
+ return null;
605
+ }
606
+ return `${base}.${indexValue}`;
607
+ }
608
+ async resolveTargetPath(context, target) {
609
+ if (target instanceof IdentifierExpression) {
610
+ return target.name;
611
+ }
612
+ if (target instanceof MemberExpression) {
613
+ return target.getIdentifierPath()?.path ?? null;
614
+ }
615
+ if (target instanceof IndexExpression) {
616
+ return this.resolveIndexPath(context, target);
617
+ }
618
+ return null;
619
+ }
525
620
  assignTarget(context, target, value) {
526
621
  if (!context.scope || !context.scope.setPath) {
527
622
  return;
@@ -1392,11 +1487,46 @@ var Parser = class _Parser {
1392
1487
  this.stream.skipWhitespace();
1393
1488
  alias = this.stream.expect("Identifier" /* Identifier */).value;
1394
1489
  }
1490
+ const { flags, flagArgs } = this.parseUseFlags();
1395
1491
  this.stream.skipWhitespace();
1396
1492
  this.stream.expect("Semicolon" /* Semicolon */);
1397
- return new UseNode(name, alias);
1493
+ return new UseNode(name, alias, flags, flagArgs);
1398
1494
  });
1399
1495
  }
1496
+ parseUseFlags() {
1497
+ const flags = {};
1498
+ const flagArgs = {};
1499
+ while (true) {
1500
+ this.stream.skipWhitespace();
1501
+ if (this.stream.peek()?.type !== "Bang" /* Bang */) {
1502
+ break;
1503
+ }
1504
+ this.stream.next();
1505
+ const name = this.stream.expect("Identifier" /* Identifier */).value;
1506
+ if (name !== "wait") {
1507
+ throw new Error(`Unknown flag ${name}`);
1508
+ }
1509
+ flags.wait = true;
1510
+ if (this.stream.peek()?.type === "LParen" /* LParen */) {
1511
+ this.stream.next();
1512
+ this.stream.skipWhitespace();
1513
+ const timeoutToken = this.stream.expect("Number" /* Number */);
1514
+ const timeoutMs = Number(timeoutToken.value);
1515
+ let intervalMs;
1516
+ this.stream.skipWhitespace();
1517
+ if (this.stream.peek()?.type === "Comma" /* Comma */) {
1518
+ this.stream.next();
1519
+ this.stream.skipWhitespace();
1520
+ const intervalToken = this.stream.expect("Number" /* Number */);
1521
+ intervalMs = Number(intervalToken.value);
1522
+ this.stream.skipWhitespace();
1523
+ }
1524
+ this.stream.expect("RParen" /* RParen */);
1525
+ flagArgs.wait = { timeoutMs, ...intervalMs !== void 0 ? { intervalMs } : {} };
1526
+ }
1527
+ }
1528
+ return { flags, flagArgs };
1529
+ }
1400
1530
  wrapErrors(fn) {
1401
1531
  try {
1402
1532
  return fn();
@@ -1627,11 +1757,11 @@ ${caret}`;
1627
1757
  parseAssignment() {
1628
1758
  const target = this.parseAssignmentTarget();
1629
1759
  this.stream.skipWhitespace();
1630
- this.stream.expect("Equals" /* Equals */);
1760
+ const operator = this.parseAssignmentOperator();
1631
1761
  this.stream.skipWhitespace();
1632
1762
  const value = this.parseExpression();
1633
1763
  this.consumeStatementTerminator();
1634
- return new AssignmentNode(target, value);
1764
+ return new AssignmentNode(target, value, operator);
1635
1765
  }
1636
1766
  parseExpression() {
1637
1767
  return this.parsePipeExpression();
@@ -2213,7 +2343,14 @@ ${caret}`;
2213
2343
  return this.parseObjectPattern();
2214
2344
  }
2215
2345
  if (token.type === "Identifier" /* Identifier */) {
2216
- return new IdentifierExpression(this.parseIdentifierPath());
2346
+ const expr = this.parseCallExpression();
2347
+ if (expr instanceof CallExpression) {
2348
+ throw new Error("Invalid assignment target CallExpression");
2349
+ }
2350
+ if (expr instanceof IdentifierExpression || expr instanceof MemberExpression || expr instanceof IndexExpression) {
2351
+ return expr;
2352
+ }
2353
+ throw new Error("Invalid assignment target");
2217
2354
  }
2218
2355
  throw new Error(`Invalid assignment target ${token.type}`);
2219
2356
  }
@@ -2492,12 +2629,31 @@ ${caret}`;
2492
2629
  while (this.stream.peekNonWhitespace(index)?.type === "Dot" /* Dot */ && this.stream.peekNonWhitespace(index + 1)?.type === "Identifier" /* Identifier */) {
2493
2630
  index += 2;
2494
2631
  }
2495
- return this.stream.peekNonWhitespace(index)?.type === "Equals" /* Equals */;
2632
+ while (this.stream.peekNonWhitespace(index)?.type === "LBracket" /* LBracket */) {
2633
+ let depth = 0;
2634
+ while (true) {
2635
+ const token = this.stream.peekNonWhitespace(index);
2636
+ if (!token) {
2637
+ return false;
2638
+ }
2639
+ if (token.type === "LBracket" /* LBracket */) {
2640
+ depth += 1;
2641
+ } else if (token.type === "RBracket" /* RBracket */) {
2642
+ depth -= 1;
2643
+ if (depth === 0) {
2644
+ index += 1;
2645
+ break;
2646
+ }
2647
+ }
2648
+ index += 1;
2649
+ }
2650
+ }
2651
+ return this.isAssignmentOperatorStart(index);
2496
2652
  }
2497
2653
  if (first.type === "At" /* At */ || first.type === "Dollar" /* Dollar */) {
2498
2654
  const second = this.stream.peekNonWhitespace(1);
2499
2655
  const third = this.stream.peekNonWhitespace(2);
2500
- return second?.type === "Identifier" /* Identifier */ && third?.type === "Equals" /* Equals */;
2656
+ return second?.type === "Identifier" /* Identifier */ && this.isAssignmentOperatorStart(2);
2501
2657
  }
2502
2658
  if (first.type === "LBrace" /* LBrace */ || first.type === "LBracket" /* LBracket */) {
2503
2659
  const stack = [];
@@ -2512,7 +2668,7 @@ ${caret}`;
2512
2668
  } else if (token.type === "RBrace" /* RBrace */ || token.type === "RBracket" /* RBracket */) {
2513
2669
  stack.pop();
2514
2670
  if (stack.length === 0) {
2515
- return this.stream.peekNonWhitespace(index + 1)?.type === "Equals" /* Equals */;
2671
+ return this.isAssignmentOperatorStart(index + 1);
2516
2672
  }
2517
2673
  }
2518
2674
  index += 1;
@@ -2520,6 +2676,20 @@ ${caret}`;
2520
2676
  }
2521
2677
  return false;
2522
2678
  }
2679
+ isAssignmentOperatorStart(index) {
2680
+ const token = this.stream.peekNonWhitespace(index);
2681
+ if (!token) {
2682
+ return false;
2683
+ }
2684
+ if (token.type === "Equals" /* Equals */) {
2685
+ return true;
2686
+ }
2687
+ if (token.type === "Plus" /* Plus */ || token.type === "Minus" /* Minus */ || token.type === "Star" /* Star */ || token.type === "Slash" /* Slash */) {
2688
+ const next = this.stream.peekNonWhitespace(index + 1);
2689
+ return next?.type === "Equals" /* Equals */;
2690
+ }
2691
+ return false;
2692
+ }
2523
2693
  isCallStart() {
2524
2694
  const first = this.stream.peekNonWhitespace(0);
2525
2695
  if (!first || first.type !== "Identifier" /* Identifier */) {
@@ -2744,10 +2914,35 @@ ${caret}`;
2744
2914
  parseAssignmentExpression() {
2745
2915
  const target = this.parseAssignmentTarget();
2746
2916
  this.stream.skipWhitespace();
2747
- this.stream.expect("Equals" /* Equals */);
2917
+ const operator = this.parseAssignmentOperator();
2748
2918
  this.stream.skipWhitespace();
2749
2919
  const value = this.parseExpression();
2750
- return new AssignmentNode(target, value);
2920
+ return new AssignmentNode(target, value, operator);
2921
+ }
2922
+ parseAssignmentOperator() {
2923
+ const next = this.stream.peek();
2924
+ if (!next) {
2925
+ throw new Error("Expected assignment operator");
2926
+ }
2927
+ if (next.type === "Equals" /* Equals */) {
2928
+ this.stream.next();
2929
+ return "=";
2930
+ }
2931
+ if (next.type === "Plus" /* Plus */ || next.type === "Minus" /* Minus */ || next.type === "Star" /* Star */ || next.type === "Slash" /* Slash */) {
2932
+ const op = this.stream.next();
2933
+ this.stream.expect("Equals" /* Equals */);
2934
+ if (op.type === "Plus" /* Plus */) {
2935
+ return "+=";
2936
+ }
2937
+ if (op.type === "Minus" /* Minus */) {
2938
+ return "-=";
2939
+ }
2940
+ if (op.type === "Star" /* Star */) {
2941
+ return "*=";
2942
+ }
2943
+ return "/=";
2944
+ }
2945
+ throw new Error("Expected assignment operator");
2751
2946
  }
2752
2947
  parseTryBlock() {
2753
2948
  this.stream.expect("Try" /* Try */);
@@ -3250,6 +3445,7 @@ var Engine = class _Engine {
3250
3445
  ignoredAdded = /* @__PURE__ */ new WeakMap();
3251
3446
  diagnostics;
3252
3447
  logger;
3448
+ pendingUses = [];
3253
3449
  constructor(options = {}) {
3254
3450
  this.diagnostics = options.diagnostics ?? false;
3255
3451
  this.logger = options.logger ?? console;
@@ -3319,6 +3515,10 @@ var Engine = class _Engine {
3319
3515
  registerBehaviors(source) {
3320
3516
  const program = new Parser(source, { customFlags: new Set(this.flagHandlers.keys()) }).parseProgram();
3321
3517
  for (const use of program.uses) {
3518
+ if (use.flags?.wait) {
3519
+ this.pendingUses.push(this.waitForUseGlobal(use));
3520
+ continue;
3521
+ }
3322
3522
  const value = this.resolveGlobalPath(use.name);
3323
3523
  if (value === void 0) {
3324
3524
  console.warn(`vsn: global '${use.name}' not found`);
@@ -3368,6 +3568,52 @@ var Engine = class _Engine {
3368
3568
  }
3369
3569
  return value;
3370
3570
  }
3571
+ async waitForUses() {
3572
+ while (this.pendingUses.length > 0) {
3573
+ const pending = this.pendingUses;
3574
+ this.pendingUses = [];
3575
+ await Promise.all(pending);
3576
+ }
3577
+ }
3578
+ waitForUseGlobal(use) {
3579
+ const config = use.flagArgs?.wait ?? {};
3580
+ const timeoutMs = config.timeoutMs ?? 1e4;
3581
+ const initialDelayMs = config.intervalMs ?? 100;
3582
+ const maxDelayMs = 1e3;
3583
+ const existing = this.resolveGlobalPath(use.name);
3584
+ if (existing !== void 0) {
3585
+ this.registerGlobal(use.alias, existing);
3586
+ return Promise.resolve();
3587
+ }
3588
+ if (timeoutMs <= 0) {
3589
+ this.emitUseError(use.name, new Error(`vsn: global '${use.name}' not found`));
3590
+ return Promise.resolve();
3591
+ }
3592
+ return new Promise((resolve) => {
3593
+ let elapsedMs = 0;
3594
+ let delayMs = initialDelayMs;
3595
+ const check = () => {
3596
+ const value = this.resolveGlobalPath(use.name);
3597
+ if (value !== void 0) {
3598
+ this.registerGlobal(use.alias, value);
3599
+ resolve();
3600
+ return;
3601
+ }
3602
+ if (elapsedMs >= timeoutMs) {
3603
+ this.emitUseError(use.name, new Error(`vsn: global '${use.name}' not found`));
3604
+ resolve();
3605
+ return;
3606
+ }
3607
+ const scheduledDelay = Math.min(delayMs, timeoutMs - elapsedMs);
3608
+ setTimeout(() => {
3609
+ elapsedMs += scheduledDelay;
3610
+ delayMs = Math.min(delayMs * 2, maxDelayMs);
3611
+ check();
3612
+ }, scheduledDelay);
3613
+ };
3614
+ check();
3615
+ });
3616
+ }
3371
3617
  getScope(element, parentScope) {
3372
3618
  const existing = this.scopes.get(element);
3373
3619
  if (existing) {
@@ -3491,6 +3737,7 @@ var Engine = class _Engine {
3491
3737
  }
3492
3738
  }
3493
3739
  async applyBehaviors(root) {
3740
+ await this.waitForUses();
3494
3741
  if (this.behaviorRegistry.length === 0) {
3495
3742
  return;
3496
3743
  }
@@ -3898,6 +4145,19 @@ var Engine = class _Engine {
3898
4145
  })
3899
4146
  );
3900
4147
  }
4148
+ emitUseError(name, error) {
4149
+ const selector = `use:${name}`;
4150
+ this.logger.warn?.("vsn:error", { error, selector });
4151
+ const target = globalThis.document;
4152
+ if (target && typeof target.dispatchEvent === "function") {
4153
+ target.dispatchEvent(
4154
+ new CustomEvent("vsn:error", {
4155
+ detail: { error, selector },
4156
+ bubbles: true
4157
+ })
4158
+ );
4159
+ }
4160
+ }
3901
4161
  attachOnHandler(element, config) {
3902
4162
  const options = this.getListenerOptions(config.modifiers);
3903
4163
  const listenerTarget = config.modifiers?.includes("outside") ? element.ownerDocument : element;