@qwik.dev/core 2.0.0-beta.7 → 2.0.0-beta.9
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/bindings/qwik.darwin-arm64.node +0 -0
- package/bindings/qwik.darwin-x64.node +0 -0
- package/bindings/qwik.linux-x64-gnu.node +0 -0
- package/bindings/qwik.win32-x64-msvc.node +0 -0
- package/bindings/qwik_wasm_bg.wasm +0 -0
- package/dist/backpatch/index.cjs +6 -0
- package/dist/backpatch/index.d.ts +2 -0
- package/dist/backpatch/index.mjs +5 -0
- package/dist/backpatch/package.json +8 -0
- package/dist/backpatch-executor.debug.js +34 -0
- package/dist/backpatch-executor.js +1 -0
- package/dist/build/package.json +1 -1
- package/dist/cli.cjs +17 -17
- package/dist/core-internal.d.ts +112 -56
- package/dist/core.cjs +1022 -469
- package/dist/core.cjs.map +1 -1
- package/dist/core.min.mjs +1 -1
- package/dist/core.mjs +1018 -469
- package/dist/core.mjs.map +1 -1
- package/dist/core.prod.cjs +654 -361
- package/dist/core.prod.mjs +729 -373
- package/dist/loader/index.cjs +2 -2
- package/dist/loader/index.mjs +2 -2
- package/dist/loader/package.json +1 -1
- package/dist/optimizer.cjs +78 -76
- package/dist/optimizer.mjs +78 -78
- package/dist/qwikloader.debug.js +0 -13
- package/dist/qwikloader.js +1 -1
- package/dist/server.cjs +217 -63
- package/dist/server.d.ts +9 -0
- package/dist/server.mjs +213 -63
- package/dist/starters/features/auth/package.json +1 -1
- package/dist/starters/features/localize/package.json +3 -3
- package/dist/starters/features/pandacss/package.json +1 -1
- package/dist/starters/features/playwright/playwright-report/index.html +943 -903
- package/dist/starters/features/postcss/postcss.config.js +1 -1
- package/dist/starters/features/tailwind/package.json +2 -2
- package/dist/starters/features/tailwind/prettier.config.js +10 -0
- package/dist/starters/features/tailwind-v3/package.json +1 -1
- package/dist/starters/features/tailwind-v3/prettier.config.js +10 -0
- package/dist/testing/index.cjs +3826 -952
- package/dist/testing/index.d.ts +972 -1
- package/dist/testing/index.mjs +3811 -946
- package/dist/testing/package.json +1 -1
- package/package.json +8 -6
- package/dist/starters/features/tailwind/.prettierrc.js +0 -3
package/dist/core.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @license
|
|
3
|
-
* @qwik.dev/core 2.0.0-beta.
|
|
3
|
+
* @qwik.dev/core 2.0.0-beta.9-dev+6b582c7
|
|
4
4
|
* Copyright QwikDev. All Rights Reserved.
|
|
5
5
|
* Use of this source code is governed by an MIT-style license that can be
|
|
6
6
|
* found in the LICENSE file at https://github.com/QwikDev/qwik/blob/main/LICENSE
|
|
@@ -9,6 +9,13 @@ import { isDev, isServer, isBrowser } from '@qwik.dev/core/build';
|
|
|
9
9
|
export { isBrowser, isDev, isServer } from '@qwik.dev/core/build';
|
|
10
10
|
import { p } from '@qwik.dev/core/preloader';
|
|
11
11
|
|
|
12
|
+
/**
|
|
13
|
+
* QWIK_VERSION
|
|
14
|
+
*
|
|
15
|
+
* @public
|
|
16
|
+
*/
|
|
17
|
+
const version = "2.0.0-beta.9-dev+6b582c7";
|
|
18
|
+
|
|
12
19
|
// same as isDev but separate so we can test
|
|
13
20
|
const qDev = globalThis.qDev !== false;
|
|
14
21
|
const qInspector = globalThis.qInspector === true;
|
|
@@ -86,6 +93,9 @@ const isArray = (v) => {
|
|
|
86
93
|
const isString = (v) => {
|
|
87
94
|
return typeof v === 'string';
|
|
88
95
|
};
|
|
96
|
+
const isNumber$1 = (v) => {
|
|
97
|
+
return typeof v === 'number';
|
|
98
|
+
};
|
|
89
99
|
const isFunction = (v) => {
|
|
90
100
|
return typeof v === 'function';
|
|
91
101
|
};
|
|
@@ -288,13 +298,6 @@ const createPlatform = () => {
|
|
|
288
298
|
});
|
|
289
299
|
});
|
|
290
300
|
},
|
|
291
|
-
nextTick: (fn) => {
|
|
292
|
-
return new Promise((resolve) => {
|
|
293
|
-
setTimeout(() => {
|
|
294
|
-
resolve(fn());
|
|
295
|
-
});
|
|
296
|
-
});
|
|
297
|
-
},
|
|
298
301
|
chunkForSymbol(symbolName, chunk) {
|
|
299
302
|
return [symbolName, chunk ?? '_'];
|
|
300
303
|
},
|
|
@@ -552,6 +555,13 @@ class SignalImpl {
|
|
|
552
555
|
this.$container$ = container;
|
|
553
556
|
this.$untrackedValue$ = value;
|
|
554
557
|
}
|
|
558
|
+
/**
|
|
559
|
+
* Use this to force running subscribers, for example when the calculated value has mutated but
|
|
560
|
+
* remained the same object
|
|
561
|
+
*/
|
|
562
|
+
force() {
|
|
563
|
+
this.$container$?.$scheduler$(7 /* ChoreType.RECOMPUTE_AND_SCHEDULE_EFFECTS */, null, this, this.$effects$);
|
|
564
|
+
}
|
|
555
565
|
get untrackedValue() {
|
|
556
566
|
return this.$untrackedValue$;
|
|
557
567
|
}
|
|
@@ -565,14 +575,7 @@ class SignalImpl {
|
|
|
565
575
|
set value(value) {
|
|
566
576
|
if (value !== this.$untrackedValue$) {
|
|
567
577
|
this.$untrackedValue$ = value;
|
|
568
|
-
|
|
569
|
-
triggerEffects(this.$container$, this, this.$effects$);
|
|
570
|
-
// this.$container$?.$scheduler$(
|
|
571
|
-
// ChoreType.RECOMPUTE_AND_SCHEDULE_EFFECTS,
|
|
572
|
-
// null,
|
|
573
|
-
// this,
|
|
574
|
-
// this.$effects$
|
|
575
|
-
// );
|
|
578
|
+
this.$container$?.$scheduler$(7 /* ChoreType.RECOMPUTE_AND_SCHEDULE_EFFECTS */, null, this, this.$effects$);
|
|
576
579
|
}
|
|
577
580
|
}
|
|
578
581
|
// prevent accidental use as value
|
|
@@ -634,12 +637,11 @@ class WrappedSignalImpl extends SignalImpl {
|
|
|
634
637
|
$funcStr$;
|
|
635
638
|
$flags$;
|
|
636
639
|
$hostElement$ = null;
|
|
637
|
-
$forceRunEffects$ = false;
|
|
638
640
|
[_EFFECT_BACK_REF] = null;
|
|
639
641
|
constructor(container, fn, args, fnStr,
|
|
640
642
|
// We need a separate flag to know when the computation needs running because
|
|
641
643
|
// we need the old value to know if effects need running after computation
|
|
642
|
-
flags = 1 /* SignalFlags.INVALID */ |
|
|
644
|
+
flags = 1 /* SignalFlags.INVALID */ | 4 /* WrappedSignalFlags.UNWRAP */) {
|
|
643
645
|
super(container, NEEDS_COMPUTATION);
|
|
644
646
|
this.$args$ = args;
|
|
645
647
|
this.$func$ = fn;
|
|
@@ -648,7 +650,6 @@ class WrappedSignalImpl extends SignalImpl {
|
|
|
648
650
|
}
|
|
649
651
|
invalidate() {
|
|
650
652
|
this.$flags$ |= 1 /* SignalFlags.INVALID */;
|
|
651
|
-
this.$forceRunEffects$ = false;
|
|
652
653
|
this.$container$?.$scheduler$(7 /* ChoreType.RECOMPUTE_AND_SCHEDULE_EFFECTS */, this.$hostElement$, this, this.$effects$);
|
|
653
654
|
}
|
|
654
655
|
/**
|
|
@@ -656,29 +657,26 @@ class WrappedSignalImpl extends SignalImpl {
|
|
|
656
657
|
* remained the same object.
|
|
657
658
|
*/
|
|
658
659
|
force() {
|
|
659
|
-
this.$
|
|
660
|
+
this.$flags$ |= 2 /* SignalFlags.RUN_EFFECTS */;
|
|
660
661
|
this.$container$?.$scheduler$(7 /* ChoreType.RECOMPUTE_AND_SCHEDULE_EFFECTS */, this.$hostElement$, this, this.$effects$);
|
|
661
662
|
}
|
|
662
663
|
get untrackedValue() {
|
|
663
|
-
|
|
664
|
-
if (didChange) {
|
|
665
|
-
this.$forceRunEffects$ = didChange;
|
|
666
|
-
}
|
|
664
|
+
this.$computeIfNeeded$();
|
|
667
665
|
assertFalse(this.$untrackedValue$ === NEEDS_COMPUTATION, 'Invalid state');
|
|
668
666
|
return this.$untrackedValue$;
|
|
669
667
|
}
|
|
670
668
|
$computeIfNeeded$() {
|
|
671
669
|
if (!(this.$flags$ & 1 /* SignalFlags.INVALID */)) {
|
|
672
|
-
return
|
|
670
|
+
return;
|
|
673
671
|
}
|
|
674
672
|
const untrackedValue = trackSignal(() => this.$func$(...this.$args$), this, "." /* EffectProperty.VNODE */, this.$container$);
|
|
675
|
-
// TODO: we should remove invalid flag here
|
|
673
|
+
// TODO: we should remove invalid flag here, but some tests are failing
|
|
676
674
|
// this.$flags$ &= ~SignalFlags.INVALID;
|
|
677
675
|
const didChange = untrackedValue !== this.$untrackedValue$;
|
|
678
676
|
if (didChange) {
|
|
677
|
+
this.$flags$ |= 2 /* SignalFlags.RUN_EFFECTS */;
|
|
679
678
|
this.$untrackedValue$ = untrackedValue;
|
|
680
679
|
}
|
|
681
|
-
return didChange;
|
|
682
680
|
}
|
|
683
681
|
// Make this signal read-only
|
|
684
682
|
set value(_) {
|
|
@@ -873,12 +871,26 @@ const _jsxBranch = (input) => {
|
|
|
873
871
|
};
|
|
874
872
|
/** @internal */
|
|
875
873
|
const _waitUntilRendered = (elm) => {
|
|
876
|
-
const
|
|
877
|
-
if (!
|
|
874
|
+
const container = _getQContainerElement(elm)?.qContainer;
|
|
875
|
+
if (!container) {
|
|
878
876
|
return Promise.resolve();
|
|
879
877
|
}
|
|
880
|
-
|
|
881
|
-
|
|
878
|
+
// Multi-cycle idle: loop WAIT_FOR_QUEUE until the flush epoch stays stable
|
|
879
|
+
// across an extra microtask, which signals that no new work re-scheduled.
|
|
880
|
+
return (async () => {
|
|
881
|
+
for (;;) {
|
|
882
|
+
await container.$scheduler$(255 /* ChoreType.WAIT_FOR_QUEUE */).$returnValue$;
|
|
883
|
+
const firstEpoch = container.$flushEpoch$ || 0;
|
|
884
|
+
// Give a microtask for any immediate follow-up scheduling to enqueue
|
|
885
|
+
await Promise.resolve();
|
|
886
|
+
const secondEpoch = container.$flushEpoch$ || 0;
|
|
887
|
+
// If no epoch change occurred during and after WAIT_FOR_QUEUE, we are idle.
|
|
888
|
+
if (firstEpoch === secondEpoch) {
|
|
889
|
+
return;
|
|
890
|
+
}
|
|
891
|
+
// Continue loop if epoch advanced, meaning more work flushed.
|
|
892
|
+
}
|
|
893
|
+
})();
|
|
882
894
|
};
|
|
883
895
|
|
|
884
896
|
/**
|
|
@@ -956,7 +968,7 @@ function jsxEventToHtmlAttribute(jsxEvent) {
|
|
|
956
968
|
}
|
|
957
969
|
return null; // Return null if not matching expected format
|
|
958
970
|
}
|
|
959
|
-
function eventNameToJsxEvent(eventName, prefix
|
|
971
|
+
function eventNameToJsxEvent(eventName, prefix) {
|
|
960
972
|
eventName = eventName.charAt(0).toUpperCase() + eventName.substring(1);
|
|
961
973
|
return prefix + eventName + EVENT_SUFFIX;
|
|
962
974
|
}
|
|
@@ -1245,13 +1257,6 @@ const isRecoverable = (err) => {
|
|
|
1245
1257
|
return true;
|
|
1246
1258
|
};
|
|
1247
1259
|
|
|
1248
|
-
/**
|
|
1249
|
-
* QWIK_VERSION
|
|
1250
|
-
*
|
|
1251
|
-
* @public
|
|
1252
|
-
*/
|
|
1253
|
-
const version = "2.0.0-beta.7-dev+2dd89a6";
|
|
1254
|
-
|
|
1255
1260
|
/** @internal */
|
|
1256
1261
|
const EMPTY_ARRAY = [];
|
|
1257
1262
|
const EMPTY_OBJ = {};
|
|
@@ -1392,7 +1397,7 @@ const useLexicalScope = () => {
|
|
|
1392
1397
|
*
|
|
1393
1398
|
* @internal
|
|
1394
1399
|
*/
|
|
1395
|
-
const
|
|
1400
|
+
const _run = (...args) => {
|
|
1396
1401
|
// This will already check container
|
|
1397
1402
|
const [runQrl] = useLexicalScope();
|
|
1398
1403
|
const context = getInvokeContext();
|
|
@@ -1406,7 +1411,9 @@ const queueQRL = (...args) => {
|
|
|
1406
1411
|
if (!scheduler) {
|
|
1407
1412
|
throw qError(1 /* QError.schedulerNotFound */);
|
|
1408
1413
|
}
|
|
1409
|
-
return
|
|
1414
|
+
// We don't return anything, the scheduler is in charge now
|
|
1415
|
+
const chore = scheduler(2 /* ChoreType.RUN_QRL */, hostElement, runQrl, args);
|
|
1416
|
+
return getChorePromise(chore);
|
|
1410
1417
|
};
|
|
1411
1418
|
|
|
1412
1419
|
/** @internal */
|
|
@@ -1430,17 +1437,17 @@ const mapApp_findIndx = (array, key, start) => {
|
|
|
1430
1437
|
return (bottom << 1) ^ -1;
|
|
1431
1438
|
};
|
|
1432
1439
|
/** @internal */
|
|
1433
|
-
const mapArray_set = (array, key, value, start) => {
|
|
1440
|
+
const mapArray_set = (array, key, value, start, allowNullValue = false) => {
|
|
1434
1441
|
const indx = mapApp_findIndx(array, key, start);
|
|
1435
1442
|
if (indx >= 0) {
|
|
1436
|
-
if (value == null) {
|
|
1443
|
+
if (value == null && !allowNullValue) {
|
|
1437
1444
|
array.splice(indx, 2);
|
|
1438
1445
|
}
|
|
1439
1446
|
else {
|
|
1440
1447
|
array[indx + 1] = value;
|
|
1441
1448
|
}
|
|
1442
1449
|
}
|
|
1443
|
-
else if (value != null) {
|
|
1450
|
+
else if (value != null || allowNullValue) {
|
|
1444
1451
|
array.splice(indx ^ -1, 0, key, value);
|
|
1445
1452
|
}
|
|
1446
1453
|
};
|
|
@@ -1454,6 +1461,9 @@ const mapArray_get = (array, key, start) => {
|
|
|
1454
1461
|
return null;
|
|
1455
1462
|
}
|
|
1456
1463
|
};
|
|
1464
|
+
const mapArray_has = (array, key, start) => {
|
|
1465
|
+
return mapApp_findIndx(array, key, start) >= 0;
|
|
1466
|
+
};
|
|
1457
1467
|
|
|
1458
1468
|
/** @internal */
|
|
1459
1469
|
const _CONST_PROPS = Symbol('CONST');
|
|
@@ -1527,13 +1537,12 @@ class ComputedSignalImpl extends SignalImpl {
|
|
|
1527
1537
|
*/
|
|
1528
1538
|
$computeQrl$;
|
|
1529
1539
|
$flags$;
|
|
1530
|
-
$forceRunEffects$ = false;
|
|
1531
1540
|
[_EFFECT_BACK_REF] = null;
|
|
1532
1541
|
constructor(container, fn,
|
|
1533
1542
|
// We need a separate flag to know when the computation needs running because
|
|
1534
1543
|
// we need the old value to know if effects need running after computation
|
|
1535
1544
|
flags = 1 /* SignalFlags.INVALID */ |
|
|
1536
|
-
|
|
1545
|
+
32 /* SerializationSignalFlags.SERIALIZATION_STRATEGY_ALWAYS */) {
|
|
1537
1546
|
// The value is used for comparison when signals trigger, which can only happen
|
|
1538
1547
|
// when it was calculated before. Therefore we can pass whatever we like.
|
|
1539
1548
|
super(container, NEEDS_COMPUTATION);
|
|
@@ -1542,7 +1551,6 @@ class ComputedSignalImpl extends SignalImpl {
|
|
|
1542
1551
|
}
|
|
1543
1552
|
invalidate() {
|
|
1544
1553
|
this.$flags$ |= 1 /* SignalFlags.INVALID */;
|
|
1545
|
-
this.$forceRunEffects$ = false;
|
|
1546
1554
|
this.$container$?.$scheduler$(7 /* ChoreType.RECOMPUTE_AND_SCHEDULE_EFFECTS */, null, this, this.$effects$);
|
|
1547
1555
|
}
|
|
1548
1556
|
/**
|
|
@@ -1550,20 +1558,17 @@ class ComputedSignalImpl extends SignalImpl {
|
|
|
1550
1558
|
* remained the same object
|
|
1551
1559
|
*/
|
|
1552
1560
|
force() {
|
|
1553
|
-
this.$
|
|
1554
|
-
|
|
1561
|
+
this.$flags$ |= 2 /* SignalFlags.RUN_EFFECTS */;
|
|
1562
|
+
super.force();
|
|
1555
1563
|
}
|
|
1556
1564
|
get untrackedValue() {
|
|
1557
|
-
|
|
1558
|
-
if (didChange) {
|
|
1559
|
-
this.$forceRunEffects$ = didChange;
|
|
1560
|
-
}
|
|
1565
|
+
this.$computeIfNeeded$();
|
|
1561
1566
|
assertFalse(this.$untrackedValue$ === NEEDS_COMPUTATION, 'Invalid state');
|
|
1562
1567
|
return this.$untrackedValue$;
|
|
1563
1568
|
}
|
|
1564
1569
|
$computeIfNeeded$() {
|
|
1565
1570
|
if (!(this.$flags$ & 1 /* SignalFlags.INVALID */)) {
|
|
1566
|
-
return
|
|
1571
|
+
return;
|
|
1567
1572
|
}
|
|
1568
1573
|
const computeQrl = this.$computeQrl$;
|
|
1569
1574
|
throwIfQRLNotResolved(computeQrl);
|
|
@@ -1582,9 +1587,12 @@ class ComputedSignalImpl extends SignalImpl {
|
|
|
1582
1587
|
this.$flags$ &= ~1 /* SignalFlags.INVALID */;
|
|
1583
1588
|
const didChange = untrackedValue !== this.$untrackedValue$;
|
|
1584
1589
|
if (didChange) {
|
|
1590
|
+
// skip first computation when value is not changed
|
|
1591
|
+
if (this.$untrackedValue$ !== NEEDS_COMPUTATION) {
|
|
1592
|
+
this.$flags$ |= 2 /* SignalFlags.RUN_EFFECTS */;
|
|
1593
|
+
}
|
|
1585
1594
|
this.$untrackedValue$ = untrackedValue;
|
|
1586
1595
|
}
|
|
1587
|
-
return didChange;
|
|
1588
1596
|
}
|
|
1589
1597
|
finally {
|
|
1590
1598
|
if (ctx) {
|
|
@@ -1610,12 +1618,12 @@ class ComputedSignalImpl extends SignalImpl {
|
|
|
1610
1618
|
*/
|
|
1611
1619
|
class SerializerSignalImpl extends ComputedSignalImpl {
|
|
1612
1620
|
constructor(container, argQrl) {
|
|
1613
|
-
super(container, argQrl, 1 /* SignalFlags.INVALID */ |
|
|
1621
|
+
super(container, argQrl, 1 /* SignalFlags.INVALID */ | 32 /* SerializationSignalFlags.SERIALIZATION_STRATEGY_ALWAYS */);
|
|
1614
1622
|
}
|
|
1615
1623
|
$didInitialize$ = false;
|
|
1616
1624
|
$computeIfNeeded$() {
|
|
1617
1625
|
if (!(this.$flags$ & 1 /* SignalFlags.INVALID */)) {
|
|
1618
|
-
return
|
|
1626
|
+
return;
|
|
1619
1627
|
}
|
|
1620
1628
|
throwIfQRLNotResolved(this.$computeQrl$);
|
|
1621
1629
|
let arg = this.$computeQrl$.resolved;
|
|
@@ -1633,9 +1641,9 @@ class SerializerSignalImpl extends ComputedSignalImpl {
|
|
|
1633
1641
|
this.$flags$ &= -2 /* SignalFlags.INVALID */;
|
|
1634
1642
|
this.$didInitialize$ = true;
|
|
1635
1643
|
if (didChange) {
|
|
1644
|
+
this.$flags$ |= 2 /* SignalFlags.RUN_EFFECTS */;
|
|
1636
1645
|
this.$untrackedValue$ = untrackedValue;
|
|
1637
1646
|
}
|
|
1638
|
-
return didChange;
|
|
1639
1647
|
}
|
|
1640
1648
|
}
|
|
1641
1649
|
|
|
@@ -1645,6 +1653,28 @@ const getStoreHandler = (value) => {
|
|
|
1645
1653
|
const getStoreTarget = (value) => {
|
|
1646
1654
|
return value?.[STORE_TARGET] || null;
|
|
1647
1655
|
};
|
|
1656
|
+
/**
|
|
1657
|
+
* Force a store to recompute and schedule effects.
|
|
1658
|
+
*
|
|
1659
|
+
* @public
|
|
1660
|
+
*/
|
|
1661
|
+
const forceStoreEffects = (value, prop) => {
|
|
1662
|
+
const handler = getStoreHandler(value);
|
|
1663
|
+
if (handler) {
|
|
1664
|
+
handler.force(prop);
|
|
1665
|
+
}
|
|
1666
|
+
};
|
|
1667
|
+
/**
|
|
1668
|
+
* @returns True if the store has effects for the given prop
|
|
1669
|
+
* @internal
|
|
1670
|
+
*/
|
|
1671
|
+
const _hasStoreEffects = (value, prop) => {
|
|
1672
|
+
const handler = getStoreHandler(value);
|
|
1673
|
+
if (handler) {
|
|
1674
|
+
return (handler.$effects$?.get(prop)?.size ?? 0) > 0;
|
|
1675
|
+
}
|
|
1676
|
+
return false;
|
|
1677
|
+
};
|
|
1648
1678
|
/**
|
|
1649
1679
|
* Get the original object that was wrapped by the store. Useful if you want to clone a store
|
|
1650
1680
|
* (structuredClone, IndexedDB,...)
|
|
@@ -1683,7 +1713,12 @@ class StoreHandler {
|
|
|
1683
1713
|
toString() {
|
|
1684
1714
|
return '[Store]';
|
|
1685
1715
|
}
|
|
1716
|
+
force(prop) {
|
|
1717
|
+
const target = getStoreTarget(this);
|
|
1718
|
+
this.$container$?.$scheduler$(7 /* ChoreType.RECOMPUTE_AND_SCHEDULE_EFFECTS */, null, this, getEffects(target, prop, this.$effects$));
|
|
1719
|
+
}
|
|
1686
1720
|
get(target, prop) {
|
|
1721
|
+
// TODO(perf): handle better `slice` calls
|
|
1687
1722
|
if (typeof prop === 'symbol') {
|
|
1688
1723
|
if (prop === STORE_TARGET) {
|
|
1689
1724
|
return target;
|
|
@@ -1746,7 +1781,11 @@ class StoreHandler {
|
|
|
1746
1781
|
if (typeof prop != 'string' || !delete target[prop]) {
|
|
1747
1782
|
return false;
|
|
1748
1783
|
}
|
|
1749
|
-
|
|
1784
|
+
if (!Array.isArray(target)) {
|
|
1785
|
+
// If the target is an array, we don't need to trigger effects.
|
|
1786
|
+
// Changing the length property will trigger effects.
|
|
1787
|
+
this.$container$?.$scheduler$(7 /* ChoreType.RECOMPUTE_AND_SCHEDULE_EFFECTS */, null, this, getEffects(target, prop, this.$effects$));
|
|
1788
|
+
}
|
|
1750
1789
|
return true;
|
|
1751
1790
|
}
|
|
1752
1791
|
has(target, prop) {
|
|
@@ -1805,8 +1844,10 @@ function addStoreEffect(target, prop, store, effectSubscription) {
|
|
|
1805
1844
|
}
|
|
1806
1845
|
function setNewValueAndTriggerEffects(prop, value, target, currentStore) {
|
|
1807
1846
|
target[prop] = value;
|
|
1808
|
-
|
|
1809
|
-
|
|
1847
|
+
const effects = getEffects(target, prop, currentStore.$effects$);
|
|
1848
|
+
if (effects) {
|
|
1849
|
+
currentStore.$container$?.$scheduler$(7 /* ChoreType.RECOMPUTE_AND_SCHEDULE_EFFECTS */, null, currentStore, effects);
|
|
1850
|
+
}
|
|
1810
1851
|
}
|
|
1811
1852
|
function getEffects(target, prop, storeEffects) {
|
|
1812
1853
|
let effectsToTrigger;
|
|
@@ -1934,7 +1975,7 @@ class AsyncComputedSignalImpl extends ComputedSignalImpl {
|
|
|
1934
1975
|
}
|
|
1935
1976
|
$computeIfNeeded$() {
|
|
1936
1977
|
if (!(this.$flags$ & 1 /* SignalFlags.INVALID */)) {
|
|
1937
|
-
return
|
|
1978
|
+
return;
|
|
1938
1979
|
}
|
|
1939
1980
|
const computeQrl = this.$computeQrl$;
|
|
1940
1981
|
throwIfQRLNotResolved(computeQrl);
|
|
@@ -1964,6 +2005,7 @@ class AsyncComputedSignalImpl extends ComputedSignalImpl {
|
|
|
1964
2005
|
this.$flags$ &= -2 /* SignalFlags.INVALID */;
|
|
1965
2006
|
const didChange = untrackedValue !== this.$untrackedValue$;
|
|
1966
2007
|
if (didChange) {
|
|
2008
|
+
this.$flags$ |= 2 /* SignalFlags.RUN_EFFECTS */;
|
|
1967
2009
|
this.$untrackedValue$ = untrackedValue;
|
|
1968
2010
|
}
|
|
1969
2011
|
return didChange;
|
|
@@ -2051,7 +2093,7 @@ const _wrapProp = (...args) => {
|
|
|
2051
2093
|
if (!(obj instanceof AsyncComputedSignalImpl)) {
|
|
2052
2094
|
assertEqual(prop, 'value', 'Left side is a signal, prop must be value');
|
|
2053
2095
|
}
|
|
2054
|
-
if (obj instanceof WrappedSignalImpl && obj.flags &
|
|
2096
|
+
if (obj instanceof WrappedSignalImpl && obj.flags & 4 /* WrappedSignalFlags.UNWRAP */) {
|
|
2055
2097
|
return obj;
|
|
2056
2098
|
}
|
|
2057
2099
|
return getWrapped(args);
|
|
@@ -2112,7 +2154,7 @@ function isSlotProp(prop) {
|
|
|
2112
2154
|
return !prop.startsWith('q:') && !prop.startsWith(NON_SERIALIZABLE_MARKER_PREFIX);
|
|
2113
2155
|
}
|
|
2114
2156
|
/** @internal */
|
|
2115
|
-
const _restProps = (props, omit, target = {}) => {
|
|
2157
|
+
const _restProps = (props, omit = [], target = {}) => {
|
|
2116
2158
|
let constPropsTarget = null;
|
|
2117
2159
|
const constProps = props[_CONST_PROPS];
|
|
2118
2160
|
if (constProps) {
|
|
@@ -2567,12 +2609,12 @@ function addUseOnEvents(jsx, useOnEvents) {
|
|
|
2567
2609
|
}
|
|
2568
2610
|
if (targetElement) {
|
|
2569
2611
|
if (targetElement.type === 'script' && key === qVisibleEvent) {
|
|
2570
|
-
eventKey = 'document:
|
|
2612
|
+
eventKey = 'document:onQInit$';
|
|
2571
2613
|
logWarn('You are trying to add an event "' +
|
|
2572
2614
|
key +
|
|
2573
2615
|
'" using `useVisibleTask$` hook, ' +
|
|
2574
2616
|
'but a node to which you can add an event is not found. ' +
|
|
2575
|
-
'Using document:
|
|
2617
|
+
'Using document:onQInit$ instead.');
|
|
2576
2618
|
}
|
|
2577
2619
|
addUseOnEvent(targetElement, eventKey, useOnEvents[key]);
|
|
2578
2620
|
}
|
|
@@ -2695,13 +2737,13 @@ const applyQwikComponentBody = (ssr, jsx, component) => {
|
|
|
2695
2737
|
if (srcProps && srcProps.children) {
|
|
2696
2738
|
delete srcProps.children;
|
|
2697
2739
|
}
|
|
2698
|
-
const scheduler = ssr.$scheduler$;
|
|
2699
2740
|
host.setProp(OnRenderProp, componentQrl);
|
|
2700
2741
|
host.setProp(ELEMENT_PROPS, srcProps);
|
|
2701
2742
|
if (jsx.key !== null) {
|
|
2702
2743
|
host.setProp(ELEMENT_KEY, jsx.key);
|
|
2703
2744
|
}
|
|
2704
|
-
|
|
2745
|
+
const componentChore = ssr.$scheduler$(6 /* ChoreType.COMPONENT */, host, componentQrl, srcProps);
|
|
2746
|
+
return getChorePromise(componentChore);
|
|
2705
2747
|
};
|
|
2706
2748
|
|
|
2707
2749
|
class ParentComponentData {
|
|
@@ -2809,7 +2851,14 @@ function processJSXNode(ssr, enqueue, value, options) {
|
|
|
2809
2851
|
appendQwikInspectorAttribute(jsx, qwikInspectorAttrValue);
|
|
2810
2852
|
}
|
|
2811
2853
|
}
|
|
2812
|
-
const innerHTML = ssr.openElement(type, varPropsToSsrAttrs(jsx.varProps, jsx.constProps,
|
|
2854
|
+
const innerHTML = ssr.openElement(type, varPropsToSsrAttrs(jsx.varProps, jsx.constProps, {
|
|
2855
|
+
serializationCtx: ssr.serializationCtx,
|
|
2856
|
+
styleScopedId: options.styleScoped,
|
|
2857
|
+
key: jsx.key,
|
|
2858
|
+
}), constPropsToSsrAttrs(jsx.constProps, jsx.varProps, {
|
|
2859
|
+
serializationCtx: ssr.serializationCtx,
|
|
2860
|
+
styleScopedId: options.styleScoped,
|
|
2861
|
+
}), qwikInspectorAttrValue);
|
|
2813
2862
|
if (innerHTML) {
|
|
2814
2863
|
ssr.htmlNode(innerHTML);
|
|
2815
2864
|
}
|
|
@@ -2925,16 +2974,17 @@ function processJSXNode(ssr, enqueue, value, options) {
|
|
|
2925
2974
|
}
|
|
2926
2975
|
}
|
|
2927
2976
|
}
|
|
2928
|
-
function varPropsToSsrAttrs(varProps, constProps,
|
|
2929
|
-
return toSsrAttrs(varProps, constProps,
|
|
2977
|
+
function varPropsToSsrAttrs(varProps, constProps, options) {
|
|
2978
|
+
return toSsrAttrs(varProps, constProps, false, options);
|
|
2930
2979
|
}
|
|
2931
|
-
function constPropsToSsrAttrs(constProps, varProps,
|
|
2932
|
-
return toSsrAttrs(constProps, varProps,
|
|
2980
|
+
function constPropsToSsrAttrs(constProps, varProps, options) {
|
|
2981
|
+
return toSsrAttrs(constProps, varProps, true, options);
|
|
2933
2982
|
}
|
|
2934
|
-
function toSsrAttrs(record, anotherRecord,
|
|
2983
|
+
function toSsrAttrs(record, anotherRecord, isConst, options) {
|
|
2935
2984
|
if (record == null) {
|
|
2936
2985
|
return null;
|
|
2937
2986
|
}
|
|
2987
|
+
const pushMergedEventProps = !isConst;
|
|
2938
2988
|
const ssrAttrs = [];
|
|
2939
2989
|
for (const key in record) {
|
|
2940
2990
|
let value = record[key];
|
|
@@ -2971,7 +3021,7 @@ function toSsrAttrs(record, anotherRecord, serializationCtx, pushMergedEventProp
|
|
|
2971
3021
|
}
|
|
2972
3022
|
}
|
|
2973
3023
|
}
|
|
2974
|
-
const eventValue = setEvent(serializationCtx, key, value);
|
|
3024
|
+
const eventValue = setEvent(options.serializationCtx, key, value);
|
|
2975
3025
|
if (eventValue) {
|
|
2976
3026
|
ssrAttrs.push(jsxEventToHtmlAttribute(key), eventValue);
|
|
2977
3027
|
}
|
|
@@ -2981,7 +3031,7 @@ function toSsrAttrs(record, anotherRecord, serializationCtx, pushMergedEventProp
|
|
|
2981
3031
|
// write signal as is. We will track this signal inside `writeAttrs`
|
|
2982
3032
|
if (isClassAttr(key)) {
|
|
2983
3033
|
// additionally append styleScopedId for class attr
|
|
2984
|
-
ssrAttrs.push(key, [value, styleScopedId]);
|
|
3034
|
+
ssrAttrs.push(key, [value, options.styleScopedId]);
|
|
2985
3035
|
}
|
|
2986
3036
|
else {
|
|
2987
3037
|
ssrAttrs.push(key, value);
|
|
@@ -2989,13 +3039,13 @@ function toSsrAttrs(record, anotherRecord, serializationCtx, pushMergedEventProp
|
|
|
2989
3039
|
continue;
|
|
2990
3040
|
}
|
|
2991
3041
|
if (isPreventDefault(key)) {
|
|
2992
|
-
addPreventDefaultEventToSerializationContext(serializationCtx, key);
|
|
3042
|
+
addPreventDefaultEventToSerializationContext(options.serializationCtx, key);
|
|
2993
3043
|
}
|
|
2994
|
-
value = serializeAttribute(key, value, styleScopedId);
|
|
3044
|
+
value = serializeAttribute(key, value, options.styleScopedId);
|
|
2995
3045
|
ssrAttrs.push(key, value);
|
|
2996
3046
|
}
|
|
2997
|
-
if (key != null) {
|
|
2998
|
-
ssrAttrs.push(ELEMENT_KEY, key);
|
|
3047
|
+
if (options.key != null) {
|
|
3048
|
+
ssrAttrs.push(ELEMENT_KEY, options.key);
|
|
2999
3049
|
}
|
|
3000
3050
|
return ssrAttrs;
|
|
3001
3051
|
}
|
|
@@ -3044,7 +3094,7 @@ function setEvent(serializationCtx, key, rawValue) {
|
|
|
3044
3094
|
* For internal qrls (starting with `_`) we assume that they do the right thing.
|
|
3045
3095
|
*/
|
|
3046
3096
|
if (!qrl.$symbol$.startsWith('_') && (qrl.$captureRef$ || qrl.$capture$)) {
|
|
3047
|
-
qrl = createQRL(null, '_run',
|
|
3097
|
+
qrl = createQRL(null, '_run', _run, null, null, [qrl]);
|
|
3048
3098
|
}
|
|
3049
3099
|
return qrlToString(serializationCtx, qrl);
|
|
3050
3100
|
};
|
|
@@ -3124,10 +3174,9 @@ const useTaskQrl = (qrl) => {
|
|
|
3124
3174
|
// deleted and we need to be able to release the task subscriptions.
|
|
3125
3175
|
set(task);
|
|
3126
3176
|
const container = iCtx.$container$;
|
|
3127
|
-
const
|
|
3128
|
-
if (isPromise(
|
|
3129
|
-
|
|
3130
|
-
promise.catch(() => { });
|
|
3177
|
+
const result = runTask(task, container, iCtx.$hostElement$);
|
|
3178
|
+
if (isPromise(result)) {
|
|
3179
|
+
throw result;
|
|
3131
3180
|
}
|
|
3132
3181
|
};
|
|
3133
3182
|
const runTask = (task, container, host) => {
|
|
@@ -3139,7 +3188,7 @@ const runTask = (task, container, host) => {
|
|
|
3139
3188
|
const track = trackFn(task, container);
|
|
3140
3189
|
const [cleanup] = cleanupFn(task, (reason) => container.handleError(reason, host));
|
|
3141
3190
|
const taskApi = { track, cleanup };
|
|
3142
|
-
|
|
3191
|
+
return safeCall(() => taskFn(taskApi), cleanup, (err) => {
|
|
3143
3192
|
// If a Promise is thrown, that means we need to re-run the task.
|
|
3144
3193
|
if (isPromise(err)) {
|
|
3145
3194
|
return err.then(() => runTask(task, container, host));
|
|
@@ -3148,7 +3197,6 @@ const runTask = (task, container, host) => {
|
|
|
3148
3197
|
throw err;
|
|
3149
3198
|
}
|
|
3150
3199
|
});
|
|
3151
|
-
return result;
|
|
3152
3200
|
};
|
|
3153
3201
|
const cleanupTask = (task) => {
|
|
3154
3202
|
const destroy = task.$destroy$;
|
|
@@ -3190,7 +3238,7 @@ const isTask = (value) => {
|
|
|
3190
3238
|
*/
|
|
3191
3239
|
const scheduleTask = (_event, element) => {
|
|
3192
3240
|
const [task] = useLexicalScope();
|
|
3193
|
-
const type = task.$flags$ & 1 /* TaskFlags.VISIBLE_TASK */ ?
|
|
3241
|
+
const type = task.$flags$ & 1 /* TaskFlags.VISIBLE_TASK */ ? 16 /* ChoreType.VISIBLE */ : 3 /* ChoreType.TASK */;
|
|
3194
3242
|
const container = getDomContainer(element);
|
|
3195
3243
|
container.$scheduler$(type, task);
|
|
3196
3244
|
};
|
|
@@ -3442,7 +3490,7 @@ class PropsProxyHandler {
|
|
|
3442
3490
|
? this.$constProps$[prop]
|
|
3443
3491
|
: this.$varProps$[prop];
|
|
3444
3492
|
// a proxied value that the optimizer made
|
|
3445
|
-
return value instanceof WrappedSignalImpl && value.$flags$ &
|
|
3493
|
+
return value instanceof WrappedSignalImpl && value.$flags$ & 4 /* WrappedSignalFlags.UNWRAP */
|
|
3446
3494
|
? value.value
|
|
3447
3495
|
: value;
|
|
3448
3496
|
}
|
|
@@ -3518,6 +3566,24 @@ class PropsProxyHandler {
|
|
|
3518
3566
|
const directGetPropsProxyProp = (jsx, prop) => {
|
|
3519
3567
|
return (jsx.constProps && prop in jsx.constProps ? jsx.constProps[prop] : jsx.varProps[prop]);
|
|
3520
3568
|
};
|
|
3569
|
+
/** @internal */
|
|
3570
|
+
const _getVarProps = (props) => {
|
|
3571
|
+
if (!props) {
|
|
3572
|
+
return null;
|
|
3573
|
+
}
|
|
3574
|
+
return _VAR_PROPS in props
|
|
3575
|
+
? 'children' in props
|
|
3576
|
+
? { ...props[_VAR_PROPS], children: props.children }
|
|
3577
|
+
: props[_VAR_PROPS]
|
|
3578
|
+
: props;
|
|
3579
|
+
};
|
|
3580
|
+
/** @internal */
|
|
3581
|
+
const _getConstProps = (props) => {
|
|
3582
|
+
if (!props) {
|
|
3583
|
+
return null;
|
|
3584
|
+
}
|
|
3585
|
+
return _CONST_PROPS in props ? props[_CONST_PROPS] : null;
|
|
3586
|
+
};
|
|
3521
3587
|
|
|
3522
3588
|
const isForeignObjectElement = (elementName) => {
|
|
3523
3589
|
return isDev ? elementName.toLowerCase() === 'foreignobject' : elementName === 'foreignObject';
|
|
@@ -3838,6 +3904,10 @@ const vnode_diff = (container, jsxNode, vStartNode, scopedStyleIdPrefix) => {
|
|
|
3838
3904
|
vNewNode = null;
|
|
3839
3905
|
vCurrent = vnode_getFirstChild(vStartNode);
|
|
3840
3906
|
stackPush(jsxNode, true);
|
|
3907
|
+
if (vParent[0 /* VNodeProps.flags */] & 32 /* VNodeFlags.Deleted */) {
|
|
3908
|
+
// Ignore diff if the parent is deleted.
|
|
3909
|
+
return;
|
|
3910
|
+
}
|
|
3841
3911
|
while (stack.length) {
|
|
3842
3912
|
while (jsxIdx < jsxCount) {
|
|
3843
3913
|
assertFalse(vParent === vCurrent, "Parent and current can't be the same");
|
|
@@ -4266,8 +4336,10 @@ const vnode_diff = (container, jsxNode, vStartNode, scopedStyleIdPrefix) => {
|
|
|
4266
4336
|
value = trackSignalAndAssignHost(value, vNewNode, key, container, signalData);
|
|
4267
4337
|
}
|
|
4268
4338
|
if (key === dangerouslySetInnerHTML) {
|
|
4269
|
-
|
|
4270
|
-
|
|
4339
|
+
if (value) {
|
|
4340
|
+
element.innerHTML = String(value);
|
|
4341
|
+
element.setAttribute(QContainerAttr, "html" /* QContainerValue.HTML */);
|
|
4342
|
+
}
|
|
4271
4343
|
continue;
|
|
4272
4344
|
}
|
|
4273
4345
|
if (elementName === 'textarea' && key === 'value') {
|
|
@@ -4368,8 +4440,13 @@ const vnode_diff = (container, jsxNode, vStartNode, scopedStyleIdPrefix) => {
|
|
|
4368
4440
|
let returnValue = false;
|
|
4369
4441
|
qrls.flat(2).forEach((qrl) => {
|
|
4370
4442
|
if (qrl) {
|
|
4371
|
-
|
|
4372
|
-
|
|
4443
|
+
if (isSyncQrl(qrl)) {
|
|
4444
|
+
qrl(event, element);
|
|
4445
|
+
}
|
|
4446
|
+
else {
|
|
4447
|
+
const value = container.$scheduler$(2 /* ChoreType.RUN_QRL */, vNode, qrl, [event, element]);
|
|
4448
|
+
returnValue = returnValue || value === true;
|
|
4449
|
+
}
|
|
4373
4450
|
}
|
|
4374
4451
|
});
|
|
4375
4452
|
return returnValue;
|
|
@@ -4871,7 +4948,7 @@ function cleanup(container, vNode) {
|
|
|
4871
4948
|
const task = obj;
|
|
4872
4949
|
clearAllEffects(container, task);
|
|
4873
4950
|
if (task.$flags$ & 1 /* TaskFlags.VISIBLE_TASK */) {
|
|
4874
|
-
container.$scheduler$(
|
|
4951
|
+
container.$scheduler$(32 /* ChoreType.CLEANUP_VISIBLE */, task);
|
|
4875
4952
|
}
|
|
4876
4953
|
else {
|
|
4877
4954
|
cleanupTask(task);
|
|
@@ -4922,7 +4999,16 @@ function cleanup(container, vNode) {
|
|
|
4922
4999
|
*/
|
|
4923
5000
|
const vFirstChild = vnode_getFirstChild(vCursor);
|
|
4924
5001
|
if (vFirstChild) {
|
|
4925
|
-
vnode_walkVNode(vFirstChild)
|
|
5002
|
+
vnode_walkVNode(vFirstChild, (vNode) => {
|
|
5003
|
+
/**
|
|
5004
|
+
* Instead of an ID, we store a direct reference to the VNode. This is necessary to
|
|
5005
|
+
* locate the slot's parent in a detached subtree, as the ID would become invalid.
|
|
5006
|
+
*/
|
|
5007
|
+
if (vNode[0 /* VNodeProps.flags */] & 2 /* VNodeFlags.Virtual */) {
|
|
5008
|
+
// The QSlotParent is used to find the slot parent during scheduling
|
|
5009
|
+
vnode_getProp(vNode, QSlotParent, (id) => vnode_locate(container.rootVNode, id));
|
|
5010
|
+
}
|
|
5011
|
+
});
|
|
4926
5012
|
return;
|
|
4927
5013
|
}
|
|
4928
5014
|
}
|
|
@@ -4999,8 +5085,8 @@ const useResourceQrl = (qrl, opts) => {
|
|
|
4999
5085
|
const resource = createResourceReturn(container, opts);
|
|
5000
5086
|
const el = iCtx.$hostElement$;
|
|
5001
5087
|
const task = new Task(8 /* TaskFlags.DIRTY */ | 4 /* TaskFlags.RESOURCE */, i, el, qrl, resource, null);
|
|
5002
|
-
container.$scheduler$(3 /* ChoreType.TASK */, task);
|
|
5003
5088
|
set(resource);
|
|
5089
|
+
runResource(task, container, el);
|
|
5004
5090
|
return resource;
|
|
5005
5091
|
};
|
|
5006
5092
|
// <docs markdown="../readme.md#useResource">
|
|
@@ -5068,10 +5154,10 @@ const Resource = (props) => {
|
|
|
5068
5154
|
function getResourceValueAsPromise(props) {
|
|
5069
5155
|
const resource = props.value;
|
|
5070
5156
|
if (isResourceReturn(resource)) {
|
|
5157
|
+
// create a subscription for the resource._state changes
|
|
5158
|
+
const state = resource._state;
|
|
5071
5159
|
const isBrowser = !isServerPlatform();
|
|
5072
5160
|
if (isBrowser) {
|
|
5073
|
-
// create a subscription for the resource._state changes
|
|
5074
|
-
const state = resource._state;
|
|
5075
5161
|
if (state === 'pending' && props.onPending) {
|
|
5076
5162
|
return Promise.resolve().then(useBindInvokeContext(props.onPending));
|
|
5077
5163
|
}
|
|
@@ -5086,14 +5172,7 @@ function getResourceValueAsPromise(props) {
|
|
|
5086
5172
|
}
|
|
5087
5173
|
}
|
|
5088
5174
|
}
|
|
5089
|
-
|
|
5090
|
-
if (value) {
|
|
5091
|
-
return value.then(useBindInvokeContext(props.onResolved), useBindInvokeContext(props.onRejected));
|
|
5092
|
-
}
|
|
5093
|
-
else {
|
|
5094
|
-
// this is temporary value until the `runResource` is executed and promise is assigned to the value
|
|
5095
|
-
return Promise.resolve(undefined);
|
|
5096
|
-
}
|
|
5175
|
+
return untrack(() => resource.value).then(useBindInvokeContext(props.onResolved), useBindInvokeContext(props.onRejected));
|
|
5097
5176
|
}
|
|
5098
5177
|
else if (isPromise(resource)) {
|
|
5099
5178
|
return resource.then(useBindInvokeContext(props.onResolved), useBindInvokeContext(props.onRejected));
|
|
@@ -5111,7 +5190,7 @@ const _createResourceReturn = (opts) => {
|
|
|
5111
5190
|
const resource = {
|
|
5112
5191
|
__brand: 'resource',
|
|
5113
5192
|
value: undefined,
|
|
5114
|
-
loading: isServerPlatform()
|
|
5193
|
+
loading: !isServerPlatform(),
|
|
5115
5194
|
_resolved: undefined,
|
|
5116
5195
|
_error: undefined,
|
|
5117
5196
|
_state: 'pending',
|
|
@@ -5162,19 +5241,22 @@ const runResource = (task, container, host) => {
|
|
|
5162
5241
|
done = true;
|
|
5163
5242
|
if (resolved) {
|
|
5164
5243
|
done = true;
|
|
5165
|
-
|
|
5166
|
-
|
|
5167
|
-
|
|
5168
|
-
|
|
5244
|
+
resourceTarget.loading = false;
|
|
5245
|
+
resourceTarget._state = 'resolved';
|
|
5246
|
+
resourceTarget._resolved = value;
|
|
5247
|
+
resourceTarget._error = undefined;
|
|
5169
5248
|
resolve(value);
|
|
5170
5249
|
}
|
|
5171
5250
|
else {
|
|
5172
5251
|
done = true;
|
|
5173
|
-
|
|
5174
|
-
|
|
5175
|
-
|
|
5252
|
+
resourceTarget.loading = false;
|
|
5253
|
+
resourceTarget._state = 'rejected';
|
|
5254
|
+
resourceTarget._error = value;
|
|
5176
5255
|
reject(value);
|
|
5177
5256
|
}
|
|
5257
|
+
if (!isServerPlatform()) {
|
|
5258
|
+
forceStoreEffects(resource, '_state');
|
|
5259
|
+
}
|
|
5178
5260
|
return true;
|
|
5179
5261
|
}
|
|
5180
5262
|
return false;
|
|
@@ -5190,17 +5272,17 @@ const runResource = (task, container, host) => {
|
|
|
5190
5272
|
}
|
|
5191
5273
|
});
|
|
5192
5274
|
// Execute mutation inside empty invocation
|
|
5275
|
+
// TODO: is it right? why we need to invoke inside context and trigger effects?
|
|
5193
5276
|
invoke(iCtx, () => {
|
|
5194
5277
|
// console.log('RESOURCE.pending: ');
|
|
5195
5278
|
resource._state = 'pending';
|
|
5196
5279
|
resource.loading = !isServerPlatform();
|
|
5197
|
-
|
|
5280
|
+
resource.value = new Promise((r, re) => {
|
|
5198
5281
|
resolve = r;
|
|
5199
5282
|
reject = re;
|
|
5200
|
-
})
|
|
5201
|
-
promise.catch(ignoreErrorToPreventNodeFromCrashing);
|
|
5283
|
+
});
|
|
5202
5284
|
});
|
|
5203
|
-
const promise = safeCall(() =>
|
|
5285
|
+
const promise = safeCall(() => taskFn(opts), (value) => {
|
|
5204
5286
|
setState(true, value);
|
|
5205
5287
|
}, (err) => {
|
|
5206
5288
|
if (isPromise(err)) {
|
|
@@ -5223,10 +5305,6 @@ const runResource = (task, container, host) => {
|
|
|
5223
5305
|
}
|
|
5224
5306
|
return promise;
|
|
5225
5307
|
};
|
|
5226
|
-
const ignoreErrorToPreventNodeFromCrashing = (err) => {
|
|
5227
|
-
// ignore error to prevent node from crashing
|
|
5228
|
-
// node will crash in promise is rejected and no one is listening to the rejection.
|
|
5229
|
-
};
|
|
5230
5308
|
|
|
5231
5309
|
/// These global variables are used to avoid creating new arrays for each call to `vnode_documentPosition`.
|
|
5232
5310
|
const aVNodePath = [];
|
|
@@ -5309,11 +5387,11 @@ const ssrNodeDocumentPosition = (a, b) => {
|
|
|
5309
5387
|
let bDepth = -1;
|
|
5310
5388
|
while (a) {
|
|
5311
5389
|
const ssrNode = (aSsrNodePath[++aDepth] = a);
|
|
5312
|
-
a = ssrNode.
|
|
5390
|
+
a = ssrNode.parentComponent;
|
|
5313
5391
|
}
|
|
5314
5392
|
while (b) {
|
|
5315
5393
|
const ssrNode = (bSsrNodePath[++bDepth] = b);
|
|
5316
|
-
b = ssrNode.
|
|
5394
|
+
b = ssrNode.parentComponent;
|
|
5317
5395
|
}
|
|
5318
5396
|
while (aDepth >= 0 && bDepth >= 0) {
|
|
5319
5397
|
a = aSsrNodePath[aDepth];
|
|
@@ -5330,6 +5408,210 @@ const ssrNodeDocumentPosition = (a, b) => {
|
|
|
5330
5408
|
return aDepth < bDepth ? -1 : 1;
|
|
5331
5409
|
};
|
|
5332
5410
|
|
|
5411
|
+
/**
|
|
5412
|
+
* Rules for determining if a chore is blocked by another chore. Some chores can block other chores.
|
|
5413
|
+
* They cannot run until the blocking chore has completed.
|
|
5414
|
+
*
|
|
5415
|
+
* The match function is used to determine if the blocked chore is blocked by the blocking chore.
|
|
5416
|
+
* The match function is called with the blocked chore, the blocking chore, and the container.
|
|
5417
|
+
*/
|
|
5418
|
+
const VISIBLE_BLOCKING_RULES = [
|
|
5419
|
+
// NODE_DIFF blocks VISIBLE on same host,
|
|
5420
|
+
// if the blocked chore is a child of the blocking chore
|
|
5421
|
+
// or the blocked chore is a sibling of the blocking chore
|
|
5422
|
+
{
|
|
5423
|
+
blockedType: 16 /* ChoreType.VISIBLE */,
|
|
5424
|
+
blockingType: 4 /* ChoreType.NODE_DIFF */,
|
|
5425
|
+
match: (blocked, blocking, container) => isDescendant(blocked, blocking, container) || isDescendant(blocking, blocked, container),
|
|
5426
|
+
},
|
|
5427
|
+
// COMPONENT blocks VISIBLE on same host
|
|
5428
|
+
// if the blocked chore is a child of the blocking chore
|
|
5429
|
+
// or the blocked chore is a sibling of the blocking chore
|
|
5430
|
+
{
|
|
5431
|
+
blockedType: 16 /* ChoreType.VISIBLE */,
|
|
5432
|
+
blockingType: 6 /* ChoreType.COMPONENT */,
|
|
5433
|
+
match: (blocked, blocking, container) => isDescendant(blocked, blocking, container) || isDescendant(blocking, blocked, container),
|
|
5434
|
+
},
|
|
5435
|
+
];
|
|
5436
|
+
const BLOCKING_RULES = [
|
|
5437
|
+
// QRL_RESOLVE blocks RUN_QRL, TASK, VISIBLE on same host
|
|
5438
|
+
{
|
|
5439
|
+
blockedType: 2 /* ChoreType.RUN_QRL */,
|
|
5440
|
+
blockingType: 1 /* ChoreType.QRL_RESOLVE */,
|
|
5441
|
+
match: (blocked, blocking) => {
|
|
5442
|
+
const blockedQrl = blocked.$target$;
|
|
5443
|
+
const blockingQrl = blocking.$target$;
|
|
5444
|
+
return isSameHost(blocked, blocking) && isSameQrl(blockedQrl, blockingQrl);
|
|
5445
|
+
},
|
|
5446
|
+
},
|
|
5447
|
+
{
|
|
5448
|
+
blockedType: 3 /* ChoreType.TASK */,
|
|
5449
|
+
blockingType: 1 /* ChoreType.QRL_RESOLVE */,
|
|
5450
|
+
match: (blocked, blocking) => {
|
|
5451
|
+
const blockedTask = blocked.$payload$;
|
|
5452
|
+
const blockingQrl = blocking.$target$;
|
|
5453
|
+
return isSameHost(blocked, blocking) && isSameQrl(blockedTask.$qrl$, blockingQrl);
|
|
5454
|
+
},
|
|
5455
|
+
},
|
|
5456
|
+
{
|
|
5457
|
+
blockedType: 16 /* ChoreType.VISIBLE */,
|
|
5458
|
+
blockingType: 1 /* ChoreType.QRL_RESOLVE */,
|
|
5459
|
+
match: (blocked, blocking) => {
|
|
5460
|
+
const blockedTask = blocked.$payload$;
|
|
5461
|
+
const blockingQrl = blocking.$target$;
|
|
5462
|
+
return isSameHost(blocked, blocking) && isSameQrl(blockedTask.$qrl$, blockingQrl);
|
|
5463
|
+
},
|
|
5464
|
+
},
|
|
5465
|
+
// COMPONENT blocks NODE_DIFF, NODE_PROP on same host
|
|
5466
|
+
{
|
|
5467
|
+
blockedType: 4 /* ChoreType.NODE_DIFF */,
|
|
5468
|
+
blockingType: 6 /* ChoreType.COMPONENT */,
|
|
5469
|
+
match: (blocked, blocking) => blocked.$host$ === blocking.$host$,
|
|
5470
|
+
},
|
|
5471
|
+
{
|
|
5472
|
+
blockedType: 5 /* ChoreType.NODE_PROP */,
|
|
5473
|
+
blockingType: 6 /* ChoreType.COMPONENT */,
|
|
5474
|
+
match: (blocked, blocking) => blocked.$host$ === blocking.$host$,
|
|
5475
|
+
},
|
|
5476
|
+
...VISIBLE_BLOCKING_RULES,
|
|
5477
|
+
// TASK blocks subsequent TASKs in the same component
|
|
5478
|
+
{
|
|
5479
|
+
blockedType: 3 /* ChoreType.TASK */,
|
|
5480
|
+
blockingType: 3 /* ChoreType.TASK */,
|
|
5481
|
+
match: (blocked, blocking, container) => {
|
|
5482
|
+
if (blocked.$host$ !== blocking.$host$) {
|
|
5483
|
+
return false;
|
|
5484
|
+
}
|
|
5485
|
+
const blockedIdx = blocked.$idx$;
|
|
5486
|
+
if (!isNumber$1(blockedIdx) || blockedIdx <= 0) {
|
|
5487
|
+
return false;
|
|
5488
|
+
}
|
|
5489
|
+
const previousTask = findPreviousTaskInComponent(blocked.$host$, blockedIdx, container);
|
|
5490
|
+
return previousTask === blocking.$payload$;
|
|
5491
|
+
},
|
|
5492
|
+
},
|
|
5493
|
+
];
|
|
5494
|
+
function isDescendant(descendantChore, ancestorChore, container) {
|
|
5495
|
+
const descendantHost = descendantChore.$host$;
|
|
5496
|
+
const ancestorHost = ancestorChore.$host$;
|
|
5497
|
+
if (!vnode_isVNode(descendantHost) || !vnode_isVNode(ancestorHost)) {
|
|
5498
|
+
return false;
|
|
5499
|
+
}
|
|
5500
|
+
return vnode_isDescendantOf(descendantHost, ancestorHost, container.rootVNode);
|
|
5501
|
+
}
|
|
5502
|
+
function isSameHost(a, b) {
|
|
5503
|
+
return a.$host$ === b.$host$;
|
|
5504
|
+
}
|
|
5505
|
+
function isSameQrl(a, b) {
|
|
5506
|
+
return a.$symbol$ === b.$symbol$;
|
|
5507
|
+
}
|
|
5508
|
+
function findBlockingChoreInQueue(chore, choreQueue, container) {
|
|
5509
|
+
for (const candidate of choreQueue) {
|
|
5510
|
+
// everything after VISIBLE is not blocking. Visible task, task and resource should not block anything in this rule.
|
|
5511
|
+
if (candidate.$type$ >= 16 /* ChoreType.VISIBLE */ || candidate.$type$ === 3 /* ChoreType.TASK */) {
|
|
5512
|
+
continue;
|
|
5513
|
+
}
|
|
5514
|
+
if (isDescendant(chore, candidate, container)) {
|
|
5515
|
+
return candidate;
|
|
5516
|
+
}
|
|
5517
|
+
}
|
|
5518
|
+
return null;
|
|
5519
|
+
}
|
|
5520
|
+
function findBlockingChore(chore, choreQueue, blockedChores, runningChores, container) {
|
|
5521
|
+
const blockingChoreInChoreQueue = findBlockingChoreInQueue(chore, choreQueue, container);
|
|
5522
|
+
if (blockingChoreInChoreQueue) {
|
|
5523
|
+
return blockingChoreInChoreQueue;
|
|
5524
|
+
}
|
|
5525
|
+
const blockingChoreInBlockedChores = findBlockingChoreInQueue(chore, Array.from(blockedChores), container);
|
|
5526
|
+
if (blockingChoreInBlockedChores) {
|
|
5527
|
+
return blockingChoreInBlockedChores;
|
|
5528
|
+
}
|
|
5529
|
+
const blockingChoreInRunningChores = findBlockingChoreInQueue(chore, Array.from(runningChores), container);
|
|
5530
|
+
if (blockingChoreInRunningChores) {
|
|
5531
|
+
return blockingChoreInRunningChores;
|
|
5532
|
+
}
|
|
5533
|
+
for (const rule of BLOCKING_RULES) {
|
|
5534
|
+
if (chore.$type$ !== rule.blockedType) {
|
|
5535
|
+
continue;
|
|
5536
|
+
}
|
|
5537
|
+
// Check in choreQueue
|
|
5538
|
+
// TODO(perf): better to iterate in reverse order?
|
|
5539
|
+
for (const candidate of choreQueue) {
|
|
5540
|
+
if (candidate.$type$ === rule.blockingType && rule.match(chore, candidate, container)) {
|
|
5541
|
+
return candidate;
|
|
5542
|
+
}
|
|
5543
|
+
}
|
|
5544
|
+
// Check in blockedChores
|
|
5545
|
+
for (const candidate of blockedChores) {
|
|
5546
|
+
if (candidate.$type$ === rule.blockingType && rule.match(chore, candidate, container)) {
|
|
5547
|
+
return candidate;
|
|
5548
|
+
}
|
|
5549
|
+
}
|
|
5550
|
+
// Check in runningChores
|
|
5551
|
+
for (const candidate of runningChores) {
|
|
5552
|
+
if (candidate.$type$ === rule.blockingType && rule.match(chore, candidate, container)) {
|
|
5553
|
+
return candidate;
|
|
5554
|
+
}
|
|
5555
|
+
}
|
|
5556
|
+
}
|
|
5557
|
+
return null;
|
|
5558
|
+
}
|
|
5559
|
+
function findPreviousTaskInComponent(host, currentTaskIdx, container) {
|
|
5560
|
+
const elementSeq = container.getHostProp(host, ELEMENT_SEQ);
|
|
5561
|
+
if (!elementSeq || elementSeq.length <= currentTaskIdx) {
|
|
5562
|
+
return null;
|
|
5563
|
+
}
|
|
5564
|
+
for (let i = currentTaskIdx - 1; i >= 0; i--) {
|
|
5565
|
+
const candidate = elementSeq[i];
|
|
5566
|
+
if (candidate instanceof Task && candidate.$flags$ & 2 /* TaskFlags.TASK */) {
|
|
5567
|
+
return candidate;
|
|
5568
|
+
}
|
|
5569
|
+
}
|
|
5570
|
+
return null;
|
|
5571
|
+
}
|
|
5572
|
+
function findBlockingChoreForVisible(chore, runningChores, container) {
|
|
5573
|
+
for (const rule of VISIBLE_BLOCKING_RULES) {
|
|
5574
|
+
if (chore.$type$ !== rule.blockedType) {
|
|
5575
|
+
continue;
|
|
5576
|
+
}
|
|
5577
|
+
for (const candidate of runningChores) {
|
|
5578
|
+
if (candidate.$type$ === rule.blockingType && rule.match(chore, candidate, container)) {
|
|
5579
|
+
return candidate;
|
|
5580
|
+
}
|
|
5581
|
+
}
|
|
5582
|
+
}
|
|
5583
|
+
return null;
|
|
5584
|
+
}
|
|
5585
|
+
|
|
5586
|
+
// This can't be in platform.ts because it uses MessageChannel which cannot post messages with functions
|
|
5587
|
+
// TODO: move this to platform.ts somehow
|
|
5588
|
+
const createNextTick = (fn) => {
|
|
5589
|
+
let nextTick;
|
|
5590
|
+
// according to the https://developer.mozilla.org/en-US/docs/Web/API/Window/setImmediate#notes
|
|
5591
|
+
if (typeof setImmediate === 'function') {
|
|
5592
|
+
// setImmediate is the fastest way to schedule a task, but works only in node.js
|
|
5593
|
+
nextTick = () => {
|
|
5594
|
+
setImmediate(fn);
|
|
5595
|
+
};
|
|
5596
|
+
}
|
|
5597
|
+
else if (typeof MessageChannel !== 'undefined') {
|
|
5598
|
+
const channel = new MessageChannel();
|
|
5599
|
+
channel.port1.onmessage = () => {
|
|
5600
|
+
fn();
|
|
5601
|
+
};
|
|
5602
|
+
nextTick = () => {
|
|
5603
|
+
channel.port2.postMessage(null);
|
|
5604
|
+
};
|
|
5605
|
+
}
|
|
5606
|
+
else {
|
|
5607
|
+
// setTimeout is a fallback, creates 4ms delay
|
|
5608
|
+
nextTick = () => {
|
|
5609
|
+
setTimeout(fn);
|
|
5610
|
+
};
|
|
5611
|
+
}
|
|
5612
|
+
return nextTick;
|
|
5613
|
+
};
|
|
5614
|
+
|
|
5333
5615
|
/**
|
|
5334
5616
|
* Scheduler is responsible for running application code in predictable order.
|
|
5335
5617
|
*
|
|
@@ -5413,29 +5695,43 @@ const ssrNodeDocumentPosition = (a, b) => {
|
|
|
5413
5695
|
*/
|
|
5414
5696
|
// Turn this on to get debug output of what the scheduler is doing.
|
|
5415
5697
|
const DEBUG = false;
|
|
5416
|
-
|
|
5417
|
-
|
|
5418
|
-
|
|
5419
|
-
|
|
5420
|
-
|
|
5421
|
-
|
|
5422
|
-
|
|
5698
|
+
var ChoreState;
|
|
5699
|
+
(function (ChoreState) {
|
|
5700
|
+
ChoreState[ChoreState["NONE"] = 0] = "NONE";
|
|
5701
|
+
ChoreState[ChoreState["RUNNING"] = 1] = "RUNNING";
|
|
5702
|
+
ChoreState[ChoreState["FAILED"] = 2] = "FAILED";
|
|
5703
|
+
ChoreState[ChoreState["DONE"] = 3] = "DONE";
|
|
5704
|
+
})(ChoreState || (ChoreState = {}));
|
|
5705
|
+
const getChorePromise = (chore) => chore.$state$ === ChoreState.NONE
|
|
5706
|
+
? (chore.$returnValue$ ||= new Promise((resolve, reject) => {
|
|
5707
|
+
chore.$resolve$ = resolve;
|
|
5708
|
+
chore.$reject$ = reject;
|
|
5709
|
+
}))
|
|
5710
|
+
: chore.$returnValue$;
|
|
5711
|
+
const createScheduler = (container, journalFlush, choreQueue = [], blockedChores = new Set(), runningChores = new Set()) => {
|
|
5712
|
+
let drainChore = null;
|
|
5423
5713
|
let drainScheduled = false;
|
|
5714
|
+
let isDraining = false;
|
|
5715
|
+
let isJournalFlushRunning = false;
|
|
5716
|
+
let flushBudgetStart = 0;
|
|
5717
|
+
let currentTime = performance.now();
|
|
5718
|
+
const nextTick = createNextTick(drainChoreQueue);
|
|
5719
|
+
function drainInNextTick() {
|
|
5720
|
+
if (!drainScheduled) {
|
|
5721
|
+
drainScheduled = true;
|
|
5722
|
+
nextTick();
|
|
5723
|
+
}
|
|
5724
|
+
}
|
|
5725
|
+
// Drain for ~16.67ms, then apply journal flush for ~16.67ms, then repeat
|
|
5726
|
+
// We divide by 60 because we want to run at 60fps
|
|
5727
|
+
const FREQUENCY_MS = Math.floor(1000 / 60);
|
|
5424
5728
|
return schedule;
|
|
5425
5729
|
///// IMPLEMENTATION /////
|
|
5426
5730
|
function schedule(type, hostOrTask = null, targetOrQrl = null, payload = null) {
|
|
5427
|
-
|
|
5428
|
-
|
|
5429
|
-
const runLater = type !== 255 /* ChoreType.WAIT_FOR_ALL */ && !isComponentSsr && type !== 2 /* ChoreType.RUN_QRL */;
|
|
5430
|
-
const isTask = type === 3 /* ChoreType.TASK */ || type === 32 /* ChoreType.VISIBLE */ || type === 48 /* ChoreType.CLEANUP_VISIBLE */;
|
|
5431
|
-
const isClientOnly = type === 16 /* ChoreType.JOURNAL_FLUSH */ ||
|
|
5432
|
-
type === 4 /* ChoreType.NODE_DIFF */ ||
|
|
5433
|
-
type === 5 /* ChoreType.NODE_PROP */ ||
|
|
5434
|
-
type === 1 /* ChoreType.QRL_RESOLVE */ ||
|
|
5435
|
-
type === 7 /* ChoreType.RECOMPUTE_AND_SCHEDULE_EFFECTS */;
|
|
5436
|
-
if (isServer && isClientOnly) {
|
|
5437
|
-
return;
|
|
5731
|
+
if (type === 255 /* ChoreType.WAIT_FOR_QUEUE */ && drainChore) {
|
|
5732
|
+
return drainChore;
|
|
5438
5733
|
}
|
|
5734
|
+
const isTask = type === 3 /* ChoreType.TASK */ || type === 16 /* ChoreType.VISIBLE */ || type === 32 /* ChoreType.CLEANUP_VISIBLE */;
|
|
5439
5735
|
if (isTask) {
|
|
5440
5736
|
hostOrTask.$flags$ |= 8 /* TaskFlags.DIRTY */;
|
|
5441
5737
|
}
|
|
@@ -5449,167 +5745,310 @@ const createScheduler = (container, scheduleDrain, journalFlush) => {
|
|
|
5449
5745
|
$host$: isTask ? hostOrTask.$el$ : hostOrTask,
|
|
5450
5746
|
$target$: targetOrQrl,
|
|
5451
5747
|
$payload$: isTask ? hostOrTask : payload,
|
|
5452
|
-
$
|
|
5453
|
-
$
|
|
5748
|
+
$state$: ChoreState.NONE,
|
|
5749
|
+
$blockedChores$: null,
|
|
5750
|
+
$startTime$: undefined,
|
|
5751
|
+
$endTime$: undefined,
|
|
5752
|
+
$resolve$: undefined,
|
|
5753
|
+
$reject$: undefined,
|
|
5454
5754
|
$returnValue$: null,
|
|
5455
|
-
$executed$: false,
|
|
5456
5755
|
};
|
|
5457
|
-
|
|
5458
|
-
|
|
5459
|
-
|
|
5460
|
-
|
|
5461
|
-
|
|
5462
|
-
|
|
5463
|
-
|
|
5756
|
+
if (type === 255 /* ChoreType.WAIT_FOR_QUEUE */) {
|
|
5757
|
+
getChorePromise(chore);
|
|
5758
|
+
drainChore = chore;
|
|
5759
|
+
drainInNextTick();
|
|
5760
|
+
return chore;
|
|
5761
|
+
}
|
|
5762
|
+
const isServer = isServerPlatform();
|
|
5763
|
+
const isClientOnly = type === 4 /* ChoreType.NODE_DIFF */ || type === 1 /* ChoreType.QRL_RESOLVE */;
|
|
5764
|
+
if (isServer && isClientOnly) {
|
|
5765
|
+
// Mark skipped client-only chores as completed on the server
|
|
5766
|
+
finishChore(chore, undefined);
|
|
5767
|
+
return chore;
|
|
5768
|
+
}
|
|
5769
|
+
if (isServer && chore.$host$ && isSsrNode(chore.$host$)) {
|
|
5770
|
+
const isUpdatable = !!(chore.$host$.flags & 1 /* SsrNodeFlags.Updatable */);
|
|
5771
|
+
if (!isUpdatable) {
|
|
5772
|
+
if (
|
|
5773
|
+
// backpatching exceptions:
|
|
5774
|
+
// - node prop is allowed because it is used to update the node property
|
|
5775
|
+
// - recompute and schedule effects because it triggers effects (so node prop too)
|
|
5776
|
+
chore.$type$ !== 5 /* ChoreType.NODE_PROP */ &&
|
|
5777
|
+
chore.$type$ !== 7 /* ChoreType.RECOMPUTE_AND_SCHEDULE_EFFECTS */) {
|
|
5778
|
+
// We are running on the server.
|
|
5779
|
+
// On server we can't schedule task for a different host!
|
|
5780
|
+
// Server is SSR, and therefore scheduling for anything but the current host
|
|
5781
|
+
// implies that things need to be re-run and that is not supported because of streaming.
|
|
5782
|
+
const warningMessage = `A '${choreTypeToName(chore.$type$)}' chore was scheduled on a host element that has already been streamed to the client.
|
|
5783
|
+
This can lead to inconsistencies between Server-Side Rendering (SSR) and Client-Side Rendering (CSR).
|
|
5784
|
+
|
|
5785
|
+
Problematic chore:
|
|
5786
|
+
- Type: ${choreTypeToName(chore.$type$)}
|
|
5787
|
+
- Host: ${chore.$host$.toString()}
|
|
5788
|
+
- Nearest element location: ${chore.$host$.currentFile}
|
|
5789
|
+
|
|
5790
|
+
This is often caused by modifying a signal in an already rendered component during SSR.`;
|
|
5791
|
+
logWarn(warningMessage);
|
|
5792
|
+
return chore;
|
|
5793
|
+
}
|
|
5794
|
+
}
|
|
5795
|
+
}
|
|
5796
|
+
const blockingChore = findBlockingChore(chore, choreQueue, blockedChores, runningChores, container);
|
|
5797
|
+
if (blockingChore) {
|
|
5798
|
+
addBlockedChore(chore, blockingChore, blockedChores);
|
|
5799
|
+
return chore;
|
|
5464
5800
|
}
|
|
5465
|
-
|
|
5466
|
-
|
|
5467
|
-
|
|
5801
|
+
chore = sortedInsert(choreQueue, chore, container.rootVNode || null);
|
|
5802
|
+
const runImmediately = (isServer && type === 6 /* ChoreType.COMPONENT */) || type === 2 /* ChoreType.RUN_QRL */;
|
|
5803
|
+
if (runImmediately && !isDraining) {
|
|
5804
|
+
immediateDrain();
|
|
5468
5805
|
}
|
|
5469
5806
|
else {
|
|
5470
|
-
|
|
5471
|
-
}
|
|
5472
|
-
|
|
5473
|
-
|
|
5474
|
-
function
|
|
5475
|
-
|
|
5476
|
-
|
|
5477
|
-
|
|
5478
|
-
|
|
5479
|
-
|
|
5480
|
-
|
|
5481
|
-
|
|
5482
|
-
|
|
5483
|
-
|
|
5484
|
-
|
|
5485
|
-
|
|
5486
|
-
|
|
5807
|
+
drainInNextTick();
|
|
5808
|
+
}
|
|
5809
|
+
return chore;
|
|
5810
|
+
}
|
|
5811
|
+
function immediateDrain() {
|
|
5812
|
+
drainScheduled = true;
|
|
5813
|
+
drainChoreQueue();
|
|
5814
|
+
}
|
|
5815
|
+
////////////////////////////////////////////////////////////////////////////////
|
|
5816
|
+
// Drain queue helpers
|
|
5817
|
+
////////////////////////////////////////////////////////////////////////////////
|
|
5818
|
+
function applyJournalFlush() {
|
|
5819
|
+
if (!isJournalFlushRunning) {
|
|
5820
|
+
// prevent multiple journal flushes from running at the same time
|
|
5821
|
+
isJournalFlushRunning = true;
|
|
5822
|
+
journalFlush();
|
|
5823
|
+
isJournalFlushRunning = false;
|
|
5824
|
+
flushBudgetStart = performance.now();
|
|
5825
|
+
}
|
|
5826
|
+
}
|
|
5827
|
+
function shouldApplyJournalFlush(isServer) {
|
|
5828
|
+
return !isServer && currentTime - flushBudgetStart >= FREQUENCY_MS;
|
|
5829
|
+
}
|
|
5830
|
+
function drainChoreQueue() {
|
|
5831
|
+
const isServer = isServerPlatform();
|
|
5832
|
+
drainScheduled = false;
|
|
5833
|
+
if (isDraining) {
|
|
5834
|
+
return;
|
|
5835
|
+
}
|
|
5836
|
+
// early return if the queue is empty
|
|
5837
|
+
if (!choreQueue.length) {
|
|
5838
|
+
applyJournalFlush();
|
|
5839
|
+
if (drainChore && !runningChores.size) {
|
|
5840
|
+
// resolve drainChore only if there are no running chores, because
|
|
5841
|
+
// we are sure that we are done
|
|
5842
|
+
drainChore.$resolve$(null);
|
|
5843
|
+
drainChore = null;
|
|
5487
5844
|
}
|
|
5488
|
-
|
|
5489
|
-
|
|
5490
|
-
|
|
5491
|
-
|
|
5492
|
-
|
|
5845
|
+
return;
|
|
5846
|
+
}
|
|
5847
|
+
isDraining = true;
|
|
5848
|
+
flushBudgetStart = performance.now();
|
|
5849
|
+
const maybeFinishDrain = () => {
|
|
5850
|
+
if (choreQueue.length) {
|
|
5851
|
+
drainInNextTick();
|
|
5852
|
+
return false;
|
|
5853
|
+
}
|
|
5854
|
+
if (drainChore && runningChores.size) {
|
|
5855
|
+
if (shouldApplyJournalFlush(isServer)) {
|
|
5856
|
+
// apply journal flush even if we are not finished draining the queue
|
|
5857
|
+
applyJournalFlush();
|
|
5493
5858
|
}
|
|
5494
|
-
|
|
5859
|
+
return false;
|
|
5495
5860
|
}
|
|
5496
|
-
|
|
5497
|
-
|
|
5498
|
-
|
|
5499
|
-
|
|
5500
|
-
|
|
5861
|
+
currentChore = null;
|
|
5862
|
+
applyJournalFlush();
|
|
5863
|
+
drainChore?.$resolve$(null);
|
|
5864
|
+
drainChore = null;
|
|
5865
|
+
return true;
|
|
5866
|
+
};
|
|
5867
|
+
const scheduleBlockedChoresAndDrainIfNeeded = (chore) => {
|
|
5868
|
+
let blockedChoresScheduled = false;
|
|
5869
|
+
if (chore.$blockedChores$) {
|
|
5870
|
+
for (const blockedChore of chore.$blockedChores$) {
|
|
5871
|
+
const blockingChore = findBlockingChore(blockedChore, choreQueue, blockedChores, runningChores, container);
|
|
5872
|
+
if (blockingChore) {
|
|
5873
|
+
addBlockedChore(blockedChore, blockingChore, blockedChores);
|
|
5874
|
+
}
|
|
5875
|
+
else {
|
|
5876
|
+
blockedChores.delete(blockedChore);
|
|
5877
|
+
sortedInsert(choreQueue, blockedChore, container.rootVNode || null);
|
|
5878
|
+
blockedChoresScheduled = true;
|
|
5879
|
+
}
|
|
5880
|
+
}
|
|
5881
|
+
chore.$blockedChores$ = null;
|
|
5501
5882
|
}
|
|
5502
|
-
|
|
5503
|
-
|
|
5504
|
-
|
|
5505
|
-
|
|
5506
|
-
|
|
5507
|
-
const host = chore.$host$;
|
|
5508
|
-
assertEqual(currentChore, null, 'Chore already running.');
|
|
5509
|
-
currentChore = chore;
|
|
5510
|
-
let returnValue = null;
|
|
5883
|
+
if (blockedChoresScheduled && !isDraining) {
|
|
5884
|
+
drainInNextTick();
|
|
5885
|
+
}
|
|
5886
|
+
};
|
|
5887
|
+
let currentChore = null;
|
|
5511
5888
|
try {
|
|
5512
|
-
|
|
5513
|
-
|
|
5514
|
-
|
|
5515
|
-
|
|
5516
|
-
|
|
5517
|
-
|
|
5518
|
-
|
|
5519
|
-
|
|
5520
|
-
|
|
5521
|
-
|
|
5522
|
-
|
|
5523
|
-
|
|
5889
|
+
while (choreQueue.length) {
|
|
5890
|
+
currentTime = performance.now();
|
|
5891
|
+
const chore = (currentChore = choreQueue.shift());
|
|
5892
|
+
if (chore.$state$ !== ChoreState.NONE) {
|
|
5893
|
+
continue;
|
|
5894
|
+
}
|
|
5895
|
+
if (vNodeAlreadyDeleted(chore) &&
|
|
5896
|
+
// we need to process cleanup tasks for deleted nodes
|
|
5897
|
+
chore.$type$ !== 32 /* ChoreType.CLEANUP_VISIBLE */) {
|
|
5898
|
+
// skip deleted chore
|
|
5899
|
+
DEBUG && debugTrace('skip chore', chore, choreQueue, blockedChores);
|
|
5900
|
+
continue;
|
|
5901
|
+
}
|
|
5902
|
+
if (chore.$type$ === 16 /* ChoreType.VISIBLE */) {
|
|
5903
|
+
// ensure that the journal flush is applied before the visible chore is executed
|
|
5904
|
+
// so that the visible chore can see the latest DOM changes
|
|
5905
|
+
applyJournalFlush();
|
|
5906
|
+
const blockingChore = findBlockingChoreForVisible(chore, runningChores, container);
|
|
5907
|
+
if (blockingChore && blockingChore.$state$ === ChoreState.RUNNING) {
|
|
5908
|
+
addBlockedChore(chore, blockingChore, blockedChores);
|
|
5909
|
+
continue;
|
|
5524
5910
|
}
|
|
5525
|
-
|
|
5526
|
-
|
|
5527
|
-
|
|
5528
|
-
|
|
5529
|
-
|
|
5530
|
-
|
|
5911
|
+
}
|
|
5912
|
+
// Note that this never throws
|
|
5913
|
+
chore.$startTime$ = performance.now();
|
|
5914
|
+
const result = executeChore(chore, isServer);
|
|
5915
|
+
chore.$returnValue$ = result;
|
|
5916
|
+
if (isPromise(result)) {
|
|
5917
|
+
runningChores.add(chore);
|
|
5918
|
+
chore.$state$ = ChoreState.RUNNING;
|
|
5919
|
+
result
|
|
5920
|
+
.then((value) => {
|
|
5921
|
+
finishChore(chore, value);
|
|
5922
|
+
})
|
|
5923
|
+
.catch((e) => {
|
|
5924
|
+
if (chore.$state$ !== ChoreState.RUNNING) {
|
|
5925
|
+
// we already handled the error
|
|
5926
|
+
return;
|
|
5927
|
+
}
|
|
5928
|
+
handleError(chore, e);
|
|
5929
|
+
})
|
|
5930
|
+
.finally(() => {
|
|
5931
|
+
runningChores.delete(chore);
|
|
5932
|
+
// Note that we ignore failed chores so the app keeps working
|
|
5933
|
+
// TODO decide if this is ok and document it
|
|
5934
|
+
scheduleBlockedChoresAndDrainIfNeeded(chore);
|
|
5935
|
+
// If drainChore is not null, we are waiting for it to finish.
|
|
5936
|
+
// If there are no running chores, we can finish the drain.
|
|
5937
|
+
if (!runningChores.size) {
|
|
5938
|
+
let finished = false;
|
|
5939
|
+
if (drainChore) {
|
|
5940
|
+
finished = maybeFinishDrain();
|
|
5531
5941
|
}
|
|
5532
|
-
|
|
5533
|
-
|
|
5534
|
-
|
|
5942
|
+
if (!finished && !isDraining) {
|
|
5943
|
+
// if finished, then journal flush is already applied
|
|
5944
|
+
applyJournalFlush();
|
|
5535
5945
|
}
|
|
5536
|
-
}, (err) => container.handleError(err, host));
|
|
5537
|
-
}
|
|
5538
|
-
break;
|
|
5539
|
-
case 2 /* ChoreType.RUN_QRL */:
|
|
5540
|
-
{
|
|
5541
|
-
const fn = chore.$target$.getFn();
|
|
5542
|
-
const result = retryOnPromise(() => fn(...chore.$payload$));
|
|
5543
|
-
if (isPromise(result)) {
|
|
5544
|
-
const handled = result
|
|
5545
|
-
.finally(() => {
|
|
5546
|
-
qrlRuns.splice(qrlRuns.indexOf(handled), 1);
|
|
5547
|
-
})
|
|
5548
|
-
.catch((error) => {
|
|
5549
|
-
container.handleError(error, chore.$host$);
|
|
5550
|
-
});
|
|
5551
|
-
// Don't wait for the promise to resolve
|
|
5552
|
-
// TODO come up with a better solution, we also want concurrent signal handling with tasks but serial tasks
|
|
5553
|
-
qrlRuns.push(handled);
|
|
5554
|
-
DEBUG &&
|
|
5555
|
-
debugTrace('execute.DONE (but still running)', chore, currentChore, choreQueue);
|
|
5556
|
-
chore.$returnValue$ = handled;
|
|
5557
|
-
chore.$resolve$?.(handled);
|
|
5558
|
-
currentChore = null;
|
|
5559
|
-
chore.$executed$ = true;
|
|
5560
|
-
// early out so we don't call after()
|
|
5561
|
-
return;
|
|
5562
5946
|
}
|
|
5563
|
-
|
|
5564
|
-
|
|
5565
|
-
|
|
5566
|
-
|
|
5567
|
-
|
|
5568
|
-
|
|
5569
|
-
|
|
5570
|
-
|
|
5571
|
-
|
|
5572
|
-
|
|
5573
|
-
|
|
5574
|
-
|
|
5575
|
-
|
|
5576
|
-
|
|
5577
|
-
|
|
5578
|
-
|
|
5947
|
+
});
|
|
5948
|
+
}
|
|
5949
|
+
else {
|
|
5950
|
+
finishChore(chore, result);
|
|
5951
|
+
scheduleBlockedChoresAndDrainIfNeeded(chore);
|
|
5952
|
+
}
|
|
5953
|
+
if (shouldApplyJournalFlush(isServer)) {
|
|
5954
|
+
applyJournalFlush();
|
|
5955
|
+
drainInNextTick();
|
|
5956
|
+
return;
|
|
5957
|
+
}
|
|
5958
|
+
}
|
|
5959
|
+
}
|
|
5960
|
+
catch (e) {
|
|
5961
|
+
handleError(currentChore, e);
|
|
5962
|
+
scheduleBlockedChoresAndDrainIfNeeded(currentChore);
|
|
5963
|
+
}
|
|
5964
|
+
finally {
|
|
5965
|
+
isDraining = false;
|
|
5966
|
+
maybeFinishDrain();
|
|
5967
|
+
}
|
|
5968
|
+
}
|
|
5969
|
+
function finishChore(chore, value) {
|
|
5970
|
+
chore.$endTime$ = performance.now();
|
|
5971
|
+
chore.$state$ = ChoreState.DONE;
|
|
5972
|
+
chore.$returnValue$ = value;
|
|
5973
|
+
chore.$resolve$?.(value);
|
|
5974
|
+
}
|
|
5975
|
+
function handleError(chore, e) {
|
|
5976
|
+
chore.$endTime$ = performance.now();
|
|
5977
|
+
chore.$state$ = ChoreState.FAILED;
|
|
5978
|
+
// If we used the result as promise, this won't exist
|
|
5979
|
+
chore.$reject$?.(e);
|
|
5980
|
+
container.handleError(e, chore.$host$);
|
|
5981
|
+
}
|
|
5982
|
+
function executeChore(chore, isServer) {
|
|
5983
|
+
const host = chore.$host$;
|
|
5984
|
+
let returnValue;
|
|
5985
|
+
switch (chore.$type$) {
|
|
5986
|
+
case 6 /* ChoreType.COMPONENT */:
|
|
5987
|
+
{
|
|
5988
|
+
returnValue = safeCall(() => executeComponent(container, host, host, chore.$target$, chore.$payload$), (jsx) => {
|
|
5989
|
+
if (isServer) {
|
|
5990
|
+
return jsx;
|
|
5579
5991
|
}
|
|
5580
5992
|
else {
|
|
5581
|
-
|
|
5993
|
+
const styleScopedId = container.getHostProp(host, QScopedStyle);
|
|
5994
|
+
return retryOnPromise(() => vnode_diff(container, jsx, host, addComponentStylePrefix(styleScopedId)));
|
|
5582
5995
|
}
|
|
5996
|
+
}, (err) => {
|
|
5997
|
+
handleError(chore, err);
|
|
5998
|
+
});
|
|
5999
|
+
}
|
|
6000
|
+
break;
|
|
6001
|
+
case 2 /* ChoreType.RUN_QRL */:
|
|
6002
|
+
{
|
|
6003
|
+
const fn = chore.$target$.getFn();
|
|
6004
|
+
returnValue = retryOnPromise(() => fn(...chore.$payload$));
|
|
6005
|
+
}
|
|
6006
|
+
break;
|
|
6007
|
+
case 3 /* ChoreType.TASK */:
|
|
6008
|
+
case 16 /* ChoreType.VISIBLE */:
|
|
6009
|
+
{
|
|
6010
|
+
const payload = chore.$payload$;
|
|
6011
|
+
if (payload.$flags$ & 4 /* TaskFlags.RESOURCE */) {
|
|
6012
|
+
returnValue = runResource(payload, container, host);
|
|
5583
6013
|
}
|
|
5584
|
-
|
|
5585
|
-
|
|
5586
|
-
{
|
|
5587
|
-
const task = chore.$payload$;
|
|
5588
|
-
cleanupTask(task);
|
|
6014
|
+
else {
|
|
6015
|
+
returnValue = runTask(payload, container, host);
|
|
5589
6016
|
}
|
|
5590
|
-
|
|
5591
|
-
|
|
5592
|
-
|
|
5593
|
-
|
|
5594
|
-
|
|
5595
|
-
|
|
5596
|
-
|
|
5597
|
-
|
|
5598
|
-
|
|
6017
|
+
}
|
|
6018
|
+
break;
|
|
6019
|
+
case 32 /* ChoreType.CLEANUP_VISIBLE */:
|
|
6020
|
+
{
|
|
6021
|
+
const task = chore.$payload$;
|
|
6022
|
+
cleanupTask(task);
|
|
6023
|
+
}
|
|
6024
|
+
break;
|
|
6025
|
+
case 4 /* ChoreType.NODE_DIFF */:
|
|
6026
|
+
{
|
|
6027
|
+
const parentVirtualNode = chore.$target$;
|
|
6028
|
+
let jsx = chore.$payload$;
|
|
6029
|
+
if (isSignal(jsx)) {
|
|
6030
|
+
jsx = jsx.value;
|
|
5599
6031
|
}
|
|
5600
|
-
|
|
5601
|
-
|
|
5602
|
-
|
|
5603
|
-
|
|
5604
|
-
|
|
5605
|
-
|
|
5606
|
-
|
|
5607
|
-
|
|
5608
|
-
|
|
5609
|
-
|
|
5610
|
-
|
|
5611
|
-
|
|
5612
|
-
|
|
6032
|
+
returnValue = retryOnPromise(() => vnode_diff(container, jsx, parentVirtualNode, null));
|
|
6033
|
+
}
|
|
6034
|
+
break;
|
|
6035
|
+
case 5 /* ChoreType.NODE_PROP */:
|
|
6036
|
+
{
|
|
6037
|
+
const virtualNode = chore.$host$;
|
|
6038
|
+
const payload = chore.$payload$;
|
|
6039
|
+
let value = payload.$value$;
|
|
6040
|
+
if (isSignal(value)) {
|
|
6041
|
+
value = value.value;
|
|
6042
|
+
}
|
|
6043
|
+
const isConst = payload.$isConst$;
|
|
6044
|
+
const journal = container.$journal$;
|
|
6045
|
+
const property = chore.$idx$;
|
|
6046
|
+
const serializedValue = serializeAttribute(property, value, payload.$scopedStyleIdPrefix$);
|
|
6047
|
+
if (isServer) {
|
|
6048
|
+
container.addBackpatchEntry(chore.$host$.id, property, serializedValue);
|
|
6049
|
+
returnValue = null;
|
|
6050
|
+
}
|
|
6051
|
+
else {
|
|
5613
6052
|
if (isConst) {
|
|
5614
6053
|
const element = virtualNode[6 /* ElementVNodeProps.element */];
|
|
5615
6054
|
journal.push(2 /* VNodeJournalOpCode.SetAttribute */, element, property, serializedValue);
|
|
@@ -5617,64 +6056,49 @@ const createScheduler = (container, scheduleDrain, journalFlush) => {
|
|
|
5617
6056
|
else {
|
|
5618
6057
|
vnode_setAttr(journal, virtualNode, property, serializedValue);
|
|
5619
6058
|
}
|
|
6059
|
+
returnValue = undefined;
|
|
5620
6060
|
}
|
|
5621
|
-
break;
|
|
5622
|
-
case 1 /* ChoreType.QRL_RESOLVE */: {
|
|
5623
|
-
{
|
|
5624
|
-
const target = chore.$target$;
|
|
5625
|
-
returnValue = !target.resolved ? target.resolve() : null;
|
|
5626
|
-
}
|
|
5627
|
-
break;
|
|
5628
6061
|
}
|
|
5629
|
-
|
|
5630
|
-
|
|
5631
|
-
|
|
5632
|
-
|
|
6062
|
+
break;
|
|
6063
|
+
case 1 /* ChoreType.QRL_RESOLVE */: {
|
|
6064
|
+
{
|
|
6065
|
+
const target = chore.$target$;
|
|
6066
|
+
returnValue = (!target.resolved ? target.resolve() : null);
|
|
6067
|
+
}
|
|
6068
|
+
break;
|
|
6069
|
+
}
|
|
6070
|
+
case 7 /* ChoreType.RECOMPUTE_AND_SCHEDULE_EFFECTS */: {
|
|
6071
|
+
{
|
|
6072
|
+
const target = chore.$target$;
|
|
6073
|
+
const effects = chore.$payload$;
|
|
6074
|
+
if (!effects?.size) {
|
|
6075
|
+
break;
|
|
6076
|
+
}
|
|
6077
|
+
let shouldCompute = target instanceof ComputedSignalImpl || target instanceof WrappedSignalImpl;
|
|
6078
|
+
if (target instanceof AsyncComputedSignalImpl && effects !== target.$effects$) {
|
|
6079
|
+
shouldCompute = false;
|
|
6080
|
+
}
|
|
6081
|
+
if (shouldCompute) {
|
|
5633
6082
|
const ctx = newInvokeContext();
|
|
5634
6083
|
ctx.$container$ = container;
|
|
5635
|
-
|
|
5636
|
-
|
|
5637
|
-
target.$
|
|
5638
|
-
|
|
5639
|
-
|
|
6084
|
+
// needed for computed signals and throwing QRLs
|
|
6085
|
+
returnValue = maybeThen(retryOnPromise(() => invoke.call(target, ctx, target.$computeIfNeeded$)), () => {
|
|
6086
|
+
if (target.$flags$ & 2 /* SignalFlags.RUN_EFFECTS */) {
|
|
6087
|
+
target.$flags$ &= -3 /* SignalFlags.RUN_EFFECTS */;
|
|
6088
|
+
return retryOnPromise(() => triggerEffects(container, target, effects));
|
|
5640
6089
|
}
|
|
5641
|
-
|
|
5642
|
-
|
|
5643
|
-
|
|
5644
|
-
|
|
5645
|
-
|
|
5646
|
-
|
|
5647
|
-
}
|
|
5648
|
-
else {
|
|
5649
|
-
returnValue = retryOnPromise(() => triggerEffects(container, target, effects));
|
|
5650
|
-
}
|
|
6090
|
+
});
|
|
6091
|
+
}
|
|
6092
|
+
else {
|
|
6093
|
+
returnValue = retryOnPromise(() => {
|
|
6094
|
+
triggerEffects(container, target, effects);
|
|
6095
|
+
});
|
|
5651
6096
|
}
|
|
5652
|
-
break;
|
|
5653
6097
|
}
|
|
6098
|
+
break;
|
|
5654
6099
|
}
|
|
5655
6100
|
}
|
|
5656
|
-
|
|
5657
|
-
returnValue = Promise.reject(e);
|
|
5658
|
-
}
|
|
5659
|
-
const after = (value, error) => {
|
|
5660
|
-
currentChore = null;
|
|
5661
|
-
chore.$executed$ = true;
|
|
5662
|
-
if (error) {
|
|
5663
|
-
container.handleError(error, host);
|
|
5664
|
-
}
|
|
5665
|
-
else {
|
|
5666
|
-
chore.$returnValue$ = value;
|
|
5667
|
-
chore.$resolve$?.(value);
|
|
5668
|
-
}
|
|
5669
|
-
};
|
|
5670
|
-
if (isPromise(returnValue)) {
|
|
5671
|
-
chore.$promise$ = returnValue.then(after, (error) => after(undefined, error));
|
|
5672
|
-
chore.$resolve$?.(chore.$promise$);
|
|
5673
|
-
chore.$resolve$ = undefined;
|
|
5674
|
-
}
|
|
5675
|
-
else {
|
|
5676
|
-
after(returnValue);
|
|
5677
|
-
}
|
|
6101
|
+
return returnValue;
|
|
5678
6102
|
}
|
|
5679
6103
|
/**
|
|
5680
6104
|
* Compares two chores to determine their execution order in the scheduler's queue.
|
|
@@ -5703,15 +6127,6 @@ const createScheduler = (container, scheduleDrain, journalFlush) => {
|
|
|
5703
6127
|
else {
|
|
5704
6128
|
assertFalse(vnode_isVNode(aHost), 'expected aHost to be SSRNode but it is a VNode');
|
|
5705
6129
|
assertFalse(vnode_isVNode(bHost), 'expected bHost to be SSRNode but it is a VNode');
|
|
5706
|
-
// we are running on the server.
|
|
5707
|
-
// On server we can't schedule task for a different host!
|
|
5708
|
-
// Server is SSR, and therefore scheduling for anything but the current host
|
|
5709
|
-
// implies that things need to be re-run nad that is not supported because of streaming.
|
|
5710
|
-
const errorMessage = `SERVER: during HTML streaming, re-running tasks on a different host is not allowed.
|
|
5711
|
-
You are attempting to change a state that has already been streamed to the client.
|
|
5712
|
-
This can lead to inconsistencies between Server-Side Rendering (SSR) and Client-Side Rendering (CSR).
|
|
5713
|
-
Problematic Node: ${aHost.toString()}`;
|
|
5714
|
-
logWarn(errorMessage);
|
|
5715
6130
|
const hostDiff = ssrNodeDocumentPosition(aHost, bHost);
|
|
5716
6131
|
if (hostDiff !== 0) {
|
|
5717
6132
|
return hostDiff;
|
|
@@ -5735,8 +6150,14 @@ const createScheduler = (container, scheduleDrain, journalFlush) => {
|
|
|
5735
6150
|
// 1 means that we are going to process chores as FIFO
|
|
5736
6151
|
return 1;
|
|
5737
6152
|
}
|
|
5738
|
-
//
|
|
5739
|
-
|
|
6153
|
+
// ensure that the effect chores are scheduled for the same target
|
|
6154
|
+
// TODO: can we do this better?
|
|
6155
|
+
if (a.$type$ === 7 /* ChoreType.RECOMPUTE_AND_SCHEDULE_EFFECTS */ &&
|
|
6156
|
+
b.$type$ === 7 /* ChoreType.RECOMPUTE_AND_SCHEDULE_EFFECTS */ &&
|
|
6157
|
+
((a.$target$ instanceof StoreHandler && b.$target$ instanceof StoreHandler) ||
|
|
6158
|
+
(a.$target$ instanceof AsyncComputedSignalImpl &&
|
|
6159
|
+
b.$target$ instanceof AsyncComputedSignalImpl)) &&
|
|
6160
|
+
a.$payload$ !== b.$payload$) {
|
|
5740
6161
|
return 1;
|
|
5741
6162
|
}
|
|
5742
6163
|
// The chores are the same and will run only once
|
|
@@ -5768,6 +6189,15 @@ const createScheduler = (container, scheduleDrain, journalFlush) => {
|
|
|
5768
6189
|
/// We need to ensure that the `queue` is sorted by priority.
|
|
5769
6190
|
/// 1. Find a place where to insert into.
|
|
5770
6191
|
const idx = sortedFindIndex(sortedArray, value, rootVNode);
|
|
6192
|
+
if (idx < 0 && runningChores.size) {
|
|
6193
|
+
// 1.1. Check if the chore is already running.
|
|
6194
|
+
for (const chore of runningChores) {
|
|
6195
|
+
const comp = choreComparator(value, chore, rootVNode);
|
|
6196
|
+
if (comp === 0) {
|
|
6197
|
+
return chore;
|
|
6198
|
+
}
|
|
6199
|
+
}
|
|
6200
|
+
}
|
|
5771
6201
|
if (idx < 0) {
|
|
5772
6202
|
/// 2. Insert the chore into the queue.
|
|
5773
6203
|
sortedArray.splice(~idx, 0, value);
|
|
@@ -5782,9 +6212,6 @@ const createScheduler = (container, scheduleDrain, journalFlush) => {
|
|
|
5782
6212
|
if (existing.$payload$ !== value.$payload$) {
|
|
5783
6213
|
existing.$payload$ = value.$payload$;
|
|
5784
6214
|
}
|
|
5785
|
-
if (existing.$executed$) {
|
|
5786
|
-
existing.$executed$ = false;
|
|
5787
|
-
}
|
|
5788
6215
|
return existing;
|
|
5789
6216
|
}
|
|
5790
6217
|
};
|
|
@@ -5796,6 +6223,25 @@ function vNodeAlreadyDeleted(chore) {
|
|
|
5796
6223
|
vnode_isVNode(chore.$host$) &&
|
|
5797
6224
|
chore.$host$[0 /* VNodeProps.flags */] & 32 /* VNodeFlags.Deleted */);
|
|
5798
6225
|
}
|
|
6226
|
+
function addBlockedChore(blockedChore, blockingChore, blockedChores) {
|
|
6227
|
+
blockingChore.$blockedChores$ ||= [];
|
|
6228
|
+
blockingChore.$blockedChores$.push(blockedChore);
|
|
6229
|
+
blockedChores.add(blockedChore);
|
|
6230
|
+
}
|
|
6231
|
+
function choreTypeToName(type) {
|
|
6232
|
+
return ({
|
|
6233
|
+
[1 /* ChoreType.QRL_RESOLVE */]: 'Resolve QRL',
|
|
6234
|
+
[2 /* ChoreType.RUN_QRL */]: 'Run QRL',
|
|
6235
|
+
[3 /* ChoreType.TASK */]: 'Task',
|
|
6236
|
+
[4 /* ChoreType.NODE_DIFF */]: 'Changes diffing',
|
|
6237
|
+
[5 /* ChoreType.NODE_PROP */]: 'Updating node property',
|
|
6238
|
+
[6 /* ChoreType.COMPONENT */]: 'Component',
|
|
6239
|
+
[7 /* ChoreType.RECOMPUTE_AND_SCHEDULE_EFFECTS */]: 'Signal recompute',
|
|
6240
|
+
[16 /* ChoreType.VISIBLE */]: 'Visible',
|
|
6241
|
+
[32 /* ChoreType.CLEANUP_VISIBLE */]: 'Cleanup visible',
|
|
6242
|
+
[255 /* ChoreType.WAIT_FOR_QUEUE */]: 'Wait for queue',
|
|
6243
|
+
}[type] || 'Unknown: ' + type);
|
|
6244
|
+
}
|
|
5799
6245
|
function debugChoreTypeToString(type) {
|
|
5800
6246
|
return ({
|
|
5801
6247
|
[1 /* ChoreType.QRL_RESOLVE */]: 'QRL_RESOLVE',
|
|
@@ -5805,31 +6251,87 @@ function debugChoreTypeToString(type) {
|
|
|
5805
6251
|
[5 /* ChoreType.NODE_PROP */]: 'NODE_PROP',
|
|
5806
6252
|
[6 /* ChoreType.COMPONENT */]: 'COMPONENT',
|
|
5807
6253
|
[7 /* ChoreType.RECOMPUTE_AND_SCHEDULE_EFFECTS */]: 'RECOMPUTE_SIGNAL',
|
|
5808
|
-
[16 /* ChoreType.
|
|
5809
|
-
[32 /* ChoreType.
|
|
5810
|
-
[
|
|
5811
|
-
[255 /* ChoreType.WAIT_FOR_ALL */]: 'WAIT_FOR_ALL',
|
|
6254
|
+
[16 /* ChoreType.VISIBLE */]: 'VISIBLE',
|
|
6255
|
+
[32 /* ChoreType.CLEANUP_VISIBLE */]: 'CLEANUP_VISIBLE',
|
|
6256
|
+
[255 /* ChoreType.WAIT_FOR_QUEUE */]: 'WAIT_FOR_QUEUE',
|
|
5812
6257
|
}[type] || 'UNKNOWN: ' + type);
|
|
5813
6258
|
}
|
|
5814
|
-
function
|
|
5815
|
-
const
|
|
5816
|
-
|
|
5817
|
-
|
|
5818
|
-
|
|
5819
|
-
|
|
5820
|
-
|
|
5821
|
-
|
|
5822
|
-
|
|
5823
|
-
|
|
5824
|
-
|
|
5825
|
-
|
|
5826
|
-
|
|
5827
|
-
|
|
5828
|
-
|
|
5829
|
-
|
|
5830
|
-
|
|
6259
|
+
function debugTrace(action, arg, queue, blockedChores) {
|
|
6260
|
+
const lines = [];
|
|
6261
|
+
// Header
|
|
6262
|
+
lines.push(`Scheduler: ${action}`);
|
|
6263
|
+
// Argument section
|
|
6264
|
+
if (arg) {
|
|
6265
|
+
lines.push('');
|
|
6266
|
+
if (arg && '$type$' in arg) {
|
|
6267
|
+
const chore = arg;
|
|
6268
|
+
const type = debugChoreTypeToString(chore.$type$);
|
|
6269
|
+
const host = String(chore.$host$).replaceAll(/\n.*/gim, '');
|
|
6270
|
+
const qrlTarget = chore.$target$?.$symbol$;
|
|
6271
|
+
const targetOrHost = chore.$type$ === 1 /* ChoreType.QRL_RESOLVE */ || chore.$type$ === 2 /* ChoreType.RUN_QRL */
|
|
6272
|
+
? qrlTarget
|
|
6273
|
+
: host;
|
|
6274
|
+
lines.push(`🎯 Current Chore:`);
|
|
6275
|
+
lines.push(` Type: ${type}`);
|
|
6276
|
+
lines.push(` Host: ${targetOrHost}`);
|
|
6277
|
+
// Show execution time if available
|
|
6278
|
+
if (chore.$startTime$ && chore.$endTime$) {
|
|
6279
|
+
const executionTime = chore.$endTime$ - chore.$startTime$;
|
|
6280
|
+
lines.push(` Time: ${executionTime.toFixed(2)}ms`);
|
|
6281
|
+
}
|
|
6282
|
+
else if (chore.$startTime$) {
|
|
6283
|
+
const elapsedTime = performance.now() - chore.$startTime$;
|
|
6284
|
+
lines.push(` Time: ${elapsedTime.toFixed(2)}ms (running)`);
|
|
6285
|
+
}
|
|
6286
|
+
// Show blocked chores for this chore
|
|
6287
|
+
if (chore.$blockedChores$ && chore.$blockedChores$.length > 0) {
|
|
6288
|
+
lines.push(` ⛔ Blocked Chores:`);
|
|
6289
|
+
chore.$blockedChores$.forEach((blockedChore, index) => {
|
|
6290
|
+
const blockedType = debugChoreTypeToString(blockedChore.$type$);
|
|
6291
|
+
const blockedTarget = String(blockedChore.$host$).replaceAll(/\n.*/gim, '');
|
|
6292
|
+
lines.push(` ${index + 1}. ${blockedType} ${blockedTarget} ${blockedChore.$idx$}`);
|
|
6293
|
+
});
|
|
6294
|
+
}
|
|
6295
|
+
}
|
|
6296
|
+
else {
|
|
6297
|
+
lines.push(`📝 Argument: ${String(arg).replaceAll(/\n.*/gim, '')}`);
|
|
6298
|
+
}
|
|
6299
|
+
}
|
|
6300
|
+
// Queue section
|
|
6301
|
+
if (queue && queue.length > 0) {
|
|
6302
|
+
lines.push('');
|
|
6303
|
+
lines.push(`📋 Queue (${queue.length} items):`);
|
|
6304
|
+
queue.forEach((chore, index) => {
|
|
6305
|
+
const isActive = chore === arg;
|
|
6306
|
+
const activeMarker = isActive ? `▶ ` : ' ';
|
|
6307
|
+
const type = debugChoreTypeToString(chore.$type$);
|
|
6308
|
+
const state = chore.$state$ ? `[${ChoreState[chore.$state$]}]` : '';
|
|
6309
|
+
const host = String(chore.$host$).replaceAll(/\n.*/gim, '');
|
|
6310
|
+
const qrlTarget = chore.$target$?.$symbol$;
|
|
6311
|
+
const target = chore.$type$ === 1 /* ChoreType.QRL_RESOLVE */ || chore.$type$ === 2 /* ChoreType.RUN_QRL */
|
|
6312
|
+
? qrlTarget
|
|
6313
|
+
: host;
|
|
6314
|
+
const line = `${activeMarker}${state} ${type} ${target} ${chore.$idx$}`;
|
|
6315
|
+
lines.push(line);
|
|
5831
6316
|
});
|
|
5832
6317
|
}
|
|
6318
|
+
// Blocked chores section
|
|
6319
|
+
if (blockedChores && blockedChores.size > 0) {
|
|
6320
|
+
lines.push('');
|
|
6321
|
+
lines.push(`🚫 Blocked Chores (${blockedChores.size} items):`);
|
|
6322
|
+
Array.from(blockedChores).forEach((chore, index) => {
|
|
6323
|
+
const type = debugChoreTypeToString(chore.$type$);
|
|
6324
|
+
const host = String(chore.$host$).replaceAll(/\n.*/gim, '');
|
|
6325
|
+
const qrlTarget = chore.$target$?.$symbol$;
|
|
6326
|
+
const target = chore.$type$ === 1 /* ChoreType.QRL_RESOLVE */ || chore.$type$ === 2 /* ChoreType.RUN_QRL */
|
|
6327
|
+
? qrlTarget
|
|
6328
|
+
: host;
|
|
6329
|
+
lines.push(` ${index + 1}. ${type} ${target} ${chore.$idx$}`);
|
|
6330
|
+
});
|
|
6331
|
+
}
|
|
6332
|
+
// Footer
|
|
6333
|
+
lines.push('');
|
|
6334
|
+
lines.push('─'.repeat(60));
|
|
5833
6335
|
// eslint-disable-next-line no-console
|
|
5834
6336
|
console.log(lines.join('\n') + '\n');
|
|
5835
6337
|
}
|
|
@@ -5847,7 +6349,8 @@ class _SharedContainer {
|
|
|
5847
6349
|
$currentUniqueId$ = 0;
|
|
5848
6350
|
$instanceHash$ = null;
|
|
5849
6351
|
$buildBase$ = null;
|
|
5850
|
-
|
|
6352
|
+
$flushEpoch$ = 0;
|
|
6353
|
+
constructor(journalFlush, serverData, locale) {
|
|
5851
6354
|
this.$serverData$ = serverData;
|
|
5852
6355
|
this.$locale$ = locale;
|
|
5853
6356
|
this.$version$ = version;
|
|
@@ -5855,7 +6358,7 @@ class _SharedContainer {
|
|
|
5855
6358
|
this.$getObjectById$ = (_id) => {
|
|
5856
6359
|
throw Error('Not implemented');
|
|
5857
6360
|
};
|
|
5858
|
-
this.$scheduler$ = createScheduler(this,
|
|
6361
|
+
this.$scheduler$ = createScheduler(this, journalFlush);
|
|
5859
6362
|
}
|
|
5860
6363
|
trackSignalValue(signal, subscriber, property, data) {
|
|
5861
6364
|
return trackSignalAndAssignHost(signal, subscriber, property, this, data);
|
|
@@ -6164,9 +6667,10 @@ function processVNodeData$1(document) {
|
|
|
6164
6667
|
const shadowRootContainer = node;
|
|
6165
6668
|
const shadowRoot = shadowRootContainer?.shadowRoot;
|
|
6166
6669
|
if (shadowRoot) {
|
|
6670
|
+
const firstShadowRootChild = firstChild(shadowRoot);
|
|
6167
6671
|
walkContainer(
|
|
6168
6672
|
// we need to create a new walker for the shadow root
|
|
6169
|
-
document.createTreeWalker(
|
|
6673
|
+
document.createTreeWalker(firstShadowRootChild, 0x1 /* NodeFilter.SHOW_ELEMENT */ | 0x80 /* NodeFilter.SHOW_COMMENT */), null, firstShadowRootChild, null, '', null);
|
|
6170
6674
|
}
|
|
6171
6675
|
}
|
|
6172
6676
|
if ((nodeType & 2 /* NodeType.ELEMENT */) === 2 /* NodeType.ELEMENT */) {
|
|
@@ -6242,7 +6746,7 @@ function getDomContainerFromQContainerElement(qContainerElement) {
|
|
|
6242
6746
|
/** @internal */
|
|
6243
6747
|
function _getQContainerElement(element) {
|
|
6244
6748
|
const qContainerElement = Array.isArray(element)
|
|
6245
|
-
? vnode_getDomParent(element)
|
|
6749
|
+
? vnode_getDomParent(element, true)
|
|
6246
6750
|
: element;
|
|
6247
6751
|
return qContainerElement.closest(QContainerSelector);
|
|
6248
6752
|
}
|
|
@@ -6257,7 +6761,6 @@ class DomContainer extends _SharedContainer {
|
|
|
6257
6761
|
rootVNode;
|
|
6258
6762
|
document;
|
|
6259
6763
|
$journal$;
|
|
6260
|
-
renderDone = null;
|
|
6261
6764
|
$rawStateData$;
|
|
6262
6765
|
$storeProxyMap$ = new WeakMap();
|
|
6263
6766
|
$qFuncs$;
|
|
@@ -6267,9 +6770,11 @@ class DomContainer extends _SharedContainer {
|
|
|
6267
6770
|
vNodeLocate = (id) => vnode_locate(this.rootVNode, id);
|
|
6268
6771
|
$stateData$;
|
|
6269
6772
|
$styleIds$ = null;
|
|
6270
|
-
$renderCount$ = 0;
|
|
6271
6773
|
constructor(element) {
|
|
6272
|
-
super(() =>
|
|
6774
|
+
super(() => {
|
|
6775
|
+
this.$flushEpoch$++;
|
|
6776
|
+
vnode_applyJournal(this.$journal$);
|
|
6777
|
+
}, {}, element.getAttribute(QLocaleAttr));
|
|
6273
6778
|
this.qContainer = element.getAttribute(QContainerAttr);
|
|
6274
6779
|
if (!this.qContainer) {
|
|
6275
6780
|
throw qError(25 /* QError.elementWithoutContainer */);
|
|
@@ -6311,20 +6816,17 @@ class DomContainer extends _SharedContainer {
|
|
|
6311
6816
|
}
|
|
6312
6817
|
handleError(err, host) {
|
|
6313
6818
|
if (qDev && host) {
|
|
6314
|
-
// Clean vdom
|
|
6315
6819
|
if (typeof document !== 'undefined') {
|
|
6316
6820
|
const vHost = host;
|
|
6317
|
-
const errorDiv = document.createElement('errored-host');
|
|
6318
|
-
if (err && err instanceof Error) {
|
|
6319
|
-
errorDiv.props = { error: err };
|
|
6320
|
-
}
|
|
6321
|
-
errorDiv.setAttribute('q:key', '_error_');
|
|
6322
6821
|
const journal = [];
|
|
6323
|
-
const
|
|
6324
|
-
|
|
6325
|
-
|
|
6326
|
-
|
|
6327
|
-
|
|
6822
|
+
const vHostParent = vnode_getParent(vHost);
|
|
6823
|
+
const vHostNextSibling = vnode_getNextSibling(vHost);
|
|
6824
|
+
const vErrorDiv = vnode_createErrorDiv(document, vHost, err, journal);
|
|
6825
|
+
// If the host is an element node, we need to insert the error div into its parent.
|
|
6826
|
+
const insertHost = vnode_isElementVNode(vHost) ? vHostParent || vHost : vHost;
|
|
6827
|
+
// If the host is different then we need to insert errored-host in the same position as the host.
|
|
6828
|
+
const insertBefore = insertHost === vHost ? null : vHostNextSibling;
|
|
6829
|
+
vnode_insertBefore(journal, insertHost, vErrorDiv, insertBefore);
|
|
6328
6830
|
vnode_applyJournal(journal);
|
|
6329
6831
|
}
|
|
6330
6832
|
if (err && err instanceof Error) {
|
|
@@ -6344,19 +6846,16 @@ class DomContainer extends _SharedContainer {
|
|
|
6344
6846
|
}
|
|
6345
6847
|
setContext(host, context, value) {
|
|
6346
6848
|
let ctx = this.getHostProp(host, QCtxAttr);
|
|
6347
|
-
if (
|
|
6849
|
+
if (ctx == null) {
|
|
6348
6850
|
this.setHostProp(host, QCtxAttr, (ctx = []));
|
|
6349
6851
|
}
|
|
6350
|
-
mapArray_set(ctx, context.id, value, 0);
|
|
6852
|
+
mapArray_set(ctx, context.id, value, 0, true);
|
|
6351
6853
|
}
|
|
6352
6854
|
resolveContext(host, contextId) {
|
|
6353
6855
|
while (host) {
|
|
6354
6856
|
const ctx = this.getHostProp(host, QCtxAttr);
|
|
6355
|
-
if (ctx) {
|
|
6356
|
-
|
|
6357
|
-
if (value) {
|
|
6358
|
-
return value;
|
|
6359
|
-
}
|
|
6857
|
+
if (ctx != null && mapArray_has(ctx, contextId.id, 0)) {
|
|
6858
|
+
return mapArray_get(ctx, contextId.id, 0);
|
|
6360
6859
|
}
|
|
6361
6860
|
host = this.getParentHost(host);
|
|
6362
6861
|
}
|
|
@@ -6402,29 +6901,6 @@ class DomContainer extends _SharedContainer {
|
|
|
6402
6901
|
}
|
|
6403
6902
|
return vnode_getProp(vNode, name, getObjectById);
|
|
6404
6903
|
}
|
|
6405
|
-
scheduleRender() {
|
|
6406
|
-
this.$renderCount$++;
|
|
6407
|
-
this.renderDone ||= getPlatform().nextTick(() => this.processChores());
|
|
6408
|
-
return this.renderDone.finally(() => emitEvent('qrender', { instanceHash: this.$instanceHash$, renderCount: this.$renderCount$ }));
|
|
6409
|
-
}
|
|
6410
|
-
processChores() {
|
|
6411
|
-
let renderCount = this.$renderCount$;
|
|
6412
|
-
const result = this.$scheduler$(255 /* ChoreType.WAIT_FOR_ALL */);
|
|
6413
|
-
if (isPromise(result)) {
|
|
6414
|
-
return result.then(async () => {
|
|
6415
|
-
while (renderCount !== this.$renderCount$) {
|
|
6416
|
-
renderCount = this.$renderCount$;
|
|
6417
|
-
await this.$scheduler$(255 /* ChoreType.WAIT_FOR_ALL */);
|
|
6418
|
-
}
|
|
6419
|
-
this.renderDone = null;
|
|
6420
|
-
});
|
|
6421
|
-
}
|
|
6422
|
-
if (renderCount !== this.$renderCount$) {
|
|
6423
|
-
this.processChores();
|
|
6424
|
-
return;
|
|
6425
|
-
}
|
|
6426
|
-
this.renderDone = null;
|
|
6427
|
-
}
|
|
6428
6904
|
ensureProjectionResolved(vNode) {
|
|
6429
6905
|
if ((vNode[0 /* VNodeProps.flags */] & 16 /* VNodeFlags.Resolved */) === 0) {
|
|
6430
6906
|
vNode[0 /* VNodeProps.flags */] |= 16 /* VNodeFlags.Resolved */;
|
|
@@ -6434,7 +6910,9 @@ class DomContainer extends _SharedContainer {
|
|
|
6434
6910
|
if (isSlotProp(prop)) {
|
|
6435
6911
|
const value = props[i + 1];
|
|
6436
6912
|
if (typeof value == 'string') {
|
|
6437
|
-
|
|
6913
|
+
const projection = this.vNodeLocate(value);
|
|
6914
|
+
props[i + 1] = projection;
|
|
6915
|
+
vnode_getProp(projection, QSlotParent, (id) => this.vNodeLocate(id));
|
|
6438
6916
|
}
|
|
6439
6917
|
}
|
|
6440
6918
|
}
|
|
@@ -6555,7 +7033,7 @@ const addQrlToSerializationCtx = (effectSubscriber, container) => {
|
|
|
6555
7033
|
}
|
|
6556
7034
|
};
|
|
6557
7035
|
const triggerEffects = (container, signal, effects) => {
|
|
6558
|
-
const isBrowser =
|
|
7036
|
+
const isBrowser = !isServerPlatform();
|
|
6559
7037
|
if (effects) {
|
|
6560
7038
|
const scheduleEffect = (effectSubscription) => {
|
|
6561
7039
|
const consumer = effectSubscription[0 /* EffectSubscriptionProp.CONSUMER */];
|
|
@@ -6565,7 +7043,7 @@ const triggerEffects = (container, signal, effects) => {
|
|
|
6565
7043
|
consumer.$flags$ |= 8 /* TaskFlags.DIRTY */;
|
|
6566
7044
|
let choreType = 3 /* ChoreType.TASK */;
|
|
6567
7045
|
if (consumer.$flags$ & 1 /* TaskFlags.VISIBLE_TASK */) {
|
|
6568
|
-
choreType =
|
|
7046
|
+
choreType = 16 /* ChoreType.VISIBLE */;
|
|
6569
7047
|
}
|
|
6570
7048
|
container.$scheduler$(choreType, consumer);
|
|
6571
7049
|
}
|
|
@@ -6588,22 +7066,22 @@ const triggerEffects = (container, signal, effects) => {
|
|
|
6588
7066
|
const props = container.getHostProp(host, ELEMENT_PROPS);
|
|
6589
7067
|
container.$scheduler$(6 /* ChoreType.COMPONENT */, host, qrl, props);
|
|
6590
7068
|
}
|
|
6591
|
-
else if (
|
|
6592
|
-
if (
|
|
7069
|
+
else if (property === "." /* EffectProperty.VNODE */) {
|
|
7070
|
+
if (isBrowser) {
|
|
6593
7071
|
const host = consumer;
|
|
6594
7072
|
container.$scheduler$(4 /* ChoreType.NODE_DIFF */, host, host, signal);
|
|
6595
7073
|
}
|
|
6596
|
-
|
|
6597
|
-
|
|
6598
|
-
|
|
6599
|
-
|
|
6600
|
-
|
|
6601
|
-
|
|
6602
|
-
|
|
6603
|
-
|
|
6604
|
-
|
|
6605
|
-
|
|
6606
|
-
|
|
7074
|
+
}
|
|
7075
|
+
else {
|
|
7076
|
+
const host = consumer;
|
|
7077
|
+
const effectData = effectSubscription[3 /* EffectSubscriptionProp.DATA */];
|
|
7078
|
+
if (effectData instanceof SubscriptionData) {
|
|
7079
|
+
const data = effectData.data;
|
|
7080
|
+
const payload = {
|
|
7081
|
+
...data,
|
|
7082
|
+
$value$: signal,
|
|
7083
|
+
};
|
|
7084
|
+
container.$scheduler$(5 /* ChoreType.NODE_PROP */, host, property, payload);
|
|
6607
7085
|
}
|
|
6608
7086
|
}
|
|
6609
7087
|
};
|
|
@@ -6624,10 +7102,10 @@ const getComputedSignalFlags = (serializationStrategy) => {
|
|
|
6624
7102
|
// flags |= ComputedSignalFlags.SERIALIZATION_STRATEGY_AUTO;
|
|
6625
7103
|
// break;
|
|
6626
7104
|
case 'never':
|
|
6627
|
-
flags |=
|
|
7105
|
+
flags |= 16 /* SerializationSignalFlags.SERIALIZATION_STRATEGY_NEVER */;
|
|
6628
7106
|
break;
|
|
6629
7107
|
case 'always':
|
|
6630
|
-
flags |=
|
|
7108
|
+
flags |= 32 /* SerializationSignalFlags.SERIALIZATION_STRATEGY_ALWAYS */;
|
|
6631
7109
|
break;
|
|
6632
7110
|
}
|
|
6633
7111
|
return flags;
|
|
@@ -7019,6 +7497,9 @@ function vnode_walkVNode(vNode, callback) {
|
|
|
7019
7497
|
}
|
|
7020
7498
|
let vParent = null;
|
|
7021
7499
|
do {
|
|
7500
|
+
if (callback?.(vCursor, vParent)) {
|
|
7501
|
+
return;
|
|
7502
|
+
}
|
|
7022
7503
|
const vFirstChild = vnode_getFirstChild(vCursor);
|
|
7023
7504
|
if (vFirstChild) {
|
|
7024
7505
|
vCursor = vFirstChild;
|
|
@@ -7187,6 +7668,7 @@ const vnode_ensureTextInflated = (journal, vnode) => {
|
|
|
7187
7668
|
const flags = textVNode[0 /* VNodeProps.flags */];
|
|
7188
7669
|
if ((flags & 8 /* VNodeFlags.Inflated */) === 0) {
|
|
7189
7670
|
const parentNode = vnode_getDomParent(vnode);
|
|
7671
|
+
assertDefined(parentNode, 'Missing parent node.');
|
|
7190
7672
|
const sharedTextNode = textVNode[4 /* TextVNodeProps.node */];
|
|
7191
7673
|
const doc = parentNode.ownerDocument;
|
|
7192
7674
|
// Walk the previous siblings and inflate them.
|
|
@@ -7345,6 +7827,18 @@ const indexOfAlphanumeric = (id, length) => {
|
|
|
7345
7827
|
}
|
|
7346
7828
|
return length;
|
|
7347
7829
|
};
|
|
7830
|
+
const vnode_createErrorDiv = (document, host, err, journal) => {
|
|
7831
|
+
const errorDiv = document.createElement('errored-host');
|
|
7832
|
+
if (err && err instanceof Error) {
|
|
7833
|
+
errorDiv.props = { error: err };
|
|
7834
|
+
}
|
|
7835
|
+
errorDiv.setAttribute('q:key', '_error_');
|
|
7836
|
+
const vErrorDiv = vnode_newElement(errorDiv, 'errored-host');
|
|
7837
|
+
vnode_getDOMChildNodes(journal, host, true).forEach((child) => {
|
|
7838
|
+
vnode_insertBefore(journal, vErrorDiv, child, null);
|
|
7839
|
+
});
|
|
7840
|
+
return vErrorDiv;
|
|
7841
|
+
};
|
|
7348
7842
|
const parseBoolean = (value) => {
|
|
7349
7843
|
if (value === 'false') {
|
|
7350
7844
|
return false;
|
|
@@ -7483,7 +7977,7 @@ const vnode_insertBefore = (journal, parent, newChild, insertBefore) => {
|
|
|
7483
7977
|
* unlink the previous or next sibling, we don't know that after "a" node is "b". So we need to
|
|
7484
7978
|
* find children first (and inflate them).
|
|
7485
7979
|
*/
|
|
7486
|
-
const domParentVNode = vnode_getDomParentVNode(parent);
|
|
7980
|
+
const domParentVNode = vnode_getDomParentVNode(parent, false);
|
|
7487
7981
|
const parentNode = domParentVNode && domParentVNode[6 /* ElementVNodeProps.element */];
|
|
7488
7982
|
let domChildren = null;
|
|
7489
7983
|
if (domParentVNode) {
|
|
@@ -7532,27 +8026,31 @@ const vnode_insertBefore = (journal, parent, newChild, insertBefore) => {
|
|
|
7532
8026
|
newChildCurrentParent !== parent)) {
|
|
7533
8027
|
vnode_remove(journal, newChildCurrentParent, newChild, false);
|
|
7534
8028
|
}
|
|
7535
|
-
|
|
7536
|
-
if
|
|
7537
|
-
|
|
7538
|
-
|
|
7539
|
-
|
|
7540
|
-
|
|
7541
|
-
|
|
7542
|
-
|
|
8029
|
+
const parentIsDeleted = parent[0 /* VNodeProps.flags */] & 32 /* VNodeFlags.Deleted */;
|
|
8030
|
+
// if the parent is deleted, then we don't need to insert the new child
|
|
8031
|
+
if (!parentIsDeleted) {
|
|
8032
|
+
let adjustedInsertBefore = null;
|
|
8033
|
+
if (insertBefore == null) {
|
|
8034
|
+
if (vnode_isVirtualVNode(parent)) {
|
|
8035
|
+
// If `insertBefore` is null, than we need to insert at the end of the list.
|
|
8036
|
+
// Well, not quite. If the parent is a virtual node, our "last node" is not the same
|
|
8037
|
+
// as the DOM "last node". So in that case we need to look for the "next node" from
|
|
8038
|
+
// our parent.
|
|
8039
|
+
adjustedInsertBefore = vnode_getDomSibling(parent, true, false);
|
|
8040
|
+
}
|
|
8041
|
+
}
|
|
8042
|
+
else if (vnode_isVirtualVNode(insertBefore)) {
|
|
8043
|
+
// If the `insertBefore` is virtual, than we need to descend into the virtual and find e actual
|
|
8044
|
+
adjustedInsertBefore = vnode_getDomSibling(insertBefore, true, true);
|
|
8045
|
+
}
|
|
8046
|
+
else {
|
|
8047
|
+
adjustedInsertBefore = insertBefore;
|
|
8048
|
+
}
|
|
8049
|
+
adjustedInsertBefore && vnode_ensureInflatedIfText(journal, adjustedInsertBefore);
|
|
8050
|
+
// Here we know the insertBefore node
|
|
8051
|
+
if (domChildren && domChildren.length) {
|
|
8052
|
+
journal.push(5 /* VNodeJournalOpCode.Insert */, parentNode, vnode_getNode(adjustedInsertBefore), ...domChildren);
|
|
7543
8053
|
}
|
|
7544
|
-
}
|
|
7545
|
-
else if (vnode_isVirtualVNode(insertBefore)) {
|
|
7546
|
-
// If the `insertBefore` is virtual, than we need to descend into the virtual and find e actual
|
|
7547
|
-
adjustedInsertBefore = vnode_getDomSibling(insertBefore, true, true);
|
|
7548
|
-
}
|
|
7549
|
-
else {
|
|
7550
|
-
adjustedInsertBefore = insertBefore;
|
|
7551
|
-
}
|
|
7552
|
-
adjustedInsertBefore && vnode_ensureInflatedIfText(journal, adjustedInsertBefore);
|
|
7553
|
-
// Here we know the insertBefore node
|
|
7554
|
-
if (domChildren && domChildren.length) {
|
|
7555
|
-
journal.push(5 /* VNodeJournalOpCode.Insert */, parentNode, vnode_getNode(adjustedInsertBefore), ...domChildren);
|
|
7556
8054
|
}
|
|
7557
8055
|
// link newChild into the previous/next list
|
|
7558
8056
|
const vNext = insertBefore;
|
|
@@ -7574,14 +8072,22 @@ const vnode_insertBefore = (journal, parent, newChild, insertBefore) => {
|
|
|
7574
8072
|
newChild[2 /* VNodeProps.previousSibling */] = vPrevious;
|
|
7575
8073
|
newChild[3 /* VNodeProps.nextSibling */] = vNext;
|
|
7576
8074
|
newChild[1 /* VNodeProps.parent */] = parent;
|
|
8075
|
+
if (parentIsDeleted) {
|
|
8076
|
+
// if the parent is deleted, then the new child is also deleted
|
|
8077
|
+
newChild[0 /* VNodeProps.flags */] |= 32 /* VNodeFlags.Deleted */;
|
|
8078
|
+
}
|
|
7577
8079
|
};
|
|
7578
|
-
const vnode_getDomParent = (vnode) => {
|
|
7579
|
-
vnode = vnode_getDomParentVNode(vnode);
|
|
8080
|
+
const vnode_getDomParent = (vnode, includeProjection = true) => {
|
|
8081
|
+
vnode = vnode_getDomParentVNode(vnode, includeProjection);
|
|
7580
8082
|
return (vnode && vnode[6 /* ElementVNodeProps.element */]);
|
|
7581
8083
|
};
|
|
7582
|
-
const vnode_getDomParentVNode = (vnode) => {
|
|
8084
|
+
const vnode_getDomParentVNode = (vnode, includeProjection = true) => {
|
|
7583
8085
|
while (vnode && !vnode_isElementVNode(vnode)) {
|
|
7584
|
-
vnode =
|
|
8086
|
+
vnode =
|
|
8087
|
+
vnode[1 /* VNodeProps.parent */] ||
|
|
8088
|
+
(includeProjection
|
|
8089
|
+
? vnode_getProp(vnode, QSlotParent, (id) => (vnode_isVNode(id) ? id : null))
|
|
8090
|
+
: null);
|
|
7585
8091
|
}
|
|
7586
8092
|
return vnode;
|
|
7587
8093
|
};
|
|
@@ -7591,7 +8097,7 @@ const vnode_remove = (journal, vParent, vToRemove, removeDOM) => {
|
|
|
7591
8097
|
vnode_ensureTextInflated(journal, vToRemove);
|
|
7592
8098
|
}
|
|
7593
8099
|
if (removeDOM) {
|
|
7594
|
-
const domParent = vnode_getDomParent(vParent);
|
|
8100
|
+
const domParent = vnode_getDomParent(vParent, false);
|
|
7595
8101
|
const isInnerHTMLParent = vnode_getAttr(vParent, dangerouslySetInnerHTML);
|
|
7596
8102
|
if (isInnerHTMLParent) {
|
|
7597
8103
|
// ignore children, as they are inserted via innerHTML
|
|
@@ -8072,6 +8578,26 @@ const vnode_getProps = (vnode) => {
|
|
|
8072
8578
|
const vnode_getParent = (vnode) => {
|
|
8073
8579
|
return vnode[1 /* VNodeProps.parent */] || null;
|
|
8074
8580
|
};
|
|
8581
|
+
const vnode_isDescendantOf = (vnode, ancestor, rootVNode) => {
|
|
8582
|
+
let parent = vnode_getParentOrProjectionParent(vnode, rootVNode);
|
|
8583
|
+
while (parent) {
|
|
8584
|
+
if (parent === ancestor) {
|
|
8585
|
+
return true;
|
|
8586
|
+
}
|
|
8587
|
+
parent = vnode_getParentOrProjectionParent(parent, rootVNode);
|
|
8588
|
+
}
|
|
8589
|
+
return false;
|
|
8590
|
+
};
|
|
8591
|
+
const vnode_getParentOrProjectionParent = (vnode, rootVNode) => {
|
|
8592
|
+
if (rootVNode) {
|
|
8593
|
+
const parentProjection = vnode_getProp(vnode, QSlotParent, (id) => vnode_locate(rootVNode, id));
|
|
8594
|
+
if (parentProjection) {
|
|
8595
|
+
// This is a projection, so we need to check the parent of the projection
|
|
8596
|
+
return parentProjection;
|
|
8597
|
+
}
|
|
8598
|
+
}
|
|
8599
|
+
return vnode_getParent(vnode);
|
|
8600
|
+
};
|
|
8075
8601
|
const vnode_getNode = (vnode) => {
|
|
8076
8602
|
if (vnode === null || vnode_isVirtualVNode(vnode)) {
|
|
8077
8603
|
return null;
|
|
@@ -8431,13 +8957,15 @@ class DeserializationHandler {
|
|
|
8431
8957
|
}
|
|
8432
8958
|
const container = this.$container$;
|
|
8433
8959
|
let propValue = allocate(container, typeId, value);
|
|
8960
|
+
Reflect.set(target, property, propValue);
|
|
8961
|
+
this.$data$[idx] = undefined;
|
|
8962
|
+
this.$data$[idx + 1] = propValue;
|
|
8434
8963
|
/** We stored the reference, so now we can inflate, allowing cycles. */
|
|
8435
8964
|
if (typeId >= 14 /* TypeIds.Error */) {
|
|
8436
8965
|
propValue = inflate(container, propValue, typeId, value);
|
|
8966
|
+
Reflect.set(target, property, propValue);
|
|
8967
|
+
this.$data$[idx + 1] = propValue;
|
|
8437
8968
|
}
|
|
8438
|
-
Reflect.set(target, property, propValue);
|
|
8439
|
-
this.$data$[idx] = undefined;
|
|
8440
|
-
this.$data$[idx + 1] = propValue;
|
|
8441
8969
|
return propValue;
|
|
8442
8970
|
}
|
|
8443
8971
|
has(target, property) {
|
|
@@ -8616,7 +9144,7 @@ const inflate = (container, target, typeId, data) => {
|
|
|
8616
9144
|
*/
|
|
8617
9145
|
// try to download qrl in this tick
|
|
8618
9146
|
computed.$computeQrl$.resolve();
|
|
8619
|
-
container.$scheduler
|
|
9147
|
+
container.$scheduler$(1 /* ChoreType.QRL_RESOLVE */, null, computed.$computeQrl$);
|
|
8620
9148
|
}
|
|
8621
9149
|
break;
|
|
8622
9150
|
}
|
|
@@ -9404,8 +9932,8 @@ async function serialize(serializationContext) {
|
|
|
9404
9932
|
}
|
|
9405
9933
|
else if (value instanceof ComputedSignalImpl) {
|
|
9406
9934
|
let v = value.$untrackedValue$;
|
|
9407
|
-
const shouldAlwaysSerialize = value.$flags$ &
|
|
9408
|
-
const shouldNeverSerialize = value.$flags$ &
|
|
9935
|
+
const shouldAlwaysSerialize = value.$flags$ & 32 /* SerializationSignalFlags.SERIALIZATION_STRATEGY_ALWAYS */;
|
|
9936
|
+
const shouldNeverSerialize = value.$flags$ & 16 /* SerializationSignalFlags.SERIALIZATION_STRATEGY_NEVER */;
|
|
9409
9937
|
const isInvalid = value.$flags$ & 1 /* SignalFlags.INVALID */;
|
|
9410
9938
|
const isSkippable = fastSkipSerialize(value.$untrackedValue$);
|
|
9411
9939
|
if (shouldAlwaysSerialize) {
|
|
@@ -10747,7 +11275,7 @@ const render = async (parent, jsxNode, opts = {}) => {
|
|
|
10747
11275
|
container.$serverData$ = opts.serverData || {};
|
|
10748
11276
|
const host = container.rootVNode;
|
|
10749
11277
|
container.$scheduler$(4 /* ChoreType.NODE_DIFF */, host, host, jsxNode);
|
|
10750
|
-
await container.$scheduler$(255 /* ChoreType.
|
|
11278
|
+
await container.$scheduler$(255 /* ChoreType.WAIT_FOR_QUEUE */).$returnValue$;
|
|
10751
11279
|
return {
|
|
10752
11280
|
cleanup: () => {
|
|
10753
11281
|
cleanup(container, container.rootVNode);
|
|
@@ -11585,7 +12113,7 @@ const useVisibleTaskQrl = (qrl, opts) => {
|
|
|
11585
12113
|
useRunTask(task, eagerness);
|
|
11586
12114
|
if (!isServerPlatform()) {
|
|
11587
12115
|
qrl.resolve(iCtx.$element$);
|
|
11588
|
-
iCtx.$container$.$scheduler$(
|
|
12116
|
+
iCtx.$container$.$scheduler$(16 /* ChoreType.VISIBLE */, task);
|
|
11589
12117
|
}
|
|
11590
12118
|
};
|
|
11591
12119
|
const useRunTask = (task, eagerness) => {
|
|
@@ -11814,7 +12342,7 @@ const PrefetchServiceWorker = (opts) => {
|
|
|
11814
12342
|
// the file 'qwik-prefetch-service-worker.js' is not located in /build/
|
|
11815
12343
|
resolvedOpts.path = baseUrl + resolvedOpts.path;
|
|
11816
12344
|
}
|
|
11817
|
-
let code = PREFETCH_CODE.replace("
|
|
12345
|
+
let code = PREFETCH_CODE.replace('"_URL_"', JSON.stringify(resolvedOpts.path.split('/').pop()));
|
|
11818
12346
|
if (!isDev) {
|
|
11819
12347
|
// consecutive spaces are indentation
|
|
11820
12348
|
code = code.replaceAll(/\s\s+/gm, '');
|
|
@@ -11844,6 +12372,17 @@ const PREFETCH_CODE = /*#__PURE__*/ ((c // Service worker container
|
|
|
11844
12372
|
});
|
|
11845
12373
|
});
|
|
11846
12374
|
}
|
|
12375
|
+
if ('caches' in window) {
|
|
12376
|
+
caches
|
|
12377
|
+
.keys()
|
|
12378
|
+
.then((names) => {
|
|
12379
|
+
const cacheName = names.find((name) => name.startsWith('QwikBundles'));
|
|
12380
|
+
if (cacheName) {
|
|
12381
|
+
caches.delete(cacheName).catch(console.error);
|
|
12382
|
+
}
|
|
12383
|
+
})
|
|
12384
|
+
.catch(console.error);
|
|
12385
|
+
}
|
|
11847
12386
|
}).toString();
|
|
11848
12387
|
/**
|
|
11849
12388
|
* @deprecated This is no longer needed as the preloading happens automatically in qrl-class. You
|
|
@@ -11852,5 +12391,15 @@ const PREFETCH_CODE = /*#__PURE__*/ ((c // Service worker container
|
|
|
11852
12391
|
*/
|
|
11853
12392
|
const PrefetchGraph = (_opts = {}) => null;
|
|
11854
12393
|
|
|
11855
|
-
|
|
12394
|
+
//////////////////////////////////////////////////////////////////////////////////////////
|
|
12395
|
+
// Protect against duplicate imports
|
|
12396
|
+
//////////////////////////////////////////////////////////////////////////////////////////
|
|
12397
|
+
if (globalThis.__qwik) {
|
|
12398
|
+
console.error(`==============================================\n` +
|
|
12399
|
+
`Qwik version ${globalThis.__qwik} already imported while importing ${version}. Verify external vs bundled imports etc. This can lead to issues due to duplicated shared structures.\n` +
|
|
12400
|
+
`==============================================\n`);
|
|
12401
|
+
}
|
|
12402
|
+
globalThis.__qwik = version;
|
|
12403
|
+
|
|
12404
|
+
export { $, Fragment, NoSerializeSymbol, PrefetchGraph, PrefetchServiceWorker, RenderOnce, Resource, SSRComment, SSRRaw, SSRStream, SSRStreamBlock, SerializerSymbol, SkipRender, Slot, _CONST_PROPS, DomContainer as _DomContainer, _EFFECT_BACK_REF, EMPTY_ARRAY as _EMPTY_ARRAY, _IMMUTABLE, _SharedContainer, SubscriptionData as _SubscriptionData, _UNINITIALIZED, _VAR_PROPS, _deserialize, dumpState as _dumpState, _fnSignal, _getConstProps, _getContextContainer, _getContextElement, _getContextEvent, getDomContainer as _getDomContainer, _getQContainerElement, _getVarProps, _hasStoreEffects, isJSXNode as _isJSXNode, isStore as _isStore, isStringifiable as _isStringifiable, isTask as _isTask, _jsxBranch, _jsxC, _jsxQ, _jsxS, _jsxSorted, _jsxSplit, mapApp_findIndx as _mapApp_findIndx, mapArray_get as _mapArray_get, mapArray_set as _mapArray_set, _noopQrl, _noopQrlDEV, preprocessState as _preprocessState, _qrlSync, _regSymbol, _resolveContextWithoutSequentialScope, _restProps, _run, _serializationWeakRef, _serialize, scheduleTask as _task, verifySerializable as _verifySerializable, vnode_ensureElementInflated as _vnode_ensureElementInflated, vnode_getAttr as _vnode_getAttr, vnode_getAttrKeys as _vnode_getAttrKeys, vnode_getFirstChild as _vnode_getFirstChild, vnode_getNextSibling as _vnode_getNextSibling, vnode_getPropStartIndex as _vnode_getPropStartIndex, vnode_getProps as _vnode_getProps, vnode_isMaterialized as _vnode_isMaterialized, vnode_isTextVNode as _vnode_isTextVNode, vnode_isVirtualVNode as _vnode_isVirtualVNode, vnode_toString as _vnode_toString, _waitUntilRendered, _walkJSX, _wrapProp, _wrapSignal, component$, componentQrl, createAsyncComputed$, createAsyncComputedSignal as createAsyncComputedQrl, createComputed$, createComputedSignal as createComputedQrl, createContextId, h as createElement, createSerializer$, createSerializerSignal as createSerializerQrl, createSignal, event$, eventQrl, forceStoreEffects, getDomContainer, getLocale, getPlatform, h, implicit$FirstArg, inlinedQrl, inlinedQrlDEV, isSignal, jsx, jsxDEV, jsx as jsxs, noSerialize, qrl, qrlDEV, render, setPlatform, sync$, untrack, unwrapStore, useAsyncComputed$, useAsyncComputedQrl, useComputed$, useComputedQrl, useConstant, useContext, useContextProvider, useErrorBoundary, useId, useLexicalScope, useOn, useOnDocument, useOnWindow, useResource$, useResourceQrl, useSerializer$, useSerializerQrl, useServerData, useSignal, useStore, useStyles$, useStylesQrl, useStylesScoped$, useStylesScopedQrl, useTask$, useTaskQrl, useVisibleTask$, useVisibleTaskQrl, version, withLocale };
|
|
11856
12405
|
//# sourceMappingURL=core.mjs.map
|