@agile-ts/core
Advanced tools
Comparing version 0.2.0-alpha.3 to 0.2.0-alpha.4
@@ -1,758 +0,22 @@ | ||
import { Agile, Item, Group, GroupKey, Selector, SelectorKey, StorageKey, GroupConfigInterface, CollectionPersistent, GroupAddConfigInterface, SideEffectConfigInterface, SelectorConfigInterface, PatchOptionConfigInterface } from '../internal'; | ||
export declare class Collection<DataType extends Object = DefaultItem, GroupValueType = Array<ItemKey>> { | ||
agileInstance: () => Agile; | ||
config: CollectionConfigInterface; | ||
private initialConfig; | ||
_key?: CollectionKey; | ||
size: number; | ||
data: { | ||
[key: string]: Item<DataType>; | ||
}; | ||
isPersisted: boolean; | ||
persistent: CollectionPersistent<DataType> | undefined; | ||
groups: { | ||
[key: string]: Group<DataType>; | ||
}; | ||
selectors: { | ||
[key: string]: Selector<DataType>; | ||
}; | ||
isInstantiated: boolean; | ||
isCollection: boolean; | ||
/** | ||
* A Collection manages a reactive set of Information | ||
* that we need to remember globally at a later point in time. | ||
* While providing a toolkit to use and mutate this set of Information. | ||
* | ||
* It is designed for arrays of data objects following the same pattern. | ||
* | ||
* Each of these data object must have a unique `primaryKey` to be correctly identified later. | ||
* | ||
* You can create as many global Collections as you need. | ||
* | ||
* [Learn more..](https://agile-ts.org/docs/core/collection/) | ||
* | ||
* @public | ||
* @param agileInstance - Instance of Agile the Collection belongs to. | ||
* @param config - Configuration object | ||
*/ | ||
constructor(agileInstance: Agile, config?: CollectionConfig<DataType>); | ||
/** | ||
* Updates the key/name identifier of the Collection. | ||
* | ||
* [Learn more..](https://agile-ts.org/docs/core/collection/properties#key) | ||
* | ||
* @public | ||
* @param value - New key/name identifier. | ||
*/ | ||
set key(value: CollectionKey | undefined); | ||
/** | ||
* Returns the key/name identifier of the Collection. | ||
* | ||
* [Learn more..](https://agile-ts.org/docs/core/collection/properties#key) | ||
* | ||
* @public | ||
*/ | ||
get key(): CollectionKey | undefined; | ||
/** | ||
* Updates the key/name identifier of the Collection. | ||
* | ||
* [Learn more..](https://agile-ts.org/docs/core/collection/methods/#setkey) | ||
* | ||
* @public | ||
* @param value - New key/name identifier. | ||
*/ | ||
setKey(value: CollectionKey | undefined): this; | ||
/** | ||
* Creates a new Group without associating it to the Collection. | ||
* | ||
* This way of creating a Group is intended for use in the Collection configuration object, | ||
* where the `constructor()` takes care of the binding. | ||
* | ||
* After a successful initiation of the Collection we recommend using `createGroup()`, | ||
* because it automatically connects the Group to the Collection. | ||
* | ||
* [Learn more..](https://agile-ts.org/docs/core/collection/methods/#group) | ||
* | ||
* @public | ||
* @param initialItems - Key/Name identifiers of the Items to be clustered by the Group. | ||
* @param config - Configuration object | ||
*/ | ||
Group(initialItems?: Array<ItemKey>, config?: GroupConfigInterface): Group<DataType>; | ||
/** | ||
* Creates a new Selector without associating it to the Collection. | ||
* | ||
* This way of creating a Selector is intended for use in the Collection configuration object, | ||
* where the `constructor()` takes care of the binding. | ||
* | ||
* After a successful initiation of the Collection we recommend using `createSelector()`, | ||
* because it automatically connects the Group to the Collection. | ||
* | ||
* [Learn more..](https://agile-ts.org/docs/core/collection/methods/#selector) | ||
* | ||
* @public | ||
* @param initialKey - Key/Name identifier of the Item to be represented by the Selector. | ||
* @param config - Configuration object | ||
*/ | ||
Selector(initialKey: ItemKey | null, config?: SelectorConfigInterface): Selector<DataType>; | ||
/** | ||
* Sets up the specified Groups or Group keys | ||
* and assigns them to the Collection if they are valid. | ||
* | ||
* It also instantiates and assigns the default Group to the Collection. | ||
* The default Group reflects the default pattern of the Collection. | ||
* | ||
* @internal | ||
* @param groups - Entire Groups or Group keys to be set up. | ||
*/ | ||
initGroups(groups: { | ||
[key: string]: Group<any>; | ||
} | string[]): void; | ||
/** | ||
* Sets up the specified Selectors or Selector keys | ||
* and assigns them to the Collection if they are valid. | ||
* | ||
* @internal | ||
* @param selectors - Entire Selectors or Selector keys to be set up. | ||
*/ | ||
initSelectors(selectors: { | ||
[key: string]: Selector<any>; | ||
} | string[]): void; | ||
/** | ||
* Appends new data objects following the same pattern to the end of the Collection. | ||
* | ||
* Each collected `data object` requires a unique identifier at the primaryKey property (by default 'id') | ||
* to be correctly identified later. | ||
* | ||
* For example, if we collect some kind of user object, | ||
* it must contain such unique identifier at 'id' | ||
* to be added to the Collection. | ||
* ``` | ||
* MY_COLLECTION.collect({id: '1', name: 'jeff'}); // valid | ||
* MY_COLLECTION.collect({name: 'frank'}); // invalid | ||
* ``` | ||
* | ||
* [Learn more..](https://agile-ts.org/docs/core/collection/methods/#collect) | ||
* | ||
* @public | ||
* @param data - Data objects or entire Items to be added. | ||
* @param groupKeys - Group/s to which the specified data objects or Items are to be added. | ||
* @param config - Configuration object | ||
*/ | ||
collect(data: DataType | Item<DataType> | Array<DataType | Item<DataType>>, groupKeys?: GroupKey | Array<GroupKey>, config?: CollectConfigInterface<DataType>): this; | ||
/** | ||
* Updates the Item `data object` with the specified `object with changes`, if the Item exists. | ||
* By default the `object with changes` is merged into the Item `data object` at top level. | ||
* | ||
* [Learn more..](https://agile-ts.org/docs/core/collection/methods/#update) | ||
* | ||
* @public | ||
* @param itemKey - Key/Name identifier of the Item to be updated. | ||
* @param changes - Object with changes to be merged into the Item data object. | ||
* @param config - Configuration object | ||
*/ | ||
update(itemKey: ItemKey, changes: DefaultItem | DataType, config?: UpdateConfigInterface): Item<DataType> | undefined; | ||
/** | ||
* Creates a new Group and associates it to the Collection. | ||
* | ||
* [Learn more..](https://agile-ts.org/docs/core/collection/methods/#createGroup) | ||
* | ||
* @public | ||
* @param groupKey - Unique identifier of the Group to be created. | ||
* @param initialItems - Key/Name identifiers of the Items to be clustered by the Group. | ||
*/ | ||
createGroup(groupKey: GroupKey, initialItems?: Array<ItemKey>): Group<DataType>; | ||
/** | ||
* Returns a boolean indicating whether a Group with the specified `groupKey` | ||
* exists in the Collection or not. | ||
* | ||
* [Learn more..](https://agile-ts.org/docs/core/collection/methods/#hasgroup) | ||
* | ||
* @public | ||
* @param groupKey - Key/Name identifier of the Group to be checked for existence. | ||
* @param config - Configuration object | ||
*/ | ||
hasGroup(groupKey: GroupKey | undefined, config?: HasConfigInterface): boolean; | ||
/** | ||
* Retrieves a single Group with the specified key/name identifier from the Collection. | ||
* | ||
* If the to retrieve Group doesn't exist, `undefined` is returned. | ||
* | ||
* [Learn more..](https://agile-ts.org/docs/core/collection/methods/#getgroup) | ||
* | ||
* @public | ||
* @param groupKey - Key/Name identifier of the Group. | ||
* @param config - Configuration object | ||
*/ | ||
getGroup(groupKey: GroupKey | undefined | null, config?: HasConfigInterface): Group<DataType> | undefined; | ||
/** | ||
* Retrieves the default Group from the Collection. | ||
* | ||
* Every Collection should have a default Group, | ||
* which represents the default pattern of the Collection. | ||
* | ||
* If the default Group, for what ever reason, doesn't exist, `undefined` is returned. | ||
* | ||
* [Learn more..](https://agile-ts.org/docs/core/collection/methods/#getdefaultgroup) | ||
* | ||
* @public | ||
*/ | ||
getDefaultGroup(): Group<DataType> | undefined; | ||
/** | ||
* Retrieves a single Group with the specified key/name identifier from the Collection. | ||
* | ||
* If the to retrieve Group doesn't exist, a reference Group is returned. | ||
* This has the advantage that Components that have the reference Group bound to themselves | ||
* are rerenderd when the original Group is created. | ||
* | ||
* [Learn more..](https://agile-ts.org/docs/core/collection/methods/#getgroupwithreference) | ||
* | ||
* @public | ||
* @param groupKey - Key/Name identifier of the Group. | ||
*/ | ||
getGroupWithReference(groupKey: GroupKey): Group<DataType>; | ||
/** | ||
* Removes a Group with the specified key/name identifier from the Collection, | ||
* if it exists in the Collection. | ||
* | ||
* [Learn more..](https://agile-ts.org/docs/core/collection/methods/#removegroup) | ||
* | ||
* @public | ||
* @param groupKey - Key/Name identifier of the Group to be removed. | ||
*/ | ||
removeGroup(groupKey: GroupKey): this; | ||
/** | ||
* Returns the count of registered Groups in the Collection. | ||
* | ||
* [Learn more..](https://agile-ts.org/docs/core/collection/methods/#getgroupcount) | ||
* | ||
* @public | ||
*/ | ||
getGroupCount(): number; | ||
/** | ||
* Creates a new Selector and associates it to the Collection. | ||
* | ||
* [Learn more..](https://agile-ts.org/docs/core/collection/methods/#createSelector) | ||
* | ||
* @public | ||
* @param selectorKey - Unique identifier of the Selector to be created. | ||
* @param itemKey - Key/Name identifier of the Item to be represented by the Selector. | ||
*/ | ||
createSelector(selectorKey: SelectorKey, itemKey: ItemKey | null): Selector<DataType>; | ||
/** | ||
* Creates a new Selector and associates it to the Collection. | ||
* | ||
* The specified `itemKey` is used as the unique identifier key of the new Selector. | ||
* ``` | ||
* MY_COLLECTION.select('1'); | ||
* // is equivalent to | ||
* MY_COLLECTION.createSelector('1', '1'); | ||
* ``` | ||
* | ||
* [Learn more..](https://agile-ts.org/docs/core/collection/methods/#select) | ||
* | ||
* @public | ||
* @param itemKey - Key/Name identifier of the Item to be represented by the Selector | ||
* and used as unique identifier of the Selector. | ||
*/ | ||
select(itemKey: ItemKey): Selector<DataType>; | ||
/** | ||
* Returns a boolean indicating whether a Selector with the specified `selectorKey` | ||
* exists in the Collection or not. | ||
* | ||
* [Learn more..](https://agile-ts.org/docs/core/collection/methods/#hasselector) | ||
* | ||
* @public | ||
* @param selectorKey - Key/Name identifier of the Selector to be checked for existence. | ||
* @param config - Configuration object | ||
*/ | ||
hasSelector(selectorKey: SelectorKey | undefined, config?: HasConfigInterface): boolean; | ||
/** | ||
* Retrieves a single Selector with the specified key/name identifier from the Collection. | ||
* | ||
* If the to retrieve Selector doesn't exist, `undefined` is returned. | ||
* | ||
* [Learn more..](https://agile-ts.org/docs/core/collection/methods/#getselector) | ||
* | ||
* @public | ||
* @param selectorKey - Key/Name identifier of the Selector. | ||
* @param config - Configuration object | ||
*/ | ||
getSelector(selectorKey: SelectorKey | undefined | null, config?: HasConfigInterface): Selector<DataType> | undefined; | ||
/** | ||
* Retrieves a single Selector with the specified key/name identifier from the Collection. | ||
* | ||
* If the to retrieve Selector doesn't exist, a reference Selector is returned. | ||
* This has the advantage that Components that have the reference Selector bound to themselves | ||
* are rerenderd when the original Selector is created. | ||
* | ||
* [Learn more..](https://agile-ts.org/docs/core/collection/methods/#getselectorwithreference) | ||
* | ||
* @public | ||
* @param selectorKey - Key/Name identifier of the Selector. | ||
*/ | ||
getSelectorWithReference(selectorKey: SelectorKey): Selector<DataType>; | ||
/** | ||
* Removes a Selector with the specified key/name identifier from the Collection, | ||
* if it exists in the Collection. | ||
* | ||
* [Learn more..](https://agile-ts.org/docs/core/collection/methods/#removeselector) | ||
* | ||
* @public | ||
* @param selectorKey - Key/Name identifier of the Selector to be removed. | ||
*/ | ||
removeSelector(selectorKey: SelectorKey): this; | ||
/** | ||
* Returns the count of registered Selectors in the Collection. | ||
* | ||
* [Learn more..](https://agile-ts.org/docs/core/collection/methods/#getselectorcount) | ||
* | ||
* @public | ||
*/ | ||
getSelectorCount(): number; | ||
/** | ||
* Returns a boolean indicating whether a Item with the specified `itemKey` | ||
* exists in the Collection or not. | ||
* | ||
* [Learn more..](https://agile-ts.org/docs/core/collection/methods/#hasitem) | ||
* | ||
* @public | ||
* @param itemKey - Key/Name identifier of the Item. | ||
* @param config - Configuration object | ||
*/ | ||
hasItem(itemKey: ItemKey | undefined, config?: HasConfigInterface): boolean; | ||
/** | ||
* Retrieves a single Item with the specified key/name identifier from the Collection. | ||
* | ||
* If the to retrieve Item doesn't exist, `undefined` is returned. | ||
* | ||
* [Learn more..](https://agile-ts.org/docs/core/collection/methods/#getitem) | ||
* | ||
* @public | ||
* @param itemKey - Key/Name identifier of the Item. | ||
* @param config - Configuration object | ||
*/ | ||
getItem(itemKey: ItemKey | undefined | null, config?: HasConfigInterface): Item<DataType> | undefined; | ||
/** | ||
* Retrieves a single Item with the specified key/name identifier from the Collection. | ||
* | ||
* If the to retrieve Item doesn't exist, a reference Item is returned. | ||
* This has the advantage that Components that have the reference Item bound to themselves | ||
* are rerenderd when the original Item is created. | ||
* | ||
* [Learn more..](https://agile-ts.org/docs/core/collection/methods/#getitemwithreference) | ||
* | ||
* @public | ||
* @param itemKey - Key/Name identifier of the Item. | ||
*/ | ||
getItemWithReference(itemKey: ItemKey): Item<DataType>; | ||
/** | ||
* Creates a placeholder Item | ||
* that can be used to hold a reference to a not existing Item. | ||
* | ||
* @internal | ||
* @param itemKey - Unique identifier of the to create placeholder Item. | ||
* @param addToCollection - Whether to add the Item to be created to the Collection. | ||
*/ | ||
createPlaceholderItem(itemKey: ItemKey, addToCollection?: boolean): Item<DataType>; | ||
/** | ||
* Retrieves the value (data object) of a single Item | ||
* with the specified key/name identifier from the Collection. | ||
* | ||
* If the to retrieve Item containing the value doesn't exist, `undefined` is returned. | ||
* | ||
* [Learn more..](https://agile-ts.org/docs/core/collection/methods/#getitemvalue) | ||
* | ||
* @public | ||
* @param itemKey - Key/Name identifier of the Item. | ||
* @param config - Configuration object | ||
*/ | ||
getItemValue(itemKey: ItemKey | undefined, config?: HasConfigInterface): DataType | undefined; | ||
/** | ||
* Retrieves all Items from the Collection. | ||
* ``` | ||
* MY_COLLECTION.getAllItems(); | ||
* // is equivalent to | ||
* MY_COLLECTION.getDefaultGroup().items; | ||
* ``` | ||
* | ||
* [Learn more..](https://agile-ts.org/docs/core/collection/methods/#getallitems) | ||
* | ||
* @public | ||
* @param config - Configuration object | ||
*/ | ||
getAllItems(config?: HasConfigInterface): Array<Item<DataType>>; | ||
/** | ||
* Retrieves the values (data objects) of all Items from the Collection. | ||
* ``` | ||
* MY_COLLECTION.getAllItemValues(); | ||
* // is equivalent to | ||
* MY_COLLECTION.getDefaultGroup().output; | ||
* ``` | ||
* | ||
* [Learn more..](https://agile-ts.org/docs/core/collection/methods/#getallitemvalues) | ||
* | ||
* @public | ||
* @param config - Configuration object | ||
*/ | ||
getAllItemValues(config?: HasConfigInterface): Array<DataType>; | ||
/** | ||
* Preserves the Collection `value` in the corresponding external Storage. | ||
* | ||
* The Collection key/name is used as the unique identifier for the Persistent. | ||
* If that is not desired or the Collection has no unique identifier, | ||
* please specify a separate unique identifier for the Persistent. | ||
* | ||
* [Learn more..](https://agile-ts.org/docs/core/collection/methods/#persist) | ||
* | ||
* @public | ||
* @param config - Configuration object | ||
*/ | ||
persist(config?: CollectionPersistentConfigInterface): this; | ||
/** | ||
* Preserves the Collection `value` in the corresponding external Storage. | ||
* | ||
* The specified key is used as the unique identifier for the Persistent. | ||
* | ||
* [Learn more..](https://agile-ts.org/docs/core/collection/methods/#persist) | ||
* | ||
* @public | ||
* @param key - Key/Name identifier of Persistent. | ||
* @param config - Configuration object | ||
*/ | ||
persist(key?: StorageKey, config?: CollectionPersistentConfigInterface): this; | ||
/** | ||
* Fires immediately after the persisted `value` | ||
* is loaded into the Collection from a corresponding external Storage. | ||
* | ||
* Registering such callback function makes only sense | ||
* when the Collection is [persisted](https://agile-ts.org/docs/core/collection/methods/#persist) in an external Storage. | ||
* | ||
* [Learn more..](https://agile-ts.org/docs/core/collection/methods/#onload) | ||
* | ||
* @public | ||
* @param callback - A function to be executed after the externally persisted `value` was loaded into the Collection. | ||
*/ | ||
onLoad(callback: (success: boolean) => void): this; | ||
/** | ||
* Removes all Items from the Collection | ||
* and resets all Groups and Selectors of the Collection. | ||
* | ||
* [Learn more..](https://agile-ts.org/docs/core/collection/methods/#reset) | ||
* | ||
* @public | ||
*/ | ||
reset(): this; | ||
/** | ||
* Puts `itemKeys/s` into Group/s. | ||
* | ||
* [Learn more..](https://agile-ts.org/docs/core/collection/methods/#put) | ||
* | ||
* @public | ||
* @param itemKeys - `itemKey/s` to be put into the specified Group/s. | ||
* @param groupKeys - Key/Name Identifier/s of the Group/s the specified `itemKey/s` are to put in. | ||
* @param config - Configuration object | ||
*/ | ||
put(itemKeys: ItemKey | Array<ItemKey>, groupKeys: GroupKey | Array<GroupKey>, config?: GroupAddConfigInterface): this; | ||
/** | ||
* Moves specified `itemKey/s` from one Group to another Group. | ||
* | ||
* [Learn more..](https://agile-ts.org/docs/core/collection/methods/#move) | ||
* | ||
* @public | ||
* @param itemKeys - `itemKey/s` to be moved. | ||
* @param oldGroupKey - Key/Name Identifier of the Group the `itemKey/s` are moved from. | ||
* @param newGroupKey - Key/Name Identifier of the Group the `itemKey/s` are moved in. | ||
* @param config - Configuration object | ||
*/ | ||
move(itemKeys: ItemKey | Array<ItemKey>, oldGroupKey: GroupKey, newGroupKey: GroupKey, config?: GroupAddConfigInterface): this; | ||
/** | ||
* Updates the key/name identifier of the Item | ||
* and returns a boolean indicating | ||
* whether the Item identifier was updated successfully. | ||
* | ||
* @internal | ||
* @param oldItemKey - Old key/name Item identifier. | ||
* @param newItemKey - New key/name Item identifier. | ||
* @param config - Configuration object | ||
*/ | ||
updateItemKey(oldItemKey: ItemKey, newItemKey: ItemKey, config?: UpdateItemKeyConfigInterface): boolean; | ||
/** | ||
* Returns all key/name identifiers of the Group/s containing the specified `itemKey`. | ||
* | ||
* [Learn more..](https://agile-ts.org/docs/core/collection/methods/#getgroupkeysthathaveitemkey) | ||
* | ||
* @public | ||
* @param itemKey - `itemKey` to be contained in Group/s. | ||
*/ | ||
getGroupKeysThatHaveItemKey(itemKey: ItemKey): Array<GroupKey>; | ||
/** | ||
* Removes Item/s from: | ||
* | ||
* - `.everywhere()`: | ||
* Removes Item/s from the entire Collection and all its Groups and Selectors (i.e. from everywhere) | ||
* ``` | ||
* MY_COLLECTION.remove('1').everywhere(); | ||
* // is equivalent to | ||
* MY_COLLECTION.removeItems('1'); | ||
* ``` | ||
* - `.fromGroups()`: | ||
* Removes Item/s only from specified Groups. | ||
* ``` | ||
* MY_COLLECTION.remove('1').fromGroups(['1', '2']); | ||
* // is equivalent to | ||
* MY_COLLECTION.removeFromGroups('1', ['1', '2']); | ||
* ``` | ||
* | ||
* [Learn more..](https://agile-ts.org/docs/core/collection/methods/#remove) | ||
* | ||
* @public | ||
* @param itemKeys - Item/s with identifier/s to be removed. | ||
*/ | ||
remove(itemKeys: ItemKey | Array<ItemKey>): { | ||
fromGroups: (groups: Array<ItemKey> | ItemKey) => Collection<DataType>; | ||
everywhere: (config?: RemoveItemsConfigInterface) => Collection<DataType>; | ||
}; | ||
/** | ||
* Remove Item/s from specified Group/s. | ||
* | ||
* [Learn more..](https://agile-ts.org/docs/core/collection/methods/#removefromgroups) | ||
* | ||
* @public | ||
* @param itemKeys - Key/Name Identifier/s of the Item/s to be removed from the Group/s. | ||
* @param groupKeys - Key/Name Identifier/s of the Group/s the Item/s are to remove from. | ||
*/ | ||
removeFromGroups(itemKeys: ItemKey | Array<ItemKey>, groupKeys: GroupKey | Array<GroupKey>): this; | ||
/** | ||
* Removes Item/s from the entire Collection and all the Collection's Groups and Selectors. | ||
* | ||
* [Learn more..](https://agile-ts.org/docs/core/collection/methods/#removeitems) | ||
* | ||
* @public | ||
* @param itemKeys - Key/Name identifier/s of the Item/s to be removed from the entire Collection. | ||
* @param config - Configuration object | ||
*/ | ||
removeItems(itemKeys: ItemKey | Array<ItemKey>, config?: RemoveItemsConfigInterface): this; | ||
/** | ||
* Assigns the provided `data` object to an already existing Item | ||
* with specified key/name identifier found in the `data` object. | ||
* If the Item doesn't exist yet, a new Item with the `data` object as value | ||
* is created and assigned to the Collection. | ||
* | ||
* Returns a boolean indicating | ||
* whether the `data` object was assigned/updated successfully. | ||
* | ||
* @internal | ||
* @param data - Data object | ||
* @param config - Configuration object | ||
*/ | ||
assignData(data: DataType, config?: AssignDataConfigInterface): boolean; | ||
/** | ||
* Assigns the specified Item to the Collection | ||
* at the key/name identifier of the Item. | ||
* | ||
* And returns a boolean indicating | ||
* whether the Item was assigned successfully. | ||
* | ||
* @internal | ||
* @param item - Item to be added. | ||
* @param config - Configuration object | ||
*/ | ||
assignItem(item: Item<DataType>, config?: AssignItemConfigInterface): boolean; | ||
/** | ||
* Rebuilds all Groups that contain the specified `itemKey`. | ||
* | ||
* @internal | ||
* @itemKey - `itemKey` Groups must contain to be rebuilt. | ||
* @config - Configuration object | ||
*/ | ||
rebuildGroupsThatIncludeItemKey(itemKey: ItemKey, config?: RebuildGroupsThatIncludeItemKeyConfigInterface): void; | ||
} | ||
export declare type DefaultItem = Record<string, any>; | ||
export declare type CollectionKey = string | number; | ||
export declare type ItemKey = string | number; | ||
export interface CreateCollectionConfigInterface<DataType = DefaultItem> { | ||
/** | ||
* Initial Groups of the Collection. | ||
* @default [] | ||
*/ | ||
groups?: { | ||
[key: string]: Group<any>; | ||
} | string[]; | ||
/** | ||
* Initial Selectors of the Collection | ||
* @default [] | ||
*/ | ||
selectors?: { | ||
[key: string]: Selector<any>; | ||
} | string[]; | ||
/** | ||
* Key/Name identifier of the Collection. | ||
* @default undefined | ||
*/ | ||
key?: CollectionKey; | ||
/** | ||
* Key/Name of the property | ||
* which represents the unique Item identifier | ||
* in collected data objects. | ||
* @default 'id' | ||
*/ | ||
primaryKey?: string; | ||
/** | ||
* Key/Name identifier of the default Group that is created shortly after instantiation. | ||
* The default Group represents the default pattern of the Collection. | ||
* @default 'default' | ||
*/ | ||
defaultGroupKey?: GroupKey; | ||
/** | ||
* Initial data objects of the Collection. | ||
* @default [] | ||
*/ | ||
initialData?: Array<DataType>; | ||
} | ||
export declare type CollectionConfig<DataType extends Object = DefaultItem> = CreateCollectionConfigInterface<DataType> | ((collection: Collection<DataType>) => CreateCollectionConfigInterface<DataType>); | ||
export interface CollectionConfigInterface { | ||
/** | ||
* Key/Name of the property | ||
* which represents the unique Item identifier | ||
* in collected data objects. | ||
* @default 'id' | ||
*/ | ||
primaryKey: string; | ||
/** | ||
* Key/Name identifier of the default Group that is created shortly after instantiation. | ||
* The default Group represents the default pattern of the Collection. | ||
* @default 'default' | ||
*/ | ||
defaultGroupKey: ItemKey; | ||
} | ||
export interface CollectConfigInterface<DataType = any> extends AssignDataConfigInterface { | ||
/** | ||
* In which way the collected data should be added to the Collection. | ||
* - 'push' = at the end | ||
* - 'unshift' = at the beginning | ||
* https://www.tutorialspoint.com/what-are-the-differences-between-unshift-and-push-methods-in-javascript | ||
* @default 'push' | ||
*/ | ||
method?: 'push' | 'unshift'; | ||
/** | ||
* Performs the specified action for each collected data object. | ||
* @default undefined | ||
*/ | ||
forEachItem?: (data: DataType | Item<DataType>, key: ItemKey, success: boolean, index: number) => void; | ||
/** | ||
* Whether to create a Selector for each collected data object. | ||
* @default false | ||
*/ | ||
select?: boolean; | ||
} | ||
export interface UpdateConfigInterface { | ||
/** | ||
* Whether to merge the data object with changes into the existing Item data object | ||
* or overwrite the existing Item data object entirely. | ||
* @default true | ||
*/ | ||
patch?: boolean | PatchOptionConfigInterface; | ||
/** | ||
* Whether to update the data object in background. | ||
* So that the UI isn't notified of these changes and thus doesn't rerender. | ||
* @default false | ||
*/ | ||
background?: boolean; | ||
} | ||
export interface UpdateItemKeyConfigInterface { | ||
/** | ||
* Whether to update the Item key/name identifier in background | ||
* So that the UI isn't notified of these changes and thus doesn't rerender. | ||
* @default false | ||
*/ | ||
background?: boolean; | ||
} | ||
export interface RebuildGroupsThatIncludeItemKeyConfigInterface { | ||
/** | ||
* Whether to rebuilt the Group in background. | ||
* So that the UI isn't notified of these changes and thus doesn't rerender. | ||
* @default false | ||
*/ | ||
background?: boolean; | ||
/** | ||
* Whether to execute the defined side effects. | ||
* @default true | ||
*/ | ||
sideEffects?: SideEffectConfigInterface; | ||
} | ||
export interface HasConfigInterface { | ||
/** | ||
* Whether Items that do not officially exist, | ||
* such as placeholder Items, can be found | ||
* @default true | ||
*/ | ||
notExisting?: boolean; | ||
} | ||
export interface CollectionPersistentConfigInterface { | ||
/** | ||
* Whether the Persistent should automatically load | ||
* the persisted value into the Collection after its instantiation. | ||
* @default true | ||
*/ | ||
loadValue?: boolean; | ||
/** | ||
* Key/Name identifier of Storages | ||
* in which the Collection value should be or is persisted. | ||
* @default [`defaultStorageKey`] | ||
*/ | ||
storageKeys?: StorageKey[]; | ||
/** | ||
* Key/Name identifier of the default Storage of the specified Storage keys. | ||
* | ||
* The Collection value is loaded from the default Storage by default | ||
* and is only loaded from the remaining Storages (`storageKeys`) | ||
* if the loading from the default Storage failed. | ||
* | ||
* @default first index of the specified Storage keys or the AgileTs default Storage key | ||
*/ | ||
defaultStorageKey?: StorageKey; | ||
} | ||
export interface RemoveItemsConfigInterface { | ||
/** | ||
* Whether to remove not officially existing Items (such as placeholder Items). | ||
* Keep in mind that sometimes it won't remove an Item entirely | ||
* as another Instance (like a Selector) might need to keep reference to it. | ||
* https://github.com/agile-ts/agile/pull/152 | ||
* @default false | ||
*/ | ||
notExisting?: boolean; | ||
/** | ||
* Whether to remove Selectors that have selected an Item to be removed. | ||
* @default false | ||
*/ | ||
removeSelector?: boolean; | ||
} | ||
export interface AssignDataConfigInterface { | ||
/** | ||
* When the Item identifier of the to assign data object already exists in the Collection, | ||
* whether to merge the newly assigned data into the existing one | ||
* or overwrite the existing one entirely. | ||
* @default true | ||
*/ | ||
patch?: boolean; | ||
/** | ||
* Whether to assign the data object to the Collection in background. | ||
* So that the UI isn't notified of these changes and thus doesn't rerender. | ||
* @default false | ||
*/ | ||
background?: boolean; | ||
} | ||
export interface AssignItemConfigInterface { | ||
/** | ||
* If an Item with the Item identifier already exists, | ||
* whether to overwrite it entirely with the new one. | ||
* @default false | ||
*/ | ||
overwrite?: boolean; | ||
/** | ||
* Whether to assign the Item to the Collection in background. | ||
* So that the UI isn't notified of these changes and thus doesn't rerender. | ||
* @default false | ||
*/ | ||
background?: boolean; | ||
} | ||
import { Collection, CollectionConfig, DefaultItem, Agile } from '../internal'; | ||
export * from './collection'; | ||
/** | ||
* Returns a newly created Collection. | ||
* | ||
* A Collection manages a reactive set of Information | ||
* that we need to remember globally at a later point in time. | ||
* While providing a toolkit to use and mutate this set of Information. | ||
* | ||
* It is designed for arrays of data objects following the same pattern. | ||
* | ||
* Each of these data object must have a unique `primaryKey` to be correctly identified later. | ||
* | ||
* You can create as many global Collections as you need. | ||
* | ||
* [Learn more..](https://agile-ts.org/docs/core/agile-instance/methods#createcollection) | ||
* | ||
* @public | ||
* @param config - Configuration object | ||
* @param agileInstance - Instance of Agile the Collection belongs to. | ||
*/ | ||
export declare function createCollection<DataType extends Object = DefaultItem>(config?: CollectionConfig<DataType>, agileInstance?: Agile): Collection<DataType>; |
"use strict"; | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); | ||
}) : (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
o[k2] = m[k]; | ||
})); | ||
var __exportStar = (this && this.__exportStar) || function(m, exports) { | ||
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p); | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.Collection = void 0; | ||
exports.createCollection = void 0; | ||
const internal_1 = require("../internal"); | ||
class Collection { | ||
constructor(agileInstance, config = {}) { | ||
this.size = 0; | ||
this.data = {}; | ||
this.isPersisted = false; | ||
this.groups = {}; | ||
this.selectors = {}; | ||
this.isInstantiated = false; | ||
this.isCollection = true; | ||
this.agileInstance = () => agileInstance; | ||
let _config = typeof config === 'function' ? config(this) : config; | ||
_config = internal_1.defineConfig(_config, { | ||
primaryKey: 'id', | ||
groups: {}, | ||
selectors: {}, | ||
defaultGroupKey: 'default', | ||
}); | ||
this._key = _config.key; | ||
this.config = { | ||
defaultGroupKey: _config.defaultGroupKey, | ||
primaryKey: _config.primaryKey, | ||
}; | ||
this.initialConfig = _config; | ||
this.initGroups(_config.groups); | ||
this.initSelectors(_config.selectors); | ||
this.isInstantiated = true; | ||
if (_config.initialData) | ||
this.collect(_config.initialData); | ||
for (const key in this.selectors) | ||
this.selectors[key].reselect(); | ||
} | ||
set key(value) { | ||
this.setKey(value); | ||
} | ||
get key() { | ||
return this._key; | ||
} | ||
setKey(value) { | ||
var _a, _b; | ||
const oldKey = this._key; | ||
this._key = value; | ||
if (value != null && ((_a = this.persistent) === null || _a === void 0 ? void 0 : _a._key) === oldKey) | ||
(_b = this.persistent) === null || _b === void 0 ? void 0 : _b.setKey(value); | ||
return this; | ||
} | ||
Group(initialItems, config = {}) { | ||
var _a; | ||
if (this.isInstantiated) { | ||
const key = (_a = config.key) !== null && _a !== void 0 ? _a : internal_1.generateId(); | ||
internal_1.LogCodeManager.log('1B:02:00'); | ||
return this.createGroup(key, initialItems); | ||
} | ||
return new internal_1.Group(this, initialItems, config); | ||
} | ||
Selector(initialKey, config = {}) { | ||
var _a; | ||
if (this.isInstantiated) { | ||
const key = (_a = config.key) !== null && _a !== void 0 ? _a : internal_1.generateId(); | ||
internal_1.LogCodeManager.log('1B:02:01'); | ||
return this.createSelector(key, initialKey); | ||
} | ||
return new internal_1.Selector(this, initialKey, config); | ||
} | ||
initGroups(groups) { | ||
if (!groups) | ||
return; | ||
let groupsObject = {}; | ||
if (Array.isArray(groups)) { | ||
groups.forEach((groupKey) => { | ||
groupsObject[groupKey] = new internal_1.Group(this, [], { | ||
key: groupKey, | ||
}); | ||
}); | ||
} | ||
else | ||
groupsObject = groups; | ||
groupsObject[this.config.defaultGroupKey] = new internal_1.Group(this, [], { | ||
key: this.config.defaultGroupKey, | ||
}); | ||
for (const key in groupsObject) | ||
if (groupsObject[key]._key == null) | ||
groupsObject[key].setKey(key); | ||
this.groups = groupsObject; | ||
} | ||
initSelectors(selectors) { | ||
if (!selectors) | ||
return; | ||
let selectorsObject = {}; | ||
if (Array.isArray(selectors)) { | ||
selectors.forEach((selectorKey) => { | ||
selectorsObject[selectorKey] = new internal_1.Selector(this, selectorKey, { | ||
key: selectorKey, | ||
}); | ||
}); | ||
} | ||
else | ||
selectorsObject = selectors; | ||
for (const key in selectorsObject) | ||
if (selectorsObject[key]._key == null) | ||
selectorsObject[key].setKey(key); | ||
this.selectors = selectorsObject; | ||
} | ||
collect(data, groupKeys, config = {}) { | ||
const _data = internal_1.normalizeArray(data); | ||
const _groupKeys = internal_1.normalizeArray(groupKeys); | ||
const defaultGroupKey = this.config.defaultGroupKey; | ||
const primaryKey = this.config.primaryKey; | ||
config = internal_1.defineConfig(config, { | ||
method: 'push', | ||
background: false, | ||
patch: false, | ||
select: false, | ||
}); | ||
if (!_groupKeys.includes(defaultGroupKey)) | ||
_groupKeys.push(defaultGroupKey); | ||
_groupKeys.forEach((key) => this.groups[key] == null && this.createGroup(key)); | ||
_data.forEach((data, index) => { | ||
let itemKey; | ||
let success = false; | ||
if (data instanceof internal_1.Item) { | ||
success = this.assignItem(data, { | ||
background: config.background, | ||
}); | ||
itemKey = data._key; | ||
} | ||
else { | ||
success = this.assignData(data, { | ||
patch: config.patch, | ||
background: config.background, | ||
}); | ||
itemKey = data[primaryKey]; | ||
} | ||
if (success) { | ||
_groupKeys.forEach((groupKey) => { | ||
var _a; | ||
(_a = this.getGroup(groupKey)) === null || _a === void 0 ? void 0 : _a.add(itemKey, { | ||
method: config.method, | ||
background: config.background, | ||
}); | ||
}); | ||
if (config.select) | ||
this.createSelector(itemKey, itemKey); | ||
} | ||
if (config.forEachItem) | ||
config.forEachItem(data, itemKey, success, index); | ||
}); | ||
return this; | ||
} | ||
update(itemKey, changes, config = {}) { | ||
const item = this.getItem(itemKey, { notExisting: true }); | ||
const primaryKey = this.config.primaryKey; | ||
config = internal_1.defineConfig(config, { | ||
patch: true, | ||
background: false, | ||
}); | ||
if (item == null) { | ||
internal_1.LogCodeManager.log('1B:03:00', [itemKey, this._key]); | ||
return undefined; | ||
} | ||
if (!internal_1.isValidObject(changes)) { | ||
internal_1.LogCodeManager.log('1B:03:01', [itemKey, this._key]); | ||
return undefined; | ||
} | ||
const oldItemKey = item._value[primaryKey]; | ||
const newItemKey = changes[primaryKey] || oldItemKey; | ||
if (oldItemKey !== newItemKey) | ||
this.updateItemKey(oldItemKey, newItemKey, { | ||
background: config.background, | ||
}); | ||
if (config.patch) { | ||
if (changes[primaryKey]) | ||
delete changes[primaryKey]; | ||
let patchConfig = typeof config.patch === 'object' ? config.patch : {}; | ||
patchConfig = Object.assign({ addNewProperties: true }, patchConfig); | ||
item.patch(changes, { | ||
background: config.background, | ||
addNewProperties: patchConfig.addNewProperties, | ||
}); | ||
} | ||
else { | ||
if (changes[this.config.primaryKey] !== itemKey) { | ||
changes[this.config.primaryKey] = itemKey; | ||
internal_1.LogCodeManager.log('1B:02:02', [], changes); | ||
} | ||
item.set(changes, { | ||
background: config.background, | ||
}); | ||
} | ||
return item; | ||
} | ||
createGroup(groupKey, initialItems = []) { | ||
let group = this.getGroup(groupKey, { notExisting: true }); | ||
if (!this.isInstantiated) | ||
internal_1.LogCodeManager.log('1B:02:03'); | ||
if (group != null) { | ||
if (!group.isPlaceholder) { | ||
internal_1.LogCodeManager.log('1B:03:02', [groupKey]); | ||
return group; | ||
} | ||
group.set(initialItems, { overwrite: true }); | ||
return group; | ||
} | ||
group = new internal_1.Group(this, initialItems, { key: groupKey }); | ||
this.groups[groupKey] = group; | ||
return group; | ||
} | ||
hasGroup(groupKey, config = {}) { | ||
return !!this.getGroup(groupKey, config); | ||
} | ||
getGroup(groupKey, config = {}) { | ||
config = internal_1.defineConfig(config, { | ||
notExisting: false, | ||
}); | ||
const group = groupKey ? this.groups[groupKey] : undefined; | ||
if (group == null || (!config.notExisting && !group.exists)) | ||
return undefined; | ||
internal_1.ComputedTracker.tracked(group.observers['value']); | ||
return group; | ||
} | ||
getDefaultGroup() { | ||
return this.getGroup(this.config.defaultGroupKey); | ||
} | ||
getGroupWithReference(groupKey) { | ||
let group = this.getGroup(groupKey, { notExisting: true }); | ||
if (group == null) { | ||
group = new internal_1.Group(this, [], { | ||
key: groupKey, | ||
isPlaceholder: true, | ||
}); | ||
this.groups[groupKey] = group; | ||
} | ||
internal_1.ComputedTracker.tracked(group.observers['value']); | ||
return group; | ||
} | ||
removeGroup(groupKey) { | ||
if (this.groups[groupKey] != null) | ||
delete this.groups[groupKey]; | ||
return this; | ||
} | ||
getGroupCount() { | ||
let size = 0; | ||
Object.keys(this.groups).map(() => size++); | ||
return size; | ||
} | ||
createSelector(selectorKey, itemKey) { | ||
let selector = this.getSelector(selectorKey, { notExisting: true }); | ||
if (!this.isInstantiated) | ||
internal_1.LogCodeManager.log('1B:02:04'); | ||
if (selector != null) { | ||
if (!selector.isPlaceholder) { | ||
internal_1.LogCodeManager.log('1B:03:03', [selectorKey]); | ||
return selector; | ||
} | ||
selector.select(itemKey, { overwrite: true }); | ||
return selector; | ||
} | ||
selector = new internal_1.Selector(this, itemKey, { | ||
key: selectorKey, | ||
}); | ||
this.selectors[selectorKey] = selector; | ||
return selector; | ||
} | ||
select(itemKey) { | ||
return this.createSelector(itemKey, itemKey); | ||
} | ||
hasSelector(selectorKey, config = {}) { | ||
return !!this.getSelector(selectorKey, config); | ||
} | ||
getSelector(selectorKey, config = {}) { | ||
config = internal_1.defineConfig(config, { | ||
notExisting: false, | ||
}); | ||
const selector = selectorKey ? this.selectors[selectorKey] : undefined; | ||
if (selector == null || (!config.notExisting && !selector.exists)) | ||
return undefined; | ||
internal_1.ComputedTracker.tracked(selector.observers['value']); | ||
return selector; | ||
} | ||
getSelectorWithReference(selectorKey) { | ||
let selector = this.getSelector(selectorKey, { notExisting: true }); | ||
if (selector == null) { | ||
selector = new internal_1.Selector(this, null, { | ||
key: selectorKey, | ||
isPlaceholder: true, | ||
}); | ||
this.selectors[selectorKey] = selector; | ||
} | ||
internal_1.ComputedTracker.tracked(selector.observers['value']); | ||
return selector; | ||
} | ||
removeSelector(selectorKey) { | ||
if (this.selectors[selectorKey] != null) { | ||
this.selectors[selectorKey].unselect(); | ||
delete this.selectors[selectorKey]; | ||
} | ||
return this; | ||
} | ||
getSelectorCount() { | ||
let size = 0; | ||
Object.keys(this.selectors).map(() => size++); | ||
return size; | ||
} | ||
hasItem(itemKey, config = {}) { | ||
return !!this.getItem(itemKey, config); | ||
} | ||
getItem(itemKey, config = {}) { | ||
config = internal_1.defineConfig(config, { | ||
notExisting: false, | ||
}); | ||
const item = itemKey != null ? this.data[itemKey] : undefined; | ||
if (item == null || (!config.notExisting && !item.exists)) | ||
return undefined; | ||
internal_1.ComputedTracker.tracked(item.observers['value']); | ||
return item; | ||
} | ||
getItemWithReference(itemKey) { | ||
let item = this.getItem(itemKey, { notExisting: true }); | ||
if (item == null) | ||
item = this.createPlaceholderItem(itemKey, true); | ||
internal_1.ComputedTracker.tracked(item.observers['value']); | ||
return item; | ||
} | ||
createPlaceholderItem(itemKey, addToCollection = false) { | ||
const item = new internal_1.Item(this, { | ||
[this.config.primaryKey]: itemKey, | ||
dummy: 'item', | ||
}, { isPlaceholder: true }); | ||
if (addToCollection && | ||
!Object.prototype.hasOwnProperty.call(this.data, itemKey)) | ||
this.data[itemKey] = item; | ||
internal_1.ComputedTracker.tracked(item.observers['value']); | ||
return item; | ||
} | ||
getItemValue(itemKey, config = {}) { | ||
const item = this.getItem(itemKey, config); | ||
if (item == null) | ||
return undefined; | ||
return item.value; | ||
} | ||
getAllItems(config = {}) { | ||
config = internal_1.defineConfig(config, { | ||
notExisting: false, | ||
}); | ||
const defaultGroup = this.getDefaultGroup(); | ||
let items = []; | ||
if (config.notExisting) { | ||
for (const key in this.data) | ||
items.push(this.data[key]); | ||
} | ||
else { | ||
items = (defaultGroup === null || defaultGroup === void 0 ? void 0 : defaultGroup.getItems()) || []; | ||
} | ||
return items; | ||
} | ||
getAllItemValues(config = {}) { | ||
const items = this.getAllItems(config); | ||
return items.map((item) => item.value); | ||
} | ||
persist(keyOrConfig = {}, config = {}) { | ||
let _config; | ||
let key; | ||
if (internal_1.isValidObject(keyOrConfig)) { | ||
_config = keyOrConfig; | ||
key = this._key; | ||
} | ||
else { | ||
_config = config || {}; | ||
key = keyOrConfig; | ||
} | ||
_config = internal_1.defineConfig(_config, { | ||
loadValue: true, | ||
storageKeys: [], | ||
defaultStorageKey: null, | ||
}); | ||
if (this.persistent != null && this.isPersisted) | ||
return this; | ||
this.persistent = new internal_1.CollectionPersistent(this, { | ||
instantiate: _config.loadValue, | ||
storageKeys: _config.storageKeys, | ||
key: key, | ||
defaultStorageKey: _config.defaultStorageKey, | ||
}); | ||
return this; | ||
} | ||
onLoad(callback) { | ||
if (!this.persistent) | ||
return this; | ||
if (!internal_1.isFunction(callback)) { | ||
internal_1.LogCodeManager.log('00:03:01', ['OnLoad Callback', 'function']); | ||
return this; | ||
} | ||
this.persistent.onLoad = callback; | ||
if (this.isPersisted) | ||
callback(true); | ||
return this; | ||
} | ||
reset() { | ||
var _a, _b; | ||
this.data = {}; | ||
this.size = 0; | ||
for (const key in this.groups) | ||
(_a = this.getGroup(key)) === null || _a === void 0 ? void 0 : _a.reset(); | ||
for (const key in this.selectors) | ||
(_b = this.getSelector(key)) === null || _b === void 0 ? void 0 : _b.reset(); | ||
return this; | ||
} | ||
put(itemKeys, groupKeys, config = {}) { | ||
const _itemKeys = internal_1.normalizeArray(itemKeys); | ||
const _groupKeys = internal_1.normalizeArray(groupKeys); | ||
_groupKeys.forEach((groupKey) => { | ||
var _a; | ||
(_a = this.getGroup(groupKey)) === null || _a === void 0 ? void 0 : _a.add(_itemKeys, config); | ||
}); | ||
return this; | ||
} | ||
move(itemKeys, oldGroupKey, newGroupKey, config = {}) { | ||
var _a, _b; | ||
const _itemKeys = internal_1.normalizeArray(itemKeys); | ||
(_a = this.getGroup(oldGroupKey)) === null || _a === void 0 ? void 0 : _a.remove(_itemKeys, internal_1.removeProperties(config, ['method', 'overwrite'])); | ||
(_b = this.getGroup(newGroupKey)) === null || _b === void 0 ? void 0 : _b.add(_itemKeys, config); | ||
return this; | ||
} | ||
updateItemKey(oldItemKey, newItemKey, config = {}) { | ||
var _a; | ||
const item = this.getItem(oldItemKey, { notExisting: true }); | ||
config = internal_1.defineConfig(config, { | ||
background: false, | ||
}); | ||
if (item == null || oldItemKey === newItemKey) | ||
return false; | ||
if (this.hasItem(newItemKey)) { | ||
internal_1.LogCodeManager.log('1B:03:04', [oldItemKey, newItemKey, this._key]); | ||
return false; | ||
} | ||
delete this.data[oldItemKey]; | ||
this.data[newItemKey] = item; | ||
item.setKey(newItemKey, { | ||
background: config.background, | ||
}); | ||
if (item.persistent != null && | ||
item.persistent._key === | ||
internal_1.CollectionPersistent.getItemStorageKey(oldItemKey, this._key)) | ||
(_a = item.persistent) === null || _a === void 0 ? void 0 : _a.setKey(internal_1.CollectionPersistent.getItemStorageKey(newItemKey, this._key)); | ||
for (const groupKey in this.groups) { | ||
const group = this.getGroup(groupKey, { notExisting: true }); | ||
if (group == null || !group.has(oldItemKey)) | ||
continue; | ||
group.replace(oldItemKey, newItemKey, { background: config.background }); | ||
} | ||
for (const selectorKey in this.selectors) { | ||
const selector = this.getSelector(selectorKey, { notExisting: true }); | ||
if (selector == null) | ||
continue; | ||
if (selector.hasSelected(newItemKey, false)) { | ||
selector.reselect({ | ||
force: true, | ||
background: config.background, | ||
}); | ||
} | ||
if (selector.hasSelected(oldItemKey, false)) | ||
selector.select(newItemKey, { | ||
background: config.background, | ||
}); | ||
} | ||
return true; | ||
} | ||
getGroupKeysThatHaveItemKey(itemKey) { | ||
const groupKeys = []; | ||
for (const groupKey in this.groups) { | ||
const group = this.groups[groupKey]; | ||
if (group === null || group === void 0 ? void 0 : group.has(itemKey)) | ||
groupKeys.push(groupKey); | ||
} | ||
return groupKeys; | ||
} | ||
remove(itemKeys) { | ||
return { | ||
fromGroups: (groups) => this.removeFromGroups(itemKeys, groups), | ||
everywhere: (config) => this.removeItems(itemKeys, config || {}), | ||
}; | ||
} | ||
removeFromGroups(itemKeys, groupKeys) { | ||
const _itemKeys = internal_1.normalizeArray(itemKeys); | ||
const _groupKeys = internal_1.normalizeArray(groupKeys); | ||
_itemKeys.forEach((itemKey) => { | ||
let removedFromGroupsCount = 0; | ||
_groupKeys.forEach((groupKey) => { | ||
const group = this.getGroup(groupKey, { notExisting: true }); | ||
if (!(group === null || group === void 0 ? void 0 : group.has(itemKey))) | ||
return; | ||
group.remove(itemKey); | ||
removedFromGroupsCount++; | ||
}); | ||
if (removedFromGroupsCount >= | ||
this.getGroupKeysThatHaveItemKey(itemKey).length) | ||
this.removeItems(itemKey); | ||
}); | ||
return this; | ||
} | ||
removeItems(itemKeys, config = {}) { | ||
config = internal_1.defineConfig(config, { | ||
notExisting: false, | ||
removeSelector: false, | ||
}); | ||
const _itemKeys = internal_1.normalizeArray(itemKeys); | ||
_itemKeys.forEach((itemKey) => { | ||
var _a, _b; | ||
const item = this.getItem(itemKey, { notExisting: config.notExisting }); | ||
if (item == null) | ||
return; | ||
const wasPlaceholder = item.isPlaceholder; | ||
for (const groupKey in this.groups) { | ||
const group = this.getGroup(groupKey, { notExisting: true }); | ||
if (group === null || group === void 0 ? void 0 : group.has(itemKey)) | ||
group === null || group === void 0 ? void 0 : group.remove(itemKey); | ||
} | ||
(_a = item.persistent) === null || _a === void 0 ? void 0 : _a.removePersistedValue(); | ||
delete this.data[itemKey]; | ||
for (const selectorKey in this.selectors) { | ||
const selector = this.getSelector(selectorKey, { notExisting: true }); | ||
if (selector != null && selector.hasSelected(itemKey, false)) { | ||
if (config.removeSelector) { | ||
this.removeSelector((_b = selector._key) !== null && _b !== void 0 ? _b : 'unknown'); | ||
} | ||
else { | ||
selector.reselect({ force: true }); | ||
} | ||
} | ||
} | ||
if (!wasPlaceholder) | ||
this.size--; | ||
}); | ||
return this; | ||
} | ||
assignData(data, config = {}) { | ||
config = internal_1.defineConfig(config, { | ||
patch: false, | ||
background: false, | ||
}); | ||
const _data = internal_1.copy(data); | ||
const primaryKey = this.config.primaryKey; | ||
if (!internal_1.isValidObject(_data)) { | ||
internal_1.LogCodeManager.log('1B:03:05', [this._key]); | ||
return false; | ||
} | ||
if (!Object.prototype.hasOwnProperty.call(_data, primaryKey)) { | ||
internal_1.LogCodeManager.log('1B:02:05', [this._key, primaryKey]); | ||
_data[primaryKey] = internal_1.generateId(); | ||
} | ||
const itemKey = _data[primaryKey]; | ||
const item = this.getItem(itemKey, { notExisting: true }); | ||
const wasPlaceholder = (item === null || item === void 0 ? void 0 : item.isPlaceholder) || false; | ||
if (item != null) { | ||
if (config.patch) { | ||
item.patch(_data, { background: config.background }); | ||
} | ||
else { | ||
item.set(_data, { background: config.background }); | ||
} | ||
} | ||
else { | ||
this.assignItem(new internal_1.Item(this, _data), { | ||
background: config.background, | ||
}); | ||
} | ||
if (wasPlaceholder) | ||
this.size++; | ||
return true; | ||
} | ||
assignItem(item, config = {}) { | ||
config = internal_1.defineConfig(config, { | ||
overwrite: false, | ||
background: false, | ||
}); | ||
const primaryKey = this.config.primaryKey; | ||
let itemKey = item._value[primaryKey]; | ||
let increaseCollectionSize = true; | ||
if (!Object.prototype.hasOwnProperty.call(item._value, primaryKey)) { | ||
internal_1.LogCodeManager.log('1B:02:05', [this._key, primaryKey]); | ||
itemKey = internal_1.generateId(); | ||
item.patch({ [this.config.primaryKey]: itemKey }, { background: config.background }); | ||
item._key = itemKey; | ||
} | ||
if (item.collection() !== this) { | ||
internal_1.LogCodeManager.log('1B:03:06', [this._key, item.collection()._key]); | ||
return false; | ||
} | ||
if (this.getItem(itemKey, { notExisting: true }) != null) { | ||
if (!config.overwrite) { | ||
this.assignData(item._value); | ||
return true; | ||
} | ||
else | ||
increaseCollectionSize = false; | ||
} | ||
this.data[itemKey] = item; | ||
this.rebuildGroupsThatIncludeItemKey(itemKey, { | ||
background: config.background, | ||
}); | ||
if (increaseCollectionSize) | ||
this.size++; | ||
return true; | ||
} | ||
rebuildGroupsThatIncludeItemKey(itemKey, config = {}) { | ||
config = internal_1.defineConfig(config, { | ||
background: false, | ||
sideEffects: { | ||
enabled: true, | ||
exclude: [], | ||
}, | ||
}); | ||
for (const groupKey in this.groups) { | ||
const group = this.getGroup(groupKey); | ||
if (group === null || group === void 0 ? void 0 : group.has(itemKey)) { | ||
group === null || group === void 0 ? void 0 : group.rebuild({ | ||
background: config === null || config === void 0 ? void 0 : config.background, | ||
sideEffects: config === null || config === void 0 ? void 0 : config.sideEffects, | ||
storage: false, | ||
}); | ||
} | ||
} | ||
} | ||
__exportStar(require("./collection"), exports); | ||
function createCollection(config, agileInstance = internal_1.shared) { | ||
return new internal_1.Collection(agileInstance, config); | ||
} | ||
exports.Collection = Collection; | ||
exports.createCollection = createCollection; |
@@ -1,109 +0,42 @@ | ||
import { State, Agile, Observer, StateConfigInterface, Collection, StateIngestConfigInterface } from '../internal'; | ||
export declare class Computed<ComputedValueType = any> extends State<ComputedValueType> { | ||
config: ComputedConfigInterface; | ||
computeFunction: ComputeFunctionType<ComputedValueType>; | ||
deps: Set<Observer>; | ||
hardCodedDeps: Array<Observer>; | ||
isComputed: boolean; | ||
/** | ||
* A Computed is an extension of the State Class | ||
* that computes its value based on a specified compute function. | ||
* | ||
* The computed value will be cached to avoid unnecessary recomputes | ||
* and is only recomputed when one of its direct dependencies changes. | ||
* | ||
* Direct dependencies can be States and Collections. | ||
* So when, for example, a dependent State value changes, the computed value is recomputed. | ||
* | ||
* [Learn more..](https://agile-ts.org/docs/core/computed/) | ||
* | ||
* @public | ||
* @param agileInstance - Instance of Agile the Computed belongs to. | ||
* @param computeFunction - Function to compute the computed value. | ||
* @param config - Configuration object | ||
*/ | ||
constructor(agileInstance: Agile, computeFunction: ComputeFunctionType<ComputedValueType>, config?: CreateComputedConfigInterface); | ||
/** | ||
* Forces a recomputation of the cached value with the compute function. | ||
* | ||
* [Learn more..](https://agile-ts.org/docs/core/computed/methods/#recompute) | ||
* | ||
* @public | ||
* @param config - Configuration object | ||
*/ | ||
recompute(config?: RecomputeConfigInterface): this; | ||
/** | ||
* Assigns a new function to the Computed Class for computing its value. | ||
* | ||
* The dependencies of the new compute function are automatically detected | ||
* and accordingly updated. | ||
* | ||
* An initial computation is performed with the new function | ||
* to change the obsolete cached value. | ||
* | ||
* [Learn more..](https://agile-ts.org/docs/core/computed/methods/#updatecomputefunction) | ||
* | ||
* @public | ||
* @param computeFunction - New function to compute the value of the Computed Class. | ||
* @param deps - Hard coded dependencies on which the Computed Class depends. | ||
* @param config - Configuration object | ||
*/ | ||
updateComputeFunction(computeFunction: () => ComputedValueType, deps?: Array<DependableAgileInstancesType>, config?: RecomputeConfigInterface): this; | ||
/** | ||
* Computes and returns the new value of the Computed Class | ||
* and autodetects used dependencies in the compute function. | ||
* | ||
* @internal | ||
* @param config - Configuration object | ||
*/ | ||
compute(config?: ComputeConfigInterface): Promise<ComputedValueType>; | ||
/** | ||
* Not usable in Computed Class. | ||
*/ | ||
persist(): this; | ||
import { Computed, ComputeFunctionType, CreateComputedConfigInterface, DependableAgileInstancesType, CreateAgileSubInstanceInterface } from '../internal'; | ||
export * from './computed'; | ||
export interface CreateComputedConfigInterfaceWithAgile extends CreateAgileSubInstanceInterface, CreateComputedConfigInterface { | ||
} | ||
export declare type ComputeFunctionType<ComputedValueType = any> = () => ComputedValueType | Promise<ComputedValueType>; | ||
export interface CreateComputedConfigInterface extends StateConfigInterface { | ||
/** | ||
* Hard-coded dependencies the Computed Class should depend on. | ||
* @default [] | ||
*/ | ||
computedDeps?: Array<DependableAgileInstancesType>; | ||
/** | ||
* Whether the Computed should automatically detect | ||
* used dependencies in the specified compute method. | ||
* | ||
* Note that the automatic dependency detection does not work | ||
* in an asynchronous compute method! | ||
* | ||
* @default true if the compute method isn't asynchronous, otherwise false | ||
*/ | ||
autodetect?: boolean; | ||
} | ||
export interface ComputedConfigInterface { | ||
/** | ||
* Whether the Computed can automatically detect | ||
* used dependencies in the compute method. | ||
* | ||
* Note that the automatic dependency detection does not work | ||
* in an asynchronous compute method! | ||
* | ||
* @default true if the compute method isn't asynchronous, otherwise false | ||
*/ | ||
autodetect: boolean; | ||
} | ||
export interface ComputeConfigInterface { | ||
/** | ||
* Whether the Computed can automatically detect | ||
* used dependencies in the compute method. | ||
* | ||
* Note that the automatic dependency detection does not work | ||
* in an asynchronous compute method! | ||
* | ||
* @default true | ||
*/ | ||
autodetect?: boolean; | ||
} | ||
export interface RecomputeConfigInterface extends StateIngestConfigInterface, ComputeConfigInterface { | ||
} | ||
export declare type DependableAgileInstancesType = State | Collection<any> | Observer; | ||
/** | ||
* Returns a newly created Computed. | ||
* | ||
* A Computed is an extension of the State Class | ||
* that computes its value based on a specified compute function. | ||
* | ||
* The computed value will be cached to avoid unnecessary recomputes | ||
* and is only recomputed when one of its direct dependencies changes. | ||
* | ||
* Direct dependencies can be States and Collections. | ||
* So when, for example, a dependent State value changes, the computed value is recomputed. | ||
* | ||
* [Learn more..](https://agile-ts.org/docs/core/agile-instance/methods#createstate) | ||
* | ||
* @public | ||
* @param computeFunction - Function to compute the computed value. | ||
* @param config - Configuration object | ||
*/ | ||
export declare function createComputed<ComputedValueType = any>(computeFunction: ComputeFunctionType<ComputedValueType>, config?: CreateComputedConfigInterfaceWithAgile): Computed<ComputedValueType>; | ||
/** | ||
* Returns a newly created Computed. | ||
* | ||
* A Computed is an extension of the State Class | ||
* that computes its value based on a specified compute function. | ||
* | ||
* The computed value will be cached to avoid unnecessary recomputes | ||
* and is only recomputed when one of its direct dependencies changes. | ||
* | ||
* Direct dependencies can be States and Collections. | ||
* So when, for example, a dependent State value changes, the computed value is recomputed. | ||
* | ||
* [Learn more..](https://agile-ts.org/docs/core/agile-instance/methods#createcomputed) | ||
* | ||
* @public | ||
* @param computeFunction - Function to compute the computed value. | ||
* @param deps - Hard-coded dependencies on which the Computed Class should depend. | ||
*/ | ||
export declare function createComputed<ComputedValueType = any>(computeFunction: ComputeFunctionType<ComputedValueType>, deps?: Array<DependableAgileInstancesType>): Computed<ComputedValueType>; |
"use strict"; | ||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { | ||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } | ||
return new (P || (P = Promise))(function (resolve, reject) { | ||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } | ||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } | ||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } | ||
step((generator = generator.apply(thisArg, _arguments || [])).next()); | ||
}); | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); | ||
}) : (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
o[k2] = m[k]; | ||
})); | ||
var __exportStar = (this && this.__exportStar) || function(m, exports) { | ||
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p); | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.Computed = void 0; | ||
exports.createComputed = void 0; | ||
const internal_1 = require("../internal"); | ||
class Computed extends internal_1.State { | ||
constructor(agileInstance, computeFunction, config = {}) { | ||
super(agileInstance, null, { | ||
key: config.key, | ||
dependents: config.dependents, | ||
__exportStar(require("./computed"), exports); | ||
function createComputed(computeFunction, configOrDeps) { | ||
let _config = {}; | ||
if (Array.isArray(configOrDeps)) { | ||
_config = internal_1.defineConfig(_config, { | ||
computedDeps: configOrDeps, | ||
}); | ||
this.deps = new Set(); | ||
this.hardCodedDeps = []; | ||
this.isComputed = true; | ||
config = internal_1.defineConfig(config, { | ||
computedDeps: [], | ||
autodetect: !internal_1.isAsyncFunction(computeFunction), | ||
}); | ||
this.agileInstance = () => agileInstance; | ||
this.computeFunction = computeFunction; | ||
this.config = { | ||
autodetect: config.autodetect, | ||
}; | ||
this.hardCodedDeps = internal_1.extractRelevantObservers(config.computedDeps).filter((dep) => dep !== undefined); | ||
this.deps = new Set(this.hardCodedDeps); | ||
this.deps.forEach((observer) => { | ||
observer.addDependent(this.observers['value']); | ||
}); | ||
this.recompute({ autodetect: config.autodetect, overwrite: true }); | ||
} | ||
recompute(config = {}) { | ||
config = internal_1.defineConfig(config, { | ||
autodetect: false, | ||
}); | ||
this.compute({ autodetect: config.autodetect }).then((result) => { | ||
this.observers['value'].ingestValue(result, internal_1.removeProperties(config, ['autodetect'])); | ||
}); | ||
return this; | ||
else { | ||
if (configOrDeps) | ||
_config = configOrDeps; | ||
} | ||
updateComputeFunction(computeFunction, deps = [], config = {}) { | ||
config = internal_1.defineConfig(config, { | ||
autodetect: this.config.autodetect, | ||
}); | ||
this.deps.forEach((observer) => { | ||
observer.removeDependent(this.observers['value']); | ||
}); | ||
this.hardCodedDeps = internal_1.extractRelevantObservers(deps).filter((dep) => dep !== undefined); | ||
this.deps = new Set(this.hardCodedDeps); | ||
this.deps.forEach((observer) => { | ||
observer.addDependent(this.observers['value']); | ||
}); | ||
this.computeFunction = computeFunction; | ||
this.recompute(internal_1.removeProperties(config, ['overwriteDeps'])); | ||
return this; | ||
} | ||
compute(config = {}) { | ||
return __awaiter(this, void 0, void 0, function* () { | ||
config = internal_1.defineConfig(config, { | ||
autodetect: this.config.autodetect, | ||
}); | ||
if (config.autodetect) | ||
internal_1.ComputedTracker.track(); | ||
const computedValue = this.computeFunction(); | ||
if (config.autodetect) { | ||
const foundDeps = internal_1.ComputedTracker.getTrackedObservers(); | ||
this.deps.forEach((observer) => { | ||
if (!foundDeps.includes(observer) && | ||
!this.hardCodedDeps.includes(observer)) { | ||
this.deps.delete(observer); | ||
observer.removeDependent(this.observers['value']); | ||
} | ||
}); | ||
foundDeps.forEach((observer) => { | ||
if (!this.deps.has(observer)) { | ||
this.deps.add(observer); | ||
observer.addDependent(this.observers['value']); | ||
} | ||
}); | ||
} | ||
return computedValue; | ||
}); | ||
} | ||
persist() { | ||
internal_1.LogCodeManager.log('19:03:00'); | ||
return this; | ||
} | ||
_config = internal_1.defineConfig(_config, { agileInstance: internal_1.shared }); | ||
return new internal_1.Computed(_config.agileInstance, computeFunction, internal_1.removeProperties(_config, ['agileInstance'])); | ||
} | ||
exports.Computed = Computed; | ||
exports.createComputed = createComputed; |
@@ -1,624 +0,5 @@ | ||
import { Item, Group, Selector, isValidObject, normalizeArray, copy, CollectionPersistent, ComputedTracker, generateId, removeProperties, isFunction, LogCodeManager, defineConfig, } from '../internal'; | ||
export class Collection { | ||
constructor(agileInstance, config = {}) { | ||
this.size = 0; | ||
this.data = {}; | ||
this.isPersisted = false; | ||
this.groups = {}; | ||
this.selectors = {}; | ||
this.isInstantiated = false; | ||
this.isCollection = true; | ||
this.agileInstance = () => agileInstance; | ||
let _config = typeof config === 'function' ? config(this) : config; | ||
_config = defineConfig(_config, { | ||
primaryKey: 'id', | ||
groups: {}, | ||
selectors: {}, | ||
defaultGroupKey: 'default', | ||
}); | ||
this._key = _config.key; | ||
this.config = { | ||
defaultGroupKey: _config.defaultGroupKey, | ||
primaryKey: _config.primaryKey, | ||
}; | ||
this.initialConfig = _config; | ||
this.initGroups(_config.groups); | ||
this.initSelectors(_config.selectors); | ||
this.isInstantiated = true; | ||
if (_config.initialData) | ||
this.collect(_config.initialData); | ||
for (const key in this.selectors) | ||
this.selectors[key].reselect(); | ||
} | ||
set key(value) { | ||
this.setKey(value); | ||
} | ||
get key() { | ||
return this._key; | ||
} | ||
setKey(value) { | ||
var _a, _b; | ||
const oldKey = this._key; | ||
this._key = value; | ||
if (value != null && ((_a = this.persistent) === null || _a === void 0 ? void 0 : _a._key) === oldKey) | ||
(_b = this.persistent) === null || _b === void 0 ? void 0 : _b.setKey(value); | ||
return this; | ||
} | ||
Group(initialItems, config = {}) { | ||
var _a; | ||
if (this.isInstantiated) { | ||
const key = (_a = config.key) !== null && _a !== void 0 ? _a : generateId(); | ||
LogCodeManager.log('1B:02:00'); | ||
return this.createGroup(key, initialItems); | ||
} | ||
return new Group(this, initialItems, config); | ||
} | ||
Selector(initialKey, config = {}) { | ||
var _a; | ||
if (this.isInstantiated) { | ||
const key = (_a = config.key) !== null && _a !== void 0 ? _a : generateId(); | ||
LogCodeManager.log('1B:02:01'); | ||
return this.createSelector(key, initialKey); | ||
} | ||
return new Selector(this, initialKey, config); | ||
} | ||
initGroups(groups) { | ||
if (!groups) | ||
return; | ||
let groupsObject = {}; | ||
if (Array.isArray(groups)) { | ||
groups.forEach((groupKey) => { | ||
groupsObject[groupKey] = new Group(this, [], { | ||
key: groupKey, | ||
}); | ||
}); | ||
} | ||
else | ||
groupsObject = groups; | ||
groupsObject[this.config.defaultGroupKey] = new Group(this, [], { | ||
key: this.config.defaultGroupKey, | ||
}); | ||
for (const key in groupsObject) | ||
if (groupsObject[key]._key == null) | ||
groupsObject[key].setKey(key); | ||
this.groups = groupsObject; | ||
} | ||
initSelectors(selectors) { | ||
if (!selectors) | ||
return; | ||
let selectorsObject = {}; | ||
if (Array.isArray(selectors)) { | ||
selectors.forEach((selectorKey) => { | ||
selectorsObject[selectorKey] = new Selector(this, selectorKey, { | ||
key: selectorKey, | ||
}); | ||
}); | ||
} | ||
else | ||
selectorsObject = selectors; | ||
for (const key in selectorsObject) | ||
if (selectorsObject[key]._key == null) | ||
selectorsObject[key].setKey(key); | ||
this.selectors = selectorsObject; | ||
} | ||
collect(data, groupKeys, config = {}) { | ||
const _data = normalizeArray(data); | ||
const _groupKeys = normalizeArray(groupKeys); | ||
const defaultGroupKey = this.config.defaultGroupKey; | ||
const primaryKey = this.config.primaryKey; | ||
config = defineConfig(config, { | ||
method: 'push', | ||
background: false, | ||
patch: false, | ||
select: false, | ||
}); | ||
if (!_groupKeys.includes(defaultGroupKey)) | ||
_groupKeys.push(defaultGroupKey); | ||
_groupKeys.forEach((key) => this.groups[key] == null && this.createGroup(key)); | ||
_data.forEach((data, index) => { | ||
let itemKey; | ||
let success = false; | ||
if (data instanceof Item) { | ||
success = this.assignItem(data, { | ||
background: config.background, | ||
}); | ||
itemKey = data._key; | ||
} | ||
else { | ||
success = this.assignData(data, { | ||
patch: config.patch, | ||
background: config.background, | ||
}); | ||
itemKey = data[primaryKey]; | ||
} | ||
if (success) { | ||
_groupKeys.forEach((groupKey) => { | ||
var _a; | ||
(_a = this.getGroup(groupKey)) === null || _a === void 0 ? void 0 : _a.add(itemKey, { | ||
method: config.method, | ||
background: config.background, | ||
}); | ||
}); | ||
if (config.select) | ||
this.createSelector(itemKey, itemKey); | ||
} | ||
if (config.forEachItem) | ||
config.forEachItem(data, itemKey, success, index); | ||
}); | ||
return this; | ||
} | ||
update(itemKey, changes, config = {}) { | ||
const item = this.getItem(itemKey, { notExisting: true }); | ||
const primaryKey = this.config.primaryKey; | ||
config = defineConfig(config, { | ||
patch: true, | ||
background: false, | ||
}); | ||
if (item == null) { | ||
LogCodeManager.log('1B:03:00', [itemKey, this._key]); | ||
return undefined; | ||
} | ||
if (!isValidObject(changes)) { | ||
LogCodeManager.log('1B:03:01', [itemKey, this._key]); | ||
return undefined; | ||
} | ||
const oldItemKey = item._value[primaryKey]; | ||
const newItemKey = changes[primaryKey] || oldItemKey; | ||
if (oldItemKey !== newItemKey) | ||
this.updateItemKey(oldItemKey, newItemKey, { | ||
background: config.background, | ||
}); | ||
if (config.patch) { | ||
if (changes[primaryKey]) | ||
delete changes[primaryKey]; | ||
let patchConfig = typeof config.patch === 'object' ? config.patch : {}; | ||
patchConfig = Object.assign({ addNewProperties: true }, patchConfig); | ||
item.patch(changes, { | ||
background: config.background, | ||
addNewProperties: patchConfig.addNewProperties, | ||
}); | ||
} | ||
else { | ||
if (changes[this.config.primaryKey] !== itemKey) { | ||
changes[this.config.primaryKey] = itemKey; | ||
LogCodeManager.log('1B:02:02', [], changes); | ||
} | ||
item.set(changes, { | ||
background: config.background, | ||
}); | ||
} | ||
return item; | ||
} | ||
createGroup(groupKey, initialItems = []) { | ||
let group = this.getGroup(groupKey, { notExisting: true }); | ||
if (!this.isInstantiated) | ||
LogCodeManager.log('1B:02:03'); | ||
if (group != null) { | ||
if (!group.isPlaceholder) { | ||
LogCodeManager.log('1B:03:02', [groupKey]); | ||
return group; | ||
} | ||
group.set(initialItems, { overwrite: true }); | ||
return group; | ||
} | ||
group = new Group(this, initialItems, { key: groupKey }); | ||
this.groups[groupKey] = group; | ||
return group; | ||
} | ||
hasGroup(groupKey, config = {}) { | ||
return !!this.getGroup(groupKey, config); | ||
} | ||
getGroup(groupKey, config = {}) { | ||
config = defineConfig(config, { | ||
notExisting: false, | ||
}); | ||
const group = groupKey ? this.groups[groupKey] : undefined; | ||
if (group == null || (!config.notExisting && !group.exists)) | ||
return undefined; | ||
ComputedTracker.tracked(group.observers['value']); | ||
return group; | ||
} | ||
getDefaultGroup() { | ||
return this.getGroup(this.config.defaultGroupKey); | ||
} | ||
getGroupWithReference(groupKey) { | ||
let group = this.getGroup(groupKey, { notExisting: true }); | ||
if (group == null) { | ||
group = new Group(this, [], { | ||
key: groupKey, | ||
isPlaceholder: true, | ||
}); | ||
this.groups[groupKey] = group; | ||
} | ||
ComputedTracker.tracked(group.observers['value']); | ||
return group; | ||
} | ||
removeGroup(groupKey) { | ||
if (this.groups[groupKey] != null) | ||
delete this.groups[groupKey]; | ||
return this; | ||
} | ||
getGroupCount() { | ||
let size = 0; | ||
Object.keys(this.groups).map(() => size++); | ||
return size; | ||
} | ||
createSelector(selectorKey, itemKey) { | ||
let selector = this.getSelector(selectorKey, { notExisting: true }); | ||
if (!this.isInstantiated) | ||
LogCodeManager.log('1B:02:04'); | ||
if (selector != null) { | ||
if (!selector.isPlaceholder) { | ||
LogCodeManager.log('1B:03:03', [selectorKey]); | ||
return selector; | ||
} | ||
selector.select(itemKey, { overwrite: true }); | ||
return selector; | ||
} | ||
selector = new Selector(this, itemKey, { | ||
key: selectorKey, | ||
}); | ||
this.selectors[selectorKey] = selector; | ||
return selector; | ||
} | ||
select(itemKey) { | ||
return this.createSelector(itemKey, itemKey); | ||
} | ||
hasSelector(selectorKey, config = {}) { | ||
return !!this.getSelector(selectorKey, config); | ||
} | ||
getSelector(selectorKey, config = {}) { | ||
config = defineConfig(config, { | ||
notExisting: false, | ||
}); | ||
const selector = selectorKey ? this.selectors[selectorKey] : undefined; | ||
if (selector == null || (!config.notExisting && !selector.exists)) | ||
return undefined; | ||
ComputedTracker.tracked(selector.observers['value']); | ||
return selector; | ||
} | ||
getSelectorWithReference(selectorKey) { | ||
let selector = this.getSelector(selectorKey, { notExisting: true }); | ||
if (selector == null) { | ||
selector = new Selector(this, null, { | ||
key: selectorKey, | ||
isPlaceholder: true, | ||
}); | ||
this.selectors[selectorKey] = selector; | ||
} | ||
ComputedTracker.tracked(selector.observers['value']); | ||
return selector; | ||
} | ||
removeSelector(selectorKey) { | ||
if (this.selectors[selectorKey] != null) { | ||
this.selectors[selectorKey].unselect(); | ||
delete this.selectors[selectorKey]; | ||
} | ||
return this; | ||
} | ||
getSelectorCount() { | ||
let size = 0; | ||
Object.keys(this.selectors).map(() => size++); | ||
return size; | ||
} | ||
hasItem(itemKey, config = {}) { | ||
return !!this.getItem(itemKey, config); | ||
} | ||
getItem(itemKey, config = {}) { | ||
config = defineConfig(config, { | ||
notExisting: false, | ||
}); | ||
const item = itemKey != null ? this.data[itemKey] : undefined; | ||
if (item == null || (!config.notExisting && !item.exists)) | ||
return undefined; | ||
ComputedTracker.tracked(item.observers['value']); | ||
return item; | ||
} | ||
getItemWithReference(itemKey) { | ||
let item = this.getItem(itemKey, { notExisting: true }); | ||
if (item == null) | ||
item = this.createPlaceholderItem(itemKey, true); | ||
ComputedTracker.tracked(item.observers['value']); | ||
return item; | ||
} | ||
createPlaceholderItem(itemKey, addToCollection = false) { | ||
const item = new Item(this, { | ||
[this.config.primaryKey]: itemKey, | ||
dummy: 'item', | ||
}, { isPlaceholder: true }); | ||
if (addToCollection && | ||
!Object.prototype.hasOwnProperty.call(this.data, itemKey)) | ||
this.data[itemKey] = item; | ||
ComputedTracker.tracked(item.observers['value']); | ||
return item; | ||
} | ||
getItemValue(itemKey, config = {}) { | ||
const item = this.getItem(itemKey, config); | ||
if (item == null) | ||
return undefined; | ||
return item.value; | ||
} | ||
getAllItems(config = {}) { | ||
config = defineConfig(config, { | ||
notExisting: false, | ||
}); | ||
const defaultGroup = this.getDefaultGroup(); | ||
let items = []; | ||
if (config.notExisting) { | ||
for (const key in this.data) | ||
items.push(this.data[key]); | ||
} | ||
else { | ||
items = (defaultGroup === null || defaultGroup === void 0 ? void 0 : defaultGroup.getItems()) || []; | ||
} | ||
return items; | ||
} | ||
getAllItemValues(config = {}) { | ||
const items = this.getAllItems(config); | ||
return items.map((item) => item.value); | ||
} | ||
persist(keyOrConfig = {}, config = {}) { | ||
let _config; | ||
let key; | ||
if (isValidObject(keyOrConfig)) { | ||
_config = keyOrConfig; | ||
key = this._key; | ||
} | ||
else { | ||
_config = config || {}; | ||
key = keyOrConfig; | ||
} | ||
_config = defineConfig(_config, { | ||
loadValue: true, | ||
storageKeys: [], | ||
defaultStorageKey: null, | ||
}); | ||
if (this.persistent != null && this.isPersisted) | ||
return this; | ||
this.persistent = new CollectionPersistent(this, { | ||
instantiate: _config.loadValue, | ||
storageKeys: _config.storageKeys, | ||
key: key, | ||
defaultStorageKey: _config.defaultStorageKey, | ||
}); | ||
return this; | ||
} | ||
onLoad(callback) { | ||
if (!this.persistent) | ||
return this; | ||
if (!isFunction(callback)) { | ||
LogCodeManager.log('00:03:01', ['OnLoad Callback', 'function']); | ||
return this; | ||
} | ||
this.persistent.onLoad = callback; | ||
if (this.isPersisted) | ||
callback(true); | ||
return this; | ||
} | ||
reset() { | ||
var _a, _b; | ||
this.data = {}; | ||
this.size = 0; | ||
for (const key in this.groups) | ||
(_a = this.getGroup(key)) === null || _a === void 0 ? void 0 : _a.reset(); | ||
for (const key in this.selectors) | ||
(_b = this.getSelector(key)) === null || _b === void 0 ? void 0 : _b.reset(); | ||
return this; | ||
} | ||
put(itemKeys, groupKeys, config = {}) { | ||
const _itemKeys = normalizeArray(itemKeys); | ||
const _groupKeys = normalizeArray(groupKeys); | ||
_groupKeys.forEach((groupKey) => { | ||
var _a; | ||
(_a = this.getGroup(groupKey)) === null || _a === void 0 ? void 0 : _a.add(_itemKeys, config); | ||
}); | ||
return this; | ||
} | ||
move(itemKeys, oldGroupKey, newGroupKey, config = {}) { | ||
var _a, _b; | ||
const _itemKeys = normalizeArray(itemKeys); | ||
(_a = this.getGroup(oldGroupKey)) === null || _a === void 0 ? void 0 : _a.remove(_itemKeys, removeProperties(config, ['method', 'overwrite'])); | ||
(_b = this.getGroup(newGroupKey)) === null || _b === void 0 ? void 0 : _b.add(_itemKeys, config); | ||
return this; | ||
} | ||
updateItemKey(oldItemKey, newItemKey, config = {}) { | ||
var _a; | ||
const item = this.getItem(oldItemKey, { notExisting: true }); | ||
config = defineConfig(config, { | ||
background: false, | ||
}); | ||
if (item == null || oldItemKey === newItemKey) | ||
return false; | ||
if (this.hasItem(newItemKey)) { | ||
LogCodeManager.log('1B:03:04', [oldItemKey, newItemKey, this._key]); | ||
return false; | ||
} | ||
delete this.data[oldItemKey]; | ||
this.data[newItemKey] = item; | ||
item.setKey(newItemKey, { | ||
background: config.background, | ||
}); | ||
if (item.persistent != null && | ||
item.persistent._key === | ||
CollectionPersistent.getItemStorageKey(oldItemKey, this._key)) | ||
(_a = item.persistent) === null || _a === void 0 ? void 0 : _a.setKey(CollectionPersistent.getItemStorageKey(newItemKey, this._key)); | ||
for (const groupKey in this.groups) { | ||
const group = this.getGroup(groupKey, { notExisting: true }); | ||
if (group == null || !group.has(oldItemKey)) | ||
continue; | ||
group.replace(oldItemKey, newItemKey, { background: config.background }); | ||
} | ||
for (const selectorKey in this.selectors) { | ||
const selector = this.getSelector(selectorKey, { notExisting: true }); | ||
if (selector == null) | ||
continue; | ||
if (selector.hasSelected(newItemKey, false)) { | ||
selector.reselect({ | ||
force: true, | ||
background: config.background, | ||
}); | ||
} | ||
if (selector.hasSelected(oldItemKey, false)) | ||
selector.select(newItemKey, { | ||
background: config.background, | ||
}); | ||
} | ||
return true; | ||
} | ||
getGroupKeysThatHaveItemKey(itemKey) { | ||
const groupKeys = []; | ||
for (const groupKey in this.groups) { | ||
const group = this.groups[groupKey]; | ||
if (group === null || group === void 0 ? void 0 : group.has(itemKey)) | ||
groupKeys.push(groupKey); | ||
} | ||
return groupKeys; | ||
} | ||
remove(itemKeys) { | ||
return { | ||
fromGroups: (groups) => this.removeFromGroups(itemKeys, groups), | ||
everywhere: (config) => this.removeItems(itemKeys, config || {}), | ||
}; | ||
} | ||
removeFromGroups(itemKeys, groupKeys) { | ||
const _itemKeys = normalizeArray(itemKeys); | ||
const _groupKeys = normalizeArray(groupKeys); | ||
_itemKeys.forEach((itemKey) => { | ||
let removedFromGroupsCount = 0; | ||
_groupKeys.forEach((groupKey) => { | ||
const group = this.getGroup(groupKey, { notExisting: true }); | ||
if (!(group === null || group === void 0 ? void 0 : group.has(itemKey))) | ||
return; | ||
group.remove(itemKey); | ||
removedFromGroupsCount++; | ||
}); | ||
if (removedFromGroupsCount >= | ||
this.getGroupKeysThatHaveItemKey(itemKey).length) | ||
this.removeItems(itemKey); | ||
}); | ||
return this; | ||
} | ||
removeItems(itemKeys, config = {}) { | ||
config = defineConfig(config, { | ||
notExisting: false, | ||
removeSelector: false, | ||
}); | ||
const _itemKeys = normalizeArray(itemKeys); | ||
_itemKeys.forEach((itemKey) => { | ||
var _a, _b; | ||
const item = this.getItem(itemKey, { notExisting: config.notExisting }); | ||
if (item == null) | ||
return; | ||
const wasPlaceholder = item.isPlaceholder; | ||
for (const groupKey in this.groups) { | ||
const group = this.getGroup(groupKey, { notExisting: true }); | ||
if (group === null || group === void 0 ? void 0 : group.has(itemKey)) | ||
group === null || group === void 0 ? void 0 : group.remove(itemKey); | ||
} | ||
(_a = item.persistent) === null || _a === void 0 ? void 0 : _a.removePersistedValue(); | ||
delete this.data[itemKey]; | ||
for (const selectorKey in this.selectors) { | ||
const selector = this.getSelector(selectorKey, { notExisting: true }); | ||
if (selector != null && selector.hasSelected(itemKey, false)) { | ||
if (config.removeSelector) { | ||
this.removeSelector((_b = selector._key) !== null && _b !== void 0 ? _b : 'unknown'); | ||
} | ||
else { | ||
selector.reselect({ force: true }); | ||
} | ||
} | ||
} | ||
if (!wasPlaceholder) | ||
this.size--; | ||
}); | ||
return this; | ||
} | ||
assignData(data, config = {}) { | ||
config = defineConfig(config, { | ||
patch: false, | ||
background: false, | ||
}); | ||
const _data = copy(data); | ||
const primaryKey = this.config.primaryKey; | ||
if (!isValidObject(_data)) { | ||
LogCodeManager.log('1B:03:05', [this._key]); | ||
return false; | ||
} | ||
if (!Object.prototype.hasOwnProperty.call(_data, primaryKey)) { | ||
LogCodeManager.log('1B:02:05', [this._key, primaryKey]); | ||
_data[primaryKey] = generateId(); | ||
} | ||
const itemKey = _data[primaryKey]; | ||
const item = this.getItem(itemKey, { notExisting: true }); | ||
const wasPlaceholder = (item === null || item === void 0 ? void 0 : item.isPlaceholder) || false; | ||
if (item != null) { | ||
if (config.patch) { | ||
item.patch(_data, { background: config.background }); | ||
} | ||
else { | ||
item.set(_data, { background: config.background }); | ||
} | ||
} | ||
else { | ||
this.assignItem(new Item(this, _data), { | ||
background: config.background, | ||
}); | ||
} | ||
if (wasPlaceholder) | ||
this.size++; | ||
return true; | ||
} | ||
assignItem(item, config = {}) { | ||
config = defineConfig(config, { | ||
overwrite: false, | ||
background: false, | ||
}); | ||
const primaryKey = this.config.primaryKey; | ||
let itemKey = item._value[primaryKey]; | ||
let increaseCollectionSize = true; | ||
if (!Object.prototype.hasOwnProperty.call(item._value, primaryKey)) { | ||
LogCodeManager.log('1B:02:05', [this._key, primaryKey]); | ||
itemKey = generateId(); | ||
item.patch({ [this.config.primaryKey]: itemKey }, { background: config.background }); | ||
item._key = itemKey; | ||
} | ||
if (item.collection() !== this) { | ||
LogCodeManager.log('1B:03:06', [this._key, item.collection()._key]); | ||
return false; | ||
} | ||
if (this.getItem(itemKey, { notExisting: true }) != null) { | ||
if (!config.overwrite) { | ||
this.assignData(item._value); | ||
return true; | ||
} | ||
else | ||
increaseCollectionSize = false; | ||
} | ||
this.data[itemKey] = item; | ||
this.rebuildGroupsThatIncludeItemKey(itemKey, { | ||
background: config.background, | ||
}); | ||
if (increaseCollectionSize) | ||
this.size++; | ||
return true; | ||
} | ||
rebuildGroupsThatIncludeItemKey(itemKey, config = {}) { | ||
config = defineConfig(config, { | ||
background: false, | ||
sideEffects: { | ||
enabled: true, | ||
exclude: [], | ||
}, | ||
}); | ||
for (const groupKey in this.groups) { | ||
const group = this.getGroup(groupKey); | ||
if (group === null || group === void 0 ? void 0 : group.has(itemKey)) { | ||
group === null || group === void 0 ? void 0 : group.rebuild({ | ||
background: config === null || config === void 0 ? void 0 : config.background, | ||
sideEffects: config === null || config === void 0 ? void 0 : config.sideEffects, | ||
storage: false, | ||
}); | ||
} | ||
} | ||
} | ||
import { Collection, shared, } from '../internal'; | ||
export * from './collection'; | ||
export function createCollection(config, agileInstance = shared) { | ||
return new Collection(agileInstance, config); | ||
} |
@@ -1,92 +0,16 @@ | ||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { | ||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } | ||
return new (P || (P = Promise))(function (resolve, reject) { | ||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } | ||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } | ||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } | ||
step((generator = generator.apply(thisArg, _arguments || [])).next()); | ||
}); | ||
}; | ||
import { State, ComputedTracker, removeProperties, LogCodeManager, isAsyncFunction, extractRelevantObservers, defineConfig, } from '../internal'; | ||
export class Computed extends State { | ||
constructor(agileInstance, computeFunction, config = {}) { | ||
super(agileInstance, null, { | ||
key: config.key, | ||
dependents: config.dependents, | ||
import { Computed, defineConfig, removeProperties, shared, } from '../internal'; | ||
export * from './computed'; | ||
export function createComputed(computeFunction, configOrDeps) { | ||
let _config = {}; | ||
if (Array.isArray(configOrDeps)) { | ||
_config = defineConfig(_config, { | ||
computedDeps: configOrDeps, | ||
}); | ||
this.deps = new Set(); | ||
this.hardCodedDeps = []; | ||
this.isComputed = true; | ||
config = defineConfig(config, { | ||
computedDeps: [], | ||
autodetect: !isAsyncFunction(computeFunction), | ||
}); | ||
this.agileInstance = () => agileInstance; | ||
this.computeFunction = computeFunction; | ||
this.config = { | ||
autodetect: config.autodetect, | ||
}; | ||
this.hardCodedDeps = extractRelevantObservers(config.computedDeps).filter((dep) => dep !== undefined); | ||
this.deps = new Set(this.hardCodedDeps); | ||
this.deps.forEach((observer) => { | ||
observer.addDependent(this.observers['value']); | ||
}); | ||
this.recompute({ autodetect: config.autodetect, overwrite: true }); | ||
} | ||
recompute(config = {}) { | ||
config = defineConfig(config, { | ||
autodetect: false, | ||
}); | ||
this.compute({ autodetect: config.autodetect }).then((result) => { | ||
this.observers['value'].ingestValue(result, removeProperties(config, ['autodetect'])); | ||
}); | ||
return this; | ||
else { | ||
if (configOrDeps) | ||
_config = configOrDeps; | ||
} | ||
updateComputeFunction(computeFunction, deps = [], config = {}) { | ||
config = defineConfig(config, { | ||
autodetect: this.config.autodetect, | ||
}); | ||
this.deps.forEach((observer) => { | ||
observer.removeDependent(this.observers['value']); | ||
}); | ||
this.hardCodedDeps = extractRelevantObservers(deps).filter((dep) => dep !== undefined); | ||
this.deps = new Set(this.hardCodedDeps); | ||
this.deps.forEach((observer) => { | ||
observer.addDependent(this.observers['value']); | ||
}); | ||
this.computeFunction = computeFunction; | ||
this.recompute(removeProperties(config, ['overwriteDeps'])); | ||
return this; | ||
} | ||
compute(config = {}) { | ||
return __awaiter(this, void 0, void 0, function* () { | ||
config = defineConfig(config, { | ||
autodetect: this.config.autodetect, | ||
}); | ||
if (config.autodetect) | ||
ComputedTracker.track(); | ||
const computedValue = this.computeFunction(); | ||
if (config.autodetect) { | ||
const foundDeps = ComputedTracker.getTrackedObservers(); | ||
this.deps.forEach((observer) => { | ||
if (!foundDeps.includes(observer) && | ||
!this.hardCodedDeps.includes(observer)) { | ||
this.deps.delete(observer); | ||
observer.removeDependent(this.observers['value']); | ||
} | ||
}); | ||
foundDeps.forEach((observer) => { | ||
if (!this.deps.has(observer)) { | ||
this.deps.add(observer); | ||
observer.addDependent(this.observers['value']); | ||
} | ||
}); | ||
} | ||
return computedValue; | ||
}); | ||
} | ||
persist() { | ||
LogCodeManager.log('19:03:00'); | ||
return this; | ||
} | ||
_config = defineConfig(_config, { agileInstance: shared }); | ||
return new Computed(_config.agileInstance, computeFunction, removeProperties(_config, ['agileInstance'])); | ||
} |
@@ -1,67 +0,1 @@ | ||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { | ||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } | ||
return new (P || (P = Promise))(function (resolve, reject) { | ||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } | ||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } | ||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } | ||
step((generator = generator.apply(thisArg, _arguments || [])).next()); | ||
}); | ||
}; | ||
import { Integration, LogCodeManager, defineConfig } from '../internal'; | ||
const onRegisterInitialIntegrationCallbacks = []; | ||
export class Integrations { | ||
constructor(agileInstance, config = {}) { | ||
this.integrations = new Set(); | ||
config = defineConfig(config, { | ||
autoIntegrate: true, | ||
}); | ||
this.agileInstance = () => agileInstance; | ||
if (config.autoIntegrate) { | ||
Integrations.onRegisterInitialIntegration((integration) => { | ||
this.integrate(integration); | ||
}); | ||
} | ||
} | ||
static addInitialIntegration(integration) { | ||
if (integration instanceof Integration) { | ||
onRegisterInitialIntegrationCallbacks.forEach((callback) => callback(integration)); | ||
Integrations.initialIntegrations.push(integration); | ||
} | ||
} | ||
static onRegisterInitialIntegration(callback) { | ||
onRegisterInitialIntegrationCallbacks.push(callback); | ||
Integrations.initialIntegrations.forEach((integration) => { | ||
callback(integration); | ||
}); | ||
} | ||
integrate(integration) { | ||
return __awaiter(this, void 0, void 0, function* () { | ||
if (integration._key == null) { | ||
LogCodeManager.log('18:03:00', [integration._key, this.agileInstance().key], integration); | ||
return false; | ||
} | ||
if (integration.methods.bind) | ||
integration.ready = yield integration.methods.bind(this.agileInstance()); | ||
else | ||
integration.ready = true; | ||
this.integrations.add(integration); | ||
integration.integrated = true; | ||
LogCodeManager.log('18:00:00', [integration._key, this.agileInstance().key], integration); | ||
return true; | ||
}); | ||
} | ||
update(componentInstance, updatedData) { | ||
this.integrations.forEach((integration) => { | ||
if (!integration.ready) { | ||
LogCodeManager.log('18:02:00', [integration._key]); | ||
return; | ||
} | ||
if (integration.methods.updateMethod) | ||
integration.methods.updateMethod(componentInstance, updatedData); | ||
}); | ||
} | ||
hasIntegration() { | ||
return this.integrations.size > 0; | ||
} | ||
} | ||
Integrations.initialIntegrations = []; | ||
export * from './integrations'; |
@@ -1,7 +0,1 @@ | ||
export let loggerPackage = null; | ||
try { | ||
loggerPackage = require('@agile-ts/logger'); | ||
} | ||
catch (e) { | ||
} | ||
const logCodeTypes = { | ||
@@ -13,3 +7,3 @@ '00': 'success', | ||
}; | ||
const logCodeMessages = { | ||
const niceLogCodeMessages = { | ||
'10:00:00': 'Created new AgileInstance.', | ||
@@ -94,5 +88,9 @@ '10:02:00': 'Be careful when binding multiple Agile Instances globally in one application!', | ||
}; | ||
const logCodeMessages = typeof process === 'object' && process.env.NODE_ENV !== 'production' | ||
? niceLogCodeMessages | ||
: {}; | ||
function getLog(logCode, replacers = []) { | ||
var _a; | ||
let result = (_a = logCodeMessages[logCode]) !== null && _a !== void 0 ? _a : `'${logCode}' is a unknown logCode!`; | ||
let result = logCodeMessages[logCode]; | ||
if (result == null) | ||
return logCode; | ||
for (let i = 0; i < replacers.length; i++) { | ||
@@ -129,12 +127,35 @@ result = result.replace('${' + i + '}', replacers[i]); | ||
} | ||
export const LogCodeManager = { | ||
getLog, | ||
log, | ||
logCodeLogTypes: logCodeTypes, | ||
logCodeMessages: logCodeMessages, | ||
getLogger: () => { | ||
var _a; | ||
return (_a = loggerPackage === null || loggerPackage === void 0 ? void 0 : loggerPackage.sharedAgileLogger) !== null && _a !== void 0 ? _a : null; | ||
}, | ||
logIfTags, | ||
}; | ||
let tempLogCodeManager; | ||
if (typeof process === 'object' && process.env.NODE_ENV !== 'production') { | ||
tempLogCodeManager = { | ||
getLog, | ||
log, | ||
logCodeLogTypes: logCodeTypes, | ||
logCodeMessages: logCodeMessages, | ||
getLogger: () => { | ||
var _a; | ||
let loggerPackage = null; | ||
try { | ||
loggerPackage = require('@agile-ts/logger'); | ||
} | ||
catch (e) { | ||
} | ||
return (_a = loggerPackage === null || loggerPackage === void 0 ? void 0 : loggerPackage.sharedAgileLogger) !== null && _a !== void 0 ? _a : null; | ||
}, | ||
logIfTags, | ||
}; | ||
} | ||
else { | ||
tempLogCodeManager = { | ||
getLog: (logCode, replacers) => logCode, | ||
log, | ||
logCodeLogTypes: logCodeTypes, | ||
logCodeMessages: logCodeMessages, | ||
getLogger: () => { | ||
return null; | ||
}, | ||
logIfTags: (tags, logCode, replacers) => { | ||
}, | ||
}; | ||
} | ||
export const LogCodeManager = tempLogCodeManager; |
@@ -1,131 +0,1 @@ | ||
import { CallbackSubscriptionContainer, ComponentSubscriptionContainer, notEqual, LogCodeManager, defineConfig, } from '../internal'; | ||
export class Runtime { | ||
constructor(agileInstance) { | ||
this.currentJob = null; | ||
this.jobQueue = []; | ||
this.jobsToRerender = []; | ||
this.notReadyJobsToRerender = new Set(); | ||
this.isPerformingJobs = false; | ||
this.bucketTimeout = null; | ||
this.agileInstance = () => agileInstance; | ||
} | ||
ingest(job, config = {}) { | ||
config = defineConfig(config, { | ||
perform: !this.isPerformingJobs, | ||
}); | ||
this.jobQueue.push(job); | ||
LogCodeManager.logIfTags(['runtime'], '16:01:00', [job._key], job); | ||
if (config.perform) { | ||
const performJob = this.jobQueue.shift(); | ||
if (performJob) | ||
this.perform(performJob); | ||
} | ||
} | ||
perform(job) { | ||
this.isPerformingJobs = true; | ||
this.currentJob = job; | ||
job.observer.perform(job); | ||
job.performed = true; | ||
job.observer.dependents.forEach((observer) => observer.ingest()); | ||
if (job.rerender) | ||
this.jobsToRerender.push(job); | ||
this.currentJob = null; | ||
LogCodeManager.logIfTags(['runtime'], '16:01:01', [job._key], job); | ||
if (this.jobQueue.length > 0) { | ||
const performJob = this.jobQueue.shift(); | ||
if (performJob) | ||
this.perform(performJob); | ||
} | ||
else { | ||
this.isPerformingJobs = false; | ||
if (this.jobsToRerender.length > 0) { | ||
if (this.agileInstance().config.bucket) { | ||
if (this.bucketTimeout == null) { | ||
this.bucketTimeout = setTimeout(() => { | ||
this.bucketTimeout = null; | ||
this.updateSubscribers(); | ||
}); | ||
} | ||
} | ||
else | ||
this.updateSubscribers(); | ||
} | ||
} | ||
} | ||
updateSubscribers() { | ||
const jobsToRerender = this.jobsToRerender.concat(Array.from(this.notReadyJobsToRerender)); | ||
this.notReadyJobsToRerender = new Set(); | ||
this.jobsToRerender = []; | ||
if (!this.agileInstance().hasIntegration() || jobsToRerender.length <= 0) | ||
return false; | ||
const subscriptionContainerToUpdate = this.extractToUpdateSubscriptionContainer(jobsToRerender); | ||
if (subscriptionContainerToUpdate.length <= 0) | ||
return false; | ||
this.updateSubscriptionContainer(subscriptionContainerToUpdate); | ||
return true; | ||
} | ||
extractToUpdateSubscriptionContainer(jobs) { | ||
const subscriptionsToUpdate = new Set(); | ||
for (let i = 0; i < jobs.length; i++) { | ||
const job = jobs[i]; | ||
job.subscriptionContainersToUpdate.forEach((subscriptionContainer) => { | ||
let updateSubscriptionContainer = true; | ||
if (!subscriptionContainer.ready) { | ||
if (!job.config.maxTriesToUpdate || | ||
job.timesTriedToUpdateCount < job.config.maxTriesToUpdate) { | ||
job.timesTriedToUpdateCount++; | ||
this.notReadyJobsToRerender.add(job); | ||
LogCodeManager.log('16:02:00', [subscriptionContainer.key], subscriptionContainer); | ||
} | ||
else { | ||
LogCodeManager.log('16:02:01', [job.config.maxTriesToUpdate], subscriptionContainer); | ||
} | ||
return; | ||
} | ||
updateSubscriptionContainer = | ||
updateSubscriptionContainer && | ||
this.handleSelectors(subscriptionContainer, job); | ||
if (updateSubscriptionContainer) { | ||
subscriptionContainer.updatedSubscribers.add(job.observer); | ||
subscriptionsToUpdate.add(subscriptionContainer); | ||
} | ||
job.subscriptionContainersToUpdate.delete(subscriptionContainer); | ||
}); | ||
} | ||
return Array.from(subscriptionsToUpdate); | ||
} | ||
updateSubscriptionContainer(subscriptionsToUpdate) { | ||
for (let i = 0; i < subscriptionsToUpdate.length; i++) { | ||
const subscriptionContainer = subscriptionsToUpdate[i]; | ||
if (subscriptionContainer instanceof CallbackSubscriptionContainer) | ||
subscriptionContainer.callback(); | ||
if (subscriptionContainer instanceof ComponentSubscriptionContainer) | ||
this.agileInstance().integrations.update(subscriptionContainer.component, this.getUpdatedObserverValues(subscriptionContainer)); | ||
subscriptionContainer.updatedSubscribers.clear(); | ||
} | ||
LogCodeManager.logIfTags(['runtime'], '16:01:02', [], subscriptionsToUpdate); | ||
} | ||
getUpdatedObserverValues(subscriptionContainer) { | ||
var _a; | ||
const props = {}; | ||
for (const observer of subscriptionContainer.updatedSubscribers) { | ||
const key = (_a = subscriptionContainer.subscriberKeysWeakMap.get(observer)) !== null && _a !== void 0 ? _a : observer.key; | ||
if (key != null) | ||
props[key] = observer.value; | ||
} | ||
return props; | ||
} | ||
handleSelectors(subscriptionContainer, job) { | ||
var _a; | ||
const selectorMethods = (_a = subscriptionContainer.selectorsWeakMap.get(job.observer)) === null || _a === void 0 ? void 0 : _a.methods; | ||
if (selectorMethods == null) | ||
return true; | ||
const previousValue = job.observer.previousValue; | ||
const newValue = job.observer.value; | ||
for (const selectorMethod of selectorMethods) { | ||
if (notEqual(selectorMethod(newValue), selectorMethod(previousValue))) | ||
return true; | ||
} | ||
return false; | ||
} | ||
} | ||
export * from './runtime'; |
@@ -1,2 +0,2 @@ | ||
import { Agile, Collection, Computed, defineConfig, removeProperties, runsOnServer, State, Storage, } from './internal'; | ||
import { Agile, runsOnServer } from './internal'; | ||
let sharedAgileInstance = new Agile({ | ||
@@ -10,27 +10,1 @@ key: 'shared', | ||
} | ||
export function createStorage(config) { | ||
return new Storage(config); | ||
} | ||
export function createState(initialValue, config = {}) { | ||
config = defineConfig(config, { | ||
agileInstance: sharedAgileInstance, | ||
}); | ||
return new State(config.agileInstance, initialValue, removeProperties(config, ['agileInstance'])); | ||
} | ||
export function createComputed(computeFunction, configOrDeps) { | ||
let _config = {}; | ||
if (Array.isArray(configOrDeps)) { | ||
_config = defineConfig(_config, { | ||
computedDeps: configOrDeps, | ||
}); | ||
} | ||
else { | ||
if (configOrDeps) | ||
_config = configOrDeps; | ||
} | ||
_config = defineConfig(_config, { agileInstance: sharedAgileInstance }); | ||
return new Computed(_config.agileInstance, computeFunction, removeProperties(_config, ['agileInstance'])); | ||
} | ||
export function createCollection(config, agileInstance = sharedAgileInstance) { | ||
return new Collection(agileInstance, config); | ||
} |
@@ -1,261 +0,8 @@ | ||
import { copy, flatMerge, isValidObject, StateObserver, StatePersistent, equal, isFunction, notEqual, generateId, ComputedTracker, removeProperties, LogCodeManager, defineConfig, } from '../internal'; | ||
export class State { | ||
constructor(agileInstance, initialValue, config = {}) { | ||
this.isSet = false; | ||
this.isPlaceholder = false; | ||
this.observers = {}; | ||
this.sideEffects = {}; | ||
this.isPersisted = false; | ||
config = defineConfig(config, { | ||
dependents: [], | ||
isPlaceholder: false, | ||
}); | ||
this.agileInstance = () => agileInstance; | ||
this._key = config.key; | ||
this.observers['value'] = new StateObserver(this, { | ||
key: config.key, | ||
dependents: config.dependents, | ||
}); | ||
this.initialStateValue = copy(initialValue); | ||
this._value = copy(initialValue); | ||
this.previousStateValue = copy(initialValue); | ||
this.nextStateValue = copy(initialValue); | ||
this.isPlaceholder = true; | ||
this.computeExistsMethod = (v) => { | ||
return v != null; | ||
}; | ||
if (!config.isPlaceholder) | ||
this.set(initialValue, { overwrite: true }); | ||
} | ||
set value(value) { | ||
this.set(value); | ||
} | ||
get value() { | ||
ComputedTracker.tracked(this.observers['value']); | ||
return copy(this._value); | ||
} | ||
set key(value) { | ||
this.setKey(value); | ||
} | ||
get key() { | ||
return this._key; | ||
} | ||
setKey(value) { | ||
var _a, _b; | ||
const oldKey = this._key; | ||
this._key = value; | ||
for (const observerKey in this.observers) | ||
this.observers[observerKey]._key = value; | ||
if (value != null && ((_a = this.persistent) === null || _a === void 0 ? void 0 : _a._key) === oldKey) | ||
(_b = this.persistent) === null || _b === void 0 ? void 0 : _b.setKey(value); | ||
return this; | ||
} | ||
set(value, config = {}) { | ||
config = defineConfig(config, { | ||
force: false, | ||
}); | ||
const _value = isFunction(value) | ||
? value(copy(this._value)) | ||
: value; | ||
this.observers['value'].ingestValue(_value, config); | ||
return this; | ||
} | ||
ingest(config = {}) { | ||
this.observers['value'].ingest(config); | ||
return this; | ||
} | ||
undo(config = {}) { | ||
this.set(this.previousStateValue, config); | ||
return this; | ||
} | ||
reset(config = {}) { | ||
this.set(this.initialStateValue, config); | ||
return this; | ||
} | ||
patch(targetWithChanges, config = {}) { | ||
config = defineConfig(config, { | ||
addNewProperties: true, | ||
}); | ||
if (!isValidObject(this.nextStateValue, true)) { | ||
LogCodeManager.log('14:03:02'); | ||
return this; | ||
} | ||
if (!isValidObject(targetWithChanges, true)) { | ||
LogCodeManager.log('00:03:01', ['TargetWithChanges', 'object']); | ||
return this; | ||
} | ||
if (Array.isArray(targetWithChanges) && | ||
Array.isArray(this.nextStateValue)) { | ||
this.nextStateValue = [ | ||
...this.nextStateValue, | ||
...targetWithChanges, | ||
]; | ||
} | ||
else { | ||
this.nextStateValue = flatMerge(this.nextStateValue, targetWithChanges, { addNewProperties: config.addNewProperties }); | ||
} | ||
this.ingest(removeProperties(config, ['addNewProperties'])); | ||
return this; | ||
} | ||
watch(keyOrCallback, callback) { | ||
const generateKey = isFunction(keyOrCallback); | ||
let _callback; | ||
let key; | ||
if (generateKey) { | ||
key = generateId(); | ||
_callback = keyOrCallback; | ||
} | ||
else { | ||
key = keyOrCallback; | ||
_callback = callback; | ||
} | ||
if (!isFunction(_callback)) { | ||
LogCodeManager.log('00:03:01', ['Watcher Callback', 'function']); | ||
return this; | ||
} | ||
this.addSideEffect(key, (instance) => { | ||
_callback(instance.value, key); | ||
}, { weight: 0 }); | ||
return generateKey ? key : this; | ||
} | ||
removeWatcher(key) { | ||
this.removeSideEffect(key); | ||
return this; | ||
} | ||
onInaugurated(callback) { | ||
const watcherKey = 'InauguratedWatcherKey'; | ||
this.watch(watcherKey, (value, key) => { | ||
callback(value, key); | ||
this.removeSideEffect(watcherKey); | ||
}); | ||
return this; | ||
} | ||
persist(keyOrConfig = {}, config = {}) { | ||
let _config; | ||
let key; | ||
if (isValidObject(keyOrConfig)) { | ||
_config = keyOrConfig; | ||
key = this._key; | ||
} | ||
else { | ||
_config = config || {}; | ||
key = keyOrConfig; | ||
} | ||
_config = defineConfig(_config, { | ||
loadValue: true, | ||
storageKeys: [], | ||
defaultStorageKey: null, | ||
}); | ||
if (this.persistent != null && this.isPersisted) | ||
return this; | ||
this.persistent = new StatePersistent(this, { | ||
instantiate: _config.loadValue, | ||
storageKeys: _config.storageKeys, | ||
key: key, | ||
defaultStorageKey: _config.defaultStorageKey, | ||
}); | ||
return this; | ||
} | ||
onLoad(callback) { | ||
if (!this.persistent) | ||
return this; | ||
if (!isFunction(callback)) { | ||
LogCodeManager.log('00:03:01', ['OnLoad Callback', 'function']); | ||
return this; | ||
} | ||
this.persistent.onLoad = callback; | ||
if (this.isPersisted) | ||
callback(true); | ||
return this; | ||
} | ||
interval(handler, delay) { | ||
if (!isFunction(handler)) { | ||
LogCodeManager.log('00:03:01', ['Interval Callback', 'function']); | ||
return this; | ||
} | ||
if (this.currentInterval) { | ||
LogCodeManager.log('14:03:03', [], this.currentInterval); | ||
return this; | ||
} | ||
this.currentInterval = setInterval(() => { | ||
this.set(handler(this._value)); | ||
}, delay !== null && delay !== void 0 ? delay : 1000); | ||
return this; | ||
} | ||
clearInterval() { | ||
if (this.currentInterval) { | ||
clearInterval(this.currentInterval); | ||
delete this.currentInterval; | ||
} | ||
} | ||
get exists() { | ||
return !this.isPlaceholder && this.computeExistsMethod(this.value); | ||
} | ||
computeExists(method) { | ||
if (!isFunction(method)) { | ||
LogCodeManager.log('00:03:01', ['Compute Exists Method', 'function']); | ||
return this; | ||
} | ||
this.computeExistsMethod = method; | ||
return this; | ||
} | ||
computeValue(method) { | ||
if (!isFunction(method)) { | ||
LogCodeManager.log('00:03:01', ['Compute Value Method', 'function']); | ||
return this; | ||
} | ||
this.computeValueMethod = method; | ||
this.set(this.nextStateValue); | ||
return this; | ||
} | ||
is(value) { | ||
return equal(value, this.value); | ||
} | ||
isNot(value) { | ||
return notEqual(value, this.value); | ||
} | ||
invert() { | ||
switch (typeof this.nextStateValue) { | ||
case 'boolean': | ||
this.set(!this.nextStateValue); | ||
break; | ||
case 'object': | ||
if (Array.isArray(this.nextStateValue)) | ||
this.set(this.nextStateValue.reverse()); | ||
break; | ||
case 'string': | ||
this.set(this.nextStateValue.split('').reverse().join('')); | ||
break; | ||
case 'number': | ||
this.set((this.nextStateValue * -1)); | ||
break; | ||
default: | ||
LogCodeManager.log('14:03:04', [typeof this.nextStateValue]); | ||
} | ||
return this; | ||
} | ||
addSideEffect(key, callback, config = {}) { | ||
config = defineConfig(config, { | ||
weight: 10, | ||
}); | ||
if (!isFunction(callback)) { | ||
LogCodeManager.log('00:03:01', ['Side Effect Callback', 'function']); | ||
return this; | ||
} | ||
this.sideEffects[key] = { | ||
callback: callback, | ||
weight: config.weight, | ||
}; | ||
return this; | ||
} | ||
removeSideEffect(key) { | ||
delete this.sideEffects[key]; | ||
return this; | ||
} | ||
hasSideEffect(key) { | ||
return !!this.sideEffects[key]; | ||
} | ||
getPersistableValue() { | ||
return this._value; | ||
} | ||
import { State, defineConfig, removeProperties, shared, } from '../internal'; | ||
export * from './state'; | ||
export function createState(initialValue, config = {}) { | ||
config = defineConfig(config, { | ||
agileInstance: shared, | ||
}); | ||
return new State(config.agileInstance, initialValue, removeProperties(config, ['agileInstance'])); | ||
} |
@@ -1,129 +0,5 @@ | ||
import { Storage, notEqual, LogCodeManager, defineConfig, } from '../internal'; | ||
export class Storages { | ||
constructor(agileInstance, config = {}) { | ||
this.storages = {}; | ||
this.persistentInstances = new Set(); | ||
this.agileInstance = () => agileInstance; | ||
config = defineConfig(config, { | ||
localStorage: false, | ||
defaultStorageKey: null, | ||
}); | ||
this.config = { defaultStorageKey: config.defaultStorageKey }; | ||
if (config.localStorage) | ||
this.instantiateLocalStorage(); | ||
} | ||
instantiateLocalStorage() { | ||
if (!Storages.localStorageAvailable()) { | ||
LogCodeManager.log('11:02:00'); | ||
return false; | ||
} | ||
const _localStorage = new Storage({ | ||
key: 'localStorage', | ||
async: false, | ||
methods: { | ||
get: localStorage.getItem.bind(localStorage), | ||
set: localStorage.setItem.bind(localStorage), | ||
remove: localStorage.removeItem.bind(localStorage), | ||
}, | ||
}); | ||
return this.register(_localStorage, { default: true }); | ||
} | ||
register(storage, config = {}) { | ||
const hasRegisteredAnyStorage = notEqual(this.storages, {}); | ||
if (Object.prototype.hasOwnProperty.call(this.storages, storage.key)) { | ||
LogCodeManager.log('11:03:00', [storage.key]); | ||
return false; | ||
} | ||
if (!hasRegisteredAnyStorage && config.default === false) | ||
LogCodeManager.log('11:02:01'); | ||
if (!hasRegisteredAnyStorage) | ||
config.default = true; | ||
this.storages[storage.key] = storage; | ||
if (config.default) | ||
this.config.defaultStorageKey = storage.key; | ||
this.persistentInstances.forEach((persistent) => { | ||
if (persistent.storageKeys.includes(storage.key)) { | ||
const isValid = persistent.validatePersistent(); | ||
if (isValid) | ||
persistent.initialLoading(); | ||
return; | ||
} | ||
if (persistent.config.defaultStorageKey == null) { | ||
persistent.assignStorageKeys(); | ||
const isValid = persistent.validatePersistent(); | ||
if (isValid) | ||
persistent.initialLoading(); | ||
} | ||
}); | ||
LogCodeManager.log('13:00:00', [storage.key], storage); | ||
return true; | ||
} | ||
getStorage(storageKey) { | ||
if (!storageKey) | ||
return undefined; | ||
const storage = this.storages[storageKey]; | ||
if (!storage) { | ||
LogCodeManager.log('11:03:01', [storageKey]); | ||
return undefined; | ||
} | ||
if (!storage.ready) { | ||
LogCodeManager.log('11:03:02', [storageKey]); | ||
return undefined; | ||
} | ||
return storage; | ||
} | ||
get(storageItemKey, storageKey) { | ||
if (!this.hasStorage()) { | ||
LogCodeManager.log('11:03:03'); | ||
return Promise.resolve(undefined); | ||
} | ||
if (storageKey) { | ||
const storage = this.getStorage(storageKey); | ||
if (storage) | ||
return storage.get(storageItemKey); | ||
} | ||
const defaultStorage = this.getStorage(this.config.defaultStorageKey); | ||
return ((defaultStorage === null || defaultStorage === void 0 ? void 0 : defaultStorage.get(storageItemKey)) || Promise.resolve(undefined)); | ||
} | ||
set(storageItemKey, value, storageKeys) { | ||
var _a; | ||
if (!this.hasStorage()) { | ||
LogCodeManager.log('11:03:04'); | ||
return; | ||
} | ||
if (storageKeys != null) { | ||
for (const storageKey of storageKeys) | ||
(_a = this.getStorage(storageKey)) === null || _a === void 0 ? void 0 : _a.set(storageItemKey, value); | ||
return; | ||
} | ||
const defaultStorage = this.getStorage(this.config.defaultStorageKey); | ||
defaultStorage === null || defaultStorage === void 0 ? void 0 : defaultStorage.set(storageItemKey, value); | ||
} | ||
remove(storageItemKey, storageKeys) { | ||
var _a; | ||
if (!this.hasStorage()) { | ||
LogCodeManager.log('11:03:05'); | ||
return; | ||
} | ||
if (storageKeys) { | ||
for (const storageKey of storageKeys) | ||
(_a = this.getStorage(storageKey)) === null || _a === void 0 ? void 0 : _a.remove(storageItemKey); | ||
return; | ||
} | ||
const defaultStorage = this.getStorage(this.config.defaultStorageKey); | ||
defaultStorage === null || defaultStorage === void 0 ? void 0 : defaultStorage.remove(storageItemKey); | ||
} | ||
hasStorage() { | ||
return notEqual(this.storages, {}); | ||
} | ||
static localStorageAvailable() { | ||
try { | ||
localStorage.setItem('_myDummyKey_', 'myDummyValue'); | ||
localStorage.removeItem('_myDummyKey_'); | ||
return true; | ||
} | ||
catch (e) { | ||
return false; | ||
} | ||
} | ||
import { Storage } from '../internal'; | ||
export * from './storages'; | ||
export function createStorage(config) { | ||
return new Storage(config); | ||
} |
@@ -1,67 +0,1 @@ | ||
import { Agile, Integration } from '../internal'; | ||
export declare class Integrations { | ||
agileInstance: () => Agile; | ||
integrations: Set<Integration>; | ||
static initialIntegrations: Integration[]; | ||
/** | ||
* Registers the specified Integration in each existing or not-yet created Agile Instance. | ||
* | ||
* @public | ||
* @param integration - Integration to be registered in each Agile Instance. | ||
*/ | ||
static addInitialIntegration(integration: Integration): void; | ||
/** | ||
* Fires on each external added Integration. | ||
* | ||
* @public | ||
* @param callback - Callback to be fired when an Integration was externally added. | ||
*/ | ||
static onRegisterInitialIntegration(callback: (integration: Integration) => void): void; | ||
/** | ||
* The Integrations Class manages all Integrations for an Agile Instance | ||
* and provides an interface to easily update | ||
* and invoke functions in all registered Integrations. | ||
* | ||
* @internal | ||
* @param agileInstance - Instance of Agile the Integrations belongs to. | ||
* @param config - Configuration object | ||
*/ | ||
constructor(agileInstance: Agile, config?: IntegrationsConfigInterface); | ||
/** | ||
* Integrates the specified Integration into AgileTs | ||
* and sets it to ready when the binding was successful. | ||
* | ||
* @public | ||
* @param integration - Integration to be integrated into AgileTs. | ||
*/ | ||
integrate(integration: Integration): Promise<boolean>; | ||
/** | ||
* Updates the specified UI-Component Instance | ||
* with the updated data object in all registered Integrations that are ready. | ||
* | ||
* In doing so, it calls the `updateMethod()` method | ||
* in all registered Integrations with the specified parameters. | ||
* | ||
* @public | ||
* @param componentInstance - Component Instance to be updated. | ||
* @param updatedData - Data object with updated data. | ||
*/ | ||
update(componentInstance: any, updatedData: Object): void; | ||
/** | ||
* Returns a boolean indicating whether any Integration | ||
* has been registered with the Agile Instance or not. | ||
* | ||
* @public | ||
*/ | ||
hasIntegration(): boolean; | ||
} | ||
export interface IntegrationsConfigInterface { | ||
/** | ||
* Whether external added Integrations | ||
* are to integrate automatically into the Integrations Class. | ||
* For example, when the package '@agile-ts/react' was installed, | ||
* whether to automatically integrate the 'reactIntegration'. | ||
* @default true | ||
*/ | ||
autoIntegrate?: boolean; | ||
} | ||
export * from './integrations'; |
"use strict"; | ||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { | ||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } | ||
return new (P || (P = Promise))(function (resolve, reject) { | ||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } | ||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } | ||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } | ||
step((generator = generator.apply(thisArg, _arguments || [])).next()); | ||
}); | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); | ||
}) : (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
o[k2] = m[k]; | ||
})); | ||
var __exportStar = (this && this.__exportStar) || function(m, exports) { | ||
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p); | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.Integrations = void 0; | ||
const internal_1 = require("../internal"); | ||
const onRegisterInitialIntegrationCallbacks = []; | ||
class Integrations { | ||
constructor(agileInstance, config = {}) { | ||
this.integrations = new Set(); | ||
config = internal_1.defineConfig(config, { | ||
autoIntegrate: true, | ||
}); | ||
this.agileInstance = () => agileInstance; | ||
if (config.autoIntegrate) { | ||
Integrations.onRegisterInitialIntegration((integration) => { | ||
this.integrate(integration); | ||
}); | ||
} | ||
} | ||
static addInitialIntegration(integration) { | ||
if (integration instanceof internal_1.Integration) { | ||
onRegisterInitialIntegrationCallbacks.forEach((callback) => callback(integration)); | ||
Integrations.initialIntegrations.push(integration); | ||
} | ||
} | ||
static onRegisterInitialIntegration(callback) { | ||
onRegisterInitialIntegrationCallbacks.push(callback); | ||
Integrations.initialIntegrations.forEach((integration) => { | ||
callback(integration); | ||
}); | ||
} | ||
integrate(integration) { | ||
return __awaiter(this, void 0, void 0, function* () { | ||
if (integration._key == null) { | ||
internal_1.LogCodeManager.log('18:03:00', [integration._key, this.agileInstance().key], integration); | ||
return false; | ||
} | ||
if (integration.methods.bind) | ||
integration.ready = yield integration.methods.bind(this.agileInstance()); | ||
else | ||
integration.ready = true; | ||
this.integrations.add(integration); | ||
integration.integrated = true; | ||
internal_1.LogCodeManager.log('18:00:00', [integration._key, this.agileInstance().key], integration); | ||
return true; | ||
}); | ||
} | ||
update(componentInstance, updatedData) { | ||
this.integrations.forEach((integration) => { | ||
if (!integration.ready) { | ||
internal_1.LogCodeManager.log('18:02:00', [integration._key]); | ||
return; | ||
} | ||
if (integration.methods.updateMethod) | ||
integration.methods.updateMethod(componentInstance, updatedData); | ||
}); | ||
} | ||
hasIntegration() { | ||
return this.integrations.size > 0; | ||
} | ||
} | ||
exports.Integrations = Integrations; | ||
Integrations.initialIntegrations = []; | ||
__exportStar(require("./integrations"), exports); |
@@ -1,3 +0,8 @@ | ||
export declare let loggerPackage: any; | ||
declare const logCodeMessages: { | ||
declare const logCodeTypes: { | ||
'00': string; | ||
'01': string; | ||
'02': string; | ||
'03': string; | ||
}; | ||
declare const niceLogCodeMessages: { | ||
'10:00:00': string; | ||
@@ -64,2 +69,3 @@ '10:02:00': string; | ||
}; | ||
declare const logCodeMessages: typeof niceLogCodeMessages; | ||
/** | ||
@@ -97,79 +103,7 @@ * Returns the log message according to the specified log code. | ||
declare function logIfTags<T extends LogCodesArrayType<typeof logCodeMessages>>(tags: string[], logCode: T, replacers?: any[], ...data: any[]): void; | ||
/** | ||
* The Log Code Manager keeps track | ||
* and manages all important Logs of AgileTs. | ||
* | ||
* @internal | ||
*/ | ||
export declare const LogCodeManager: { | ||
getLog: typeof getLog; | ||
log: typeof log; | ||
logCodeLogTypes: { | ||
'00': string; | ||
'01': string; | ||
'02': string; | ||
'03': string; | ||
}; | ||
logCodeMessages: { | ||
'10:00:00': string; | ||
'10:02:00': string; | ||
'11:02:00': string; | ||
'11:02:01': string; | ||
'11:03:00': string; | ||
'11:03:01': string; | ||
'11:03:02': string; | ||
'11:03:03': string; | ||
'11:03:04': string; | ||
'11:03:05': string; | ||
'12:03:00': string; | ||
'12:03:01': string; | ||
'12:03:02': string; | ||
'13:00:00': string; | ||
'13:01:00': string; | ||
'13:01:01': string; | ||
'13:01:02': string; | ||
'13:02:00': string; | ||
'13:03:00': string; | ||
'14:03:01': string; | ||
'14:03:02': string; | ||
'14:03:03': string; | ||
'14:03:04': string; | ||
'15:01:00': string; | ||
'15:01:01': string; | ||
'15:01:02': string; | ||
'15:01:03': string; | ||
'16:01:00': string; | ||
'16:01:01': string; | ||
'16:01:02': string; | ||
'16:02:00': string; | ||
'16:02:01': string; | ||
'17:03:00': string; | ||
'18:00:00': string; | ||
'18:02:00': string; | ||
'18:03:00': string; | ||
'19:03:00': string; | ||
'1A:02:00': string; | ||
'1A:02:01': string; | ||
'1B:02:00': string; | ||
'1B:02:01': string; | ||
'1B:02:02': string; | ||
'1B:02:03': string; | ||
'1B:02:04': string; | ||
'1B:02:05': string; | ||
'1B:03:00': string; | ||
'1B:03:01': string; | ||
'1B:03:02': string; | ||
'1B:03:03': string; | ||
'1B:03:04': string; | ||
'1B:03:05': string; | ||
'1B:03:06': string; | ||
'1C:02:00': string; | ||
'1C:03:00': string; | ||
'1C:03:01': string; | ||
'20:03:00': string; | ||
'20:03:01': string; | ||
'20:03:02': string; | ||
'00:03:00': string; | ||
'00:03:01': string; | ||
}; | ||
logCodeLogTypes: typeof logCodeTypes; | ||
logCodeMessages: typeof logCodeMessages; | ||
getLogger: () => any; | ||
@@ -176,0 +110,0 @@ logIfTags: typeof logIfTags; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.LogCodeManager = exports.loggerPackage = void 0; | ||
exports.loggerPackage = null; | ||
try { | ||
exports.loggerPackage = require('@agile-ts/logger'); | ||
} | ||
catch (e) { | ||
} | ||
exports.LogCodeManager = void 0; | ||
const logCodeTypes = { | ||
@@ -16,3 +10,3 @@ '00': 'success', | ||
}; | ||
const logCodeMessages = { | ||
const niceLogCodeMessages = { | ||
'10:00:00': 'Created new AgileInstance.', | ||
@@ -97,5 +91,9 @@ '10:02:00': 'Be careful when binding multiple Agile Instances globally in one application!', | ||
}; | ||
const logCodeMessages = typeof process === 'object' && process.env.NODE_ENV !== 'production' | ||
? niceLogCodeMessages | ||
: {}; | ||
function getLog(logCode, replacers = []) { | ||
var _a; | ||
let result = (_a = logCodeMessages[logCode]) !== null && _a !== void 0 ? _a : `'${logCode}' is a unknown logCode!`; | ||
let result = logCodeMessages[logCode]; | ||
if (result == null) | ||
return logCode; | ||
for (let i = 0; i < replacers.length; i++) { | ||
@@ -132,12 +130,35 @@ result = result.replace('${' + i + '}', replacers[i]); | ||
} | ||
exports.LogCodeManager = { | ||
getLog, | ||
log, | ||
logCodeLogTypes: logCodeTypes, | ||
logCodeMessages: logCodeMessages, | ||
getLogger: () => { | ||
var _a; | ||
return (_a = exports.loggerPackage === null || exports.loggerPackage === void 0 ? void 0 : exports.loggerPackage.sharedAgileLogger) !== null && _a !== void 0 ? _a : null; | ||
}, | ||
logIfTags, | ||
}; | ||
let tempLogCodeManager; | ||
if (typeof process === 'object' && process.env.NODE_ENV !== 'production') { | ||
tempLogCodeManager = { | ||
getLog, | ||
log, | ||
logCodeLogTypes: logCodeTypes, | ||
logCodeMessages: logCodeMessages, | ||
getLogger: () => { | ||
var _a; | ||
let loggerPackage = null; | ||
try { | ||
loggerPackage = require('@agile-ts/logger'); | ||
} | ||
catch (e) { | ||
} | ||
return (_a = loggerPackage === null || loggerPackage === void 0 ? void 0 : loggerPackage.sharedAgileLogger) !== null && _a !== void 0 ? _a : null; | ||
}, | ||
logIfTags, | ||
}; | ||
} | ||
else { | ||
tempLogCodeManager = { | ||
getLog: (logCode, replacers) => logCode, | ||
log, | ||
logCodeLogTypes: logCodeTypes, | ||
logCodeMessages: logCodeMessages, | ||
getLogger: () => { | ||
return null; | ||
}, | ||
logIfTags: (tags, logCode, replacers) => { | ||
}, | ||
}; | ||
} | ||
exports.LogCodeManager = tempLogCodeManager; |
@@ -1,120 +0,1 @@ | ||
/// <reference types="node" /> | ||
import { Agile, SubscriptionContainer, RuntimeJob } from '../internal'; | ||
export declare class Runtime { | ||
agileInstance: () => Agile; | ||
currentJob: RuntimeJob | null; | ||
jobQueue: Array<RuntimeJob>; | ||
jobsToRerender: Array<RuntimeJob>; | ||
notReadyJobsToRerender: Set<RuntimeJob>; | ||
isPerformingJobs: boolean; | ||
bucketTimeout: NodeJS.Timeout | null; | ||
/** | ||
* The Runtime queues and executes incoming Observer-based Jobs | ||
* to prevent [race conditions](https://en.wikipedia.org/wiki/Race_condition#:~:text=A%20race%20condition%20or%20race,the%20possible%20behaviors%20is%20undesirable.) | ||
* and optimized the re-rendering of the Observer's subscribed UI-Components. | ||
* | ||
* Each queued Job is executed when it is its turn | ||
* by calling the Job Observer's `perform()` method. | ||
* | ||
* After successful execution, the Job is added to a re-render queue, | ||
* which is first put into the browser's 'Bucket' and started to work off | ||
* when resources are left. | ||
* | ||
* The re-render queue is designed for optimizing the render count | ||
* by batching multiple re-render Jobs of the same UI-Component | ||
* and ignoring re-render requests for unmounted UI-Components. | ||
* | ||
* @internal | ||
* @param agileInstance - Instance of Agile the Runtime belongs to. | ||
*/ | ||
constructor(agileInstance: Agile); | ||
/** | ||
* Adds the specified Observer-based Job to the internal Job queue, | ||
* where it is executed when it is its turn. | ||
* | ||
* After successful execution, the Job is assigned to the re-render queue, | ||
* where all the Observer's subscribed Subscription Containers (UI-Components) | ||
* are updated (re-rendered). | ||
* | ||
* @public | ||
* @param job - Job to be added to the Job queue. | ||
* @param config - Configuration object | ||
*/ | ||
ingest(job: RuntimeJob, config?: IngestConfigInterface): void; | ||
/** | ||
* Performs the specified Job | ||
* and assigns it to the re-render queue if necessary. | ||
* | ||
* After the execution of the provided Job, it is checked whether | ||
* there are still Jobs left in the Job queue. | ||
* - If so, the next Job in the `jobQueue` is performed. | ||
* - If not, the `jobsToRerender` queue is started to work off. | ||
* | ||
* @internal | ||
* @param job - Job to be performed. | ||
*/ | ||
perform(job: RuntimeJob): void; | ||
/** | ||
* Processes the `jobsToRerender` queue by updating (causing a re-render on) | ||
* the subscribed Subscription Containers (UI-Components) of each Job Observer. | ||
* | ||
* It returns a boolean indicating whether | ||
* any Subscription Container (UI-Component) was updated (re-rendered) or not. | ||
* | ||
* @internal | ||
*/ | ||
updateSubscribers(): boolean; | ||
/** | ||
* Extracts the Subscription Containers (UI-Components) | ||
* to be updated (re-rendered) from the specified Runtime Jobs. | ||
* | ||
* @internal | ||
* @param jobs - Jobs from which to extract the Subscription Containers to be updated. | ||
*/ | ||
extractToUpdateSubscriptionContainer(jobs: Array<RuntimeJob>): Array<SubscriptionContainer>; | ||
/** | ||
* Updates the specified Subscription Containers. | ||
* | ||
* Updating a Subscription Container triggers a re-render | ||
* on the Component it represents, based on the type of the Subscription Containers. | ||
* | ||
* @internal | ||
* @param subscriptionsToUpdate - Subscription Containers to be updated. | ||
*/ | ||
updateSubscriptionContainer(subscriptionsToUpdate: Array<SubscriptionContainer>): void; | ||
/** | ||
* Maps the values of the updated Observers (`updatedSubscribers`) | ||
* of the specified Subscription Container into a key map object. | ||
* | ||
* The key containing the Observer value is extracted from the Observer itself | ||
* or from the Subscription Container's `subscriberKeysWeakMap`. | ||
* | ||
* @internal | ||
* @param subscriptionContainer - Subscription Container from which the `updatedSubscribers` are to be mapped into a key map. | ||
*/ | ||
getUpdatedObserverValues(subscriptionContainer: SubscriptionContainer): { | ||
[key: string]: any; | ||
}; | ||
/** | ||
* Returns a boolean indicating whether the specified Subscription Container can be updated or not, | ||
* based on its selector functions (`selectorsWeakMap`). | ||
* | ||
* This is done by checking the '.value' and the '.previousValue' property of the Observer represented by the Job. | ||
* If a selected property differs, the Subscription Container (UI-Component) is allowed to update (re-render) | ||
* and `true` is returned. | ||
* | ||
* If the Subscription Container has no selector function at all, `true` is returned. | ||
* | ||
* @internal | ||
* @param subscriptionContainer - Subscription Container to be checked if it can be updated. | ||
* @param job - Job containing the Observer that is subscribed to the Subscription Container. | ||
*/ | ||
handleSelectors(subscriptionContainer: SubscriptionContainer, job: RuntimeJob): boolean; | ||
} | ||
export interface IngestConfigInterface { | ||
/** | ||
* Whether the ingested Job should be performed immediately | ||
* or added to the queue first and then executed when it is his turn. | ||
*/ | ||
perform?: boolean; | ||
} | ||
export * from './runtime'; |
"use strict"; | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); | ||
}) : (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
o[k2] = m[k]; | ||
})); | ||
var __exportStar = (this && this.__exportStar) || function(m, exports) { | ||
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p); | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.Runtime = void 0; | ||
const internal_1 = require("../internal"); | ||
class Runtime { | ||
constructor(agileInstance) { | ||
this.currentJob = null; | ||
this.jobQueue = []; | ||
this.jobsToRerender = []; | ||
this.notReadyJobsToRerender = new Set(); | ||
this.isPerformingJobs = false; | ||
this.bucketTimeout = null; | ||
this.agileInstance = () => agileInstance; | ||
} | ||
ingest(job, config = {}) { | ||
config = internal_1.defineConfig(config, { | ||
perform: !this.isPerformingJobs, | ||
}); | ||
this.jobQueue.push(job); | ||
internal_1.LogCodeManager.logIfTags(['runtime'], '16:01:00', [job._key], job); | ||
if (config.perform) { | ||
const performJob = this.jobQueue.shift(); | ||
if (performJob) | ||
this.perform(performJob); | ||
} | ||
} | ||
perform(job) { | ||
this.isPerformingJobs = true; | ||
this.currentJob = job; | ||
job.observer.perform(job); | ||
job.performed = true; | ||
job.observer.dependents.forEach((observer) => observer.ingest()); | ||
if (job.rerender) | ||
this.jobsToRerender.push(job); | ||
this.currentJob = null; | ||
internal_1.LogCodeManager.logIfTags(['runtime'], '16:01:01', [job._key], job); | ||
if (this.jobQueue.length > 0) { | ||
const performJob = this.jobQueue.shift(); | ||
if (performJob) | ||
this.perform(performJob); | ||
} | ||
else { | ||
this.isPerformingJobs = false; | ||
if (this.jobsToRerender.length > 0) { | ||
if (this.agileInstance().config.bucket) { | ||
if (this.bucketTimeout == null) { | ||
this.bucketTimeout = setTimeout(() => { | ||
this.bucketTimeout = null; | ||
this.updateSubscribers(); | ||
}); | ||
} | ||
} | ||
else | ||
this.updateSubscribers(); | ||
} | ||
} | ||
} | ||
updateSubscribers() { | ||
const jobsToRerender = this.jobsToRerender.concat(Array.from(this.notReadyJobsToRerender)); | ||
this.notReadyJobsToRerender = new Set(); | ||
this.jobsToRerender = []; | ||
if (!this.agileInstance().hasIntegration() || jobsToRerender.length <= 0) | ||
return false; | ||
const subscriptionContainerToUpdate = this.extractToUpdateSubscriptionContainer(jobsToRerender); | ||
if (subscriptionContainerToUpdate.length <= 0) | ||
return false; | ||
this.updateSubscriptionContainer(subscriptionContainerToUpdate); | ||
return true; | ||
} | ||
extractToUpdateSubscriptionContainer(jobs) { | ||
const subscriptionsToUpdate = new Set(); | ||
for (let i = 0; i < jobs.length; i++) { | ||
const job = jobs[i]; | ||
job.subscriptionContainersToUpdate.forEach((subscriptionContainer) => { | ||
let updateSubscriptionContainer = true; | ||
if (!subscriptionContainer.ready) { | ||
if (!job.config.maxTriesToUpdate || | ||
job.timesTriedToUpdateCount < job.config.maxTriesToUpdate) { | ||
job.timesTriedToUpdateCount++; | ||
this.notReadyJobsToRerender.add(job); | ||
internal_1.LogCodeManager.log('16:02:00', [subscriptionContainer.key], subscriptionContainer); | ||
} | ||
else { | ||
internal_1.LogCodeManager.log('16:02:01', [job.config.maxTriesToUpdate], subscriptionContainer); | ||
} | ||
return; | ||
} | ||
updateSubscriptionContainer = | ||
updateSubscriptionContainer && | ||
this.handleSelectors(subscriptionContainer, job); | ||
if (updateSubscriptionContainer) { | ||
subscriptionContainer.updatedSubscribers.add(job.observer); | ||
subscriptionsToUpdate.add(subscriptionContainer); | ||
} | ||
job.subscriptionContainersToUpdate.delete(subscriptionContainer); | ||
}); | ||
} | ||
return Array.from(subscriptionsToUpdate); | ||
} | ||
updateSubscriptionContainer(subscriptionsToUpdate) { | ||
for (let i = 0; i < subscriptionsToUpdate.length; i++) { | ||
const subscriptionContainer = subscriptionsToUpdate[i]; | ||
if (subscriptionContainer instanceof internal_1.CallbackSubscriptionContainer) | ||
subscriptionContainer.callback(); | ||
if (subscriptionContainer instanceof internal_1.ComponentSubscriptionContainer) | ||
this.agileInstance().integrations.update(subscriptionContainer.component, this.getUpdatedObserverValues(subscriptionContainer)); | ||
subscriptionContainer.updatedSubscribers.clear(); | ||
} | ||
internal_1.LogCodeManager.logIfTags(['runtime'], '16:01:02', [], subscriptionsToUpdate); | ||
} | ||
getUpdatedObserverValues(subscriptionContainer) { | ||
var _a; | ||
const props = {}; | ||
for (const observer of subscriptionContainer.updatedSubscribers) { | ||
const key = (_a = subscriptionContainer.subscriberKeysWeakMap.get(observer)) !== null && _a !== void 0 ? _a : observer.key; | ||
if (key != null) | ||
props[key] = observer.value; | ||
} | ||
return props; | ||
} | ||
handleSelectors(subscriptionContainer, job) { | ||
var _a; | ||
const selectorMethods = (_a = subscriptionContainer.selectorsWeakMap.get(job.observer)) === null || _a === void 0 ? void 0 : _a.methods; | ||
if (selectorMethods == null) | ||
return true; | ||
const previousValue = job.observer.previousValue; | ||
const newValue = job.observer.value; | ||
for (const selectorMethod of selectorMethods) { | ||
if (internal_1.notEqual(selectorMethod(newValue), selectorMethod(previousValue))) | ||
return true; | ||
} | ||
return false; | ||
} | ||
} | ||
exports.Runtime = Runtime; | ||
__exportStar(require("./runtime"), exports); |
@@ -1,2 +0,2 @@ | ||
import { Agile, Collection, CollectionConfig, Computed, ComputeFunctionType, CreateComputedConfigInterface, CreateStorageConfigInterface, DefaultItem, DependableAgileInstancesType, State, StateConfigInterface, Storage } from './internal'; | ||
import { Agile } from './internal'; | ||
/** | ||
@@ -13,96 +13,2 @@ * Shared Agile Instance that is used when no Agile Instance was specified. | ||
export declare function assignSharedAgileInstance(agileInstance: Agile): void; | ||
/** | ||
* Returns a newly created Storage. | ||
* | ||
* A Storage Class serves as an interface to external storages, | ||
* such as the [Async Storage](https://github.com/react-native-async-storage/async-storage) or | ||
* [Local Storage](https://www.w3schools.com/html/html5_webstorage.asp). | ||
* | ||
* It creates the foundation to easily [`persist()`](https://agile-ts.org/docs/core/state/methods#persist) [Agile Sub Instances](https://agile-ts.org/docs/introduction/#agile-sub-instance) | ||
* (like States or Collections) in nearly any external storage. | ||
* | ||
* [Learn more..](https://agile-ts.org/docs/core/agile-instance/methods#createstorage) | ||
* | ||
* @public | ||
* @param config - Configuration object | ||
*/ | ||
export declare function createStorage(config: CreateStorageConfigInterface): Storage; | ||
/** | ||
* Returns a newly created State. | ||
* | ||
* A State manages a piece of Information | ||
* that we need to remember globally at a later point in time. | ||
* While providing a toolkit to use and mutate this piece of Information. | ||
* | ||
* You can create as many global States as you need. | ||
* | ||
* [Learn more..](https://agile-ts.org/docs/core/agile-instance/methods#createstate) | ||
* | ||
* @public | ||
* @param initialValue - Initial value of the State. | ||
* @param config - Configuration object | ||
*/ | ||
export declare function createState<ValueType = any>(initialValue: ValueType, config?: CreateStateConfigInterfaceWithAgile): State<ValueType>; | ||
/** | ||
* Returns a newly created Computed. | ||
* | ||
* A Computed is an extension of the State Class | ||
* that computes its value based on a specified compute function. | ||
* | ||
* The computed value will be cached to avoid unnecessary recomputes | ||
* and is only recomputed when one of its direct dependencies changes. | ||
* | ||
* Direct dependencies can be States and Collections. | ||
* So when, for example, a dependent State value changes, the computed value is recomputed. | ||
* | ||
* [Learn more..](https://agile-ts.org/docs/core/agile-instance/methods#createstate) | ||
* | ||
* @public | ||
* @param computeFunction - Function to compute the computed value. | ||
* @param config - Configuration object | ||
*/ | ||
export declare function createComputed<ComputedValueType = any>(computeFunction: ComputeFunctionType<ComputedValueType>, config?: CreateComputedConfigInterfaceWithAgile): Computed<ComputedValueType>; | ||
/** | ||
* Returns a newly created Computed. | ||
* | ||
* A Computed is an extension of the State Class | ||
* that computes its value based on a specified compute function. | ||
* | ||
* The computed value will be cached to avoid unnecessary recomputes | ||
* and is only recomputed when one of its direct dependencies changes. | ||
* | ||
* Direct dependencies can be States and Collections. | ||
* So when, for example, a dependent State value changes, the computed value is recomputed. | ||
* | ||
* [Learn more..](https://agile-ts.org/docs/core/agile-instance/methods#createcomputed) | ||
* | ||
* @public | ||
* @param computeFunction - Function to compute the computed value. | ||
* @param deps - Hard-coded dependencies on which the Computed Class should depend. | ||
*/ | ||
export declare function createComputed<ComputedValueType = any>(computeFunction: ComputeFunctionType<ComputedValueType>, deps?: Array<DependableAgileInstancesType>): Computed<ComputedValueType>; | ||
/** | ||
* Returns a newly created Collection. | ||
* | ||
* A Collection manages a reactive set of Information | ||
* that we need to remember globally at a later point in time. | ||
* While providing a toolkit to use and mutate this set of Information. | ||
* | ||
* It is designed for arrays of data objects following the same pattern. | ||
* | ||
* Each of these data object must have a unique `primaryKey` to be correctly identified later. | ||
* | ||
* You can create as many global Collections as you need. | ||
* | ||
* [Learn more..](https://agile-ts.org/docs/core/agile-instance/methods#createcollection) | ||
* | ||
* @public | ||
* @param config - Configuration object | ||
* @param agileInstance - Instance of Agile the Collection belongs to. | ||
*/ | ||
export declare function createCollection<DataType extends Object = DefaultItem>(config?: CollectionConfig<DataType>, agileInstance?: Agile): Collection<DataType>; | ||
export interface CreateComputedConfigInterfaceWithAgile extends CreateAgileSubInstanceInterface, CreateComputedConfigInterface { | ||
} | ||
export interface CreateStateConfigInterfaceWithAgile extends CreateAgileSubInstanceInterface, StateConfigInterface { | ||
} | ||
export interface CreateAgileSubInstanceInterface { | ||
@@ -109,0 +15,0 @@ /** |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.createCollection = exports.createComputed = exports.createState = exports.createStorage = exports.assignSharedAgileInstance = exports.shared = void 0; | ||
exports.assignSharedAgileInstance = exports.shared = void 0; | ||
const internal_1 = require("./internal"); | ||
@@ -14,31 +14,1 @@ let sharedAgileInstance = new internal_1.Agile({ | ||
exports.assignSharedAgileInstance = assignSharedAgileInstance; | ||
function createStorage(config) { | ||
return new internal_1.Storage(config); | ||
} | ||
exports.createStorage = createStorage; | ||
function createState(initialValue, config = {}) { | ||
config = internal_1.defineConfig(config, { | ||
agileInstance: sharedAgileInstance, | ||
}); | ||
return new internal_1.State(config.agileInstance, initialValue, internal_1.removeProperties(config, ['agileInstance'])); | ||
} | ||
exports.createState = createState; | ||
function createComputed(computeFunction, configOrDeps) { | ||
let _config = {}; | ||
if (Array.isArray(configOrDeps)) { | ||
_config = internal_1.defineConfig(_config, { | ||
computedDeps: configOrDeps, | ||
}); | ||
} | ||
else { | ||
if (configOrDeps) | ||
_config = configOrDeps; | ||
} | ||
_config = internal_1.defineConfig(_config, { agileInstance: sharedAgileInstance }); | ||
return new internal_1.Computed(_config.agileInstance, computeFunction, internal_1.removeProperties(_config, ['agileInstance'])); | ||
} | ||
exports.createComputed = createComputed; | ||
function createCollection(config, agileInstance = sharedAgileInstance) { | ||
return new internal_1.Collection(agileInstance, config); | ||
} | ||
exports.createCollection = createCollection; |
@@ -1,429 +0,20 @@ | ||
/// <reference types="node" /> | ||
import { Agile, StorageKey, StateObserver, StatePersistent, Observer, PersistentKey, StateIngestConfigInterface } from '../internal'; | ||
export declare class State<ValueType = any> { | ||
agileInstance: () => Agile; | ||
_key?: StateKey; | ||
isSet: boolean; | ||
isPlaceholder: boolean; | ||
initialStateValue: ValueType; | ||
_value: ValueType; | ||
previousStateValue: ValueType; | ||
nextStateValue: ValueType; | ||
observers: StateObserversInterface<ValueType>; | ||
sideEffects: { | ||
[key: string]: SideEffectInterface<State<ValueType>>; | ||
}; | ||
computeValueMethod?: ComputeValueMethod<ValueType>; | ||
computeExistsMethod: ComputeExistsMethod<ValueType>; | ||
isPersisted: boolean; | ||
persistent: StatePersistent | undefined; | ||
currentInterval?: NodeJS.Timer | number; | ||
/** | ||
* A State manages a piece of Information | ||
* that we need to remember globally at a later point in time. | ||
* While providing a toolkit to use and mutate this piece of Information. | ||
* | ||
* You can create as many global States as you need. | ||
* | ||
* [Learn more..](https://agile-ts.org/docs/core/state/) | ||
* | ||
* @public | ||
* @param agileInstance - Instance of Agile the State belongs to. | ||
* @param initialValue - Initial value of the State. | ||
* @param config - Configuration object | ||
*/ | ||
constructor(agileInstance: Agile, initialValue: ValueType, config?: StateConfigInterface); | ||
/** | ||
* Assigns a new value to the State | ||
* and rerenders all subscribed Components. | ||
* | ||
* [Learn more..](https://agile-ts.org/docs/core/state/properties#value) | ||
* | ||
* @public | ||
* @param value - New State value. | ||
*/ | ||
set value(value: ValueType); | ||
/** | ||
* Returns a reference-free version of the current State value. | ||
* | ||
* [Learn more..](https://agile-ts.org/docs/core/state/properties#value) | ||
* | ||
* @public | ||
*/ | ||
get value(): ValueType; | ||
/** | ||
* Updates the key/name identifier of the State. | ||
* | ||
* [Learn more..](https://agile-ts.org/docs/core/state/properties#key) | ||
* | ||
* @public | ||
* @param value - New key/name identifier. | ||
*/ | ||
set key(value: StateKey | undefined); | ||
/** | ||
* Returns the key/name identifier of the State. | ||
* | ||
* [Learn more..](https://agile-ts.org/docs/core/state/properties#key) | ||
* | ||
* @public | ||
*/ | ||
get key(): StateKey | undefined; | ||
/** | ||
* Updates the key/name identifier of the State. | ||
* | ||
* [Learn more..](https://agile-ts.org/docs/core/state/methods/#setkey) | ||
* | ||
* @public | ||
* @param value - New key/name identifier. | ||
*/ | ||
setKey(value: StateKey | undefined): this; | ||
/** | ||
* Assigns a new value to the State | ||
* and re-renders all subscribed UI-Components. | ||
* | ||
* [Learn more..](https://agile-ts.org/docs/core/state/methods/#set) | ||
* | ||
* @public | ||
* @param value - New State value | ||
* @param config - Configuration object | ||
*/ | ||
set(value: ValueType | ((value: ValueType) => ValueType), config?: StateIngestConfigInterface): this; | ||
/** | ||
* Ingests the State without any specified new value into the runtime. | ||
* | ||
* Since no new value was defined either the new State value is computed | ||
* based on a compute method (Computed Class) | ||
* or the `nextStateValue` is taken as the next State value. | ||
* | ||
* [Learn more..](https://agile-ts.org/docs/core/state/methods/#ingest) | ||
* | ||
* @internal | ||
* @param config - Configuration object | ||
*/ | ||
ingest(config?: StateIngestConfigInterface): this; | ||
/** | ||
* Undoes the latest State value change. | ||
* | ||
* [Learn more..](https://agile-ts.org/docs/core/state/methods/#undo) | ||
* | ||
* @public | ||
* @param config - Configuration object | ||
*/ | ||
undo(config?: StateIngestConfigInterface): this; | ||
/** | ||
* Resets the State value to its initial value. | ||
* | ||
* [Learn more..](https://agile-ts.org/docs/core/state/methods/#reset) | ||
* | ||
* @public | ||
* @param config - Configuration object | ||
*/ | ||
reset(config?: StateIngestConfigInterface): this; | ||
/** | ||
* Merges the specified `targetWithChanges` object into the current State value. | ||
* This merge can differ for different value combinations: | ||
* - If the current State value is an `object`, it does a partial update for the object. | ||
* - If the current State value is an `array` and the specified argument is an array too, | ||
* it concatenates the current State value with the value of the argument. | ||
* - If the current State value is neither an `object` nor an `array`, the patch can't be performed. | ||
* | ||
* [Learn more..](https://agile-ts.org/docs/core/state/methods/#patch) | ||
* | ||
* @public | ||
* @param targetWithChanges - Object to be merged into the current State value. | ||
* @param config - Configuration object | ||
*/ | ||
patch(targetWithChanges: Object, config?: PatchConfigInterface): this; | ||
/** | ||
* Fires on each State value change. | ||
* | ||
* Returns the key/name identifier of the created watcher callback. | ||
* | ||
* [Learn more..](https://agile-ts.org/docs/core/state/methods/#watch) | ||
* | ||
* @public | ||
* @param callback - A function to be executed on each State value change. | ||
*/ | ||
watch(callback: StateWatcherCallback<ValueType>): string; | ||
/** | ||
* Fires on each State value change. | ||
* | ||
* [Learn more..](https://agile-ts.org/docs/core/state/methods/#watch) | ||
* | ||
* @public | ||
* @param key - Key/Name identifier of the watcher callback. | ||
* @param callback - A function to be executed on each State value change. | ||
*/ | ||
watch(key: string, callback: StateWatcherCallback<ValueType>): this; | ||
/** | ||
* Removes a watcher callback with the specified key/name identifier from the State. | ||
* | ||
* [Learn more..](https://agile-ts.org/docs/core/state/methods/#removewatcher) | ||
* | ||
* @public | ||
* @param key - Key/Name identifier of the watcher callback to be removed. | ||
*/ | ||
removeWatcher(key: string): this; | ||
/** | ||
* Fires on the initial State value assignment and then destroys itself. | ||
* | ||
* [Learn more..](https://agile-ts.org/docs/core/state/methods/#oninaugurated) | ||
* | ||
* @public | ||
* @param callback - A function to be executed after the first State value assignment. | ||
*/ | ||
onInaugurated(callback: StateWatcherCallback<ValueType>): this; | ||
/** | ||
* Preserves the State `value` in the corresponding external Storage. | ||
* | ||
* The State key/name is used as the unique identifier for the Persistent. | ||
* If that is not desired or the State has no unique identifier, | ||
* please specify a separate unique identifier for the Persistent. | ||
* | ||
* [Learn more..](https://agile-ts.org/docs/core/state/methods/#persist) | ||
* | ||
* @public | ||
* @param config - Configuration object | ||
*/ | ||
persist(config?: StatePersistentConfigInterface): this; | ||
/** | ||
* Preserves the State `value` in the corresponding external Storage. | ||
* | ||
* The specified key is used as the unique identifier for the Persistent. | ||
* | ||
* [Learn more..](https://agile-ts.org/docs/core/state/methods/#persist) | ||
* | ||
* @public | ||
* @param key - Key/Name identifier of Persistent. | ||
* @param config - Configuration object | ||
*/ | ||
persist(key?: PersistentKey, config?: StatePersistentConfigInterface): this; | ||
/** | ||
* Fires immediately after the persisted `value` | ||
* is loaded into the State from a corresponding external Storage. | ||
* | ||
* Registering such callback function makes only sense | ||
* when the State is [persisted](https://agile-ts.org/docs/core/state/methods/#persist). | ||
* | ||
* [Learn more..](https://agile-ts.org/docs/core/state/methods/#onload) | ||
* | ||
* @public | ||
* @param callback - A function to be executed after the externally persisted `value` was loaded into the State. | ||
*/ | ||
onLoad(callback: (success: boolean) => void): this; | ||
/** | ||
* Repeatedly calls the specified callback function, | ||
* with a fixed time delay between each call. | ||
* | ||
* [Learn more..](https://agile-ts.org/docs/core/state/methods/#interval) | ||
* | ||
* @public | ||
* @param handler - A function to be executed every delay milliseconds. | ||
* @param delay - The time, in milliseconds (thousandths of a second), | ||
* the timer should delay in between executions of the specified function. | ||
*/ | ||
interval(handler: (value: ValueType) => ValueType, delay?: number): this; | ||
/** | ||
* Cancels a active timed, repeating action | ||
* which was previously established by a call to `interval()`. | ||
* | ||
* [Learn more..](https://agile-ts.org/docs/core/state/methods/#clearinterval) | ||
* | ||
* @public | ||
*/ | ||
clearInterval(): void; | ||
/** | ||
* Returns a boolean indicating whether the State exists. | ||
* | ||
* It calculates the value based on the `computeExistsMethod()` | ||
* and whether the State is a placeholder. | ||
* | ||
* [Learn more..](https://agile-ts.org/docs/core/state/methods/#exists) | ||
* | ||
* @public | ||
*/ | ||
get exists(): boolean; | ||
/** | ||
* Defines the method used to compute the existence of the State. | ||
* | ||
* It is retrieved on each `exists()` method call | ||
* to determine whether the State exists or not. | ||
* | ||
* [Learn more..](https://agile-ts.org/docs/core/state/methods/#computeexists) | ||
* | ||
* @public | ||
* @param method - Method to compute the existence of the State. | ||
*/ | ||
computeExists(method: ComputeExistsMethod<ValueType>): this; | ||
/** | ||
* Defines the method used to compute the value of the State. | ||
* | ||
* It is retrieved on each State value change, | ||
* in order to compute the new State value | ||
* based on the specified compute method. | ||
* | ||
* [Learn more..](https://agile-ts.org/docs/core/state/methods/#computevalue) | ||
* | ||
* @public | ||
* @param method - Method to compute the value of the State. | ||
*/ | ||
computeValue(method: ComputeValueMethod<ValueType>): this; | ||
/** | ||
* Returns a boolean indicating whether the specified value is equal to the current State value. | ||
* | ||
* Equivalent to `===` with the difference that it looks at the value | ||
* and not on the reference in the case of objects. | ||
* | ||
* @public | ||
* @param value - Value to be compared with the current State value. | ||
*/ | ||
is(value: ValueType): boolean; | ||
/** | ||
* Returns a boolean indicating whether the specified value is not equal to the current State value. | ||
* | ||
* Equivalent to `!==` with the difference that it looks at the value | ||
* and not on the reference in the case of objects. | ||
* | ||
* @public | ||
* @param value - Value to be compared with the current State value. | ||
*/ | ||
isNot(value: ValueType): boolean; | ||
/** | ||
* Inverts the current State value. | ||
* | ||
* Some examples are: | ||
* - `'jeff'` -> `'ffej'` | ||
* - `true` -> `false` | ||
* - `[1, 2, 3]` -> `[3, 2, 1]` | ||
* - `10` -> `-10` | ||
* | ||
* @public | ||
*/ | ||
invert(): this; | ||
/** | ||
* | ||
* Registers a `callback` function that is executed in the `runtime` | ||
* as a side effect of State changes. | ||
* | ||
* For example, it is called when the State value changes from 'jeff' to 'hans'. | ||
* | ||
* A typical side effect of a State change | ||
* could be the updating of the external Storage value. | ||
* | ||
* @internal | ||
* @param key - Key/Name identifier of the to register side effect. | ||
* @param callback - Callback function to be fired on each State value change. | ||
* @param config - Configuration object | ||
*/ | ||
addSideEffect<Instance extends State<ValueType>>(key: string, callback: SideEffectFunctionType<Instance>, config?: AddSideEffectConfigInterface): this; | ||
/** | ||
* Removes a side effect callback with the specified key/name identifier from the State. | ||
* | ||
* [Learn more..](https://agile-ts.org/docs/core/state/methods/#removesideeffect) | ||
* | ||
* @internal | ||
* @param key - Key/Name identifier of the side effect callback to be removed. | ||
*/ | ||
removeSideEffect(key: string): this; | ||
/** | ||
* Returns a boolean indicating whether a side effect callback with the specified `key` | ||
* exists in the State or not. | ||
* | ||
* [Learn more..](https://agile-ts.org/docs/core/state/methods/#hassideeffect) | ||
* | ||
* @internal | ||
* @param key - Key/Name identifier of the side effect callback to be checked for existence. | ||
*/ | ||
hasSideEffect(key: string): boolean; | ||
/** | ||
* Returns the persistable value of the State. | ||
* | ||
* @internal | ||
*/ | ||
getPersistableValue(): any; | ||
import { State, StateConfigInterface, CreateAgileSubInstanceInterface } from '../internal'; | ||
export * from './state'; | ||
export interface CreateStateConfigInterfaceWithAgile extends CreateAgileSubInstanceInterface, StateConfigInterface { | ||
} | ||
export declare type StateKey = string | number; | ||
export interface StateObserversInterface<ValueType = any> { | ||
/** | ||
* Observer responsible for the value of the State. | ||
*/ | ||
value: StateObserver<ValueType>; | ||
} | ||
export interface StateConfigInterface { | ||
/** | ||
* Key/Name identifier of the State. | ||
* @default undefined | ||
*/ | ||
key?: StateKey; | ||
/** | ||
* Observers that depend on the State. | ||
* @default [] | ||
*/ | ||
dependents?: Array<Observer>; | ||
/** | ||
* Whether the State should be a placeholder | ||
* and therefore should only exist in the background. | ||
* @default false | ||
*/ | ||
isPlaceholder?: boolean; | ||
} | ||
export interface PatchConfigInterface extends StateIngestConfigInterface, PatchOptionConfigInterface { | ||
} | ||
export interface PatchOptionConfigInterface { | ||
/** | ||
* Whether to add new properties to the object during the merge. | ||
* @default true | ||
*/ | ||
addNewProperties?: boolean; | ||
} | ||
export interface StatePersistentConfigInterface { | ||
/** | ||
* Whether the Persistent should automatically load | ||
* the persisted value into the State after its instantiation. | ||
* @default true | ||
*/ | ||
loadValue?: boolean; | ||
/** | ||
* Key/Name identifier of Storages | ||
* in which the State value should be or is persisted. | ||
* @default [`defaultStorageKey`] | ||
*/ | ||
storageKeys?: StorageKey[]; | ||
/** | ||
* Key/Name identifier of the default Storage of the specified Storage keys. | ||
* | ||
* The State value is loaded from the default Storage by default | ||
* and is only loaded from the remaining Storages (`storageKeys`) | ||
* if the loading from the default Storage failed. | ||
* | ||
* @default first index of the specified Storage keys or the AgileTs default Storage key | ||
*/ | ||
defaultStorageKey?: StorageKey; | ||
} | ||
export declare type StateWatcherCallback<T = any> = (value: T, key: string) => void; | ||
export declare type ComputeValueMethod<T = any> = (value: T) => T; | ||
export declare type ComputeExistsMethod<T = any> = (value: T) => boolean; | ||
export declare type SideEffectFunctionType<Instance extends State> = (instance: Instance, properties?: { | ||
[key: string]: any; | ||
}) => void; | ||
export interface SideEffectInterface<Instance extends State> { | ||
/** | ||
* Callback function to be called on every State value change. | ||
* @return () => {} | ||
*/ | ||
callback: SideEffectFunctionType<Instance>; | ||
/** | ||
* Weight of the side effect. | ||
* The weight determines the order of execution of the registered side effects. | ||
* The higher the weight, the earlier it is executed. | ||
*/ | ||
weight: number; | ||
} | ||
export interface AddSideEffectConfigInterface { | ||
/** | ||
* Weight of the side effect. | ||
* The weight determines the order of execution of the registered side effects. | ||
* The higher the weight, the earlier it is executed. | ||
*/ | ||
weight?: number; | ||
} | ||
/** | ||
* Returns a newly created State. | ||
* | ||
* A State manages a piece of Information | ||
* that we need to remember globally at a later point in time. | ||
* While providing a toolkit to use and mutate this piece of Information. | ||
* | ||
* You can create as many global States as you need. | ||
* | ||
* [Learn more..](https://agile-ts.org/docs/core/agile-instance/methods#createstate) | ||
* | ||
* @public | ||
* @param initialValue - Initial value of the State. | ||
* @param config - Configuration object | ||
*/ | ||
export declare function createState<ValueType = any>(initialValue: ValueType, config?: CreateStateConfigInterfaceWithAgile): State<ValueType>; |
"use strict"; | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); | ||
}) : (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
o[k2] = m[k]; | ||
})); | ||
var __exportStar = (this && this.__exportStar) || function(m, exports) { | ||
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p); | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.State = void 0; | ||
exports.createState = void 0; | ||
const internal_1 = require("../internal"); | ||
class State { | ||
constructor(agileInstance, initialValue, config = {}) { | ||
this.isSet = false; | ||
this.isPlaceholder = false; | ||
this.observers = {}; | ||
this.sideEffects = {}; | ||
this.isPersisted = false; | ||
config = internal_1.defineConfig(config, { | ||
dependents: [], | ||
isPlaceholder: false, | ||
}); | ||
this.agileInstance = () => agileInstance; | ||
this._key = config.key; | ||
this.observers['value'] = new internal_1.StateObserver(this, { | ||
key: config.key, | ||
dependents: config.dependents, | ||
}); | ||
this.initialStateValue = internal_1.copy(initialValue); | ||
this._value = internal_1.copy(initialValue); | ||
this.previousStateValue = internal_1.copy(initialValue); | ||
this.nextStateValue = internal_1.copy(initialValue); | ||
this.isPlaceholder = true; | ||
this.computeExistsMethod = (v) => { | ||
return v != null; | ||
}; | ||
if (!config.isPlaceholder) | ||
this.set(initialValue, { overwrite: true }); | ||
} | ||
set value(value) { | ||
this.set(value); | ||
} | ||
get value() { | ||
internal_1.ComputedTracker.tracked(this.observers['value']); | ||
return internal_1.copy(this._value); | ||
} | ||
set key(value) { | ||
this.setKey(value); | ||
} | ||
get key() { | ||
return this._key; | ||
} | ||
setKey(value) { | ||
var _a, _b; | ||
const oldKey = this._key; | ||
this._key = value; | ||
for (const observerKey in this.observers) | ||
this.observers[observerKey]._key = value; | ||
if (value != null && ((_a = this.persistent) === null || _a === void 0 ? void 0 : _a._key) === oldKey) | ||
(_b = this.persistent) === null || _b === void 0 ? void 0 : _b.setKey(value); | ||
return this; | ||
} | ||
set(value, config = {}) { | ||
config = internal_1.defineConfig(config, { | ||
force: false, | ||
}); | ||
const _value = internal_1.isFunction(value) | ||
? value(internal_1.copy(this._value)) | ||
: value; | ||
this.observers['value'].ingestValue(_value, config); | ||
return this; | ||
} | ||
ingest(config = {}) { | ||
this.observers['value'].ingest(config); | ||
return this; | ||
} | ||
undo(config = {}) { | ||
this.set(this.previousStateValue, config); | ||
return this; | ||
} | ||
reset(config = {}) { | ||
this.set(this.initialStateValue, config); | ||
return this; | ||
} | ||
patch(targetWithChanges, config = {}) { | ||
config = internal_1.defineConfig(config, { | ||
addNewProperties: true, | ||
}); | ||
if (!internal_1.isValidObject(this.nextStateValue, true)) { | ||
internal_1.LogCodeManager.log('14:03:02'); | ||
return this; | ||
} | ||
if (!internal_1.isValidObject(targetWithChanges, true)) { | ||
internal_1.LogCodeManager.log('00:03:01', ['TargetWithChanges', 'object']); | ||
return this; | ||
} | ||
if (Array.isArray(targetWithChanges) && | ||
Array.isArray(this.nextStateValue)) { | ||
this.nextStateValue = [ | ||
...this.nextStateValue, | ||
...targetWithChanges, | ||
]; | ||
} | ||
else { | ||
this.nextStateValue = internal_1.flatMerge(this.nextStateValue, targetWithChanges, { addNewProperties: config.addNewProperties }); | ||
} | ||
this.ingest(internal_1.removeProperties(config, ['addNewProperties'])); | ||
return this; | ||
} | ||
watch(keyOrCallback, callback) { | ||
const generateKey = internal_1.isFunction(keyOrCallback); | ||
let _callback; | ||
let key; | ||
if (generateKey) { | ||
key = internal_1.generateId(); | ||
_callback = keyOrCallback; | ||
} | ||
else { | ||
key = keyOrCallback; | ||
_callback = callback; | ||
} | ||
if (!internal_1.isFunction(_callback)) { | ||
internal_1.LogCodeManager.log('00:03:01', ['Watcher Callback', 'function']); | ||
return this; | ||
} | ||
this.addSideEffect(key, (instance) => { | ||
_callback(instance.value, key); | ||
}, { weight: 0 }); | ||
return generateKey ? key : this; | ||
} | ||
removeWatcher(key) { | ||
this.removeSideEffect(key); | ||
return this; | ||
} | ||
onInaugurated(callback) { | ||
const watcherKey = 'InauguratedWatcherKey'; | ||
this.watch(watcherKey, (value, key) => { | ||
callback(value, key); | ||
this.removeSideEffect(watcherKey); | ||
}); | ||
return this; | ||
} | ||
persist(keyOrConfig = {}, config = {}) { | ||
let _config; | ||
let key; | ||
if (internal_1.isValidObject(keyOrConfig)) { | ||
_config = keyOrConfig; | ||
key = this._key; | ||
} | ||
else { | ||
_config = config || {}; | ||
key = keyOrConfig; | ||
} | ||
_config = internal_1.defineConfig(_config, { | ||
loadValue: true, | ||
storageKeys: [], | ||
defaultStorageKey: null, | ||
}); | ||
if (this.persistent != null && this.isPersisted) | ||
return this; | ||
this.persistent = new internal_1.StatePersistent(this, { | ||
instantiate: _config.loadValue, | ||
storageKeys: _config.storageKeys, | ||
key: key, | ||
defaultStorageKey: _config.defaultStorageKey, | ||
}); | ||
return this; | ||
} | ||
onLoad(callback) { | ||
if (!this.persistent) | ||
return this; | ||
if (!internal_1.isFunction(callback)) { | ||
internal_1.LogCodeManager.log('00:03:01', ['OnLoad Callback', 'function']); | ||
return this; | ||
} | ||
this.persistent.onLoad = callback; | ||
if (this.isPersisted) | ||
callback(true); | ||
return this; | ||
} | ||
interval(handler, delay) { | ||
if (!internal_1.isFunction(handler)) { | ||
internal_1.LogCodeManager.log('00:03:01', ['Interval Callback', 'function']); | ||
return this; | ||
} | ||
if (this.currentInterval) { | ||
internal_1.LogCodeManager.log('14:03:03', [], this.currentInterval); | ||
return this; | ||
} | ||
this.currentInterval = setInterval(() => { | ||
this.set(handler(this._value)); | ||
}, delay !== null && delay !== void 0 ? delay : 1000); | ||
return this; | ||
} | ||
clearInterval() { | ||
if (this.currentInterval) { | ||
clearInterval(this.currentInterval); | ||
delete this.currentInterval; | ||
} | ||
} | ||
get exists() { | ||
return !this.isPlaceholder && this.computeExistsMethod(this.value); | ||
} | ||
computeExists(method) { | ||
if (!internal_1.isFunction(method)) { | ||
internal_1.LogCodeManager.log('00:03:01', ['Compute Exists Method', 'function']); | ||
return this; | ||
} | ||
this.computeExistsMethod = method; | ||
return this; | ||
} | ||
computeValue(method) { | ||
if (!internal_1.isFunction(method)) { | ||
internal_1.LogCodeManager.log('00:03:01', ['Compute Value Method', 'function']); | ||
return this; | ||
} | ||
this.computeValueMethod = method; | ||
this.set(this.nextStateValue); | ||
return this; | ||
} | ||
is(value) { | ||
return internal_1.equal(value, this.value); | ||
} | ||
isNot(value) { | ||
return internal_1.notEqual(value, this.value); | ||
} | ||
invert() { | ||
switch (typeof this.nextStateValue) { | ||
case 'boolean': | ||
this.set(!this.nextStateValue); | ||
break; | ||
case 'object': | ||
if (Array.isArray(this.nextStateValue)) | ||
this.set(this.nextStateValue.reverse()); | ||
break; | ||
case 'string': | ||
this.set(this.nextStateValue.split('').reverse().join('')); | ||
break; | ||
case 'number': | ||
this.set((this.nextStateValue * -1)); | ||
break; | ||
default: | ||
internal_1.LogCodeManager.log('14:03:04', [typeof this.nextStateValue]); | ||
} | ||
return this; | ||
} | ||
addSideEffect(key, callback, config = {}) { | ||
config = internal_1.defineConfig(config, { | ||
weight: 10, | ||
}); | ||
if (!internal_1.isFunction(callback)) { | ||
internal_1.LogCodeManager.log('00:03:01', ['Side Effect Callback', 'function']); | ||
return this; | ||
} | ||
this.sideEffects[key] = { | ||
callback: callback, | ||
weight: config.weight, | ||
}; | ||
return this; | ||
} | ||
removeSideEffect(key) { | ||
delete this.sideEffects[key]; | ||
return this; | ||
} | ||
hasSideEffect(key) { | ||
return !!this.sideEffects[key]; | ||
} | ||
getPersistableValue() { | ||
return this._value; | ||
} | ||
__exportStar(require("./state"), exports); | ||
function createState(initialValue, config = {}) { | ||
config = internal_1.defineConfig(config, { | ||
agileInstance: internal_1.shared, | ||
}); | ||
return new internal_1.State(config.agileInstance, initialValue, internal_1.removeProperties(config, ['agileInstance'])); | ||
} | ||
exports.State = State; | ||
exports.createState = createState; |
@@ -1,157 +0,18 @@ | ||
import { Agile, Storage, Persistent, StorageKey, StorageItemKey } from '../internal'; | ||
export declare class Storages { | ||
agileInstance: () => Agile; | ||
config: StoragesConfigInterface; | ||
storages: { | ||
[key: string]: Storage; | ||
}; | ||
persistentInstances: Set<Persistent>; | ||
/** | ||
* The Storages Class manages all external Storages for an Agile Instance | ||
* and provides an interface to easily store, | ||
* load and remove values from multiple Storages at once. | ||
* | ||
* @internal | ||
* @param agileInstance - Instance of Agile the Storages belongs to. | ||
* @param config - Configuration object | ||
*/ | ||
constructor(agileInstance: Agile, config?: CreateStoragesConfigInterface); | ||
/** | ||
* Instantiates and registers the | ||
* [Local Storage](https://developer.mozilla.org/de/docs/Web/API/Window/localStorage). | ||
* | ||
* Note that the Local Storage is only available in a web environment. | ||
* | ||
* @internal | ||
*/ | ||
instantiateLocalStorage(): boolean; | ||
/** | ||
* Registers the specified Storage with AgileTs | ||
* and updates the Persistent Instances that have already attempted | ||
* to use the previously unregistered Storage. | ||
* | ||
* @public | ||
* @param storage - Storage to be registered with AgileTs. | ||
* @param config - Configuration object | ||
*/ | ||
register(storage: Storage, config?: RegisterConfigInterface): boolean; | ||
/** | ||
* Retrieves a single Storage with the specified key/name identifier | ||
* from the Storages Class. | ||
* | ||
* If the to retrieve Storage doesn't exist, `undefined` is returned. | ||
* | ||
* @public | ||
* @param storageKey - Key/Name identifier of the Storage. | ||
*/ | ||
getStorage(storageKey: StorageKey | undefined | null): Storage | undefined; | ||
/** | ||
* Retrieves the stored value at the specified Storage Item key | ||
* from the defined external Storage (`storageKey`). | ||
* | ||
* When no Storage has been specified, | ||
* the value is retrieved from the default Storage. | ||
* | ||
* @public | ||
* @param storageItemKey - Key/Name identifier of the value to be retrieved. | ||
* @param storageKey - Key/Name identifier of the external Storage | ||
* from which the value is to be retrieved. | ||
*/ | ||
get<GetType = any>(storageItemKey: StorageItemKey, storageKey?: StorageKey): Promise<GetType | undefined>; | ||
/** | ||
* Stores or updates the value at the specified Storage Item key | ||
* in the defined external Storages (`storageKeys`). | ||
* | ||
* When no Storage has been specified, | ||
* the value is stored/updated in the default Storage. | ||
* | ||
* @public | ||
* @param storageItemKey - Key/Name identifier of the value to be stored. | ||
* @param value - Value to be stored in an external Storage. | ||
* @param storageKeys - Key/Name identifier of the external Storage | ||
* where the value is to be stored. | ||
*/ | ||
set(storageItemKey: StorageItemKey, value: any, storageKeys?: StorageKey[]): void; | ||
/** | ||
* Removes the value at the specified Storage Item key | ||
* from the defined external Storages (`storageKeys`). | ||
* | ||
* When no Storage has been specified, | ||
* the value is removed from the default Storage. | ||
* | ||
* @public | ||
* @param storageItemKey - Key/Name identifier of the value to be removed. | ||
* @param storageKeys - Key/Name identifier of the external Storage | ||
* from which the value is to be removed. | ||
*/ | ||
remove(storageItemKey: StorageItemKey, storageKeys?: StorageKey[]): void; | ||
/** | ||
* Returns a boolean indicating whether any Storage | ||
* has been registered with the Agile Instance or not. | ||
* | ||
* @public | ||
*/ | ||
hasStorage(): boolean; | ||
/** | ||
* Returns a boolean indication whether the | ||
* [Local Storage](https://developer.mozilla.org/de/docs/Web/API/Window/localStorage) | ||
* is available in the current environment. | ||
* | ||
* @public | ||
*/ | ||
static localStorageAvailable(): boolean; | ||
} | ||
export interface CreateStoragesConfigInterface { | ||
/** | ||
* Whether to register the Local Storage by default. | ||
* Note that the Local Storage is only available in a web environment. | ||
* @default false | ||
*/ | ||
localStorage?: boolean; | ||
/** | ||
* Key/Name identifier of the default Storage. | ||
* | ||
* The default Storage represents the default Storage of the Storages Class, | ||
* on which executed actions are performed if no specific Storage was specified. | ||
* | ||
* Also, the persisted value is loaded from the default Storage by default, | ||
* since only one persisted value can be applied. | ||
* If the loading of the value from the default Storage failed, | ||
* an attempt is made to load the value from the remaining Storages. | ||
* | ||
* @default undefined | ||
*/ | ||
defaultStorageKey?: StorageKey; | ||
} | ||
export interface StoragesConfigInterface { | ||
/** | ||
* Key/Name identifier of the default Storage. | ||
* | ||
* The default Storage represents the default Storage of the Storages Class, | ||
* on which executed actions are performed if no specific Storage was specified. | ||
* | ||
* Also, the persisted value is loaded from the default Storage by default, | ||
* since only one persisted value can be applied. | ||
* If the loading of the value from the default Storage failed, | ||
* an attempt is made to load the value from the remaining Storages. | ||
* | ||
* @default undefined | ||
*/ | ||
defaultStorageKey: StorageKey | null; | ||
} | ||
export interface RegisterConfigInterface { | ||
/** | ||
* Whether the to register Storage should become the default Storage. | ||
* | ||
* The default Storage represents the default Storage of the Storages Class, | ||
* on which executed actions are performed if no specific Storage was specified. | ||
* | ||
* Also, the persisted value is loaded from the default Storage by default, | ||
* since only one persisted value can be applied. | ||
* If the loading of the value from the default Storage failed, | ||
* an attempt is made to load the value from the remaining Storages. | ||
* | ||
* @default false | ||
*/ | ||
default?: boolean; | ||
} | ||
import { CreateStorageConfigInterface, Storage } from '../internal'; | ||
export * from './storages'; | ||
/** | ||
* Returns a newly created Storage. | ||
* | ||
* A Storage Class serves as an interface to external storages, | ||
* such as the [Async Storage](https://github.com/react-native-async-storage/async-storage) or | ||
* [Local Storage](https://www.w3schools.com/html/html5_webstorage.asp). | ||
* | ||
* It creates the foundation to easily [`persist()`](https://agile-ts.org/docs/core/state/methods#persist) [Agile Sub Instances](https://agile-ts.org/docs/introduction/#agile-sub-instance) | ||
* (like States or Collections) in nearly any external storage. | ||
* | ||
* [Learn more..](https://agile-ts.org/docs/core/agile-instance/methods#createstorage) | ||
* | ||
* @public | ||
* @param config - Configuration object | ||
*/ | ||
export declare function createStorage(config: CreateStorageConfigInterface): Storage; |
"use strict"; | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); | ||
}) : (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
o[k2] = m[k]; | ||
})); | ||
var __exportStar = (this && this.__exportStar) || function(m, exports) { | ||
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p); | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.Storages = void 0; | ||
exports.createStorage = void 0; | ||
const internal_1 = require("../internal"); | ||
class Storages { | ||
constructor(agileInstance, config = {}) { | ||
this.storages = {}; | ||
this.persistentInstances = new Set(); | ||
this.agileInstance = () => agileInstance; | ||
config = internal_1.defineConfig(config, { | ||
localStorage: false, | ||
defaultStorageKey: null, | ||
}); | ||
this.config = { defaultStorageKey: config.defaultStorageKey }; | ||
if (config.localStorage) | ||
this.instantiateLocalStorage(); | ||
} | ||
instantiateLocalStorage() { | ||
if (!Storages.localStorageAvailable()) { | ||
internal_1.LogCodeManager.log('11:02:00'); | ||
return false; | ||
} | ||
const _localStorage = new internal_1.Storage({ | ||
key: 'localStorage', | ||
async: false, | ||
methods: { | ||
get: localStorage.getItem.bind(localStorage), | ||
set: localStorage.setItem.bind(localStorage), | ||
remove: localStorage.removeItem.bind(localStorage), | ||
}, | ||
}); | ||
return this.register(_localStorage, { default: true }); | ||
} | ||
register(storage, config = {}) { | ||
const hasRegisteredAnyStorage = internal_1.notEqual(this.storages, {}); | ||
if (Object.prototype.hasOwnProperty.call(this.storages, storage.key)) { | ||
internal_1.LogCodeManager.log('11:03:00', [storage.key]); | ||
return false; | ||
} | ||
if (!hasRegisteredAnyStorage && config.default === false) | ||
internal_1.LogCodeManager.log('11:02:01'); | ||
if (!hasRegisteredAnyStorage) | ||
config.default = true; | ||
this.storages[storage.key] = storage; | ||
if (config.default) | ||
this.config.defaultStorageKey = storage.key; | ||
this.persistentInstances.forEach((persistent) => { | ||
if (persistent.storageKeys.includes(storage.key)) { | ||
const isValid = persistent.validatePersistent(); | ||
if (isValid) | ||
persistent.initialLoading(); | ||
return; | ||
} | ||
if (persistent.config.defaultStorageKey == null) { | ||
persistent.assignStorageKeys(); | ||
const isValid = persistent.validatePersistent(); | ||
if (isValid) | ||
persistent.initialLoading(); | ||
} | ||
}); | ||
internal_1.LogCodeManager.log('13:00:00', [storage.key], storage); | ||
return true; | ||
} | ||
getStorage(storageKey) { | ||
if (!storageKey) | ||
return undefined; | ||
const storage = this.storages[storageKey]; | ||
if (!storage) { | ||
internal_1.LogCodeManager.log('11:03:01', [storageKey]); | ||
return undefined; | ||
} | ||
if (!storage.ready) { | ||
internal_1.LogCodeManager.log('11:03:02', [storageKey]); | ||
return undefined; | ||
} | ||
return storage; | ||
} | ||
get(storageItemKey, storageKey) { | ||
if (!this.hasStorage()) { | ||
internal_1.LogCodeManager.log('11:03:03'); | ||
return Promise.resolve(undefined); | ||
} | ||
if (storageKey) { | ||
const storage = this.getStorage(storageKey); | ||
if (storage) | ||
return storage.get(storageItemKey); | ||
} | ||
const defaultStorage = this.getStorage(this.config.defaultStorageKey); | ||
return ((defaultStorage === null || defaultStorage === void 0 ? void 0 : defaultStorage.get(storageItemKey)) || Promise.resolve(undefined)); | ||
} | ||
set(storageItemKey, value, storageKeys) { | ||
var _a; | ||
if (!this.hasStorage()) { | ||
internal_1.LogCodeManager.log('11:03:04'); | ||
return; | ||
} | ||
if (storageKeys != null) { | ||
for (const storageKey of storageKeys) | ||
(_a = this.getStorage(storageKey)) === null || _a === void 0 ? void 0 : _a.set(storageItemKey, value); | ||
return; | ||
} | ||
const defaultStorage = this.getStorage(this.config.defaultStorageKey); | ||
defaultStorage === null || defaultStorage === void 0 ? void 0 : defaultStorage.set(storageItemKey, value); | ||
} | ||
remove(storageItemKey, storageKeys) { | ||
var _a; | ||
if (!this.hasStorage()) { | ||
internal_1.LogCodeManager.log('11:03:05'); | ||
return; | ||
} | ||
if (storageKeys) { | ||
for (const storageKey of storageKeys) | ||
(_a = this.getStorage(storageKey)) === null || _a === void 0 ? void 0 : _a.remove(storageItemKey); | ||
return; | ||
} | ||
const defaultStorage = this.getStorage(this.config.defaultStorageKey); | ||
defaultStorage === null || defaultStorage === void 0 ? void 0 : defaultStorage.remove(storageItemKey); | ||
} | ||
hasStorage() { | ||
return internal_1.notEqual(this.storages, {}); | ||
} | ||
static localStorageAvailable() { | ||
try { | ||
localStorage.setItem('_myDummyKey_', 'myDummyValue'); | ||
localStorage.removeItem('_myDummyKey_'); | ||
return true; | ||
} | ||
catch (e) { | ||
return false; | ||
} | ||
} | ||
__exportStar(require("./storages"), exports); | ||
function createStorage(config) { | ||
return new internal_1.Storage(config); | ||
} | ||
exports.Storages = Storages; | ||
exports.createStorage = createStorage; |
{ | ||
"name": "@agile-ts/core", | ||
"version": "0.2.0-alpha.3", | ||
"version": "0.2.0-alpha.4", | ||
"author": "BennoDev", | ||
@@ -39,2 +39,3 @@ "license": "MIT", | ||
"release": "node ./scripts/prepublish.js && yarn run prepare", | ||
"release:manual": "yarn run prepare && yarn run release && npm publish && git checkout README.md", | ||
"pack": "npm pack", | ||
@@ -44,4 +45,3 @@ "test": "jest", | ||
"lint": "eslint src/**/*", | ||
"size": "yarn run build && size-limit", | ||
"release:manual": "yarn run prepare && yarn run release && npm publish && git checkout README.md" | ||
"size": "yarn run build && size-limit" | ||
}, | ||
@@ -48,0 +48,0 @@ "devDependencies": { |
@@ -209,3 +209,11 @@ <img src="https://raw.githubusercontent.com/agile-ts/agile/master/static/header_background.png" alt="AgileTs"> | ||
### ♥️ Contributors | ||
<a href="https://github.com/agile-ts/agile/graphs/contributors"> | ||
<img src="https://contrib.rocks/image?repo=agile-ts/agile" /> | ||
</a> | ||
[Become a contributor](https://github.com/agile-ts/agile/blob/master/CONTRIBUTING.md) | ||
<br /> | ||
@@ -212,0 +220,0 @@ |
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 1 instance in 1 package
435620
112
10149
244
6