@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
JavaScript
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