@warp-drive/ember 5.4.1-beta.1 → 5.4.1-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +27 -0
- package/dist/index.js +68 -667
- package/dist/index.js.map +1 -1
- package/dist/install.js +67 -0
- package/dist/install.js.map +1 -0
- package/package.json +13 -15
- package/unstable-preview-types/-private/await.d.ts +1 -1
- package/unstable-preview-types/-private/await.d.ts.map +1 -1
- package/unstable-preview-types/-private/request.d.ts +8 -8
- package/unstable-preview-types/-private/request.d.ts.map +1 -1
- package/unstable-preview-types/index.d.ts +205 -7
- package/unstable-preview-types/index.d.ts.map +1 -1
- package/unstable-preview-types/install.d.ts +17 -0
- package/unstable-preview-types/install.d.ts.map +1 -0
- package/unstable-preview-types/-private/promise-state.d.ts +0 -91
- package/unstable-preview-types/-private/promise-state.d.ts.map +0 -1
- package/unstable-preview-types/-private/request-state.d.ts +0 -149
- package/unstable-preview-types/-private/request-state.d.ts.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,675 +1,12 @@
|
|
|
1
|
-
import { tracked, cached } from '@glimmer/tracking';
|
|
2
|
-
import { getPromiseResult, setPromiseResult } from '@ember-data/request';
|
|
3
1
|
import { service } from '@ember/service';
|
|
4
2
|
import Component from '@glimmer/component';
|
|
3
|
+
import { tracked, cached } from '@glimmer/tracking';
|
|
5
4
|
import { macroCondition, moduleExists, importSync, getGlobalConfig } from '@embroider/macros';
|
|
5
|
+
import { getPromiseState, getRequestState } from '@ember-data/store/-private';
|
|
6
|
+
export { getPromiseState, getRequestState } from '@ember-data/store/-private';
|
|
6
7
|
import { EnableHydration } from '@warp-drive/core-types/request';
|
|
7
8
|
import { precompileTemplate } from '@ember/template-compilation';
|
|
8
9
|
import { setComponentTemplate } from '@ember/component';
|
|
9
|
-
const deferred = /* @__PURE__ */new WeakMap();
|
|
10
|
-
function deferDecorator(proto, prop, desc) {
|
|
11
|
-
let map = deferred.get(proto);
|
|
12
|
-
if (!map) {
|
|
13
|
-
map = /* @__PURE__ */new Map();
|
|
14
|
-
deferred.set(proto, map);
|
|
15
|
-
}
|
|
16
|
-
map.set(prop, desc);
|
|
17
|
-
}
|
|
18
|
-
function findDeferredDecorator(target, prop) {
|
|
19
|
-
var _a;
|
|
20
|
-
let cursor = target.prototype;
|
|
21
|
-
while (cursor) {
|
|
22
|
-
let desc = (_a = deferred.get(cursor)) == null ? void 0 : _a.get(prop);
|
|
23
|
-
if (desc) {
|
|
24
|
-
return desc;
|
|
25
|
-
}
|
|
26
|
-
cursor = cursor.prototype;
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
function decorateFieldV2(prototype, prop, decorators, initializer) {
|
|
30
|
-
let desc = {
|
|
31
|
-
configurable: true,
|
|
32
|
-
enumerable: true,
|
|
33
|
-
writable: true,
|
|
34
|
-
initializer: null
|
|
35
|
-
};
|
|
36
|
-
if (initializer) {
|
|
37
|
-
desc.initializer = initializer;
|
|
38
|
-
}
|
|
39
|
-
for (let decorator of decorators) {
|
|
40
|
-
desc = decorator(prototype, prop, desc) || desc;
|
|
41
|
-
}
|
|
42
|
-
if (desc.initializer === void 0) {
|
|
43
|
-
Object.defineProperty(prototype, prop, desc);
|
|
44
|
-
} else {
|
|
45
|
-
deferDecorator(prototype, prop, desc);
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
function decorateMethodV2(prototype, prop, decorators) {
|
|
49
|
-
const origDesc = Object.getOwnPropertyDescriptor(prototype, prop);
|
|
50
|
-
let desc = {
|
|
51
|
-
...origDesc
|
|
52
|
-
};
|
|
53
|
-
for (let decorator of decorators) {
|
|
54
|
-
desc = decorator(prototype, prop, desc) || desc;
|
|
55
|
-
}
|
|
56
|
-
if (desc.initializer !== void 0) {
|
|
57
|
-
desc.value = desc.initializer ? desc.initializer.call(prototype) : void 0;
|
|
58
|
-
desc.initializer = void 0;
|
|
59
|
-
}
|
|
60
|
-
Object.defineProperty(prototype, prop, desc);
|
|
61
|
-
}
|
|
62
|
-
function initializeDeferredDecorator(target, prop) {
|
|
63
|
-
let desc = findDeferredDecorator(target.constructor, prop);
|
|
64
|
-
if (desc) {
|
|
65
|
-
Object.defineProperty(target, prop, {
|
|
66
|
-
enumerable: desc.enumerable,
|
|
67
|
-
configurable: desc.configurable,
|
|
68
|
-
writable: desc.writable,
|
|
69
|
-
value: desc.initializer ? desc.initializer.call(target) : void 0
|
|
70
|
-
});
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
/**
|
|
75
|
-
* @module @warp-drive/ember
|
|
76
|
-
*/
|
|
77
|
-
const RequestCache = new WeakMap();
|
|
78
|
-
function isAbortError(error) {
|
|
79
|
-
return error instanceof DOMException && error.name === 'AbortError';
|
|
80
|
-
}
|
|
81
|
-
async function watchStream(stream, state) {
|
|
82
|
-
const reader = stream.getReader();
|
|
83
|
-
let bytesLoaded = 0;
|
|
84
|
-
let shouldForward = state._stream !== null && state._stream.readable.locked;
|
|
85
|
-
let isForwarding = shouldForward;
|
|
86
|
-
let writer = state._stream?.writable.getWriter();
|
|
87
|
-
const buffer = [];
|
|
88
|
-
state._isPending = false;
|
|
89
|
-
state._isStarted = true;
|
|
90
|
-
state._startTime = performance.now();
|
|
91
|
-
while (true) {
|
|
92
|
-
const {
|
|
93
|
-
value,
|
|
94
|
-
done
|
|
95
|
-
} = await reader.read();
|
|
96
|
-
if (done) {
|
|
97
|
-
break;
|
|
98
|
-
}
|
|
99
|
-
bytesLoaded += value.byteLength;
|
|
100
|
-
state._bytesLoaded = bytesLoaded;
|
|
101
|
-
state._lastPacketTime = performance.now();
|
|
102
|
-
shouldForward = shouldForward || state._stream !== null && state._stream.readable.locked;
|
|
103
|
-
if (shouldForward) {
|
|
104
|
-
if (!isForwarding) {
|
|
105
|
-
isForwarding = true;
|
|
106
|
-
writer = state._stream.writable.getWriter();
|
|
107
|
-
for (const item of buffer) {
|
|
108
|
-
await writer.ready;
|
|
109
|
-
await writer.write(item);
|
|
110
|
-
}
|
|
111
|
-
buffer.length = 0;
|
|
112
|
-
}
|
|
113
|
-
await writer.ready;
|
|
114
|
-
await writer.write(value);
|
|
115
|
-
} else {
|
|
116
|
-
buffer.push(value);
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
// if we are still forwarding, we need to close the writer
|
|
121
|
-
if (isForwarding) {
|
|
122
|
-
await writer.ready;
|
|
123
|
-
await writer.close();
|
|
124
|
-
} else if (state._stream) {
|
|
125
|
-
// if we are not forwarding, we need to cancel the stream
|
|
126
|
-
await state._stream.readable.cancel('The Stream Has Already Ended');
|
|
127
|
-
state._stream = null;
|
|
128
|
-
}
|
|
129
|
-
const endTime = performance.now();
|
|
130
|
-
state._endTime = endTime;
|
|
131
|
-
state._isComplete = true;
|
|
132
|
-
state._isStarted = false;
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
/**
|
|
136
|
-
* Lazily consumes the stream of a request, providing a number of
|
|
137
|
-
* reactive properties that can be used to build UIs that respond
|
|
138
|
-
* to the progress of a request.
|
|
139
|
-
*
|
|
140
|
-
* @class RequestLoadingState
|
|
141
|
-
* @public
|
|
142
|
-
*/
|
|
143
|
-
class RequestLoadingState {
|
|
144
|
-
_stream = null;
|
|
145
|
-
_future;
|
|
146
|
-
_triggered = false;
|
|
147
|
-
_trigger() {
|
|
148
|
-
if (this._triggered) {
|
|
149
|
-
return;
|
|
150
|
-
}
|
|
151
|
-
this._triggered = true;
|
|
152
|
-
const future = this._future;
|
|
153
|
-
const promise = future.getStream();
|
|
154
|
-
if (promise.sizeHint) {
|
|
155
|
-
this._sizeHint = promise.sizeHint;
|
|
156
|
-
}
|
|
157
|
-
this.promise = promise.then(stream => {
|
|
158
|
-
if (!stream) {
|
|
159
|
-
this._isPending = false;
|
|
160
|
-
this._isComplete = true;
|
|
161
|
-
return;
|
|
162
|
-
}
|
|
163
|
-
return watchStream(stream, this);
|
|
164
|
-
}, error => {
|
|
165
|
-
this._isPending = false;
|
|
166
|
-
this._isStarted = false;
|
|
167
|
-
if (isAbortError(error)) {
|
|
168
|
-
this._isCancelled = true;
|
|
169
|
-
this._isComplete = true;
|
|
170
|
-
}
|
|
171
|
-
this._isErrored = true;
|
|
172
|
-
this._error = error;
|
|
173
|
-
});
|
|
174
|
-
}
|
|
175
|
-
promise = null;
|
|
176
|
-
static {
|
|
177
|
-
decorateFieldV2(this.prototype, "_sizeHint", [tracked], function () {
|
|
178
|
-
return 0;
|
|
179
|
-
});
|
|
180
|
-
}
|
|
181
|
-
#_sizeHint = (initializeDeferredDecorator(this, "_sizeHint"), void 0);
|
|
182
|
-
static {
|
|
183
|
-
decorateFieldV2(this.prototype, "_bytesLoaded", [tracked], function () {
|
|
184
|
-
return 0;
|
|
185
|
-
});
|
|
186
|
-
}
|
|
187
|
-
#_bytesLoaded = (initializeDeferredDecorator(this, "_bytesLoaded"), void 0);
|
|
188
|
-
static {
|
|
189
|
-
decorateFieldV2(this.prototype, "_startTime", [tracked], function () {
|
|
190
|
-
return 0;
|
|
191
|
-
});
|
|
192
|
-
}
|
|
193
|
-
#_startTime = (initializeDeferredDecorator(this, "_startTime"), void 0);
|
|
194
|
-
static {
|
|
195
|
-
decorateFieldV2(this.prototype, "_endTime", [tracked], function () {
|
|
196
|
-
return 0;
|
|
197
|
-
});
|
|
198
|
-
}
|
|
199
|
-
#_endTime = (initializeDeferredDecorator(this, "_endTime"), void 0);
|
|
200
|
-
static {
|
|
201
|
-
decorateFieldV2(this.prototype, "_lastPacketTime", [tracked], function () {
|
|
202
|
-
return 0;
|
|
203
|
-
});
|
|
204
|
-
}
|
|
205
|
-
#_lastPacketTime = (initializeDeferredDecorator(this, "_lastPacketTime"), void 0);
|
|
206
|
-
static {
|
|
207
|
-
decorateFieldV2(this.prototype, "_isPending", [tracked], function () {
|
|
208
|
-
return true;
|
|
209
|
-
});
|
|
210
|
-
}
|
|
211
|
-
#_isPending = (initializeDeferredDecorator(this, "_isPending"), void 0);
|
|
212
|
-
static {
|
|
213
|
-
decorateFieldV2(this.prototype, "_isStarted", [tracked], function () {
|
|
214
|
-
return false;
|
|
215
|
-
});
|
|
216
|
-
}
|
|
217
|
-
#_isStarted = (initializeDeferredDecorator(this, "_isStarted"), void 0);
|
|
218
|
-
static {
|
|
219
|
-
decorateFieldV2(this.prototype, "_isComplete", [tracked], function () {
|
|
220
|
-
return false;
|
|
221
|
-
});
|
|
222
|
-
}
|
|
223
|
-
#_isComplete = (initializeDeferredDecorator(this, "_isComplete"), void 0);
|
|
224
|
-
static {
|
|
225
|
-
decorateFieldV2(this.prototype, "_isCancelled", [tracked], function () {
|
|
226
|
-
return false;
|
|
227
|
-
});
|
|
228
|
-
}
|
|
229
|
-
#_isCancelled = (initializeDeferredDecorator(this, "_isCancelled"), void 0);
|
|
230
|
-
static {
|
|
231
|
-
decorateFieldV2(this.prototype, "_isErrored", [tracked], function () {
|
|
232
|
-
return false;
|
|
233
|
-
});
|
|
234
|
-
}
|
|
235
|
-
#_isErrored = (initializeDeferredDecorator(this, "_isErrored"), void 0);
|
|
236
|
-
static {
|
|
237
|
-
decorateFieldV2(this.prototype, "_error", [tracked], function () {
|
|
238
|
-
return null;
|
|
239
|
-
});
|
|
240
|
-
}
|
|
241
|
-
#_error = (initializeDeferredDecorator(this, "_error"), void 0);
|
|
242
|
-
get isPending() {
|
|
243
|
-
this._trigger();
|
|
244
|
-
return this._isPending;
|
|
245
|
-
}
|
|
246
|
-
get sizeHint() {
|
|
247
|
-
this._trigger();
|
|
248
|
-
return this._sizeHint;
|
|
249
|
-
}
|
|
250
|
-
get stream() {
|
|
251
|
-
this._trigger();
|
|
252
|
-
if (!this._stream) {
|
|
253
|
-
if (this._isComplete || this._isCancelled || this._isErrored) {
|
|
254
|
-
return null;
|
|
255
|
-
}
|
|
256
|
-
this._stream = new TransformStream();
|
|
257
|
-
}
|
|
258
|
-
return this._stream.readable;
|
|
259
|
-
}
|
|
260
|
-
get isStarted() {
|
|
261
|
-
this._trigger();
|
|
262
|
-
return this._isStarted;
|
|
263
|
-
}
|
|
264
|
-
get bytesLoaded() {
|
|
265
|
-
this._trigger();
|
|
266
|
-
return this._bytesLoaded;
|
|
267
|
-
}
|
|
268
|
-
get startTime() {
|
|
269
|
-
this._trigger();
|
|
270
|
-
return this._startTime;
|
|
271
|
-
}
|
|
272
|
-
get endTime() {
|
|
273
|
-
this._trigger();
|
|
274
|
-
return this._endTime;
|
|
275
|
-
}
|
|
276
|
-
get lastPacketTime() {
|
|
277
|
-
this._trigger();
|
|
278
|
-
return this._lastPacketTime;
|
|
279
|
-
}
|
|
280
|
-
get isComplete() {
|
|
281
|
-
this._trigger();
|
|
282
|
-
return this._isComplete;
|
|
283
|
-
}
|
|
284
|
-
get isCancelled() {
|
|
285
|
-
this._trigger();
|
|
286
|
-
return this._isCancelled;
|
|
287
|
-
}
|
|
288
|
-
get isErrored() {
|
|
289
|
-
this._trigger();
|
|
290
|
-
return this._isErrored;
|
|
291
|
-
}
|
|
292
|
-
get error() {
|
|
293
|
-
this._trigger();
|
|
294
|
-
return this._error;
|
|
295
|
-
}
|
|
296
|
-
get elapsedTime() {
|
|
297
|
-
return (this.endTime || this.lastPacketTime) - this.startTime;
|
|
298
|
-
}
|
|
299
|
-
get completedRatio() {
|
|
300
|
-
return this.sizeHint ? this.bytesLoaded / this.sizeHint : 0;
|
|
301
|
-
}
|
|
302
|
-
get remainingRatio() {
|
|
303
|
-
return 1 - this.completedRatio;
|
|
304
|
-
}
|
|
305
|
-
get duration() {
|
|
306
|
-
return this.endTime - this.startTime;
|
|
307
|
-
}
|
|
308
|
-
get speed() {
|
|
309
|
-
// bytes per second
|
|
310
|
-
return this.bytesLoaded / (this.elapsedTime / 1000);
|
|
311
|
-
}
|
|
312
|
-
constructor(future) {
|
|
313
|
-
this._future = future;
|
|
314
|
-
}
|
|
315
|
-
abort = () => {
|
|
316
|
-
this._future.abort();
|
|
317
|
-
};
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
/**
|
|
321
|
-
* RequestState extends the concept of PromiseState to provide a reactive
|
|
322
|
-
* wrapper for a request `Future` which allows you write declarative code
|
|
323
|
-
* around a Future's control flow.
|
|
324
|
-
*
|
|
325
|
-
* It is useful in both Template and JavaScript contexts, allowing you
|
|
326
|
-
* to quickly derive behaviors and data from pending, error and success
|
|
327
|
-
* states.
|
|
328
|
-
*
|
|
329
|
-
* The key difference between a Promise and a Future is that Futures provide
|
|
330
|
-
* access to a stream of their content, the identity of the request (if any)
|
|
331
|
-
* as well as the ability to attempt to abort the request.
|
|
332
|
-
*
|
|
333
|
-
* ```ts
|
|
334
|
-
* interface Future<T> extends Promise<T>> {
|
|
335
|
-
* getStream(): Promise<ReadableStream>;
|
|
336
|
-
* abort(): void;
|
|
337
|
-
* lid: StableDocumentIdentifier | null;
|
|
338
|
-
* }
|
|
339
|
-
* ```
|
|
340
|
-
*
|
|
341
|
-
* These additional APIs allow us to craft even richer state experiences.
|
|
342
|
-
*
|
|
343
|
-
* To get the state of a request, use `getRequestState`.
|
|
344
|
-
*
|
|
345
|
-
* @class RequestState
|
|
346
|
-
* @public
|
|
347
|
-
*/
|
|
348
|
-
class RequestState {
|
|
349
|
-
#request;
|
|
350
|
-
#loadingState = null;
|
|
351
|
-
static {
|
|
352
|
-
decorateFieldV2(this.prototype, "result", [tracked], function () {
|
|
353
|
-
return null;
|
|
354
|
-
});
|
|
355
|
-
}
|
|
356
|
-
#result = (initializeDeferredDecorator(this, "result"), void 0);
|
|
357
|
-
static {
|
|
358
|
-
decorateFieldV2(this.prototype, "error", [tracked], function () {
|
|
359
|
-
return null;
|
|
360
|
-
});
|
|
361
|
-
}
|
|
362
|
-
#error = (initializeDeferredDecorator(this, "error"), void 0);
|
|
363
|
-
static {
|
|
364
|
-
decorateFieldV2(this.prototype, "isLoading", [tracked], function () {
|
|
365
|
-
return true;
|
|
366
|
-
});
|
|
367
|
-
}
|
|
368
|
-
#isLoading = (initializeDeferredDecorator(this, "isLoading"), void 0);
|
|
369
|
-
static {
|
|
370
|
-
decorateFieldV2(this.prototype, "isSuccess", [tracked], function () {
|
|
371
|
-
return false;
|
|
372
|
-
});
|
|
373
|
-
}
|
|
374
|
-
#isSuccess = (initializeDeferredDecorator(this, "isSuccess"), void 0);
|
|
375
|
-
static {
|
|
376
|
-
decorateFieldV2(this.prototype, "isError", [tracked], function () {
|
|
377
|
-
return false;
|
|
378
|
-
});
|
|
379
|
-
}
|
|
380
|
-
#isError = (initializeDeferredDecorator(this, "isError"), void 0);
|
|
381
|
-
static {
|
|
382
|
-
decorateFieldV2(this.prototype, "request", [tracked], function () {
|
|
383
|
-
return null;
|
|
384
|
-
});
|
|
385
|
-
}
|
|
386
|
-
#request_ = (initializeDeferredDecorator(this, "request"), void 0);
|
|
387
|
-
static {
|
|
388
|
-
decorateFieldV2(this.prototype, "response", [tracked], function () {
|
|
389
|
-
return null;
|
|
390
|
-
});
|
|
391
|
-
}
|
|
392
|
-
#response = (initializeDeferredDecorator(this, "response"), void 0);
|
|
393
|
-
get isCancelled() {
|
|
394
|
-
return this.isError && isAbortError(this.error);
|
|
395
|
-
}
|
|
396
|
-
get loadingState() {
|
|
397
|
-
if (!this.#loadingState) {
|
|
398
|
-
this.#loadingState = new RequestLoadingState(this.#request);
|
|
399
|
-
}
|
|
400
|
-
return this.#loadingState;
|
|
401
|
-
}
|
|
402
|
-
constructor(future) {
|
|
403
|
-
this.#request = future;
|
|
404
|
-
const state = getPromiseResult(future);
|
|
405
|
-
if (state) {
|
|
406
|
-
this.request = state.result.request;
|
|
407
|
-
this.response = state.result.response;
|
|
408
|
-
this.isLoading = false;
|
|
409
|
-
if (state.isError) {
|
|
410
|
-
this.error = state.result;
|
|
411
|
-
this.isError = true;
|
|
412
|
-
} else {
|
|
413
|
-
this.result = state.result.content;
|
|
414
|
-
this.isSuccess = true;
|
|
415
|
-
}
|
|
416
|
-
} else {
|
|
417
|
-
void future.then(result => {
|
|
418
|
-
setPromiseResult(future, {
|
|
419
|
-
isError: false,
|
|
420
|
-
result
|
|
421
|
-
});
|
|
422
|
-
this.result = result.content;
|
|
423
|
-
this.isSuccess = true;
|
|
424
|
-
this.isLoading = false;
|
|
425
|
-
this.request = result.request;
|
|
426
|
-
this.response = result.response;
|
|
427
|
-
}, error => {
|
|
428
|
-
setPromiseResult(future, {
|
|
429
|
-
isError: true,
|
|
430
|
-
result: error
|
|
431
|
-
});
|
|
432
|
-
this.error = error;
|
|
433
|
-
this.isError = true;
|
|
434
|
-
this.isLoading = false;
|
|
435
|
-
this.request = error.request;
|
|
436
|
-
this.response = error.response;
|
|
437
|
-
});
|
|
438
|
-
}
|
|
439
|
-
}
|
|
440
|
-
}
|
|
441
|
-
|
|
442
|
-
/**
|
|
443
|
-
*
|
|
444
|
-
*
|
|
445
|
-
* `getRequestState` can be used in both JavaScript and Template contexts.
|
|
446
|
-
*
|
|
447
|
-
* ```ts
|
|
448
|
-
* import { getRequestState } from '@warp-drive/ember';
|
|
449
|
-
*
|
|
450
|
-
* const state = getRequestState(future);
|
|
451
|
-
* ```
|
|
452
|
-
*
|
|
453
|
-
* For instance, we could write a getter on a component that updates whenever
|
|
454
|
-
* the request state advances or the future changes, by combining the function
|
|
455
|
-
* with the use of `@cached`
|
|
456
|
-
*
|
|
457
|
-
* ```ts
|
|
458
|
-
* class Component {
|
|
459
|
-
* @cached
|
|
460
|
-
* get title() {
|
|
461
|
-
* const state = getRequestState(this.args.request);
|
|
462
|
-
* if (state.isPending) {
|
|
463
|
-
* return 'loading...';
|
|
464
|
-
* }
|
|
465
|
-
* if (state.isError) { return null; }
|
|
466
|
-
* return state.result.title;
|
|
467
|
-
* }
|
|
468
|
-
* }
|
|
469
|
-
* ```
|
|
470
|
-
*
|
|
471
|
-
* Or in a template as a helper:
|
|
472
|
-
*
|
|
473
|
-
* ```gjs
|
|
474
|
-
* import { getRequestState } from '@warp-drive/ember';
|
|
475
|
-
*
|
|
476
|
-
* <template>
|
|
477
|
-
* {{#let (getRequestState @request) as |state|}}
|
|
478
|
-
* {{#if state.isPending}}
|
|
479
|
-
* <Spinner />
|
|
480
|
-
* {{else if state.isError}}
|
|
481
|
-
* <ErrorForm @error={{state.error}} />
|
|
482
|
-
* {{else}}
|
|
483
|
-
* <h1>{{state.result.title}}</h1>
|
|
484
|
-
* {{/if}}
|
|
485
|
-
* {{/let}}
|
|
486
|
-
* </template>
|
|
487
|
-
* ```
|
|
488
|
-
*
|
|
489
|
-
* If looking to use in a template, consider also the `<Request />` component
|
|
490
|
-
* which offers a numbe of additional capabilities for requests *beyond* what
|
|
491
|
-
* `RequestState` provides.
|
|
492
|
-
*
|
|
493
|
-
* @method getRequestState
|
|
494
|
-
* @for @warp-drive/ember
|
|
495
|
-
* @static
|
|
496
|
-
* @public
|
|
497
|
-
* @param future
|
|
498
|
-
* @return {RequestState}
|
|
499
|
-
*/
|
|
500
|
-
function getRequestState(future) {
|
|
501
|
-
let state = RequestCache.get(future);
|
|
502
|
-
if (!state) {
|
|
503
|
-
state = new RequestState(future);
|
|
504
|
-
RequestCache.set(future, state);
|
|
505
|
-
}
|
|
506
|
-
return state;
|
|
507
|
-
}
|
|
508
|
-
|
|
509
|
-
/**
|
|
510
|
-
* @module @warp-drive/ember
|
|
511
|
-
*/
|
|
512
|
-
const PromiseCache = new WeakMap();
|
|
513
|
-
|
|
514
|
-
/**
|
|
515
|
-
* PromiseState provides a reactive wrapper for a promise which allows you write declarative
|
|
516
|
-
* code around a promise's control flow. It is useful in both Template and JavaScript contexts,
|
|
517
|
-
* allowing you to quickly derive behaviors and data from pending, error and success states.
|
|
518
|
-
*
|
|
519
|
-
* ```ts
|
|
520
|
-
* interface PromiseState<T = unknown, E = unknown> {
|
|
521
|
-
* isPending: boolean;
|
|
522
|
-
* isSuccess: boolean;
|
|
523
|
-
* isError: boolean;
|
|
524
|
-
* result: T | null;
|
|
525
|
-
* error: E | null;
|
|
526
|
-
* }
|
|
527
|
-
* ```
|
|
528
|
-
*
|
|
529
|
-
* To get the state of a promise, use `getPromiseState`.
|
|
530
|
-
*
|
|
531
|
-
* @class PromiseState
|
|
532
|
-
* @public
|
|
533
|
-
*/
|
|
534
|
-
class PromiseState {
|
|
535
|
-
static {
|
|
536
|
-
decorateFieldV2(this.prototype, "result", [tracked], function () {
|
|
537
|
-
return null;
|
|
538
|
-
});
|
|
539
|
-
}
|
|
540
|
-
#result = (initializeDeferredDecorator(this, "result"), void 0);
|
|
541
|
-
static {
|
|
542
|
-
decorateFieldV2(this.prototype, "error", [tracked], function () {
|
|
543
|
-
return null;
|
|
544
|
-
});
|
|
545
|
-
}
|
|
546
|
-
#error = (initializeDeferredDecorator(this, "error"), void 0);
|
|
547
|
-
static {
|
|
548
|
-
decorateFieldV2(this.prototype, "isPending", [tracked], function () {
|
|
549
|
-
return true;
|
|
550
|
-
});
|
|
551
|
-
}
|
|
552
|
-
#isPending = (initializeDeferredDecorator(this, "isPending"), void 0);
|
|
553
|
-
static {
|
|
554
|
-
decorateFieldV2(this.prototype, "isSuccess", [tracked], function () {
|
|
555
|
-
return false;
|
|
556
|
-
});
|
|
557
|
-
}
|
|
558
|
-
#isSuccess = (initializeDeferredDecorator(this, "isSuccess"), void 0);
|
|
559
|
-
static {
|
|
560
|
-
decorateFieldV2(this.prototype, "isError", [tracked], function () {
|
|
561
|
-
return false;
|
|
562
|
-
});
|
|
563
|
-
}
|
|
564
|
-
#isError = (initializeDeferredDecorator(this, "isError"), void 0);
|
|
565
|
-
constructor(promise) {
|
|
566
|
-
const state = getPromiseResult(promise);
|
|
567
|
-
if (state) {
|
|
568
|
-
if (state.isError) {
|
|
569
|
-
this.error = state.result;
|
|
570
|
-
this.isError = true;
|
|
571
|
-
this.isPending = false;
|
|
572
|
-
} else {
|
|
573
|
-
this.result = state.result;
|
|
574
|
-
this.isSuccess = true;
|
|
575
|
-
this.isPending = false;
|
|
576
|
-
}
|
|
577
|
-
} else {
|
|
578
|
-
void promise.then(result => {
|
|
579
|
-
setPromiseResult(promise, {
|
|
580
|
-
isError: false,
|
|
581
|
-
result
|
|
582
|
-
});
|
|
583
|
-
this.result = result;
|
|
584
|
-
this.isSuccess = true;
|
|
585
|
-
this.isPending = false;
|
|
586
|
-
}, error => {
|
|
587
|
-
setPromiseResult(promise, {
|
|
588
|
-
isError: true,
|
|
589
|
-
result: error
|
|
590
|
-
});
|
|
591
|
-
this.error = error;
|
|
592
|
-
this.isError = true;
|
|
593
|
-
this.isPending = false;
|
|
594
|
-
});
|
|
595
|
-
}
|
|
596
|
-
}
|
|
597
|
-
}
|
|
598
|
-
const LegacyPromiseProxy = Symbol.for('LegacyPromiseProxy');
|
|
599
|
-
function isLegacyAwaitable(promise) {
|
|
600
|
-
return LegacyPromiseProxy in promise && 'promise' in promise && promise[LegacyPromiseProxy] === true;
|
|
601
|
-
}
|
|
602
|
-
function getPromise(promise) {
|
|
603
|
-
return isLegacyAwaitable(promise) ? promise.promise : promise;
|
|
604
|
-
}
|
|
605
|
-
|
|
606
|
-
/**
|
|
607
|
-
* Returns a reactive state-machine for the provided promise or awaitable.
|
|
608
|
-
*
|
|
609
|
-
* Repeat calls to `getPromiseState` with the same promise will return the same state object
|
|
610
|
-
* making is safe and easy to use in templates and JavaScript code to produce reactive
|
|
611
|
-
* behaviors around promises.
|
|
612
|
-
*
|
|
613
|
-
* `getPromiseState` can be used in both JavaScript and Template contexts.
|
|
614
|
-
*
|
|
615
|
-
* ```ts
|
|
616
|
-
* import { getPromiseState } from '@warp-drive/ember';
|
|
617
|
-
*
|
|
618
|
-
* const state = getPromiseState(promise);
|
|
619
|
-
* ```
|
|
620
|
-
*
|
|
621
|
-
* For instance, we could write a getter on a component that updates whenever
|
|
622
|
-
* the promise state advances or the promise changes, by combining the function
|
|
623
|
-
* with the use of `@cached`
|
|
624
|
-
*
|
|
625
|
-
* ```ts
|
|
626
|
-
* class Component {
|
|
627
|
-
* @cached
|
|
628
|
-
* get title() {
|
|
629
|
-
* const state = getPromiseState(this.args.request);
|
|
630
|
-
* if (state.isPending) {
|
|
631
|
-
* return 'loading...';
|
|
632
|
-
* }
|
|
633
|
-
* if (state.isError) { return null; }
|
|
634
|
-
* return state.result.title;
|
|
635
|
-
* }
|
|
636
|
-
* }
|
|
637
|
-
* ```
|
|
638
|
-
*
|
|
639
|
-
* Or in a template as a helper:
|
|
640
|
-
*
|
|
641
|
-
* ```gjs
|
|
642
|
-
* import { getPromiseState } from '@warp-drive/ember';
|
|
643
|
-
*
|
|
644
|
-
* <template>
|
|
645
|
-
* {{#let (getPromiseState @request) as |state|}}
|
|
646
|
-
* {{#if state.isPending}} <Spinner />
|
|
647
|
-
* {{else if state.isError}} <ErrorForm @error={{state.error}} />
|
|
648
|
-
* {{else}}
|
|
649
|
-
* <h1>{{state.result.title}}</h1>
|
|
650
|
-
* {{/if}}
|
|
651
|
-
* {{/let}}
|
|
652
|
-
* </template>
|
|
653
|
-
* ```
|
|
654
|
-
*
|
|
655
|
-
* If looking to use in a template, consider also the `<Await />` component.
|
|
656
|
-
*
|
|
657
|
-
* @method getPromiseState
|
|
658
|
-
* @for @warp-drive/ember
|
|
659
|
-
* @static
|
|
660
|
-
* @public
|
|
661
|
-
* @param {Promise<T> | Awaitable<T, E>} promise
|
|
662
|
-
* @return {PromiseState<T, E>}
|
|
663
|
-
*/
|
|
664
|
-
function getPromiseState(promise) {
|
|
665
|
-
const _promise = getPromise(promise);
|
|
666
|
-
let state = PromiseCache.get(_promise);
|
|
667
|
-
if (!state) {
|
|
668
|
-
state = new PromiseState(_promise);
|
|
669
|
-
PromiseCache.set(_promise, state);
|
|
670
|
-
}
|
|
671
|
-
return state;
|
|
672
|
-
}
|
|
673
10
|
|
|
674
11
|
/**
|
|
675
12
|
* @module @warp-drive/ember
|
|
@@ -758,6 +95,70 @@ class Await extends Component {
|
|
|
758
95
|
}), this);
|
|
759
96
|
}
|
|
760
97
|
}
|
|
98
|
+
const deferred = /* @__PURE__ */new WeakMap();
|
|
99
|
+
function deferDecorator(proto, prop, desc) {
|
|
100
|
+
let map = deferred.get(proto);
|
|
101
|
+
if (!map) {
|
|
102
|
+
map = /* @__PURE__ */new Map();
|
|
103
|
+
deferred.set(proto, map);
|
|
104
|
+
}
|
|
105
|
+
map.set(prop, desc);
|
|
106
|
+
}
|
|
107
|
+
function findDeferredDecorator(target, prop) {
|
|
108
|
+
var _a;
|
|
109
|
+
let cursor = target.prototype;
|
|
110
|
+
while (cursor) {
|
|
111
|
+
let desc = (_a = deferred.get(cursor)) == null ? void 0 : _a.get(prop);
|
|
112
|
+
if (desc) {
|
|
113
|
+
return desc;
|
|
114
|
+
}
|
|
115
|
+
cursor = cursor.prototype;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
function decorateFieldV2(prototype, prop, decorators, initializer) {
|
|
119
|
+
let desc = {
|
|
120
|
+
configurable: true,
|
|
121
|
+
enumerable: true,
|
|
122
|
+
writable: true,
|
|
123
|
+
initializer: null
|
|
124
|
+
};
|
|
125
|
+
if (initializer) {
|
|
126
|
+
desc.initializer = initializer;
|
|
127
|
+
}
|
|
128
|
+
for (let decorator of decorators) {
|
|
129
|
+
desc = decorator(prototype, prop, desc) || desc;
|
|
130
|
+
}
|
|
131
|
+
if (desc.initializer === void 0) {
|
|
132
|
+
Object.defineProperty(prototype, prop, desc);
|
|
133
|
+
} else {
|
|
134
|
+
deferDecorator(prototype, prop, desc);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
function decorateMethodV2(prototype, prop, decorators) {
|
|
138
|
+
const origDesc = Object.getOwnPropertyDescriptor(prototype, prop);
|
|
139
|
+
let desc = {
|
|
140
|
+
...origDesc
|
|
141
|
+
};
|
|
142
|
+
for (let decorator of decorators) {
|
|
143
|
+
desc = decorator(prototype, prop, desc) || desc;
|
|
144
|
+
}
|
|
145
|
+
if (desc.initializer !== void 0) {
|
|
146
|
+
desc.value = desc.initializer ? desc.initializer.call(prototype) : void 0;
|
|
147
|
+
desc.initializer = void 0;
|
|
148
|
+
}
|
|
149
|
+
Object.defineProperty(prototype, prop, desc);
|
|
150
|
+
}
|
|
151
|
+
function initializeDeferredDecorator(target, prop) {
|
|
152
|
+
let desc = findDeferredDecorator(target.constructor, prop);
|
|
153
|
+
if (desc) {
|
|
154
|
+
Object.defineProperty(target, prop, {
|
|
155
|
+
enumerable: desc.enumerable,
|
|
156
|
+
configurable: desc.configurable,
|
|
157
|
+
writable: desc.writable,
|
|
158
|
+
value: desc.initializer ? desc.initializer.call(target) : void 0
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
}
|
|
761
162
|
|
|
762
163
|
/**
|
|
763
164
|
* @module @warp-drive/ember
|
|
@@ -1560,4 +961,4 @@ class Request extends Component {
|
|
|
1560
961
|
}), this);
|
|
1561
962
|
}
|
|
1562
963
|
}
|
|
1563
|
-
export { Await, Request, Throw
|
|
964
|
+
export { Await, Request, Throw };
|