UNPKG

@ngxs-labs/entity-state

Version:

This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 7.0.5.

1,332 lines (1,310 loc) 39.7 kB
import { ofAction, ofActionCompleted, ofActionDispatched, ofActionErrored, ofActionSuccessful } from '@ngxs/store'; /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ class EntityStateError extends Error { /** * @param {?} message */ constructor(message) { super(message); } } class NoActiveEntityError extends EntityStateError { /** * @param {?=} additionalInformation */ constructor(additionalInformation = '') { super(('No active entity to affect. ' + additionalInformation).trim()); } } class NoSuchEntityError extends EntityStateError { /** * @param {?} id */ constructor(id) { super(`No entity for ID ${id}`); } } class InvalidIdError extends EntityStateError { /** * @param {?} id */ constructor(id) { super(`Invalid ID: ${id}`); } } class InvalidIdOfError extends EntityStateError { constructor() { super(`idOf returned undefined`); } } class UpdateFailedError extends EntityStateError { /** * @param {?} cause */ constructor(cause) { super(`Updating entity failed.\n\tCause: ${cause}`); } } class UnableToGenerateIdError extends EntityStateError { /** * @param {?} cause */ constructor(cause) { super(`Unable to generate an ID.\n\tCause: ${cause}`); } } /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ /** @type {?} */ const NGXS_META_KEY = 'NGXS_META'; /** * This function generates a new object for the ngxs Action with the given fn name * @template T * @param {?} fn The name of the Action to simulate, e.g. "Remove" or "Update" * @param {?} store The class of the targeted entity state, e.g. ZooState * @param {?=} payload The payload for the created action object * @return {?} */ function generateActionObject(fn, store, payload) { /** @type {?} */ const name = store[NGXS_META_KEY].path; /** @type {?} */ const ReflectedAction = (/** * @param {?} data * @return {?} */ function (data) { this.payload = data; }); /** @type {?} */ const obj = new ReflectedAction(payload); Reflect.getPrototypeOf(obj).constructor['type'] = `[${name}] ${fn}`; return obj; } /** * Utility function that returns the active entity of the given state * @template T * @param {?} state the state of an entity state * @return {?} */ function getActive(state) { return state.entities[state.active]; } /** * Returns the active entity. If none is present an error will be thrown. * @template T * @param {?} state The state to act on * @return {?} */ function mustGetActive(state) { /** @type {?} */ const active = getActive(state); if (active === undefined) { throw new NoActiveEntityError(); } return { id: state.active, active }; } /** * Undefined-safe function to access the property given by path parameter * @param {?} object The object to read from * @param {?} path The path to the property * @return {?} */ function elvis(object, path) { return path ? path.split('.').reduce((/** * @param {?} value * @param {?} key * @return {?} */ (value, key) => value && value[key]), object) : object; } /** * Returns input as an array if it isn't one already * @template T * @param {?} input The input to make an array if necessary * @return {?} */ function asArray(input) { return Array.isArray(input) ? input : [input]; } /** * Limits a number to the given boundaries * @param {?} value The input value * @param {?} min The minimum value * @param {?} max The maximum value * @return {?} */ function clamp(value, min, max) { return Math.min(max, Math.max(min, value)); } /** * Uses the clamp function is wrap is false. * Else it wrap to the max or min value respectively. * @param {?} wrap Flag to indicate if value should be wrapped * @param {?} value The input value * @param {?} min The minimum value * @param {?} max The maximum value * @return {?} */ function wrapOrClamp(wrap, value, min, max) { if (!wrap) { return clamp(value, min, max); } else if (value < min) { return max; } else if (value > max) { return min; } else { return value; } } /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ /** @enum {string} */ const EntityActionType = { Add: 'add', CreateOrReplace: 'createOrReplace', Update: 'update', UpdateActive: 'updateActive', Remove: 'remove', RemoveActive: 'removeActive', SetLoading: 'setLoading', SetError: 'setError', SetActive: 'setActive', ClearActive: 'clearActive', Reset: 'reset', GoToPage: 'goToPage', SetPageSize: 'setPageSize', }; /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ /** * @template T */ class Add { /** * Generates an action that will add the given entities to the state. * The entities given by the payload will be added. * For certain ID strategies this might fail, if it provides an existing ID. * In all other cases it will overwrite the ID value in the entity with the calculated ID. * @see CreateOrReplace#constructor * @param {?} target The targeted state class * @param {?} payload An entity or an array of entities to be added */ constructor(target, payload) { return generateActionObject(EntityActionType.Add, target, payload); } } /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ /** * @template T */ class CreateOrReplace { /** * Generates an action that will add the given entities to the state. * If an entity with the ID already exists, it will be overridden. * In all cases it will overwrite the ID value in the entity with the calculated ID. * @see Add#constructor * @param {?} target The targeted state class * @param {?} payload An entity or an array of entities to be added */ constructor(target, payload) { return generateActionObject(EntityActionType.CreateOrReplace, target, payload); } } /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ /** * @template T */ class Remove { /** * Generates an action that will remove the given entities from the state. * Put null if all entities should be removed. * @see EntitySelector * @param {?} target The targeted state class * @param {?} payload An EntitySelector payload */ constructor(target, payload) { return generateActionObject(EntityActionType.Remove, target, payload); } } /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ /** * @template T */ class Update { /** * Generates an action that will update the current active entity. * If no entity is active a runtime error will be thrown. * @see EntitySelector / Updater * @param {?} target The targeted state class * @param {?} id An EntitySelector that determines the entities to update * @param {?} data An Updater that will be applied to the selected entities */ constructor(target, id, data) { return generateActionObject(EntityActionType.Update, target, { id, data }); } } /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ class SetLoading { /** * Generates an action that will set the loading state for the given state. * @param {?} target The targeted state class * @param {?} loading The loading state */ constructor(target, loading) { return generateActionObject(EntityActionType.SetLoading, target, loading); } } /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ class SetActive { /** * Generates an action that sets an ID that identifies the active entity * @param {?} target The targeted state class * @param {?} id The ID that identifies the active entity */ constructor(target, id) { return generateActionObject(EntityActionType.SetActive, target, id); } } class ClearActive { /** * Generates an action that clears the active entity in the given state * @param {?} target The targeted state class */ constructor(target) { return generateActionObject(EntityActionType.ClearActive, target); } } class RemoveActive { /** * Generates an action that removes the active entity from the state and clears the active ID. * @param {?} target The targeted state class */ constructor(target) { return generateActionObject(EntityActionType.RemoveActive, target); } } /** * @template T */ class UpdateActive { /** * Generates an action that will update the current active entity. * If no entity is active a runtime error will be thrown. * @see Updater * @param {?} target The targeted state class * @param {?} payload An Updater payload */ constructor(target, payload) { return generateActionObject(EntityActionType.UpdateActive, target, payload); } } /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ class SetError { /** * Generates an action that will set the error state for the given state. * Put undefined to clear the error state. * @param {?} target The targeted state class * @param {?} error The error that describes the error state */ constructor(target, error) { return generateActionObject(EntityActionType.SetError, target, error); } } /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ class Reset { /** * Resets the targeted store to the default state: no entities, loading is false, error is undefined, active is undefined. * @see defaultEntityState * @param {?} target The targeted state class */ constructor(target) { return generateActionObject(EntityActionType.Reset, target); } } /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ class GoToPage { /** * Generates an action that changes the page index for pagination. * Page index starts at 0. * @param {?} target The targeted state class * @param {?} payload Payload to change the page index */ constructor(target, payload) { return generateActionObject(EntityActionType.GoToPage, target, Object.assign({ wrap: false }, payload)); } } class SetPageSize { /** * Generates an action that changes the page size * @param {?} target The targeted state class * @param {?} payload The page size */ constructor(target, payload) { return generateActionObject(EntityActionType.SetPageSize, target, payload); } } /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ /** * Returns a new object which serves as the default state. * No entities, loading is false, error is undefined, active is undefined. * pageSize is 10 and pageIndex is 0. * @template T * @param {?=} defaults * @return {?} */ function defaultEntityState(defaults = {}) { return Object.assign({ entities: {}, ids: [], loading: false, error: undefined, active: undefined, pageSize: 10, pageIndex: 0, lastUpdated: Date.now() }, defaults); } // @dynamic /** * @abstract * @template T */ class EntityState { /** * @protected * @param {?} storeClass * @param {?} _idKey * @param {?} idStrategy */ constructor(storeClass, _idKey, idStrategy) { this.idKey = (/** @type {?} */ (_idKey)); this.storePath = storeClass[NGXS_META_KEY].path; this.idGenerator = new idStrategy(_idKey); this.setup(storeClass, Object.values(EntityActionType)); } /** * @private * @return {?} */ static get staticStorePath() { /** @type {?} */ const that = this; return that[NGXS_META_KEY].path; } /** * This function is called every time an entity is updated. * It receives the current entity and a partial entity that was either passed directly or generated with a function. * The default implementation uses the spread operator to create a new entity. * You must override this method if your entity type does not support the spread operator. * @see Updater * \@example * // default behavior * onUpdate(current: Readonly<T updated: Partial<T>): T { * return {...current, ...updated}; * } * @param {?} current The current entity, readonly * @param {?} updated The new data as a partial entity * @return {?} */ onUpdate(current, updated) { return (/** @type {?} */ (Object.assign({}, current, updated))); } // ------------------- SELECTORS ------------------- /** * Returns a selector for the activeId * @return {?} */ static get activeId() { /** @type {?} */ const that = this; return (/** * @param {?} state * @return {?} */ state => { /** @type {?} */ const subState = (/** @type {?} */ (elvis(state, that.staticStorePath))); return subState.active; }); } /** * Returns a selector for the active entity * @return {?} */ static get active() { /** @type {?} */ const that = this; return (/** * @param {?} state * @return {?} */ state => { /** @type {?} */ const subState = (/** @type {?} */ (elvis(state, that.staticStorePath))); return getActive(subState); }); } /** * Returns a selector for the keys of all entities * @return {?} */ static get keys() { /** @type {?} */ const that = this; return (/** * @param {?} state * @return {?} */ state => { /** @type {?} */ const subState = (/** @type {?} */ (elvis(state, that.staticStorePath))); return Object.keys(subState.entities); }); } /** * Returns a selector for all entities, sorted by insertion order * @return {?} */ static get entities() { /** @type {?} */ const that = this; return (/** * @param {?} state * @return {?} */ state => { /** @type {?} */ const subState = (/** @type {?} */ (elvis(state, that.staticStorePath))); return subState.ids.map((/** * @param {?} id * @return {?} */ id => subState.entities[id])); }); } /** * Returns a selector for the nth entity, sorted by insertion order * @param {?} index * @return {?} */ static nthEntity(index) { // tslint:disable-line:member-ordering /** @type {?} */ const that = this; return (/** * @param {?} state * @return {?} */ state => { /** @type {?} */ const subState = (/** @type {?} */ (elvis(state, that.staticStorePath))); /** @type {?} */ const id = subState.ids[index]; return subState.entities[id]; }); } /** * Returns a selector for paginated entities, sorted by insertion order * @return {?} */ static get paginatedEntities() { // tslint:disable-line:member-ordering /** @type {?} */ const that = this; return (/** * @param {?} state * @return {?} */ state => { /** @type {?} */ const subState = (/** @type {?} */ (elvis(state, that.staticStorePath))); const { ids, pageIndex, pageSize } = subState; return ids .slice(pageIndex * pageSize, (pageIndex + 1) * pageSize) .map((/** * @param {?} id * @return {?} */ id => subState.entities[id])); }); } /** * Returns a selector for the map of entities * @return {?} */ static get entitiesMap() { /** @type {?} */ const that = this; return (/** * @param {?} state * @return {?} */ state => { /** @type {?} */ const subState = (/** @type {?} */ (elvis(state, that.staticStorePath))); return subState.entities; }); } /** * Returns a selector for the size of the entity map * @return {?} */ static get size() { /** @type {?} */ const that = this; return (/** * @param {?} state * @return {?} */ state => { /** @type {?} */ const subState = (/** @type {?} */ (elvis(state, that.staticStorePath))); return Object.keys(subState.entities).length; }); } /** * Returns a selector for the error * @return {?} */ static get error() { /** @type {?} */ const that = this; return (/** * @param {?} state * @return {?} */ state => { /** @type {?} */ const name = that.staticStorePath; return elvis(state, name).error; }); } /** * Returns a selector for the loading state * @return {?} */ static get loading() { /** @type {?} */ const that = this; return (/** * @param {?} state * @return {?} */ state => { /** @type {?} */ const name = that.staticStorePath; return elvis(state, name).loading; }); } /** * Returns a selector for the latest added entity * @return {?} */ static get latest() { /** @type {?} */ const that = this; return (/** * @param {?} state * @return {?} */ state => { /** @type {?} */ const subState = (/** @type {?} */ (elvis(state, that.staticStorePath))); /** @type {?} */ const latestId = subState.ids[subState.ids.length - 1]; return subState.entities[latestId]; }); } /** * Returns a selector for the latest added entity id * @return {?} */ static get latestId() { /** @type {?} */ const that = this; return (/** * @param {?} state * @return {?} */ state => { /** @type {?} */ const subState = (/** @type {?} */ (elvis(state, that.staticStorePath))); return subState.ids[subState.ids.length - 1]; }); } /** * Returns a selector for the update timestamp * @return {?} */ static get lastUpdated() { // tslint:disable-line:member-ordering /** @type {?} */ const that = this; return (/** * @param {?} state * @return {?} */ state => { /** @type {?} */ const subState = (/** @type {?} */ (elvis(state, that.staticStorePath))); return new Date(subState.lastUpdated); }); } /** * Returns a selector for age, based on the update timestamp * @return {?} */ static get age() { // tslint:disable-line:member-ordering /** @type {?} */ const that = this; return (/** * @param {?} state * @return {?} */ state => { /** @type {?} */ const subState = (/** @type {?} */ (elvis(state, that.staticStorePath))); return Date.now() - subState.lastUpdated; }); } // ------------------- ACTION HANDLERS ------------------- /** * The entities given by the payload will be added. * For certain ID strategies this might fail, if it provides an existing ID. * In all cases it will overwrite the ID value in the entity with the calculated ID. * @param {?} __0 * @param {?} __1 * @return {?} */ add({ getState, patchState }, { payload }) { /** @type {?} */ const updated = this._addOrReplace(getState(), payload, ( // for automated ID strategies this mostly shouldn't throw an UnableToGenerateIdError error // for EntityIdGenerator it will throw an error if no ID is present /** * @param {?} p * @param {?} state * @return {?} */ (p, state) => this.idGenerator.generateId(p, state))); patchState(Object.assign({}, updated, { lastUpdated: Date.now() })); } /** * The entities given by the payload will be added. * It first checks if the ID provided by each entity does exist. * If it does the current entity will be replaced. * In all cases it will overwrite the ID value in the entity with the calculated ID. * @param {?} __0 * @param {?} __1 * @return {?} */ createOrReplace({ getState, patchState }, { payload }) { /** @type {?} */ const updated = this._addOrReplace(getState(), payload, (/** * @param {?} p * @param {?} state * @return {?} */ (p, state) => this.idGenerator.getPresentIdOrGenerate(p, state))); patchState(Object.assign({}, updated, { lastUpdated: Date.now() })); } /** * @param {?} __0 * @param {?} __1 * @return {?} */ update({ getState, patchState }, { payload }) { /** @type {?} */ let entities = Object.assign({}, getState().entities); // create copy /** @type {?} */ let affected; if (payload.id === null) { affected = Object.values(entities); } else if (typeof payload.id === 'function') { affected = Object.values(entities).filter((/** * @param {?} e * @return {?} */ e => ((/** @type {?} */ (payload.id)))(e))); } else { /** @type {?} */ const ids = asArray(payload.id); affected = Object.values(entities).filter((/** * @param {?} e * @return {?} */ e => ids.includes(this.idOf(e)))); } if (typeof payload.data === 'function') { affected.forEach((/** * @param {?} e * @return {?} */ e => { entities = this._update(entities, ((/** @type {?} */ (payload.data)))(e), this.idOf(e)); })); } else { affected.forEach((/** * @param {?} e * @return {?} */ e => { entities = this._update(entities, (/** @type {?} */ (payload.data)), this.idOf(e)); })); } patchState({ entities, lastUpdated: Date.now() }); } /** * @param {?} __0 * @param {?} __1 * @return {?} */ updateActive({ getState, patchState }, { payload }) { /** @type {?} */ const state = getState(); const { id, active } = mustGetActive(state); const { entities } = state; if (typeof payload === 'function') { patchState({ entities: Object.assign({}, this._update(entities, payload(active), id)), lastUpdated: Date.now() }); } else { patchState({ entities: Object.assign({}, this._update(entities, payload, id)), lastUpdated: Date.now() }); } } /** * @param {?} __0 * @return {?} */ removeActive({ getState, patchState }) { const { entities, ids, active } = getState(); delete entities[active]; patchState({ entities: Object.assign({}, entities), ids: ids.filter((/** * @param {?} id * @return {?} */ id => id !== active)), active: undefined, lastUpdated: Date.now() }); } /** * @param {?} __0 * @param {?} __1 * @return {?} */ remove({ getState, patchState }, { payload }) { const { entities, ids, active } = getState(); if (payload === null) { patchState({ entities: {}, ids: [], active: undefined, lastUpdated: Date.now() }); } else { /** @type {?} */ const deleteIds = typeof payload === 'function' ? Object.values(entities) .filter((/** * @param {?} e * @return {?} */ e => payload(e))) .map((/** * @param {?} e * @return {?} */ e => this.idOf(e))) : asArray(payload); /** @type {?} */ const wasActive = deleteIds.includes(active); deleteIds.forEach((/** * @param {?} id * @return {?} */ id => delete entities[id])); patchState({ entities: Object.assign({}, entities), ids: ids.filter((/** * @param {?} id * @return {?} */ id => !deleteIds.includes(id))), active: wasActive ? undefined : active, lastUpdated: Date.now() }); } } /** * @param {?} __0 * @return {?} */ reset({ setState }) { setState(defaultEntityState()); } /** * @param {?} __0 * @param {?} __1 * @return {?} */ setLoading({ patchState }, { payload }) { patchState({ loading: payload }); } /** * @param {?} __0 * @param {?} __1 * @return {?} */ setActive({ patchState }, { payload }) { patchState({ active: payload }); } /** * @param {?} __0 * @return {?} */ clearActive({ patchState }) { patchState({ active: undefined }); } /** * @param {?} __0 * @param {?} __1 * @return {?} */ setError({ patchState }, { payload }) { patchState({ error: payload }); } /** * @param {?} __0 * @param {?} __1 * @return {?} */ goToPage({ getState, patchState }, { payload }) { if ('page' in payload) { patchState({ pageIndex: payload.page }); return; } else if (payload['first']) { patchState({ pageIndex: 0 }); return; } const { pageSize, pageIndex, ids } = getState(); /** @type {?} */ const totalSize = ids.length; /** @type {?} */ const maxIndex = Math.floor(totalSize / pageSize); if ('last' in payload) { patchState({ pageIndex: maxIndex }); } else { /** @type {?} */ const step = payload['prev'] ? -1 : 1; /** @type {?} */ let index = pageIndex + step; index = wrapOrClamp(payload.wrap, index, 0, maxIndex); patchState({ pageIndex: index }); } } /** * @param {?} __0 * @param {?} __1 * @return {?} */ setPageSize({ patchState }, { payload }) { patchState({ pageSize: payload }); } // ------------------- UTILITY ------------------- /** * A utility function to update the given state with the given entities. * It returns a state model with the new entities map and IDs. * For each given entity an ID will be generated. The generated ID will overwrite the current value: * <code>entity[this.idKey] = generatedId(entity, state);</code> * If the ID wasn't present, it will be added to the state's IDs array. * @private * @param {?} state The current state to act on * @param {?} payload One or multiple partial entities * @param {?} generateId A function to generate an ID for each given entity * @return {?} */ _addOrReplace(state, payload, generateId) { const { entities, ids } = state; asArray(payload).forEach((/** * @param {?} entity * @return {?} */ entity => { /** @type {?} */ const id = generateId(entity, state); entity[this.idKey] = id; entities[id] = entity; if (!ids.includes(id)) { ids.push(id); } })); return { entities: Object.assign({}, entities), ids: [...ids] }; } /** * A utility function to update the given entities map with the provided partial entity. * After checking if an entity with the given ID is present, the #onUpdate method is called. * @private * @param {?} entities The current entity map * @param {?} entity The partial entity to update with * @param {?=} id The ID to find the current entity in the map * @return {?} */ _update(entities, entity, id = this.idOf(entity)) { if (id === undefined) { throw new UpdateFailedError(new InvalidIdError(id)); } /** @type {?} */ const current = entities[id]; if (current === undefined) { throw new UpdateFailedError(new NoSuchEntityError(id)); } entities[id] = this.onUpdate(current, entity); return entities; } /** * @private * @param {?} storeClass * @param {?} actions * @return {?} */ setup(storeClass, actions) { // validation if a matching action handler exists has moved to reflection-validation tests actions.forEach((/** * @param {?} fn * @return {?} */ fn => { /** @type {?} */ const actionName = `[${this.storePath}] ${fn}`; storeClass[NGXS_META_KEY].actions[actionName] = [ { fn: fn, options: {}, type: actionName } ]; })); } /** * Returns the id of the given entity, based on the defined idKey. * This methods allows Partial entities and thus might return undefined. * Other methods calling this one have to handle this case themselves. * @protected * @param {?} data a partial entity * @return {?} */ idOf(data) { return data[this.idKey]; } } /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ var IdStrategy; (function (IdStrategy) { /** * @abstract * @template T */ class IdGenerator { /** * @protected * @param {?} idKey */ constructor(idKey) { this.idKey = idKey; } /** * Checks if the given id is in the state's ID array * @param {?} id the ID to check * @param {?} state the current state * @return {?} */ isIdInState(id, state) { return state.ids.includes(id); } /** * This function tries to get the present ID of the given entity with #getIdOf. * If it's undefined the #generateId function will be used. * @see getIdOf / generateId * @param {?} entity The entity to get the ID from * @param {?} state The current state * @return {?} */ getPresentIdOrGenerate(entity, state) { /** @type {?} */ const presentId = this.getIdOf(entity); return presentId === undefined ? this.generateId(entity, state) : presentId; } /** * A wrapper for #getIdOf. If the function returns undefined an error will be thrown. * @see getIdOf / InvalidIdOfError * @param {?} entity The entity to get the ID from * @return {?} */ mustGetIdOf(entity) { /** @type {?} */ const id = this.getIdOf(entity); if (id === undefined) { throw new InvalidIdOfError(); } return id; } /** * Returns the ID for the given entity. Can return undefined. * @param {?} entity The entity to get the ID from * @return {?} */ getIdOf(entity) { return entity[this.idKey]; } } IdStrategy.IdGenerator = IdGenerator; /** * @template T */ class IncrementingIdGenerator extends IdGenerator { /** * @param {?} idKey */ constructor(idKey) { super(idKey); } /** * @param {?} entity * @param {?} state * @return {?} */ generateId(entity, state) { /** @type {?} */ const max = Math.max(-1, ...state.ids.map((/** * @param {?} id * @return {?} */ id => parseInt(id, 10)))); return (max + 1).toString(10); } } IdStrategy.IncrementingIdGenerator = IncrementingIdGenerator; /** * @template T */ class UUIDGenerator extends IdGenerator { /** * @param {?} idKey */ constructor(idKey) { super(idKey); } /** * @param {?} entity * @param {?} state * @return {?} */ generateId(entity, state) { /** @type {?} */ let nextId; do { nextId = this.uuidv4(); } while (this.isIdInState(nextId, state)); return nextId; } /** * @private * @return {?} */ uuidv4() { // https://stackoverflow.com/a/2117523 return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (/** * @param {?} c * @return {?} */ function (c) { /** @type {?} */ const r = (Math.random() * 16) | 0; // tslint:disable-line /** @type {?} */ const v = c === 'x' ? r : (r & 0x3) | 0x8; return v.toString(16); })); } } IdStrategy.UUIDGenerator = UUIDGenerator; /** * @template T */ class EntityIdGenerator extends IdGenerator { /** * @param {?} idKey */ constructor(idKey) { super(idKey); } /** * @param {?} entity * @param {?} state * @return {?} */ generateId(entity, state) { /** @type {?} */ const id = this.mustGetIdOf(entity); if (this.isIdInState(id, state)) { throw new UnableToGenerateIdError(`The provided ID already exists: ${id}`); } return id; } } IdStrategy.EntityIdGenerator = EntityIdGenerator; })(IdStrategy || (IdStrategy = {})); /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ /** @type {?} */ const ofEntityAction = (/** * @param {?} state * @param {?} actionType * @return {?} */ (state, actionType) => { /** @type {?} */ const statePath = state[NGXS_META_KEY].path; /** @type {?} */ const type = `[${statePath}] ${actionType}`; return ofAction({ type: type }); }); /** @type {?} */ const ofEntityActionDispatched = (/** * @param {?} state * @param {?} actionType * @return {?} */ (state, actionType) => { /** @type {?} */ const statePath = state[NGXS_META_KEY].path; /** @type {?} */ const type = `[${statePath}] ${actionType}`; return ofActionDispatched({ type: type }); }); /** @type {?} */ const ofEntityActionSuccessful = (/** * @param {?} state * @param {?} actionType * @return {?} */ (state, actionType) => { /** @type {?} */ const statePath = state[NGXS_META_KEY].path; /** @type {?} */ const type = `[${statePath}] ${actionType}`; return ofActionSuccessful({ type: type }); }); /** @type {?} */ const ofEntityActionErrored = (/** * @param {?} state * @param {?} actionType * @return {?} */ (state, actionType) => { /** @type {?} */ const statePath = state[NGXS_META_KEY].path; /** @type {?} */ const type = `[${statePath}] ${actionType}`; return ofActionErrored({ type: type }); }); /** @type {?} */ const ofEntityActionCompleted = (/** * @param {?} state * @param {?} actionType * @return {?} */ (state, actionType) => { /** @type {?} */ const statePath = state[NGXS_META_KEY].path; /** @type {?} */ const type = `[${statePath}] ${actionType}`; return ofActionCompleted({ type: type }); }); /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ export { Add, CreateOrReplace, Remove, Update, SetLoading, SetActive, ClearActive, RemoveActive, UpdateActive, SetError, Reset, EntityActionType, GoToPage, SetPageSize, defaultEntityState, EntityState, EntityStateError, NoActiveEntityError, NoSuchEntityError, InvalidIdError, InvalidIdOfError, UpdateFailedError, UnableToGenerateIdError, IdStrategy, ofEntityAction, ofEntityActionDispatched, ofEntityActionSuccessful, ofEntityActionErrored, ofEntityActionCompleted }; //# sourceMappingURL=ngxs-labs-entity-state.js.map