@ngxs-labs/entity-state
Version:
<p align="center"> <img src="https://raw.githubusercontent.com/ngxs-labs/emitter/master/docs/assets/logo.png"> </p>
1,019 lines (987 loc) • 39.7 kB
JavaScript
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@ngxs/store')) :
typeof define === 'function' && define.amd ? define(['exports', '@ngxs/store'], factory) :
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory((global['ngxs-labs'] = global['ngxs-labs'] || {}, global['ngxs-labs']['entity-state'] = {}), global.ngxs.store));
}(this, (function (exports, store) { 'use strict';
/*! *****************************************************************************
Copyright (c) Microsoft Corporation. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License"); you may not use
this file except in compliance with the License. You may obtain a copy of the
License at http://www.apache.org/licenses/LICENSE-2.0
THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
MERCHANTABLITY OR NON-INFRINGEMENT.
See the Apache Version 2.0 License for specific language governing permissions
and limitations under the License.
***************************************************************************** */
var __assign = function() {
__assign = Object.assign || function __assign(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
*/
/**
* @template T
* @param {...?} operators
* @return {?}
*/
function compose() {
var operators = [];
for (var _i = 0; _i < arguments.length; _i++) {
operators[_i] = arguments[_i];
}
return (/**
* @param {?} existing
* @return {?}
*/
function composeOperator(existing) {
return operators.reduce((/**
* @param {?} accumulator
* @param {?} operator
* @return {?}
*/
function (accumulator, operator) { return operator(accumulator); }), existing);
});
}
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
*/
/**
* @template T
* @param {?} value
* @return {?}
*/
function isStateOperator(value) {
return typeof value === 'function';
}
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
*/
/**
* @template T
* @param {?} patchObject
* @return {?}
*/
function patch(patchObject) {
return (/**
* @template U
* @param {?} existing
* @return {?}
*/
function patchStateOperator(existing) {
/** @type {?} */
var clone = null;
for (var k in patchObject) {
/** @type {?} */
var newValue = patchObject[k];
/** @type {?} */
var existingPropValue = existing[k];
/** @type {?} */
var newPropValue = isStateOperator(newValue)
? newValue((/** @type {?} */ (existingPropValue)))
: newValue;
if (newPropValue !== existingPropValue) {
if (!clone) {
clone = __assign({}, ((/** @type {?} */ (existing))));
}
clone[k] = newPropValue;
}
}
return clone || existing;
});
}
class EntityStateError extends Error {
constructor(message) {
super(message);
}
}
class NoActiveEntityError extends EntityStateError {
constructor(additionalInformation = '') {
super(('No active entity to affect. ' + additionalInformation).trim());
}
}
class NoSuchEntityError extends EntityStateError {
constructor(id) {
super(`No entity for ID ${id}`);
}
}
class InvalidIdError extends EntityStateError {
constructor(id) {
super(`Invalid ID: ${id}`);
}
}
class InvalidIdOfError extends EntityStateError {
constructor() {
super(`idOf returned undefined`);
}
}
class UpdateFailedError extends EntityStateError {
constructor(cause) {
super(`Updating entity failed.\n\tCause: ${cause}`);
}
}
class UnableToGenerateIdError extends EntityStateError {
constructor(cause) {
super(`Unable to generate an ID.\n\tCause: ${cause}`);
}
}
class InvalidEntitySelectorError extends EntityStateError {
constructor(invalidSelector) {
super(`Cannot use ${invalidSelector} as EntitySelector`);
}
}
const NGXS_META_KEY = 'NGXS_META';
/**
* This function generates a new object for the ngxs Action with the given fn name
* @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
*/
function generateActionObject(fn, store, payload) {
const name = store[NGXS_META_KEY].path;
const ReflectedAction = function (data) {
this.payload = data;
};
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
* @param state the state of an entity state
*/
function getActive(state) {
return state.entities[state.active];
}
/**
* Returns the active entity. If none is present an error will be thrown.
* @param state The state to act on
*/
function mustGetActive(state) {
const active = getActive(state);
if (active === undefined) {
throw new NoActiveEntityError();
}
return { id: state.active, active };
}
/**
* Returns input as an array if it isn't one already
* @param input The input to make an array if necessary
*/
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
*/
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
*/
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;
}
}
/**
* Enum that contains all existing Actions for the Entity State adapter.
*/
(function (EntityActionType) {
EntityActionType["Add"] = "add";
EntityActionType["CreateOrReplace"] = "createOrReplace";
EntityActionType["Update"] = "update";
EntityActionType["UpdateAll"] = "updateAll";
EntityActionType["UpdateActive"] = "updateActive";
EntityActionType["Remove"] = "remove";
EntityActionType["RemoveAll"] = "removeAll";
EntityActionType["RemoveActive"] = "removeActive";
EntityActionType["SetLoading"] = "setLoading";
EntityActionType["SetError"] = "setError";
EntityActionType["SetActive"] = "setActive";
EntityActionType["ClearActive"] = "clearActive";
EntityActionType["Reset"] = "reset";
EntityActionType["GoToPage"] = "goToPage";
EntityActionType["SetPageSize"] = "setPageSize";
})(exports.EntityActionType || (exports.EntityActionType = {}));
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.
* @param target The targeted state class
* @param payload An entity or an array of entities to be added
* @see CreateOrReplace#constructor
*/
constructor(target, payload) {
return generateActionObject(exports.EntityActionType.Add, target, payload);
}
}
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.
* @param target The targeted state class
* @param payload An entity or an array of entities to be added
* @see Add#constructor
*/
constructor(target, payload) {
return generateActionObject(exports.EntityActionType.CreateOrReplace, target, payload);
}
}
class Remove {
/**
* Generates an action that will remove the given entities from the state.
* @param target The targeted state class
* @param payload An EntitySelector payload
* @see EntitySelector
* @see RemoveAll
*/
constructor(target, payload) {
return generateActionObject(exports.EntityActionType.Remove, target, payload);
}
}
class RemoveAll {
/**
* Generates an action that will remove all entities from the state.
* @param target The targeted state class
*/
constructor(target) {
return generateActionObject(exports.EntityActionType.RemoveAll, target);
}
}
class Update {
/**
* Generates an action that will update all entities, specified by the given selector.
* @param target The targeted state class
* @param selector An EntitySelector that determines the entities to update
* @param data An Updater that will be applied to the selected entities
* @see EntitySelector
* @see Updater
*/
constructor(target, selector, data) {
return generateActionObject(exports.EntityActionType.Update, target, {
selector,
data
});
}
}
class UpdateAll {
/**
* Generates an action that will update all entities.
* If no entity is active a runtime error will be thrown.
* @param target The targeted state class
* @param data An Updater that will be applied to all entities
* @see EntitySelector
* @see Updater
*/
constructor(target, data) {
return generateActionObject(exports.EntityActionType.UpdateAll, target, {
data
});
}
}
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(exports.EntityActionType.SetLoading, target, loading);
}
}
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(exports.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(exports.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(exports.EntityActionType.RemoveActive, target);
}
}
class UpdateActive {
/**
* Generates an action that will update the current active entity.
* If no entity is active a runtime error will be thrown.
* @param target The targeted state class
* @param payload An Updater payload
* @see Updater
*/
constructor(target, payload) {
return generateActionObject(exports.EntityActionType.UpdateActive, target, payload);
}
}
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(exports.EntityActionType.SetError, target, error);
}
}
class Reset {
/**
* Resets the targeted store to the default state: no entities, loading is false, error is undefined, active is undefined.
* @param target The targeted state class
* @see defaultEntityState
*/
constructor(target) {
return generateActionObject(exports.EntityActionType.Reset, target);
}
}
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(exports.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(exports.EntityActionType.SetPageSize, target, payload);
}
}
/**
* Adds or replaces the given entities to the state.
* For each entity an ID will be calculated, based on the given provider.
* This operator ensures that the calculated ID is added to the entity, at the specified id-field.
* The `lastUpdated` timestamp will be updated.
* @param entities the new entities to add or replace
* @param idKey key of the id-field of an entity
* @param idProvider function to provide an ID for the given entity
*/
function addOrReplace(entities, idKey, idProvider) {
return (state) => {
const nextEntities = Object.assign({}, state.entities);
const nextIds = [...state.ids];
let nextState = state; // will be reassigned while looping over new entities
asArray(entities).forEach(entity => {
const id = idProvider(entity, nextState);
let updatedEntity = entity;
if (entity[idKey] !== id) {
// ensure ID is in the entity
updatedEntity = Object.assign(Object.assign({}, entity), { [idKey]: id });
}
nextEntities[id] = updatedEntity;
if (!nextIds.includes(id)) {
nextIds.push(id);
}
nextState = Object.assign(Object.assign({}, nextState), { entities: nextEntities, ids: nextIds });
});
return Object.assign(Object.assign({}, nextState), { entities: nextEntities, ids: nextIds, lastUpdated: Date.now() });
};
}
function updateTimestamp() {
return patch({
lastUpdated: Date.now()
});
}
function alsoUpdateTimestamp(operator) {
return compose(updateTimestamp(), operator);
}
/**
* Removes all entities, clears the active entity and updates the `lastUpdated` timestamp.
*/
function removeAllEntities() {
return (state) => {
return Object.assign(Object.assign({}, state), { entities: {}, ids: [], active: undefined, lastUpdated: Date.now() });
};
}
/**
* Removes the entities specified by the given IDs.
* The active entity will be cleared if included in the given IDs.
* Updates the `lastUpdated` timestamp.
* @param ids IDs to remove
*/
function removeEntities(ids) {
const entityRemoval = patch({
entities: removeEntitiesFromDictionary(ids),
ids: removeEntitiesFromArray(ids)
});
return compose(entityRemoval, clearActiveIfRemoved(ids), updateTimestamp());
}
/**
* Only clears the `active` entity, if it's included in the given array.
* All other fields will remain untouched in any case.
* @param idsForRemoval the IDs to be removed
*/
function clearActiveIfRemoved(idsForRemoval) {
return (state) => {
return Object.assign(Object.assign({}, state), { active: idsForRemoval.includes(state.active) ? undefined : state.active });
};
}
/**
* Removes the given items from the existing items, based on equality.
* @param forRemoval items to remove
*/
function removeEntitiesFromArray(forRemoval) {
return (existing) => {
return existing.filter(value => !forRemoval.includes(value));
};
}
/**
* Removes items from the dictionary, based on the given keys.
* @param keysForRemoval the keys to be removed
*/
function removeEntitiesFromDictionary(keysForRemoval) {
return (existing) => {
const clone = Object.assign({}, existing);
keysForRemoval.forEach(key => delete clone[key]);
return clone;
};
}
/**
* Updates the given entities in the state.
* Entities will be merged with the given `onUpdate` function.
* @param payload the updated entities
* @param idKey key of the id-field of an entity
* @param onUpdate update function to call on each entity
*/
function update(payload, idKey, onUpdate) {
return (state) => {
let entities = Object.assign({}, state.entities); // create copy
const affected = getAffectedValues(Object.values(entities), payload.selector, idKey);
if (typeof payload.data === 'function') {
affected.forEach(entity => {
entities = updateDictionary(entities, payload.data(entity), entity[idKey], onUpdate);
});
}
else {
affected.forEach(entity => {
entities = updateDictionary(entities, payload.data, entity[idKey], onUpdate);
});
}
return Object.assign(Object.assign({}, state), { entities, lastUpdated: Date.now() });
};
}
function updateActive(payload, idKey, onUpdate) {
return (state) => {
const { id: activeId, active } = mustGetActive(state);
const { entities } = state;
if (typeof payload === 'function') {
return Object.assign(Object.assign({}, state), { entities: updateDictionary(entities, payload(active), activeId, onUpdate), lastUpdated: Date.now() });
}
else {
return Object.assign(Object.assign({}, state), { entities: updateDictionary(entities, payload, activeId, onUpdate), lastUpdated: Date.now() });
}
};
}
function updateDictionary(entities, entity, id, onUpdate) {
if (id === undefined) {
throw new UpdateFailedError(new InvalidIdError(id));
}
const current = entities[id];
if (current === undefined) {
throw new UpdateFailedError(new NoSuchEntityError(id));
}
const updated = onUpdate(current, entity);
return Object.assign(Object.assign({}, entities), { [id]: updated });
}
function getAffectedValues(entities, selector, idKey) {
if (selector === null) {
return entities;
}
else if (typeof selector === 'function') {
return entities.filter(entity => selector(entity));
}
else {
const ids = asArray(selector);
return entities.filter(entity => ids.includes(entity[idKey]));
}
}
/**
* 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.
*/
function defaultEntityState(defaults = {}) {
return Object.assign({ entities: {}, ids: [], loading: false, error: undefined, active: undefined, pageSize: 10, pageIndex: 0, lastUpdated: Date.now() }, defaults);
}
// tslint:disable:member-ordering
// @dynamic
class EntityState {
constructor(storeClass, _idKey, idStrategy) {
this.idKey = _idKey;
this.storePath = storeClass[NGXS_META_KEY].path;
this.idGenerator = new idStrategy(_idKey);
this.setup(storeClass, Object.values(exports.EntityActionType));
}
/**
* 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
* @param current The current entity, readonly
* @param updated The new data as a partial entity
* @example
* // default behavior
* onUpdate(current: Readonly<T updated: Partial<T>): T {
return {...current, ...updated};
}
*/
onUpdate(current, updated) {
return Object.assign(Object.assign({}, current), updated);
}
// ------------------- SELECTORS -------------------
/**
* Returns a selector for the activeId
*/
static get activeId() {
return store.createSelector([this], state => state.active);
}
/**
* Returns a selector for the active entity
*/
static get active() {
return store.createSelector([this], state => getActive(state));
}
/**
* Returns a selector for the keys of all entities
*/
static get keys() {
return store.createSelector([this], state => {
return Object.keys(state.entities);
});
}
/**
* Returns a selector for all entities, sorted by insertion order
*/
static get entities() {
return store.createSelector([this], state => {
return state.ids.map(id => state.entities[id]);
});
}
/**
* Returns a selector for the nth entity, sorted by insertion order
*/
static nthEntity(index) {
return store.createSelector([this], state => {
const id = state.ids[index];
return state.entities[id];
});
}
/**
* Returns a selector for paginated entities, sorted by insertion order
*/
static get paginatedEntities() {
return store.createSelector([this], state => {
const { ids, pageIndex, pageSize } = state;
return ids
.slice(pageIndex * pageSize, (pageIndex + 1) * pageSize)
.map(id => state.entities[id]);
});
}
/**
* Returns a selector for the map of entities
*/
static get entitiesMap() {
return store.createSelector([this], state => {
return state.entities;
});
}
/**
* Returns a selector for the size of the entity map
*/
static get size() {
return store.createSelector([this], state => {
return Object.keys(state.entities).length;
});
}
/**
* Returns a selector for the error
*/
static get error() {
return store.createSelector([this], state => {
return state.error;
});
}
/**
* Returns a selector for the loading state
*/
static get loading() {
return store.createSelector([this], state => {
return state.loading;
});
}
/**
* Returns a selector for the latest added entity
*/
static get latest() {
return store.createSelector([this], state => {
const latestId = state.ids[state.ids.length - 1];
return state.entities[latestId];
});
}
/**
* Returns a selector for the latest added entity id
*/
static get latestId() {
return store.createSelector([this], state => {
return state.ids[state.ids.length - 1];
});
}
/**
* Returns a selector for the update timestamp
*/
static get lastUpdated() {
return store.createSelector([this], state => {
return new Date(state.lastUpdated);
});
}
/**
* Returns a selector for age, based on the update timestamp
*/
static get age() {
return store.createSelector([this], state => {
return Date.now() - state.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.
*/
add({ setState }, { payload }) {
setState(addOrReplace(payload, this.idKey, (entity, state) => this.idGenerator.generateId(entity, state)));
}
/**
* 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.
*/
createOrReplace({ setState }, { payload }) {
setState(addOrReplace(payload, this.idKey, (entity, state) => this.idGenerator.getPresentIdOrGenerate(entity, state)));
}
update({ setState }, { payload }) {
if (payload.selector == null) {
throw new InvalidEntitySelectorError(payload);
}
setState(update(payload, this.idKey, (current, updated) => this.onUpdate(current, updated)));
}
updateAll({ setState }, { payload }) {
setState(update(Object.assign(Object.assign({}, payload), { selector: null }), this.idKey, (current, updated) => this.onUpdate(current, updated)));
}
updateActive({ setState }, { payload }) {
setState(updateActive(payload, this.idKey, (current, updated) => this.onUpdate(current, updated)));
}
removeActive({ getState, setState }) {
const { active } = getState();
setState(removeEntities([active]));
}
remove({ getState, setState, patchState }, { payload }) {
if (payload === null) {
throw new InvalidEntitySelectorError(payload);
}
else {
const deleteIds = typeof payload === 'function'
? Object.values(getState().entities)
.filter(entity => payload(entity))
.map(entity => this.idOf(entity))
: asArray(payload);
// can't pass in predicate as you need IDs and thus EntityState#idOf
setState(removeEntities(deleteIds));
}
}
removeAll({ setState }) {
setState(removeAllEntities());
}
reset({ setState }) {
setState(defaultEntityState());
}
setLoading({ patchState }, { payload }) {
patchState({ loading: payload });
}
setActive({ patchState }, { payload }) {
patchState({ active: payload });
}
clearActive({ patchState }) {
patchState({ active: undefined });
}
setError({ patchState }, { payload }) {
patchState({ error: payload });
}
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();
const totalSize = ids.length;
const maxIndex = Math.floor(totalSize / pageSize);
if ('last' in payload) {
patchState({ pageIndex: maxIndex });
}
else {
const step = payload['prev'] ? -1 : 1;
let index = pageIndex + step;
index = wrapOrClamp(payload.wrap, index, 0, maxIndex);
patchState({ pageIndex: index });
}
}
setPageSize({ patchState }, { payload }) {
patchState({ pageSize: payload });
}
// ------------------- UTILITY -------------------
setup(storeClass, actions) {
// validation if a matching action handler exists has moved to reflection-validation tests
actions.forEach(fn => {
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.
* @param data a partial entity
*/
idOf(data) {
return data[this.idKey];
}
}
(function (IdStrategy) {
class IdGenerator {
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
*/
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.
* @param entity The entity to get the ID from
* @param state The current state
* @see getIdOf
* @see generateId
*/
getPresentIdOrGenerate(entity, state) {
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.
* @param entity The entity to get the ID from
* @see getIdOf
* @see InvalidIdOfError
*/
mustGetIdOf(entity) {
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
*/
getIdOf(entity) {
return entity[this.idKey];
}
}
IdStrategy.IdGenerator = IdGenerator;
class IncrementingIdGenerator extends IdGenerator {
constructor(idKey) {
super(idKey);
}
generateId(entity, state) {
const max = Math.max(-1, ...state.ids.map(id => parseInt(id, 10)));
return (max + 1).toString(10);
}
}
IdStrategy.IncrementingIdGenerator = IncrementingIdGenerator;
class UUIDGenerator extends IdGenerator {
constructor(idKey) {
super(idKey);
}
generateId(entity, state) {
let nextId;
do {
nextId = this.uuidv4();
} while (this.isIdInState(nextId, state));
return nextId;
}
uuidv4() {
// https://stackoverflow.com/a/2117523
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
const r = (Math.random() * 16) | 0; // tslint:disable-line
const v = c === 'x' ? r : (r & 0x3) | 0x8; // tslint:disable-line
return v.toString(16);
});
}
}
IdStrategy.UUIDGenerator = UUIDGenerator;
class EntityIdGenerator extends IdGenerator {
constructor(idKey) {
super(idKey);
}
generateId(entity, state) {
const id = this.mustGetIdOf(entity);
if (this.isIdInState(id, state)) {
throw new UnableToGenerateIdError(`The provided ID already exists: ${id}`);
}
return id;
}
}
IdStrategy.EntityIdGenerator = EntityIdGenerator;
})(exports.IdStrategy || (exports.IdStrategy = {}));
const ofEntityAction = (state, actionType) => {
const statePath = state[NGXS_META_KEY].path;
const type = `[${statePath}] ${actionType}`;
return store.ofAction({
type: type
});
};
const ofEntityActionDispatched = (state, actionType) => {
const statePath = state[NGXS_META_KEY].path;
const type = `[${statePath}] ${actionType}`;
return store.ofActionDispatched({
type: type
});
};
const ofEntityActionSuccessful = (state, actionType) => {
const statePath = state[NGXS_META_KEY].path;
const type = `[${statePath}] ${actionType}`;
return store.ofActionSuccessful({
type: type
});
};
const ofEntityActionErrored = (state, actionType) => {
const statePath = state[NGXS_META_KEY].path;
const type = `[${statePath}] ${actionType}`;
return store.ofActionErrored({
type: type
});
};
const ofEntityActionCompleted = (state, actionType) => {
const statePath = state[NGXS_META_KEY].path;
const type = `[${statePath}] ${actionType}`;
return store.ofActionCompleted({
type: type
});
};
exports.Add = Add;
exports.ClearActive = ClearActive;
exports.CreateOrReplace = CreateOrReplace;
exports.EntityState = EntityState;
exports.EntityStateError = EntityStateError;
exports.GoToPage = GoToPage;
exports.InvalidEntitySelectorError = InvalidEntitySelectorError;
exports.InvalidIdError = InvalidIdError;
exports.InvalidIdOfError = InvalidIdOfError;
exports.NoActiveEntityError = NoActiveEntityError;
exports.NoSuchEntityError = NoSuchEntityError;
exports.Remove = Remove;
exports.RemoveActive = RemoveActive;
exports.RemoveAll = RemoveAll;
exports.Reset = Reset;
exports.SetActive = SetActive;
exports.SetError = SetError;
exports.SetLoading = SetLoading;
exports.SetPageSize = SetPageSize;
exports.UnableToGenerateIdError = UnableToGenerateIdError;
exports.Update = Update;
exports.UpdateActive = UpdateActive;
exports.UpdateAll = UpdateAll;
exports.UpdateFailedError = UpdateFailedError;
exports.addOrReplace = addOrReplace;
exports.alsoUpdateTimestamp = alsoUpdateTimestamp;
exports.clearActiveIfRemoved = clearActiveIfRemoved;
exports.defaultEntityState = defaultEntityState;
exports.ofEntityAction = ofEntityAction;
exports.ofEntityActionCompleted = ofEntityActionCompleted;
exports.ofEntityActionDispatched = ofEntityActionDispatched;
exports.ofEntityActionErrored = ofEntityActionErrored;
exports.ofEntityActionSuccessful = ofEntityActionSuccessful;
exports.removeAllEntities = removeAllEntities;
exports.removeEntities = removeEntities;
exports.removeEntitiesFromArray = removeEntitiesFromArray;
exports.removeEntitiesFromDictionary = removeEntitiesFromDictionary;
exports.update = update;
exports.updateActive = updateActive;
exports.updateDictionary = updateDictionary;
exports.updateTimestamp = updateTimestamp;
Object.defineProperty(exports, '__esModule', { value: true });
})));