vsn 1.0.0 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +180 -10
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +23 -3
- package/dist/index.d.ts +23 -3
- package/dist/index.js +180 -10
- package/dist/index.js.map +1 -1
- package/dist/index.min.js +4 -4
- package/dist/index.min.js.map +1 -1
- package/package.json +15 -1
package/dist/index.cjs
CHANGED
|
@@ -513,10 +513,12 @@ var ProgramNode = class extends BaseNode {
|
|
|
513
513
|
}
|
|
514
514
|
};
|
|
515
515
|
var UseNode = class extends BaseNode {
|
|
516
|
-
constructor(name, alias) {
|
|
516
|
+
constructor(name, alias, flags = {}, flagArgs = {}) {
|
|
517
517
|
super("Use");
|
|
518
518
|
this.name = name;
|
|
519
519
|
this.alias = alias;
|
|
520
|
+
this.flags = flags;
|
|
521
|
+
this.flagArgs = flagArgs;
|
|
520
522
|
}
|
|
521
523
|
};
|
|
522
524
|
var BlockNode = class extends BaseNode {
|
|
@@ -572,16 +574,20 @@ var OnBlockNode = class extends BaseNode {
|
|
|
572
574
|
}
|
|
573
575
|
};
|
|
574
576
|
var AssignmentNode = class extends BaseNode {
|
|
575
|
-
constructor(target, value) {
|
|
577
|
+
constructor(target, value, operator = "=") {
|
|
576
578
|
super("Assignment");
|
|
577
579
|
this.target = target;
|
|
578
580
|
this.value = value;
|
|
581
|
+
this.operator = operator;
|
|
579
582
|
}
|
|
580
583
|
async evaluate(context) {
|
|
581
584
|
if (!context.scope || !context.scope.setPath) {
|
|
582
585
|
return void 0;
|
|
583
586
|
}
|
|
584
587
|
const value = await this.value.evaluate(context);
|
|
588
|
+
if (this.operator !== "=") {
|
|
589
|
+
return this.applyCompoundAssignment(context, value);
|
|
590
|
+
}
|
|
585
591
|
if (this.target instanceof IdentifierExpression && this.target.name.startsWith("root.") && context.rootScope) {
|
|
586
592
|
const path = this.target.name.slice("root.".length);
|
|
587
593
|
context.rootScope.setPath?.(`self.${path}`, value);
|
|
@@ -590,6 +596,31 @@ var AssignmentNode = class extends BaseNode {
|
|
|
590
596
|
this.assignTarget(context, this.target, value);
|
|
591
597
|
return value;
|
|
592
598
|
}
|
|
599
|
+
applyCompoundAssignment(context, value) {
|
|
600
|
+
if (!context.scope || !context.scope.setPath) {
|
|
601
|
+
return void 0;
|
|
602
|
+
}
|
|
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;
|
|
620
|
+
}
|
|
621
|
+
scope?.setPath?.(path, result);
|
|
622
|
+
return result;
|
|
623
|
+
}
|
|
593
624
|
assignTarget(context, target, value) {
|
|
594
625
|
if (!context.scope || !context.scope.setPath) {
|
|
595
626
|
return;
|
|
@@ -1460,11 +1491,46 @@ var Parser = class _Parser {
|
|
|
1460
1491
|
this.stream.skipWhitespace();
|
|
1461
1492
|
alias = this.stream.expect("Identifier" /* Identifier */).value;
|
|
1462
1493
|
}
|
|
1494
|
+
const { flags, flagArgs } = this.parseUseFlags();
|
|
1463
1495
|
this.stream.skipWhitespace();
|
|
1464
1496
|
this.stream.expect("Semicolon" /* Semicolon */);
|
|
1465
|
-
return new UseNode(name, alias);
|
|
1497
|
+
return new UseNode(name, alias, flags, flagArgs);
|
|
1466
1498
|
});
|
|
1467
1499
|
}
|
|
1500
|
+
parseUseFlags() {
|
|
1501
|
+
const flags = {};
|
|
1502
|
+
const flagArgs = {};
|
|
1503
|
+
while (true) {
|
|
1504
|
+
this.stream.skipWhitespace();
|
|
1505
|
+
if (this.stream.peek()?.type !== "Bang" /* Bang */) {
|
|
1506
|
+
break;
|
|
1507
|
+
}
|
|
1508
|
+
this.stream.next();
|
|
1509
|
+
const name = this.stream.expect("Identifier" /* Identifier */).value;
|
|
1510
|
+
if (name !== "wait") {
|
|
1511
|
+
throw new Error(`Unknown flag ${name}`);
|
|
1512
|
+
}
|
|
1513
|
+
flags.wait = true;
|
|
1514
|
+
if (this.stream.peek()?.type === "LParen" /* LParen */) {
|
|
1515
|
+
this.stream.next();
|
|
1516
|
+
this.stream.skipWhitespace();
|
|
1517
|
+
const timeoutToken = this.stream.expect("Number" /* Number */);
|
|
1518
|
+
const timeoutMs = Number(timeoutToken.value);
|
|
1519
|
+
let intervalMs;
|
|
1520
|
+
this.stream.skipWhitespace();
|
|
1521
|
+
if (this.stream.peek()?.type === "Comma" /* Comma */) {
|
|
1522
|
+
this.stream.next();
|
|
1523
|
+
this.stream.skipWhitespace();
|
|
1524
|
+
const intervalToken = this.stream.expect("Number" /* Number */);
|
|
1525
|
+
intervalMs = Number(intervalToken.value);
|
|
1526
|
+
this.stream.skipWhitespace();
|
|
1527
|
+
}
|
|
1528
|
+
this.stream.expect("RParen" /* RParen */);
|
|
1529
|
+
flagArgs.wait = { timeoutMs, ...intervalMs !== void 0 ? { intervalMs } : {} };
|
|
1530
|
+
}
|
|
1531
|
+
}
|
|
1532
|
+
return { flags, flagArgs };
|
|
1533
|
+
}
|
|
1468
1534
|
wrapErrors(fn) {
|
|
1469
1535
|
try {
|
|
1470
1536
|
return fn();
|
|
@@ -1695,11 +1761,11 @@ ${caret}`;
|
|
|
1695
1761
|
parseAssignment() {
|
|
1696
1762
|
const target = this.parseAssignmentTarget();
|
|
1697
1763
|
this.stream.skipWhitespace();
|
|
1698
|
-
this.
|
|
1764
|
+
const operator = this.parseAssignmentOperator();
|
|
1699
1765
|
this.stream.skipWhitespace();
|
|
1700
1766
|
const value = this.parseExpression();
|
|
1701
1767
|
this.consumeStatementTerminator();
|
|
1702
|
-
return new AssignmentNode(target, value);
|
|
1768
|
+
return new AssignmentNode(target, value, operator);
|
|
1703
1769
|
}
|
|
1704
1770
|
parseExpression() {
|
|
1705
1771
|
return this.parsePipeExpression();
|
|
@@ -2560,12 +2626,12 @@ ${caret}`;
|
|
|
2560
2626
|
while (this.stream.peekNonWhitespace(index)?.type === "Dot" /* Dot */ && this.stream.peekNonWhitespace(index + 1)?.type === "Identifier" /* Identifier */) {
|
|
2561
2627
|
index += 2;
|
|
2562
2628
|
}
|
|
2563
|
-
return this.
|
|
2629
|
+
return this.isAssignmentOperatorStart(index);
|
|
2564
2630
|
}
|
|
2565
2631
|
if (first.type === "At" /* At */ || first.type === "Dollar" /* Dollar */) {
|
|
2566
2632
|
const second = this.stream.peekNonWhitespace(1);
|
|
2567
2633
|
const third = this.stream.peekNonWhitespace(2);
|
|
2568
|
-
return second?.type === "Identifier" /* Identifier */ &&
|
|
2634
|
+
return second?.type === "Identifier" /* Identifier */ && this.isAssignmentOperatorStart(2);
|
|
2569
2635
|
}
|
|
2570
2636
|
if (first.type === "LBrace" /* LBrace */ || first.type === "LBracket" /* LBracket */) {
|
|
2571
2637
|
const stack = [];
|
|
@@ -2580,7 +2646,7 @@ ${caret}`;
|
|
|
2580
2646
|
} else if (token.type === "RBrace" /* RBrace */ || token.type === "RBracket" /* RBracket */) {
|
|
2581
2647
|
stack.pop();
|
|
2582
2648
|
if (stack.length === 0) {
|
|
2583
|
-
return this.
|
|
2649
|
+
return this.isAssignmentOperatorStart(index + 1);
|
|
2584
2650
|
}
|
|
2585
2651
|
}
|
|
2586
2652
|
index += 1;
|
|
@@ -2588,6 +2654,20 @@ ${caret}`;
|
|
|
2588
2654
|
}
|
|
2589
2655
|
return false;
|
|
2590
2656
|
}
|
|
2657
|
+
isAssignmentOperatorStart(index) {
|
|
2658
|
+
const token = this.stream.peekNonWhitespace(index);
|
|
2659
|
+
if (!token) {
|
|
2660
|
+
return false;
|
|
2661
|
+
}
|
|
2662
|
+
if (token.type === "Equals" /* Equals */) {
|
|
2663
|
+
return true;
|
|
2664
|
+
}
|
|
2665
|
+
if (token.type === "Plus" /* Plus */ || token.type === "Minus" /* Minus */ || token.type === "Star" /* Star */ || token.type === "Slash" /* Slash */) {
|
|
2666
|
+
const next = this.stream.peekNonWhitespace(index + 1);
|
|
2667
|
+
return next?.type === "Equals" /* Equals */;
|
|
2668
|
+
}
|
|
2669
|
+
return false;
|
|
2670
|
+
}
|
|
2591
2671
|
isCallStart() {
|
|
2592
2672
|
const first = this.stream.peekNonWhitespace(0);
|
|
2593
2673
|
if (!first || first.type !== "Identifier" /* Identifier */) {
|
|
@@ -2812,10 +2892,35 @@ ${caret}`;
|
|
|
2812
2892
|
parseAssignmentExpression() {
|
|
2813
2893
|
const target = this.parseAssignmentTarget();
|
|
2814
2894
|
this.stream.skipWhitespace();
|
|
2815
|
-
this.
|
|
2895
|
+
const operator = this.parseAssignmentOperator();
|
|
2816
2896
|
this.stream.skipWhitespace();
|
|
2817
2897
|
const value = this.parseExpression();
|
|
2818
|
-
return new AssignmentNode(target, value);
|
|
2898
|
+
return new AssignmentNode(target, value, operator);
|
|
2899
|
+
}
|
|
2900
|
+
parseAssignmentOperator() {
|
|
2901
|
+
const next = this.stream.peek();
|
|
2902
|
+
if (!next) {
|
|
2903
|
+
throw new Error("Expected assignment operator");
|
|
2904
|
+
}
|
|
2905
|
+
if (next.type === "Equals" /* Equals */) {
|
|
2906
|
+
this.stream.next();
|
|
2907
|
+
return "=";
|
|
2908
|
+
}
|
|
2909
|
+
if (next.type === "Plus" /* Plus */ || next.type === "Minus" /* Minus */ || next.type === "Star" /* Star */ || next.type === "Slash" /* Slash */) {
|
|
2910
|
+
const op = this.stream.next();
|
|
2911
|
+
this.stream.expect("Equals" /* Equals */);
|
|
2912
|
+
if (op.type === "Plus" /* Plus */) {
|
|
2913
|
+
return "+=";
|
|
2914
|
+
}
|
|
2915
|
+
if (op.type === "Minus" /* Minus */) {
|
|
2916
|
+
return "-=";
|
|
2917
|
+
}
|
|
2918
|
+
if (op.type === "Star" /* Star */) {
|
|
2919
|
+
return "*=";
|
|
2920
|
+
}
|
|
2921
|
+
return "/=";
|
|
2922
|
+
}
|
|
2923
|
+
throw new Error("Expected assignment operator");
|
|
2819
2924
|
}
|
|
2820
2925
|
parseTryBlock() {
|
|
2821
2926
|
this.stream.expect("Try" /* Try */);
|
|
@@ -3318,6 +3423,7 @@ var Engine = class _Engine {
|
|
|
3318
3423
|
ignoredAdded = /* @__PURE__ */ new WeakMap();
|
|
3319
3424
|
diagnostics;
|
|
3320
3425
|
logger;
|
|
3426
|
+
pendingUses = [];
|
|
3321
3427
|
constructor(options = {}) {
|
|
3322
3428
|
this.diagnostics = options.diagnostics ?? false;
|
|
3323
3429
|
this.logger = options.logger ?? console;
|
|
@@ -3387,6 +3493,10 @@ var Engine = class _Engine {
|
|
|
3387
3493
|
registerBehaviors(source) {
|
|
3388
3494
|
const program = new Parser(source, { customFlags: new Set(this.flagHandlers.keys()) }).parseProgram();
|
|
3389
3495
|
for (const use of program.uses) {
|
|
3496
|
+
if (use.flags?.wait) {
|
|
3497
|
+
this.pendingUses.push(this.waitForUseGlobal(use));
|
|
3498
|
+
continue;
|
|
3499
|
+
}
|
|
3390
3500
|
const value = this.resolveGlobalPath(use.name);
|
|
3391
3501
|
if (value === void 0) {
|
|
3392
3502
|
console.warn(`vsn: global '${use.name}' not found`);
|
|
@@ -3436,6 +3546,52 @@ var Engine = class _Engine {
|
|
|
3436
3546
|
}
|
|
3437
3547
|
return value;
|
|
3438
3548
|
}
|
|
3549
|
+
async waitForUses() {
|
|
3550
|
+
while (this.pendingUses.length > 0) {
|
|
3551
|
+
const pending = this.pendingUses;
|
|
3552
|
+
this.pendingUses = [];
|
|
3553
|
+
await Promise.all(pending);
|
|
3554
|
+
}
|
|
3555
|
+
}
|
|
3556
|
+
waitForUseGlobal(use) {
|
|
3557
|
+
const config = use.flagArgs?.wait ?? {};
|
|
3558
|
+
const timeoutMs = config.timeoutMs ?? 1e4;
|
|
3559
|
+
const initialDelayMs = config.intervalMs ?? 100;
|
|
3560
|
+
const maxDelayMs = 1e3;
|
|
3561
|
+
const existing = this.resolveGlobalPath(use.name);
|
|
3562
|
+
if (existing !== void 0) {
|
|
3563
|
+
this.registerGlobal(use.alias, existing);
|
|
3564
|
+
return Promise.resolve();
|
|
3565
|
+
}
|
|
3566
|
+
if (timeoutMs <= 0) {
|
|
3567
|
+
this.emitUseError(use.name, new Error(`vsn: global '${use.name}' not found`));
|
|
3568
|
+
return Promise.resolve();
|
|
3569
|
+
}
|
|
3570
|
+
return new Promise((resolve) => {
|
|
3571
|
+
let elapsedMs = 0;
|
|
3572
|
+
let delayMs = initialDelayMs;
|
|
3573
|
+
const check = () => {
|
|
3574
|
+
const value = this.resolveGlobalPath(use.name);
|
|
3575
|
+
if (value !== void 0) {
|
|
3576
|
+
this.registerGlobal(use.alias, value);
|
|
3577
|
+
resolve();
|
|
3578
|
+
return;
|
|
3579
|
+
}
|
|
3580
|
+
if (elapsedMs >= timeoutMs) {
|
|
3581
|
+
this.emitUseError(use.name, new Error(`vsn: global '${use.name}' not found`));
|
|
3582
|
+
resolve();
|
|
3583
|
+
return;
|
|
3584
|
+
}
|
|
3585
|
+
const scheduledDelay = Math.min(delayMs, timeoutMs - elapsedMs);
|
|
3586
|
+
setTimeout(() => {
|
|
3587
|
+
elapsedMs += scheduledDelay;
|
|
3588
|
+
delayMs = Math.min(delayMs * 2, maxDelayMs);
|
|
3589
|
+
check();
|
|
3590
|
+
}, scheduledDelay);
|
|
3591
|
+
};
|
|
3592
|
+
check();
|
|
3593
|
+
});
|
|
3594
|
+
}
|
|
3439
3595
|
getScope(element, parentScope) {
|
|
3440
3596
|
const existing = this.scopes.get(element);
|
|
3441
3597
|
if (existing) {
|
|
@@ -3559,6 +3715,7 @@ var Engine = class _Engine {
|
|
|
3559
3715
|
}
|
|
3560
3716
|
}
|
|
3561
3717
|
async applyBehaviors(root) {
|
|
3718
|
+
await this.waitForUses();
|
|
3562
3719
|
if (this.behaviorRegistry.length === 0) {
|
|
3563
3720
|
return;
|
|
3564
3721
|
}
|
|
@@ -3966,6 +4123,19 @@ var Engine = class _Engine {
|
|
|
3966
4123
|
})
|
|
3967
4124
|
);
|
|
3968
4125
|
}
|
|
4126
|
+
emitUseError(name, error) {
|
|
4127
|
+
const selector = `use:${name}`;
|
|
4128
|
+
this.logger.warn?.("vsn:error", { error, selector });
|
|
4129
|
+
const target = globalThis.document;
|
|
4130
|
+
if (target && typeof target.dispatchEvent === "function") {
|
|
4131
|
+
target.dispatchEvent(
|
|
4132
|
+
new CustomEvent("vsn:error", {
|
|
4133
|
+
detail: { error, selector },
|
|
4134
|
+
bubbles: true
|
|
4135
|
+
})
|
|
4136
|
+
);
|
|
4137
|
+
}
|
|
4138
|
+
}
|
|
3969
4139
|
attachOnHandler(element, config) {
|
|
3970
4140
|
const options = this.getListenerOptions(config.modifiers);
|
|
3971
4141
|
const listenerTarget = config.modifiers?.includes("outside") ? element.ownerDocument : element;
|