@warp-drive/ember 0.0.0-beta.1 → 0.0.0-beta.11
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/LICENSE.md +19 -7
- package/README.md +15 -6
- package/dist/index.js +419 -148
- package/dist/index.js.map +1 -1
- package/logos/NCC-1701-a-blue.svg +4 -0
- package/logos/NCC-1701-a-gold.svg +4 -0
- package/logos/NCC-1701-a-gold_100.svg +1 -0
- package/logos/NCC-1701-a-gold_base-64.txt +1 -0
- package/logos/NCC-1701-a.svg +4 -0
- package/logos/README.md +4 -0
- package/logos/docs-badge.svg +2 -0
- package/logos/ember-data-logo-dark.svg +12 -0
- package/logos/ember-data-logo-light.svg +12 -0
- package/logos/github-header.svg +444 -0
- package/logos/social1.png +0 -0
- package/logos/social2.png +0 -0
- package/logos/warp-drive-logo-dark.svg +4 -0
- package/logos/warp-drive-logo-gold.svg +4 -0
- package/package.json +57 -68
- package/unstable-preview-types/-private/await.d.ts.map +1 -1
- package/unstable-preview-types/-private/request.d.ts +120 -2
- package/unstable-preview-types/-private/request.d.ts.map +1 -1
- package/unstable-preview-types/index.d.ts +2 -2
package/dist/index.js
CHANGED
|
@@ -6,26 +6,7 @@ import { macroCondition, moduleExists, importSync, getGlobalConfig } from '@embr
|
|
|
6
6
|
import { EnableHydration } from '@warp-drive/core-types/request';
|
|
7
7
|
import { precompileTemplate } from '@ember/template-compilation';
|
|
8
8
|
import { setComponentTemplate } from '@ember/component';
|
|
9
|
-
|
|
10
|
-
var __export = (target, all) => {
|
|
11
|
-
for (var name in all) __defProp(target, name, {
|
|
12
|
-
get: all[name],
|
|
13
|
-
enumerable: true
|
|
14
|
-
});
|
|
15
|
-
};
|
|
16
|
-
|
|
17
|
-
// src/runtime.ts
|
|
18
|
-
var runtime_exports = {};
|
|
19
|
-
__export(runtime_exports, {
|
|
20
|
-
c: () => decorateClass,
|
|
21
|
-
f: () => decorateFieldV1,
|
|
22
|
-
g: () => decorateFieldV2,
|
|
23
|
-
i: () => initializeDeferredDecorator,
|
|
24
|
-
m: () => decorateMethodV1,
|
|
25
|
-
n: () => decorateMethodV2,
|
|
26
|
-
p: () => decoratePOJO
|
|
27
|
-
});
|
|
28
|
-
var deferred = /* @__PURE__ */new WeakMap();
|
|
9
|
+
const deferred = /* @__PURE__ */new WeakMap();
|
|
29
10
|
function deferDecorator(proto, prop, desc) {
|
|
30
11
|
let map = deferred.get(proto);
|
|
31
12
|
if (!map) {
|
|
@@ -35,18 +16,16 @@ function deferDecorator(proto, prop, desc) {
|
|
|
35
16
|
map.set(prop, desc);
|
|
36
17
|
}
|
|
37
18
|
function findDeferredDecorator(target, prop) {
|
|
19
|
+
var _a;
|
|
38
20
|
let cursor = target.prototype;
|
|
39
21
|
while (cursor) {
|
|
40
|
-
let desc = deferred.get(cursor)
|
|
22
|
+
let desc = (_a = deferred.get(cursor)) == null ? void 0 : _a.get(prop);
|
|
41
23
|
if (desc) {
|
|
42
24
|
return desc;
|
|
43
25
|
}
|
|
44
26
|
cursor = cursor.prototype;
|
|
45
27
|
}
|
|
46
28
|
}
|
|
47
|
-
function decorateFieldV1(target, prop, decorators, initializer) {
|
|
48
|
-
return decorateFieldV2(target.prototype, prop, decorators, initializer);
|
|
49
|
-
}
|
|
50
29
|
function decorateFieldV2(prototype, prop, decorators, initializer) {
|
|
51
30
|
let desc = {
|
|
52
31
|
configurable: true,
|
|
@@ -66,11 +45,6 @@ function decorateFieldV2(prototype, prop, decorators, initializer) {
|
|
|
66
45
|
deferDecorator(prototype, prop, desc);
|
|
67
46
|
}
|
|
68
47
|
}
|
|
69
|
-
function decorateMethodV1({
|
|
70
|
-
prototype
|
|
71
|
-
}, prop, decorators) {
|
|
72
|
-
return decorateMethodV2(prototype, prop, decorators);
|
|
73
|
-
}
|
|
74
48
|
function decorateMethodV2(prototype, prop, decorators) {
|
|
75
49
|
const origDesc = Object.getOwnPropertyDescriptor(prototype, prop);
|
|
76
50
|
let desc = {
|
|
@@ -96,35 +70,6 @@ function initializeDeferredDecorator(target, prop) {
|
|
|
96
70
|
});
|
|
97
71
|
}
|
|
98
72
|
}
|
|
99
|
-
function decorateClass(target, decorators) {
|
|
100
|
-
return decorators.reduce((accum, decorator) => decorator(accum) || accum, target);
|
|
101
|
-
}
|
|
102
|
-
function decoratePOJO(pojo, decorated) {
|
|
103
|
-
for (let [type, prop, decorators] of decorated) {
|
|
104
|
-
if (type === "field") {
|
|
105
|
-
decoratePojoField(pojo, prop, decorators);
|
|
106
|
-
} else {
|
|
107
|
-
decorateMethodV2(pojo, prop, decorators);
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
return pojo;
|
|
111
|
-
}
|
|
112
|
-
function decoratePojoField(pojo, prop, decorators) {
|
|
113
|
-
let desc = {
|
|
114
|
-
configurable: true,
|
|
115
|
-
enumerable: true,
|
|
116
|
-
writable: true,
|
|
117
|
-
initializer: () => Object.getOwnPropertyDescriptor(pojo, prop)?.value
|
|
118
|
-
};
|
|
119
|
-
for (let decorator of decorators) {
|
|
120
|
-
desc = decorator(pojo, prop, desc) || desc;
|
|
121
|
-
}
|
|
122
|
-
if (desc.initializer) {
|
|
123
|
-
desc.value = desc.initializer.call(pojo);
|
|
124
|
-
delete desc.initializer;
|
|
125
|
-
}
|
|
126
|
-
Object.defineProperty(pojo, prop, desc);
|
|
127
|
-
}
|
|
128
73
|
const RequestCache = new WeakMap();
|
|
129
74
|
function isAbortError(error) {
|
|
130
75
|
return error instanceof DOMException && error.name === 'AbortError';
|
|
@@ -542,10 +487,10 @@ function getPromiseState(promise) {
|
|
|
542
487
|
}
|
|
543
488
|
return state;
|
|
544
489
|
}
|
|
545
|
-
const and = (
|
|
490
|
+
const and = (x, y) => Boolean(x && y);
|
|
546
491
|
class Throw extends Component {
|
|
547
|
-
constructor(
|
|
548
|
-
super(
|
|
492
|
+
constructor(owner, args) {
|
|
493
|
+
super(owner, args);
|
|
549
494
|
// this error is opaque (user supplied) so we don't validate it
|
|
550
495
|
// as an Error instance.
|
|
551
496
|
// eslint-disable-next-line @typescript-eslint/no-throw-literal
|
|
@@ -577,33 +522,44 @@ class Await extends Component {
|
|
|
577
522
|
}), this);
|
|
578
523
|
}
|
|
579
524
|
}
|
|
580
|
-
function notNull(
|
|
525
|
+
function notNull(x) {
|
|
581
526
|
macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
|
|
582
527
|
if (!test) {
|
|
583
528
|
throw new Error('Expected a non-null value, but got null');
|
|
584
529
|
}
|
|
585
|
-
})(
|
|
586
|
-
return
|
|
530
|
+
})(x !== null) : {};
|
|
531
|
+
return x;
|
|
587
532
|
}
|
|
588
|
-
const not =
|
|
533
|
+
const not = x => !x;
|
|
589
534
|
// default to 30 seconds unavailable before we refresh
|
|
590
535
|
const DEFAULT_DEADLINE = 30_000;
|
|
591
|
-
|
|
536
|
+
const IdleBlockMissingError = new Error('No idle block provided for <Request> component, and no query or request was provided.');
|
|
537
|
+
let consume = service;
|
|
592
538
|
if (macroCondition(moduleExists('ember-provide-consume-context'))) {
|
|
593
539
|
const {
|
|
594
|
-
consume
|
|
540
|
+
consume: contextConsume
|
|
595
541
|
} = importSync('ember-provide-consume-context');
|
|
596
|
-
|
|
542
|
+
consume = contextConsume;
|
|
597
543
|
}
|
|
598
|
-
function isNeverString(
|
|
599
|
-
return
|
|
544
|
+
function isNeverString(val) {
|
|
545
|
+
return val;
|
|
600
546
|
}
|
|
547
|
+
/**
|
|
548
|
+
* The `<Request>` component is a powerful tool for managing data fetching and
|
|
549
|
+
* state in your Ember application. It provides declarative reactive control-flow
|
|
550
|
+
* for managing requests and state in your application.
|
|
551
|
+
*
|
|
552
|
+
* @typedoc
|
|
553
|
+
*/
|
|
601
554
|
class Request extends Component {
|
|
602
555
|
static {
|
|
603
|
-
decorateFieldV2(this.prototype, "_store", [
|
|
556
|
+
decorateFieldV2(this.prototype, "_store", [consume('store')]);
|
|
604
557
|
}
|
|
605
558
|
#_store = (initializeDeferredDecorator(this, "_store"), void 0);
|
|
606
559
|
/**
|
|
560
|
+
* The store instance to use for making requests. If contexts are available, this
|
|
561
|
+
* will be the `store` on the context, else it will be the store service.
|
|
562
|
+
*
|
|
607
563
|
* @internal
|
|
608
564
|
*/
|
|
609
565
|
static {
|
|
@@ -612,35 +568,286 @@ class Request extends Component {
|
|
|
612
568
|
});
|
|
613
569
|
}
|
|
614
570
|
#isOnline = (initializeDeferredDecorator(this, "isOnline"), void 0);
|
|
571
|
+
/**
|
|
572
|
+
* Whether the browser reports that the network is online.
|
|
573
|
+
*
|
|
574
|
+
* @internal
|
|
575
|
+
*/
|
|
615
576
|
static {
|
|
616
577
|
decorateFieldV2(this.prototype, "isHidden", [tracked], function () {
|
|
617
578
|
return true;
|
|
618
579
|
});
|
|
619
580
|
}
|
|
620
581
|
#isHidden = (initializeDeferredDecorator(this, "isHidden"), void 0);
|
|
582
|
+
/**
|
|
583
|
+
* Whether the browser reports that the tab is hidden.
|
|
584
|
+
*
|
|
585
|
+
* @internal
|
|
586
|
+
*/
|
|
621
587
|
static {
|
|
622
588
|
decorateFieldV2(this.prototype, "isRefreshing", [tracked], function () {
|
|
623
589
|
return false;
|
|
624
590
|
});
|
|
625
591
|
}
|
|
626
592
|
#isRefreshing = (initializeDeferredDecorator(this, "isRefreshing"), void 0);
|
|
593
|
+
/**
|
|
594
|
+
* Whether the component is currently refreshing the request.
|
|
595
|
+
*
|
|
596
|
+
* @internal
|
|
597
|
+
*/
|
|
627
598
|
static {
|
|
628
599
|
decorateFieldV2(this.prototype, "_localRequest", [tracked]);
|
|
629
600
|
}
|
|
630
601
|
#_localRequest = (initializeDeferredDecorator(this, "_localRequest"), void 0);
|
|
602
|
+
/**
|
|
603
|
+
* The most recent blocking request that was made, typically
|
|
604
|
+
* the result of a reload.
|
|
605
|
+
*
|
|
606
|
+
* This will never be the original request passed as an arg to
|
|
607
|
+
* the component.
|
|
608
|
+
*
|
|
609
|
+
* @internal
|
|
610
|
+
*/
|
|
631
611
|
static {
|
|
632
612
|
decorateFieldV2(this.prototype, "_latestRequest", [tracked]);
|
|
633
613
|
}
|
|
634
614
|
#_latestRequest = (initializeDeferredDecorator(this, "_latestRequest"), void 0);
|
|
615
|
+
/**
|
|
616
|
+
* The most recent request that was made, typically due to either a
|
|
617
|
+
* reload or a refresh.
|
|
618
|
+
*
|
|
619
|
+
* This will never be the original request passed as an arg to
|
|
620
|
+
* the component.
|
|
621
|
+
*
|
|
622
|
+
* @internal
|
|
623
|
+
*/
|
|
624
|
+
/**
|
|
625
|
+
* The time at which the network was reported as offline.
|
|
626
|
+
*
|
|
627
|
+
* @internal
|
|
628
|
+
*/
|
|
635
629
|
unavailableStart;
|
|
630
|
+
intervalStart;
|
|
631
|
+
nextInterval;
|
|
632
|
+
invalidated;
|
|
633
|
+
isUpdating;
|
|
634
|
+
/**
|
|
635
|
+
* The event listener for network status changes,
|
|
636
|
+
* cached to use the reference for removal.
|
|
637
|
+
*
|
|
638
|
+
* @internal
|
|
639
|
+
*/
|
|
636
640
|
onlineChanged;
|
|
641
|
+
/**
|
|
642
|
+
* The event listener for visibility status changes,
|
|
643
|
+
* cached to use the reference for removal.
|
|
644
|
+
*
|
|
645
|
+
* @internal
|
|
646
|
+
*/
|
|
637
647
|
backgroundChanged;
|
|
648
|
+
/**
|
|
649
|
+
* The last request passed as an arg to the component,
|
|
650
|
+
* cached for comparison.
|
|
651
|
+
*
|
|
652
|
+
* @internal
|
|
653
|
+
*/
|
|
638
654
|
_originalRequest;
|
|
655
|
+
/**
|
|
656
|
+
* The last query passed as an arg to the component,
|
|
657
|
+
* cached for comparison.
|
|
658
|
+
*
|
|
659
|
+
* @internal
|
|
660
|
+
*/
|
|
639
661
|
_originalQuery;
|
|
640
|
-
|
|
641
|
-
|
|
662
|
+
_subscription;
|
|
663
|
+
_subscribedTo;
|
|
664
|
+
constructor(owner, args) {
|
|
665
|
+
super(owner, args);
|
|
666
|
+
this._subscribedTo = null;
|
|
667
|
+
this._subscription = null;
|
|
668
|
+
this.intervalStart = null;
|
|
669
|
+
this.invalidated = false;
|
|
670
|
+
this.nextInterval = null;
|
|
642
671
|
this.installListeners();
|
|
672
|
+
void this.beginPolling();
|
|
673
|
+
}
|
|
674
|
+
async beginPolling() {
|
|
675
|
+
// await the initial request
|
|
676
|
+
try {
|
|
677
|
+
await this.request;
|
|
678
|
+
} catch {
|
|
679
|
+
// ignore errors here, we just want to wait for the request to finish
|
|
680
|
+
} finally {
|
|
681
|
+
if (!this.isDestroyed) {
|
|
682
|
+
void this.scheduleInterval();
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
get isIdle() {
|
|
687
|
+
const {
|
|
688
|
+
request,
|
|
689
|
+
query
|
|
690
|
+
} = this.args;
|
|
691
|
+
return Boolean(!request && !query);
|
|
692
|
+
}
|
|
693
|
+
static {
|
|
694
|
+
decorateMethodV2(this.prototype, "isIdle", [cached]);
|
|
695
|
+
}
|
|
696
|
+
get autorefreshTypes() {
|
|
697
|
+
const {
|
|
698
|
+
autorefresh
|
|
699
|
+
} = this.args;
|
|
700
|
+
let types;
|
|
701
|
+
if (autorefresh === true) {
|
|
702
|
+
types = ['online', 'invalid'];
|
|
703
|
+
} else if (typeof autorefresh === 'string') {
|
|
704
|
+
types = autorefresh.split(',');
|
|
705
|
+
} else {
|
|
706
|
+
types = [];
|
|
707
|
+
}
|
|
708
|
+
return new Set(types);
|
|
709
|
+
}
|
|
710
|
+
// we only run this function on component creation
|
|
711
|
+
// and when an update is triggered, so it does not
|
|
712
|
+
// react to changes in the autorefreshThreshold
|
|
713
|
+
// or autorefresh args.
|
|
714
|
+
//
|
|
715
|
+
// if we need to react to those changes, we can
|
|
716
|
+
// use a modifier or internal component or some
|
|
717
|
+
// such to trigger a re-run of this function.
|
|
718
|
+
static {
|
|
719
|
+
decorateMethodV2(this.prototype, "autorefreshTypes", [cached]);
|
|
720
|
+
}
|
|
721
|
+
async scheduleInterval() {
|
|
722
|
+
const {
|
|
723
|
+
autorefreshThreshold
|
|
724
|
+
} = this.args;
|
|
725
|
+
const hasValidThreshold = typeof autorefreshThreshold === 'number' && autorefreshThreshold > 0;
|
|
726
|
+
if (typeof window === 'undefined' ||
|
|
727
|
+
// dont schedule without a threshold
|
|
728
|
+
!hasValidThreshold ||
|
|
729
|
+
// dont schedule if we weren't told to
|
|
730
|
+
!this.autorefreshTypes.has('interval') ||
|
|
731
|
+
// dont schedule if we're already scheduled
|
|
732
|
+
this.intervalStart !== null) {
|
|
733
|
+
return;
|
|
734
|
+
}
|
|
735
|
+
// if we have a current request, wait for it to finish
|
|
736
|
+
// before scheduling the next one
|
|
737
|
+
if (this._latestRequest) {
|
|
738
|
+
try {
|
|
739
|
+
await this._latestRequest;
|
|
740
|
+
} catch {
|
|
741
|
+
// ignore errors here, we just want to wait for the request to finish
|
|
742
|
+
}
|
|
743
|
+
if (this.isDestroyed) {
|
|
744
|
+
return;
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
// setup the next interval
|
|
748
|
+
this.intervalStart = Date.now();
|
|
749
|
+
this.nextInterval = setTimeout(() => {
|
|
750
|
+
this.maybeUpdate();
|
|
751
|
+
}, autorefreshThreshold);
|
|
643
752
|
}
|
|
753
|
+
clearInterval() {
|
|
754
|
+
if (this.nextInterval) {
|
|
755
|
+
clearTimeout(this.nextInterval);
|
|
756
|
+
this.intervalStart = null;
|
|
757
|
+
}
|
|
758
|
+
}
|
|
759
|
+
updateSubscriptions() {
|
|
760
|
+
if (this.isIdle) {
|
|
761
|
+
return;
|
|
762
|
+
}
|
|
763
|
+
const requestId = this._request.lid;
|
|
764
|
+
// if we're already subscribed to this request, we don't need to do anything
|
|
765
|
+
if (this._subscribedTo === requestId) {
|
|
766
|
+
return;
|
|
767
|
+
}
|
|
768
|
+
// if we're subscribed to a different request, we need to unsubscribe
|
|
769
|
+
this.removeSubscriptions();
|
|
770
|
+
// if we have a request, we need to subscribe to it
|
|
771
|
+
if (requestId) {
|
|
772
|
+
this._subscribedTo = requestId;
|
|
773
|
+
this._subscription = this.store.notifications.subscribe(requestId, (_id, op) => {
|
|
774
|
+
// ignore subscription events that occur while our own component's request
|
|
775
|
+
// is ocurring
|
|
776
|
+
if (this.isUpdating) {
|
|
777
|
+
return;
|
|
778
|
+
}
|
|
779
|
+
switch (op) {
|
|
780
|
+
case 'invalidated':
|
|
781
|
+
{
|
|
782
|
+
// if we're subscribed to invalidations, we need to update
|
|
783
|
+
if (this.autorefreshTypes.has('invalid')) {
|
|
784
|
+
this.invalidated = true;
|
|
785
|
+
this.maybeUpdate();
|
|
786
|
+
}
|
|
787
|
+
break;
|
|
788
|
+
}
|
|
789
|
+
case 'state':
|
|
790
|
+
{
|
|
791
|
+
const latest = this.store.requestManager._deduped.get(requestId);
|
|
792
|
+
const priority = latest?.priority;
|
|
793
|
+
const state = this.reqState;
|
|
794
|
+
if (!priority) {
|
|
795
|
+
// if there is no priority, we have completed whatever request
|
|
796
|
+
// was occurring and so we are no longer refreshing (if we were)
|
|
797
|
+
this.isRefreshing = false;
|
|
798
|
+
} else if (priority.blocking && !state.isLoading) {
|
|
799
|
+
// if we are blocking, there is an active request for this identity
|
|
800
|
+
// that MUST be fulfilled from network (not cache).
|
|
801
|
+
// Thus this is not "refreshing" because we should clear out and
|
|
802
|
+
// block on this request.
|
|
803
|
+
//
|
|
804
|
+
// we receive state notifications when either a request initiates
|
|
805
|
+
// or completes.
|
|
806
|
+
//
|
|
807
|
+
// In the completes case: we may receive the state notification
|
|
808
|
+
// slightly before the request is finalized because the NotificationManager
|
|
809
|
+
// may sync flush it (and thus deliver it before the microtask completes)
|
|
810
|
+
//
|
|
811
|
+
// In the initiates case: we aren't supposed to receive one unless there
|
|
812
|
+
// is no other request in flight for this identity.
|
|
813
|
+
//
|
|
814
|
+
// However, there is a race condition here where the completed
|
|
815
|
+
// notification can trigger an update that generates a new request
|
|
816
|
+
// thus giving us an initiated notification before the older request
|
|
817
|
+
// finalizes.
|
|
818
|
+
//
|
|
819
|
+
// When this occurs, if the triggered update happens to have caused
|
|
820
|
+
// a new request to be made for the same identity AND that request
|
|
821
|
+
// is the one passed into this component as the @request arg, then
|
|
822
|
+
// getRequestState will return the state of the new request.
|
|
823
|
+
// We can detect this by checking if the request state is "loading"
|
|
824
|
+
// as outside of this case we would have a completed request.
|
|
825
|
+
//
|
|
826
|
+
// That is the reason for the `&& !state.isLoading` check above.
|
|
827
|
+
// TODO should we just treat this as refreshing?
|
|
828
|
+
this.isRefreshing = false;
|
|
829
|
+
this.maybeUpdate('policy', true);
|
|
830
|
+
} else {
|
|
831
|
+
this.isRefreshing = true;
|
|
832
|
+
}
|
|
833
|
+
}
|
|
834
|
+
}
|
|
835
|
+
});
|
|
836
|
+
}
|
|
837
|
+
}
|
|
838
|
+
removeSubscriptions() {
|
|
839
|
+
if (this._subscription) {
|
|
840
|
+
this.store.notifications.unsubscribe(this._subscription);
|
|
841
|
+
this._subscribedTo = null;
|
|
842
|
+
this._subscription = null;
|
|
843
|
+
}
|
|
844
|
+
}
|
|
845
|
+
/**
|
|
846
|
+
* Install the event listeners for network and visibility changes.
|
|
847
|
+
* This is only done in browser environments with a global `window`.
|
|
848
|
+
*
|
|
849
|
+
* @internal
|
|
850
|
+
*/
|
|
644
851
|
installListeners() {
|
|
645
852
|
if (typeof window === 'undefined') {
|
|
646
853
|
return;
|
|
@@ -648,17 +855,17 @@ class Request extends Component {
|
|
|
648
855
|
this.isOnline = window.navigator.onLine;
|
|
649
856
|
this.unavailableStart = this.isOnline ? null : Date.now();
|
|
650
857
|
this.isHidden = document.visibilityState === 'hidden';
|
|
651
|
-
this.onlineChanged =
|
|
652
|
-
this.isOnline =
|
|
653
|
-
if (
|
|
858
|
+
this.onlineChanged = event => {
|
|
859
|
+
this.isOnline = event.type === 'online';
|
|
860
|
+
if (event.type === 'offline' && this.unavailableStart === null) {
|
|
654
861
|
this.unavailableStart = Date.now();
|
|
655
862
|
}
|
|
656
863
|
this.maybeUpdate();
|
|
657
864
|
};
|
|
658
865
|
this.backgroundChanged = () => {
|
|
659
|
-
const
|
|
660
|
-
this.isHidden =
|
|
661
|
-
if (
|
|
866
|
+
const isHidden = document.visibilityState === 'hidden';
|
|
867
|
+
this.isHidden = isHidden;
|
|
868
|
+
if (isHidden && this.unavailableStart === null) {
|
|
662
869
|
this.unavailableStart = Date.now();
|
|
663
870
|
}
|
|
664
871
|
this.maybeUpdate();
|
|
@@ -676,59 +883,112 @@ class Request extends Component {
|
|
|
676
883
|
capture: true
|
|
677
884
|
});
|
|
678
885
|
}
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
default:
|
|
701
|
-
throw new Error(`Invalid ${mode1 ? 'update mode' : '@autorefreshBehavior'} for <Request />: ${isNeverString(val1)}`);
|
|
702
|
-
}
|
|
703
|
-
const wasStoreRequest1 = request1[EnableHydration] === true;
|
|
704
|
-
macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
|
|
705
|
-
if (!test) {
|
|
706
|
-
throw new Error(`Cannot supply a different store via context than was used to create the request`);
|
|
707
|
-
}
|
|
708
|
-
})(!request1.store || request1.store === this.store) : {};
|
|
709
|
-
this._latestRequest = wasStoreRequest1 ? this.store.request(request1) : this.store.requestManager.request(request1);
|
|
710
|
-
if (val1 !== 'refresh') {
|
|
711
|
-
this._localRequest = this._latestRequest;
|
|
712
|
-
}
|
|
713
|
-
return;
|
|
886
|
+
/**
|
|
887
|
+
* If the network is online and the tab is visible, either reload or refresh the request
|
|
888
|
+
* based on the component's configuration and the requested update mode.
|
|
889
|
+
*
|
|
890
|
+
* Valid modes are:
|
|
891
|
+
*
|
|
892
|
+
* - `'reload'`: Force a reload of the request.
|
|
893
|
+
* - `'refresh'`: Refresh the request in the background.
|
|
894
|
+
* - `'policy'`: Make the request, letting the store's configured CachePolicy decide whether to reload, refresh, or do nothing.
|
|
895
|
+
* - `undefined`: Make the request using the component's autorefreshBehavior setting if the autorefreshThreshold has passed.
|
|
896
|
+
*
|
|
897
|
+
* @internal
|
|
898
|
+
*/
|
|
899
|
+
maybeUpdate(mode, silent) {
|
|
900
|
+
if (this.isIdle) {
|
|
901
|
+
return;
|
|
902
|
+
}
|
|
903
|
+
const canAttempt = Boolean(this.isOnline && !this.isHidden && (mode || this.autorefreshTypes.size));
|
|
904
|
+
if (!canAttempt) {
|
|
905
|
+
if (!silent && mode && mode !== 'invalidated') {
|
|
906
|
+
throw new Error(`Reload not available: the network is not online or the tab is hidden`);
|
|
714
907
|
}
|
|
908
|
+
return;
|
|
715
909
|
}
|
|
716
|
-
|
|
717
|
-
|
|
910
|
+
const {
|
|
911
|
+
autorefreshTypes
|
|
912
|
+
} = this;
|
|
913
|
+
let shouldAttempt = this.invalidated || Boolean(mode);
|
|
914
|
+
if (!shouldAttempt && autorefreshTypes.has('online')) {
|
|
915
|
+
const {
|
|
916
|
+
unavailableStart
|
|
917
|
+
} = this;
|
|
918
|
+
const {
|
|
919
|
+
autorefreshThreshold
|
|
920
|
+
} = this.args;
|
|
921
|
+
const deadline = typeof autorefreshThreshold === 'number' ? autorefreshThreshold : DEFAULT_DEADLINE;
|
|
922
|
+
shouldAttempt = Boolean(unavailableStart && Date.now() - unavailableStart > deadline);
|
|
923
|
+
}
|
|
924
|
+
if (!shouldAttempt && autorefreshTypes.has('interval')) {
|
|
925
|
+
const {
|
|
926
|
+
intervalStart
|
|
927
|
+
} = this;
|
|
928
|
+
const {
|
|
929
|
+
autorefreshThreshold
|
|
930
|
+
} = this.args;
|
|
931
|
+
if (intervalStart && typeof autorefreshThreshold === 'number' && autorefreshThreshold > 0) {
|
|
932
|
+
shouldAttempt = Boolean(Date.now() - intervalStart >= autorefreshThreshold);
|
|
933
|
+
}
|
|
934
|
+
}
|
|
935
|
+
this.unavailableStart = null;
|
|
936
|
+
this.invalidated = false;
|
|
937
|
+
if (shouldAttempt) {
|
|
938
|
+
this.clearInterval();
|
|
939
|
+
const request = Object.assign({}, this.reqState.request);
|
|
940
|
+
const realMode = mode === 'invalidated' ? null : mode;
|
|
941
|
+
const val = realMode ?? this.args.autorefreshBehavior ?? 'policy';
|
|
942
|
+
switch (val) {
|
|
943
|
+
case 'reload':
|
|
944
|
+
request.cacheOptions = Object.assign({}, request.cacheOptions, {
|
|
945
|
+
reload: true
|
|
946
|
+
});
|
|
947
|
+
break;
|
|
948
|
+
case 'refresh':
|
|
949
|
+
request.cacheOptions = Object.assign({}, request.cacheOptions, {
|
|
950
|
+
backgroundReload: true
|
|
951
|
+
});
|
|
952
|
+
break;
|
|
953
|
+
case 'policy':
|
|
954
|
+
break;
|
|
955
|
+
default:
|
|
956
|
+
throw new Error(`Invalid ${mode ? 'update mode' : '@autorefreshBehavior'} for <Request />: ${isNeverString(val)}`);
|
|
957
|
+
}
|
|
958
|
+
const wasStoreRequest = request[EnableHydration] === true;
|
|
959
|
+
macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
|
|
960
|
+
if (!test) {
|
|
961
|
+
throw new Error(`Cannot supply a different store via context than was used to create the request`);
|
|
962
|
+
}
|
|
963
|
+
})(!request.store || request.store === this.store) : {};
|
|
964
|
+
this.isUpdating = true;
|
|
965
|
+
this._latestRequest = wasStoreRequest ? this.store.request(request) : this.store.requestManager.request(request);
|
|
966
|
+
if (val !== 'refresh') {
|
|
967
|
+
this._localRequest = this._latestRequest;
|
|
968
|
+
}
|
|
969
|
+
void this.scheduleInterval();
|
|
970
|
+
void this._latestRequest.finally(() => {
|
|
971
|
+
this.isUpdating = false;
|
|
972
|
+
});
|
|
718
973
|
}
|
|
719
974
|
}
|
|
975
|
+
/**
|
|
976
|
+
* Retry the request, reloading it from the server.
|
|
977
|
+
*
|
|
978
|
+
* @internal
|
|
979
|
+
*/
|
|
720
980
|
retry = async () => {
|
|
721
981
|
this.maybeUpdate('reload');
|
|
722
982
|
await this._localRequest;
|
|
723
983
|
};
|
|
984
|
+
/**
|
|
985
|
+
* Refresh the request, updating it in the background.
|
|
986
|
+
*
|
|
987
|
+
* @internal
|
|
988
|
+
*/
|
|
724
989
|
refresh = async () => {
|
|
725
|
-
this.isRefreshing = true;
|
|
726
990
|
this.maybeUpdate('refresh');
|
|
727
|
-
|
|
728
|
-
await this._latestRequest;
|
|
729
|
-
} finally {
|
|
730
|
-
this.isRefreshing = false;
|
|
731
|
-
}
|
|
991
|
+
await this._latestRequest;
|
|
732
992
|
};
|
|
733
993
|
get errorFeatures() {
|
|
734
994
|
return {
|
|
@@ -741,7 +1001,7 @@ class Request extends Component {
|
|
|
741
1001
|
decorateMethodV2(this.prototype, "errorFeatures", [cached]);
|
|
742
1002
|
}
|
|
743
1003
|
get contentFeatures() {
|
|
744
|
-
const
|
|
1004
|
+
const feat = {
|
|
745
1005
|
isHidden: this.isHidden,
|
|
746
1006
|
isOnline: this.isOnline,
|
|
747
1007
|
reload: this.retry,
|
|
@@ -749,20 +1009,22 @@ class Request extends Component {
|
|
|
749
1009
|
isRefreshing: this.isRefreshing,
|
|
750
1010
|
latestRequest: this._latestRequest
|
|
751
1011
|
};
|
|
752
|
-
if (
|
|
753
|
-
|
|
1012
|
+
if (feat.isRefreshing) {
|
|
1013
|
+
feat.abort = () => {
|
|
754
1014
|
this._latestRequest?.abort();
|
|
755
1015
|
};
|
|
756
1016
|
}
|
|
757
|
-
return
|
|
1017
|
+
return feat;
|
|
758
1018
|
}
|
|
759
1019
|
static {
|
|
760
1020
|
decorateMethodV2(this.prototype, "contentFeatures", [cached]);
|
|
761
1021
|
}
|
|
762
1022
|
willDestroy() {
|
|
1023
|
+
this.removeSubscriptions();
|
|
763
1024
|
if (typeof window === 'undefined') {
|
|
764
1025
|
return;
|
|
765
1026
|
}
|
|
1027
|
+
this.clearInterval();
|
|
766
1028
|
window.removeEventListener('online', this.onlineChanged, {
|
|
767
1029
|
passive: true,
|
|
768
1030
|
capture: true
|
|
@@ -776,49 +1038,57 @@ class Request extends Component {
|
|
|
776
1038
|
capture: true
|
|
777
1039
|
});
|
|
778
1040
|
}
|
|
779
|
-
get
|
|
1041
|
+
get _request() {
|
|
780
1042
|
const {
|
|
781
|
-
request
|
|
782
|
-
query
|
|
1043
|
+
request,
|
|
1044
|
+
query
|
|
783
1045
|
} = this.args;
|
|
784
1046
|
macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
|
|
785
1047
|
if (!test) {
|
|
786
1048
|
throw new Error(`Cannot use both @request and @query args with the <Request> component`);
|
|
787
1049
|
}
|
|
788
|
-
})(!
|
|
1050
|
+
})(!request || !query) : {};
|
|
789
1051
|
const {
|
|
790
|
-
_localRequest
|
|
791
|
-
_originalRequest
|
|
792
|
-
_originalQuery
|
|
1052
|
+
_localRequest,
|
|
1053
|
+
_originalRequest,
|
|
1054
|
+
_originalQuery
|
|
793
1055
|
} = this;
|
|
794
|
-
const
|
|
795
|
-
if (
|
|
796
|
-
return
|
|
1056
|
+
const isOriginalRequest = request === _originalRequest && query === _originalQuery;
|
|
1057
|
+
if (_localRequest && isOriginalRequest) {
|
|
1058
|
+
return _localRequest;
|
|
797
1059
|
}
|
|
798
1060
|
// update state checks for the next time
|
|
799
|
-
this._originalQuery =
|
|
800
|
-
this._originalRequest =
|
|
801
|
-
if (
|
|
802
|
-
return
|
|
1061
|
+
this._originalQuery = query;
|
|
1062
|
+
this._originalRequest = request;
|
|
1063
|
+
if (request) {
|
|
1064
|
+
return request;
|
|
803
1065
|
}
|
|
804
1066
|
macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
|
|
805
1067
|
if (!test) {
|
|
806
1068
|
throw new Error(`You must provide either @request or an @query arg with the <Request> component`);
|
|
807
1069
|
}
|
|
808
|
-
})(
|
|
809
|
-
return this.store.request(
|
|
1070
|
+
})(query) : {};
|
|
1071
|
+
return this.store.request(query);
|
|
1072
|
+
}
|
|
1073
|
+
static {
|
|
1074
|
+
decorateMethodV2(this.prototype, "_request", [cached]);
|
|
1075
|
+
}
|
|
1076
|
+
get request() {
|
|
1077
|
+
const request = this._request;
|
|
1078
|
+
this.updateSubscriptions();
|
|
1079
|
+
return request;
|
|
810
1080
|
}
|
|
811
1081
|
static {
|
|
812
1082
|
decorateMethodV2(this.prototype, "request", [cached]);
|
|
813
1083
|
}
|
|
814
1084
|
get store() {
|
|
815
|
-
const
|
|
1085
|
+
const store = this.args.store || this._store;
|
|
816
1086
|
macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
|
|
817
1087
|
if (!test) {
|
|
818
1088
|
throw new Error(moduleExists('ember-provide-consume-context') ? `No store was provided to the <Request> component. Either provide a store via the @store arg or via the context API provided by ember-provide-consume-context.` : `No store was provided to the <Request> component. Either provide a store via the @store arg or by registering a store service.`);
|
|
819
1089
|
}
|
|
820
|
-
})(
|
|
821
|
-
return
|
|
1090
|
+
})(store) : {};
|
|
1091
|
+
return store;
|
|
822
1092
|
}
|
|
823
1093
|
get reqState() {
|
|
824
1094
|
return getRequestState(this.request);
|
|
@@ -827,13 +1097,14 @@ class Request extends Component {
|
|
|
827
1097
|
return this.reqState.result;
|
|
828
1098
|
}
|
|
829
1099
|
static {
|
|
830
|
-
setComponentTemplate(precompileTemplate("\n {{#if this.reqState.isLoading}}\n {{yield this.reqState.loadingState to=\"loading\"}}\n {{else if (and this.reqState.isCancelled (has-block \"cancelled\"))}}\n {{yield (notNull this.reqState.error) this.errorFeatures to=\"cancelled\"}}\n {{else if (and this.reqState.isError (has-block \"error\"))}}\n {{yield (notNull this.reqState.error) this.errorFeatures to=\"error\"}}\n {{else if this.reqState.isSuccess}}\n {{yield this.result this.contentFeatures to=\"content\"}}\n {{else if (not this.reqState.isCancelled)}}\n <Throw @error={{(notNull this.reqState.error)}} />\n {{/if}}\n {{yield this.reqState to=\"always\"}}\n ", {
|
|
1100
|
+
setComponentTemplate(precompileTemplate("\n {{#if (and this.isIdle (has-block \"idle\"))}}\n {{yield to=\"idle\"}}\n {{else if this.isIdle}}\n <Throw @error={{IdleBlockMissingError}} />\n {{else if this.reqState.isLoading}}\n {{yield this.reqState.loadingState to=\"loading\"}}\n {{else if (and this.reqState.isCancelled (has-block \"cancelled\"))}}\n {{yield (notNull this.reqState.error) this.errorFeatures to=\"cancelled\"}}\n {{else if (and this.reqState.isError (has-block \"error\"))}}\n {{yield (notNull this.reqState.error) this.errorFeatures to=\"error\"}}\n {{else if this.reqState.isSuccess}}\n {{yield this.result this.contentFeatures to=\"content\"}}\n {{else if (not this.reqState.isCancelled)}}\n <Throw @error={{(notNull this.reqState.error)}} />\n {{/if}}\n {{yield this.reqState to=\"always\"}}\n ", {
|
|
831
1101
|
strictMode: true,
|
|
832
1102
|
scope: () => ({
|
|
833
1103
|
and,
|
|
1104
|
+
Throw,
|
|
1105
|
+
IdleBlockMissingError,
|
|
834
1106
|
notNull,
|
|
835
|
-
not
|
|
836
|
-
Throw
|
|
1107
|
+
not
|
|
837
1108
|
})
|
|
838
1109
|
}), this);
|
|
839
1110
|
}
|