-
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* [FEAT identifiers] adds ts interfaces for identifiers work
* [FEAT identifiers] adds uuid-v4 util * [FEAT identifiers] adds ts-interface for json-api concepts w/ember-data flavor * [FEAT identifier] adds toString to interface for even more debuggability * [FEAT identifier] makes debug members of the interface optional * [FEAT IdentifierCache] implements the cache to spec * record-data-wrapper -> store-wrapper.js (fix name) * fix store usage by relationships without leaking to RecordData
- Loading branch information
Showing
36 changed files
with
1,008 additions
and
558 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
// support IE11 | ||
declare global { | ||
interface Window { | ||
msCrypto: Crypto; | ||
} | ||
} | ||
|
||
const CRYPTO = typeof window !== 'undefined' && window.msCrypto && typeof window.msCrypto.getRandomValues === 'function' ? window.msCrypto : crypto; | ||
|
||
// we might be able to optimize this by requesting more bytes than we need at a time | ||
function rng() { | ||
// WHATWG crypto RNG - http://wiki.whatwg.org/wiki/Crypto | ||
let rnds8 = new Uint8Array(16); | ||
|
||
return CRYPTO.getRandomValues(rnds8); | ||
} | ||
|
||
/** | ||
* Convert array of 16 byte values to UUID string format of the form: | ||
* XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX | ||
*/ | ||
const byteToHex: string[] = []; | ||
for (let i = 0; i < 256; ++i) { | ||
byteToHex[i] = (i + 0x100).toString(16).substr(1); | ||
} | ||
|
||
function bytesToUuid(buf) { | ||
let bth = byteToHex; | ||
// join used to fix memory issue caused by concatenation: https://bugs.chromium.org/p/v8/issues/detail?id=3175#c4 | ||
return ([bth[buf[0]], bth[buf[1]], | ||
bth[buf[2]], bth[buf[3]], '-', | ||
bth[buf[4]], bth[buf[5]], '-', | ||
bth[buf[6]], bth[buf[7]], '-', | ||
bth[buf[8]], bth[buf[9]], '-', | ||
bth[buf[10]], bth[buf[11]], | ||
bth[buf[12]], bth[buf[13]], | ||
bth[buf[14]], bth[buf[15]]]).join(''); | ||
} | ||
|
||
export default function uuidv4(): string { | ||
let rnds = rng(); | ||
|
||
// Per 4.4, set bits for version and `clock_seq_hi_and_reserved` | ||
rnds[6] = (rnds[6] & 0x0f) | 0x40; | ||
rnds[8] = (rnds[8] & 0x3f) | 0x80; | ||
|
||
return bytesToUuid(rnds); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,128 @@ | ||
import { Dict } from '../ts-interfaces/utils'; | ||
import { StableRecordIdentifier } from '../ts-interfaces/identifier'; | ||
import IdentifierCache from '../identifiers/cache'; | ||
import InternalModel from '../system/model/internal-model'; | ||
import { assert } from '@ember/debug'; | ||
import { NewResourceObject, ExistingResourceIdentifierObject } from '../ts-interfaces/json-api'; | ||
|
||
export default class InternalModelCache { | ||
private _identifierCache: IdentifierCache; | ||
private _types: Dict<string, InternalModel[]> = Object.create(null); | ||
private _lids: Dict<string, InternalModel> = Object.create(null) | ||
private _store; | ||
|
||
constructor(store, identifierCache) { | ||
this._identifierCache = identifierCache; | ||
this._store = store; | ||
} | ||
|
||
all(modelName: string) { | ||
let all = this._types[modelName] = this._types[modelName] || []; | ||
return all; | ||
} | ||
|
||
get(identifier: StableRecordIdentifier): InternalModel | null { | ||
return this._lids[identifier.lid] || null; | ||
} | ||
|
||
private _set(identifier: StableRecordIdentifier, internalModel: InternalModel): void { | ||
this._lids[identifier.lid] = internalModel; | ||
this.all(identifier.type).push(internalModel); | ||
} | ||
|
||
createInternalModelForNewRecord(data: NewResourceObject): InternalModel { | ||
// check for an existing identifier | ||
let identifier; | ||
let internalModel; | ||
|
||
if (data.id !== null) { | ||
identifier = this._identifierCache.peekRecordIdentifier(data as ExistingResourceIdentifierObject, false); | ||
internalModel = identifier !== null ? this.get(identifier) : null; | ||
} | ||
|
||
if (internalModel && internalModel.hasScheduledDestroy()) { | ||
// unloadRecord is async, if one attempts to unload + then sync create, | ||
// we must ensure the unload is complete before starting the create | ||
// The push path will utilize _getOrCreateInternalModelFor() | ||
// which will call `cancelDestroy` instead for this unload + then | ||
// sync push scenario. Once we have true client-side | ||
// delete signaling, we should never call destroySync | ||
internalModel.destroySync(); | ||
internalModel = null; | ||
this._identifierCache.forgetRecordIdentifier(identifier); | ||
} | ||
|
||
assert( | ||
`The id ${identifier.id} has already been used with another record for modelClass '${ | ||
identifier.type | ||
}'.`, | ||
!internalModel | ||
); | ||
|
||
identifier = this._identifierCache.createIdentifierForNewRecord({ | ||
type: data.type, | ||
id: data.id | ||
}); | ||
|
||
internalModel = new InternalModel(this._store, identifier); | ||
this._set(identifier, internalModel); | ||
|
||
return internalModel; | ||
} | ||
|
||
ensureInstance(identifier: StableRecordIdentifier): InternalModel { | ||
let internalModel = this.get(identifier); | ||
|
||
if (internalModel !== null) { | ||
// unloadRecord is async, if one attempts to unload + then sync push, | ||
// we must ensure the unload is canceled before continuing | ||
// The createRecord path will utilize _createInternalModel() directly | ||
// which will call `destroySync` instead for this unload + then | ||
// sync createRecord scenario. Once we have true client-side | ||
// delete signaling, we should never call destroySync | ||
if (internalModel.hasScheduledDestroy()) { | ||
internalModel.cancelDestroy(); | ||
} | ||
|
||
return internalModel; | ||
} | ||
|
||
internalModel = new InternalModel(this._store, identifier); | ||
this._set(identifier, internalModel); | ||
|
||
return internalModel; | ||
} | ||
|
||
clear(type?: string) { | ||
let cached: InternalModel[] = []; | ||
|
||
if (type !== undefined) { | ||
let all = this.all(type); | ||
this._types[type] = []; // clear it | ||
cached.push(...all); | ||
} else { | ||
Object.keys(this._types).forEach(type => { | ||
cached.push(...this.all(type)); | ||
this._types[type] = []; // clear it | ||
}); | ||
} | ||
|
||
for (let i = 0; i < cached.length; i++) { | ||
let internalModel = cached[i]; | ||
|
||
// this then calls "remove" | ||
// but only once the time is right | ||
internalModel.unloadRecord(); | ||
} | ||
} | ||
|
||
remove(identifier: StableRecordIdentifier) { | ||
let internalModel = this._lids[identifier.lid]; | ||
delete this._lids[identifier.lid]; | ||
let all = this.all(identifier.type); | ||
let index = all.indexOf(internalModel); | ||
if (index !== -1) { | ||
all.splice(index, 1); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
Oops, something went wrong.