@ember-data-mirror/store 5.4.0-beta.9 → 5.4.1-beta.0
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 +5 -8
- package/dist/-private.js +1 -1
- package/dist/index.js +1 -1
- package/dist/{cache-handler-C5ilAUZ5.js → many-array-BwVo-2vv.js} +1828 -999
- package/dist/many-array-BwVo-2vv.js.map +1 -0
- 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 +40 -48
- package/unstable-preview-types/-private/cache-handler/handler.d.ts +62 -0
- package/unstable-preview-types/-private/cache-handler/handler.d.ts.map +1 -0
- package/unstable-preview-types/-private/{cache-handler.d.ts → cache-handler/types.d.ts} +25 -62
- package/unstable-preview-types/-private/cache-handler/types.d.ts.map +1 -0
- package/unstable-preview-types/-private/cache-handler/utils.d.ts +34 -0
- package/unstable-preview-types/-private/cache-handler/utils.d.ts.map +1 -0
- package/unstable-preview-types/-private/caches/identifier-cache.d.ts +7 -3
- package/unstable-preview-types/-private/caches/identifier-cache.d.ts.map +1 -1
- package/unstable-preview-types/-private/caches/instance-cache.d.ts +4 -1
- package/unstable-preview-types/-private/caches/instance-cache.d.ts.map +1 -1
- package/unstable-preview-types/-private/debug/utils.d.ts +9 -0
- package/unstable-preview-types/-private/debug/utils.d.ts.map +1 -0
- package/unstable-preview-types/-private/document.d.ts +23 -14
- package/unstable-preview-types/-private/document.d.ts.map +1 -1
- package/unstable-preview-types/-private/legacy-model-support/record-reference.d.ts.map +1 -1
- package/unstable-preview-types/-private/legacy-model-support/shim-model-class.d.ts +3 -3
- package/unstable-preview-types/-private/legacy-model-support/shim-model-class.d.ts.map +1 -1
- package/unstable-preview-types/-private/managers/cache-capabilities-manager.d.ts +3 -3
- package/unstable-preview-types/-private/managers/cache-capabilities-manager.d.ts.map +1 -1
- package/unstable-preview-types/-private/managers/cache-manager.d.ts +23 -1
- package/unstable-preview-types/-private/managers/cache-manager.d.ts.map +1 -1
- package/unstable-preview-types/-private/managers/notification-manager.d.ts +11 -6
- package/unstable-preview-types/-private/managers/notification-manager.d.ts.map +1 -1
- package/unstable-preview-types/-private/managers/record-array-manager.d.ts +9 -5
- package/unstable-preview-types/-private/managers/record-array-manager.d.ts.map +1 -1
- package/unstable-preview-types/-private/record-arrays/identifier-array.d.ts +14 -2
- package/unstable-preview-types/-private/record-arrays/identifier-array.d.ts.map +1 -1
- package/unstable-preview-types/-private/record-arrays/many-array.d.ts +199 -0
- package/unstable-preview-types/-private/record-arrays/many-array.d.ts.map +1 -0
- package/unstable-preview-types/-private/record-arrays/native-proxy-type-fix.d.ts +3 -3
- package/unstable-preview-types/-private/store-service.d.ts +92 -19
- package/unstable-preview-types/-private/store-service.d.ts.map +1 -1
- package/unstable-preview-types/-private.d.ts +7 -4
- package/unstable-preview-types/-private.d.ts.map +1 -1
- package/unstable-preview-types/-types/q/cache-capabilities-manager.d.ts +4 -4
- package/unstable-preview-types/-types/q/cache-capabilities-manager.d.ts.map +1 -1
- package/unstable-preview-types/-types/q/ds-model.d.ts +3 -3
- package/unstable-preview-types/-types/q/ds-model.d.ts.map +1 -1
- package/unstable-preview-types/-types/q/schema-service.d.ts +16 -8
- package/unstable-preview-types/-types/q/schema-service.d.ts.map +1 -1
- package/unstable-preview-types/index.d.ts +24 -23
- package/unstable-preview-types/index.d.ts.map +1 -1
- package/dist/cache-handler-C5ilAUZ5.js.map +0 -1
- package/unstable-preview-types/-private/cache-handler.d.ts.map +0 -1
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { deprecate, warn } from '@ember/debug';
|
|
2
2
|
import { macroCondition, getGlobalConfig, dependencySatisfies, importSync } from '@embroider/macros';
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import { dasherize } from '@ember-data-mirror/request-utils/string';
|
|
7
|
-
import { defineSignal, createSignal, subscribe, createArrayTags, addToTransaction, addTransactionCB } from '@ember-data-mirror/tracking/-private';
|
|
3
|
+
import { EnableHydration, SkipCache } from '@warp-drive-mirror/core-types/request';
|
|
4
|
+
import { setLogging, getRuntimeConfig } from '@warp-drive-mirror/core-types/runtime';
|
|
5
|
+
import { getOrSetGlobal, peekTransient, setTransient } from '@warp-drive-mirror/core-types/-private';
|
|
8
6
|
import { _backburner } from '@ember/runloop';
|
|
7
|
+
import { defineSubscription, notifySignal, defineSignal, createSignal, subscribe, createArrayTags, addToTransaction, addTransactionCB } from '@ember-data-mirror/tracking/-private';
|
|
8
|
+
import { CACHE_OWNER, DEBUG_STALE_CACHE_OWNER, DEBUG_IDENTIFIER_BUCKET, DEBUG_CLIENT_ORIGINATED } from '@warp-drive-mirror/core-types/identifier';
|
|
9
|
+
import { dasherize } from '@ember-data-mirror/request-utils/string';
|
|
9
10
|
import { compat } from '@ember-data-mirror/tracking';
|
|
10
11
|
|
|
11
12
|
/**
|
|
@@ -13,7 +14,7 @@ import { compat } from '@ember-data-mirror/tracking';
|
|
|
13
14
|
*/
|
|
14
15
|
|
|
15
16
|
function coerceId(id) {
|
|
16
|
-
if (macroCondition(getGlobalConfig().
|
|
17
|
+
if (macroCondition(getGlobalConfig().WarpDriveMirror.deprecations.DEPRECATE_NON_STRICT_ID)) {
|
|
17
18
|
let normalized;
|
|
18
19
|
if (id === null || id === undefined || id === '') {
|
|
19
20
|
normalized = null;
|
|
@@ -25,13 +26,13 @@ function coerceId(id) {
|
|
|
25
26
|
until: '6.0',
|
|
26
27
|
for: 'ember-data-mirror',
|
|
27
28
|
since: {
|
|
28
|
-
available: '
|
|
29
|
+
available: '4.13',
|
|
29
30
|
enabled: '5.3'
|
|
30
31
|
}
|
|
31
32
|
});
|
|
32
33
|
return normalized;
|
|
33
34
|
}
|
|
34
|
-
macroCondition(getGlobalConfig().
|
|
35
|
+
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
|
|
35
36
|
if (!test) {
|
|
36
37
|
throw new Error(`Resource IDs must be a non-empty string or null. Received '${String(id)}'.`);
|
|
37
38
|
}
|
|
@@ -45,7 +46,7 @@ function ensureStringId(id) {
|
|
|
45
46
|
} else if (typeof id === 'number' && !isNaN(id)) {
|
|
46
47
|
normalized = String(id);
|
|
47
48
|
}
|
|
48
|
-
macroCondition(getGlobalConfig().
|
|
49
|
+
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
|
|
49
50
|
if (!test) {
|
|
50
51
|
throw new Error(`Expected id to be a string or number, received ${String(id)}`);
|
|
51
52
|
}
|
|
@@ -53,14 +54,14 @@ function ensureStringId(id) {
|
|
|
53
54
|
return normalized;
|
|
54
55
|
}
|
|
55
56
|
function normalizeModelName(type) {
|
|
56
|
-
if (macroCondition(getGlobalConfig().
|
|
57
|
+
if (macroCondition(getGlobalConfig().WarpDriveMirror.deprecations.DEPRECATE_NON_STRICT_TYPES)) {
|
|
57
58
|
const result = dasherize(type);
|
|
58
59
|
deprecate(`The resource type '${type}' is not normalized. Update your application code to use '${result}' instead of '${type}'.`, result === type, {
|
|
59
60
|
id: 'ember-data-mirror:deprecate-non-strict-types',
|
|
60
61
|
until: '6.0',
|
|
61
62
|
for: 'ember-data-mirror',
|
|
62
63
|
since: {
|
|
63
|
-
available: '
|
|
64
|
+
available: '4.13',
|
|
64
65
|
enabled: '5.3'
|
|
65
66
|
}
|
|
66
67
|
});
|
|
@@ -75,7 +76,7 @@ function normalizeModelName(type) {
|
|
|
75
76
|
|
|
76
77
|
function installPolyfill() {
|
|
77
78
|
const isFastBoot = typeof FastBoot !== 'undefined';
|
|
78
|
-
const CRYPTO = isFastBoot ? FastBoot.require('crypto') :
|
|
79
|
+
const CRYPTO = isFastBoot ? FastBoot.require('crypto') : globalThis.crypto;
|
|
79
80
|
if (!CRYPTO.randomUUID) {
|
|
80
81
|
// we might be able to optimize this by requesting more bytes than we need at a time
|
|
81
82
|
const rng = function () {
|
|
@@ -129,21 +130,20 @@ function hasType(resource) {
|
|
|
129
130
|
/**
|
|
130
131
|
@module @ember-data-mirror/store
|
|
131
132
|
*/
|
|
132
|
-
const IDENTIFIERS = getOrSetGlobal('IDENTIFIERS', new Set());
|
|
133
133
|
const DOCUMENTS = getOrSetGlobal('DOCUMENTS', new Set());
|
|
134
134
|
function isStableIdentifier(identifier) {
|
|
135
|
-
return identifier[CACHE_OWNER] !== undefined
|
|
135
|
+
return identifier[CACHE_OWNER] !== undefined;
|
|
136
136
|
}
|
|
137
137
|
function isDocumentIdentifier(identifier) {
|
|
138
138
|
return DOCUMENTS.has(identifier);
|
|
139
139
|
}
|
|
140
140
|
const isFastBoot = typeof FastBoot !== 'undefined';
|
|
141
|
-
const _crypto = isFastBoot ? FastBoot.require('crypto') :
|
|
142
|
-
if (macroCondition(getGlobalConfig().
|
|
141
|
+
const _crypto = isFastBoot ? FastBoot.require('crypto') : globalThis.crypto;
|
|
142
|
+
if (macroCondition(getGlobalConfig().WarpDriveMirror.polyfillUUID)) {
|
|
143
143
|
installPolyfill();
|
|
144
144
|
}
|
|
145
145
|
function uuidv4() {
|
|
146
|
-
macroCondition(getGlobalConfig().
|
|
146
|
+
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
|
|
147
147
|
if (!test) {
|
|
148
148
|
throw new Error('crypto.randomUUID needs to be avaliable. Some browsers incorrectly disallow it in insecure contexts. You maybe want to enable the polyfill: https://github.com/emberjs/data#randomuuid-polyfill');
|
|
149
149
|
}
|
|
@@ -192,7 +192,7 @@ function updateTypeIdMapping(typeMap, identifier, id) {
|
|
|
192
192
|
}
|
|
193
193
|
function defaultUpdateMethod(identifier, data, bucket) {
|
|
194
194
|
if (bucket === 'record') {
|
|
195
|
-
macroCondition(getGlobalConfig().
|
|
195
|
+
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
|
|
196
196
|
if (!test) {
|
|
197
197
|
throw new Error(`Expected identifier to be a StableRecordIdentifier`);
|
|
198
198
|
}
|
|
@@ -206,7 +206,7 @@ function defaultKeyInfoMethod(resource, known) {
|
|
|
206
206
|
// TODO RFC something to make this configurable
|
|
207
207
|
const id = hasId(resource) ? coerceId(resource.id) : null;
|
|
208
208
|
const type = hasType(resource) ? normalizeModelName(resource.type) : known ? known.type : null;
|
|
209
|
-
macroCondition(getGlobalConfig().
|
|
209
|
+
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
|
|
210
210
|
if (!test) {
|
|
211
211
|
throw new Error(`Expected keyInfoForResource to provide a type for the resource`);
|
|
212
212
|
}
|
|
@@ -221,7 +221,7 @@ function defaultGenerationMethod(data, bucket) {
|
|
|
221
221
|
if (hasLid(data)) {
|
|
222
222
|
return data.lid;
|
|
223
223
|
}
|
|
224
|
-
macroCondition(getGlobalConfig().
|
|
224
|
+
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
|
|
225
225
|
if (!test) {
|
|
226
226
|
throw new Error(`Cannot generate an identifier for a resource without a type`);
|
|
227
227
|
}
|
|
@@ -241,7 +241,7 @@ function defaultGenerationMethod(data, bucket) {
|
|
|
241
241
|
}
|
|
242
242
|
return null;
|
|
243
243
|
}
|
|
244
|
-
macroCondition(getGlobalConfig().
|
|
244
|
+
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
|
|
245
245
|
{
|
|
246
246
|
throw new Error(`Unknown bucket ${bucket}`);
|
|
247
247
|
}
|
|
@@ -252,7 +252,7 @@ function defaultMergeMethod(a, _b, _c) {
|
|
|
252
252
|
return a;
|
|
253
253
|
}
|
|
254
254
|
let DEBUG_MAP;
|
|
255
|
-
if (macroCondition(getGlobalConfig().
|
|
255
|
+
if (macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG)) {
|
|
256
256
|
DEBUG_MAP = getOrSetGlobal('DEBUG_MAP', new WeakMap());
|
|
257
257
|
}
|
|
258
258
|
|
|
@@ -310,45 +310,55 @@ class IdentifierCache {
|
|
|
310
310
|
*/
|
|
311
311
|
|
|
312
312
|
_getRecordIdentifier(resource, shouldGenerate) {
|
|
313
|
-
if (macroCondition(getGlobalConfig().
|
|
314
|
-
|
|
315
|
-
|
|
313
|
+
if (macroCondition(getGlobalConfig().WarpDriveMirror.activeLogging.LOG_IDENTIFIERS)) {
|
|
314
|
+
if (getGlobalConfig().WarpDriveMirror.debug.LOG_IDENTIFIERS || globalThis.getWarpDriveRuntimeConfig().debug.LOG_IDENTIFIERS) {
|
|
315
|
+
// eslint-disable-next-line no-console
|
|
316
|
+
console.groupCollapsed(`Identifiers: ${shouldGenerate ? 'Generating' : 'Peeking'} Identifier`, resource);
|
|
317
|
+
}
|
|
316
318
|
}
|
|
317
319
|
// short circuit if we're already the stable version
|
|
318
320
|
if (isStableIdentifier(resource)) {
|
|
319
|
-
if (macroCondition(getGlobalConfig().
|
|
321
|
+
if (macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG)) {
|
|
320
322
|
// TODO should we instead just treat this case as a new generation skipping the short circuit?
|
|
321
323
|
if (!this._cache.resources.has(resource.lid) || this._cache.resources.get(resource.lid) !== resource) {
|
|
322
324
|
throw new Error(`The supplied identifier ${JSON.stringify(resource)} does not belong to this store instance`);
|
|
323
325
|
}
|
|
324
326
|
}
|
|
325
|
-
if (macroCondition(getGlobalConfig().
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
327
|
+
if (macroCondition(getGlobalConfig().WarpDriveMirror.activeLogging.LOG_IDENTIFIERS)) {
|
|
328
|
+
if (getGlobalConfig().WarpDriveMirror.debug.LOG_IDENTIFIERS || globalThis.getWarpDriveRuntimeConfig().debug.LOG_IDENTIFIERS) {
|
|
329
|
+
// eslint-disable-next-line no-console
|
|
330
|
+
console.log(`Identifiers: cache HIT - Stable ${resource.lid}`);
|
|
331
|
+
// eslint-disable-next-line no-console
|
|
332
|
+
console.groupEnd();
|
|
333
|
+
}
|
|
330
334
|
}
|
|
331
335
|
return resource;
|
|
332
336
|
}
|
|
333
337
|
|
|
334
338
|
// the resource is unknown, ask the application to identify this data for us
|
|
335
339
|
const lid = this._generate(resource, 'record');
|
|
336
|
-
if (macroCondition(getGlobalConfig().
|
|
337
|
-
|
|
338
|
-
|
|
340
|
+
if (macroCondition(getGlobalConfig().WarpDriveMirror.activeLogging.LOG_IDENTIFIERS)) {
|
|
341
|
+
if (getGlobalConfig().WarpDriveMirror.debug.LOG_IDENTIFIERS || globalThis.getWarpDriveRuntimeConfig().debug.LOG_IDENTIFIERS) {
|
|
342
|
+
// eslint-disable-next-line no-console
|
|
343
|
+
console.log(`Identifiers: ${lid ? 'no ' : ''}lid ${lid ? lid + ' ' : ''}determined for resource`, resource);
|
|
344
|
+
}
|
|
339
345
|
}
|
|
340
346
|
let identifier = /*#__NOINLINE__*/getIdentifierFromLid(this._cache, lid, resource);
|
|
341
347
|
if (identifier !== null) {
|
|
342
|
-
if (macroCondition(getGlobalConfig().
|
|
343
|
-
|
|
344
|
-
|
|
348
|
+
if (macroCondition(getGlobalConfig().WarpDriveMirror.activeLogging.LOG_IDENTIFIERS)) {
|
|
349
|
+
if (getGlobalConfig().WarpDriveMirror.debug.LOG_IDENTIFIERS || globalThis.getWarpDriveRuntimeConfig().debug.LOG_IDENTIFIERS) {
|
|
350
|
+
// eslint-disable-next-line no-console
|
|
351
|
+
console.groupEnd();
|
|
352
|
+
}
|
|
345
353
|
}
|
|
346
354
|
return identifier;
|
|
347
355
|
}
|
|
348
356
|
if (shouldGenerate === 0) {
|
|
349
|
-
if (macroCondition(getGlobalConfig().
|
|
350
|
-
|
|
351
|
-
|
|
357
|
+
if (macroCondition(getGlobalConfig().WarpDriveMirror.activeLogging.LOG_IDENTIFIERS)) {
|
|
358
|
+
if (getGlobalConfig().WarpDriveMirror.debug.LOG_IDENTIFIERS || globalThis.getWarpDriveRuntimeConfig().debug.LOG_IDENTIFIERS) {
|
|
359
|
+
// eslint-disable-next-line no-console
|
|
360
|
+
console.groupEnd();
|
|
361
|
+
}
|
|
352
362
|
}
|
|
353
363
|
return;
|
|
354
364
|
}
|
|
@@ -366,9 +376,11 @@ class IdentifierCache {
|
|
|
366
376
|
identifier = /*#__NOINLINE__*/makeStableRecordIdentifier(keyInfo, 'record', false);
|
|
367
377
|
}
|
|
368
378
|
addResourceToCache(this._cache, identifier);
|
|
369
|
-
if (macroCondition(getGlobalConfig().
|
|
370
|
-
|
|
371
|
-
|
|
379
|
+
if (macroCondition(getGlobalConfig().WarpDriveMirror.activeLogging.LOG_IDENTIFIERS)) {
|
|
380
|
+
if (getGlobalConfig().WarpDriveMirror.debug.LOG_IDENTIFIERS || globalThis.getWarpDriveRuntimeConfig().debug.LOG_IDENTIFIERS) {
|
|
381
|
+
// eslint-disable-next-line no-console
|
|
382
|
+
console.groupEnd();
|
|
383
|
+
}
|
|
372
384
|
}
|
|
373
385
|
return identifier;
|
|
374
386
|
}
|
|
@@ -408,7 +420,7 @@ class IdentifierCache {
|
|
|
408
420
|
identifier = {
|
|
409
421
|
lid: cacheKey
|
|
410
422
|
};
|
|
411
|
-
if (macroCondition(getGlobalConfig().
|
|
423
|
+
if (macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG)) {
|
|
412
424
|
Object.freeze(identifier);
|
|
413
425
|
}
|
|
414
426
|
DOCUMENTS.add(identifier);
|
|
@@ -454,7 +466,7 @@ class IdentifierCache {
|
|
|
454
466
|
}, 'record', true);
|
|
455
467
|
|
|
456
468
|
// populate our unique table
|
|
457
|
-
if (macroCondition(getGlobalConfig().
|
|
469
|
+
if (macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG)) {
|
|
458
470
|
if (this._cache.resources.has(identifier.lid)) {
|
|
459
471
|
throw new Error(`The lid generated for the new record is not unique as it matches an existing identifier`);
|
|
460
472
|
}
|
|
@@ -462,9 +474,11 @@ class IdentifierCache {
|
|
|
462
474
|
|
|
463
475
|
/*#__NOINLINE__*/
|
|
464
476
|
addResourceToCache(this._cache, identifier);
|
|
465
|
-
if (macroCondition(getGlobalConfig().
|
|
466
|
-
|
|
467
|
-
|
|
477
|
+
if (macroCondition(getGlobalConfig().WarpDriveMirror.activeLogging.LOG_IDENTIFIERS)) {
|
|
478
|
+
if (getGlobalConfig().WarpDriveMirror.debug.LOG_IDENTIFIERS || globalThis.getWarpDriveRuntimeConfig().debug.LOG_IDENTIFIERS) {
|
|
479
|
+
// eslint-disable-next-line no-console
|
|
480
|
+
console.log(`Identifiers: created identifier ${String(identifier)} for newly generated resource`, data);
|
|
481
|
+
}
|
|
468
482
|
}
|
|
469
483
|
return identifier;
|
|
470
484
|
}
|
|
@@ -511,9 +525,11 @@ class IdentifierCache {
|
|
|
511
525
|
if (hadLid) {
|
|
512
526
|
data.lid = identifier.lid;
|
|
513
527
|
}
|
|
514
|
-
if (macroCondition(getGlobalConfig().
|
|
515
|
-
|
|
516
|
-
|
|
528
|
+
if (macroCondition(getGlobalConfig().WarpDriveMirror.activeLogging.LOG_IDENTIFIERS)) {
|
|
529
|
+
if (getGlobalConfig().WarpDriveMirror.debug.LOG_IDENTIFIERS || globalThis.getWarpDriveRuntimeConfig().debug.LOG_IDENTIFIERS) {
|
|
530
|
+
// eslint-disable-next-line no-console
|
|
531
|
+
console.log(`Identifiers: merged identifiers ${generatedIdentifier.lid} and ${existingIdentifier.lid} for resource into ${identifier.lid}`, data);
|
|
532
|
+
}
|
|
517
533
|
}
|
|
518
534
|
}
|
|
519
535
|
const id = identifier.id;
|
|
@@ -523,12 +539,14 @@ class IdentifierCache {
|
|
|
523
539
|
|
|
524
540
|
// add to our own secondary lookup table
|
|
525
541
|
if (id !== newId && newId !== null) {
|
|
526
|
-
if (macroCondition(getGlobalConfig().
|
|
527
|
-
|
|
528
|
-
|
|
542
|
+
if (macroCondition(getGlobalConfig().WarpDriveMirror.activeLogging.LOG_IDENTIFIERS)) {
|
|
543
|
+
if (getGlobalConfig().WarpDriveMirror.debug.LOG_IDENTIFIERS || globalThis.getWarpDriveRuntimeConfig().debug.LOG_IDENTIFIERS) {
|
|
544
|
+
// eslint-disable-next-line no-console
|
|
545
|
+
console.log(`Identifiers: updated id for identifier ${identifier.lid} from '${String(id)}' to '${String(newId)}' for resource`, data);
|
|
546
|
+
}
|
|
529
547
|
}
|
|
530
548
|
const typeSet = this._cache.resourcesByType[identifier.type];
|
|
531
|
-
macroCondition(getGlobalConfig().
|
|
549
|
+
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
|
|
532
550
|
if (!test) {
|
|
533
551
|
throw new Error(`Expected to find a typeSet for ${identifier.type}`);
|
|
534
552
|
}
|
|
@@ -537,9 +555,11 @@ class IdentifierCache {
|
|
|
537
555
|
if (id !== null) {
|
|
538
556
|
typeSet.id.delete(id);
|
|
539
557
|
}
|
|
540
|
-
} else if (macroCondition(getGlobalConfig().
|
|
541
|
-
|
|
542
|
-
|
|
558
|
+
} else if (macroCondition(getGlobalConfig().WarpDriveMirror.activeLogging.LOG_IDENTIFIERS)) {
|
|
559
|
+
if (getGlobalConfig().WarpDriveMirror.debug.LOG_IDENTIFIERS || globalThis.getWarpDriveRuntimeConfig().debug.LOG_IDENTIFIERS) {
|
|
560
|
+
// eslint-disable-next-line no-console
|
|
561
|
+
console.log(`Identifiers: updated identifier ${identifier.lid} resource`, data);
|
|
562
|
+
}
|
|
543
563
|
}
|
|
544
564
|
return identifier;
|
|
545
565
|
}
|
|
@@ -549,7 +569,7 @@ class IdentifierCache {
|
|
|
549
569
|
* @private
|
|
550
570
|
*/
|
|
551
571
|
_mergeRecordIdentifiers(keyInfo, identifier, existingIdentifier, data) {
|
|
552
|
-
macroCondition(getGlobalConfig().
|
|
572
|
+
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
|
|
553
573
|
if (!test) {
|
|
554
574
|
throw new Error(`Expected keyInfo to contain an id`);
|
|
555
575
|
}
|
|
@@ -600,7 +620,7 @@ class IdentifierCache {
|
|
|
600
620
|
forgetRecordIdentifier(identifierObject) {
|
|
601
621
|
const identifier = this.getOrCreateRecordIdentifier(identifierObject);
|
|
602
622
|
const typeSet = this._cache.resourcesByType[identifier.type];
|
|
603
|
-
macroCondition(getGlobalConfig().
|
|
623
|
+
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
|
|
604
624
|
if (!test) {
|
|
605
625
|
throw new Error(`Expected to find a typeSet for ${identifier.type}`);
|
|
606
626
|
}
|
|
@@ -617,15 +637,16 @@ class IdentifierCache {
|
|
|
617
637
|
});
|
|
618
638
|
this._cache.polymorphicLidBackMap.delete(identifier.lid);
|
|
619
639
|
}
|
|
620
|
-
if (macroCondition(getGlobalConfig().
|
|
640
|
+
if (macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG)) {
|
|
621
641
|
identifier[DEBUG_STALE_CACHE_OWNER] = identifier[CACHE_OWNER];
|
|
622
642
|
}
|
|
623
643
|
identifier[CACHE_OWNER] = undefined;
|
|
624
|
-
IDENTIFIERS.delete(identifier);
|
|
625
644
|
this._forget(identifier, 'record');
|
|
626
|
-
if (macroCondition(getGlobalConfig().
|
|
627
|
-
|
|
628
|
-
|
|
645
|
+
if (macroCondition(getGlobalConfig().WarpDriveMirror.activeLogging.LOG_IDENTIFIERS)) {
|
|
646
|
+
if (getGlobalConfig().WarpDriveMirror.debug.LOG_IDENTIFIERS || globalThis.getWarpDriveRuntimeConfig().debug.LOG_IDENTIFIERS) {
|
|
647
|
+
// eslint-disable-next-line no-console
|
|
648
|
+
console.log(`Identifiers: released identifier ${identifierObject.lid}`);
|
|
649
|
+
}
|
|
629
650
|
}
|
|
630
651
|
}
|
|
631
652
|
destroy() {
|
|
@@ -637,20 +658,17 @@ class IdentifierCache {
|
|
|
637
658
|
}
|
|
638
659
|
}
|
|
639
660
|
function makeStableRecordIdentifier(recordIdentifier, bucket, clientOriginated) {
|
|
640
|
-
|
|
641
|
-
if (macroCondition(getGlobalConfig().WarpDrive.env.DEBUG)) {
|
|
661
|
+
if (macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG)) {
|
|
642
662
|
// we enforce immutability in dev
|
|
643
663
|
// but preserve our ability to do controlled updates to the reference
|
|
644
664
|
let wrapper = {
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
},
|
|
665
|
+
type: recordIdentifier.type,
|
|
666
|
+
lid: recordIdentifier.lid,
|
|
648
667
|
get id() {
|
|
649
668
|
return recordIdentifier.id;
|
|
650
|
-
}
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
},
|
|
669
|
+
}
|
|
670
|
+
};
|
|
671
|
+
const proto = {
|
|
654
672
|
get [CACHE_OWNER]() {
|
|
655
673
|
return recordIdentifier[CACHE_OWNER];
|
|
656
674
|
},
|
|
@@ -662,9 +680,15 @@ function makeStableRecordIdentifier(recordIdentifier, bucket, clientOriginated)
|
|
|
662
680
|
},
|
|
663
681
|
set [DEBUG_STALE_CACHE_OWNER](value) {
|
|
664
682
|
recordIdentifier[DEBUG_STALE_CACHE_OWNER] = value;
|
|
683
|
+
},
|
|
684
|
+
get [DEBUG_CLIENT_ORIGINATED]() {
|
|
685
|
+
return clientOriginated;
|
|
686
|
+
},
|
|
687
|
+
get [DEBUG_IDENTIFIER_BUCKET]() {
|
|
688
|
+
return bucket;
|
|
665
689
|
}
|
|
666
690
|
};
|
|
667
|
-
Object.defineProperty(
|
|
691
|
+
Object.defineProperty(proto, 'toString', {
|
|
668
692
|
enumerable: false,
|
|
669
693
|
value: () => {
|
|
670
694
|
const {
|
|
@@ -675,7 +699,7 @@ function makeStableRecordIdentifier(recordIdentifier, bucket, clientOriginated)
|
|
|
675
699
|
return `${clientOriginated ? '[CLIENT_ORIGINATED] ' : ''}${String(type)}:${String(id)} (${lid})`;
|
|
676
700
|
}
|
|
677
701
|
});
|
|
678
|
-
Object.defineProperty(
|
|
702
|
+
Object.defineProperty(proto, 'toJSON', {
|
|
679
703
|
enumerable: false,
|
|
680
704
|
value: () => {
|
|
681
705
|
const {
|
|
@@ -690,9 +714,7 @@ function makeStableRecordIdentifier(recordIdentifier, bucket, clientOriginated)
|
|
|
690
714
|
};
|
|
691
715
|
}
|
|
692
716
|
});
|
|
693
|
-
wrapper
|
|
694
|
-
wrapper[DEBUG_IDENTIFIER_BUCKET] = bucket;
|
|
695
|
-
IDENTIFIERS.add(wrapper);
|
|
717
|
+
Object.setPrototypeOf(wrapper, proto);
|
|
696
718
|
DEBUG_MAP.set(wrapper, recordIdentifier);
|
|
697
719
|
wrapper = freeze(wrapper);
|
|
698
720
|
return wrapper;
|
|
@@ -700,7 +722,7 @@ function makeStableRecordIdentifier(recordIdentifier, bucket, clientOriginated)
|
|
|
700
722
|
return recordIdentifier;
|
|
701
723
|
}
|
|
702
724
|
function performRecordIdentifierUpdate(identifier, keyInfo, data, updateFn) {
|
|
703
|
-
if (macroCondition(getGlobalConfig().
|
|
725
|
+
if (macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG)) {
|
|
704
726
|
const {
|
|
705
727
|
id,
|
|
706
728
|
type
|
|
@@ -775,9 +797,11 @@ function detectMerge(cache, keyInfo, identifier, data) {
|
|
|
775
797
|
}
|
|
776
798
|
function getIdentifierFromLid(cache, lid, resource) {
|
|
777
799
|
const identifier = cache.resources.get(lid);
|
|
778
|
-
if (macroCondition(getGlobalConfig().
|
|
779
|
-
|
|
780
|
-
|
|
800
|
+
if (macroCondition(getGlobalConfig().WarpDriveMirror.activeLogging.LOG_IDENTIFIERS)) {
|
|
801
|
+
if (getGlobalConfig().WarpDriveMirror.debug.LOG_IDENTIFIERS || globalThis.getWarpDriveRuntimeConfig().debug.LOG_IDENTIFIERS) {
|
|
802
|
+
// eslint-disable-next-line no-console
|
|
803
|
+
console.log(`Identifiers: cache ${identifier ? 'HIT' : 'MISS'} - Non-Stable ${lid}`, resource);
|
|
804
|
+
}
|
|
781
805
|
}
|
|
782
806
|
return identifier || null;
|
|
783
807
|
}
|
|
@@ -796,92 +820,477 @@ function addResourceToCache(cache, identifier) {
|
|
|
796
820
|
typeSet.id.set(identifier.id, identifier);
|
|
797
821
|
}
|
|
798
822
|
}
|
|
823
|
+
const TEXT_COLORS = {
|
|
824
|
+
TEXT: 'inherit',
|
|
825
|
+
notify: ['white', 'white', 'inherit', 'magenta', 'inherit'],
|
|
826
|
+
'reactive-ui': ['white', 'white', 'inherit', 'magenta', 'inherit'],
|
|
827
|
+
graph: ['white', 'white', 'inherit', 'magenta', 'inherit'],
|
|
828
|
+
request: ['white', 'white', 'inherit', 'magenta', 'inherit'],
|
|
829
|
+
cache: ['white', 'white', 'inherit', 'magenta', 'inherit']
|
|
830
|
+
};
|
|
831
|
+
const BG_COLORS = {
|
|
832
|
+
TEXT: 'transparent',
|
|
833
|
+
notify: ['dimgray', 'cadetblue', 'transparent', 'transparent', 'transparent'],
|
|
834
|
+
'reactive-ui': ['dimgray', 'cadetblue', 'transparent', 'transparent', 'transparent'],
|
|
835
|
+
graph: ['dimgray', 'cadetblue', 'transparent', 'transparent', 'transparent'],
|
|
836
|
+
request: ['dimgray', 'cadetblue', 'transparent', 'transparent', 'transparent'],
|
|
837
|
+
cache: ['dimgray', 'cadetblue', 'transparent', 'transparent', 'transparent']
|
|
838
|
+
};
|
|
839
|
+
const NOTIFY_BORDER = {
|
|
840
|
+
TEXT: 0,
|
|
841
|
+
notify: [3, 2, 0, 0, 0],
|
|
842
|
+
'reactive-ui': [3, 2, 0, 0, 0],
|
|
843
|
+
graph: [3, 2, 0, 0, 0],
|
|
844
|
+
request: [3, 2, 0, 0, 0],
|
|
845
|
+
cache: [3, 2, 0, 0, 0]
|
|
846
|
+
};
|
|
847
|
+
const LIGHT_DARK_ALT = {
|
|
848
|
+
lightgreen: 'green',
|
|
849
|
+
green: 'lightgreen'
|
|
850
|
+
};
|
|
851
|
+
function badge(isLight, color, bgColor, border) {
|
|
852
|
+
return [`color: ${correctColor(isLight, color)}; background-color: ${correctColor(isLight, bgColor)}; padding: ${border}px ${2 * border}px; border-radius: ${border}px;`, `color: ${TEXT_COLORS.TEXT}; background-color: ${BG_COLORS.TEXT};`];
|
|
853
|
+
}
|
|
854
|
+
function colorForBucket(isLight, scope, bucket) {
|
|
855
|
+
if (scope === 'notify') {
|
|
856
|
+
return bucket === 'added' ? badge(isLight, 'lightgreen', 'transparent', 0) : bucket === 'removed' ? badge(isLight, 'red', 'transparent', 0) : badge(isLight, TEXT_COLORS[scope][2], BG_COLORS[scope][2], NOTIFY_BORDER[scope][2]);
|
|
857
|
+
}
|
|
858
|
+
if (scope === 'reactive-ui') {
|
|
859
|
+
return bucket === 'created' ? badge(isLight, 'lightgreen', 'transparent', 0) : bucket === 'disconnected' ? badge(isLight, 'red', 'transparent', 0) : badge(isLight, TEXT_COLORS[scope][2], BG_COLORS[scope][2], NOTIFY_BORDER[scope][2]);
|
|
860
|
+
}
|
|
861
|
+
if (scope === 'cache') {
|
|
862
|
+
return bucket === 'inserted' ? badge(isLight, 'lightgreen', 'transparent', 0) : bucket === 'removed' ? badge(isLight, 'red', 'transparent', 0) : badge(isLight, TEXT_COLORS[scope][2], BG_COLORS[scope][2], NOTIFY_BORDER[scope][2]);
|
|
863
|
+
}
|
|
864
|
+
return badge(isLight, TEXT_COLORS[scope][3], BG_COLORS[scope][3], NOTIFY_BORDER[scope][3]);
|
|
865
|
+
}
|
|
866
|
+
function logGroup(scope, prefix, subScop1, subScop2, subScop3, subScop4) {
|
|
867
|
+
// eslint-disable-next-line no-console
|
|
868
|
+
console.groupCollapsed(..._log(scope, prefix, subScop1, subScop2, subScop3, subScop4));
|
|
869
|
+
}
|
|
870
|
+
function log(scope, prefix, subScop1, subScop2, subScop3, subScop4) {
|
|
871
|
+
// eslint-disable-next-line no-console
|
|
872
|
+
console.log(..._log(scope, prefix, subScop1, subScop2, subScop3, subScop4));
|
|
873
|
+
}
|
|
874
|
+
function correctColor(isLight, color) {
|
|
875
|
+
if (!isLight) {
|
|
876
|
+
return color;
|
|
877
|
+
}
|
|
878
|
+
return color in LIGHT_DARK_ALT ? LIGHT_DARK_ALT[color] : color;
|
|
879
|
+
}
|
|
880
|
+
function isLightMode() {
|
|
881
|
+
if (window?.matchMedia?.('(prefers-color-scheme: light)').matches) {
|
|
882
|
+
return true;
|
|
883
|
+
}
|
|
884
|
+
return false;
|
|
885
|
+
}
|
|
886
|
+
function _log(scope, prefix, subScop1, subScop2, subScop3, subScop4) {
|
|
887
|
+
const isLight = isLightMode();
|
|
888
|
+
switch (scope) {
|
|
889
|
+
case 'reactive-ui':
|
|
890
|
+
case 'notify':
|
|
891
|
+
{
|
|
892
|
+
const scopePath = prefix ? `[${prefix}] ${scope}` : scope;
|
|
893
|
+
const path = subScop4 ? `${subScop3}.${subScop4}` : subScop3;
|
|
894
|
+
return [`%c@warp%c-%cdrive%c %c${scopePath}%c %c${subScop1}%c %c${subScop2}%c %c${path}%c`, ...badge(isLight, 'lightgreen', 'transparent', 0), ...badge(isLight, 'magenta', 'transparent', 0), ...badge(isLight, TEXT_COLORS[scope][0], BG_COLORS[scope][0], NOTIFY_BORDER[scope][0]), ...badge(isLight, TEXT_COLORS[scope][1], BG_COLORS[scope][1], NOTIFY_BORDER[scope][1]), ...badge(isLight, TEXT_COLORS[scope][2], BG_COLORS[scope][2], NOTIFY_BORDER[scope][2]), ...colorForBucket(isLight, scope, path)];
|
|
895
|
+
}
|
|
896
|
+
case 'cache':
|
|
897
|
+
{
|
|
898
|
+
const scopePath = prefix ? `${scope} (${prefix})` : scope;
|
|
899
|
+
return [`%c@warp%c-%cdrive%c %c${scopePath}%c %c${subScop1}%c %c${subScop2}%c %c${subScop3}%c %c${subScop4}%c`, ...badge(isLight, 'lightgreen', 'transparent', 0), ...badge(isLight, 'magenta', 'transparent', 0), ...badge(isLight, TEXT_COLORS[scope][0], BG_COLORS[scope][0], NOTIFY_BORDER[scope][0]), ...badge(isLight, TEXT_COLORS[scope][1], BG_COLORS[scope][1], NOTIFY_BORDER[scope][1]), ...badge(isLight, TEXT_COLORS[scope][2], BG_COLORS[scope][2], NOTIFY_BORDER[scope][2]), ...colorForBucket(isLight, scope, subScop3), ...badge(isLight, TEXT_COLORS[scope][4], BG_COLORS[scope][4], NOTIFY_BORDER[scope][4])];
|
|
900
|
+
}
|
|
901
|
+
}
|
|
902
|
+
return [];
|
|
903
|
+
}
|
|
799
904
|
|
|
800
905
|
/**
|
|
801
|
-
|
|
802
|
-
*/
|
|
906
|
+
* @module @ember-data-mirror/store
|
|
907
|
+
*/
|
|
908
|
+
function urlFromLink(link) {
|
|
909
|
+
if (typeof link === 'string') return link;
|
|
910
|
+
return link.href;
|
|
911
|
+
}
|
|
803
912
|
|
|
804
913
|
/**
|
|
805
|
-
|
|
806
|
-
|
|
914
|
+
* A Document is a class that wraps the response content from a request to the API
|
|
915
|
+
* returned by `Cache.put` or `Cache.peek`, converting resource-identifiers into
|
|
916
|
+
* record instances.
|
|
917
|
+
*
|
|
918
|
+
* It is not directly instantiated by the user, and its properties should not
|
|
919
|
+
* be directly modified. Whether individual properties are mutable or not is
|
|
920
|
+
* determined by the record instance itself.
|
|
921
|
+
*
|
|
922
|
+
* @public
|
|
923
|
+
* @class ReactiveDocument
|
|
924
|
+
*/
|
|
925
|
+
class ReactiveDocument {
|
|
926
|
+
/**
|
|
927
|
+
* The links object for this document, if any
|
|
928
|
+
*
|
|
929
|
+
* e.g.
|
|
930
|
+
*
|
|
931
|
+
* ```
|
|
932
|
+
* {
|
|
933
|
+
* self: '/articles?page[number]=3',
|
|
934
|
+
* }
|
|
935
|
+
* ```
|
|
936
|
+
*
|
|
937
|
+
* @property links
|
|
938
|
+
* @type {object|undefined} - a links object
|
|
939
|
+
* @public
|
|
940
|
+
*/
|
|
807
941
|
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
}
|
|
822
|
-
});
|
|
823
|
-
}
|
|
824
|
-
destroy() {
|
|
825
|
-
this.store.notifications.unsubscribe(this.___token);
|
|
826
|
-
}
|
|
827
|
-
get type() {
|
|
828
|
-
return this.identifier().type;
|
|
829
|
-
}
|
|
942
|
+
/**
|
|
943
|
+
* The primary data for this document, if any.
|
|
944
|
+
*
|
|
945
|
+
* If this document has no primary data (e.g. because it is an error document)
|
|
946
|
+
* this property will be `undefined`.
|
|
947
|
+
*
|
|
948
|
+
* For collections this will be an array of record instances,
|
|
949
|
+
* for single resource requests it will be a single record instance or null.
|
|
950
|
+
*
|
|
951
|
+
* @property data
|
|
952
|
+
* @public
|
|
953
|
+
* @type {object|Array<object>|null|undefined} - a data object
|
|
954
|
+
*/
|
|
830
955
|
|
|
831
956
|
/**
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
957
|
+
* The errors returned by the API for this request, if any
|
|
958
|
+
*
|
|
959
|
+
* @property errors
|
|
960
|
+
* @public
|
|
961
|
+
* @type {object|undefined} - an errors object
|
|
962
|
+
*/
|
|
963
|
+
|
|
964
|
+
/**
|
|
965
|
+
* The meta object for this document, if any
|
|
966
|
+
*
|
|
967
|
+
* @property meta
|
|
968
|
+
* @public
|
|
969
|
+
* @type {object|undefined} - a meta object
|
|
970
|
+
*/
|
|
971
|
+
|
|
972
|
+
/**
|
|
973
|
+
* The identifier associated with this document, if any
|
|
974
|
+
*
|
|
975
|
+
* @property identifier
|
|
976
|
+
* @public
|
|
977
|
+
* @type {StableDocumentIdentifier|null}
|
|
978
|
+
*/
|
|
979
|
+
|
|
980
|
+
constructor(store, identifier, localCache) {
|
|
981
|
+
this._store = store;
|
|
982
|
+
this._localCache = localCache;
|
|
983
|
+
this.identifier = identifier;
|
|
984
|
+
|
|
985
|
+
// TODO if we ever enable auto-cleanup of the cache, we will need to tear this down
|
|
986
|
+
// in a destroy method
|
|
987
|
+
if (identifier) {
|
|
988
|
+
store.notifications.subscribe(identifier, (_identifier, type) => {
|
|
989
|
+
switch (type) {
|
|
990
|
+
case 'updated':
|
|
991
|
+
// FIXME in the case of a collection we need to notify it's length
|
|
992
|
+
// and have it recalc
|
|
993
|
+
notifySignal(this, 'data');
|
|
994
|
+
notifySignal(this, 'links');
|
|
995
|
+
notifySignal(this, 'meta');
|
|
996
|
+
notifySignal(this, 'errors');
|
|
997
|
+
break;
|
|
998
|
+
}
|
|
999
|
+
});
|
|
1000
|
+
}
|
|
1001
|
+
}
|
|
1002
|
+
async #request(link, options) {
|
|
1003
|
+
const href = this.links?.[link];
|
|
1004
|
+
if (!href) {
|
|
1005
|
+
return null;
|
|
1006
|
+
}
|
|
1007
|
+
options.method = options.method || 'GET';
|
|
1008
|
+
Object.assign(options, {
|
|
1009
|
+
url: urlFromLink(href)
|
|
1010
|
+
});
|
|
1011
|
+
const response = await this._store.request(options);
|
|
1012
|
+
return response.content;
|
|
847
1013
|
}
|
|
848
1014
|
|
|
849
1015
|
/**
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
1016
|
+
* Fetches the related link for this document, returning a promise that resolves
|
|
1017
|
+
* with the document when the request completes. If no related link is present,
|
|
1018
|
+
* will fallback to the self link if present
|
|
1019
|
+
*
|
|
1020
|
+
* @method fetch
|
|
1021
|
+
* @public
|
|
1022
|
+
* @param {object} options
|
|
1023
|
+
* @return Promise<Document>
|
|
1024
|
+
*/
|
|
1025
|
+
fetch(options = {}) {
|
|
1026
|
+
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
|
|
1027
|
+
if (!test) {
|
|
1028
|
+
throw new Error(`No self or related link`);
|
|
1029
|
+
}
|
|
1030
|
+
})(this.links?.related || this.links?.self) : {};
|
|
1031
|
+
options.cacheOptions = options.cacheOptions || {};
|
|
1032
|
+
options.cacheOptions.key = this.identifier?.lid;
|
|
1033
|
+
return this.#request(this.links.related ? 'related' : 'self', options);
|
|
864
1034
|
}
|
|
865
1035
|
|
|
866
1036
|
/**
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
*/
|
|
879
|
-
remoteType() {
|
|
880
|
-
return 'identity';
|
|
1037
|
+
* Fetches the next link for this document, returning a promise that resolves
|
|
1038
|
+
* with the new document when the request completes, or null if there is no
|
|
1039
|
+
* next link.
|
|
1040
|
+
*
|
|
1041
|
+
* @method next
|
|
1042
|
+
* @public
|
|
1043
|
+
* @param {object} options
|
|
1044
|
+
* @return Promise<Document | null>
|
|
1045
|
+
*/
|
|
1046
|
+
next(options = {}) {
|
|
1047
|
+
return this.#request('next', options);
|
|
881
1048
|
}
|
|
882
1049
|
|
|
883
1050
|
/**
|
|
884
|
-
|
|
1051
|
+
* Fetches the prev link for this document, returning a promise that resolves
|
|
1052
|
+
* with the new document when the request completes, or null if there is no
|
|
1053
|
+
* prev link.
|
|
1054
|
+
*
|
|
1055
|
+
* @method prev
|
|
1056
|
+
* @public
|
|
1057
|
+
* @param {object} options
|
|
1058
|
+
* @return Promise<Document | null>
|
|
1059
|
+
*/
|
|
1060
|
+
prev(options = {}) {
|
|
1061
|
+
return this.#request('prev', options);
|
|
1062
|
+
}
|
|
1063
|
+
|
|
1064
|
+
/**
|
|
1065
|
+
* Fetches the first link for this document, returning a promise that resolves
|
|
1066
|
+
* with the new document when the request completes, or null if there is no
|
|
1067
|
+
* first link.
|
|
1068
|
+
*
|
|
1069
|
+
* @method first
|
|
1070
|
+
* @public
|
|
1071
|
+
* @param {object} options
|
|
1072
|
+
* @return Promise<Document | null>
|
|
1073
|
+
*/
|
|
1074
|
+
first(options = {}) {
|
|
1075
|
+
return this.#request('first', options);
|
|
1076
|
+
}
|
|
1077
|
+
|
|
1078
|
+
/**
|
|
1079
|
+
* Fetches the last link for this document, returning a promise that resolves
|
|
1080
|
+
* with the new document when the request completes, or null if there is no
|
|
1081
|
+
* last link.
|
|
1082
|
+
*
|
|
1083
|
+
* @method last
|
|
1084
|
+
* @public
|
|
1085
|
+
* @param {object} options
|
|
1086
|
+
* @return Promise<Document | null>
|
|
1087
|
+
*/
|
|
1088
|
+
last(options = {}) {
|
|
1089
|
+
return this.#request('last', options);
|
|
1090
|
+
}
|
|
1091
|
+
|
|
1092
|
+
/**
|
|
1093
|
+
* Implemented for `JSON.stringify` support.
|
|
1094
|
+
*
|
|
1095
|
+
* Returns the JSON representation of the document wrapper.
|
|
1096
|
+
*
|
|
1097
|
+
* This is a shallow serialization, it does not deeply serialize
|
|
1098
|
+
* the document's contents, leaving that to the individual record
|
|
1099
|
+
* instances to determine how to do, if at all.
|
|
1100
|
+
*
|
|
1101
|
+
* @method toJSON
|
|
1102
|
+
* @public
|
|
1103
|
+
* @return
|
|
1104
|
+
*/
|
|
1105
|
+
toJSON() {
|
|
1106
|
+
const data = {};
|
|
1107
|
+
data.identifier = this.identifier;
|
|
1108
|
+
if (this.data !== undefined) {
|
|
1109
|
+
data.data = this.data;
|
|
1110
|
+
}
|
|
1111
|
+
if (this.links !== undefined) {
|
|
1112
|
+
data.links = this.links;
|
|
1113
|
+
}
|
|
1114
|
+
if (this.errors !== undefined) {
|
|
1115
|
+
data.errors = this.errors;
|
|
1116
|
+
}
|
|
1117
|
+
if (this.meta !== undefined) {
|
|
1118
|
+
data.meta = this.meta;
|
|
1119
|
+
}
|
|
1120
|
+
return data;
|
|
1121
|
+
}
|
|
1122
|
+
}
|
|
1123
|
+
defineSubscription(ReactiveDocument.prototype, 'errors', {
|
|
1124
|
+
get() {
|
|
1125
|
+
const {
|
|
1126
|
+
identifier
|
|
1127
|
+
} = this;
|
|
1128
|
+
if (!identifier) {
|
|
1129
|
+
const {
|
|
1130
|
+
document
|
|
1131
|
+
} = this._localCache;
|
|
1132
|
+
if ('errors' in document) {
|
|
1133
|
+
return document.errors;
|
|
1134
|
+
}
|
|
1135
|
+
return;
|
|
1136
|
+
}
|
|
1137
|
+
const doc = this._store.cache.peek(identifier);
|
|
1138
|
+
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
|
|
1139
|
+
if (!test) {
|
|
1140
|
+
throw new Error(`No cache data was found for the document '${identifier.lid}'`);
|
|
1141
|
+
}
|
|
1142
|
+
})(doc) : {};
|
|
1143
|
+
return 'errors' in doc ? doc.errors : undefined;
|
|
1144
|
+
}
|
|
1145
|
+
});
|
|
1146
|
+
defineSubscription(ReactiveDocument.prototype, 'data', {
|
|
1147
|
+
get() {
|
|
1148
|
+
const {
|
|
1149
|
+
identifier,
|
|
1150
|
+
_localCache
|
|
1151
|
+
} = this;
|
|
1152
|
+
const doc = identifier ? this._store.cache.peek(identifier) : _localCache.document;
|
|
1153
|
+
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
|
|
1154
|
+
if (!test) {
|
|
1155
|
+
throw new Error(`No cache data was found for the document '${identifier?.lid ?? '<uncached document>'}'`);
|
|
1156
|
+
}
|
|
1157
|
+
})(doc) : {};
|
|
1158
|
+
const data = 'data' in doc ? doc.data : undefined;
|
|
1159
|
+
if (Array.isArray(data)) {
|
|
1160
|
+
return this._store.recordArrayManager.getCollection({
|
|
1161
|
+
type: identifier ? identifier.lid : _localCache.request.url,
|
|
1162
|
+
identifiers: data.slice(),
|
|
1163
|
+
doc: identifier ? undefined : doc,
|
|
1164
|
+
identifier: identifier ?? null
|
|
1165
|
+
});
|
|
1166
|
+
} else if (data) {
|
|
1167
|
+
return this._store.peekRecord(data);
|
|
1168
|
+
} else {
|
|
1169
|
+
return data;
|
|
1170
|
+
}
|
|
1171
|
+
}
|
|
1172
|
+
});
|
|
1173
|
+
defineSubscription(ReactiveDocument.prototype, 'links', {
|
|
1174
|
+
get() {
|
|
1175
|
+
const {
|
|
1176
|
+
identifier
|
|
1177
|
+
} = this;
|
|
1178
|
+
if (!identifier) {
|
|
1179
|
+
return this._localCache.document.links;
|
|
1180
|
+
}
|
|
1181
|
+
const data = this._store.cache.peek(identifier);
|
|
1182
|
+
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
|
|
1183
|
+
if (!test) {
|
|
1184
|
+
throw new Error(`No cache data was found for the document '${identifier.lid}'`);
|
|
1185
|
+
}
|
|
1186
|
+
})(data) : {};
|
|
1187
|
+
return data.links;
|
|
1188
|
+
}
|
|
1189
|
+
});
|
|
1190
|
+
defineSubscription(ReactiveDocument.prototype, 'meta', {
|
|
1191
|
+
get() {
|
|
1192
|
+
const {
|
|
1193
|
+
identifier
|
|
1194
|
+
} = this;
|
|
1195
|
+
if (!identifier) {
|
|
1196
|
+
return this._localCache.document.meta;
|
|
1197
|
+
}
|
|
1198
|
+
const data = this._store.cache.peek(identifier);
|
|
1199
|
+
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
|
|
1200
|
+
if (!test) {
|
|
1201
|
+
throw new Error(`No cache data was found for the document '${identifier.lid}'`);
|
|
1202
|
+
}
|
|
1203
|
+
})(data) : {};
|
|
1204
|
+
return data.meta;
|
|
1205
|
+
}
|
|
1206
|
+
});
|
|
1207
|
+
|
|
1208
|
+
/**
|
|
1209
|
+
@module @ember-data-mirror/store
|
|
1210
|
+
*/
|
|
1211
|
+
|
|
1212
|
+
/**
|
|
1213
|
+
A `RecordReference` is a low-level API that allows users and
|
|
1214
|
+
addon authors to perform meta-operations on a record.
|
|
1215
|
+
|
|
1216
|
+
@class RecordReference
|
|
1217
|
+
@public
|
|
1218
|
+
*/
|
|
1219
|
+
class RecordReference {
|
|
1220
|
+
// unsubscribe token given to us by the notification manager
|
|
1221
|
+
___token;
|
|
1222
|
+
___identifier;
|
|
1223
|
+
constructor(store, identifier) {
|
|
1224
|
+
this.store = store;
|
|
1225
|
+
this.___identifier = identifier;
|
|
1226
|
+
this.___token = store.notifications.subscribe(identifier, (_, bucket, notifiedKey) => {
|
|
1227
|
+
if (bucket === 'identity' || bucket === 'attributes' && notifiedKey === 'id') {
|
|
1228
|
+
this._ref++;
|
|
1229
|
+
}
|
|
1230
|
+
});
|
|
1231
|
+
}
|
|
1232
|
+
destroy() {
|
|
1233
|
+
this.store.notifications.unsubscribe(this.___token);
|
|
1234
|
+
}
|
|
1235
|
+
get type() {
|
|
1236
|
+
return this.identifier().type;
|
|
1237
|
+
}
|
|
1238
|
+
|
|
1239
|
+
/**
|
|
1240
|
+
The `id` of the record that this reference refers to.
|
|
1241
|
+
Together, the `type` and `id` properties form a composite key for
|
|
1242
|
+
the identity map.
|
|
1243
|
+
Example
|
|
1244
|
+
```javascript
|
|
1245
|
+
let userRef = store.getReference('user', 1);
|
|
1246
|
+
userRef.id(); // '1'
|
|
1247
|
+
```
|
|
1248
|
+
@method id
|
|
1249
|
+
@public
|
|
1250
|
+
@return {String} The id of the record.
|
|
1251
|
+
*/
|
|
1252
|
+
id() {
|
|
1253
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
|
|
1254
|
+
this._ref; // consume the tracked prop
|
|
1255
|
+
return this.___identifier.id;
|
|
1256
|
+
}
|
|
1257
|
+
|
|
1258
|
+
/**
|
|
1259
|
+
The `identifier` of the record that this reference refers to.
|
|
1260
|
+
Together, the `type` and `id` properties form a composite key for
|
|
1261
|
+
the identity map.
|
|
1262
|
+
Example
|
|
1263
|
+
```javascript
|
|
1264
|
+
let userRef = store.getReference('user', 1);
|
|
1265
|
+
userRef.identifier(); // '1'
|
|
1266
|
+
```
|
|
1267
|
+
@method identifier
|
|
1268
|
+
@public
|
|
1269
|
+
@return {String} The identifier of the record.
|
|
1270
|
+
*/
|
|
1271
|
+
identifier() {
|
|
1272
|
+
return this.___identifier;
|
|
1273
|
+
}
|
|
1274
|
+
|
|
1275
|
+
/**
|
|
1276
|
+
How the reference will be looked up when it is loaded. Currently
|
|
1277
|
+
this always returns `identity` to signify that a record will be
|
|
1278
|
+
loaded by its `type` and `id`.
|
|
1279
|
+
Example
|
|
1280
|
+
```javascript
|
|
1281
|
+
const userRef = store.getReference('user', 1);
|
|
1282
|
+
userRef.remoteType(); // 'identity'
|
|
1283
|
+
```
|
|
1284
|
+
@method remoteType
|
|
1285
|
+
@public
|
|
1286
|
+
@return {String} 'identity'
|
|
1287
|
+
*/
|
|
1288
|
+
remoteType() {
|
|
1289
|
+
return 'identity';
|
|
1290
|
+
}
|
|
1291
|
+
|
|
1292
|
+
/**
|
|
1293
|
+
This API allows you to provide a reference with new data. The
|
|
885
1294
|
simplest usage of this API is similar to `store.push`: you provide a
|
|
886
1295
|
normalized hash of data and the object represented by the reference
|
|
887
1296
|
will update.
|
|
@@ -955,7 +1364,7 @@ class RecordReference {
|
|
|
955
1364
|
if (id !== null) {
|
|
956
1365
|
return this.store.findRecord(this.type, id);
|
|
957
1366
|
}
|
|
958
|
-
macroCondition(getGlobalConfig().
|
|
1367
|
+
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
|
|
959
1368
|
{
|
|
960
1369
|
throw new Error(`Unable to fetch record of type ${this.type} without an id`);
|
|
961
1370
|
}
|
|
@@ -982,7 +1391,7 @@ class RecordReference {
|
|
|
982
1391
|
reload: true
|
|
983
1392
|
});
|
|
984
1393
|
}
|
|
985
|
-
macroCondition(getGlobalConfig().
|
|
1394
|
+
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
|
|
986
1395
|
{
|
|
987
1396
|
throw new Error(`Unable to fetch record of type ${this.type} without an id`);
|
|
988
1397
|
}
|
|
@@ -1038,7 +1447,7 @@ class CacheCapabilitiesManager {
|
|
|
1038
1447
|
});
|
|
1039
1448
|
}
|
|
1040
1449
|
notifyChange(identifier, namespace, key) {
|
|
1041
|
-
macroCondition(getGlobalConfig().
|
|
1450
|
+
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
|
|
1042
1451
|
if (!test) {
|
|
1043
1452
|
throw new Error(`Expected a stable identifier`);
|
|
1044
1453
|
}
|
|
@@ -1057,7 +1466,7 @@ class CacheCapabilitiesManager {
|
|
|
1057
1466
|
return this._store.schema;
|
|
1058
1467
|
}
|
|
1059
1468
|
setRecordId(identifier, id) {
|
|
1060
|
-
macroCondition(getGlobalConfig().
|
|
1469
|
+
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
|
|
1061
1470
|
if (!test) {
|
|
1062
1471
|
throw new Error(`Expected a stable identifier`);
|
|
1063
1472
|
}
|
|
@@ -1068,7 +1477,7 @@ class CacheCapabilitiesManager {
|
|
|
1068
1477
|
return Boolean(this._store._instanceCache.peek(identifier));
|
|
1069
1478
|
}
|
|
1070
1479
|
disconnectRecord(identifier) {
|
|
1071
|
-
macroCondition(getGlobalConfig().
|
|
1480
|
+
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
|
|
1072
1481
|
if (!test) {
|
|
1073
1482
|
throw new Error(`Expected a stable identifier`);
|
|
1074
1483
|
}
|
|
@@ -1077,7 +1486,7 @@ class CacheCapabilitiesManager {
|
|
|
1077
1486
|
this._pendingNotifies.delete(identifier);
|
|
1078
1487
|
}
|
|
1079
1488
|
}
|
|
1080
|
-
if (macroCondition(getGlobalConfig().
|
|
1489
|
+
if (macroCondition(getGlobalConfig().WarpDriveMirror.deprecations.ENABLE_LEGACY_SCHEMA_SERVICE)) {
|
|
1081
1490
|
CacheCapabilitiesManager.prototype.getSchemaDefinitionService = function () {
|
|
1082
1491
|
// FIXME add deprecation for this
|
|
1083
1492
|
return this._store.schema;
|
|
@@ -1091,7 +1500,7 @@ if (macroCondition(getGlobalConfig().WarpDrive.deprecations.ENABLE_LEGACY_SCHEMA
|
|
|
1091
1500
|
|
|
1092
1501
|
const CacheForIdentifierCache = getOrSetGlobal('CacheForIdentifierCache', new Map());
|
|
1093
1502
|
function setCacheFor(identifier, cache) {
|
|
1094
|
-
macroCondition(getGlobalConfig().
|
|
1503
|
+
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
|
|
1095
1504
|
if (!test) {
|
|
1096
1505
|
throw new Error(`Illegal set of identifier`);
|
|
1097
1506
|
}
|
|
@@ -1141,7 +1550,7 @@ function peekRecordIdentifier(record) {
|
|
|
1141
1550
|
*/
|
|
1142
1551
|
|
|
1143
1552
|
function recordIdentifierFor(record) {
|
|
1144
|
-
macroCondition(getGlobalConfig().
|
|
1553
|
+
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
|
|
1145
1554
|
if (!test) {
|
|
1146
1555
|
throw new Error(`${String(record)} is not a record instantiated by @ember-data-mirror/store`);
|
|
1147
1556
|
}
|
|
@@ -1149,7 +1558,7 @@ function recordIdentifierFor(record) {
|
|
|
1149
1558
|
return RecordCache.get(record);
|
|
1150
1559
|
}
|
|
1151
1560
|
function setRecordIdentifier(record, identifier) {
|
|
1152
|
-
if (macroCondition(getGlobalConfig().
|
|
1561
|
+
if (macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG)) {
|
|
1153
1562
|
if (RecordCache.has(record) && RecordCache.get(record) !== identifier) {
|
|
1154
1563
|
throw new Error(`${String(record)} was already assigned an identifier`);
|
|
1155
1564
|
}
|
|
@@ -1168,7 +1577,7 @@ function setRecordIdentifier(record, identifier) {
|
|
|
1168
1577
|
const StoreMap = getOrSetGlobal('StoreMap', new Map());
|
|
1169
1578
|
function storeFor(record) {
|
|
1170
1579
|
const store = StoreMap.get(record);
|
|
1171
|
-
macroCondition(getGlobalConfig().
|
|
1580
|
+
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
|
|
1172
1581
|
if (!test) {
|
|
1173
1582
|
throw new Error(`A record in a disconnected state cannot utilize the store. This typically means the record has been destroyed, most commonly by unloading it.`);
|
|
1174
1583
|
}
|
|
@@ -1176,12 +1585,13 @@ function storeFor(record) {
|
|
|
1176
1585
|
return store;
|
|
1177
1586
|
}
|
|
1178
1587
|
class InstanceCache {
|
|
1179
|
-
__instances = {
|
|
1180
|
-
record: new Map(),
|
|
1181
|
-
reference: new WeakMap()
|
|
1182
|
-
};
|
|
1183
1588
|
constructor(store) {
|
|
1184
1589
|
this.store = store;
|
|
1590
|
+
this.__instances = {
|
|
1591
|
+
record: new Map(),
|
|
1592
|
+
reference: new WeakMap(),
|
|
1593
|
+
document: new Map()
|
|
1594
|
+
};
|
|
1185
1595
|
this._storeWrapper = new CacheCapabilitiesManager(this.store);
|
|
1186
1596
|
store.identifierCache.__configureMerge((identifier, matchedIdentifier, resourceData) => {
|
|
1187
1597
|
let keptIdentifier = identifier;
|
|
@@ -1210,7 +1620,7 @@ class InstanceCache {
|
|
|
1210
1620
|
if ('id' in resourceData) {
|
|
1211
1621
|
throw new Error(`Failed to update the 'id' for the RecordIdentifier '${identifier.type}:${String(identifier.id)} (${identifier.lid})' to '${String(resourceData.id)}', because that id is already in use by '${matchedIdentifier.type}:${String(matchedIdentifier.id)} (${matchedIdentifier.lid})'`);
|
|
1212
1622
|
}
|
|
1213
|
-
macroCondition(getGlobalConfig().
|
|
1623
|
+
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
|
|
1214
1624
|
{
|
|
1215
1625
|
throw new Error(`Failed to update the RecordIdentifier '${identifier.type}:${String(identifier.id)} (${identifier.lid})' to merge with the detected duplicate identifier '${matchedIdentifier.type}:${String(matchedIdentifier.id)} (${String(matchedIdentifier.lid)})'`);
|
|
1216
1626
|
}
|
|
@@ -1236,10 +1646,18 @@ class InstanceCache {
|
|
|
1236
1646
|
peek(identifier) {
|
|
1237
1647
|
return this.__instances.record.get(identifier);
|
|
1238
1648
|
}
|
|
1649
|
+
getDocument(identifier) {
|
|
1650
|
+
let doc = this.__instances.document.get(identifier);
|
|
1651
|
+
if (!doc) {
|
|
1652
|
+
doc = new ReactiveDocument(this.store, identifier, null);
|
|
1653
|
+
this.__instances.document.set(identifier, doc);
|
|
1654
|
+
}
|
|
1655
|
+
return doc;
|
|
1656
|
+
}
|
|
1239
1657
|
getRecord(identifier, properties) {
|
|
1240
1658
|
let record = this.__instances.record.get(identifier);
|
|
1241
1659
|
if (!record) {
|
|
1242
|
-
macroCondition(getGlobalConfig().
|
|
1660
|
+
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
|
|
1243
1661
|
if (!test) {
|
|
1244
1662
|
throw new Error(`Cannot create a new record instance while the store is being destroyed`);
|
|
1245
1663
|
}
|
|
@@ -1251,9 +1669,16 @@ class InstanceCache {
|
|
|
1251
1669
|
setCacheFor(record, cache);
|
|
1252
1670
|
StoreMap.set(record, this.store);
|
|
1253
1671
|
this.__instances.record.set(identifier, record);
|
|
1254
|
-
if (macroCondition(getGlobalConfig().
|
|
1255
|
-
|
|
1256
|
-
|
|
1672
|
+
if (macroCondition(getGlobalConfig().WarpDriveMirror.activeLogging.LOG_INSTANCE_CACHE)) {
|
|
1673
|
+
if (getGlobalConfig().WarpDriveMirror.debug.LOG_INSTANCE_CACHE || globalThis.getWarpDriveRuntimeConfig().debug.LOG_INSTANCE_CACHE) {
|
|
1674
|
+
logGroup('reactive-ui', '', identifier.type, identifier.lid, 'created', '');
|
|
1675
|
+
// eslint-disable-next-line no-console
|
|
1676
|
+
console.log({
|
|
1677
|
+
properties
|
|
1678
|
+
});
|
|
1679
|
+
// eslint-disable-next-line no-console
|
|
1680
|
+
console.groupEnd();
|
|
1681
|
+
}
|
|
1257
1682
|
}
|
|
1258
1683
|
}
|
|
1259
1684
|
return record;
|
|
@@ -1290,7 +1715,7 @@ class InstanceCache {
|
|
|
1290
1715
|
}
|
|
1291
1716
|
disconnect(identifier) {
|
|
1292
1717
|
const record = this.__instances.record.get(identifier);
|
|
1293
|
-
macroCondition(getGlobalConfig().
|
|
1718
|
+
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
|
|
1294
1719
|
if (!test) {
|
|
1295
1720
|
throw new Error('Cannot destroy record while it is still materialized');
|
|
1296
1721
|
}
|
|
@@ -1299,27 +1724,30 @@ class InstanceCache {
|
|
|
1299
1724
|
this.store.identifierCache.forgetRecordIdentifier(identifier);
|
|
1300
1725
|
removeRecordDataFor(identifier);
|
|
1301
1726
|
this.store._requestCache._clearEntries(identifier);
|
|
1302
|
-
if (macroCondition(getGlobalConfig().
|
|
1303
|
-
|
|
1304
|
-
|
|
1727
|
+
if (macroCondition(getGlobalConfig().WarpDriveMirror.activeLogging.LOG_INSTANCE_CACHE)) {
|
|
1728
|
+
if (getGlobalConfig().WarpDriveMirror.debug.LOG_INSTANCE_CACHE || globalThis.getWarpDriveRuntimeConfig().debug.LOG_INSTANCE_CACHE) {
|
|
1729
|
+
log('reactive-ui', '', identifier.type, identifier.lid, 'disconnected', '');
|
|
1730
|
+
}
|
|
1305
1731
|
}
|
|
1306
1732
|
}
|
|
1307
1733
|
unloadRecord(identifier) {
|
|
1308
|
-
if (macroCondition(getGlobalConfig().
|
|
1734
|
+
if (macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG)) {
|
|
1309
1735
|
const requests = this.store.getRequestStateService().getPendingRequestsForRecord(identifier);
|
|
1310
1736
|
if (requests.some(req => {
|
|
1311
1737
|
return req.type === 'mutation';
|
|
1312
1738
|
})) {
|
|
1313
|
-
macroCondition(getGlobalConfig().
|
|
1739
|
+
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
|
|
1314
1740
|
{
|
|
1315
1741
|
throw new Error(`You can only unload a record which is not inFlight. '${String(identifier)}'`);
|
|
1316
1742
|
}
|
|
1317
1743
|
})() : {};
|
|
1318
1744
|
}
|
|
1319
1745
|
}
|
|
1320
|
-
if (macroCondition(getGlobalConfig().
|
|
1321
|
-
|
|
1322
|
-
|
|
1746
|
+
if (macroCondition(getGlobalConfig().WarpDriveMirror.activeLogging.LOG_INSTANCE_CACHE)) {
|
|
1747
|
+
if (getGlobalConfig().WarpDriveMirror.debug.LOG_INSTANCE_CACHE || globalThis.getWarpDriveRuntimeConfig().debug.LOG_INSTANCE_CACHE) {
|
|
1748
|
+
// eslint-disable-next-line no-console
|
|
1749
|
+
console.groupCollapsed(`InstanceCache: unloading record for ${String(identifier)}`);
|
|
1750
|
+
}
|
|
1323
1751
|
}
|
|
1324
1752
|
|
|
1325
1753
|
// TODO is this join still necessary?
|
|
@@ -1332,27 +1760,33 @@ class InstanceCache {
|
|
|
1332
1760
|
StoreMap.delete(record);
|
|
1333
1761
|
RecordCache.delete(record);
|
|
1334
1762
|
removeRecordDataFor(record);
|
|
1335
|
-
if (macroCondition(getGlobalConfig().
|
|
1336
|
-
|
|
1337
|
-
|
|
1763
|
+
if (macroCondition(getGlobalConfig().WarpDriveMirror.activeLogging.LOG_INSTANCE_CACHE)) {
|
|
1764
|
+
if (getGlobalConfig().WarpDriveMirror.debug.LOG_INSTANCE_CACHE || globalThis.getWarpDriveRuntimeConfig().debug.LOG_INSTANCE_CACHE) {
|
|
1765
|
+
// eslint-disable-next-line no-console
|
|
1766
|
+
console.log(`InstanceCache: destroyed record for ${String(identifier)}`);
|
|
1767
|
+
}
|
|
1338
1768
|
}
|
|
1339
1769
|
}
|
|
1340
1770
|
if (cache) {
|
|
1341
1771
|
cache.unloadRecord(identifier);
|
|
1342
1772
|
removeRecordDataFor(identifier);
|
|
1343
|
-
if (macroCondition(getGlobalConfig().
|
|
1344
|
-
|
|
1345
|
-
|
|
1773
|
+
if (macroCondition(getGlobalConfig().WarpDriveMirror.activeLogging.LOG_INSTANCE_CACHE)) {
|
|
1774
|
+
if (getGlobalConfig().WarpDriveMirror.debug.LOG_INSTANCE_CACHE || globalThis.getWarpDriveRuntimeConfig().debug.LOG_INSTANCE_CACHE) {
|
|
1775
|
+
// eslint-disable-next-line no-console
|
|
1776
|
+
console.log(`InstanceCache: destroyed cache for ${String(identifier)}`);
|
|
1777
|
+
}
|
|
1346
1778
|
}
|
|
1347
1779
|
} else {
|
|
1348
1780
|
this.disconnect(identifier);
|
|
1349
1781
|
}
|
|
1350
1782
|
this.store._requestCache._clearEntries(identifier);
|
|
1351
|
-
if (macroCondition(getGlobalConfig().
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1783
|
+
if (macroCondition(getGlobalConfig().WarpDriveMirror.activeLogging.LOG_INSTANCE_CACHE)) {
|
|
1784
|
+
if (getGlobalConfig().WarpDriveMirror.debug.LOG_INSTANCE_CACHE || globalThis.getWarpDriveRuntimeConfig().debug.LOG_INSTANCE_CACHE) {
|
|
1785
|
+
// eslint-disable-next-line no-console
|
|
1786
|
+
console.log(`InstanceCache: unloaded RecordData for ${String(identifier)}`);
|
|
1787
|
+
// eslint-disable-next-line no-console
|
|
1788
|
+
console.groupEnd();
|
|
1789
|
+
}
|
|
1356
1790
|
}
|
|
1357
1791
|
});
|
|
1358
1792
|
}
|
|
@@ -1387,7 +1821,7 @@ class InstanceCache {
|
|
|
1387
1821
|
const oldId = identifier.id;
|
|
1388
1822
|
|
|
1389
1823
|
// ID absolutely can't be missing if the oldID is empty (missing Id in response for a new record)
|
|
1390
|
-
macroCondition(getGlobalConfig().
|
|
1824
|
+
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
|
|
1391
1825
|
if (!test) {
|
|
1392
1826
|
throw new Error(`'${type}' was saved to the server, but the response does not have an id and your record does not either.`);
|
|
1393
1827
|
}
|
|
@@ -1395,7 +1829,7 @@ class InstanceCache {
|
|
|
1395
1829
|
|
|
1396
1830
|
// ID absolutely can't be different than oldID if oldID is not null
|
|
1397
1831
|
// TODO this assertion and restriction may not strictly be needed in the identifiers world
|
|
1398
|
-
macroCondition(getGlobalConfig().
|
|
1832
|
+
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
|
|
1399
1833
|
if (!test) {
|
|
1400
1834
|
throw new Error(`Cannot update the id for '${type}:${lid}' from '${String(oldId)}' to '${id}'.`);
|
|
1401
1835
|
}
|
|
@@ -1407,15 +1841,17 @@ class InstanceCache {
|
|
|
1407
1841
|
warn(`Your ${type} record was saved to the server, but the response does not have an id.`, !(oldId !== null && id === null));
|
|
1408
1842
|
return;
|
|
1409
1843
|
}
|
|
1410
|
-
if (macroCondition(getGlobalConfig().
|
|
1411
|
-
|
|
1412
|
-
|
|
1844
|
+
if (macroCondition(getGlobalConfig().WarpDriveMirror.activeLogging.LOG_INSTANCE_CACHE)) {
|
|
1845
|
+
if (getGlobalConfig().WarpDriveMirror.debug.LOG_INSTANCE_CACHE || globalThis.getWarpDriveRuntimeConfig().debug.LOG_INSTANCE_CACHE) {
|
|
1846
|
+
// eslint-disable-next-line no-console
|
|
1847
|
+
console.log(`InstanceCache: updating id to '${id}' for record ${String(identifier)}`);
|
|
1848
|
+
}
|
|
1413
1849
|
}
|
|
1414
1850
|
const existingIdentifier = this.store.identifierCache.peekRecordIdentifier({
|
|
1415
1851
|
type,
|
|
1416
1852
|
id
|
|
1417
1853
|
});
|
|
1418
|
-
macroCondition(getGlobalConfig().
|
|
1854
|
+
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
|
|
1419
1855
|
if (!test) {
|
|
1420
1856
|
throw new Error(`'${type}' was saved to the server, but the response returned the new id '${id}', which has already been used with another record.'`);
|
|
1421
1857
|
}
|
|
@@ -1480,7 +1916,7 @@ function preloadData(store, identifier, preload) {
|
|
|
1480
1916
|
function preloadRelationship(schema, preloadValue) {
|
|
1481
1917
|
const relatedType = schema.type;
|
|
1482
1918
|
if (schema.kind === 'hasMany') {
|
|
1483
|
-
macroCondition(getGlobalConfig().
|
|
1919
|
+
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
|
|
1484
1920
|
if (!test) {
|
|
1485
1921
|
throw new Error('You need to pass in an array to set a hasMany property on a record');
|
|
1486
1922
|
}
|
|
@@ -1489,7 +1925,7 @@ function preloadRelationship(schema, preloadValue) {
|
|
|
1489
1925
|
data: preloadValue.map(value => _convertPreloadRelationshipToJSON(value, relatedType))
|
|
1490
1926
|
};
|
|
1491
1927
|
}
|
|
1492
|
-
macroCondition(getGlobalConfig().
|
|
1928
|
+
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
|
|
1493
1929
|
if (!test) {
|
|
1494
1930
|
throw new Error('You should not pass in an array to set a belongsTo property on a record');
|
|
1495
1931
|
}
|
|
@@ -1726,7 +2162,9 @@ class CacheManager {
|
|
|
1726
2162
|
peek(identifier) {
|
|
1727
2163
|
return this.#cache.peek(identifier);
|
|
1728
2164
|
}
|
|
1729
|
-
|
|
2165
|
+
peekRemoteState(identifier) {
|
|
2166
|
+
return this.#cache.peekRemoteState(identifier);
|
|
2167
|
+
}
|
|
1730
2168
|
/**
|
|
1731
2169
|
* Peek the Cache for the existing request data associated with
|
|
1732
2170
|
* a cacheable request
|
|
@@ -1949,6 +2387,19 @@ class CacheManager {
|
|
|
1949
2387
|
return this.#cache.getAttr(identifier, propertyName);
|
|
1950
2388
|
}
|
|
1951
2389
|
|
|
2390
|
+
/**
|
|
2391
|
+
* Retrieve the remote state for an attribute from the cache
|
|
2392
|
+
*
|
|
2393
|
+
* @method getRemoteAttr
|
|
2394
|
+
* @public
|
|
2395
|
+
* @param identifier
|
|
2396
|
+
* @param propertyName
|
|
2397
|
+
* @return {unknown}
|
|
2398
|
+
*/
|
|
2399
|
+
getRemoteAttr(identifier, propertyName) {
|
|
2400
|
+
return this.#cache.getRemoteAttr(identifier, propertyName);
|
|
2401
|
+
}
|
|
2402
|
+
|
|
1952
2403
|
/**
|
|
1953
2404
|
* Mutate the data for an attribute in the cache
|
|
1954
2405
|
*
|
|
@@ -2073,6 +2524,19 @@ class CacheManager {
|
|
|
2073
2524
|
return this.#cache.getRelationship(identifier, propertyName);
|
|
2074
2525
|
}
|
|
2075
2526
|
|
|
2527
|
+
/**
|
|
2528
|
+
* Query the cache for the remote state of a relationship property
|
|
2529
|
+
*
|
|
2530
|
+
* @method getRelationship
|
|
2531
|
+
* @public
|
|
2532
|
+
* @param identifier
|
|
2533
|
+
* @param propertyName
|
|
2534
|
+
* @return resource relationship object
|
|
2535
|
+
*/
|
|
2536
|
+
getRemoteRelationship(identifier, propertyName) {
|
|
2537
|
+
return this.#cache.getRemoteRelationship(identifier, propertyName);
|
|
2538
|
+
}
|
|
2539
|
+
|
|
2076
2540
|
// Resource State
|
|
2077
2541
|
// ===============
|
|
2078
2542
|
|
|
@@ -2156,32 +2620,56 @@ class CacheManager {
|
|
|
2156
2620
|
/**
|
|
2157
2621
|
* @module @ember-data-mirror/store
|
|
2158
2622
|
*/
|
|
2159
|
-
|
|
2160
|
-
let tokenId = 0;
|
|
2161
|
-
const CacheOperations = new Set(['added', 'removed', 'state', 'updated']);
|
|
2623
|
+
|
|
2162
2624
|
function isCacheOperationValue(value) {
|
|
2163
|
-
return
|
|
2625
|
+
return value === 'added' || value === 'state' || value === 'updated' || value === 'removed' || value === 'invalidated';
|
|
2164
2626
|
}
|
|
2165
2627
|
function runLoopIsFlushing() {
|
|
2166
2628
|
//@ts-expect-error
|
|
2167
2629
|
return !!_backburner.currentInstance && _backburner._autorun !== true;
|
|
2168
2630
|
}
|
|
2169
|
-
function
|
|
2170
|
-
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
|
|
2631
|
+
function count(label) {
|
|
2632
|
+
// @ts-expect-error
|
|
2633
|
+
// eslint-disable-next-line
|
|
2634
|
+
globalThis.__WarpDriveMetricCountData[label] = (globalThis.__WarpDriveMetricCountData[label] || 0) + 1;
|
|
2635
|
+
}
|
|
2636
|
+
function asInternalToken(token) {
|
|
2637
|
+
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
|
|
2638
|
+
if (!test) {
|
|
2639
|
+
throw new Error(`Expected a token with a 'for' property`);
|
|
2640
|
+
}
|
|
2641
|
+
})(token && typeof token === 'function' && 'for' in token) : {};
|
|
2642
|
+
}
|
|
2643
|
+
function _unsubscribe(token, cache) {
|
|
2644
|
+
asInternalToken(token);
|
|
2645
|
+
const identifier = token.for;
|
|
2646
|
+
if (macroCondition(getGlobalConfig().WarpDriveMirror.activeLogging.LOG_NOTIFICATIONS)) {
|
|
2647
|
+
if (getGlobalConfig().WarpDriveMirror.debug.LOG_NOTIFICATIONS || globalThis.getWarpDriveRuntimeConfig().debug.LOG_NOTIFICATIONS) {
|
|
2648
|
+
if (!identifier) {
|
|
2649
|
+
// eslint-disable-next-line no-console
|
|
2650
|
+
console.log('Passed unknown unsubscribe token to unsubscribe', identifier);
|
|
2651
|
+
}
|
|
2175
2652
|
}
|
|
2176
2653
|
}
|
|
2177
2654
|
if (identifier) {
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
|
|
2655
|
+
const callbacks = cache.get(identifier);
|
|
2656
|
+
if (!callbacks) {
|
|
2657
|
+
return;
|
|
2658
|
+
}
|
|
2659
|
+
const index = callbacks.indexOf(token);
|
|
2660
|
+
if (index === -1) {
|
|
2661
|
+
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
|
|
2662
|
+
if (!test) {
|
|
2663
|
+
throw new Error(`Cannot unsubscribe a token that is not subscribed`);
|
|
2664
|
+
}
|
|
2665
|
+
})(index !== -1) : {};
|
|
2666
|
+
return;
|
|
2667
|
+
}
|
|
2668
|
+
callbacks.splice(index, 1);
|
|
2669
|
+
}
|
|
2670
|
+
}
|
|
2671
|
+
|
|
2672
|
+
/**
|
|
2185
2673
|
* The NotificationManager provides the ability to subscribe to
|
|
2186
2674
|
* changes to Cache state.
|
|
2187
2675
|
*
|
|
@@ -2198,7 +2686,6 @@ class NotificationManager {
|
|
|
2198
2686
|
this._buffered = new Map();
|
|
2199
2687
|
this._hasFlush = false;
|
|
2200
2688
|
this._cache = new Map();
|
|
2201
|
-
this._tokens = new Map();
|
|
2202
2689
|
}
|
|
2203
2690
|
|
|
2204
2691
|
/**
|
|
@@ -2230,22 +2717,31 @@ class NotificationManager {
|
|
|
2230
2717
|
*/
|
|
2231
2718
|
|
|
2232
2719
|
subscribe(identifier, callback) {
|
|
2233
|
-
macroCondition(getGlobalConfig().
|
|
2720
|
+
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
|
|
2234
2721
|
if (!test) {
|
|
2235
2722
|
throw new Error(`Expected to receive a stable Identifier to subscribe to`);
|
|
2236
2723
|
}
|
|
2237
2724
|
})(identifier === 'resource' || identifier === 'document' || isStableIdentifier(identifier) || isDocumentIdentifier(identifier)) : {};
|
|
2238
|
-
let
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2725
|
+
let callbacks = this._cache.get(identifier);
|
|
2726
|
+
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
|
|
2727
|
+
if (!test) {
|
|
2728
|
+
throw new Error(`expected to receive a valid callback`);
|
|
2729
|
+
}
|
|
2730
|
+
})(typeof callback === 'function') : {};
|
|
2731
|
+
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
|
|
2732
|
+
if (!test) {
|
|
2733
|
+
throw new Error(`cannot subscribe with the same callback twice`);
|
|
2734
|
+
}
|
|
2735
|
+
})(!callbacks || !callbacks.includes(callback)) : {};
|
|
2736
|
+
// we use the callback as the cancellation token
|
|
2737
|
+
//@ts-expect-error
|
|
2738
|
+
callback.for = identifier;
|
|
2739
|
+
if (!callbacks) {
|
|
2740
|
+
callbacks = [];
|
|
2741
|
+
this._cache.set(identifier, callbacks);
|
|
2242
2742
|
}
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
} : {};
|
|
2246
|
-
map.set(unsubToken, callback);
|
|
2247
|
-
this._tokens.set(unsubToken, identifier);
|
|
2248
|
-
return unsubToken;
|
|
2743
|
+
callbacks.push(callback);
|
|
2744
|
+
return callback;
|
|
2249
2745
|
}
|
|
2250
2746
|
|
|
2251
2747
|
/**
|
|
@@ -2257,7 +2753,7 @@ class NotificationManager {
|
|
|
2257
2753
|
*/
|
|
2258
2754
|
unsubscribe(token) {
|
|
2259
2755
|
if (!this.isDestroyed) {
|
|
2260
|
-
_unsubscribe(
|
|
2756
|
+
_unsubscribe(token, this._cache);
|
|
2261
2757
|
}
|
|
2262
2758
|
}
|
|
2263
2759
|
|
|
@@ -2273,23 +2769,21 @@ class NotificationManager {
|
|
|
2273
2769
|
*/
|
|
2274
2770
|
|
|
2275
2771
|
notify(identifier, value, key) {
|
|
2276
|
-
macroCondition(getGlobalConfig().
|
|
2772
|
+
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
|
|
2277
2773
|
if (!test) {
|
|
2278
2774
|
throw new Error(`Notify does not accept a key argument for the namespace '${value}'. Received key '${key || ''}'.`);
|
|
2279
2775
|
}
|
|
2280
2776
|
})(!key || value === 'attributes' || value === 'relationships') : {};
|
|
2281
2777
|
if (!isStableIdentifier(identifier) && !isDocumentIdentifier(identifier)) {
|
|
2282
|
-
if (macroCondition(getGlobalConfig().
|
|
2283
|
-
|
|
2284
|
-
|
|
2778
|
+
if (macroCondition(getGlobalConfig().WarpDriveMirror.activeLogging.LOG_NOTIFICATIONS)) {
|
|
2779
|
+
if (getGlobalConfig().WarpDriveMirror.debug.LOG_NOTIFICATIONS || globalThis.getWarpDriveRuntimeConfig().debug.LOG_NOTIFICATIONS) {
|
|
2780
|
+
// eslint-disable-next-line no-console
|
|
2781
|
+
console.log(`Notifying: Expected to receive a stable Identifier to notify '${value}' '${key || ''}' with, but ${String(identifier)} is not in the cache`, identifier);
|
|
2782
|
+
}
|
|
2285
2783
|
}
|
|
2286
2784
|
return false;
|
|
2287
2785
|
}
|
|
2288
|
-
|
|
2289
|
-
// eslint-disable-next-line no-console
|
|
2290
|
-
console.log(`Buffering Notify: ${String(identifier.lid)}\t${value}\t${key || ''}`);
|
|
2291
|
-
}
|
|
2292
|
-
const hasSubscribers = Boolean(this._cache.get(identifier)?.size);
|
|
2786
|
+
const hasSubscribers = Boolean(this._cache.get(identifier)?.length);
|
|
2293
2787
|
if (isCacheOperationValue(value) || hasSubscribers) {
|
|
2294
2788
|
let buffer = this._buffered.get(identifier);
|
|
2295
2789
|
if (!buffer) {
|
|
@@ -2297,7 +2791,29 @@ class NotificationManager {
|
|
|
2297
2791
|
this._buffered.set(identifier, buffer);
|
|
2298
2792
|
}
|
|
2299
2793
|
buffer.push([value, key]);
|
|
2300
|
-
|
|
2794
|
+
if (macroCondition(getGlobalConfig().WarpDriveMirror.activeLogging.LOG_METRIC_COUNTS)) {
|
|
2795
|
+
if (getGlobalConfig().WarpDriveMirror.debug.LOG_METRIC_COUNTS || globalThis.getWarpDriveRuntimeConfig().debug.LOG_METRIC_COUNTS) {
|
|
2796
|
+
count(`notify ${'type' in identifier ? identifier.type : '<document>'} ${value} ${key}`);
|
|
2797
|
+
}
|
|
2798
|
+
}
|
|
2799
|
+
if (!this._scheduleNotify()) {
|
|
2800
|
+
if (macroCondition(getGlobalConfig().WarpDriveMirror.activeLogging.LOG_NOTIFICATIONS)) {
|
|
2801
|
+
if (getGlobalConfig().WarpDriveMirror.debug.LOG_NOTIFICATIONS || globalThis.getWarpDriveRuntimeConfig().debug.LOG_NOTIFICATIONS) {
|
|
2802
|
+
log('notify', 'buffered', `${'type' in identifier ? identifier.type : 'document'}`, identifier.lid, `${value}`, key || '');
|
|
2803
|
+
}
|
|
2804
|
+
}
|
|
2805
|
+
}
|
|
2806
|
+
} else {
|
|
2807
|
+
if (macroCondition(getGlobalConfig().WarpDriveMirror.activeLogging.LOG_NOTIFICATIONS)) {
|
|
2808
|
+
if (getGlobalConfig().WarpDriveMirror.debug.LOG_NOTIFICATIONS || globalThis.getWarpDriveRuntimeConfig().debug.LOG_NOTIFICATIONS) {
|
|
2809
|
+
log('notify', 'discarded', `${'type' in identifier ? identifier.type : 'document'}`, identifier.lid, `${value}`, key || '');
|
|
2810
|
+
}
|
|
2811
|
+
}
|
|
2812
|
+
if (macroCondition(getGlobalConfig().WarpDriveMirror.activeLogging.LOG_METRIC_COUNTS)) {
|
|
2813
|
+
if (getGlobalConfig().WarpDriveMirror.debug.LOG_METRIC_COUNTS || globalThis.getWarpDriveRuntimeConfig().debug.LOG_METRIC_COUNTS) {
|
|
2814
|
+
count(`DISCARDED notify ${'type' in identifier ? identifier.type : '<document>'} ${value} ${key}`);
|
|
2815
|
+
}
|
|
2816
|
+
}
|
|
2301
2817
|
}
|
|
2302
2818
|
return hasSubscribers;
|
|
2303
2819
|
}
|
|
@@ -2308,33 +2824,36 @@ class NotificationManager {
|
|
|
2308
2824
|
const asyncFlush = this.store._enableAsyncFlush;
|
|
2309
2825
|
if (this._hasFlush) {
|
|
2310
2826
|
if (asyncFlush !== false && !runLoopIsFlushing()) {
|
|
2311
|
-
return;
|
|
2827
|
+
return false;
|
|
2312
2828
|
}
|
|
2313
2829
|
}
|
|
2314
2830
|
if (asyncFlush && !runLoopIsFlushing()) {
|
|
2315
2831
|
this._hasFlush = true;
|
|
2316
|
-
return;
|
|
2832
|
+
return false;
|
|
2317
2833
|
}
|
|
2318
2834
|
this._flush();
|
|
2835
|
+
return true;
|
|
2319
2836
|
}
|
|
2320
2837
|
_flush() {
|
|
2321
|
-
|
|
2322
|
-
|
|
2838
|
+
const buffered = this._buffered;
|
|
2839
|
+
if (buffered.size) {
|
|
2840
|
+
this._buffered = new Map();
|
|
2841
|
+
buffered.forEach((states, identifier) => {
|
|
2323
2842
|
states.forEach(args => {
|
|
2324
2843
|
// @ts-expect-error
|
|
2325
2844
|
this._flushNotification(identifier, args[0], args[1]);
|
|
2326
2845
|
});
|
|
2327
2846
|
});
|
|
2328
|
-
this._buffered = new Map();
|
|
2329
2847
|
}
|
|
2330
2848
|
this._hasFlush = false;
|
|
2331
2849
|
this._onFlushCB?.();
|
|
2332
2850
|
this._onFlushCB = undefined;
|
|
2333
2851
|
}
|
|
2334
2852
|
_flushNotification(identifier, value, key) {
|
|
2335
|
-
if (macroCondition(getGlobalConfig().
|
|
2336
|
-
|
|
2337
|
-
|
|
2853
|
+
if (macroCondition(getGlobalConfig().WarpDriveMirror.activeLogging.LOG_NOTIFICATIONS)) {
|
|
2854
|
+
if (getGlobalConfig().WarpDriveMirror.debug.LOG_NOTIFICATIONS || globalThis.getWarpDriveRuntimeConfig().debug.LOG_NOTIFICATIONS) {
|
|
2855
|
+
log('notify', '', `${'type' in identifier ? identifier.type : 'document'}`, identifier.lid, `${value}`, key || '');
|
|
2856
|
+
}
|
|
2338
2857
|
}
|
|
2339
2858
|
|
|
2340
2859
|
// TODO for documents this will need to switch based on Identifier kind
|
|
@@ -2346,11 +2865,11 @@ class NotificationManager {
|
|
|
2346
2865
|
});
|
|
2347
2866
|
}
|
|
2348
2867
|
}
|
|
2349
|
-
const
|
|
2350
|
-
if (!
|
|
2868
|
+
const callbacks = this._cache.get(identifier);
|
|
2869
|
+
if (!callbacks || !callbacks.length) {
|
|
2351
2870
|
return false;
|
|
2352
2871
|
}
|
|
2353
|
-
|
|
2872
|
+
callbacks.forEach(cb => {
|
|
2354
2873
|
// @ts-expect-error overload doesn't narrow within body
|
|
2355
2874
|
cb(identifier, value, key);
|
|
2356
2875
|
});
|
|
@@ -2358,7 +2877,6 @@ class NotificationManager {
|
|
|
2358
2877
|
}
|
|
2359
2878
|
destroy() {
|
|
2360
2879
|
this.isDestroyed = true;
|
|
2361
|
-
this._tokens.clear();
|
|
2362
2880
|
this._cache.clear();
|
|
2363
2881
|
}
|
|
2364
2882
|
}
|
|
@@ -2372,71 +2890,6 @@ class NotificationManager {
|
|
|
2372
2890
|
*/
|
|
2373
2891
|
|
|
2374
2892
|
const NativeProxy = Proxy;
|
|
2375
|
-
var __defProp = Object.defineProperty;
|
|
2376
|
-
var __export = (target, all) => {
|
|
2377
|
-
for (var name in all) __defProp(target, name, {
|
|
2378
|
-
get: all[name],
|
|
2379
|
-
enumerable: true
|
|
2380
|
-
});
|
|
2381
|
-
};
|
|
2382
|
-
|
|
2383
|
-
// src/runtime.ts
|
|
2384
|
-
var runtime_exports = {};
|
|
2385
|
-
__export(runtime_exports, {
|
|
2386
|
-
c: () => decorateClass,
|
|
2387
|
-
f: () => decorateFieldV1,
|
|
2388
|
-
g: () => decorateFieldV2,
|
|
2389
|
-
i: () => initializeDeferredDecorator,
|
|
2390
|
-
m: () => decorateMethodV1,
|
|
2391
|
-
n: () => decorateMethodV2,
|
|
2392
|
-
p: () => decoratePOJO
|
|
2393
|
-
});
|
|
2394
|
-
var deferred = /* @__PURE__ */new WeakMap();
|
|
2395
|
-
function deferDecorator(proto, prop, desc) {
|
|
2396
|
-
let map = deferred.get(proto);
|
|
2397
|
-
if (!map) {
|
|
2398
|
-
map = /* @__PURE__ */new Map();
|
|
2399
|
-
deferred.set(proto, map);
|
|
2400
|
-
}
|
|
2401
|
-
map.set(prop, desc);
|
|
2402
|
-
}
|
|
2403
|
-
function findDeferredDecorator(target, prop) {
|
|
2404
|
-
let cursor = target.prototype;
|
|
2405
|
-
while (cursor) {
|
|
2406
|
-
let desc = deferred.get(cursor)?.get(prop);
|
|
2407
|
-
if (desc) {
|
|
2408
|
-
return desc;
|
|
2409
|
-
}
|
|
2410
|
-
cursor = cursor.prototype;
|
|
2411
|
-
}
|
|
2412
|
-
}
|
|
2413
|
-
function decorateFieldV1(target, prop, decorators, initializer) {
|
|
2414
|
-
return decorateFieldV2(target.prototype, prop, decorators, initializer);
|
|
2415
|
-
}
|
|
2416
|
-
function decorateFieldV2(prototype, prop, decorators, initializer) {
|
|
2417
|
-
let desc = {
|
|
2418
|
-
configurable: true,
|
|
2419
|
-
enumerable: true,
|
|
2420
|
-
writable: true,
|
|
2421
|
-
initializer: null
|
|
2422
|
-
};
|
|
2423
|
-
if (initializer) {
|
|
2424
|
-
desc.initializer = initializer;
|
|
2425
|
-
}
|
|
2426
|
-
for (let decorator of decorators) {
|
|
2427
|
-
desc = decorator(prototype, prop, desc) || desc;
|
|
2428
|
-
}
|
|
2429
|
-
if (desc.initializer === void 0) {
|
|
2430
|
-
Object.defineProperty(prototype, prop, desc);
|
|
2431
|
-
} else {
|
|
2432
|
-
deferDecorator(prototype, prop, desc);
|
|
2433
|
-
}
|
|
2434
|
-
}
|
|
2435
|
-
function decorateMethodV1({
|
|
2436
|
-
prototype
|
|
2437
|
-
}, prop, decorators) {
|
|
2438
|
-
return decorateMethodV2(prototype, prop, decorators);
|
|
2439
|
-
}
|
|
2440
2893
|
function decorateMethodV2(prototype, prop, decorators) {
|
|
2441
2894
|
const origDesc = Object.getOwnPropertyDescriptor(prototype, prop);
|
|
2442
2895
|
let desc = {
|
|
@@ -2451,46 +2904,6 @@ function decorateMethodV2(prototype, prop, decorators) {
|
|
|
2451
2904
|
}
|
|
2452
2905
|
Object.defineProperty(prototype, prop, desc);
|
|
2453
2906
|
}
|
|
2454
|
-
function initializeDeferredDecorator(target, prop) {
|
|
2455
|
-
let desc = findDeferredDecorator(target.constructor, prop);
|
|
2456
|
-
if (desc) {
|
|
2457
|
-
Object.defineProperty(target, prop, {
|
|
2458
|
-
enumerable: desc.enumerable,
|
|
2459
|
-
configurable: desc.configurable,
|
|
2460
|
-
writable: desc.writable,
|
|
2461
|
-
value: desc.initializer ? desc.initializer.call(target) : void 0
|
|
2462
|
-
});
|
|
2463
|
-
}
|
|
2464
|
-
}
|
|
2465
|
-
function decorateClass(target, decorators) {
|
|
2466
|
-
return decorators.reduce((accum, decorator) => decorator(accum) || accum, target);
|
|
2467
|
-
}
|
|
2468
|
-
function decoratePOJO(pojo, decorated) {
|
|
2469
|
-
for (let [type, prop, decorators] of decorated) {
|
|
2470
|
-
if (type === "field") {
|
|
2471
|
-
decoratePojoField(pojo, prop, decorators);
|
|
2472
|
-
} else {
|
|
2473
|
-
decorateMethodV2(pojo, prop, decorators);
|
|
2474
|
-
}
|
|
2475
|
-
}
|
|
2476
|
-
return pojo;
|
|
2477
|
-
}
|
|
2478
|
-
function decoratePojoField(pojo, prop, decorators) {
|
|
2479
|
-
let desc = {
|
|
2480
|
-
configurable: true,
|
|
2481
|
-
enumerable: true,
|
|
2482
|
-
writable: true,
|
|
2483
|
-
initializer: () => Object.getOwnPropertyDescriptor(pojo, prop)?.value
|
|
2484
|
-
};
|
|
2485
|
-
for (let decorator of decorators) {
|
|
2486
|
-
desc = decorator(pojo, prop, desc) || desc;
|
|
2487
|
-
}
|
|
2488
|
-
if (desc.initializer) {
|
|
2489
|
-
desc.value = desc.initializer.call(pojo);
|
|
2490
|
-
delete desc.initializer;
|
|
2491
|
-
}
|
|
2492
|
-
Object.defineProperty(pojo, prop, desc);
|
|
2493
|
-
}
|
|
2494
2907
|
|
|
2495
2908
|
/**
|
|
2496
2909
|
@module @ember-data-mirror/store
|
|
@@ -2527,7 +2940,7 @@ function safeForEach(instance, arr, store, callback, target) {
|
|
|
2527
2940
|
}
|
|
2528
2941
|
// clone to prevent mutation
|
|
2529
2942
|
arr = arr.slice();
|
|
2530
|
-
macroCondition(getGlobalConfig().
|
|
2943
|
+
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
|
|
2531
2944
|
if (!test) {
|
|
2532
2945
|
throw new Error('`forEach` expects a function as first argument.');
|
|
2533
2946
|
}
|
|
@@ -2577,6 +2990,7 @@ class IdentifierArray {
|
|
|
2577
2990
|
isDestroying = false;
|
|
2578
2991
|
isDestroyed = false;
|
|
2579
2992
|
_updatingPromise = null;
|
|
2993
|
+
identifier;
|
|
2580
2994
|
[IS_COLLECTION] = true;
|
|
2581
2995
|
[SOURCE];
|
|
2582
2996
|
[NOTIFY]() {
|
|
@@ -2615,6 +3029,7 @@ class IdentifierArray {
|
|
|
2615
3029
|
this.modelName = options.type;
|
|
2616
3030
|
this.store = options.store;
|
|
2617
3031
|
this._manager = options.manager;
|
|
3032
|
+
this.identifier = options.identifier || null;
|
|
2618
3033
|
this[SOURCE] = options.identifiers;
|
|
2619
3034
|
this[ARRAY_SIGNAL] = createSignal(this, 'length');
|
|
2620
3035
|
const store = options.store;
|
|
@@ -2681,7 +3096,7 @@ class IdentifierArray {
|
|
|
2681
3096
|
// array functions must run through Reflect to work properly
|
|
2682
3097
|
// binding via other means will not work.
|
|
2683
3098
|
if (!options.allowMutation) {
|
|
2684
|
-
macroCondition(getGlobalConfig().
|
|
3099
|
+
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
|
|
2685
3100
|
if (!test) {
|
|
2686
3101
|
throw new Error(`Mutating this array of records via ${String(prop)} is not allowed.`);
|
|
2687
3102
|
}
|
|
@@ -2689,7 +3104,7 @@ class IdentifierArray {
|
|
|
2689
3104
|
return;
|
|
2690
3105
|
}
|
|
2691
3106
|
const args = Array.prototype.slice.call(arguments);
|
|
2692
|
-
macroCondition(getGlobalConfig().
|
|
3107
|
+
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
|
|
2693
3108
|
if (!test) {
|
|
2694
3109
|
throw new Error(`Cannot start a new array transaction while a previous transaction is underway`);
|
|
2695
3110
|
}
|
|
@@ -2735,7 +3150,7 @@ class IdentifierArray {
|
|
|
2735
3150
|
} else if (transaction) {
|
|
2736
3151
|
return Reflect.set(target, prop, value);
|
|
2737
3152
|
} else {
|
|
2738
|
-
macroCondition(getGlobalConfig().
|
|
3153
|
+
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
|
|
2739
3154
|
{
|
|
2740
3155
|
throw new Error(`unexpected length set`);
|
|
2741
3156
|
}
|
|
@@ -2762,7 +3177,7 @@ class IdentifierArray {
|
|
|
2762
3177
|
if (index === null || index > target.length) {
|
|
2763
3178
|
if (index !== null && transaction) {
|
|
2764
3179
|
const identifier = recordIdentifierFor(value);
|
|
2765
|
-
macroCondition(getGlobalConfig().
|
|
3180
|
+
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
|
|
2766
3181
|
if (!test) {
|
|
2767
3182
|
throw new Error(`Cannot set index ${index} past the end of the array.`);
|
|
2768
3183
|
}
|
|
@@ -2777,7 +3192,7 @@ class IdentifierArray {
|
|
|
2777
3192
|
return false;
|
|
2778
3193
|
}
|
|
2779
3194
|
if (!options.allowMutation) {
|
|
2780
|
-
macroCondition(getGlobalConfig().
|
|
3195
|
+
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
|
|
2781
3196
|
if (!test) {
|
|
2782
3197
|
throw new Error(`Mutating ${String(prop)} on this Array is not allowed.`);
|
|
2783
3198
|
}
|
|
@@ -2785,9 +3200,8 @@ class IdentifierArray {
|
|
|
2785
3200
|
return false;
|
|
2786
3201
|
}
|
|
2787
3202
|
const original = target[index];
|
|
2788
|
-
const newIdentifier = extractIdentifierFromRecord$
|
|
2789
|
-
|
|
2790
|
-
macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
|
|
3203
|
+
const newIdentifier = extractIdentifierFromRecord$2(value);
|
|
3204
|
+
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
|
|
2791
3205
|
if (!test) {
|
|
2792
3206
|
throw new Error(`Expected a record`);
|
|
2793
3207
|
}
|
|
@@ -2816,7 +3230,7 @@ class IdentifierArray {
|
|
|
2816
3230
|
return true;
|
|
2817
3231
|
},
|
|
2818
3232
|
deleteProperty(target, prop) {
|
|
2819
|
-
macroCondition(getGlobalConfig().
|
|
3233
|
+
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
|
|
2820
3234
|
if (!test) {
|
|
2821
3235
|
throw new Error(`Deleting keys on managed arrays is disallowed`);
|
|
2822
3236
|
}
|
|
@@ -2827,9 +3241,18 @@ class IdentifierArray {
|
|
|
2827
3241
|
return Reflect.deleteProperty(target, prop);
|
|
2828
3242
|
},
|
|
2829
3243
|
getPrototypeOf() {
|
|
2830
|
-
return
|
|
3244
|
+
return Array.prototype;
|
|
2831
3245
|
}
|
|
2832
3246
|
});
|
|
3247
|
+
if (macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG)) {
|
|
3248
|
+
Object.defineProperty(this, '__SHOW_ME_THE_DATA_(debug mode only)__', {
|
|
3249
|
+
enumerable: false,
|
|
3250
|
+
configurable: true,
|
|
3251
|
+
get() {
|
|
3252
|
+
return proxy.slice();
|
|
3253
|
+
}
|
|
3254
|
+
});
|
|
3255
|
+
}
|
|
2833
3256
|
createArrayTags(proxy, _SIGNAL);
|
|
2834
3257
|
this[NOTIFY] = this[NOTIFY].bind(proxy);
|
|
2835
3258
|
return proxy;
|
|
@@ -2872,7 +3295,7 @@ class IdentifierArray {
|
|
|
2872
3295
|
is finished.
|
|
2873
3296
|
*/
|
|
2874
3297
|
_update() {
|
|
2875
|
-
macroCondition(getGlobalConfig().
|
|
3298
|
+
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
|
|
2876
3299
|
if (!test) {
|
|
2877
3300
|
throw new Error(`_update cannot be used with this array`);
|
|
2878
3301
|
}
|
|
@@ -2915,7 +3338,11 @@ const desc = {
|
|
|
2915
3338
|
enumerable: true,
|
|
2916
3339
|
configurable: false,
|
|
2917
3340
|
get: function () {
|
|
2918
|
-
|
|
3341
|
+
// here to support computed chains
|
|
3342
|
+
// and {{#each}}
|
|
3343
|
+
if (macroCondition(getGlobalConfig().WarpDriveMirror.deprecations.DEPRECATE_COMPUTED_CHAINS)) {
|
|
3344
|
+
return this;
|
|
3345
|
+
}
|
|
2919
3346
|
}
|
|
2920
3347
|
};
|
|
2921
3348
|
compat(desc);
|
|
@@ -2935,12 +3362,12 @@ class Collection extends IdentifierArray {
|
|
|
2935
3362
|
} = this;
|
|
2936
3363
|
|
|
2937
3364
|
// TODO save options from initial request?
|
|
2938
|
-
macroCondition(getGlobalConfig().
|
|
3365
|
+
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
|
|
2939
3366
|
if (!test) {
|
|
2940
3367
|
throw new Error(`update cannot be used with this array`);
|
|
2941
3368
|
}
|
|
2942
3369
|
})(this.modelName) : {};
|
|
2943
|
-
macroCondition(getGlobalConfig().
|
|
3370
|
+
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
|
|
2944
3371
|
if (!test) {
|
|
2945
3372
|
throw new Error(`update cannot be used with no query`);
|
|
2946
3373
|
}
|
|
@@ -2966,8 +3393,8 @@ Collection.prototype.query = null;
|
|
|
2966
3393
|
// Ensure instanceof works correctly
|
|
2967
3394
|
// Object.setPrototypeOf(IdentifierArray.prototype, Array.prototype);
|
|
2968
3395
|
|
|
2969
|
-
function assertRecordPassedToHasMany(record) {
|
|
2970
|
-
macroCondition(getGlobalConfig().
|
|
3396
|
+
function assertRecordPassedToHasMany$1(record) {
|
|
3397
|
+
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
|
|
2971
3398
|
if (!test) {
|
|
2972
3399
|
throw new Error(`All elements of a hasMany relationship must be instances of Model, you passed $${typeof record}`);
|
|
2973
3400
|
}
|
|
@@ -2980,11 +3407,11 @@ function assertRecordPassedToHasMany(record) {
|
|
|
2980
3407
|
}
|
|
2981
3408
|
}()) : {};
|
|
2982
3409
|
}
|
|
2983
|
-
function extractIdentifierFromRecord$
|
|
3410
|
+
function extractIdentifierFromRecord$2(record) {
|
|
2984
3411
|
if (!record) {
|
|
2985
3412
|
return null;
|
|
2986
3413
|
}
|
|
2987
|
-
assertRecordPassedToHasMany(record);
|
|
3414
|
+
assertRecordPassedToHasMany$1(record);
|
|
2988
3415
|
return recordIdentifierFor(record);
|
|
2989
3416
|
}
|
|
2990
3417
|
|
|
@@ -3059,6 +3486,12 @@ class RecordArrayManager {
|
|
|
3059
3486
|
this._identifiers = new Map();
|
|
3060
3487
|
this._set = new Map();
|
|
3061
3488
|
this._visibilitySet = new Map();
|
|
3489
|
+
this._subscription = this.store.notifications.subscribe('document', (identifier, type) => {
|
|
3490
|
+
if (type === 'updated' && this._keyedArrays.has(identifier.lid)) {
|
|
3491
|
+
const array = this._keyedArrays.get(identifier.lid);
|
|
3492
|
+
this.dirtyArray(array, 0, true);
|
|
3493
|
+
}
|
|
3494
|
+
});
|
|
3062
3495
|
this._subscription = this.store.notifications.subscribe('resource', (identifier, type) => {
|
|
3063
3496
|
if (type === 'added') {
|
|
3064
3497
|
this._visibilitySet.set(identifier, true);
|
|
@@ -3073,11 +3506,38 @@ class RecordArrayManager {
|
|
|
3073
3506
|
}
|
|
3074
3507
|
_syncArray(array) {
|
|
3075
3508
|
const pending = this._pending.get(array);
|
|
3076
|
-
|
|
3509
|
+
const isRequestArray = isCollection(array);
|
|
3510
|
+
if (!isRequestArray && !pending || this.isDestroying || this.isDestroyed) {
|
|
3077
3511
|
return;
|
|
3078
3512
|
}
|
|
3079
|
-
|
|
3080
|
-
|
|
3513
|
+
|
|
3514
|
+
// first flush any staged changes
|
|
3515
|
+
if (pending) {
|
|
3516
|
+
sync(array, pending, this._set.get(array));
|
|
3517
|
+
this._pending.delete(array);
|
|
3518
|
+
}
|
|
3519
|
+
|
|
3520
|
+
// then pull new state if required
|
|
3521
|
+
if (isRequestArray) {
|
|
3522
|
+
const tag = array[ARRAY_SIGNAL];
|
|
3523
|
+
if (tag.reason === 'cache-sync') {
|
|
3524
|
+
tag.reason = null;
|
|
3525
|
+
const doc = this.store.cache.peek(array.identifier);
|
|
3526
|
+
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
|
|
3527
|
+
if (!test) {
|
|
3528
|
+
throw new Error(`Expected to find a document for ${array.identifier.lid} but found none`);
|
|
3529
|
+
}
|
|
3530
|
+
})(doc) : {};
|
|
3531
|
+
const data = !('data' in doc) || !Array.isArray(doc.data) ? [] : doc.data;
|
|
3532
|
+
// TODO technically we should destroy here if
|
|
3533
|
+
// !('data' in doc) || !Array.isArray(doc.data)
|
|
3534
|
+
// is true.
|
|
3535
|
+
this.populateManagedArray(array, data, null);
|
|
3536
|
+
}
|
|
3537
|
+
}
|
|
3538
|
+
}
|
|
3539
|
+
mutate(mutation) {
|
|
3540
|
+
this.store.cache.mutate(mutation);
|
|
3081
3541
|
}
|
|
3082
3542
|
|
|
3083
3543
|
/**
|
|
@@ -3113,9 +3573,13 @@ class RecordArrayManager {
|
|
|
3113
3573
|
}
|
|
3114
3574
|
return array;
|
|
3115
3575
|
}
|
|
3116
|
-
|
|
3576
|
+
getCollection(config) {
|
|
3577
|
+
if (config.identifier && this._keyedArrays.has(config.identifier.lid)) {
|
|
3578
|
+
return this._keyedArrays.get(config.identifier.lid);
|
|
3579
|
+
}
|
|
3117
3580
|
const options = {
|
|
3118
3581
|
type: config.type,
|
|
3582
|
+
identifier: config.identifier || null,
|
|
3119
3583
|
links: config.doc?.links || null,
|
|
3120
3584
|
meta: config.doc?.meta || null,
|
|
3121
3585
|
query: config.query || null,
|
|
@@ -3128,16 +3592,22 @@ class RecordArrayManager {
|
|
|
3128
3592
|
const array = new Collection(options);
|
|
3129
3593
|
this._managed.add(array);
|
|
3130
3594
|
this._set.set(array, new Set(options.identifiers || []));
|
|
3595
|
+
if (config.identifier) {
|
|
3596
|
+
this._keyedArrays.set(config.identifier.lid, array);
|
|
3597
|
+
}
|
|
3131
3598
|
if (config.identifiers) {
|
|
3132
3599
|
associate(this._identifiers, array, config.identifiers);
|
|
3133
3600
|
}
|
|
3134
3601
|
return array;
|
|
3135
3602
|
}
|
|
3136
|
-
dirtyArray(array, delta) {
|
|
3603
|
+
dirtyArray(array, delta, shouldSyncFromCache) {
|
|
3137
3604
|
if (array === FAKE_ARR) {
|
|
3138
3605
|
return;
|
|
3139
3606
|
}
|
|
3140
3607
|
const tag = array[ARRAY_SIGNAL];
|
|
3608
|
+
if (shouldSyncFromCache) {
|
|
3609
|
+
tag.reason = 'cache-sync';
|
|
3610
|
+
}
|
|
3141
3611
|
if (!tag.shouldReset) {
|
|
3142
3612
|
tag.shouldReset = true;
|
|
3143
3613
|
addTransactionCB(array[NOTIFY]);
|
|
@@ -3196,13 +3666,18 @@ class RecordArrayManager {
|
|
|
3196
3666
|
populateManagedArray(array, identifiers, payload) {
|
|
3197
3667
|
this._pending.delete(array);
|
|
3198
3668
|
const source = array[SOURCE];
|
|
3669
|
+
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
|
|
3670
|
+
if (!test) {
|
|
3671
|
+
throw new Error(`The new state of the collection should not be using the same array reference as the original state.`);
|
|
3672
|
+
}
|
|
3673
|
+
})(source !== identifiers) : {};
|
|
3199
3674
|
const old = source.slice();
|
|
3200
3675
|
source.length = 0;
|
|
3201
3676
|
fastPush(source, identifiers);
|
|
3202
3677
|
this._set.set(array, new Set(identifiers));
|
|
3203
3678
|
notifyArray(array);
|
|
3204
|
-
array.meta = payload
|
|
3205
|
-
array.links = payload
|
|
3679
|
+
array.meta = payload?.meta || null;
|
|
3680
|
+
array.links = payload?.links || null;
|
|
3206
3681
|
array.isLoaded = true;
|
|
3207
3682
|
disassociate(this._identifiers, array, old);
|
|
3208
3683
|
associate(this._identifiers, array, identifiers);
|
|
@@ -3216,7 +3691,7 @@ class RecordArrayManager {
|
|
|
3216
3691
|
changes.delete(identifier);
|
|
3217
3692
|
} else {
|
|
3218
3693
|
changes.set(identifier, 'add');
|
|
3219
|
-
this.dirtyArray(array, changes.size);
|
|
3694
|
+
this.dirtyArray(array, changes.size, false);
|
|
3220
3695
|
}
|
|
3221
3696
|
});
|
|
3222
3697
|
}
|
|
@@ -3230,7 +3705,7 @@ class RecordArrayManager {
|
|
|
3230
3705
|
changes.delete(identifier);
|
|
3231
3706
|
} else {
|
|
3232
3707
|
changes.set(identifier, 'del');
|
|
3233
|
-
this.dirtyArray(array, changes.size);
|
|
3708
|
+
this.dirtyArray(array, changes.size, false);
|
|
3234
3709
|
}
|
|
3235
3710
|
});
|
|
3236
3711
|
}
|
|
@@ -3335,6 +3810,9 @@ function sync(array, changes, arraySet) {
|
|
|
3335
3810
|
*/
|
|
3336
3811
|
}
|
|
3337
3812
|
}
|
|
3813
|
+
function isCollection(array) {
|
|
3814
|
+
return array.identifier !== null;
|
|
3815
|
+
}
|
|
3338
3816
|
|
|
3339
3817
|
/**
|
|
3340
3818
|
* @module @ember-data-mirror/store
|
|
@@ -3342,7 +3820,7 @@ function sync(array, changes, arraySet) {
|
|
|
3342
3820
|
|
|
3343
3821
|
const Touching = getOrSetGlobal('Touching', Symbol('touching'));
|
|
3344
3822
|
const RequestPromise = getOrSetGlobal('RequestPromise', Symbol('promise'));
|
|
3345
|
-
const EMPTY_ARR = macroCondition(getGlobalConfig().
|
|
3823
|
+
const EMPTY_ARR = macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? Object.freeze([]) : [];
|
|
3346
3824
|
function hasRecordIdentifier(op) {
|
|
3347
3825
|
return 'recordIdentifier' in op;
|
|
3348
3826
|
}
|
|
@@ -3413,7 +3891,7 @@ class RequestStateService {
|
|
|
3413
3891
|
throw error;
|
|
3414
3892
|
});
|
|
3415
3893
|
}
|
|
3416
|
-
macroCondition(getGlobalConfig().
|
|
3894
|
+
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
|
|
3417
3895
|
{
|
|
3418
3896
|
throw new Error(`Expected a well formed query`);
|
|
3419
3897
|
}
|
|
@@ -3550,12 +4028,12 @@ function constructResource(type, id, lid) {
|
|
|
3550
4028
|
if ('id' in resource) {
|
|
3551
4029
|
resource.id = coerceId(resource.id);
|
|
3552
4030
|
}
|
|
3553
|
-
macroCondition(getGlobalConfig().
|
|
4031
|
+
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
|
|
3554
4032
|
if (!test) {
|
|
3555
4033
|
throw new Error('Expected either id or lid to be a valid string');
|
|
3556
4034
|
}
|
|
3557
4035
|
})('id' in resource && isNonEmptyString(resource.id) || isNonEmptyString(resource.lid)) : {};
|
|
3558
|
-
macroCondition(getGlobalConfig().
|
|
4036
|
+
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
|
|
3559
4037
|
if (!test) {
|
|
3560
4038
|
throw new Error('if id is present, the type must be a string');
|
|
3561
4039
|
}
|
|
@@ -3571,7 +4049,7 @@ function constructResource(type, id, lid) {
|
|
|
3571
4049
|
}
|
|
3572
4050
|
throw new Error('Expected either id or lid to be a valid string');
|
|
3573
4051
|
}
|
|
3574
|
-
macroCondition(getGlobalConfig().
|
|
4052
|
+
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
|
|
3575
4053
|
if (!test) {
|
|
3576
4054
|
throw new Error('type must be a string');
|
|
3577
4055
|
}
|
|
@@ -3595,6 +4073,118 @@ function constructResource(type, id, lid) {
|
|
|
3595
4073
|
*/
|
|
3596
4074
|
// this import location is deprecated but breaks in 4.8 and older
|
|
3597
4075
|
|
|
4076
|
+
// @ts-expect-error adding to globalThis
|
|
4077
|
+
globalThis.setWarpDriveLogging = setLogging;
|
|
4078
|
+
|
|
4079
|
+
// @ts-expect-error adding to globalThis
|
|
4080
|
+
globalThis.getWarpDriveRuntimeConfig = getRuntimeConfig;
|
|
4081
|
+
if (macroCondition(getGlobalConfig().WarpDriveMirror.activeLogging.LOG_METRIC_COUNTS)) {
|
|
4082
|
+
if (getGlobalConfig().WarpDriveMirror.debug.LOG_METRIC_COUNTS || globalThis.getWarpDriveRuntimeConfig().debug.LOG_METRIC_COUNTS) {
|
|
4083
|
+
// @ts-expect-error adding to globalThis
|
|
4084
|
+
// eslint-disable-next-line
|
|
4085
|
+
globalThis.__WarpDriveMetricCountData = globalThis.__WarpDriveMetricCountData || {};
|
|
4086
|
+
|
|
4087
|
+
// @ts-expect-error adding to globalThis
|
|
4088
|
+
globalThis.getWarpDriveMetricCounts = () => {
|
|
4089
|
+
// @ts-expect-error
|
|
4090
|
+
// eslint-disable-next-line
|
|
4091
|
+
return structuredClone(globalThis.__WarpDriveMetricCountData);
|
|
4092
|
+
};
|
|
4093
|
+
|
|
4094
|
+
// @ts-expect-error adding to globalThis
|
|
4095
|
+
globalThis.resetWarpDriveMetricCounts = () => {
|
|
4096
|
+
// @ts-expect-error
|
|
4097
|
+
globalThis.__WarpDriveMetricCountData = {};
|
|
4098
|
+
};
|
|
4099
|
+
if (macroCondition(getGlobalConfig().WarpDriveMirror.activeLogging.__INTERNAL_LOG_NATIVE_MAP_SET_COUNTS)) {
|
|
4100
|
+
if (getGlobalConfig().WarpDriveMirror.debug.__INTERNAL_LOG_NATIVE_MAP_SET_COUNTS || globalThis.getWarpDriveRuntimeConfig().debug.__INTERNAL_LOG_NATIVE_MAP_SET_COUNTS) {
|
|
4101
|
+
// @ts-expect-error adding to globalThis
|
|
4102
|
+
globalThis.__primitiveInstanceId = 0;
|
|
4103
|
+
function interceptAndLog(klassName, methodName) {
|
|
4104
|
+
const klass = globalThis[klassName];
|
|
4105
|
+
if (methodName === 'constructor') {
|
|
4106
|
+
const instantiationLabel = `new ${klassName}()`;
|
|
4107
|
+
// @ts-expect-error
|
|
4108
|
+
globalThis[klassName] = class extends klass {
|
|
4109
|
+
// @ts-expect-error
|
|
4110
|
+
constructor(...args) {
|
|
4111
|
+
// eslint-disable-next-line
|
|
4112
|
+
super(...args);
|
|
4113
|
+
// @ts-expect-error
|
|
4114
|
+
|
|
4115
|
+
const instanceId = globalThis.__primitiveInstanceId++;
|
|
4116
|
+
// @ts-expect-error
|
|
4117
|
+
// eslint-disable-next-line
|
|
4118
|
+
globalThis.__WarpDriveMetricCountData[instantiationLabel] =
|
|
4119
|
+
// @ts-expect-error
|
|
4120
|
+
// eslint-disable-next-line
|
|
4121
|
+
(globalThis.__WarpDriveMetricCountData[instantiationLabel] || 0) + 1;
|
|
4122
|
+
// @ts-expect-error
|
|
4123
|
+
this.instanceName = `${klassName}:${instanceId} - ${new Error().stack?.split('\n')[2]}`;
|
|
4124
|
+
}
|
|
4125
|
+
};
|
|
4126
|
+
} else {
|
|
4127
|
+
// @ts-expect-error
|
|
4128
|
+
// eslint-disable-next-line
|
|
4129
|
+
const original = klass.prototype[methodName];
|
|
4130
|
+
const logName = `${klassName}.${methodName}`;
|
|
4131
|
+
|
|
4132
|
+
// @ts-expect-error
|
|
4133
|
+
klass.prototype[methodName] = function (...args) {
|
|
4134
|
+
// @ts-expect-error
|
|
4135
|
+
// eslint-disable-next-line
|
|
4136
|
+
globalThis.__WarpDriveMetricCountData[logName] = (globalThis.__WarpDriveMetricCountData[logName] || 0) + 1;
|
|
4137
|
+
// @ts-expect-error
|
|
4138
|
+
const {
|
|
4139
|
+
instanceName
|
|
4140
|
+
} = this;
|
|
4141
|
+
if (!instanceName) {
|
|
4142
|
+
// @ts-expect-error
|
|
4143
|
+
const instanceId = globalThis.__primitiveInstanceId++;
|
|
4144
|
+
// @ts-expect-error
|
|
4145
|
+
this.instanceName = `${klassName}.${methodName}:${instanceId} - ${new Error().stack?.split('\n')[2]}`;
|
|
4146
|
+
}
|
|
4147
|
+
const instanceLogName = `${logName} (${instanceName})`;
|
|
4148
|
+
// @ts-expect-error
|
|
4149
|
+
// eslint-disable-next-line
|
|
4150
|
+
globalThis.__WarpDriveMetricCountData[instanceLogName] =
|
|
4151
|
+
// @ts-expect-error
|
|
4152
|
+
// eslint-disable-next-line
|
|
4153
|
+
(globalThis.__WarpDriveMetricCountData[instanceLogName] || 0) + 1;
|
|
4154
|
+
// eslint-disable-next-line
|
|
4155
|
+
return original.apply(this, args);
|
|
4156
|
+
};
|
|
4157
|
+
}
|
|
4158
|
+
}
|
|
4159
|
+
interceptAndLog('Set', 'constructor');
|
|
4160
|
+
interceptAndLog('Set', 'add');
|
|
4161
|
+
interceptAndLog('Set', 'delete');
|
|
4162
|
+
interceptAndLog('Set', 'has');
|
|
4163
|
+
interceptAndLog('Set', 'set');
|
|
4164
|
+
interceptAndLog('Set', 'get');
|
|
4165
|
+
interceptAndLog('Map', 'constructor');
|
|
4166
|
+
interceptAndLog('Map', 'set');
|
|
4167
|
+
interceptAndLog('Map', 'delete');
|
|
4168
|
+
interceptAndLog('Map', 'has');
|
|
4169
|
+
interceptAndLog('Map', 'add');
|
|
4170
|
+
interceptAndLog('Map', 'get');
|
|
4171
|
+
interceptAndLog('WeakSet', 'constructor');
|
|
4172
|
+
interceptAndLog('WeakSet', 'add');
|
|
4173
|
+
interceptAndLog('WeakSet', 'delete');
|
|
4174
|
+
interceptAndLog('WeakSet', 'has');
|
|
4175
|
+
interceptAndLog('WeakSet', 'set');
|
|
4176
|
+
interceptAndLog('WeakSet', 'get');
|
|
4177
|
+
interceptAndLog('WeakMap', 'constructor');
|
|
4178
|
+
interceptAndLog('WeakMap', 'set');
|
|
4179
|
+
interceptAndLog('WeakMap', 'delete');
|
|
4180
|
+
interceptAndLog('WeakMap', 'has');
|
|
4181
|
+
interceptAndLog('WeakMap', 'add');
|
|
4182
|
+
interceptAndLog('WeakMap', 'get');
|
|
4183
|
+
}
|
|
4184
|
+
}
|
|
4185
|
+
}
|
|
4186
|
+
}
|
|
4187
|
+
|
|
3598
4188
|
// `AwaitedKeys` is needed here to resolve any promise types like `PromiseBelongsTo`.
|
|
3599
4189
|
|
|
3600
4190
|
/**
|
|
@@ -3639,7 +4229,8 @@ const EmptyClass = class {
|
|
|
3639
4229
|
// eslint-disable-next-line @typescript-eslint/no-useless-constructor
|
|
3640
4230
|
constructor(args) {}
|
|
3641
4231
|
};
|
|
3642
|
-
const
|
|
4232
|
+
const _BaseClass = macroCondition(dependencySatisfies('ember-source', '*')) ? macroCondition(getGlobalConfig().WarpDriveMirror.deprecations.DEPRECATE_STORE_EXTENDS_EMBER_OBJECT) ? importSync('@ember/object') : EmptyClass : EmptyClass;
|
|
4233
|
+
const BaseClass = _BaseClass.default ? _BaseClass.default : _BaseClass;
|
|
3643
4234
|
if (BaseClass !== EmptyClass) {
|
|
3644
4235
|
deprecate(`The Store class extending from EmberObject is deprecated.
|
|
3645
4236
|
Please remove usage of EmberObject APIs and mark your class as not requiring it.
|
|
@@ -3660,8 +4251,9 @@ const app = new EmberApp(defaults, {
|
|
|
3660
4251
|
id: 'ember-data-mirror:deprecate-store-extends-ember-object',
|
|
3661
4252
|
until: '6.0',
|
|
3662
4253
|
for: 'ember-data-mirror',
|
|
4254
|
+
url: 'https://deprecations.emberjs.com/id/ember-data-deprecate-store-extends-ember-object',
|
|
3663
4255
|
since: {
|
|
3664
|
-
available: '
|
|
4256
|
+
available: '4.13',
|
|
3665
4257
|
enabled: '5.4'
|
|
3666
4258
|
}
|
|
3667
4259
|
});
|
|
@@ -3721,12 +4313,9 @@ class Store extends BaseClass {
|
|
|
3721
4313
|
* import Fetch from '@ember-data-mirror/request/fetch';
|
|
3722
4314
|
*
|
|
3723
4315
|
* class extends Store {
|
|
3724
|
-
*
|
|
3725
|
-
*
|
|
3726
|
-
*
|
|
3727
|
-
* this.requestManager.use([Fetch]);
|
|
3728
|
-
* this.requestManager.useCache(CacheHandler);
|
|
3729
|
-
* }
|
|
4316
|
+
* requestManager = new RequestManager()
|
|
4317
|
+
* .use([Fetch])
|
|
4318
|
+
* .useCache(CacheHandler);
|
|
3730
4319
|
* }
|
|
3731
4320
|
* ```
|
|
3732
4321
|
*
|
|
@@ -3766,6 +4355,17 @@ class Store extends BaseClass {
|
|
|
3766
4355
|
|
|
3767
4356
|
// Private
|
|
3768
4357
|
|
|
4358
|
+
/**
|
|
4359
|
+
* Async flush buffers notifications until flushed
|
|
4360
|
+
* by finalization of a future configured by store.request
|
|
4361
|
+
*
|
|
4362
|
+
* This is useful for ensuring that notifications are delivered
|
|
4363
|
+
* prior to the promise resolving but without risk of promise
|
|
4364
|
+
* interleaving.
|
|
4365
|
+
*
|
|
4366
|
+
* @internal
|
|
4367
|
+
*/
|
|
4368
|
+
|
|
3769
4369
|
// DEBUG-only properties
|
|
3770
4370
|
|
|
3771
4371
|
get isDestroying() {
|
|
@@ -3799,18 +4399,17 @@ class Store extends BaseClass {
|
|
|
3799
4399
|
// private
|
|
3800
4400
|
this._requestCache = new RequestStateService(this);
|
|
3801
4401
|
this._instanceCache = new InstanceCache(this);
|
|
3802
|
-
this._documentCache = new Map();
|
|
3803
4402
|
this.isDestroying = false;
|
|
3804
4403
|
this.isDestroyed = false;
|
|
3805
4404
|
}
|
|
3806
4405
|
_run(cb) {
|
|
3807
|
-
macroCondition(getGlobalConfig().
|
|
4406
|
+
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
|
|
3808
4407
|
if (!test) {
|
|
3809
4408
|
throw new Error(`EmberData should never encounter a nested run`);
|
|
3810
4409
|
}
|
|
3811
4410
|
})(!this._cbs) : {};
|
|
3812
4411
|
const _cbs = this._cbs = {};
|
|
3813
|
-
if (macroCondition(getGlobalConfig().
|
|
4412
|
+
if (macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG)) {
|
|
3814
4413
|
try {
|
|
3815
4414
|
cb();
|
|
3816
4415
|
if (_cbs.coalesce) {
|
|
@@ -3839,6 +4438,16 @@ class Store extends BaseClass {
|
|
|
3839
4438
|
this._cbs = null;
|
|
3840
4439
|
}
|
|
3841
4440
|
}
|
|
4441
|
+
|
|
4442
|
+
/**
|
|
4443
|
+
* Executes the callback, ensurng that any work that calls
|
|
4444
|
+
* store._schedule is executed after in the right order.
|
|
4445
|
+
*
|
|
4446
|
+
* When queues already exist, scheduled callbacks will
|
|
4447
|
+
* join the existing queue.
|
|
4448
|
+
*
|
|
4449
|
+
* @internal
|
|
4450
|
+
*/
|
|
3842
4451
|
_join(cb) {
|
|
3843
4452
|
if (this._cbs) {
|
|
3844
4453
|
cb();
|
|
@@ -3847,12 +4456,12 @@ class Store extends BaseClass {
|
|
|
3847
4456
|
}
|
|
3848
4457
|
}
|
|
3849
4458
|
_schedule(name, cb) {
|
|
3850
|
-
macroCondition(getGlobalConfig().
|
|
4459
|
+
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
|
|
3851
4460
|
if (!test) {
|
|
3852
4461
|
throw new Error(`EmberData expects to schedule only when there is an active run`);
|
|
3853
4462
|
}
|
|
3854
4463
|
})(!!this._cbs) : {};
|
|
3855
|
-
macroCondition(getGlobalConfig().
|
|
4464
|
+
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
|
|
3856
4465
|
if (!test) {
|
|
3857
4466
|
throw new Error(`EmberData expects only one flush per queue name, cannot schedule ${name}`);
|
|
3858
4467
|
}
|
|
@@ -3875,7 +4484,7 @@ class Store extends BaseClass {
|
|
|
3875
4484
|
return this._requestCache;
|
|
3876
4485
|
}
|
|
3877
4486
|
_getAllPending() {
|
|
3878
|
-
if (macroCondition(getGlobalConfig().
|
|
4487
|
+
if (macroCondition(getGlobalConfig().WarpDriveMirror.env.TESTING)) {
|
|
3879
4488
|
const all = [];
|
|
3880
4489
|
const pending = this._requestCache._pending;
|
|
3881
4490
|
pending.forEach(requests => {
|
|
@@ -3947,33 +4556,37 @@ class Store extends BaseClass {
|
|
|
3947
4556
|
// the user has had the chance to set the prop.
|
|
3948
4557
|
const opts = {
|
|
3949
4558
|
store: this,
|
|
3950
|
-
[EnableHydration]: true
|
|
4559
|
+
[EnableHydration]: requestConfig[EnableHydration] ?? true
|
|
3951
4560
|
};
|
|
3952
4561
|
if (requestConfig.records) {
|
|
3953
4562
|
const identifierCache = this.identifierCache;
|
|
3954
4563
|
opts.records = requestConfig.records.map(r => identifierCache.getOrCreateRecordIdentifier(r));
|
|
3955
4564
|
}
|
|
3956
|
-
if (macroCondition(getGlobalConfig().
|
|
4565
|
+
if (macroCondition(getGlobalConfig().WarpDriveMirror.env.TESTING)) {
|
|
3957
4566
|
if (this.DISABLE_WAITER) {
|
|
3958
4567
|
opts.disableTestWaiter = typeof requestConfig.disableTestWaiter === 'boolean' ? requestConfig.disableTestWaiter : true;
|
|
3959
4568
|
}
|
|
3960
4569
|
}
|
|
3961
|
-
if (macroCondition(getGlobalConfig().
|
|
3962
|
-
|
|
3963
|
-
|
|
3964
|
-
|
|
3965
|
-
|
|
3966
|
-
|
|
4570
|
+
if (macroCondition(getGlobalConfig().WarpDriveMirror.activeLogging.LOG_REQUESTS)) {
|
|
4571
|
+
if (getGlobalConfig().WarpDriveMirror.debug.LOG_REQUESTS || globalThis.getWarpDriveRuntimeConfig().debug.LOG_REQUESTS) {
|
|
4572
|
+
let options;
|
|
4573
|
+
try {
|
|
4574
|
+
options = JSON.parse(JSON.stringify(requestConfig));
|
|
4575
|
+
} catch {
|
|
4576
|
+
options = requestConfig;
|
|
4577
|
+
}
|
|
4578
|
+
// eslint-disable-next-line no-console
|
|
4579
|
+
console.log(`request: [[START]] ${requestConfig.op && !requestConfig.url ? '(LEGACY) ' : ''}${requestConfig.op || '<unknown operation>'} ${requestConfig.url || '<empty url>'} ${requestConfig.method || '<empty method>'}`, options);
|
|
3967
4580
|
}
|
|
3968
|
-
// eslint-disable-next-line no-console
|
|
3969
|
-
console.log(`request: [[START]] ${requestConfig.op && !requestConfig.url ? '(LEGACY) ' : ''}${requestConfig.op || '<unknown operation>'} ${requestConfig.url || '<empty url>'} ${requestConfig.method || '<empty method>'}`, options);
|
|
3970
4581
|
}
|
|
3971
4582
|
const request = Object.assign({}, requestConfig, opts);
|
|
3972
4583
|
const future = this.requestManager.request(request);
|
|
3973
4584
|
future.onFinalize(() => {
|
|
3974
|
-
if (macroCondition(getGlobalConfig().
|
|
3975
|
-
|
|
3976
|
-
|
|
4585
|
+
if (macroCondition(getGlobalConfig().WarpDriveMirror.activeLogging.LOG_REQUESTS)) {
|
|
4586
|
+
if (getGlobalConfig().WarpDriveMirror.debug.LOG_REQUESTS || globalThis.getWarpDriveRuntimeConfig().debug.LOG_REQUESTS) {
|
|
4587
|
+
// eslint-disable-next-line no-console
|
|
4588
|
+
console.log(`request: [[FINALIZE]] ${requestConfig.op && !requestConfig.url ? '(LEGACY) ' : ''}${requestConfig.op || '<unknown operation>'} ${requestConfig.url || '<empty url>'} ${requestConfig.method || '<empty method>'}`);
|
|
4589
|
+
}
|
|
3977
4590
|
}
|
|
3978
4591
|
// skip flush for legacy belongsTo
|
|
3979
4592
|
if (requestConfig.op === 'findBelongsTo' && !requestConfig.url) {
|
|
@@ -4036,15 +4649,15 @@ class Store extends BaseClass {
|
|
|
4036
4649
|
modelFor(type) {
|
|
4037
4650
|
// FIXME add deprecation and deprecation stripping
|
|
4038
4651
|
// FIXME/TODO update RFC to remove this method
|
|
4039
|
-
if (macroCondition(getGlobalConfig().
|
|
4652
|
+
if (macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG)) {
|
|
4040
4653
|
assertDestroyedStoreOnly(this, 'modelFor');
|
|
4041
4654
|
}
|
|
4042
|
-
macroCondition(getGlobalConfig().
|
|
4655
|
+
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
|
|
4043
4656
|
if (!test) {
|
|
4044
4657
|
throw new Error(`You need to pass <type> to the store's modelFor method`);
|
|
4045
4658
|
}
|
|
4046
4659
|
})(typeof type === 'string' && type.length) : {};
|
|
4047
|
-
macroCondition(getGlobalConfig().
|
|
4660
|
+
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
|
|
4048
4661
|
if (!test) {
|
|
4049
4662
|
throw new Error(`No model was found for '${type}' and no schema handles the type`);
|
|
4050
4663
|
}
|
|
@@ -4080,15 +4693,15 @@ class Store extends BaseClass {
|
|
|
4080
4693
|
*/
|
|
4081
4694
|
|
|
4082
4695
|
createRecord(type, inputProperties) {
|
|
4083
|
-
if (macroCondition(getGlobalConfig().
|
|
4696
|
+
if (macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG)) {
|
|
4084
4697
|
assertDestroyingStore(this, 'createRecord');
|
|
4085
4698
|
}
|
|
4086
|
-
macroCondition(getGlobalConfig().
|
|
4699
|
+
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
|
|
4087
4700
|
if (!test) {
|
|
4088
4701
|
throw new Error(`You need to pass a model name to the store's createRecord method`);
|
|
4089
4702
|
}
|
|
4090
4703
|
})(type) : {};
|
|
4091
|
-
macroCondition(getGlobalConfig().
|
|
4704
|
+
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
|
|
4092
4705
|
if (!test) {
|
|
4093
4706
|
throw new Error(`Passing classes to store methods has been removed. Please pass a dasherized string instead of ${type}`);
|
|
4094
4707
|
}
|
|
@@ -4127,7 +4740,7 @@ class Store extends BaseClass {
|
|
|
4127
4740
|
};
|
|
4128
4741
|
if (resource.id) {
|
|
4129
4742
|
const identifier = this.identifierCache.peekRecordIdentifier(resource);
|
|
4130
|
-
macroCondition(getGlobalConfig().
|
|
4743
|
+
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
|
|
4131
4744
|
if (!test) {
|
|
4132
4745
|
throw new Error(`The id ${String(properties.id)} has already been used with another '${normalizedModelName}' record.`);
|
|
4133
4746
|
}
|
|
@@ -4156,12 +4769,12 @@ class Store extends BaseClass {
|
|
|
4156
4769
|
@param {unknown} record
|
|
4157
4770
|
*/
|
|
4158
4771
|
deleteRecord(record) {
|
|
4159
|
-
if (macroCondition(getGlobalConfig().
|
|
4772
|
+
if (macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG)) {
|
|
4160
4773
|
assertDestroyingStore(this, 'deleteRecord');
|
|
4161
4774
|
}
|
|
4162
4775
|
const identifier = peekRecordIdentifier(record);
|
|
4163
4776
|
const cache = this.cache;
|
|
4164
|
-
macroCondition(getGlobalConfig().
|
|
4777
|
+
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
|
|
4165
4778
|
if (!test) {
|
|
4166
4779
|
throw new Error(`expected the record to be connected to a cache`);
|
|
4167
4780
|
}
|
|
@@ -4179,16 +4792,15 @@ class Store extends BaseClass {
|
|
|
4179
4792
|
This will cause the record to be destroyed and freed up for garbage collection.
|
|
4180
4793
|
Example
|
|
4181
4794
|
```javascript
|
|
4182
|
-
store.findRecord('post', '1')
|
|
4183
|
-
|
|
4184
|
-
});
|
|
4795
|
+
const { content: { data: post } } = await store.request(findRecord({ type: 'post', id: '1' }));
|
|
4796
|
+
store.unloadRecord(post);
|
|
4185
4797
|
```
|
|
4186
4798
|
@method unloadRecord
|
|
4187
4799
|
@public
|
|
4188
4800
|
@param {Model} record
|
|
4189
4801
|
*/
|
|
4190
4802
|
unloadRecord(record) {
|
|
4191
|
-
if (macroCondition(getGlobalConfig().
|
|
4803
|
+
if (macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG)) {
|
|
4192
4804
|
assertDestroyingStore(this, 'unloadRecord');
|
|
4193
4805
|
}
|
|
4194
4806
|
const identifier = peekRecordIdentifier(record);
|
|
@@ -4402,7 +5014,7 @@ class Store extends BaseClass {
|
|
|
4402
5014
|
```app/routes/post.js
|
|
4403
5015
|
export default class PostRoute extends Route {
|
|
4404
5016
|
model(params) {
|
|
4405
|
-
return this.store.findRecord('post', params.post_id, { include: 'comments' });
|
|
5017
|
+
return this.store.findRecord('post', params.post_id, { include: ['comments'] });
|
|
4406
5018
|
}
|
|
4407
5019
|
}
|
|
4408
5020
|
```
|
|
@@ -4424,13 +5036,13 @@ class Store extends BaseClass {
|
|
|
4424
5036
|
In this case, the post's comments would then be available in your template as
|
|
4425
5037
|
`model.comments`.
|
|
4426
5038
|
Multiple relationships can be requested using an `include` parameter consisting of a
|
|
4427
|
-
|
|
5039
|
+
list of relationship names, while nested relationships can be specified
|
|
4428
5040
|
using a dot-separated sequence of relationship names. So to request both the post's
|
|
4429
5041
|
comments and the authors of those comments the request would look like this:
|
|
4430
5042
|
```app/routes/post.js
|
|
4431
5043
|
export default class PostRoute extends Route {
|
|
4432
5044
|
model(params) {
|
|
4433
|
-
return this.store.findRecord('post', params.post_id, { include: 'comments,comments.author' });
|
|
5045
|
+
return this.store.findRecord('post', params.post_id, { include: ['comments','comments.author'] });
|
|
4434
5046
|
}
|
|
4435
5047
|
}
|
|
4436
5048
|
```
|
|
@@ -4476,10 +5088,10 @@ class Store extends BaseClass {
|
|
|
4476
5088
|
*/
|
|
4477
5089
|
|
|
4478
5090
|
findRecord(resource, id, options) {
|
|
4479
|
-
if (macroCondition(getGlobalConfig().
|
|
5091
|
+
if (macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG)) {
|
|
4480
5092
|
assertDestroyingStore(this, 'findRecord');
|
|
4481
5093
|
}
|
|
4482
|
-
macroCondition(getGlobalConfig().
|
|
5094
|
+
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
|
|
4483
5095
|
if (!test) {
|
|
4484
5096
|
throw new Error(`You need to pass a modelName or resource identifier as the first argument to the store's findRecord method`);
|
|
4485
5097
|
}
|
|
@@ -4487,7 +5099,7 @@ class Store extends BaseClass {
|
|
|
4487
5099
|
if (isMaybeIdentifier(resource)) {
|
|
4488
5100
|
options = id;
|
|
4489
5101
|
} else {
|
|
4490
|
-
macroCondition(getGlobalConfig().
|
|
5102
|
+
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
|
|
4491
5103
|
if (!test) {
|
|
4492
5104
|
throw new Error(`Passing classes to store methods has been removed. Please pass a dasherized string instead of ${resource}`);
|
|
4493
5105
|
}
|
|
@@ -4555,7 +5167,7 @@ class Store extends BaseClass {
|
|
|
4555
5167
|
*/
|
|
4556
5168
|
// TODO @deprecate getReference (and references generally)
|
|
4557
5169
|
getReference(resource, id) {
|
|
4558
|
-
if (macroCondition(getGlobalConfig().
|
|
5170
|
+
if (macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG)) {
|
|
4559
5171
|
assertDestroyingStore(this, 'getReference');
|
|
4560
5172
|
}
|
|
4561
5173
|
let resourceIdentifier;
|
|
@@ -4566,7 +5178,7 @@ class Store extends BaseClass {
|
|
|
4566
5178
|
const normalizedId = ensureStringId(id);
|
|
4567
5179
|
resourceIdentifier = constructResource(type, normalizedId);
|
|
4568
5180
|
}
|
|
4569
|
-
macroCondition(getGlobalConfig().
|
|
5181
|
+
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
|
|
4570
5182
|
if (!test) {
|
|
4571
5183
|
throw new Error('getReference expected to receive either a resource identifier or type and id as arguments');
|
|
4572
5184
|
}
|
|
@@ -4618,15 +5230,15 @@ class Store extends BaseClass {
|
|
|
4618
5230
|
// this is basically an "are we not empty" query.
|
|
4619
5231
|
return isLoaded ? this._instanceCache.getRecord(stableIdentifier) : null;
|
|
4620
5232
|
}
|
|
4621
|
-
if (macroCondition(getGlobalConfig().
|
|
5233
|
+
if (macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG)) {
|
|
4622
5234
|
assertDestroyingStore(this, 'peekRecord');
|
|
4623
5235
|
}
|
|
4624
|
-
macroCondition(getGlobalConfig().
|
|
5236
|
+
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
|
|
4625
5237
|
if (!test) {
|
|
4626
5238
|
throw new Error(`You need to pass a model name to the store's peekRecord method`);
|
|
4627
5239
|
}
|
|
4628
5240
|
})(identifier) : {};
|
|
4629
|
-
macroCondition(getGlobalConfig().
|
|
5241
|
+
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
|
|
4630
5242
|
if (!test) {
|
|
4631
5243
|
throw new Error(`Passing classes to store methods has been removed. Please pass a dasherized string instead of ${String(identifier)}`);
|
|
4632
5244
|
}
|
|
@@ -4681,20 +5293,20 @@ class Store extends BaseClass {
|
|
|
4681
5293
|
*/
|
|
4682
5294
|
|
|
4683
5295
|
query(type, query, options = {}) {
|
|
4684
|
-
if (macroCondition(getGlobalConfig().
|
|
5296
|
+
if (macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG)) {
|
|
4685
5297
|
assertDestroyingStore(this, 'query');
|
|
4686
5298
|
}
|
|
4687
|
-
macroCondition(getGlobalConfig().
|
|
5299
|
+
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
|
|
4688
5300
|
if (!test) {
|
|
4689
5301
|
throw new Error(`You need to pass a model name to the store's query method`);
|
|
4690
5302
|
}
|
|
4691
5303
|
})(type) : {};
|
|
4692
|
-
macroCondition(getGlobalConfig().
|
|
5304
|
+
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
|
|
4693
5305
|
if (!test) {
|
|
4694
5306
|
throw new Error(`You need to pass a query hash to the store's query method`);
|
|
4695
5307
|
}
|
|
4696
5308
|
})(query) : {};
|
|
4697
|
-
macroCondition(getGlobalConfig().
|
|
5309
|
+
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
|
|
4698
5310
|
if (!test) {
|
|
4699
5311
|
throw new Error(`Passing classes to store methods has been removed. Please pass a dasherized string instead of ${type}`);
|
|
4700
5312
|
}
|
|
@@ -4796,20 +5408,20 @@ class Store extends BaseClass {
|
|
|
4796
5408
|
*/
|
|
4797
5409
|
|
|
4798
5410
|
queryRecord(type, query, options) {
|
|
4799
|
-
if (macroCondition(getGlobalConfig().
|
|
5411
|
+
if (macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG)) {
|
|
4800
5412
|
assertDestroyingStore(this, 'queryRecord');
|
|
4801
5413
|
}
|
|
4802
|
-
macroCondition(getGlobalConfig().
|
|
5414
|
+
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
|
|
4803
5415
|
if (!test) {
|
|
4804
5416
|
throw new Error(`You need to pass a model name to the store's queryRecord method`);
|
|
4805
5417
|
}
|
|
4806
5418
|
})(type) : {};
|
|
4807
|
-
macroCondition(getGlobalConfig().
|
|
5419
|
+
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
|
|
4808
5420
|
if (!test) {
|
|
4809
5421
|
throw new Error(`You need to pass a query hash to the store's queryRecord method`);
|
|
4810
5422
|
}
|
|
4811
5423
|
})(query) : {};
|
|
4812
|
-
macroCondition(getGlobalConfig().
|
|
5424
|
+
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
|
|
4813
5425
|
if (!test) {
|
|
4814
5426
|
throw new Error(`Passing classes to store methods has been removed. Please pass a dasherized string instead of ${type}`);
|
|
4815
5427
|
}
|
|
@@ -4951,18 +5563,18 @@ class Store extends BaseClass {
|
|
|
4951
5563
|
```app/routes/posts.js
|
|
4952
5564
|
export default class PostsRoute extends Route {
|
|
4953
5565
|
model() {
|
|
4954
|
-
return this.store.findAll('post', { include: 'comments' });
|
|
5566
|
+
return this.store.findAll('post', { include: ['comments'] });
|
|
4955
5567
|
}
|
|
4956
5568
|
}
|
|
4957
5569
|
```
|
|
4958
5570
|
Multiple relationships can be requested using an `include` parameter consisting of a
|
|
4959
|
-
|
|
5571
|
+
list or relationship names, while nested relationships can be specified
|
|
4960
5572
|
using a dot-separated sequence of relationship names. So to request both the posts'
|
|
4961
5573
|
comments and the authors of those comments the request would look like this:
|
|
4962
5574
|
```app/routes/posts.js
|
|
4963
5575
|
export default class PostsRoute extends Route {
|
|
4964
5576
|
model() {
|
|
4965
|
-
return this.store.findAll('post', { include: 'comments,comments.author' });
|
|
5577
|
+
return this.store.findAll('post', { include: ['comments','comments.author'] });
|
|
4966
5578
|
}
|
|
4967
5579
|
}
|
|
4968
5580
|
```
|
|
@@ -4976,15 +5588,15 @@ class Store extends BaseClass {
|
|
|
4976
5588
|
*/
|
|
4977
5589
|
|
|
4978
5590
|
findAll(type, options = {}) {
|
|
4979
|
-
if (macroCondition(getGlobalConfig().
|
|
5591
|
+
if (macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG)) {
|
|
4980
5592
|
assertDestroyingStore(this, 'findAll');
|
|
4981
5593
|
}
|
|
4982
|
-
macroCondition(getGlobalConfig().
|
|
5594
|
+
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
|
|
4983
5595
|
if (!test) {
|
|
4984
5596
|
throw new Error(`You need to pass a model name to the store's findAll method`);
|
|
4985
5597
|
}
|
|
4986
5598
|
})(type) : {};
|
|
4987
|
-
macroCondition(getGlobalConfig().
|
|
5599
|
+
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
|
|
4988
5600
|
if (!test) {
|
|
4989
5601
|
throw new Error(`Passing classes to store methods has been removed. Please pass a dasherized string instead of ${type}`);
|
|
4990
5602
|
}
|
|
@@ -5024,15 +5636,15 @@ class Store extends BaseClass {
|
|
|
5024
5636
|
*/
|
|
5025
5637
|
|
|
5026
5638
|
peekAll(type) {
|
|
5027
|
-
if (macroCondition(getGlobalConfig().
|
|
5639
|
+
if (macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG)) {
|
|
5028
5640
|
assertDestroyingStore(this, 'peekAll');
|
|
5029
5641
|
}
|
|
5030
|
-
macroCondition(getGlobalConfig().
|
|
5642
|
+
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
|
|
5031
5643
|
if (!test) {
|
|
5032
5644
|
throw new Error(`You need to pass a model name to the store's peekAll method`);
|
|
5033
5645
|
}
|
|
5034
5646
|
})(type) : {};
|
|
5035
|
-
macroCondition(getGlobalConfig().
|
|
5647
|
+
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
|
|
5036
5648
|
if (!test) {
|
|
5037
5649
|
throw new Error(`Passing classes to store methods has been removed. Please pass a dasherized string instead of ${type}`);
|
|
5038
5650
|
}
|
|
@@ -5054,10 +5666,10 @@ class Store extends BaseClass {
|
|
|
5054
5666
|
*/
|
|
5055
5667
|
|
|
5056
5668
|
unloadAll(type) {
|
|
5057
|
-
if (macroCondition(getGlobalConfig().
|
|
5669
|
+
if (macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG)) {
|
|
5058
5670
|
assertDestroyedStoreOnly(this, 'unloadAll');
|
|
5059
5671
|
}
|
|
5060
|
-
macroCondition(getGlobalConfig().
|
|
5672
|
+
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
|
|
5061
5673
|
if (!test) {
|
|
5062
5674
|
throw new Error(`Passing classes to store methods has been removed. Please pass a dasherized string instead of ${String(type)}`);
|
|
5063
5675
|
}
|
|
@@ -5208,7 +5820,7 @@ class Store extends BaseClass {
|
|
|
5208
5820
|
*/
|
|
5209
5821
|
|
|
5210
5822
|
push(data) {
|
|
5211
|
-
if (macroCondition(getGlobalConfig().
|
|
5823
|
+
if (macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG)) {
|
|
5212
5824
|
assertDestroyingStore(this, 'push');
|
|
5213
5825
|
}
|
|
5214
5826
|
const pushed = this._push(data, false);
|
|
@@ -5230,19 +5842,9 @@ class Store extends BaseClass {
|
|
|
5230
5842
|
@return {StableRecordIdentifier|Array<StableRecordIdentifier>|null} identifiers for the primary records that had data loaded
|
|
5231
5843
|
*/
|
|
5232
5844
|
_push(jsonApiDoc, asyncFlush) {
|
|
5233
|
-
if (macroCondition(getGlobalConfig().
|
|
5845
|
+
if (macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG)) {
|
|
5234
5846
|
assertDestroyingStore(this, '_push');
|
|
5235
5847
|
}
|
|
5236
|
-
if (macroCondition(getGlobalConfig().WarpDrive.debug.LOG_PAYLOADS)) {
|
|
5237
|
-
try {
|
|
5238
|
-
const data = JSON.parse(JSON.stringify(jsonApiDoc));
|
|
5239
|
-
// eslint-disable-next-line no-console
|
|
5240
|
-
console.log('EmberData | Payload - push', data);
|
|
5241
|
-
} catch (e) {
|
|
5242
|
-
// eslint-disable-next-line no-console
|
|
5243
|
-
console.log('EmberData | Payload - push', jsonApiDoc);
|
|
5244
|
-
}
|
|
5245
|
-
}
|
|
5246
5848
|
if (asyncFlush) {
|
|
5247
5849
|
this._enableAsyncFlush = true;
|
|
5248
5850
|
}
|
|
@@ -5268,10 +5870,10 @@ class Store extends BaseClass {
|
|
|
5268
5870
|
* @return {Promise<record>}
|
|
5269
5871
|
*/
|
|
5270
5872
|
saveRecord(record, options = {}) {
|
|
5271
|
-
if (macroCondition(getGlobalConfig().
|
|
5873
|
+
if (macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG)) {
|
|
5272
5874
|
assertDestroyingStore(this, 'saveRecord');
|
|
5273
5875
|
}
|
|
5274
|
-
macroCondition(getGlobalConfig().
|
|
5876
|
+
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
|
|
5275
5877
|
if (!test) {
|
|
5276
5878
|
throw new Error(`Unable to initiate save for a record in a disconnected state`);
|
|
5277
5879
|
}
|
|
@@ -5283,7 +5885,7 @@ class Store extends BaseClass {
|
|
|
5283
5885
|
// but just in case we reject here to prevent bad things.
|
|
5284
5886
|
return Promise.reject(new Error(`Record Is Disconnected`));
|
|
5285
5887
|
}
|
|
5286
|
-
macroCondition(getGlobalConfig().
|
|
5888
|
+
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
|
|
5287
5889
|
if (!test) {
|
|
5288
5890
|
throw new Error(`Cannot initiate a save request for an unloaded record: ${identifier.lid}`);
|
|
5289
5891
|
}
|
|
@@ -5340,7 +5942,7 @@ class Store extends BaseClass {
|
|
|
5340
5942
|
} = this._instanceCache;
|
|
5341
5943
|
if (!cache) {
|
|
5342
5944
|
cache = this._instanceCache.cache = this.createCache(this._instanceCache._storeWrapper);
|
|
5343
|
-
if (macroCondition(getGlobalConfig().
|
|
5945
|
+
if (macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG)) {
|
|
5344
5946
|
cache = new CacheManager(cache);
|
|
5345
5947
|
}
|
|
5346
5948
|
}
|
|
@@ -5364,19 +5966,19 @@ class Store extends BaseClass {
|
|
|
5364
5966
|
return new this(args);
|
|
5365
5967
|
}
|
|
5366
5968
|
}
|
|
5367
|
-
if (macroCondition(getGlobalConfig().
|
|
5969
|
+
if (macroCondition(getGlobalConfig().WarpDriveMirror.deprecations.ENABLE_LEGACY_SCHEMA_SERVICE)) {
|
|
5368
5970
|
Store.prototype.getSchemaDefinitionService = function () {
|
|
5369
|
-
macroCondition(getGlobalConfig().
|
|
5971
|
+
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
|
|
5370
5972
|
if (!test) {
|
|
5371
5973
|
throw new Error(`You must registerSchemaDefinitionService with the store to use custom model classes`);
|
|
5372
5974
|
}
|
|
5373
5975
|
})(this._schema) : {};
|
|
5374
5976
|
deprecate(`Use \`store.schema\` instead of \`store.getSchemaDefinitionService()\``, false, {
|
|
5375
5977
|
id: 'ember-data-mirror:schema-service-updates',
|
|
5376
|
-
until: '
|
|
5978
|
+
until: '6.0',
|
|
5377
5979
|
for: 'ember-data-mirror',
|
|
5378
5980
|
since: {
|
|
5379
|
-
available: '
|
|
5981
|
+
available: '4.13',
|
|
5380
5982
|
enabled: '5.4'
|
|
5381
5983
|
}
|
|
5382
5984
|
});
|
|
@@ -5385,10 +5987,10 @@ if (macroCondition(getGlobalConfig().WarpDrive.deprecations.ENABLE_LEGACY_SCHEMA
|
|
|
5385
5987
|
Store.prototype.registerSchemaDefinitionService = function (schema) {
|
|
5386
5988
|
deprecate(`Use \`store.createSchemaService\` instead of \`store.registerSchemaDefinitionService()\``, false, {
|
|
5387
5989
|
id: 'ember-data-mirror:schema-service-updates',
|
|
5388
|
-
until: '
|
|
5990
|
+
until: '6.0',
|
|
5389
5991
|
for: 'ember-data-mirror',
|
|
5390
5992
|
since: {
|
|
5391
|
-
available: '
|
|
5993
|
+
available: '4.13',
|
|
5392
5994
|
enabled: '5.4'
|
|
5393
5995
|
}
|
|
5394
5996
|
});
|
|
@@ -5397,10 +5999,10 @@ if (macroCondition(getGlobalConfig().WarpDrive.deprecations.ENABLE_LEGACY_SCHEMA
|
|
|
5397
5999
|
Store.prototype.registerSchema = function (schema) {
|
|
5398
6000
|
deprecate(`Use \`store.createSchemaService\` instead of \`store.registerSchema()\``, false, {
|
|
5399
6001
|
id: 'ember-data-mirror:schema-service-updates',
|
|
5400
|
-
until: '
|
|
6002
|
+
until: '6.0',
|
|
5401
6003
|
for: 'ember-data-mirror',
|
|
5402
6004
|
since: {
|
|
5403
|
-
available: '
|
|
6005
|
+
available: '4.13',
|
|
5404
6006
|
enabled: '5.4'
|
|
5405
6007
|
}
|
|
5406
6008
|
});
|
|
@@ -5409,10 +6011,10 @@ if (macroCondition(getGlobalConfig().WarpDrive.deprecations.ENABLE_LEGACY_SCHEMA
|
|
|
5409
6011
|
}
|
|
5410
6012
|
let assertDestroyingStore;
|
|
5411
6013
|
let assertDestroyedStoreOnly;
|
|
5412
|
-
if (macroCondition(getGlobalConfig().
|
|
6014
|
+
if (macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG)) {
|
|
5413
6015
|
// eslint-disable-next-line @typescript-eslint/no-shadow
|
|
5414
6016
|
assertDestroyingStore = function assertDestroyingStore(store, method) {
|
|
5415
|
-
macroCondition(getGlobalConfig().
|
|
6017
|
+
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
|
|
5416
6018
|
if (!test) {
|
|
5417
6019
|
throw new Error(`Attempted to call store.${method}(), but the store instance has already been destroyed.`);
|
|
5418
6020
|
}
|
|
@@ -5420,7 +6022,7 @@ if (macroCondition(getGlobalConfig().WarpDrive.env.DEBUG)) {
|
|
|
5420
6022
|
};
|
|
5421
6023
|
// eslint-disable-next-line @typescript-eslint/no-shadow
|
|
5422
6024
|
assertDestroyedStoreOnly = function assertDestroyedStoreOnly(store, method) {
|
|
5423
|
-
macroCondition(getGlobalConfig().
|
|
6025
|
+
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
|
|
5424
6026
|
if (!test) {
|
|
5425
6027
|
throw new Error(`Attempted to call store.${method}(), but the store instance has already been destroyed.`);
|
|
5426
6028
|
}
|
|
@@ -5434,13 +6036,13 @@ function normalizeProperties(store, identifier, properties) {
|
|
|
5434
6036
|
// assert here
|
|
5435
6037
|
if (properties !== undefined) {
|
|
5436
6038
|
if ('id' in properties) {
|
|
5437
|
-
macroCondition(getGlobalConfig().
|
|
6039
|
+
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
|
|
5438
6040
|
if (!test) {
|
|
5439
6041
|
throw new Error(`expected id to be a string or null`);
|
|
5440
6042
|
}
|
|
5441
6043
|
})(properties.id !== undefined) : {};
|
|
5442
6044
|
}
|
|
5443
|
-
macroCondition(getGlobalConfig().
|
|
6045
|
+
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
|
|
5444
6046
|
if (!test) {
|
|
5445
6047
|
throw new Error(`You passed '${typeof properties}' as properties for record creation instead of an object.`);
|
|
5446
6048
|
}
|
|
@@ -5460,12 +6062,12 @@ function normalizeProperties(store, identifier, properties) {
|
|
|
5460
6062
|
const field = defs.get(prop);
|
|
5461
6063
|
if (!field) continue;
|
|
5462
6064
|
if (field.kind === 'hasMany') {
|
|
5463
|
-
if (macroCondition(getGlobalConfig().
|
|
6065
|
+
if (macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG)) {
|
|
5464
6066
|
assertRecordsPassedToHasMany(properties[prop]);
|
|
5465
6067
|
}
|
|
5466
|
-
properties[prop] = extractIdentifiersFromRecords(properties[prop]);
|
|
6068
|
+
properties[prop] = extractIdentifiersFromRecords$1(properties[prop]);
|
|
5467
6069
|
} else if (field.kind === 'belongsTo') {
|
|
5468
|
-
properties[prop] = extractIdentifierFromRecord(properties[prop]);
|
|
6070
|
+
properties[prop] = extractIdentifierFromRecord$1(properties[prop]);
|
|
5469
6071
|
}
|
|
5470
6072
|
}
|
|
5471
6073
|
}
|
|
@@ -5473,12 +6075,12 @@ function normalizeProperties(store, identifier, properties) {
|
|
|
5473
6075
|
return properties;
|
|
5474
6076
|
}
|
|
5475
6077
|
function assertRecordsPassedToHasMany(records) {
|
|
5476
|
-
macroCondition(getGlobalConfig().
|
|
6078
|
+
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
|
|
5477
6079
|
if (!test) {
|
|
5478
6080
|
throw new Error(`You must pass an array of records to set a hasMany relationship`);
|
|
5479
6081
|
}
|
|
5480
6082
|
})(Array.isArray(records)) : {};
|
|
5481
|
-
macroCondition(getGlobalConfig().
|
|
6083
|
+
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
|
|
5482
6084
|
if (!test) {
|
|
5483
6085
|
throw new Error(`All elements of a hasMany relationship must be instances of Model, you passed ${records.map(r => `${typeof r}`).join(', ')}`);
|
|
5484
6086
|
}
|
|
@@ -5493,459 +6095,340 @@ function assertRecordsPassedToHasMany(records) {
|
|
|
5493
6095
|
});
|
|
5494
6096
|
}()) : {};
|
|
5495
6097
|
}
|
|
5496
|
-
function extractIdentifiersFromRecords(records) {
|
|
5497
|
-
return records.map(record => extractIdentifierFromRecord(record));
|
|
6098
|
+
function extractIdentifiersFromRecords$1(records) {
|
|
6099
|
+
return records.map(record => extractIdentifierFromRecord$1(record));
|
|
5498
6100
|
}
|
|
5499
|
-
function extractIdentifierFromRecord(recordOrPromiseRecord) {
|
|
6101
|
+
function extractIdentifierFromRecord$1(recordOrPromiseRecord) {
|
|
5500
6102
|
if (!recordOrPromiseRecord) {
|
|
5501
6103
|
return null;
|
|
5502
6104
|
}
|
|
5503
6105
|
const extract = recordIdentifierFor;
|
|
5504
6106
|
return extract(recordOrPromiseRecord);
|
|
5505
6107
|
}
|
|
6108
|
+
const MUTATION_OPS = new Set(['createRecord', 'updateRecord', 'deleteRecord']);
|
|
6109
|
+
function calcShouldFetch(store, request, hasCachedValue, identifier) {
|
|
6110
|
+
const {
|
|
6111
|
+
cacheOptions
|
|
6112
|
+
} = request;
|
|
6113
|
+
return request.op && MUTATION_OPS.has(request.op) || cacheOptions?.reload || !hasCachedValue || (store.lifetimes && identifier ? store.lifetimes.isHardExpired(identifier, store) : false);
|
|
6114
|
+
}
|
|
6115
|
+
function calcShouldBackgroundFetch(store, request, willFetch, identifier) {
|
|
6116
|
+
const {
|
|
6117
|
+
cacheOptions
|
|
6118
|
+
} = request;
|
|
6119
|
+
return cacheOptions?.backgroundReload || (store.lifetimes && identifier ? store.lifetimes.isSoftExpired(identifier, store) : false);
|
|
6120
|
+
}
|
|
6121
|
+
function isMutation(request) {
|
|
6122
|
+
return Boolean(request.op && MUTATION_OPS.has(request.op));
|
|
6123
|
+
}
|
|
6124
|
+
function isCacheAffecting(document) {
|
|
6125
|
+
if (!isMutation(document.request)) {
|
|
6126
|
+
return true;
|
|
6127
|
+
}
|
|
6128
|
+
// a mutation combined with a 204 has no cache impact when no known records were involved
|
|
6129
|
+
// a createRecord with a 201 with an empty response and no known records should similarly
|
|
6130
|
+
// have no cache impact
|
|
6131
|
+
|
|
6132
|
+
if (document.request.op === 'createRecord' && document.response?.status === 201) {
|
|
6133
|
+
return document.content ? Object.keys(document.content).length > 0 : false;
|
|
6134
|
+
}
|
|
6135
|
+
return document.response?.status !== 204;
|
|
6136
|
+
}
|
|
6137
|
+
function isAggregateError(error) {
|
|
6138
|
+
return error instanceof AggregateError || error.name === 'AggregateError' && Array.isArray(error.errors);
|
|
6139
|
+
}
|
|
6140
|
+
// TODO @runspired, consider if we should deep freeze errors (potentially only in debug) vs cloning them
|
|
6141
|
+
function cloneError(error) {
|
|
6142
|
+
const isAggregate = isAggregateError(error);
|
|
6143
|
+
const cloned = isAggregate ? new AggregateError(structuredClone(error.errors), error.message) : new Error(error.message);
|
|
6144
|
+
cloned.stack = error.stack;
|
|
6145
|
+
cloned.error = error.error;
|
|
6146
|
+
|
|
6147
|
+
// copy over enumerable properties
|
|
6148
|
+
Object.assign(cloned, error);
|
|
6149
|
+
return cloned;
|
|
6150
|
+
}
|
|
6151
|
+
function getPriority(identifier, deduped, priority) {
|
|
6152
|
+
if (identifier) {
|
|
6153
|
+
const existing = deduped.get(identifier);
|
|
6154
|
+
if (existing) {
|
|
6155
|
+
return existing.priority;
|
|
6156
|
+
}
|
|
6157
|
+
}
|
|
6158
|
+
return priority;
|
|
6159
|
+
}
|
|
5506
6160
|
|
|
5507
6161
|
/**
|
|
5508
6162
|
* @module @ember-data-mirror/store
|
|
5509
6163
|
*/
|
|
5510
|
-
function urlFromLink(link) {
|
|
5511
|
-
if (typeof link === 'string') return link;
|
|
5512
|
-
return link.href;
|
|
5513
|
-
}
|
|
5514
6164
|
|
|
5515
6165
|
/**
|
|
5516
|
-
* A
|
|
5517
|
-
* returned by `Cache.put` or `Cache.peek`, converting resource-identifiers into
|
|
5518
|
-
* record instances.
|
|
6166
|
+
* A CacheHandler that adds support for using an EmberData Cache with a RequestManager.
|
|
5519
6167
|
*
|
|
5520
|
-
*
|
|
5521
|
-
*
|
|
5522
|
-
*
|
|
6168
|
+
* This handler will only run when a request has supplied a `store` instance. Requests
|
|
6169
|
+
* issued by the store via `store.request()` will automatically have the `store` instance
|
|
6170
|
+
* attached to the request.
|
|
5523
6171
|
*
|
|
5524
|
-
*
|
|
5525
|
-
*
|
|
6172
|
+
* ```ts
|
|
6173
|
+
* requestManager.request({
|
|
6174
|
+
* store: store,
|
|
6175
|
+
* url: '/api/posts',
|
|
6176
|
+
* method: 'GET'
|
|
6177
|
+
* });
|
|
6178
|
+
* ```
|
|
6179
|
+
*
|
|
6180
|
+
* When this handler elects to handle a request, it will return the raw `StructuredDocument`
|
|
6181
|
+
* unless the request has `[EnableHydration]` set to `true`. In this case, the handler will
|
|
6182
|
+
* return a `Document` instance that will automatically update the UI when the cache is updated
|
|
6183
|
+
* in the future and will hydrate any identifiers in the StructuredDocument into Record instances.
|
|
6184
|
+
*
|
|
6185
|
+
* When issuing a request via the store, [EnableHydration] is automatically set to `true`. This
|
|
6186
|
+
* means that if desired you can issue requests that utilize the cache without needing to also
|
|
6187
|
+
* utilize Record instances if desired.
|
|
6188
|
+
*
|
|
6189
|
+
* Said differently, you could elect to issue all requests via a RequestManager, without ever using
|
|
6190
|
+
* the store directly, by setting [EnableHydration] to `true` and providing a store instance. Not
|
|
6191
|
+
* necessarily the most useful thing, but the decoupled nature of the RequestManager and incremental-feature
|
|
6192
|
+
* approach of EmberData allows for this flexibility.
|
|
6193
|
+
*
|
|
6194
|
+
* ```ts
|
|
6195
|
+
* import { EnableHydration } from '@warp-drive-mirror/core-types/request';
|
|
6196
|
+
*
|
|
6197
|
+
* requestManager.request({
|
|
6198
|
+
* store: store,
|
|
6199
|
+
* url: '/api/posts',
|
|
6200
|
+
* method: 'GET',
|
|
6201
|
+
* [EnableHydration]: true
|
|
6202
|
+
* });
|
|
6203
|
+
*
|
|
6204
|
+
* @typedoc
|
|
5526
6205
|
*/
|
|
5527
|
-
|
|
5528
|
-
|
|
5529
|
-
|
|
5530
|
-
|
|
5531
|
-
|
|
5532
|
-
|
|
5533
|
-
|
|
5534
|
-
|
|
5535
|
-
|
|
5536
|
-
|
|
5537
|
-
|
|
5538
|
-
|
|
5539
|
-
|
|
5540
|
-
* @type {object|undefined} - a links object
|
|
5541
|
-
* @public
|
|
5542
|
-
*/
|
|
5543
|
-
|
|
5544
|
-
/**
|
|
5545
|
-
* The primary data for this document, if any.
|
|
5546
|
-
*
|
|
5547
|
-
* If this document has no primary data (e.g. because it is an error document)
|
|
5548
|
-
* this property will be `undefined`.
|
|
5549
|
-
*
|
|
5550
|
-
* For collections this will be an array of record instances,
|
|
5551
|
-
* for single resource requests it will be a single record instance or null.
|
|
5552
|
-
*
|
|
5553
|
-
* @property data
|
|
5554
|
-
* @public
|
|
5555
|
-
* @type {object|Array<object>|null|undefined} - a data object
|
|
5556
|
-
*/
|
|
5557
|
-
|
|
5558
|
-
/**
|
|
5559
|
-
* The errors returned by the API for this request, if any
|
|
5560
|
-
*
|
|
5561
|
-
* @property errors
|
|
5562
|
-
* @public
|
|
5563
|
-
* @type {object|undefined} - an errors object
|
|
5564
|
-
*/
|
|
5565
|
-
|
|
5566
|
-
/**
|
|
5567
|
-
* The meta object for this document, if any
|
|
5568
|
-
*
|
|
5569
|
-
* @property meta
|
|
5570
|
-
* @public
|
|
5571
|
-
* @type {object|undefined} - a meta object
|
|
5572
|
-
*/
|
|
6206
|
+
const CacheHandler = {
|
|
6207
|
+
request(context, next) {
|
|
6208
|
+
// if we have no cache or no cache-key skip cache handling
|
|
6209
|
+
if (!context.request.store || context.request.cacheOptions?.[SkipCache]) {
|
|
6210
|
+
return next(context.request);
|
|
6211
|
+
}
|
|
6212
|
+
const {
|
|
6213
|
+
store
|
|
6214
|
+
} = context.request;
|
|
6215
|
+
const identifier = store.identifierCache.getOrCreateDocumentIdentifier(context.request);
|
|
6216
|
+
if (identifier) {
|
|
6217
|
+
context.setIdentifier(identifier);
|
|
6218
|
+
}
|
|
5573
6219
|
|
|
5574
|
-
|
|
5575
|
-
|
|
5576
|
-
|
|
5577
|
-
|
|
5578
|
-
* @public
|
|
5579
|
-
* @type {StableDocumentIdentifier|null}
|
|
5580
|
-
*/
|
|
6220
|
+
// used to dedupe existing requests that match
|
|
6221
|
+
const DEDUPE = store.requestManager._deduped;
|
|
6222
|
+
const activeRequest = identifier && DEDUPE.get(identifier);
|
|
6223
|
+
const peeked = identifier ? store.cache.peekRequest(identifier) : null;
|
|
5581
6224
|
|
|
5582
|
-
|
|
5583
|
-
|
|
5584
|
-
|
|
5585
|
-
|
|
5586
|
-
|
|
5587
|
-
|
|
5588
|
-
|
|
5589
|
-
|
|
5590
|
-
|
|
6225
|
+
// determine if we should skip cache
|
|
6226
|
+
if (calcShouldFetch(store, context.request, !!peeked, identifier)) {
|
|
6227
|
+
if (activeRequest) {
|
|
6228
|
+
activeRequest.priority = {
|
|
6229
|
+
blocking: true
|
|
6230
|
+
};
|
|
6231
|
+
return activeRequest.promise;
|
|
6232
|
+
}
|
|
6233
|
+
let promise = fetchContentAndHydrate(next, context, identifier, {
|
|
6234
|
+
blocking: true
|
|
6235
|
+
});
|
|
6236
|
+
if (identifier) {
|
|
6237
|
+
promise = promise.finally(() => {
|
|
6238
|
+
DEDUPE.delete(identifier);
|
|
6239
|
+
store.notifications.notify(identifier, 'state');
|
|
6240
|
+
});
|
|
6241
|
+
DEDUPE.set(identifier, {
|
|
6242
|
+
priority: {
|
|
6243
|
+
blocking: true
|
|
6244
|
+
},
|
|
6245
|
+
promise
|
|
6246
|
+
});
|
|
6247
|
+
store.notifications.notify(identifier, 'state');
|
|
6248
|
+
}
|
|
6249
|
+
return promise;
|
|
5591
6250
|
}
|
|
5592
|
-
options.method = options.method || 'GET';
|
|
5593
|
-
Object.assign(options, {
|
|
5594
|
-
url: urlFromLink(href)
|
|
5595
|
-
});
|
|
5596
|
-
const response = await this.#store.request(options);
|
|
5597
|
-
return response.content;
|
|
5598
|
-
}
|
|
5599
6251
|
|
|
5600
|
-
|
|
5601
|
-
|
|
5602
|
-
|
|
5603
|
-
|
|
5604
|
-
|
|
5605
|
-
|
|
5606
|
-
|
|
5607
|
-
|
|
5608
|
-
|
|
5609
|
-
|
|
5610
|
-
|
|
5611
|
-
|
|
5612
|
-
|
|
5613
|
-
|
|
6252
|
+
// if we have not skipped cache, determine if we should update behind the scenes
|
|
6253
|
+
if (calcShouldBackgroundFetch(store, context.request, false, identifier)) {
|
|
6254
|
+
let promise = activeRequest?.promise || fetchContentAndHydrate(next, context, identifier, {
|
|
6255
|
+
blocking: false
|
|
6256
|
+
});
|
|
6257
|
+
if (identifier && !activeRequest) {
|
|
6258
|
+
promise = promise.finally(() => {
|
|
6259
|
+
DEDUPE.delete(identifier);
|
|
6260
|
+
store.notifications.notify(identifier, 'state');
|
|
6261
|
+
});
|
|
6262
|
+
DEDUPE.set(identifier, {
|
|
6263
|
+
priority: {
|
|
6264
|
+
blocking: false
|
|
6265
|
+
},
|
|
6266
|
+
promise
|
|
6267
|
+
});
|
|
6268
|
+
store.notifications.notify(identifier, 'state');
|
|
5614
6269
|
}
|
|
5615
|
-
|
|
5616
|
-
options.cacheOptions = options.cacheOptions || {};
|
|
5617
|
-
options.cacheOptions.key = this.identifier?.lid;
|
|
5618
|
-
return this.#request(this.links.related ? 'related' : 'self', options);
|
|
5619
|
-
}
|
|
5620
|
-
|
|
5621
|
-
/**
|
|
5622
|
-
* Fetches the next link for this document, returning a promise that resolves
|
|
5623
|
-
* with the new document when the request completes, or null if there is no
|
|
5624
|
-
* next link.
|
|
5625
|
-
*
|
|
5626
|
-
* @method next
|
|
5627
|
-
* @public
|
|
5628
|
-
* @param {object} options
|
|
5629
|
-
* @return Promise<Document | null>
|
|
5630
|
-
*/
|
|
5631
|
-
next(options = {}) {
|
|
5632
|
-
return this.#request('next', options);
|
|
5633
|
-
}
|
|
5634
|
-
|
|
5635
|
-
/**
|
|
5636
|
-
* Fetches the prev link for this document, returning a promise that resolves
|
|
5637
|
-
* with the new document when the request completes, or null if there is no
|
|
5638
|
-
* prev link.
|
|
5639
|
-
*
|
|
5640
|
-
* @method prev
|
|
5641
|
-
* @public
|
|
5642
|
-
* @param {object} options
|
|
5643
|
-
* @return Promise<Document | null>
|
|
5644
|
-
*/
|
|
5645
|
-
prev(options = {}) {
|
|
5646
|
-
return this.#request('prev', options);
|
|
5647
|
-
}
|
|
5648
|
-
|
|
5649
|
-
/**
|
|
5650
|
-
* Fetches the first link for this document, returning a promise that resolves
|
|
5651
|
-
* with the new document when the request completes, or null if there is no
|
|
5652
|
-
* first link.
|
|
5653
|
-
*
|
|
5654
|
-
* @method first
|
|
5655
|
-
* @public
|
|
5656
|
-
* @param {object} options
|
|
5657
|
-
* @return Promise<Document | null>
|
|
5658
|
-
*/
|
|
5659
|
-
first(options = {}) {
|
|
5660
|
-
return this.#request('first', options);
|
|
5661
|
-
}
|
|
5662
|
-
|
|
5663
|
-
/**
|
|
5664
|
-
* Fetches the last link for this document, returning a promise that resolves
|
|
5665
|
-
* with the new document when the request completes, or null if there is no
|
|
5666
|
-
* last link.
|
|
5667
|
-
*
|
|
5668
|
-
* @method last
|
|
5669
|
-
* @public
|
|
5670
|
-
* @param {object} options
|
|
5671
|
-
* @return Promise<Document | null>
|
|
5672
|
-
*/
|
|
5673
|
-
last(options = {}) {
|
|
5674
|
-
return this.#request('last', options);
|
|
5675
|
-
}
|
|
5676
|
-
|
|
5677
|
-
/**
|
|
5678
|
-
* Implemented for `JSON.stringify` support.
|
|
5679
|
-
*
|
|
5680
|
-
* Returns the JSON representation of the document wrapper.
|
|
5681
|
-
*
|
|
5682
|
-
* This is a shallow serialization, it does not deeply serialize
|
|
5683
|
-
* the document's contents, leaving that to the individual record
|
|
5684
|
-
* instances to determine how to do, if at all.
|
|
5685
|
-
*
|
|
5686
|
-
* @method toJSON
|
|
5687
|
-
* @public
|
|
5688
|
-
* @return
|
|
5689
|
-
*/
|
|
5690
|
-
toJSON() {
|
|
5691
|
-
const data = {};
|
|
5692
|
-
data.identifier = this.identifier;
|
|
5693
|
-
if (this.data !== undefined) {
|
|
5694
|
-
data.data = this.data;
|
|
5695
|
-
}
|
|
5696
|
-
if (this.links !== undefined) {
|
|
5697
|
-
data.links = this.links;
|
|
5698
|
-
}
|
|
5699
|
-
if (this.errors !== undefined) {
|
|
5700
|
-
data.errors = this.errors;
|
|
6270
|
+
store.requestManager._pending.set(context.id, promise);
|
|
5701
6271
|
}
|
|
5702
|
-
|
|
5703
|
-
|
|
6272
|
+
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
|
|
6273
|
+
if (!test) {
|
|
6274
|
+
throw new Error(`Expected a peeked request to be present`);
|
|
6275
|
+
}
|
|
6276
|
+
})(peeked) : {};
|
|
6277
|
+
const shouldHydrate = context.request[EnableHydration] || false;
|
|
6278
|
+
context.setResponse(peeked.response);
|
|
6279
|
+
if ('error' in peeked) {
|
|
6280
|
+
const content = shouldHydrate ? maybeUpdateUiObjects(store, context.request, {
|
|
6281
|
+
shouldHydrate,
|
|
6282
|
+
identifier
|
|
6283
|
+
}, peeked.content) : peeked.content;
|
|
6284
|
+
const newError = cloneError(peeked);
|
|
6285
|
+
newError.content = content;
|
|
6286
|
+
throw newError;
|
|
5704
6287
|
}
|
|
5705
|
-
|
|
6288
|
+
const result = shouldHydrate ? maybeUpdateUiObjects(store, context.request, {
|
|
6289
|
+
shouldHydrate,
|
|
6290
|
+
identifier
|
|
6291
|
+
}, peeked.content) : peeked.content;
|
|
6292
|
+
return result;
|
|
5706
6293
|
}
|
|
5707
|
-
}
|
|
5708
|
-
|
|
5709
|
-
defineSignal(Document.prototype, 'links');
|
|
5710
|
-
defineSignal(Document.prototype, 'errors');
|
|
5711
|
-
defineSignal(Document.prototype, 'meta');
|
|
5712
|
-
|
|
5713
|
-
/**
|
|
5714
|
-
* @module @ember-data-mirror/store
|
|
5715
|
-
*/
|
|
5716
|
-
|
|
5717
|
-
/**
|
|
5718
|
-
* A service which an application may provide to the store via
|
|
5719
|
-
* the store's `lifetimes` property to configure the behavior
|
|
5720
|
-
* of the CacheHandler.
|
|
5721
|
-
*
|
|
5722
|
-
* The default behavior for request lifetimes is to never expire
|
|
5723
|
-
* unless manually refreshed via `cacheOptions.reload` or `cacheOptions.backgroundReload`.
|
|
5724
|
-
*
|
|
5725
|
-
* Implementing this service allows you to programatically define
|
|
5726
|
-
* when a request should be considered expired.
|
|
5727
|
-
*
|
|
5728
|
-
* @class <Interface> CachePolicy
|
|
5729
|
-
* @public
|
|
5730
|
-
*/
|
|
5731
|
-
const MUTATION_OPS = new Set(['createRecord', 'updateRecord', 'deleteRecord']);
|
|
5732
|
-
function isErrorDocument(document) {
|
|
5733
|
-
return 'errors' in document;
|
|
5734
|
-
}
|
|
5735
|
-
function maybeUpdateUiObjects(store, request, options, document, isFromCache) {
|
|
6294
|
+
};
|
|
6295
|
+
function maybeUpdateUiObjects(store, request, options, document) {
|
|
5736
6296
|
const {
|
|
5737
6297
|
identifier
|
|
5738
6298
|
} = options;
|
|
5739
|
-
if (!document) {
|
|
5740
|
-
macroCondition(getGlobalConfig().
|
|
6299
|
+
if (!document || !options.shouldHydrate) {
|
|
6300
|
+
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
|
|
5741
6301
|
if (!test) {
|
|
5742
6302
|
throw new Error(`The CacheHandler expected response content but none was found`);
|
|
5743
6303
|
}
|
|
5744
6304
|
})(!options.shouldHydrate) : {};
|
|
5745
6305
|
return document;
|
|
5746
6306
|
}
|
|
5747
|
-
if (
|
|
5748
|
-
|
|
5749
|
-
return document;
|
|
5750
|
-
}
|
|
5751
|
-
let doc;
|
|
5752
|
-
if (identifier) {
|
|
5753
|
-
doc = store._documentCache.get(identifier);
|
|
5754
|
-
}
|
|
5755
|
-
if (!doc) {
|
|
5756
|
-
doc = new Document(store, identifier);
|
|
5757
|
-
copyDocumentProperties(doc, document);
|
|
5758
|
-
if (identifier) {
|
|
5759
|
-
store._documentCache.set(identifier, doc);
|
|
5760
|
-
}
|
|
5761
|
-
} else if (!isFromCache) {
|
|
5762
|
-
doc.data = undefined;
|
|
5763
|
-
copyDocumentProperties(doc, document);
|
|
5764
|
-
}
|
|
5765
|
-
return options.shouldHydrate ? doc : document;
|
|
6307
|
+
if (identifier) {
|
|
6308
|
+
return store._instanceCache.getDocument(identifier);
|
|
5766
6309
|
}
|
|
5767
|
-
|
|
5768
|
-
|
|
5769
|
-
|
|
5770
|
-
|
|
5771
|
-
|
|
5772
|
-
|
|
5773
|
-
|
|
5774
|
-
|
|
5775
|
-
|
|
5776
|
-
|
|
5777
|
-
|
|
5778
|
-
|
|
5779
|
-
|
|
5780
|
-
|
|
5781
|
-
|
|
5782
|
-
|
|
5783
|
-
|
|
5784
|
-
|
|
5785
|
-
|
|
5786
|
-
|
|
5787
|
-
let managed = recordArrayManager._keyedArrays.get(identifier.lid);
|
|
5788
|
-
if (!managed) {
|
|
5789
|
-
managed = recordArrayManager.createArray({
|
|
5790
|
-
type: identifier.lid,
|
|
5791
|
-
identifiers: document.data,
|
|
5792
|
-
doc: document
|
|
5793
|
-
});
|
|
5794
|
-
recordArrayManager._keyedArrays.set(identifier.lid, managed);
|
|
5795
|
-
const doc = new Document(store, identifier);
|
|
5796
|
-
doc.data = managed;
|
|
5797
|
-
doc.meta = document.meta;
|
|
5798
|
-
doc.links = document.links;
|
|
5799
|
-
store._documentCache.set(identifier, doc);
|
|
5800
|
-
return options.shouldHydrate ? doc : document;
|
|
5801
|
-
} else {
|
|
5802
|
-
const doc = store._documentCache.get(identifier);
|
|
5803
|
-
if (!isFromCache) {
|
|
5804
|
-
recordArrayManager.populateManagedArray(managed, document.data, document);
|
|
5805
|
-
doc.data = managed;
|
|
5806
|
-
doc.meta = document.meta;
|
|
5807
|
-
doc.links = document.links;
|
|
5808
|
-
}
|
|
5809
|
-
return options.shouldHydrate ? doc : document;
|
|
6310
|
+
|
|
6311
|
+
// if we don't have an identifier, we give the document
|
|
6312
|
+
// its own local cache
|
|
6313
|
+
return new ReactiveDocument(store, null, {
|
|
6314
|
+
request,
|
|
6315
|
+
document
|
|
6316
|
+
});
|
|
6317
|
+
}
|
|
6318
|
+
function updateCacheForSuccess(store, request, options, document) {
|
|
6319
|
+
let response = null;
|
|
6320
|
+
if (isMutation(request)) {
|
|
6321
|
+
const record = request.data?.record || request.records?.[0];
|
|
6322
|
+
if (record) {
|
|
6323
|
+
response = store.cache.didCommit(record, document);
|
|
6324
|
+
|
|
6325
|
+
// a mutation combined with a 204 has no cache impact when no known records were involved
|
|
6326
|
+
// a createRecord with a 201 with an empty response and no known records should similarly
|
|
6327
|
+
// have no cache impact
|
|
6328
|
+
} else if (isCacheAffecting(document)) {
|
|
6329
|
+
response = store.cache.put(document);
|
|
5810
6330
|
}
|
|
5811
6331
|
} else {
|
|
5812
|
-
|
|
5813
|
-
return document;
|
|
5814
|
-
}
|
|
5815
|
-
const data = document.data ? store.peekRecord(document.data) : null;
|
|
5816
|
-
let doc;
|
|
5817
|
-
if (identifier) {
|
|
5818
|
-
doc = store._documentCache.get(identifier);
|
|
5819
|
-
}
|
|
5820
|
-
if (!doc) {
|
|
5821
|
-
doc = new Document(store, identifier);
|
|
5822
|
-
doc.data = data;
|
|
5823
|
-
copyDocumentProperties(doc, document);
|
|
5824
|
-
if (identifier) {
|
|
5825
|
-
store._documentCache.set(identifier, doc);
|
|
5826
|
-
}
|
|
5827
|
-
} else if (!isFromCache) {
|
|
5828
|
-
doc.data = data;
|
|
5829
|
-
copyDocumentProperties(doc, document);
|
|
5830
|
-
}
|
|
5831
|
-
return options.shouldHydrate ? doc : document;
|
|
6332
|
+
response = store.cache.put(document);
|
|
5832
6333
|
}
|
|
6334
|
+
return maybeUpdateUiObjects(store, request, options, response);
|
|
5833
6335
|
}
|
|
5834
|
-
function
|
|
6336
|
+
function handleFetchSuccess(store, context, options, document) {
|
|
5835
6337
|
const {
|
|
5836
|
-
|
|
5837
|
-
} =
|
|
5838
|
-
|
|
6338
|
+
request
|
|
6339
|
+
} = context;
|
|
6340
|
+
store.requestManager._pending.delete(context.id);
|
|
6341
|
+
store._enableAsyncFlush = true;
|
|
6342
|
+
let response;
|
|
6343
|
+
store._join(() => {
|
|
6344
|
+
response = updateCacheForSuccess(store, request, options, document);
|
|
6345
|
+
});
|
|
6346
|
+
store._enableAsyncFlush = null;
|
|
6347
|
+
if (store.lifetimes?.didRequest) {
|
|
6348
|
+
store.lifetimes.didRequest(context.request, document.response, options.identifier, store);
|
|
6349
|
+
}
|
|
6350
|
+
const finalPriority = getPriority(options.identifier, store.requestManager._deduped, options.priority);
|
|
6351
|
+
if (finalPriority.blocking) {
|
|
6352
|
+
return response;
|
|
6353
|
+
} else {
|
|
6354
|
+
store.notifications._flush();
|
|
6355
|
+
}
|
|
5839
6356
|
}
|
|
5840
|
-
function
|
|
5841
|
-
|
|
5842
|
-
|
|
5843
|
-
|
|
5844
|
-
|
|
6357
|
+
function updateCacheForError(store, context, options, error) {
|
|
6358
|
+
let response;
|
|
6359
|
+
if (isMutation(context.request)) {
|
|
6360
|
+
// TODO similar to didCommit we should spec this to be similar to cache.put for handling full response
|
|
6361
|
+
// currently we let the response remain undefiend.
|
|
6362
|
+
const errors = error && error.content && typeof error.content === 'object' && 'errors' in error.content && Array.isArray(error.content.errors) ? error.content.errors : undefined;
|
|
6363
|
+
const record = context.request.data?.record || context.request.records?.[0];
|
|
6364
|
+
store.cache.commitWasRejected(record, errors);
|
|
6365
|
+
} else {
|
|
6366
|
+
response = store.cache.put(error);
|
|
6367
|
+
return maybeUpdateUiObjects(store, context.request, options, response);
|
|
6368
|
+
}
|
|
5845
6369
|
}
|
|
5846
|
-
function
|
|
5847
|
-
|
|
6370
|
+
function handleFetchError(store, context, options, error) {
|
|
6371
|
+
store.requestManager._pending.delete(context.id);
|
|
6372
|
+
if (context.request.signal?.aborted) {
|
|
6373
|
+
throw error;
|
|
6374
|
+
}
|
|
6375
|
+
store._enableAsyncFlush = true;
|
|
6376
|
+
let response;
|
|
6377
|
+
store._join(() => {
|
|
6378
|
+
response = updateCacheForError(store, context, options, error);
|
|
6379
|
+
});
|
|
6380
|
+
store._enableAsyncFlush = null;
|
|
6381
|
+
if (options.identifier && store.lifetimes?.didRequest) {
|
|
6382
|
+
store.lifetimes.didRequest(context.request, error.response, options.identifier, store);
|
|
6383
|
+
}
|
|
6384
|
+
if (isMutation(context.request)) {
|
|
6385
|
+
throw error;
|
|
6386
|
+
}
|
|
6387
|
+
const finalPriority = getPriority(options.identifier, store.requestManager._deduped, options.priority);
|
|
6388
|
+
if (finalPriority.blocking) {
|
|
6389
|
+
const newError = cloneError(error);
|
|
6390
|
+
newError.content = response;
|
|
6391
|
+
throw newError;
|
|
6392
|
+
} else {
|
|
6393
|
+
store.notifications._flush();
|
|
6394
|
+
}
|
|
5848
6395
|
}
|
|
5849
|
-
function fetchContentAndHydrate(next, context, identifier,
|
|
6396
|
+
function fetchContentAndHydrate(next, context, identifier, priority) {
|
|
5850
6397
|
const {
|
|
5851
6398
|
store
|
|
5852
6399
|
} = context.request;
|
|
5853
6400
|
const shouldHydrate = context.request[EnableHydration] || false;
|
|
6401
|
+
const options = {
|
|
6402
|
+
shouldHydrate,
|
|
6403
|
+
identifier,
|
|
6404
|
+
priority
|
|
6405
|
+
};
|
|
5854
6406
|
let isMut = false;
|
|
5855
6407
|
if (isMutation(context.request)) {
|
|
5856
6408
|
isMut = true;
|
|
5857
6409
|
// TODO should we handle multiple records in request.records by iteratively calling willCommit for each
|
|
5858
6410
|
const record = context.request.data?.record || context.request.records?.[0];
|
|
5859
|
-
macroCondition(getGlobalConfig().
|
|
6411
|
+
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
|
|
5860
6412
|
if (!test) {
|
|
5861
6413
|
throw new Error(`Expected to receive a list of records included in the ${context.request.op} request`);
|
|
5862
6414
|
}
|
|
5863
6415
|
})(record || !shouldHydrate) : {};
|
|
5864
6416
|
if (record) {
|
|
5865
|
-
store.cache.willCommit(record, context);
|
|
5866
|
-
}
|
|
5867
|
-
}
|
|
5868
|
-
if (store.lifetimes?.willRequest) {
|
|
5869
|
-
store.lifetimes.willRequest(context.request, identifier, store);
|
|
5870
|
-
}
|
|
5871
|
-
const promise = next(context.request).then(document => {
|
|
5872
|
-
store.requestManager._pending.delete(context.id);
|
|
5873
|
-
store._enableAsyncFlush = true;
|
|
5874
|
-
let response;
|
|
5875
|
-
store._join(() => {
|
|
5876
|
-
if (isMutation(context.request)) {
|
|
5877
|
-
const record = context.request.data?.record || context.request.records?.[0];
|
|
5878
|
-
if (record) {
|
|
5879
|
-
response = store.cache.didCommit(record, document);
|
|
5880
|
-
|
|
5881
|
-
// a mutation combined with a 204 has no cache impact when no known records were involved
|
|
5882
|
-
// a createRecord with a 201 with an empty response and no known records should similarly
|
|
5883
|
-
// have no cache impact
|
|
5884
|
-
} else if (isCacheAffecting(document)) {
|
|
5885
|
-
response = store.cache.put(document);
|
|
5886
|
-
}
|
|
5887
|
-
} else {
|
|
5888
|
-
response = store.cache.put(document);
|
|
5889
|
-
}
|
|
5890
|
-
response = maybeUpdateUiObjects(store, context.request, {
|
|
5891
|
-
shouldHydrate,
|
|
5892
|
-
shouldFetch,
|
|
5893
|
-
shouldBackgroundFetch,
|
|
5894
|
-
identifier
|
|
5895
|
-
}, response, false);
|
|
5896
|
-
});
|
|
5897
|
-
store._enableAsyncFlush = null;
|
|
5898
|
-
if (store.lifetimes?.didRequest) {
|
|
5899
|
-
store.lifetimes.didRequest(context.request, document.response, identifier, store);
|
|
5900
|
-
}
|
|
5901
|
-
if (shouldFetch) {
|
|
5902
|
-
return response;
|
|
5903
|
-
} else if (shouldBackgroundFetch) {
|
|
5904
|
-
store.notifications._flush();
|
|
5905
|
-
}
|
|
5906
|
-
}, error => {
|
|
5907
|
-
store.requestManager._pending.delete(context.id);
|
|
5908
|
-
if (context.request.signal?.aborted) {
|
|
5909
|
-
throw error;
|
|
5910
|
-
}
|
|
5911
|
-
store.requestManager._pending.delete(context.id);
|
|
5912
|
-
store._enableAsyncFlush = true;
|
|
5913
|
-
let response;
|
|
5914
|
-
store._join(() => {
|
|
5915
|
-
if (isMutation(context.request)) {
|
|
5916
|
-
// TODO similar to didCommit we should spec this to be similar to cache.put for handling full response
|
|
5917
|
-
// currently we let the response remain undefiend.
|
|
5918
|
-
const errors = error && error.content && typeof error.content === 'object' && 'errors' in error.content && Array.isArray(error.content.errors) ? error.content.errors : undefined;
|
|
5919
|
-
const record = context.request.data?.record || context.request.records?.[0];
|
|
5920
|
-
store.cache.commitWasRejected(record, errors);
|
|
5921
|
-
// re-throw the original error to preserve `errors` property.
|
|
5922
|
-
throw error;
|
|
5923
|
-
} else {
|
|
5924
|
-
response = store.cache.put(error);
|
|
5925
|
-
response = maybeUpdateUiObjects(store, context.request, {
|
|
5926
|
-
shouldHydrate,
|
|
5927
|
-
shouldFetch,
|
|
5928
|
-
shouldBackgroundFetch,
|
|
5929
|
-
identifier
|
|
5930
|
-
}, response, false);
|
|
5931
|
-
}
|
|
5932
|
-
});
|
|
5933
|
-
store._enableAsyncFlush = null;
|
|
5934
|
-
if (identifier && store.lifetimes?.didRequest) {
|
|
5935
|
-
store.lifetimes.didRequest(context.request, error.response, identifier, store);
|
|
5936
|
-
}
|
|
5937
|
-
if (!shouldBackgroundFetch) {
|
|
5938
|
-
const newError = cloneError(error);
|
|
5939
|
-
newError.content = response;
|
|
5940
|
-
throw newError;
|
|
5941
|
-
} else {
|
|
5942
|
-
store.notifications._flush();
|
|
6417
|
+
store.cache.willCommit(record, context);
|
|
5943
6418
|
}
|
|
6419
|
+
}
|
|
6420
|
+
if (store.lifetimes?.willRequest) {
|
|
6421
|
+
store.lifetimes.willRequest(context.request, identifier, store);
|
|
6422
|
+
}
|
|
6423
|
+
const promise = next(context.request).then(document => {
|
|
6424
|
+
return handleFetchSuccess(store, context, options, document);
|
|
6425
|
+
}, error => {
|
|
6426
|
+
return handleFetchError(store, context, options, error);
|
|
5944
6427
|
});
|
|
5945
6428
|
if (!isMut) {
|
|
5946
6429
|
return promise;
|
|
5947
6430
|
}
|
|
5948
|
-
macroCondition(getGlobalConfig().
|
|
6431
|
+
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
|
|
5949
6432
|
if (!test) {
|
|
5950
6433
|
throw new Error(`Expected a mutation`);
|
|
5951
6434
|
}
|
|
@@ -5962,129 +6445,475 @@ function fetchContentAndHydrate(next, context, identifier, shouldFetch, shouldBa
|
|
|
5962
6445
|
}]
|
|
5963
6446
|
});
|
|
5964
6447
|
}
|
|
5965
|
-
function isAggregateError(error) {
|
|
5966
|
-
return error instanceof AggregateError || error.name === 'AggregateError' && Array.isArray(error.errors);
|
|
5967
|
-
}
|
|
5968
|
-
// TODO @runspired, consider if we should deep freeze errors (potentially only in debug) vs cloning them
|
|
5969
|
-
function cloneError(error) {
|
|
5970
|
-
const isAggregate = isAggregateError(error);
|
|
5971
|
-
const cloned = isAggregate ? new AggregateError(structuredClone(error.errors), error.message) : new Error(error.message);
|
|
5972
|
-
cloned.stack = error.stack;
|
|
5973
|
-
cloned.error = error.error;
|
|
5974
|
-
|
|
5975
|
-
// copy over enumerable properties
|
|
5976
|
-
Object.assign(cloned, error);
|
|
5977
|
-
return cloned;
|
|
5978
|
-
}
|
|
5979
6448
|
|
|
5980
6449
|
/**
|
|
5981
|
-
|
|
5982
|
-
|
|
5983
|
-
|
|
5984
|
-
|
|
5985
|
-
|
|
5986
|
-
*
|
|
5987
|
-
* ```ts
|
|
5988
|
-
* requestManager.request({
|
|
5989
|
-
* store: store,
|
|
5990
|
-
* url: '/api/posts',
|
|
5991
|
-
* method: 'GET'
|
|
5992
|
-
* });
|
|
5993
|
-
* ```
|
|
5994
|
-
*
|
|
5995
|
-
* When this handler elects to handle a request, it will return the raw `StructuredDocument`
|
|
5996
|
-
* unless the request has `[EnableHydration]` set to `true`. In this case, the handler will
|
|
5997
|
-
* return a `Document` instance that will automatically update the UI when the cache is updated
|
|
5998
|
-
* in the future and will hydrate any identifiers in the StructuredDocument into Record instances.
|
|
5999
|
-
*
|
|
6000
|
-
* When issuing a request via the store, [EnableHydration] is automatically set to `true`. This
|
|
6001
|
-
* means that if desired you can issue requests that utilize the cache without needing to also
|
|
6002
|
-
* utilize Record instances if desired.
|
|
6003
|
-
*
|
|
6004
|
-
* Said differently, you could elect to issue all requests via a RequestManager, without ever using
|
|
6005
|
-
* the store directly, by setting [EnableHydration] to `true` and providing a store instance. Not
|
|
6006
|
-
* necessarily the most useful thing, but the decoupled nature of the RequestManager and incremental-feature
|
|
6007
|
-
* approach of EmberData allows for this flexibility.
|
|
6008
|
-
*
|
|
6009
|
-
* ```ts
|
|
6010
|
-
* import { EnableHydration } from '@warp-drive-mirror/core-types/request';
|
|
6011
|
-
*
|
|
6012
|
-
* requestManager.request({
|
|
6013
|
-
* store: store,
|
|
6014
|
-
* url: '/api/posts',
|
|
6015
|
-
* method: 'GET',
|
|
6016
|
-
* [EnableHydration]: true
|
|
6017
|
-
* });
|
|
6018
|
-
*
|
|
6019
|
-
* @typedoc
|
|
6020
|
-
*/
|
|
6021
|
-
const CacheHandler = {
|
|
6022
|
-
request(context, next) {
|
|
6023
|
-
// if we have no cache or no cache-key skip cache handling
|
|
6024
|
-
if (!context.request.store || context.request.cacheOptions?.[SkipCache]) {
|
|
6025
|
-
return next(context.request);
|
|
6026
|
-
}
|
|
6027
|
-
const {
|
|
6028
|
-
store
|
|
6029
|
-
} = context.request;
|
|
6030
|
-
const identifier = store.identifierCache.getOrCreateDocumentIdentifier(context.request);
|
|
6031
|
-
const peeked = identifier ? store.cache.peekRequest(identifier) : null;
|
|
6450
|
+
@module @ember-data-mirror/store
|
|
6451
|
+
*/
|
|
6452
|
+
/**
|
|
6453
|
+
A `ManyArray` is a `MutableArray` that represents the contents of a has-many
|
|
6454
|
+
relationship.
|
|
6032
6455
|
|
|
6033
|
-
|
|
6034
|
-
|
|
6035
|
-
|
|
6456
|
+
The `ManyArray` is instantiated lazily the first time the relationship is
|
|
6457
|
+
requested.
|
|
6458
|
+
|
|
6459
|
+
This class is not intended to be directly instantiated by consuming applications.
|
|
6460
|
+
|
|
6461
|
+
### Inverses
|
|
6462
|
+
|
|
6463
|
+
Often, the relationships in Ember Data applications will have
|
|
6464
|
+
an inverse. For example, imagine the following models are
|
|
6465
|
+
defined:
|
|
6466
|
+
|
|
6467
|
+
```app/models/post.js
|
|
6468
|
+
import Model, { hasMany } from '@ember-data-mirror/model';
|
|
6469
|
+
|
|
6470
|
+
export default class PostModel extends Model {
|
|
6471
|
+
@hasMany('comment') comments;
|
|
6472
|
+
}
|
|
6473
|
+
```
|
|
6474
|
+
|
|
6475
|
+
```app/models/comment.js
|
|
6476
|
+
import Model, { belongsTo } from '@ember-data-mirror/model';
|
|
6477
|
+
|
|
6478
|
+
export default class CommentModel extends Model {
|
|
6479
|
+
@belongsTo('post') post;
|
|
6480
|
+
}
|
|
6481
|
+
```
|
|
6482
|
+
|
|
6483
|
+
If you created a new instance of `Post` and added
|
|
6484
|
+
a `Comment` record to its `comments` has-many
|
|
6485
|
+
relationship, you would expect the comment's `post`
|
|
6486
|
+
property to be set to the post that contained
|
|
6487
|
+
the has-many.
|
|
6488
|
+
|
|
6489
|
+
We call the record to which a relationship belongs-to the
|
|
6490
|
+
relationship's _owner_.
|
|
6491
|
+
|
|
6492
|
+
@class ManyArray
|
|
6493
|
+
@public
|
|
6494
|
+
*/
|
|
6495
|
+
class RelatedCollection extends IdentifierArray {
|
|
6496
|
+
/**
|
|
6497
|
+
The loading state of this array
|
|
6498
|
+
@property {Boolean} isLoaded
|
|
6499
|
+
@public
|
|
6500
|
+
*/
|
|
6501
|
+
|
|
6502
|
+
/**
|
|
6503
|
+
`true` if the relationship is polymorphic, `false` otherwise.
|
|
6504
|
+
@property {Boolean} isPolymorphic
|
|
6505
|
+
@private
|
|
6506
|
+
*/
|
|
6507
|
+
|
|
6508
|
+
/**
|
|
6509
|
+
Metadata associated with the request for async hasMany relationships.
|
|
6510
|
+
Example
|
|
6511
|
+
Given that the server returns the following JSON payload when fetching a
|
|
6512
|
+
hasMany relationship:
|
|
6513
|
+
```js
|
|
6514
|
+
{
|
|
6515
|
+
"comments": [{
|
|
6516
|
+
"id": 1,
|
|
6517
|
+
"comment": "This is the first comment",
|
|
6518
|
+
}, {
|
|
6519
|
+
// ...
|
|
6520
|
+
}],
|
|
6521
|
+
"meta": {
|
|
6522
|
+
"page": 1,
|
|
6523
|
+
"total": 5
|
|
6524
|
+
}
|
|
6036
6525
|
}
|
|
6526
|
+
```
|
|
6527
|
+
You can then access the meta data via the `meta` property:
|
|
6528
|
+
```js
|
|
6529
|
+
let comments = await post.comments;
|
|
6530
|
+
let meta = comments.meta;
|
|
6531
|
+
// meta.page => 1
|
|
6532
|
+
// meta.total => 5
|
|
6533
|
+
```
|
|
6534
|
+
@property {Object | null} meta
|
|
6535
|
+
@public
|
|
6536
|
+
*/
|
|
6037
6537
|
|
|
6038
|
-
|
|
6039
|
-
|
|
6040
|
-
|
|
6041
|
-
|
|
6538
|
+
/**
|
|
6539
|
+
* Retrieve the links for this relationship
|
|
6540
|
+
*
|
|
6541
|
+
@property {Object | null} links
|
|
6542
|
+
@public
|
|
6543
|
+
*/
|
|
6544
|
+
|
|
6545
|
+
constructor(options) {
|
|
6546
|
+
super(options);
|
|
6547
|
+
this.isLoaded = options.isLoaded || false;
|
|
6548
|
+
this.isAsync = options.isAsync || false;
|
|
6549
|
+
this.isPolymorphic = options.isPolymorphic || false;
|
|
6550
|
+
this.identifier = options.identifier;
|
|
6551
|
+
this.key = options.key;
|
|
6552
|
+
}
|
|
6553
|
+
[MUTATE](target, receiver, prop, args, _SIGNAL) {
|
|
6554
|
+
switch (prop) {
|
|
6555
|
+
case 'length 0':
|
|
6556
|
+
{
|
|
6557
|
+
Reflect.set(target, 'length', 0);
|
|
6558
|
+
mutateReplaceRelatedRecords(this, [], _SIGNAL);
|
|
6559
|
+
return true;
|
|
6560
|
+
}
|
|
6561
|
+
case 'replace cell':
|
|
6562
|
+
{
|
|
6563
|
+
const [index, prior, value] = args;
|
|
6564
|
+
target[index] = value;
|
|
6565
|
+
mutateReplaceRelatedRecord(this, {
|
|
6566
|
+
value,
|
|
6567
|
+
prior,
|
|
6568
|
+
index
|
|
6569
|
+
}, _SIGNAL);
|
|
6570
|
+
return true;
|
|
6571
|
+
}
|
|
6572
|
+
case 'push':
|
|
6573
|
+
{
|
|
6574
|
+
const newValues = extractIdentifiersFromRecords(args);
|
|
6575
|
+
assertNoDuplicates(this, target, currentState => currentState.push(...newValues), `Cannot push duplicates to a hasMany's state.`);
|
|
6576
|
+
if (macroCondition(getGlobalConfig().WarpDriveMirror.deprecations.DEPRECATE_MANY_ARRAY_DUPLICATES)) {
|
|
6577
|
+
// dedupe
|
|
6578
|
+
const seen = new Set(target);
|
|
6579
|
+
const unique = new Set();
|
|
6580
|
+
args.forEach(item => {
|
|
6581
|
+
const identifier = recordIdentifierFor(item);
|
|
6582
|
+
if (!seen.has(identifier)) {
|
|
6583
|
+
seen.add(identifier);
|
|
6584
|
+
unique.add(item);
|
|
6585
|
+
}
|
|
6586
|
+
});
|
|
6587
|
+
const newArgs = Array.from(unique);
|
|
6588
|
+
const result = Reflect.apply(target[prop], receiver, newArgs);
|
|
6589
|
+
if (newArgs.length) {
|
|
6590
|
+
mutateAddToRelatedRecords(this, {
|
|
6591
|
+
value: extractIdentifiersFromRecords(newArgs)
|
|
6592
|
+
}, _SIGNAL);
|
|
6593
|
+
}
|
|
6594
|
+
return result;
|
|
6595
|
+
}
|
|
6596
|
+
|
|
6597
|
+
// else, no dedupe, error on duplicates
|
|
6598
|
+
const result = Reflect.apply(target[prop], receiver, args);
|
|
6599
|
+
if (newValues.length) {
|
|
6600
|
+
mutateAddToRelatedRecords(this, {
|
|
6601
|
+
value: newValues
|
|
6602
|
+
}, _SIGNAL);
|
|
6603
|
+
}
|
|
6604
|
+
return result;
|
|
6605
|
+
}
|
|
6606
|
+
case 'pop':
|
|
6607
|
+
{
|
|
6608
|
+
const result = Reflect.apply(target[prop], receiver, args);
|
|
6609
|
+
if (result) {
|
|
6610
|
+
mutateRemoveFromRelatedRecords(this, {
|
|
6611
|
+
value: recordIdentifierFor(result)
|
|
6612
|
+
}, _SIGNAL);
|
|
6613
|
+
}
|
|
6614
|
+
return result;
|
|
6615
|
+
}
|
|
6616
|
+
case 'unshift':
|
|
6617
|
+
{
|
|
6618
|
+
const newValues = extractIdentifiersFromRecords(args);
|
|
6619
|
+
assertNoDuplicates(this, target, currentState => currentState.unshift(...newValues), `Cannot unshift duplicates to a hasMany's state.`);
|
|
6620
|
+
if (macroCondition(getGlobalConfig().WarpDriveMirror.deprecations.DEPRECATE_MANY_ARRAY_DUPLICATES)) {
|
|
6621
|
+
// dedupe
|
|
6622
|
+
const seen = new Set(target);
|
|
6623
|
+
const unique = new Set();
|
|
6624
|
+
args.forEach(item => {
|
|
6625
|
+
const identifier = recordIdentifierFor(item);
|
|
6626
|
+
if (!seen.has(identifier)) {
|
|
6627
|
+
seen.add(identifier);
|
|
6628
|
+
unique.add(item);
|
|
6629
|
+
}
|
|
6630
|
+
});
|
|
6631
|
+
const newArgs = Array.from(unique);
|
|
6632
|
+
const result = Reflect.apply(target[prop], receiver, newArgs);
|
|
6633
|
+
if (newArgs.length) {
|
|
6634
|
+
mutateAddToRelatedRecords(this, {
|
|
6635
|
+
value: extractIdentifiersFromRecords(newArgs),
|
|
6636
|
+
index: 0
|
|
6637
|
+
}, _SIGNAL);
|
|
6638
|
+
}
|
|
6639
|
+
return result;
|
|
6640
|
+
}
|
|
6641
|
+
|
|
6642
|
+
// else, no dedupe, error on duplicates
|
|
6643
|
+
const result = Reflect.apply(target[prop], receiver, args);
|
|
6644
|
+
if (newValues.length) {
|
|
6645
|
+
mutateAddToRelatedRecords(this, {
|
|
6646
|
+
value: newValues,
|
|
6647
|
+
index: 0
|
|
6648
|
+
}, _SIGNAL);
|
|
6649
|
+
}
|
|
6650
|
+
return result;
|
|
6651
|
+
}
|
|
6652
|
+
case 'shift':
|
|
6653
|
+
{
|
|
6654
|
+
const result = Reflect.apply(target[prop], receiver, args);
|
|
6655
|
+
if (result) {
|
|
6656
|
+
mutateRemoveFromRelatedRecords(this, {
|
|
6657
|
+
value: recordIdentifierFor(result),
|
|
6658
|
+
index: 0
|
|
6659
|
+
}, _SIGNAL);
|
|
6660
|
+
}
|
|
6661
|
+
return result;
|
|
6662
|
+
}
|
|
6663
|
+
case 'sort':
|
|
6664
|
+
{
|
|
6665
|
+
const result = Reflect.apply(target[prop], receiver, args);
|
|
6666
|
+
mutateSortRelatedRecords(this, result.map(recordIdentifierFor), _SIGNAL);
|
|
6667
|
+
return result;
|
|
6668
|
+
}
|
|
6669
|
+
case 'splice':
|
|
6670
|
+
{
|
|
6671
|
+
const [start, deleteCount, ...adds] = args;
|
|
6672
|
+
|
|
6673
|
+
// detect a full replace
|
|
6674
|
+
if (start === 0 && deleteCount === this[SOURCE].length) {
|
|
6675
|
+
const newValues = extractIdentifiersFromRecords(adds);
|
|
6676
|
+
assertNoDuplicates(this, target, currentState => currentState.splice(start, deleteCount, ...newValues), `Cannot replace a hasMany's state with a new state that contains duplicates.`);
|
|
6677
|
+
if (macroCondition(getGlobalConfig().WarpDriveMirror.deprecations.DEPRECATE_MANY_ARRAY_DUPLICATES)) {
|
|
6678
|
+
// dedupe
|
|
6679
|
+
const current = new Set(adds);
|
|
6680
|
+
const unique = Array.from(current);
|
|
6681
|
+
const newArgs = [start, deleteCount].concat(unique);
|
|
6682
|
+
const result = Reflect.apply(target[prop], receiver, newArgs);
|
|
6683
|
+
mutateReplaceRelatedRecords(this, extractIdentifiersFromRecords(unique), _SIGNAL);
|
|
6684
|
+
return result;
|
|
6685
|
+
}
|
|
6686
|
+
|
|
6687
|
+
// else, no dedupe, error on duplicates
|
|
6688
|
+
const result = Reflect.apply(target[prop], receiver, args);
|
|
6689
|
+
mutateReplaceRelatedRecords(this, newValues, _SIGNAL);
|
|
6690
|
+
return result;
|
|
6691
|
+
}
|
|
6692
|
+
const newValues = extractIdentifiersFromRecords(adds);
|
|
6693
|
+
assertNoDuplicates(this, target, currentState => currentState.splice(start, deleteCount, ...newValues), `Cannot splice a hasMany's state with a new state that contains duplicates.`);
|
|
6694
|
+
if (macroCondition(getGlobalConfig().WarpDriveMirror.deprecations.DEPRECATE_MANY_ARRAY_DUPLICATES)) {
|
|
6695
|
+
// dedupe
|
|
6696
|
+
const currentState = target.slice();
|
|
6697
|
+
currentState.splice(start, deleteCount);
|
|
6698
|
+
const seen = new Set(currentState);
|
|
6699
|
+
const unique = [];
|
|
6700
|
+
adds.forEach(item => {
|
|
6701
|
+
const identifier = recordIdentifierFor(item);
|
|
6702
|
+
if (!seen.has(identifier)) {
|
|
6703
|
+
seen.add(identifier);
|
|
6704
|
+
unique.push(item);
|
|
6705
|
+
}
|
|
6706
|
+
});
|
|
6707
|
+
const newArgs = [start, deleteCount, ...unique];
|
|
6708
|
+
const result = Reflect.apply(target[prop], receiver, newArgs);
|
|
6709
|
+
if (deleteCount > 0) {
|
|
6710
|
+
mutateRemoveFromRelatedRecords(this, {
|
|
6711
|
+
value: result.map(recordIdentifierFor),
|
|
6712
|
+
index: start
|
|
6713
|
+
}, _SIGNAL);
|
|
6714
|
+
}
|
|
6715
|
+
if (unique.length > 0) {
|
|
6716
|
+
mutateAddToRelatedRecords(this, {
|
|
6717
|
+
value: extractIdentifiersFromRecords(unique),
|
|
6718
|
+
index: start
|
|
6719
|
+
}, _SIGNAL);
|
|
6720
|
+
}
|
|
6721
|
+
return result;
|
|
6722
|
+
}
|
|
6723
|
+
|
|
6724
|
+
// else, no dedupe, error on duplicates
|
|
6725
|
+
const result = Reflect.apply(target[prop], receiver, args);
|
|
6726
|
+
if (deleteCount > 0) {
|
|
6727
|
+
mutateRemoveFromRelatedRecords(this, {
|
|
6728
|
+
value: result.map(recordIdentifierFor),
|
|
6729
|
+
index: start
|
|
6730
|
+
}, _SIGNAL);
|
|
6731
|
+
}
|
|
6732
|
+
if (newValues.length > 0) {
|
|
6733
|
+
mutateAddToRelatedRecords(this, {
|
|
6734
|
+
value: newValues,
|
|
6735
|
+
index: start
|
|
6736
|
+
}, _SIGNAL);
|
|
6737
|
+
}
|
|
6738
|
+
return result;
|
|
6739
|
+
}
|
|
6740
|
+
default:
|
|
6741
|
+
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
|
|
6742
|
+
{
|
|
6743
|
+
throw new Error(`unable to convert ${prop} into a transaction that updates the cache state for this record array`);
|
|
6744
|
+
}
|
|
6745
|
+
})() : {};
|
|
6042
6746
|
}
|
|
6043
|
-
|
|
6747
|
+
}
|
|
6748
|
+
notify() {
|
|
6749
|
+
const signal = this[ARRAY_SIGNAL];
|
|
6750
|
+
signal.shouldReset = true;
|
|
6751
|
+
notifyArray(this);
|
|
6752
|
+
}
|
|
6753
|
+
|
|
6754
|
+
/**
|
|
6755
|
+
Reloads all of the records in the manyArray. If the manyArray
|
|
6756
|
+
holds a relationship that was originally fetched using a links url
|
|
6757
|
+
EmberData will revisit the original links url to repopulate the
|
|
6758
|
+
relationship.
|
|
6759
|
+
If the ManyArray holds the result of a `store.query()` reload will
|
|
6760
|
+
re-run the original query.
|
|
6761
|
+
Example
|
|
6762
|
+
```javascript
|
|
6763
|
+
let user = store.peekRecord('user', '1')
|
|
6764
|
+
await login(user);
|
|
6765
|
+
let permissions = await user.permissions;
|
|
6766
|
+
await permissions.reload();
|
|
6767
|
+
```
|
|
6768
|
+
@method reload
|
|
6769
|
+
@public
|
|
6770
|
+
*/
|
|
6771
|
+
reload(options) {
|
|
6772
|
+
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
|
|
6044
6773
|
if (!test) {
|
|
6045
|
-
throw new Error(`Expected
|
|
6774
|
+
throw new Error(`Expected the manager for ManyArray to implement reloadHasMany`);
|
|
6046
6775
|
}
|
|
6047
|
-
})(
|
|
6048
|
-
|
|
6049
|
-
|
|
6050
|
-
if ('error' in peeked) {
|
|
6051
|
-
const content = shouldHydrate ? maybeUpdateUiObjects(store, context.request, {
|
|
6052
|
-
shouldHydrate,
|
|
6053
|
-
identifier
|
|
6054
|
-
}, peeked.content, true) : peeked.content;
|
|
6055
|
-
const newError = cloneError(peeked);
|
|
6056
|
-
newError.content = content;
|
|
6057
|
-
throw newError;
|
|
6058
|
-
}
|
|
6059
|
-
const result = shouldHydrate ? maybeUpdateUiObjects(store, context.request, {
|
|
6060
|
-
shouldHydrate,
|
|
6061
|
-
identifier
|
|
6062
|
-
}, peeked.content, true) : peeked.content;
|
|
6063
|
-
return result;
|
|
6064
|
-
}
|
|
6065
|
-
};
|
|
6066
|
-
function copyDocumentProperties(target, source) {
|
|
6067
|
-
if ('links' in source) {
|
|
6068
|
-
target.links = source.links;
|
|
6776
|
+
})(typeof this._manager.reloadHasMany === 'function') : {};
|
|
6777
|
+
// TODO this is odd, we don't ask the store for anything else like this?
|
|
6778
|
+
return this._manager.reloadHasMany(this.key, options);
|
|
6069
6779
|
}
|
|
6070
|
-
|
|
6071
|
-
|
|
6780
|
+
|
|
6781
|
+
/**
|
|
6782
|
+
Saves all of the records in the `ManyArray`.
|
|
6783
|
+
Note: this API can only be used in legacy mode with a configured Adapter.
|
|
6784
|
+
Example
|
|
6785
|
+
```javascript
|
|
6786
|
+
const { content: { data: inbox } } = await store.request(findRecord({ type: 'inbox', id: '1' }));
|
|
6787
|
+
let messages = await inbox.messages;
|
|
6788
|
+
messages.forEach((message) => {
|
|
6789
|
+
message.isRead = true;
|
|
6790
|
+
});
|
|
6791
|
+
messages.save();
|
|
6792
|
+
```
|
|
6793
|
+
@method save
|
|
6794
|
+
@public
|
|
6795
|
+
@return {PromiseArray} promise
|
|
6796
|
+
*/
|
|
6797
|
+
|
|
6798
|
+
/**
|
|
6799
|
+
Create a child record within the owner
|
|
6800
|
+
@method createRecord
|
|
6801
|
+
@public
|
|
6802
|
+
@param {Object} hash
|
|
6803
|
+
@return {Model} record
|
|
6804
|
+
*/
|
|
6805
|
+
createRecord(hash) {
|
|
6806
|
+
const {
|
|
6807
|
+
store
|
|
6808
|
+
} = this;
|
|
6809
|
+
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
|
|
6810
|
+
if (!test) {
|
|
6811
|
+
throw new Error(`Expected modelName to be set`);
|
|
6812
|
+
}
|
|
6813
|
+
})(this.modelName) : {};
|
|
6814
|
+
const record = store.createRecord(this.modelName, hash);
|
|
6815
|
+
this.push(record);
|
|
6816
|
+
return record;
|
|
6072
6817
|
}
|
|
6073
|
-
|
|
6074
|
-
|
|
6818
|
+
destroy() {
|
|
6819
|
+
super.destroy(false);
|
|
6075
6820
|
}
|
|
6076
6821
|
}
|
|
6077
|
-
|
|
6078
|
-
|
|
6079
|
-
|
|
6080
|
-
|
|
6081
|
-
|
|
6082
|
-
|
|
6083
|
-
|
|
6084
|
-
|
|
6085
|
-
|
|
6086
|
-
|
|
6822
|
+
RelatedCollection.prototype.isAsync = false;
|
|
6823
|
+
RelatedCollection.prototype.isPolymorphic = false;
|
|
6824
|
+
RelatedCollection.prototype.identifier = null;
|
|
6825
|
+
RelatedCollection.prototype.cache = null;
|
|
6826
|
+
RelatedCollection.prototype._inverseIsAsync = false;
|
|
6827
|
+
RelatedCollection.prototype.key = '';
|
|
6828
|
+
RelatedCollection.prototype.DEPRECATED_CLASS_NAME = 'ManyArray';
|
|
6829
|
+
function assertRecordPassedToHasMany(record) {
|
|
6830
|
+
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
|
|
6831
|
+
if (!test) {
|
|
6832
|
+
throw new Error(`All elements of a hasMany relationship must be instances of Model, you passed ${typeof record}`);
|
|
6833
|
+
}
|
|
6834
|
+
})(function () {
|
|
6835
|
+
try {
|
|
6836
|
+
recordIdentifierFor(record);
|
|
6837
|
+
return true;
|
|
6838
|
+
} catch {
|
|
6839
|
+
return false;
|
|
6840
|
+
}
|
|
6841
|
+
}()) : {};
|
|
6842
|
+
}
|
|
6843
|
+
function extractIdentifiersFromRecords(records) {
|
|
6844
|
+
return records.map(extractIdentifierFromRecord);
|
|
6845
|
+
}
|
|
6846
|
+
function extractIdentifierFromRecord(recordOrPromiseRecord) {
|
|
6847
|
+
assertRecordPassedToHasMany(recordOrPromiseRecord);
|
|
6848
|
+
return recordIdentifierFor(recordOrPromiseRecord);
|
|
6849
|
+
}
|
|
6850
|
+
function assertNoDuplicates(collection, target, callback, reason) {
|
|
6851
|
+
const state = target.slice();
|
|
6852
|
+
callback(state);
|
|
6853
|
+
if (state.length !== new Set(state).size) {
|
|
6854
|
+
const duplicates = state.filter((currentValue, currentIndex) => state.indexOf(currentValue) !== currentIndex);
|
|
6855
|
+
if (macroCondition(getGlobalConfig().WarpDriveMirror.deprecations.DEPRECATE_MANY_ARRAY_DUPLICATES)) {
|
|
6856
|
+
deprecate(`${reason} This behavior is deprecated. Found duplicates for the following records within the new state provided to \`<${collection.identifier.type}:${collection.identifier.id || collection.identifier.lid}>.${collection.key}\`\n\t- ${Array.from(new Set(duplicates)).map(r => isStableIdentifier(r) ? r.lid : recordIdentifierFor(r).lid).sort((a, b) => a.localeCompare(b)).join('\n\t- ')}`, false, {
|
|
6857
|
+
id: 'ember-data-mirror:deprecate-many-array-duplicates',
|
|
6858
|
+
for: 'ember-data-mirror',
|
|
6859
|
+
until: '6.0',
|
|
6860
|
+
since: {
|
|
6861
|
+
enabled: '5.3',
|
|
6862
|
+
available: '4.13'
|
|
6863
|
+
}
|
|
6864
|
+
});
|
|
6865
|
+
} else {
|
|
6866
|
+
throw new Error(`${reason} Found duplicates for the following records within the new state provided to \`<${collection.identifier.type}:${collection.identifier.id || collection.identifier.lid}>.${collection.key}\`\n\t- ${Array.from(new Set(duplicates)).map(r => isStableIdentifier(r) ? r.lid : recordIdentifierFor(r).lid).sort((a, b) => a.localeCompare(b)).join('\n\t- ')}`);
|
|
6867
|
+
}
|
|
6087
6868
|
}
|
|
6088
|
-
return document.response?.status !== 204;
|
|
6089
6869
|
}
|
|
6090
|
-
|
|
6870
|
+
function mutateAddToRelatedRecords(collection, operationInfo, _SIGNAL) {
|
|
6871
|
+
mutate(collection, {
|
|
6872
|
+
op: 'add',
|
|
6873
|
+
record: collection.identifier,
|
|
6874
|
+
field: collection.key,
|
|
6875
|
+
...operationInfo
|
|
6876
|
+
}, _SIGNAL);
|
|
6877
|
+
}
|
|
6878
|
+
function mutateRemoveFromRelatedRecords(collection, operationInfo, _SIGNAL) {
|
|
6879
|
+
mutate(collection, {
|
|
6880
|
+
op: 'remove',
|
|
6881
|
+
record: collection.identifier,
|
|
6882
|
+
field: collection.key,
|
|
6883
|
+
...operationInfo
|
|
6884
|
+
}, _SIGNAL);
|
|
6885
|
+
}
|
|
6886
|
+
function mutateReplaceRelatedRecord(collection, operationInfo, _SIGNAL) {
|
|
6887
|
+
mutate(collection, {
|
|
6888
|
+
op: 'replaceRelatedRecord',
|
|
6889
|
+
record: collection.identifier,
|
|
6890
|
+
field: collection.key,
|
|
6891
|
+
...operationInfo
|
|
6892
|
+
}, _SIGNAL);
|
|
6893
|
+
}
|
|
6894
|
+
function mutateReplaceRelatedRecords(collection, value, _SIGNAL) {
|
|
6895
|
+
mutate(collection, {
|
|
6896
|
+
op: 'replaceRelatedRecords',
|
|
6897
|
+
record: collection.identifier,
|
|
6898
|
+
field: collection.key,
|
|
6899
|
+
value
|
|
6900
|
+
}, _SIGNAL);
|
|
6901
|
+
}
|
|
6902
|
+
function mutateSortRelatedRecords(collection, value, _SIGNAL) {
|
|
6903
|
+
mutate(collection, {
|
|
6904
|
+
op: 'sortRelatedRecords',
|
|
6905
|
+
record: collection.identifier,
|
|
6906
|
+
field: collection.key,
|
|
6907
|
+
value
|
|
6908
|
+
}, _SIGNAL);
|
|
6909
|
+
}
|
|
6910
|
+
function mutate(collection, mutation, _SIGNAL) {
|
|
6911
|
+
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
|
|
6912
|
+
if (!test) {
|
|
6913
|
+
throw new Error(`Expected the manager for ManyArray to implement mutate`);
|
|
6914
|
+
}
|
|
6915
|
+
})(typeof collection._manager.mutate === 'function') : {};
|
|
6916
|
+
collection._manager.mutate(mutation);
|
|
6917
|
+
addToTransaction(_SIGNAL);
|
|
6918
|
+
}
|
|
6919
|
+
export { ARRAY_SIGNAL as A, CacheHandler as C, IdentifierArray as I, MUTATE as M, RecordArrayManager as R, Store as S, _clearCaches as _, setIdentifierGenerationMethod as a, setIdentifierUpdateMethod as b, setIdentifierForgetMethod as c, setIdentifierResetMethod as d, setKeyInfoForResource as e, isDocumentIdentifier as f, constructResource as g, coerceId as h, isStableIdentifier as i, ensureStringId as j, Collection as k, SOURCE as l, fastPush as m, notifyArray as n, removeRecordDataFor as o, peekCache as p, setRecordIdentifier as q, recordIdentifierFor as r, storeFor as s, StoreMap as t, setCacheFor as u, normalizeModelName as v, RelatedCollection as w, log as x, logGroup as y };
|