@ember-data/store 4.8.0-alpha.6 → 4.8.0-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/addon/-debug/index.js +34 -12
- package/addon/-private/caches/identifier-cache.ts +3 -2
- package/addon/-private/caches/instance-cache.ts +70 -76
- package/addon/-private/managers/record-data-manager.ts +24 -1
- package/addon/-private/store-service.ts +41 -13
- package/addon/-private/utils/uuid-polyfill.ts +58 -56
- package/package.json +4 -4
package/addon/-debug/index.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { assert } from '@ember/debug';
|
|
2
2
|
import { DEBUG } from '@glimmer/env';
|
|
3
3
|
|
|
4
|
+
import { DEPRECATE_NON_EXPLICIT_POLYMORPHISM } from '@ember-data/private-build-infra/deprecations';
|
|
5
|
+
|
|
4
6
|
/*
|
|
5
7
|
Assert that `addedRecord` has a valid type so it can be added to the
|
|
6
8
|
relationship of the `record`.
|
|
@@ -29,18 +31,38 @@ if (DEBUG) {
|
|
|
29
31
|
};
|
|
30
32
|
|
|
31
33
|
assertPolymorphicType = function assertPolymorphicType(parentIdentifier, parentDefinition, addedIdentifier, store) {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
34
|
+
let asserted = false;
|
|
35
|
+
|
|
36
|
+
if (parentDefinition.inverseIsImplicit) {
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
if (parentDefinition.isPolymorphic) {
|
|
40
|
+
let meta = store.getSchemaDefinitionService().relationshipsDefinitionFor(addedIdentifier)[
|
|
41
|
+
parentDefinition.inverseKey
|
|
42
|
+
];
|
|
43
|
+
if (meta?.options?.as) {
|
|
44
|
+
asserted = true;
|
|
45
|
+
assert(
|
|
46
|
+
`The schema for the relationship '${parentDefinition.inverseKey}' on '${addedIdentifier.type}' type does not implement '${parentDefinition.type}' and thus cannot be assigned to the '${parentDefinition.key}' relationship in '${parentIdentifier.type}'. The definition should specify 'as: "${parentDefinition.type}"' in options.`,
|
|
47
|
+
meta.options.as === parentDefinition.type
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (DEPRECATE_NON_EXPLICIT_POLYMORPHISM && !asserted) {
|
|
53
|
+
store = store._store ? store._store : store; // allow usage with storeWrapper
|
|
54
|
+
let addedModelName = addedIdentifier.type;
|
|
55
|
+
let parentModelName = parentIdentifier.type;
|
|
56
|
+
let key = parentDefinition.key;
|
|
57
|
+
let relationshipModelName = parentDefinition.type;
|
|
58
|
+
let relationshipClass = store.modelFor(relationshipModelName);
|
|
59
|
+
let addedClass = store.modelFor(addedModelName);
|
|
60
|
+
|
|
61
|
+
let assertionMessage = `The '${addedModelName}' type does not implement '${relationshipModelName}' and thus cannot be assigned to the '${key}' relationship in '${parentModelName}'. Make it a descendant of '${relationshipModelName}' or use a mixin of the same name.`;
|
|
62
|
+
let isPolymorphic = checkPolymorphic(relationshipClass, addedClass);
|
|
63
|
+
|
|
64
|
+
assert(assertionMessage, isPolymorphic);
|
|
65
|
+
}
|
|
44
66
|
};
|
|
45
67
|
}
|
|
46
68
|
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
import { assert, warn } from '@ember/debug';
|
|
5
5
|
import { DEBUG } from '@glimmer/env';
|
|
6
6
|
|
|
7
|
-
import { getOwnConfig,
|
|
7
|
+
import { getOwnConfig, macroCondition } from '@embroider/macros';
|
|
8
8
|
|
|
9
9
|
import { LOG_IDENTIFIERS } from '@ember-data/private-build-infra/debugging';
|
|
10
10
|
import type { ExistingResourceObject, ResourceIdentifierObject } from '@ember-data/types/q/ember-data-json-api';
|
|
@@ -25,6 +25,7 @@ import coerceId from '../utils/coerce-id';
|
|
|
25
25
|
import { DEBUG_CLIENT_ORIGINATED, DEBUG_IDENTIFIER_BUCKET } from '../utils/identifer-debug-consts';
|
|
26
26
|
import isNonEmptyString from '../utils/is-non-empty-string';
|
|
27
27
|
import normalizeModelName from '../utils/normalize-model-name';
|
|
28
|
+
import installPolyfill from '../utils/uuid-polyfill';
|
|
28
29
|
|
|
29
30
|
const IDENTIFIERS = new Set();
|
|
30
31
|
|
|
@@ -36,7 +37,7 @@ const isFastBoot = typeof FastBoot !== 'undefined';
|
|
|
36
37
|
const _crypto: Crypto = isFastBoot ? (FastBoot.require('crypto') as Crypto) : window.crypto;
|
|
37
38
|
|
|
38
39
|
if (macroCondition(getOwnConfig<{ polyfillUUID: boolean }>().polyfillUUID)) {
|
|
39
|
-
|
|
40
|
+
installPolyfill();
|
|
40
41
|
}
|
|
41
42
|
|
|
42
43
|
function uuidv4(): string {
|
|
@@ -7,7 +7,8 @@ import { resolve } from 'rsvp';
|
|
|
7
7
|
import { HAS_RECORD_DATA_PACKAGE } from '@ember-data/private-build-infra';
|
|
8
8
|
import { LOG_INSTANCE_CACHE } from '@ember-data/private-build-infra/debugging';
|
|
9
9
|
import { DEPRECATE_V1_RECORD_DATA, DEPRECATE_V1CACHE_STORE_APIS } from '@ember-data/private-build-infra/deprecations';
|
|
10
|
-
import type { Graph
|
|
10
|
+
import type { Graph } from '@ember-data/record-data/-private/graph/graph';
|
|
11
|
+
import type { peekGraph } from '@ember-data/record-data/-private/graph/index';
|
|
11
12
|
import type {
|
|
12
13
|
ExistingResourceIdentifierObject,
|
|
13
14
|
ExistingResourceObject,
|
|
@@ -128,44 +129,6 @@ export class InstanceCache {
|
|
|
128
129
|
reference: new WeakMap<StableRecordIdentifier, RecordReference>(),
|
|
129
130
|
};
|
|
130
131
|
|
|
131
|
-
recordIsLoaded(identifier: StableRecordIdentifier, filterDeleted: boolean = false) {
|
|
132
|
-
const recordData = this.__instances.recordData.get(identifier);
|
|
133
|
-
if (!recordData) {
|
|
134
|
-
return false;
|
|
135
|
-
}
|
|
136
|
-
const isNew = recordData.isNew(identifier);
|
|
137
|
-
const isEmpty = recordData.isEmpty(identifier);
|
|
138
|
-
|
|
139
|
-
// if we are new we must consider ourselves loaded
|
|
140
|
-
if (isNew) {
|
|
141
|
-
return !recordData.isDeleted(identifier);
|
|
142
|
-
}
|
|
143
|
-
// even if we have a past request, if we are now empty we are not loaded
|
|
144
|
-
// typically this is true after an unloadRecord call
|
|
145
|
-
|
|
146
|
-
// if we are not empty, not new && we have a fulfilled request then we are loaded
|
|
147
|
-
// we should consider allowing for something to be loaded that is simply "not empty".
|
|
148
|
-
// which is how RecordState currently handles this case; however, RecordState is buggy
|
|
149
|
-
// in that it does not account for unloading.
|
|
150
|
-
return filterDeleted && recordData.isDeletionCommitted(identifier) ? false : !isEmpty;
|
|
151
|
-
|
|
152
|
-
/*
|
|
153
|
-
const req = this.store.getRequestStateService();
|
|
154
|
-
const fulfilled = req.getLastRequestForRecord(identifier);
|
|
155
|
-
const isLocallyLoaded = !isEmpty;
|
|
156
|
-
const isLoading =
|
|
157
|
-
!isLocallyLoaded &&
|
|
158
|
-
fulfilled === null &&
|
|
159
|
-
req.getPendingRequestsForRecord(identifier).some((req) => req.type === 'query');
|
|
160
|
-
|
|
161
|
-
if (isEmpty || (filterDeleted && recordData.isDeletionCommitted(identifier)) || isLoading) {
|
|
162
|
-
return false;
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
return true;
|
|
166
|
-
*/
|
|
167
|
-
}
|
|
168
|
-
|
|
169
132
|
constructor(store: Store) {
|
|
170
133
|
this.store = store;
|
|
171
134
|
|
|
@@ -178,25 +141,24 @@ export class InstanceCache {
|
|
|
178
141
|
|
|
179
142
|
store.identifierCache.__configureMerge(
|
|
180
143
|
(identifier: StableRecordIdentifier, matchedIdentifier: StableRecordIdentifier, resourceData) => {
|
|
181
|
-
let
|
|
144
|
+
let keptIdentifier = identifier;
|
|
182
145
|
if (identifier.id !== matchedIdentifier.id) {
|
|
183
|
-
|
|
184
|
-
'id' in resourceData && identifier.id === resourceData.id ? identifier : matchedIdentifier;
|
|
146
|
+
keptIdentifier = 'id' in resourceData && identifier.id === resourceData.id ? identifier : matchedIdentifier;
|
|
185
147
|
} else if (identifier.type !== matchedIdentifier.type) {
|
|
186
|
-
|
|
148
|
+
keptIdentifier =
|
|
187
149
|
'type' in resourceData && identifier.type === resourceData.type ? identifier : matchedIdentifier;
|
|
188
150
|
}
|
|
189
|
-
let
|
|
151
|
+
let staleIdentifier = identifier === keptIdentifier ? matchedIdentifier : identifier;
|
|
190
152
|
|
|
191
153
|
// check for duplicate entities
|
|
192
|
-
let
|
|
193
|
-
let
|
|
194
|
-
let
|
|
195
|
-
let
|
|
154
|
+
let keptHasRecord = this.__instances.record.has(keptIdentifier);
|
|
155
|
+
let staleHasRecord = this.__instances.record.has(staleIdentifier);
|
|
156
|
+
let keptRecordData = this.__instances.recordData.get(keptIdentifier) || null;
|
|
157
|
+
let staleRecordData = this.__instances.recordData.get(staleIdentifier) || null;
|
|
196
158
|
|
|
197
159
|
// we cannot merge entities when both have records
|
|
198
160
|
// (this may not be strictly true, we could probably swap the recordData the record points at)
|
|
199
|
-
if (
|
|
161
|
+
if (keptHasRecord && staleHasRecord) {
|
|
200
162
|
// TODO we probably don't need to throw these errors anymore
|
|
201
163
|
// we can probably just "swap" what data source the abandoned
|
|
202
164
|
// record points at so long as
|
|
@@ -210,7 +172,7 @@ export class InstanceCache {
|
|
|
210
172
|
}:${String(matchedIdentifier.id)} (${matchedIdentifier.lid})'`
|
|
211
173
|
);
|
|
212
174
|
}
|
|
213
|
-
|
|
175
|
+
|
|
214
176
|
assert(
|
|
215
177
|
`Failed to update the RecordIdentifier '${identifier.type}:${String(identifier.id)} (${
|
|
216
178
|
identifier.lid
|
|
@@ -220,33 +182,26 @@ export class InstanceCache {
|
|
|
220
182
|
);
|
|
221
183
|
}
|
|
222
184
|
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
185
|
+
let recordData = keptRecordData || staleRecordData;
|
|
186
|
+
|
|
187
|
+
if (recordData) {
|
|
188
|
+
recordData.sync({
|
|
189
|
+
op: 'mergeIdentifiers',
|
|
190
|
+
record: staleIdentifier,
|
|
191
|
+
value: keptIdentifier,
|
|
192
|
+
});
|
|
193
|
+
} else if (HAS_RECORD_DATA_PACKAGE) {
|
|
194
|
+
// TODO notify cache always, this requires it always being a singleton
|
|
195
|
+
// and not ever specific to one record-data
|
|
196
|
+
this.store.__private_singleton_recordData?.sync({
|
|
197
|
+
op: 'mergeIdentifiers',
|
|
198
|
+
record: staleIdentifier,
|
|
199
|
+
value: keptIdentifier,
|
|
200
|
+
});
|
|
226
201
|
}
|
|
227
202
|
|
|
228
|
-
if (
|
|
229
|
-
|
|
230
|
-
return intendedIdentifier;
|
|
231
|
-
|
|
232
|
-
// only the other has a RecordData
|
|
233
|
-
// OR only the other has a Record
|
|
234
|
-
} else if (
|
|
235
|
-
(imRecordData === null && otherRecordData !== null) ||
|
|
236
|
-
(imRecordData && !imHasRecord && otherRecordData && otherHasRecord)
|
|
237
|
-
) {
|
|
238
|
-
if (imRecordData) {
|
|
239
|
-
// TODO check if we are retained in any async relationships
|
|
240
|
-
// TODO probably need to release other things
|
|
241
|
-
// im.destroy();
|
|
242
|
-
}
|
|
243
|
-
imRecordData = otherRecordData!;
|
|
244
|
-
// TODO do we need to notify the id change?
|
|
245
|
-
// TODO swap recordIdentifierFor result?
|
|
246
|
-
|
|
247
|
-
// just use im
|
|
248
|
-
} else {
|
|
249
|
-
// otherIm.destroy();
|
|
203
|
+
if (staleRecordData === null) {
|
|
204
|
+
return keptIdentifier;
|
|
250
205
|
}
|
|
251
206
|
|
|
252
207
|
/*
|
|
@@ -259,7 +214,8 @@ export class InstanceCache {
|
|
|
259
214
|
}
|
|
260
215
|
*/
|
|
261
216
|
|
|
262
|
-
|
|
217
|
+
this.unloadRecord(staleIdentifier);
|
|
218
|
+
return keptIdentifier;
|
|
263
219
|
}
|
|
264
220
|
);
|
|
265
221
|
}
|
|
@@ -365,6 +321,44 @@ export class InstanceCache {
|
|
|
365
321
|
return reference;
|
|
366
322
|
}
|
|
367
323
|
|
|
324
|
+
recordIsLoaded(identifier: StableRecordIdentifier, filterDeleted: boolean = false) {
|
|
325
|
+
const recordData = this.__instances.recordData.get(identifier);
|
|
326
|
+
if (!recordData) {
|
|
327
|
+
return false;
|
|
328
|
+
}
|
|
329
|
+
const isNew = recordData.isNew(identifier);
|
|
330
|
+
const isEmpty = recordData.isEmpty(identifier);
|
|
331
|
+
|
|
332
|
+
// if we are new we must consider ourselves loaded
|
|
333
|
+
if (isNew) {
|
|
334
|
+
return !recordData.isDeleted(identifier);
|
|
335
|
+
}
|
|
336
|
+
// even if we have a past request, if we are now empty we are not loaded
|
|
337
|
+
// typically this is true after an unloadRecord call
|
|
338
|
+
|
|
339
|
+
// if we are not empty, not new && we have a fulfilled request then we are loaded
|
|
340
|
+
// we should consider allowing for something to be loaded that is simply "not empty".
|
|
341
|
+
// which is how RecordState currently handles this case; however, RecordState is buggy
|
|
342
|
+
// in that it does not account for unloading.
|
|
343
|
+
return filterDeleted && recordData.isDeletionCommitted(identifier) ? false : !isEmpty;
|
|
344
|
+
|
|
345
|
+
/*
|
|
346
|
+
const req = this.store.getRequestStateService();
|
|
347
|
+
const fulfilled = req.getLastRequestForRecord(identifier);
|
|
348
|
+
const isLocallyLoaded = !isEmpty;
|
|
349
|
+
const isLoading =
|
|
350
|
+
!isLocallyLoaded &&
|
|
351
|
+
fulfilled === null &&
|
|
352
|
+
req.getPendingRequestsForRecord(identifier).some((req) => req.type === 'query');
|
|
353
|
+
|
|
354
|
+
if (isEmpty || (filterDeleted && recordData.isDeletionCommitted(identifier)) || isLoading) {
|
|
355
|
+
return false;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
return true;
|
|
359
|
+
*/
|
|
360
|
+
}
|
|
361
|
+
|
|
368
362
|
createSnapshot(identifier: StableRecordIdentifier, options: FindOptions = {}): Snapshot {
|
|
369
363
|
return new Snapshot(options, identifier, this.store);
|
|
370
364
|
}
|
|
@@ -6,7 +6,7 @@ import type {
|
|
|
6
6
|
SingleResourceRelationship,
|
|
7
7
|
} from '@ember-data/types/q/ember-data-json-api';
|
|
8
8
|
import type { StableRecordIdentifier } from '@ember-data/types/q/identifier';
|
|
9
|
-
import type { ChangedAttributesHash, RecordData, RecordDataV1 } from '@ember-data/types/q/record-data';
|
|
9
|
+
import type { ChangedAttributesHash, MergeOperation, RecordData, RecordDataV1 } from '@ember-data/types/q/record-data';
|
|
10
10
|
import type { JsonApiResource, JsonApiValidationError } from '@ember-data/types/q/record-data-json-api';
|
|
11
11
|
import type { Dict } from '@ember-data/types/q/utils';
|
|
12
12
|
|
|
@@ -128,6 +128,25 @@ export class NonSingletonRecordDataManager implements RecordData {
|
|
|
128
128
|
return recordData.pushData(identifier, data, hasRecord);
|
|
129
129
|
}
|
|
130
130
|
|
|
131
|
+
/**
|
|
132
|
+
* Perform an operation on the cache to update the remote state.
|
|
133
|
+
*
|
|
134
|
+
* Note: currently the only valid operation is a MergeOperation
|
|
135
|
+
* which occurs when a collision of identifiers is detected.
|
|
136
|
+
*
|
|
137
|
+
* @method sync
|
|
138
|
+
* @public
|
|
139
|
+
* @param op the operation to perform
|
|
140
|
+
* @returns {void}
|
|
141
|
+
*/
|
|
142
|
+
sync(op: MergeOperation): void {
|
|
143
|
+
const recordData = this.#recordData;
|
|
144
|
+
if (this.#isDeprecated(recordData)) {
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
recordData.sync(op);
|
|
148
|
+
}
|
|
149
|
+
|
|
131
150
|
/**
|
|
132
151
|
* Update resource data with a local mutation. Currently supports operations
|
|
133
152
|
* on relationships only.
|
|
@@ -740,6 +759,10 @@ export class SingletonRecordDataManager implements RecordData {
|
|
|
740
759
|
return this.#recordData(identifier).pushData(identifier, data, hasRecord);
|
|
741
760
|
}
|
|
742
761
|
|
|
762
|
+
sync(op: MergeOperation): void {
|
|
763
|
+
this.#recordData(op.record).sync(op);
|
|
764
|
+
}
|
|
765
|
+
|
|
743
766
|
clientDidCreate(identifier: StableRecordIdentifier, options?: Dict<unknown>): Dict<unknown> {
|
|
744
767
|
return this.#recordData(identifier).clientDidCreate(identifier, options);
|
|
745
768
|
}
|
|
@@ -407,15 +407,53 @@ class Store extends Service {
|
|
|
407
407
|
* for use when information about a resource's schema needs
|
|
408
408
|
* to be queried.
|
|
409
409
|
*
|
|
410
|
-
* This method can only be called
|
|
411
|
-
*
|
|
410
|
+
* This method can only be called more than once, but only one schema
|
|
411
|
+
* definition service may exist. Therefore if you wish to chain services
|
|
412
|
+
* you must lookup the existing service and close over it with the new
|
|
413
|
+
* service by calling `getSchemaDefinitionService` prior to registration.
|
|
414
|
+
*
|
|
415
|
+
* For Example:
|
|
416
|
+
*
|
|
417
|
+
* ```ts
|
|
418
|
+
* import Store from '@ember-data/store';
|
|
419
|
+
*
|
|
420
|
+
* class SchemaDelegator {
|
|
421
|
+
* constructor(schema) {
|
|
422
|
+
* this._schema = schema;
|
|
423
|
+
* }
|
|
424
|
+
*
|
|
425
|
+
* doesTypeExist(type: string): boolean {
|
|
426
|
+
* if (AbstractSchemas.has(type)) {
|
|
427
|
+
* return true;
|
|
428
|
+
* }
|
|
429
|
+
* return this._schema.doesTypeExist(type);
|
|
430
|
+
* }
|
|
431
|
+
*
|
|
432
|
+
* attributesDefinitionFor(identifier: RecordIdentifier | { type: string }): AttributesSchema {
|
|
433
|
+
* return this._schema.attributesDefinitionFor(identifier);
|
|
434
|
+
* }
|
|
435
|
+
*
|
|
436
|
+
* relationshipsDefinitionFor(identifier: RecordIdentifier | { type: string }): RelationshipsSchema {
|
|
437
|
+
* const schema = AbstractSchemas.get(identifier.type);
|
|
438
|
+
* return schema || this._schema.relationshipsDefinitionFor(identifier);
|
|
439
|
+
* }
|
|
440
|
+
* }
|
|
441
|
+
*
|
|
442
|
+
* export default class extends Store {
|
|
443
|
+
* constructor(...args) {
|
|
444
|
+
* super(...args);
|
|
445
|
+
*
|
|
446
|
+
* const schema = this.getSchemaDefinitionService();
|
|
447
|
+
* this.registerSchemaDefinitionService(new SchemaDelegator(schema));
|
|
448
|
+
* }
|
|
449
|
+
* }
|
|
450
|
+
* ```
|
|
412
451
|
*
|
|
413
452
|
* @method registerSchemaDefinitionService
|
|
414
453
|
* @param {SchemaDefinitionService} schema
|
|
415
454
|
* @public
|
|
416
455
|
*/
|
|
417
456
|
registerSchemaDefinitionService(schema: SchemaDefinitionService) {
|
|
418
|
-
assert(`Cannot register a schema definition service when one already exists`, !this._schemaDefinitionService);
|
|
419
457
|
this._schemaDefinitionService = schema;
|
|
420
458
|
}
|
|
421
459
|
|
|
@@ -2575,16 +2613,6 @@ class Store extends Service {
|
|
|
2575
2613
|
this.recordArrayManager.destroy();
|
|
2576
2614
|
this.identifierCache.destroy();
|
|
2577
2615
|
|
|
2578
|
-
if (HAS_RECORD_DATA_PACKAGE) {
|
|
2579
|
-
const peekGraph = (
|
|
2580
|
-
importSync('@ember-data/record-data/-private') as typeof import('@ember-data/record-data/-private')
|
|
2581
|
-
).peekGraph;
|
|
2582
|
-
let graph = peekGraph(this);
|
|
2583
|
-
if (graph) {
|
|
2584
|
-
graph.willDestroy();
|
|
2585
|
-
}
|
|
2586
|
-
}
|
|
2587
|
-
|
|
2588
2616
|
this.unloadAll();
|
|
2589
2617
|
|
|
2590
2618
|
if (DEBUG) {
|
|
@@ -1,71 +1,73 @@
|
|
|
1
1
|
/**
|
|
2
2
|
@module @ember-data/store
|
|
3
3
|
*/
|
|
4
|
-
const isFastBoot = typeof FastBoot !== 'undefined';
|
|
5
|
-
const CRYPTO: Crypto = isFastBoot ? (FastBoot.require('crypto') as Crypto) : window.crypto;
|
|
6
|
-
|
|
7
4
|
interface FastbootCrypto {
|
|
8
5
|
randomFillSync(v: Uint8Array): Uint8Array;
|
|
9
6
|
}
|
|
10
7
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
const
|
|
14
|
-
// WHATWG crypto RNG - http://wiki.whatwg.org/wiki/Crypto
|
|
15
|
-
let rnds8 = new Uint8Array(16);
|
|
8
|
+
export default function installPolyfill() {
|
|
9
|
+
const isFastBoot = typeof FastBoot !== 'undefined';
|
|
10
|
+
const CRYPTO: Crypto = isFastBoot ? (FastBoot.require('crypto') as Crypto) : window.crypto;
|
|
16
11
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
12
|
+
if (!CRYPTO.randomUUID) {
|
|
13
|
+
// we might be able to optimize this by requesting more bytes than we need at a time
|
|
14
|
+
const rng = function (): Uint8Array {
|
|
15
|
+
// WHATWG crypto RNG - http://wiki.whatwg.org/wiki/Crypto
|
|
16
|
+
let rnds8 = new Uint8Array(16);
|
|
20
17
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
};
|
|
18
|
+
if (!CRYPTO.getRandomValues && !isFastBoot) {
|
|
19
|
+
throw new Error(`Unable to generate bytes for UUID`);
|
|
20
|
+
}
|
|
25
21
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
const byteToHex: string[] = [];
|
|
31
|
-
for (let i = 0; i < 256; ++i) {
|
|
32
|
-
byteToHex[i] = (i + 0x100).toString(16).substr(1);
|
|
33
|
-
}
|
|
22
|
+
return CRYPTO.getRandomValues
|
|
23
|
+
? CRYPTO.getRandomValues(rnds8)
|
|
24
|
+
: (CRYPTO as unknown as FastbootCrypto).randomFillSync(rnds8);
|
|
25
|
+
};
|
|
34
26
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
bth
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
27
|
+
/*
|
|
28
|
+
* Convert array of 16 byte values to UUID string format of the form:
|
|
29
|
+
* XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX
|
|
30
|
+
*/
|
|
31
|
+
const byteToHex: string[] = [];
|
|
32
|
+
for (let i = 0; i < 256; ++i) {
|
|
33
|
+
byteToHex[i] = (i + 0x100).toString(16).substr(1);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const bytesToUuid = function (buf: Uint8Array) {
|
|
37
|
+
let bth = byteToHex;
|
|
38
|
+
// join used to fix memory issue caused by concatenation: https://bugs.chromium.org/p/v8/issues/detail?id=3175#c4
|
|
39
|
+
return [
|
|
40
|
+
bth[buf[0]],
|
|
41
|
+
bth[buf[1]],
|
|
42
|
+
bth[buf[2]],
|
|
43
|
+
bth[buf[3]],
|
|
44
|
+
'-',
|
|
45
|
+
bth[buf[4]],
|
|
46
|
+
bth[buf[5]],
|
|
47
|
+
'-',
|
|
48
|
+
bth[buf[6]],
|
|
49
|
+
bth[buf[7]],
|
|
50
|
+
'-',
|
|
51
|
+
bth[buf[8]],
|
|
52
|
+
bth[buf[9]],
|
|
53
|
+
'-',
|
|
54
|
+
bth[buf[10]],
|
|
55
|
+
bth[buf[11]],
|
|
56
|
+
bth[buf[12]],
|
|
57
|
+
bth[buf[13]],
|
|
58
|
+
bth[buf[14]],
|
|
59
|
+
bth[buf[15]],
|
|
60
|
+
].join('');
|
|
61
|
+
};
|
|
61
62
|
|
|
62
|
-
|
|
63
|
-
|
|
63
|
+
CRYPTO.randomUUID = function uuidv4(): string {
|
|
64
|
+
let rnds = rng();
|
|
64
65
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
66
|
+
// Per 4.4, set bits for version and `clock_seq_hi_and_reserved`
|
|
67
|
+
rnds[6] = (rnds[6] & 0x0f) | 0x40;
|
|
68
|
+
rnds[8] = (rnds[8] & 0x3f) | 0x80;
|
|
68
69
|
|
|
69
|
-
|
|
70
|
-
|
|
70
|
+
return bytesToUuid(rnds);
|
|
71
|
+
};
|
|
72
|
+
}
|
|
71
73
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ember-data/store",
|
|
3
|
-
"version": "4.8.0-
|
|
3
|
+
"version": "4.8.0-beta.0",
|
|
4
4
|
"description": "The core of EmberData. Provides the Store service which coordinates the cache with the network and presentation layers.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ember-addon"
|
|
@@ -21,8 +21,8 @@
|
|
|
21
21
|
"start": "ember serve"
|
|
22
22
|
},
|
|
23
23
|
"dependencies": {
|
|
24
|
-
"@ember-data/canary-features": "4.8.0-
|
|
25
|
-
"@ember-data/private-build-infra": "4.8.0-
|
|
24
|
+
"@ember-data/canary-features": "4.8.0-beta.0",
|
|
25
|
+
"@ember-data/private-build-infra": "4.8.0-beta.0",
|
|
26
26
|
"@ember/string": "^3.0.0",
|
|
27
27
|
"@embroider/macros": "^1.8.3",
|
|
28
28
|
"@glimmer/tracking": "^1.1.2",
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
"ember-cli-typescript": "^5.1.0"
|
|
34
34
|
},
|
|
35
35
|
"devDependencies": {
|
|
36
|
-
"@ember-data/unpublished-test-infra": "4.8.0-
|
|
36
|
+
"@ember-data/unpublished-test-infra": "4.8.0-beta.0",
|
|
37
37
|
"@ember/optional-features": "^2.0.0",
|
|
38
38
|
"@ember/test-helpers": "~2.7.0",
|
|
39
39
|
"@types/ember": "^4.0.1",
|