UNPKG

@ember-data/json-api

Version:

Provides a JSON:API document and resource cache implementation for EmberData

546 lines (541 loc) 20.9 kB
declare module '@ember-data/json-api/-private/cache' { /** * @module @ember-data/json-api */ import type { Graph } from '@ember-data/graph/-private'; import type { CacheCapabilitiesManager } from '@ember-data/store/types'; import type { Cache, ChangedAttributesHash, RelationshipDiff } from '@warp-drive/core-types/cache'; import type { Change } from '@warp-drive/core-types/cache/change'; import type { MergeOperation } from '@warp-drive/core-types/cache/operations'; import type { CollectionRelationship, ResourceRelationship } from '@warp-drive/core-types/cache/relationship'; import type { LocalRelationshipOperation } from '@warp-drive/core-types/graph'; import type { StableDocumentIdentifier, StableExistingRecordIdentifier, StableRecordIdentifier } from '@warp-drive/core-types/identifier'; import type { Value } from '@warp-drive/core-types/json/raw'; import type { StructuredDataDocument, StructuredDocument, StructuredErrorDocument } from '@warp-drive/core-types/request'; import type { CollectionResourceDataDocument, ResourceDocument, ResourceErrorDocument, ResourceMetaDocument, SingleResourceDataDocument } from '@warp-drive/core-types/spec/document'; import type { ApiError } from '@warp-drive/core-types/spec/error'; import type { CollectionResourceDocument, ExistingResourceObject, ResourceObject, SingleResourceDocument } from '@warp-drive/core-types/spec/json-api-raw'; interface CachedResource { id: string | null; remoteAttrs: Record<string, Value | undefined> | null; localAttrs: Record<string, Value | undefined> | null; defaultAttrs: Record<string, Value | undefined> | null; inflightAttrs: Record<string, Value | undefined> | null; changes: Record<string, [Value | undefined, Value]> | null; errors: ApiError[] | null; isNew: boolean; isDeleted: boolean; isDeletionCommitted: boolean; /** * debugging only * * @internal */ inflightRelationships?: Record<string, unknown> | null; } /** A JSON:API Cache implementation. What cache the store uses is configurable. Using a different implementation can be achieved by implementing the store's createCache hook. This is the cache implementation used by `ember-data`. ```js import Cache from '@ember-data/json-api'; import Store from '@ember-data/store'; export default class extends Store { createCache(wrapper) { return new Cache(wrapper); } } ``` @class Cache @public */ export default class JSONAPICache implements Cache { /** * The Cache Version that this implementation implements. * * @type {'2'} * @public * @property version */ version: '2'; _capabilities: CacheCapabilitiesManager; __cache: Map<StableRecordIdentifier, CachedResource>; __destroyedCache: Map<StableRecordIdentifier, CachedResource>; __documents: Map<string, StructuredDocument<ResourceDocument>>; __graph: Graph; constructor(storeWrapper: CacheCapabilitiesManager); /** * Cache the response to a request * * Implements `Cache.put`. * * Expects a StructuredDocument whose `content` member is a JsonApiDocument. * * ```js * cache.put({ * request: { url: 'https://api.example.com/v1/user/1' }, * content: { * data: { * type: 'user', * id: '1', * attributes: { * name: 'Chris' * } * } * } * }) * ``` * * > **Note** * > The nested `content` and `data` members are not a mistake. This is because * > there are two separate concepts involved here, the `StructuredDocument` which contains * > the context of a given Request that has been issued with the returned contents as its * > `content` property, and a `JSON:API Document` which is the json contents returned by * > this endpoint and which uses its `data` property to signify which resources are the * > primary resources associated with the request. * * StructuredDocument's with urls will be cached as full documents with * associated resource membership order and contents preserved but linked * into the cache. * * @method put * @param {StructuredDocument} doc * @return {ResourceDocument} * @public */ put<T extends SingleResourceDocument>(doc: StructuredDataDocument<T>): SingleResourceDataDocument; put<T extends CollectionResourceDocument>(doc: StructuredDataDocument<T>): CollectionResourceDataDocument; put<T extends ResourceErrorDocument>(doc: StructuredErrorDocument<T>): ResourceErrorDocument; put<T extends ResourceMetaDocument>(doc: StructuredDataDocument<T>): ResourceMetaDocument; _putDocument<T extends ResourceErrorDocument>(doc: StructuredErrorDocument<T>, data: undefined, included: undefined): ResourceErrorDocument; _putDocument<T extends ResourceMetaDocument>(doc: StructuredDataDocument<T>, data: undefined, included: undefined): ResourceMetaDocument; _putDocument<T extends SingleResourceDocument>(doc: StructuredDataDocument<T>, data: StableExistingRecordIdentifier | null, included: StableExistingRecordIdentifier[] | undefined): SingleResourceDataDocument; _putDocument<T extends CollectionResourceDocument>(doc: StructuredDataDocument<T>, data: StableExistingRecordIdentifier[], included: StableExistingRecordIdentifier[] | undefined): CollectionResourceDataDocument; /** * Update the "remote" or "canonical" (persisted) state of the Cache * by merging new information into the existing state. * * Note: currently the only valid resource operation is a MergeOperation * which occurs when a collision of identifiers is detected. * * @method patch * @public * @param {Operation} op the operation to perform * @return {void} */ patch(op: MergeOperation): void; /** * Update the "local" or "current" (unpersisted) state of the Cache * * @method mutate * @param {Mutation} mutation * @return {void} * @public */ mutate(mutation: LocalRelationshipOperation): void; /** * Peek resource data from the Cache. * * In development, if the return value * is JSON the return value * will be deep-cloned and deep-frozen * to prevent mutation thereby enforcing cache * Immutability. * * This form of peek is useful for implementations * that want to feed raw-data from cache to the UI * or which want to interact with a blob of data * directly from the presentation cache. * * An implementation might want to do this because * de-referencing records which read from their own * blob is generally safer because the record does * not require retainining connections to the Store * and Cache to present data on a per-field basis. * * This generally takes the place of `getAttr` as * an API and may even take the place of `getRelationship` * depending on implementation specifics, though this * latter usage is less recommended due to the advantages * of the Graph handling necessary entanglements and * notifications for relational data. * * @method peek * @public * @param {StableRecordIdentifier | StableDocumentIdentifier} identifier * @return {ResourceDocument | ResourceObject | null} the known resource data */ peek(identifier: StableRecordIdentifier): ResourceObject | null; peek(identifier: StableDocumentIdentifier): ResourceDocument | null; /** * Peek the Cache for the existing request data associated with * a cacheable request. * * This is effectively the reverse of `put` for a request in * that it will return the the request, response, and content * whereas `peek` will return just the `content`. * * @method peekRequest * @param {StableDocumentIdentifier} * @return {StructuredDocument<ResourceDocument> | null} * @public */ peekRequest(identifier: StableDocumentIdentifier): StructuredDocument<ResourceDocument> | null; /** * Push resource data from a remote source into the cache for this identifier * * @method upsert * @public * @param identifier * @param data * @param hasRecord * @return {void | string[]} if `hasRecord` is true then calculated key changes should be returned */ upsert(identifier: StableRecordIdentifier, data: ExistingResourceObject, calculateChanges?: boolean): void | string[]; /** * Create a fork of the cache from the current state. * * Applications should typically not call this method themselves, * preferring instead to fork at the Store level, which will * utilize this method to fork the cache. * * @method fork * @internal * @return Promise<Cache> */ fork(): Promise<Cache>; /** * Merge a fork back into a parent Cache. * * Applications should typically not call this method themselves, * preferring instead to merge at the Store level, which will * utilize this method to merge the caches. * * @method merge * @param {Cache} cache * @public * @return Promise<void> */ merge(cache: Cache): Promise<void>; /** * Generate the list of changes applied to all * record in the store. * * Each individual resource or document that has * been mutated should be described as an individual * `Change` entry in the returned array. * * A `Change` is described by an object containing up to * three properties: (1) the `identifier` of the entity that * changed; (2) the `op` code of that change being one of * `upsert` or `remove`, and if the op is `upsert` a `patch` * containing the data to merge into the cache for the given * entity. * * This `patch` is opaque to the Store but should be understood * by the Cache and may expect to be utilized by an Adapter * when generating data during a `save` operation. * * It is generally recommended that the `patch` contain only * the updated state, ignoring fields that are unchanged * * ```ts * interface Change { * identifier: StableRecordIdentifier | StableDocumentIdentifier; * op: 'upsert' | 'remove'; * patch?: unknown; * } * ``` * * @method diff * @public */ diff(): Promise<Change[]>; /** * Serialize the entire contents of the Cache into a Stream * which may be fed back into a new instance of the same Cache * via `cache.hydrate`. * * @method dump * @return {Promise<ReadableStream>} * @public */ dump(): Promise<ReadableStream<unknown>>; /** * hydrate a Cache from a Stream with content previously serialized * from another instance of the same Cache, resolving when hydration * is complete. * * This method should expect to be called both in the context of restoring * the Cache during application rehydration after SSR **AND** at unknown * times during the lifetime of an already booted application when it is * desired to bulk-load additional information into the cache. This latter * behavior supports optimizing pre/fetching of data for route transitions * via data-only SSR modes. * * @method hydrate * @param {ReadableStream} stream * @return {Promise<void>} * @public */ hydrate(stream: ReadableStream<unknown>): Promise<void>; /** * [LIFECYCLE] Signal to the cache that a new record has been instantiated on the client * * It returns properties from options that should be set on the record during the create * process. This return value behavior is deprecated. * * @method clientDidCreate * @public * @param identifier * @param createArgs */ clientDidCreate(identifier: StableRecordIdentifier, options?: Record<string, Value>): Record<string, unknown>; /** * [LIFECYCLE] Signals to the cache that a resource * will be part of a save transaction. * * @method willCommit * @public * @param identifier */ willCommit(identifier: StableRecordIdentifier): void; /** * [LIFECYCLE] Signals to the cache that a resource * was successfully updated as part of a save transaction. * * @method didCommit * @public * @param identifier * @param data */ didCommit(committedIdentifier: StableRecordIdentifier, result: StructuredDataDocument<SingleResourceDocument>): SingleResourceDataDocument; /** * [LIFECYCLE] Signals to the cache that a resource * was update via a save transaction failed. * * @method commitWasRejected * @public * @param identifier * @param errors */ commitWasRejected(identifier: StableRecordIdentifier, errors?: ApiError[]): void; /** * [LIFECYCLE] Signals to the cache that all data for a resource * should be cleared. * * This method is a candidate to become a mutation * * @method unloadRecord * @public * @param identifier */ unloadRecord(identifier: StableRecordIdentifier): void; /** * Retrieve the data for an attribute from the cache * * @method getAttr * @public * @param identifier * @param field * @return {unknown} */ getAttr(identifier: StableRecordIdentifier, attr: string | string[]): Value | undefined; /** * Mutate the data for an attribute in the cache * * This method is a candidate to become a mutation * * @method setAttr * @public * @param identifier * @param field * @param value */ setAttr(identifier: StableRecordIdentifier, attr: string | string[], value: Value): void; /** * Query the cache for the changed attributes of a resource. * * @method changedAttrs * @public * @param identifier * @return {ChangedAttributesHash} { <field>: [<old>, <new>] } */ changedAttrs(identifier: StableRecordIdentifier): ChangedAttributesHash; /** * Query the cache for whether any mutated attributes exist * * @method hasChangedAttrs * @public * @param identifier * @return {boolean} */ hasChangedAttrs(identifier: StableRecordIdentifier): boolean; /** * Tell the cache to discard any uncommitted mutations to attributes * * This method is a candidate to become a mutation * * @method rollbackAttrs * @public * @param identifier * @return {string[]} the names of fields that were restored */ rollbackAttrs(identifier: StableRecordIdentifier): string[]; /** * Query the cache for the changes to relationships of a resource. * * Returns a map of relationship names to RelationshipDiff objects. * * ```ts * type RelationshipDiff = | { kind: 'collection'; remoteState: StableRecordIdentifier[]; additions: Set<StableRecordIdentifier>; removals: Set<StableRecordIdentifier>; localState: StableRecordIdentifier[]; reordered: boolean; } | { kind: 'resource'; remoteState: StableRecordIdentifier | null; localState: StableRecordIdentifier | null; }; ``` * * @method changedRelationships * @public * @param {StableRecordIdentifier} identifier * @return {Map<string, RelationshipDiff>} */ changedRelationships(identifier: StableRecordIdentifier): Map<string, RelationshipDiff>; /** * Query the cache for whether any mutated relationships exist * * @method hasChangedRelationships * @public * @param {StableRecordIdentifier} identifier * @return {boolean} */ hasChangedRelationships(identifier: StableRecordIdentifier): boolean; /** * Tell the cache to discard any uncommitted mutations to relationships. * * This will also discard the change on any appropriate inverses. * * This method is a candidate to become a mutation * * @method rollbackRelationships * @public * @param {StableRecordIdentifier} identifier * @return {string[]} the names of relationships that were restored */ rollbackRelationships(identifier: StableRecordIdentifier): string[]; /** * Query the cache for the current state of a relationship property * * @method getRelationship * @public * @param identifier * @param field * @return resource relationship object */ getRelationship(identifier: StableRecordIdentifier, field: string): ResourceRelationship | CollectionRelationship; /** * Update the cache state for the given resource to be marked * as locally deleted, or remove such a mark. * * This method is a candidate to become a mutation * * @method setIsDeleted * @public * @param identifier * @param isDeleted {boolean} */ setIsDeleted(identifier: StableRecordIdentifier, isDeleted: boolean): void; /** * Query the cache for any validation errors applicable to the given resource. * * @method getErrors * @public * @param identifier * @return {JsonApiError[]} */ getErrors(identifier: StableRecordIdentifier): ApiError[]; /** * Query the cache for whether a given resource has any available data * * @method isEmpty * @public * @param identifier * @return {boolean} */ isEmpty(identifier: StableRecordIdentifier): boolean; /** * Query the cache for whether a given resource was created locally and not * yet persisted. * * @method isNew * @public * @param identifier * @return {boolean} */ isNew(identifier: StableRecordIdentifier): boolean; /** * Query the cache for whether a given resource is marked as deleted (but not * necessarily persisted yet). * * @method isDeleted * @public * @param identifier * @return {boolean} */ isDeleted(identifier: StableRecordIdentifier): boolean; /** * Query the cache for whether a given resource has been deleted and that deletion * has also been persisted. * * @method isDeletionCommitted * @public * @param identifier * @return {boolean} */ isDeletionCommitted(identifier: StableRecordIdentifier): boolean; /** * Private method used to populate an entry for the identifier * * @method _createCache * @internal * @param {StableRecordIdentifier} identifier * @return {CachedResource} */ _createCache(identifier: StableRecordIdentifier): CachedResource; /** * Peek whether we have cached resource data matching the identifier * without asserting if the resource data is missing. * * @method __safePeek * @param {StableRecordIdentifier} identifier * @param {Boolean} allowDestroyed * @internal * @return {CachedResource | undefined} */ __safePeek(identifier: StableRecordIdentifier, allowDestroyed: boolean): CachedResource | undefined; /** * Peek whether we have cached resource data matching the identifier * Asserts if the resource data is missing. * * @method __Peek * @param {StableRecordIdentifier} identifier * @param {Boolean} allowDestroyed * @internal * @return {CachedResource} */ __peek(identifier: StableRecordIdentifier, allowDestroyed: boolean): CachedResource; } export {}; } //# sourceMappingURL=cache.d.ts.map