[
  {
    "path": ".eslintignore",
    "content": "**/*.spec.ts\n"
  },
  {
    "path": ".eslintrc.json",
    "content": "{\n  // Настройки проекта\n  \"env\": {\n    // Проект для браузера\n    \"browser\": true,\n    // Включаем возможности ES6\n    \"es6\": true,\n    // Добавляем возможности ES2017\n    \"es2017\": true\n  },\n  // Наборы правил\n  \"extends\": [\n    // Базовый набор правил eslint\n    \"eslint:recommended\",\n    // Отключаем правила из базового набора\n    \"plugin:@typescript-eslint/eslint-recommended\",\n    // Базовые правила для TypeScript\n    \"plugin:@typescript-eslint/recommended\",\n    // Правила TS, требующие инфо о типах\n    \"plugin:@typescript-eslint/recommended-requiring-type-checking\"\n  ],\n  // Движок парсинга\n  \"parser\": \"@typescript-eslint/parser\",\n  \"parserOptions\": {\n    // Движку нужен проект TS для правил с типами\n    \"project\": \"tsconfig.json\",\n    \"tsconfigRootDir\": \".\"\n  },\n  // Плагин с наборами правил для TypeScript\n  \"plugins\": [\n    \"@typescript-eslint\"\n  ],\n  \"rules\": {\n    \"@typescript-eslint/interface-name-prefix\": \"off\",\n    \"@typescript-eslint/no-inferrable-types\": \"off\",\n    \"@typescript-eslint/no-explicit-any\": \"off\",\n    \"@typescript-eslint/no-empty-interface\": \"off\",\n    \"@typescript-eslint/unbound-method\": \"off\",\n    \"@typescript-eslint/explicit-function-return-type\": \"off\",\n    \"@typescript-eslint/no-unused-vars\": \"off\"\n  }\n}\n"
  },
  {
    "path": ".gitignore",
    "content": "dist\r\nnode_modules\r\n.idea\r\n/compiled\r\n*.metadata.json\r\n"
  },
  {
    "path": ".npmignore",
    "content": "node_modules\r\n.idea\r\n/compiled\r\n/src/testing\r\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2019 Egor Grushin\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "# painless-redux\nReducers-actions-selectors free reactive state management in redux-way\n\n# Overview\nThis package allows you to use CRUD (Create, Read, Update and Delete) manipulations with entities and workspaces.\n\nGeneral features:\n- It provides several simple methods such as get, create, remove, change etc. for using on Entity instance.\n- It provides loading state management (i.e. isLoading and error).\n- Underhood it uses any redux-like library you want to (e.g. [@ngrx/store](https://github.com/ngrx/platform)), so it means you can use [Redux DevTools](https://chrome.google.com/webstore/detail/redux-devtools/lmhkpmbekcpmknklioeibfkpmmfibljd?hl=ru) but with this library you don't have to create boilerplate code (e.g. reducers, actions, selectors, action creators etc.).\n- All methods working with outer data sources (e.g. requests passed to `get$` method) are [RxJS](https://github.com/ReactiveX/rxjs) powered.\n- It supports optimistic change, remove, add\n- It provides Workspace (documentation will be ready soon) which allows you store filter, sorting, ui states etc.\n\n# Requirements\n1. To be familiar with [redux](https://github.com/reduxjs/redux)\n2. To be familiar with [RxJS](https://github.com/ReactiveX/rxjs)\n\n# Documentation\n[Here](https://github.com/egorgrushin/painless-redux/wiki)\n\n# Plain use\n\n1. install using npm:\n\t`npm i painless-redux`\n\n2. create an store:\n    ```typescript\n\timport { createPainlessRedux, RxStore } from 'painless-redux';\n\tconst store: RxStore = <use any implementation you want>;\n    export const PAINLESS_REDUX_STORE = createPainlessRedux(store);\n    ```\n\n3. create an entity:\n\t```typescript\n\timport { createEntity } from 'painless-redux';\n\timport { PAINLESS_REDUX_STORE } from './store';\n\texport interface Painter {\n\t\tid: number | string;\n\t\tfullName: string;\n\t\tborn: number;\n\t}\n\tconst PaintersEntity = createEntity<Painter>({ name: 'painters' });\n\tPAINLESS_REDUX_STORE.registerSlot(PaintersEntity);\n\texport PaintersEntity;\n\t```\n\n4. add new entity\n    ```typescript\n   PaintersEntity.add({ id: 1, fullName: 'Vincent van Gogh', born: 1853 });\n    ```\n5. get entity or all entities\n    ```typescript\n    PaintersEntity.getById$(1).subscribe((painter: Painter) => {});\n    PaintersEntity.get$().subscribe((painters: Painter[]) => {});\n    ```\n   \n# Use with Angular\n\n[This adapter](https://github.com/egorgrushin/ngx-painless-redux) will help you to connect `painless-redux` to your Angular project, who uses [@ngrx/store](https://github.com/ngrx/platform).\n\n# Use with React\n\n[This adapter](https://www.npmjs.com/package/react-painless-redux) will help you to connect `painless-redux` to your React project, who uses [@reduxjs/toolkit](https://www.npmjs.com/package/@reduxjs/toolkit).\n\n# Common use\n\nThis part can be difficult to understand, but this is main feature of this library.\nCommonly you need to load some entities from outer source (e.g. your backend api) with given filter. To achieve this you need to prepare your data source using RxJS's observable and use `Entity.get$` method like this:\n\n\n```typescript\n    import { Observable, of } from 'rxjs';\n    import { PaintersEntity } from './painters';\n\n    const init = () => {\n        const config = {};\n        getPainters$(config).subscribe((painters: Painter[]) => {\n            // emits:\n            // 1. undefined immediately\n            // 2. painters array when getPaintersFromApi$'s observable emits.\n        });\n    }\n\n    const getPainters$ = (config: unknown): Observable<Painter[]> {\n        const dataSource$ = getPaintersFromApi$(config);\n        return PaintersEntity.get$(config, dataSource$);\n    }\n\n    const getPaintersFromApi$ = (config: unknown): Observable<Painter[]> => {\n        // use can use any data source you need, this is for demo purposes.\n        const painters: Painter[] = [\n           { id: 1, fullName: 'Leonardo da Vinci', born: 1452 },\n           { id: 2, fullName: 'Vincent van Gogh', born: 1853 },\n           { id: 3, fullName: 'Pablo Picasso', born: 1881 },\n        ];\n        return of(painters);\n    }\n```\n\n`Entity.get$` algorithm is described [here](https://github.com/egorgrushin/painless-redux/wiki/Entity#get_observable)\n\n# Pagination\n\n`Entity.get$` method supports pagination. For this you have to pass `paginator` BehaviorSubject as the last argument:\n\n```typescript\n    import { Observable, of, BehaviorSubject } from 'rxjs';\n    import { Pagination } from 'painless-redux';\n    import { PaintersEntity } from './painters';\n\n    const init = () => {\n        const paginator = new BehaviorSubject(false);\n        const config = {};\n        getPainters$(config, paginator).subscribe((painters: Painter[]) => {\n            // emits:\n            // 1. undefined immediately\n            // 2. painters array when getPaintersFromApi$'s observable emits.\n            // idle 3000ms\n            // 3. painters array from second emit merged with another getPaintersFromApi$'s observable emits.\n        });\n\n        setTimeout(() => {\n            paginator.next(true);\n        }, 3000)\n    }\n\n    const getPainters$ = (config: unknown, paginator: BehaviorSubject<boolean>): Observable<Painter[]> {\n        const dataSource = ({ from, to, size, index }: Pagination) => getPaintersFromApi$(config, from, to);\n        return PaintersEntity.get$(config, dataSource, null, paginator);\n    }\n\n    const getPaintersFromApi$ = (config: unknown, from: number, to: number): Observable<Painter[]> => {\n        // use can use any data source you need, this is for demo purposes.\n        const painters: Painter[] = [\n           { id: 1, fullName: 'Leonardo da Vinci', born: 1452 },\n           { id: 2, fullName: 'Vincent van Gogh', born: 1853 },\n           { id: 3, fullName: 'Pablo Picasso', born: 1881 },\n        ].slice(from, to);\n        return of(painters);\n    }\n\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"painless-redux\",\n  \"version\": \"4.1.17\",\n  \"description\": \"Reducers-actions-selectors free reactive state management in redux-way\",\n  \"main\": \"dist/index.js\",\n  \"scripts\": {\n    \"build\": \"rimraf dist && tsc\",\n    \"preversion\": \"npm run test && npm run build\",\n    \"test\": \"jest\"\n  },\n  \"jest\": {\n    \"roots\": [\n      \"<rootDir>/src\"\n    ],\n    \"transform\": {\n      \"^.+\\\\.tsx?$\": \"ts-jest\"\n    },\n    \"testRegex\": \"(/__tests__/.*|(\\\\.|/)(test|spec|int-spec))\\\\.tsx?$\",\n    \"moduleFileExtensions\": [\n      \"ts\",\n      \"tsx\",\n      \"js\",\n      \"jsx\",\n      \"json\",\n      \"node\"\n    ],\n    \"preset\": \"ts-jest\",\n    \"moduleNameMapper\": {\n      \"^lodash-es$\": \"<rootDir>/node_modules/lodash/index.js\"\n    }\n  },\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"git+https://github.com/egorgrushin/painless-redux.git\"\n  },\n  \"keywords\": [\n    \"redux\",\n    \"rxjs\"\n  ],\n  \"types\": \"dist/index.d.ts\",\n  \"author\": \"Grushin Egor\",\n  \"license\": \"MIT\",\n  \"bugs\": {\n    \"url\": \"https://github.com/egorgrushin/painless-redux/issues\"\n  },\n  \"homepage\": \"https://github.com/egorgrushin/painless-redux#readme\",\n  \"dependencies\": {\n    \"lodash-es\": \"^4.17.21\",\n    \"object-hash\": \"^2.0.3\",\n    \"reselect\": \"4.0.0\",\n    \"uuid\": \"^8.3.2\"\n  },\n  \"devDependencies\": {\n    \"@types/crypto-js\": \"^3.1.43\",\n    \"@types/jest\": \"^24.9.1\",\n    \"@types/lodash-es\": \"^4.17.5\",\n    \"@types/node\": \"^12.7.5\",\n    \"@types/object-hash\": \"^1.3.1\",\n    \"@types/uuid\": \"^8.3.2\",\n    \"@typescript-eslint/eslint-plugin\": \"^2.20.0\",\n    \"@typescript-eslint/parser\": \"^2.20.0\",\n    \"eslint\": \"^6.8.0\",\n    \"jest\": \"^25.2.7\",\n    \"jest-marbles\": \"^2.5.1\",\n    \"lodash\": \"^4.17.21\",\n    \"rimraf\": \"^2.6.3\",\n    \"rxjs\": \"^6.5.3\",\n    \"ts-jest\": \"^25.2.1\",\n    \"typescript\": \"^3.8.2\"\n  },\n  \"peerDependencies\": {\n    \"rxjs\": \"^6.3.3\"\n  }\n}\n"
  },
  {
    "path": "src/affect-loading-state/affect-loading-state.ts",
    "content": "import { EMPTY, Observable, of, OperatorFunction, throwError } from 'rxjs';\nimport { catchError, finalize, switchMap, tap } from 'rxjs/operators';\nimport { AffectLoadingStateFactory, AffectStateSetter } from './types';\nimport { isFunction } from 'lodash-es';\n\nexport const affectLoadingStateOperatorFactory = <T, E>(\n    setter: AffectStateSetter<T, E>,\n    rethrow: boolean = true,\n) => (\n    ...pipes: Array<OperatorFunction<any, any>>\n): OperatorFunction<any, any> => (\n    source: Observable<T>,\n) => {\n    let stateCleared: boolean;\n    return (source as any).pipe(\n        tap((value: T) => {\n            setter?.({ isLoading: true, error: undefined }, false, value);\n            stateCleared = false;\n        }),\n        switchMap((value: T) => (of(value) as any).pipe(\n            ...pipes,\n            catchError((error: E) => {\n                setter?.({ isLoading: false, error }, false, undefined);\n                stateCleared = true;\n                return rethrow ? throwError(error) : EMPTY;\n            }),\n        )),\n        tap((value: T) => {\n            setter?.({ isLoading: false }, false, value);\n            stateCleared = true;\n        }),\n        finalize(() => {\n            if (stateCleared) return;\n            setter?.({ isLoading: false }, true, undefined);\n        }),\n    );\n};\n\nexport const affectLoadingStateFactory = <T, E>(\n    setter: AffectStateSetter<T, E>,\n    rethrow: boolean = true,\n): AffectLoadingStateFactory => (...pipesOrObs: any) => {\n    const obs = pipesOrObs[0];\n    const isPipes = isFunction(obs);\n    const operatorFactory = affectLoadingStateOperatorFactory(setter, rethrow);\n    if (isPipes) return operatorFactory(...pipesOrObs as OperatorFunction<T, T>[]);\n    const operator = operatorFactory(switchMap(() => obs));\n    return (of(undefined) as any).pipe(operator);\n};\n"
  },
  {
    "path": "src/affect-loading-state/types.ts",
    "content": "import { LoadingState } from '../system-types';\nimport { Observable, OperatorFunction } from 'rxjs';\n\nexport interface AffectStateSetter<T = any, E = any> {\n    (\n        loadingState: LoadingState<E>,\n        isInterrupted: boolean,\n        value: T | undefined,\n    ): void;\n}\n\nexport interface AffectLoadingStateFactory {\n    <T>(...pipes: Array<OperatorFunction<any, any>>): OperatorFunction<T, T>;\n\n    <T>(observable: Observable<T>): Observable<T>;\n\n    <T>(...pipesOrObs: any): OperatorFunction<T, T> | Observable<T>;\n}\n"
  },
  {
    "path": "src/dispatcher/dispatcher.ts",
    "content": "import { ActionCreator, AnyAction, RxStore, SameShaped } from '../system-types';\nimport { Dispatcher } from './types';\n\nconst createCreateAction = <TActionTypes, TActions>(\n    actionCreators: SameShaped<TActionTypes, ActionCreator<TActionTypes, TActions>>,\n) => (\n    actionName: keyof TActionTypes,\n    args: any[],\n    options?: unknown,\n): TActions => {\n    const actionCreator = actionCreators[actionName];\n    return actionCreator(...args, options);\n};\n\nexport const createDispatcher = <TActionTypes, TActions extends AnyAction>(\n    rxStore: RxStore,\n    actionCreators: SameShaped<TActionTypes, ActionCreator<TActionTypes, TActions>>,\n): Dispatcher<TActionTypes, TActions> => {\n    const dispatch = (action: AnyAction): void => rxStore.dispatch(action);\n    const createAction = createCreateAction(actionCreators);\n\n    const createAndDispatch = (\n        actionName: keyof TActionTypes,\n        args: any[],\n        options?: unknown,\n    ): TActions => {\n        const action = createAction(actionName, args, options);\n        dispatch(action);\n        return action;\n    };\n\n    return {\n        dispatch,\n        createAndDispatch,\n    };\n};\n"
  },
  {
    "path": "src/dispatcher/types.ts",
    "content": "export interface Dispatcher<TActionTypes, TActions> {\n    dispatch(action: TActions): void;\n\n    createAndDispatch(\n        actionName: keyof TActionTypes,\n        args: any[],\n        options?: any,\n    ): TActions;\n}\n"
  },
  {
    "path": "src/entity/action-creators.ts",
    "content": "import { EntityActionTypes, EntitySchema } from './types';\nimport {\n    createAdd,\n    createAddList,\n    createChange,\n    createChangeList,\n    createClear,\n    createClearAll,\n    createRemove,\n    createRemoveList,\n    createResolveAdd,\n    createResolveChange,\n    createResolveChangeList,\n    createResolveRemove,\n    createResolveRemoveList,\n    createRestoreRemoved,\n    createRestoreRemovedList,\n    createSetLoadingState,\n    createSetLoadingStates,\n} from './actions';\nimport { createBatch } from '../shared/system/actions';\nimport { EntityActionCreators } from './action-creators.types';\n\nexport const createEntityActionCreators = <T, TPageMetadata>(\n    actionTypes: EntityActionTypes,\n    schema: EntitySchema<T>,\n): EntityActionCreators<T, TPageMetadata> => ({\n    ADD: createAdd<T>(actionTypes, schema),\n    RESOLVE_ADD: createResolveAdd<T>(actionTypes, schema),\n    ADD_LIST: createAddList<T, TPageMetadata>(actionTypes, schema),\n    CHANGE: createChange<T>(actionTypes),\n    RESOLVE_CHANGE: createResolveChange<T>(actionTypes),\n    REMOVE: createRemove(actionTypes),\n    RESOLVE_REMOVE: createResolveRemove<T>(actionTypes),\n    RESTORE_REMOVED: createRestoreRemoved<T>(actionTypes),\n    REMOVE_LIST: createRemoveList(actionTypes),\n    RESOLVE_REMOVE_LIST: createResolveRemoveList(actionTypes),\n    RESTORE_REMOVED_LIST: createRestoreRemovedList<T>(actionTypes),\n    SET_LOADING_STATE: createSetLoadingState(actionTypes, schema),\n    CLEAR: createClear(actionTypes, schema),\n    CLEAR_ALL: createClearAll(actionTypes),\n    BATCH: createBatch<T>(actionTypes),\n    CHANGE_LIST: createChangeList<T>(actionTypes),\n    SET_LOADING_STATES: createSetLoadingStates(actionTypes),\n    RESOLVE_CHANGE_LIST: createResolveChangeList<T>(actionTypes),\n});\n"
  },
  {
    "path": "src/entity/action-creators.types.ts",
    "content": "import {\n    EntityAddOptions,\n    EntityInternalAddListOptions,\n    EntityInternalAddOptions,\n    EntityInternalSetLoadingStateOptions,\n    EntityRemoveListOptions,\n    EntityRemoveOptions,\n    EntityType,\n    IdPatch,\n} from './types';\nimport {DeepPartial, Id, LoadingState} from '../system-types';\nimport {ChangeOptions} from '../shared/change/types';\n\n// this types for public use\nexport interface EntityActionCreators<T, TPageMetadata> {\n    ADD_LIST: (\n        entities: EntityType<T>[],\n        config?: unknown,\n        isReplace?: boolean,\n        hasMore?: boolean,\n        metadata?: TPageMetadata,\n        options?: EntityInternalAddListOptions,\n    ) => { payload: { entities: EntityType<T>[]; isReplace: boolean; hasMore: boolean; configHash: string; metadata: TPageMetadata | undefined }; options: EntityInternalAddListOptions; type: 'ADD_LIST' };\n    RESTORE_REMOVED: (id: Id) => { payload: { id: Id }; type: 'RESTORE_REMOVED' };\n    ADD: (\n        entity: EntityType<T>,\n        config?: unknown,\n        tempId?: string,\n        options?: EntityInternalAddOptions,\n    ) => { payload: { configHash: string; tempId: string | undefined; entity: { id: Id } }; options: EntityInternalAddOptions; type: 'ADD' };\n    RESOLVE_ADD: (\n        result: EntityType<T>,\n        success: boolean,\n        tempId: string,\n        config?: unknown,\n        options?: EntityAddOptions,\n    ) => { payload: { result: EntityType<T>; success: boolean; configHash: string; tempId: string }; options: EntityAddOptions; type: 'RESOLVE_ADD' };\n    CLEAR_ALL: () => { type: 'CLEAR_ALL' };\n    RESOLVE_REMOVE: (\n        id: Id,\n        success: boolean,\n        options?: EntityRemoveOptions,\n    ) => { payload: { success: boolean; id: Id }; options: EntityRemoveOptions; type: 'RESOLVE_REMOVE' };\n    REMOVE: (\n        id: Id,\n        options?: EntityRemoveOptions,\n    ) => { payload: { id: Id }; options: EntityRemoveOptions; type: 'REMOVE' };\n    REMOVE_LIST: (\n        ids: Id[],\n        options?: EntityRemoveOptions,\n    ) => { payload: { ids: Id[] }; options: EntityRemoveListOptions; type: 'REMOVE_LIST' };\n    RESOLVE_REMOVE_LIST: (\n        ids: Id[],\n        success: boolean,\n        options?: EntityRemoveOptions,\n    ) => { payload: { success: boolean; ids: Id[] }; options: EntityRemoveListOptions; type: 'RESOLVE_REMOVE_LIST' };\n    RESTORE_REMOVED_LIST: (\n        ids: Id[],\n    ) => { payload: { ids: Id[] }; type: 'RESTORE_REMOVED_LIST' };\n    CHANGE: (\n        id: Id,\n        patch: DeepPartial<T>,\n        changeId?: string,\n        options?: ChangeOptions,\n    ) => { payload: { patch: DeepPartial<unknown>; changeId: string | undefined; id: Id }; readonly options: ChangeOptions; type: 'CHANGE' };\n    CHANGE_LIST: (\n        patches: IdPatch<T>[],\n        changeId?: string,\n        options?: ChangeOptions,\n    ) => { payload: { patches: IdPatch<T>[]; changeId: string | undefined }; readonly options: ChangeOptions; type: 'CHANGE_LIST' };\n    BATCH: (actions: T[]) => { payload: { actions: T[] }; type: 'BATCH' };\n    SET_LOADING_STATE: (\n        state: LoadingState,\n        config?: unknown,\n        id?: Id,\n        key?: string,\n        options?: EntityInternalSetLoadingStateOptions,\n    ) => { payload: { configHash: string; state: LoadingState<string>; id: Id | undefined; key: string | undefined }; options: { maxPagesCount: number }; readonly type: 'SET_LOADING_STATE' };\n    SET_LOADING_STATES: (\n        state: LoadingState,\n        ids: Id[],\n        options?: EntityInternalSetLoadingStateOptions,\n    ) => { payload: { ids: Id[]; state: LoadingState<string> }; options: { maxPagesCount: number }; readonly type: 'SET_LOADING_STATES' };\n    RESOLVE_CHANGE: (\n        id: Id,\n        changeId: string,\n        success: boolean,\n        remotePatch?: DeepPartial<T>,\n        options?: ChangeOptions,\n    ) => { payload: { success: boolean; remotePatch: DeepPartial<unknown> | undefined; changeId: string; id: Id }; readonly options: ChangeOptions; type: 'RESOLVE_CHANGE' };\n    RESOLVE_CHANGE_LIST: (\n        patches: IdPatch<T>[],\n        changeId: string,\n        success: boolean,\n        options?: ChangeOptions,\n    ) => { payload: { patches: IdPatch<T>[]; changeId: string; success: boolean }; readonly options: ChangeOptions; type: 'RESOLVE_CHANGE_LIST' };\n    CLEAR: (config: unknown) => { payload: { configHash: string }; type: 'CLEAR' };\n}\n"
  },
  {
    "path": "src/entity/actions.ts",
    "content": "import {\n    EntityActionTypes,\n    EntityAddOptions,\n    EntityInternalAddListOptions,\n    EntityInternalAddOptions,\n    EntityInternalSetLoadingStateOptions,\n    EntityRemoveListOptions,\n    EntityRemoveOptions,\n    EntitySchema,\n    EntityType,\n    IdPatch,\n} from './types';\nimport { DeepPartial, Id, LoadingState } from '../system-types';\nimport { typedDefaultsDeep } from '../utils';\nimport * as loadingStateActions from '../shared/loading-state/actions';\nimport * as changeActions from '../shared/change/actions';\nimport { MAX_PAGES_COUNT } from './constants';\nimport { ChangeOptions } from '../shared/change/types';\nimport { SystemActions } from '../shared/system/actions';\n\nexport const createAddByHash = <T>(types: EntityActionTypes) => (\n    entity: EntityType<T>,\n    configHash: string,\n    tempId?: string,\n    options?: EntityInternalAddOptions,\n) => {\n    options = typedDefaultsDeep(options, { merge: true, maxPagesCount: MAX_PAGES_COUNT });\n    const payload = { entity, configHash, tempId };\n    return { type: types.ADD, payload, options } as const;\n};\n\nexport const createAdd = <T>(types: EntityActionTypes, schema: EntitySchema<T>) => (\n    entity: EntityType<T>,\n    config?: unknown,\n    tempId?: string,\n    options?: EntityInternalAddOptions,\n) => {\n    const configHash = schema.hashFn(config);\n    options = typedDefaultsDeep(options, { maxPagesCount: MAX_PAGES_COUNT });\n    tempId = options.optimistic ? tempId : undefined;\n    entity = options.optimistic ? { ...entity, id: tempId as string } : entity;\n    return createAddByHash(types)(entity, configHash, tempId, options);\n};\n\nexport const createResolveAdd = <T>(types: EntityActionTypes, schema: EntitySchema<T>) => (\n    result: EntityType<T>,\n    success: boolean,\n    tempId: string,\n    config?: unknown,\n    options?: EntityAddOptions,\n) => {\n    const configHash = schema.hashFn(config);\n    options = typedDefaultsDeep(options, { merge: true });\n    const payload = { result, configHash, tempId, success };\n    return { type: types.RESOLVE_ADD, payload, options } as const;\n};\n\nexport const createAddList = <T, TPageMetadata>(types: EntityActionTypes, schema: EntitySchema<T>) => (\n    entities: EntityType<T>[],\n    config?: unknown,\n    isReplace: boolean = false,\n    hasMore: boolean = false,\n    metadata?: TPageMetadata,\n    options?: EntityInternalAddListOptions,\n) => {\n    const configHash = schema.hashFn(config);\n    options = typedDefaultsDeep(options, { merge: true, maxPagesCount: MAX_PAGES_COUNT });\n    const payload = { entities, configHash, isReplace, hasMore, metadata };\n    return { type: types.ADD_LIST, payload, options } as const;\n};\n\nexport const createRemove = (types: EntityActionTypes) => (\n    id: Id,\n    options?: EntityRemoveOptions,\n) => {\n    options = typedDefaultsDeep(options);\n    const payload = { id };\n    return { type: types.REMOVE, payload, options } as const;\n};\n\nexport const createRemoveList = (types: EntityActionTypes) => (\n    ids: Id[],\n    options?: EntityRemoveListOptions,\n) => {\n    options = typedDefaultsDeep(options);\n    const payload = { ids };\n    return { type: types.REMOVE_LIST, payload, options } as const;\n};\n\nexport const createResolveRemoveList = (types: EntityActionTypes) => (\n    ids: Id[],\n    success: boolean,\n    options?: EntityRemoveListOptions,\n) => {\n    options = typedDefaultsDeep(options);\n    const payload = { ids, success };\n    return { type: types.RESOLVE_REMOVE_LIST, payload, options } as const;\n};\n\nexport const createSetLoadingState = (types: EntityActionTypes, schema: EntitySchema<unknown>) => (\n    state: LoadingState,\n    config?: unknown,\n    id?: Id,\n    key?: string,\n    options?: EntityInternalSetLoadingStateOptions,\n) => {\n    const actionCreator = loadingStateActions.createSetLoadingState(types);\n    const action = actionCreator(state, key, options);\n    const configHash = schema.hashFn(config);\n    return {\n        ...action,\n        payload: { ...action.payload, configHash, id },\n        options: { ...action.options, maxPagesCount: options?.maxPagesCount ?? MAX_PAGES_COUNT },\n    } as const;\n};\n\nexport const createSetLoadingStates = (types: EntityActionTypes) => (\n    state: LoadingState,\n    ids: Id[],\n    options?: EntityInternalSetLoadingStateOptions,\n) => {\n    return {\n        type: types.SET_LOADING_STATES,\n        payload: { state, ids },\n        options: { maxPagesCount: options?.maxPagesCount ?? MAX_PAGES_COUNT },\n    } as const;\n};\n\nexport const createChange = <T>(types: EntityActionTypes) => (\n    id: Id,\n    patch: DeepPartial<T>,\n    changeId?: string,\n    options?: ChangeOptions,\n) => {\n    const actionCreator = changeActions.createChange(types);\n    const action = actionCreator(patch, changeId, options);\n    return {\n        ...action,\n        type: types.CHANGE,\n        payload: { ...action.payload, id },\n    } as const;\n};\n\nexport const createChangeList = <T>(types: EntityActionTypes) => (\n    patches: IdPatch<T>[],\n    changeId?: string,\n    options?: ChangeOptions,\n) => {\n    options = typedDefaultsDeep(options, { merge: true });\n    return {\n        type: types.CHANGE_LIST,\n        payload: { patches, changeId },\n        options,\n    } as const;\n};\n\nexport const createResolveChange = <T>(types: EntityActionTypes) => (\n    id: Id,\n    changeId: string,\n    success: boolean,\n    remotePatch?: DeepPartial<T>,\n    options?: ChangeOptions,\n) => {\n    const actionCreator = changeActions.createResolveChange(types);\n    const action = actionCreator(changeId, success, remotePatch, options);\n    return {\n        ...action,\n        type: types.RESOLVE_CHANGE,\n        payload: { ...action.payload, id },\n    } as const;\n};\n\nexport const createResolveChangeList = <T>(types: EntityActionTypes) => (\n    patches: IdPatch<T>[],\n    changeId: string,\n    success: boolean,\n    options?: ChangeOptions,\n) => {\n    options = typedDefaultsDeep(options, { merge: true });\n    return {\n        type: types.RESOLVE_CHANGE_LIST,\n        payload: { patches, changeId, success },\n        options,\n    } as const;\n};\n\nexport const createResolveRemove = <T>(types: EntityActionTypes) => (\n    id: Id,\n    success: boolean,\n    options?: EntityRemoveOptions,\n) => {\n    options = typedDefaultsDeep(options);\n    return { type: types.RESOLVE_REMOVE, payload: { id, success }, options } as const;\n};\n\nexport const createRestoreRemoved = <T>(types: EntityActionTypes) => (\n    id: Id,\n) => {\n    return { type: types.RESTORE_REMOVED, payload: { id } } as const;\n};\n\nexport const createRestoreRemovedList = <T>(types: EntityActionTypes) => (\n    ids: Id[],\n) => {\n    return { type: types.RESTORE_REMOVED_LIST, payload: { ids } } as const;\n};\n\nexport const createClear = (types: EntityActionTypes, schema: EntitySchema<unknown>) => (config: unknown) => {\n    const configHash = schema.hashFn(config);\n    return { type: types.CLEAR, payload: { configHash } } as const;\n};\n\nexport const createClearAll = (types: EntityActionTypes) => () => {\n    return { type: types.CLEAR_ALL } as const;\n};\n\ntype SelfActionCreators = ReturnType<typeof createAdd>\n    | ReturnType<typeof createResolveAdd>\n    | ReturnType<typeof createAddList>\n    | ReturnType<typeof createSetLoadingState>\n    | ReturnType<typeof createChange>\n    | ReturnType<typeof createResolveChange>\n    | ReturnType<typeof createRemove>\n    | ReturnType<typeof createResolveRemove>\n    | ReturnType<typeof createRestoreRemoved>\n    | ReturnType<typeof createRemoveList>\n    | ReturnType<typeof createResolveRemoveList>\n    | ReturnType<typeof createRestoreRemovedList>\n    | ReturnType<typeof createClear>\n    | ReturnType<typeof createClearAll>\n    | ReturnType<typeof createChangeList>\n    | ReturnType<typeof createSetLoadingStates>\n    | ReturnType<typeof createResolveChangeList>\n\nexport type EntityActions = ReturnType<SelfActionCreators> | SystemActions;\n\n"
  },
  {
    "path": "src/entity/constants.ts",
    "content": "import { EntityActionTypes } from './types';\n\nexport const DEFAULT_PAGE_SIZE = 300;\nexport const MAX_PAGES_COUNT = Infinity;\nexport const ENTITY_TYPE_NAMES: Array<keyof EntityActionTypes> = [\n    'ADD',\n    'RESOLVE_ADD',\n    'ADD_LIST',\n    'SET_LOADING_STATE',\n    'CHANGE',\n    'RESOLVE_CHANGE',\n    'REMOVE',\n    'RESOLVE_REMOVE',\n    'RESTORE_REMOVED',\n    'REMOVE_LIST',\n    'RESOLVE_REMOVE_LIST',\n    'RESTORE_REMOVED_LIST',\n    'CLEAR',\n    'CLEAR_ALL',\n    'BATCH',\n    'RESOLVE_CHANGE_LIST',\n    'CHANGE_LIST',\n    'SET_LOADING_STATES',\n];\n"
  },
  {
    "path": "src/entity/entity.int-spec.ts",
    "content": "import {Id} from '../system-types';\nimport {Entity, EntityAddOptions, EntityRemoveOptions, Page} from './types';\nimport {createEntity} from './entity';\nimport {PainlessRedux} from '../painless-redux/types';\nimport {createPainlessRedux} from '../painless-redux/painless-redux';\nimport {TestStore} from '../testing/store';\nimport {cold} from 'jest-marbles';\nimport {switchMap} from 'rxjs/operators';\nimport {mocked} from 'ts-jest/utils';\nimport * as uuid from 'uuid';\nimport {ColdObservable} from 'rxjs/internal/testing/ColdObservable';\n\njest.mock('uuid');\n\ntype TestEntity = {\n    id: Id;\n    image?: string;\n    age?: number;\n    name?: string;\n}\n\ntype TPageMetadata = any;\n\ndescribe('[Integration] Entity', () => {\n\n    let entity: Entity<TestEntity, TPageMetadata>;\n    let pr: PainlessRedux;\n    let store: TestStore;\n    const filter = null;\n    const user: TestEntity = {id: 1, name: 'John'};\n\n    beforeEach(() => {\n        store = new TestStore({}, (state) => state);\n        pr = createPainlessRedux(store, {useAsapSchedulerInLoadingGuards: false});\n        entity = createEntity<TestEntity, TPageMetadata>(pr, {name: 'test', maxPagesCount: 2});\n    });\n\n    describe('#get$', () => {\n\n        test('should emit created earlier instance ', () => {\n            // arrange\n            entity.add(user);\n            const expected$ = cold('a', {a: [user]});\n            // act\n            const actual$ = entity.get$(filter);\n            // assert\n            expect(actual$).toBeObservable(expected$);\n        });\n\n        test('should emit instance after response', () => {\n            // arrange\n            const remote$ = cold('  --a', {a: {data: [user]}});\n            const expected$ = cold('a-b   ', {a: undefined, b: [user]});\n            // act\n            const actual$ = entity.get$(filter, remote$);\n            // assert\n            expect(actual$).toBeObservable(expected$);\n        });\n\n        test('should not load instance if exist with single option ', () => {\n            // arrange\n            entity.add(user);\n            const remote$ = cold('  --a', {a: [user]});\n            const expected$ = cold('a   ', {a: [user]});\n            // act\n            const actual$ = entity.get$(filter, remote$, {single: true});\n            // assert\n            expect(actual$).toBeObservable(expected$);\n        });\n\n        test('should not subscribe to remote$ if exist with single option ', () => {\n            // arrange\n            entity.add(user);\n            const remote$ = cold('  --a', {a: [user]});\n            // act\n            entity.get$(filter, remote$, {single: true}).subscribe();\n            // assert\n            expect(remote$).toHaveNoSubscriptions();\n        });\n    });\n\n    describe('getById$', () => {\n\n        test('should emit created earlier instance ', () => {\n            // arrange\n            entity.add(user);\n            const expected$ = cold('a', {a: user});\n            // act\n            const actual$ = entity.getById$(user.id);\n            // assert\n            expect(actual$).toBeObservable(expected$);\n        });\n\n        test('should emit instance after response', () => {\n            // arrange\n            const remote$ = cold('  --a', {a: user});\n            const expected$ = cold('a-b   ', {a: undefined, b: user});\n            // act\n            const actual$ = entity.getById$(user.id, remote$);\n            // assert\n            expect(actual$).toBeObservable(expected$);\n        });\n\n        test('should not load instance if exist with single option ', () => {\n            // arrange\n            entity.add(user);\n            const remote$ = cold('  --a', {a: user});\n            const expected$ = cold('a   ', {a: user});\n            // act\n            const actual$ = entity.getById$(user.id, remote$, {single: true});\n            // assert\n            expect(actual$).toBeObservable(expected$);\n        });\n\n        test('should not subscribe to remote$ if exist with single option ', () => {\n            // arrange\n            entity.add(user);\n            const remote$ = cold('  --a', {a: user});\n            // act\n            entity.getById$(user.id, remote$, {single: true}).subscribe();\n            // assert\n            expect(remote$).toHaveNoSubscriptions();\n        });\n    });\n\n    describe('getLoadingState$', () => {\n        test('should return loading state', () => {\n            // act\n            const actual$ = entity.getLoadingState$();\n            // assert\n            const expected$ = cold('a', {a: {isLoading: false}});\n            expect(actual$).toBeObservable(expected$);\n        });\n\n        test.each`\n            actionName         | action\n            ${'get$'}          | ${(remote$: ColdObservable<any>) => entity.get$(filter, remote$)}\n            ${'getById$'}      | ${(remote$: ColdObservable<any>) => entity.getById$(user.id, remote$)}\n            ${'addRemote$'}    | ${(remote$: ColdObservable<any>) => entity.addRemote$(user, user.id, remote$)}\n            ${'changeRemote$'} | ${(remote$: ColdObservable<any>) => entity.changeRemote$(user.id, {}, remote$)}\n            ${'removeRemote$'} | ${(remote$: ColdObservable<any>) => entity.removeRemote$(user.id, remote$)}\n        `('should set loading state during remote$ for entity.$actionName', ({actionName, action}) => {\n            // arrange\n            const remoteMarble = '      --a';\n            const loadingStateMarble = 'a-b';\n            const response = actionName === 'get$' ? {data: undefined} : undefined;\n            const remote$ = cold(remoteMarble, {a: response});\n            // act\n            const actual$ = entity.getLoadingState$();\n            action(remote$).subscribe();\n            // assert\n            const expected$ = cold(loadingStateMarble, {\n                a: {isLoading: true},\n                b: {isLoading: false},\n            });\n            expect(actual$).toBeObservable(expected$);\n        });\n    });\n\n    describe('getLoadingStates$', () => {\n        test('should return loading states', () => {\n            // act\n            const actual$ = entity.getLoadingStates$();\n            // assert\n            const expected$ = cold('a', {a: {}});\n            expect(actual$).toBeObservable(expected$);\n        });\n\n        test.each`\n            actionName         | action\n            ${'getById$'}      | ${(remote$: ColdObservable<any>) => entity.getById$(user.id, remote$)}\n            ${'changeRemote$'} | ${(remote$: ColdObservable<any>) => entity.changeRemote$(user.id, {}, remote$)}\n        `('should set loading state during remote$ for entity.$actionName', ({action}) => {\n            // arrange\n            const remoteMarble = '      --a';\n            const loadingStateMarble = 'a-b';\n            const remote$ = cold(remoteMarble, {a: undefined});\n            // act\n            const actual$ = entity.getLoadingStates$();\n            action(remote$).subscribe();\n            // assert\n            const expected$ = cold(loadingStateMarble, {\n                a: {[user.id]: {isLoading: true}},\n                b: {[user.id]: {isLoading: false}},\n            });\n            expect(actual$).toBeObservable(expected$);\n        });\n\n        test('should set and then remove loading state for entity.removeRemote$', () => {\n            // arrange\n            const remoteMarble = '      --a';\n            const loadingStateMarble = 'a-(bc)';\n            const remote$ = cold(remoteMarble, {a: undefined});\n            // act\n            const actual$ = entity.getLoadingStates$();\n            entity.removeRemote$(user.id, remote$).subscribe();\n            // assert\n            const expected$ = cold(loadingStateMarble, {\n                a: {[user.id]: {isLoading: true}},\n                b: {[user.id]: {isLoading: false}},\n                c: {},\n            });\n            expect(actual$).toBeObservable(expected$);\n        });\n    });\n\n    describe('#changeRemote$', () => {\n        const patch = {name: 'Alice'};\n        const remotePatch = {name: 'Mike'};\n\n        beforeEach(() => {\n            entity.add(user);\n        });\n\n        test.each`\n            useResponsePatch\n            ${false}\n            ${true}\n        `(\n            'should apply either patch or remotePatch after response based on useResponsePatch: $useResponsePatch',\n            ({useResponsePatch}) => {\n                // arrange\n                const resultPatch = useResponsePatch ? remotePatch : patch;\n                const remote$ = cold('  --a', {a: remotePatch});\n                const expected$ = cold('a-b   ', {a: [user], b: [{...user, ...resultPatch}]});\n                const actual$ = entity.get$(filter);\n                const options = {useResponsePatch};\n                // act\n                entity.changeRemote$(user.id, patch, remote$, options).subscribe();\n                // assert\n                expect(actual$).toBeObservable(expected$);\n            },\n        );\n\n        test('should ignore in case of response fail', () => {\n            // arrange\n            const remote$ = cold('  --#');\n            const expected$ = cold('a', {a: [user]});\n            const actual$ = entity.get$(filter);\n            // act\n            entity.changeRemote$(user.id, patch, remote$).subscribe();\n            // assert\n            expect(actual$).toBeObservable(expected$);\n        });\n\n        test.each`\n            useResponsePatch\n            ${false}\n            ${true}\n        `(\n            'should apply patch immediately and based on useResponsePatch: $useResponsePatch apply remotePatch',\n            ({useResponsePatch}) => {\n                // arrange\n                const remoteMarble = '    --a';\n                const expectedMarble = 'a-b-c';\n                const actMarble = '     --a';\n                const remote$ = cold(remoteMarble, {a: remotePatch});\n                const patched = {...user, ...patch};\n                const resultPatched = useResponsePatch ? {...patched, ...remotePatch} : patched;\n                const expected$ = cold(expectedMarble, {\n                    a: [user],\n                    b: [patched],\n                    c: [resultPatched],\n                });\n                const actual$ = entity.get$(filter);\n                const options = {optimistic: true, useResponsePatch};\n                // act\n                cold(actMarble).pipe(\n                    switchMap(() => entity.changeRemote$(user.id, patch, remote$, options)),\n                ).subscribe();\n                // assert\n                expect(actual$).toBeObservable(expected$);\n            },\n        );\n\n    });\n\n    describe('#removeRemote', () => {\n        beforeEach(() => {\n            entity.add(user);\n        });\n\n        test.each`\n            remoteMarble | expectedMarble\n            ${'--a'}     | ${'a-b-b'}\n            ${'--#'}     | ${'a-b-a'}\n        `('should optimistic remove or undo for $remoteMarble', ({remoteMarble, expectedMarble}) => {\n            // arrange\n            const remote$ = cold(remoteMarble);\n            const expected$ = cold(expectedMarble, {a: [user], b: []});\n            const actual$ = entity.get$(filter);\n            const options: EntityRemoveOptions = {optimistic: true};\n            // act\n            cold('--a', {a: null}).pipe(\n                switchMap(() => entity.removeRemote$(user.id, remote$, options)),\n            ).subscribe();\n            // assert\n            expect(actual$).toBeObservable(expected$);\n        });\n    });\n\n    describe('#addRemote', () => {\n        test.each`\n            remoteMarble | expectedMarble\n            ${'--a'}     | ${'a-b-c'}\n            ${'--#'}     | ${'a-b-d'}\n        `('should optimistic add with response.id or undo for $remoteMarble', ({remoteMarble, expectedMarble}) => {\n            // arrange\n            const newId = '999';\n            const tempId = '666';\n            mocked(uuid.v4).mockReturnValueOnce(tempId);\n            const response = {id: newId};\n            const remote$ = cold(remoteMarble, {a: response});\n            const expected$ = cold(expectedMarble, {\n                a: undefined,\n                b: [{...user, id: tempId}],\n                c: [{...user, id: newId}],\n                d: [],\n            });\n            const actual$ = entity.get$(filter);\n            const options: EntityAddOptions = {optimistic: true};\n            // act\n            cold('--a').pipe(\n                switchMap(() => entity.addRemote$(user, filter, remote$, options)),\n            ).subscribe();\n            // assert\n            expect(actual$).toBeObservable(expected$);\n        });\n    });\n\n    describe('#getAll$', () => {\n        test('should return all entities', () => {\n            // arrange\n            const user1 = {id: 1, name: 'User 1'};\n            const user2 = {id: 2, name: 'User 2'};\n            const actual$ = entity.getAll$();\n            const actMarble = '     -a-b';\n            const expectedMarble = 'ab-c';\n            const expected$ = cold(expectedMarble, {a: [], b: [user1], c: [user1, user2]});\n            // act\n            cold(actMarble, {a: user1, b: user2}).subscribe((value) => {\n                entity.add(value, Math.random());\n            });\n            // assert\n            expect(actual$).toBeObservable(expected$);\n        });\n    });\n\n    describe('#clear', () => {\n        test('should clear entities from specific page', () => {\n            // arrange\n            const user1 = {id: 1, name: 'User 1'};\n            const user2 = {id: 2, name: 'User 2'};\n            const filter1 = Math.random();\n            const filter2 = Math.random();\n            entity.add(user1, filter1);\n            entity.add(user2, filter2);\n            const actual$ = entity.get$(filter1);\n            const actMarble = '     --a';\n            const expectedMarble = 'a-b';\n            const expected$ = cold(expectedMarble, {a: [user1], b: undefined});\n            // act\n            cold(actMarble).subscribe(() => {\n                entity.clear(filter1);\n            });\n            // assert\n            expect(actual$).toBeObservable(expected$);\n        });\n    });\n\n    describe('#clearAll', () => {\n        test('should clear all entities', () => {\n            // arrange\n            const user1 = {id: 1, name: 'User 1'};\n            const user2 = {id: 2, name: 'User 2'};\n            entity.add(user1, Math.random());\n            entity.add(user2, Math.random());\n            const actual$ = entity.getAll$();\n            const actMarble = '     --a';\n            const expectedMarble = 'a-b';\n            const expected$ = cold(expectedMarble, {a: [user1, user2], b: []});\n            // act\n            cold(actMarble).subscribe(() => {\n                entity.clearAll();\n            });\n            // assert\n            expect(actual$).toBeObservable(expected$);\n        });\n    });\n\n    describe('#maxPagesCount', () => {\n        test('should shift all pages order and remove first', () => {\n            // arrange\n            const user1 = {id: 1, name: 'User 1'};\n            const user2 = {id: 2, name: 'User 2'};\n            const user3 = {id: 3, name: 'User 2'};\n            entity.add(user1, Math.random());\n            entity.add(user2, Math.random());\n            const actual$ = entity.getPages$();\n            const actMarble = '     --a';\n            const expectedMarble = 'a-b';\n            const page1: Page<TPageMetadata> = {ids: [user1.id], order: 0};\n            const page2: Page<TPageMetadata> = {ids: [user2.id], order: 1};\n            const page3: Page<TPageMetadata> = {ids: [user3.id]};\n\n            const expected$ = cold(expectedMarble, {\n                a: [page1, page2],\n                b: [{...page2, order: 0}, {...page3, order: 1}],\n            });\n            // act\n            cold(actMarble).subscribe(() => {\n                entity.add(user3, Math.random());\n            });\n            // assert\n            expect(actual$).toBeObservable(expected$);\n        });\n    });\n\n});\n"
  },
  {
    "path": "src/entity/entity.spec.ts",
    "content": "import { Id, LoadingState } from '../system-types';\nimport { getOrderedMarbleStream, initStoreWithPr } from '../testing/helpers';\nimport { cold } from 'jest-marbles';\nimport { TestStore } from '../testing/store';\nimport { createEntity } from './entity';\nimport { createPainlessRedux } from '../painless-redux/painless-redux';\nimport { PainlessRedux } from '../painless-redux/types';\nimport { Entity, EntityInternalSetLoadingStateOptions, EntitySchema, Pagination } from './types';\nimport { BehaviorSubject } from 'rxjs';\nimport { mocked } from 'ts-jest/utils';\nimport * as uuid from 'uuid';\nimport { ChangeOptions } from '../shared/change/types';\nimport { RequestOptions } from '../shared/types';\n\njest.mock('uuid');\n\ninterface TestEntity {\n    name: string;\n}\n\ndescribe('Entity', () => {\n\n    let entity: Entity<TestEntity, void>;\n    let pr: PainlessRedux;\n    let store: TestStore<any>;\n    let user: any;\n    let schema: Partial<EntitySchema<TestEntity>>;\n    let user2: any;\n    let user3: any;\n    let reducer: any;\n    let idFn = jest.fn((data) => data.id);\n\n    const setLoadingStateActionFactory = (\n        state: LoadingState,\n        id?: Id,\n        options?: RequestOptions,\n    ) => entity.actionCreators.SET_LOADING_STATE(\n        state,\n        undefined,\n        id,\n        undefined,\n        options as unknown as EntityInternalSetLoadingStateOptions,\n    );\n\n    beforeEach(() => {\n        store = new TestStore(undefined, (state) => state);\n        pr = createPainlessRedux(store, { useAsapSchedulerInLoadingGuards: false });\n        schema = {\n            name: 'test',\n            pageSize: 2,\n            id: idFn,\n        };\n        entity = createEntity(pr, schema);\n        reducer = initStoreWithPr(store, pr);\n        user = { id: 1, name: 'John' };\n        user2 = { id: 2, name: 'Alex' };\n        user3 = { id: 3, name: 'Frank' };\n    });\n\n    describe('#add', () => {\n        test('should add entity', () => {\n            // arrange\n            const addAction = entity.actionCreators.ADD(user);\n            const actions$ = getOrderedMarbleStream(addAction);\n            // act\n            entity.add(user);\n            // assert\n            expect(store.actions$).toBeObservable(actions$);\n        });\n\n        test('should resolve an id from schema.id even it has id already, then add an entity', () => {\n            // arrange\n            mocked(idFn).mockImplementationOnce((data): string | number => `$${data.name}`);\n            const userWithIgnoredId = { name: user.name, id: 'should not be used' };\n            const addAction = entity.actionCreators.ADD({\n                ...userWithIgnoredId,\n                id: `$${userWithIgnoredId.name}`,\n            });\n            const actions$ = getOrderedMarbleStream(addAction);\n            // act\n            entity.add(userWithIgnoredId);\n            // assert\n            expect(store.actions$).toBeObservable(actions$);\n        });\n\n        xtest('should resolve an id if it is not existed and schema.id is not defined, then add an entity', () => {\n            // arrange\n            const randomId = 'adav3r2brh35';\n            mocked(uuid.v4).mockReturnValueOnce(randomId);\n            const userWithoutId = { name: user.name };\n            const addAction = entity.actionCreators.ADD({\n                ...userWithoutId,\n                id: randomId,\n            });\n            const actions$ = getOrderedMarbleStream(addAction);\n            // act\n            entity.add(userWithoutId);\n            // assert\n            expect(store.actions$).toBeObservable(actions$);\n        });\n    });\n\n    describe('#addList', () => {\n        test('should add entities', () => {\n            // arrange\n            const users = [user, user2];\n            const addListAction = entity.actionCreators.ADD_LIST(users);\n            const actions$ = getOrderedMarbleStream(addListAction);\n            // act\n            entity.addList(users);\n            // assert\n            expect(store.actions$).toBeObservable(actions$);\n        });\n    });\n\n    describe('#change', () => {\n        test('should change entity', () => {\n            // arrange\n            const patch = { name: 'Frank' };\n\n            const changeAction = entity.actionCreators.CHANGE(user.id, patch);\n            const actions$ = getOrderedMarbleStream(changeAction);\n            // act\n            entity.change(user.id, patch);\n            // assert\n            expect(store.actions$).toBeObservable(actions$);\n        });\n    });\n\n    describe('#changeRemote', () => {\n        const patch = { name: 'Frank' };\n        const responsePatch = { name: 'Alice' };\n        const changeId = 'vn3n5k';\n        mocked(uuid.v4).mockReturnValue(changeId);\n\n        test.each`\n             useResponsePatch\n             ${false}\n             ${true}\n             `('should change entity remotely with useResponsePatch: $useResponsePatch', ({ useResponsePatch }) => {\n            // arrange\n            const options: ChangeOptions = { useResponsePatch, rethrow: false };\n            const id = user.id;\n            const remote$ = cold(' --a| ', { a: responsePatch });\n            const resolvePatch = useResponsePatch ? responsePatch : patch;\n            const changeAction = entity.actionCreators.CHANGE(id, resolvePatch, changeId, options);\n            const actions$ = cold('a-(bc)', {\n                a: setLoadingStateActionFactory({ isLoading: true }, id, options),\n                b: changeAction,\n                c: setLoadingStateActionFactory({ isLoading: false }, id, options),\n            });\n            // act\n            entity.changeRemote$(id, patch, remote$, options).subscribe();\n            // assert\n            expect(store.actions$).toBeObservable(actions$);\n        });\n\n        describe('#optimistic', () => {\n            test.each`\n\t        \tuseResponsePatch\n\t\t        ${false}\n\t\t        ${true}\n\t            `('should change entity remotely with useResponsePatch: $useResponsePatch', ({ useResponsePatch }) => {\n                const options: ChangeOptions = { optimistic: true, useResponsePatch, rethrow: false };\n                const remote$ = cold(' --a| ', { a: responsePatch });\n                const id = user.id;\n                const changeAction = entity.actionCreators.CHANGE(id, patch, changeId, options);\n                const resolvePatch = useResponsePatch ? responsePatch : undefined;\n                const resolveChangeAction = entity.actionCreators.RESOLVE_CHANGE(\n                    id,\n                    changeId,\n                    true,\n                    resolvePatch,\n                    options,\n                );\n                const actions$ = cold('a-b', {\n                    a: changeAction,\n                    b: resolveChangeAction,\n                });\n                // act\n                entity.changeRemote$(id, patch, remote$, options).subscribe();\n                // assert\n                expect(store.actions$).toBeObservable(actions$);\n            });\n\n            test('should resolve unsuccessfully if response failed', () => {\n                const options: ChangeOptions = { optimistic: true, rethrow: false };\n                const error = 'Error';\n                const failedRemote$ = cold(' --#|', undefined, error);\n                const id = user.id;\n                const changeAction = entity.actionCreators.CHANGE(id, patch, changeId, options);\n                const resolveChangeAction = entity.actionCreators.RESOLVE_CHANGE(\n                    id,\n                    changeId,\n                    false,\n                    undefined,\n                    options,\n                );\n                const actions$ = cold('a-(bc)', {\n                    a: changeAction,\n                    b: resolveChangeAction,\n                    c: setLoadingStateActionFactory({ isLoading: false, error }, id, options),\n                });\n                // act\n                entity.changeRemote$(id, patch, failedRemote$, options).subscribe();\n                // assert\n                expect(store.actions$).toBeObservable(actions$);\n            });\n        });\n\n    });\n\n    describe('#remove', () => {\n        test('should remove entity', () => {\n            // arrange\n            const removeAction = entity.actionCreators.REMOVE(user.id);\n            const actions$ = getOrderedMarbleStream(removeAction);\n            // act\n            entity.remove(user.id);\n            // assert\n            expect(store.actions$).toBeObservable(actions$);\n        });\n\n        test('should remote remove entity', () => {\n            // arrange\n            const options = { rethrow: false };\n            const removeAction = entity.actionCreators.REMOVE(user.id, options);\n            const remote$ = cold(' --a| ', { a: null });\n            const actions$ = cold('a-(bc)', {\n                a: setLoadingStateActionFactory({ isLoading: true }, user.id, options),\n                b: setLoadingStateActionFactory({ isLoading: false }, user.id, options),\n                c: removeAction,\n            });\n            // act\n            entity.removeRemote$(user.id, remote$, options).subscribe();\n            // assert\n            expect(store.actions$).toBeObservable(actions$);\n        });\n\n        test('should optimistic remove entity', () => {\n            // arrange\n            const options = { optimistic: true, rethrow: false };\n            const removeAction = entity.actionCreators.REMOVE(user.id, options);\n            const resolveRemoveAction = entity.actionCreators.RESOLVE_REMOVE(user.id, true, options);\n            const remote$ = cold(' --a| ', { a: null });\n            const actions$ = cold('a-b', {\n                a: removeAction,\n                b: resolveRemoveAction,\n            });\n            // act\n            entity.removeRemote$(user.id, remote$, options).subscribe();\n            // assert\n            expect(store.actions$).toBeObservable(actions$);\n        });\n\n        test('should optimistic undo if failed', () => {\n            // arrange\n            const options = { optimistic: true, rethrow: false };\n            const error = 'Failed';\n            const id = user.id;\n            const removeAction = entity.actionCreators.REMOVE(id, options);\n            const resolveRemoveAction = entity.actionCreators.RESOLVE_REMOVE(id, false, options);\n            const remote$ = cold(' --#| ', undefined, error);\n            const actions$ = cold('a-(bc)', {\n                a: removeAction,\n                b: resolveRemoveAction,\n                c: setLoadingStateActionFactory({ isLoading: false, error }, id, options),\n            });\n            // act\n            entity.removeRemote$(id, remote$, options).subscribe();\n            // assert\n            expect(store.actions$).toBeObservable(actions$);\n        });\n\n    });\n\n    describe('#removeList', () => {\n        test('should remove entities', () => {\n            // arrange\n            const ids = [user.id, user2.id];\n            const removeAction = entity.actionCreators.REMOVE_LIST(ids);\n            const actions$ = getOrderedMarbleStream(removeAction);\n            // act\n            entity.removeList(ids);\n            // assert\n            expect(store.actions$).toBeObservable(actions$);\n        });\n\n        test('should remote remove entities', () => {\n            // arrange\n            const options = { rethrow: false };\n            const ids = [user.id, user2.id];\n            const removeAction = entity.actionCreators.REMOVE_LIST(ids, options);\n            const remote$ = cold(' --a| ', { a: null });\n            const actions$ = cold('a-(bc)', {\n                a: entity.actionCreators.SET_LOADING_STATES({ isLoading: true }, ids),\n                b: entity.actionCreators.SET_LOADING_STATES({ isLoading: false }, ids),\n                c: removeAction,\n            });\n            // act\n            entity.removeListRemote$(ids, remote$, options).subscribe();\n            // assert\n            expect(store.actions$).toBeObservable(actions$);\n        });\n\n        test('should optimistic remove entities', () => {\n            // arrange\n            const ids = [user.id, user2.id];\n            const options = { optimistic: true, rethrow: false };\n            const removeAction = entity.actionCreators.REMOVE_LIST(ids, options);\n            const resolveRemoveAction = entity.actionCreators.RESOLVE_REMOVE_LIST(ids, true, options);\n            const remote$ = cold(' --a| ', { a: null });\n            const actions$ = cold('a-b', {\n                a: removeAction,\n                b: resolveRemoveAction,\n            });\n            // act\n            entity.removeListRemote$(ids, remote$, options).subscribe();\n            // assert\n            expect(store.actions$).toBeObservable(actions$);\n        });\n\n        test('should optimistic undo if failed', () => {\n            // arrange\n            const ids = [user.id, user2.id];\n            const options = { optimistic: true, rethrow: false };\n            const error = 'Failed';\n            const removeAction = entity.actionCreators.REMOVE_LIST(ids, options);\n            const resolveRemoveAction = entity.actionCreators.RESOLVE_REMOVE_LIST(ids, false, options);\n            const remote$ = cold(' --#| ', undefined, error);\n            const actions$ = cold('a-(bc)', {\n                a: removeAction,\n                b: resolveRemoveAction,\n                c: entity.actionCreators.SET_LOADING_STATES({ isLoading: false, error }, ids),\n            });\n            // act\n            entity.removeListRemote$(ids, remote$, options).subscribe();\n            // assert\n            expect(store.actions$).toBeObservable(actions$);\n        });\n\n    });\n\n    describe('#create', () => {\n        test('should create entity', () => {\n            // arrange\n            const createAction = entity.actionCreators.ADD(user);\n            const actions$ = cold('a', { a: createAction });\n            // act\n            entity.add(user);\n            // assert\n            expect(store.actions$).toBeObservable(actions$);\n        });\n\n        test('should remote create entity', () => {\n            // arrange\n            const options = { rethrow: false };\n            const remote$ = cold('--a|', { a: user });\n            const createAction = entity.actionCreators.ADD(user, undefined, undefined, options);\n            const actions$ = cold('a-(bc)', {\n                a: setLoadingStateActionFactory({ isLoading: true }, undefined, options),\n                b: createAction,\n                c: setLoadingStateActionFactory({ isLoading: false }, undefined, options),\n            });\n            // act\n            const actual = entity.addRemote$(user, undefined, remote$, options);\n            // assert\n            expect(actual).toBeObservable(remote$);\n            expect(store.actions$).toBeObservable(actions$);\n        });\n    });\n\n    describe('#getById$', () => {\n        test('should return observable to entity', () => {\n            // arrange\n            const storeObs = cold('a', { a: undefined });\n            // act\n            const actual = entity.getById$(1);\n            // assert\n            expect(actual).toBeObservable(storeObs);\n        });\n\n        test('should load entity', () => {\n            // arrange\n            const remote$ = cold('--a|', { a: user });\n            const addAction = entity.actionCreators.ADD(user);\n            const actions$ = cold('a-(bc)', {\n                a: setLoadingStateActionFactory({ isLoading: true }, user.id),\n                b: addAction,\n                c: setLoadingStateActionFactory({ isLoading: false }, user.id),\n            });\n            // act\n            entity.getById$(user.id, remote$).subscribe();\n            // assert\n            expect(store.actions$).toBeObservable(actions$);\n        });\n\n        test('should load entity with tuple id', () => {\n            // arrange\n            const data = { objectId: '23a4123', type: 5, name: 'Some object 1' };\n            const remote$ = cold('--a|', { a: data });\n            const id = [data.objectId, data.type].toString();\n            const addAction = entity.actionCreators.ADD({ id, ...data });\n            const actions$ = cold('a-(bc)', {\n                a: setLoadingStateActionFactory({ isLoading: true }, id),\n                b: addAction,\n                c: setLoadingStateActionFactory({ isLoading: false }, id),\n            });\n            // act\n            entity.getById$(id, remote$).subscribe();\n            // assert\n            expect(store.actions$).toBeObservable(actions$);\n        });\n    });\n\n    describe('#get$', () => {\n\n        test('should return observable to entity', () => {\n            // arrange\n            const storeObs = cold('a', { a: undefined });\n            // act\n            const actual = entity.get$(null);\n            // assert\n            expect(actual).toBeObservable(storeObs);\n        });\n\n        test('should load entities', () => {\n            // arrange\n            const users = [user];\n            const remote$ = cold('--a|', { a: { data: users } });\n            const filter = null;\n            const addAction = entity.actionCreators.ADD_LIST(users, filter, true, false);\n            const actions$ = cold('a-(bc)', {\n                a: setLoadingStateActionFactory({ isLoading: true }),\n                b: addAction,\n                c: setLoadingStateActionFactory({ isLoading: false }),\n            });\n            // act\n            entity.get$(filter, remote$).subscribe();\n            // assert\n            expect(store.actions$).toBeObservable(actions$);\n        });\n\n        xtest('should log if observable source throws error', () => {\n            // arrange\n            const error = new Error('Some error');\n            const remote$ = cold('#|', null, error);\n            global.console.error = jest.fn();\n            // act\n            entity.get$(null, remote$).subscribe();\n            // assert\n            expect(global.console.error).toBeCalled();\n        });\n\n        test('should set error if observable source throws error', () => {\n            // arrange\n            const error = 'Some error';\n            const remote$ = cold('--#|', null, error);\n            const actions$ = cold('a-b', {\n                a: setLoadingStateActionFactory({ isLoading: true }),\n                b: setLoadingStateActionFactory({ isLoading: false, error }),\n            });\n            // act\n            entity.get$(null, remote$).subscribe();\n            // assert\n            expect(store.actions$).toBeObservable(actions$);\n        });\n\n        test('should page entities', () => {\n            // arrange\n            const remoteMarble = '    --a|   --a|   --a|   --a|  ';\n            const paginationMarble = '-------a------b------b-----';\n            const actionsMarble = '   a-(bc)-a-(ec)-a-(bc)-a-(bc)';\n            const filter = null;\n            const options = undefined;\n            const users1 = [user, user2];\n            const users2 = [user3];\n            const remote$ = (value: Pagination) => {\n                const users = value.index ? users2 : users1;\n                return cold('--a|', { a: { data: users } });\n            };\n            const addAction = entity.actionCreators.ADD_LIST(users1, filter, true, true);\n            const addAction2 = entity.actionCreators.ADD_LIST(users2, filter);\n            const paginator = new BehaviorSubject<boolean>(true);\n            const setLoadingStateTrueAction = setLoadingStateActionFactory({ isLoading: true });\n            const setLoadingStateFalseAction = setLoadingStateActionFactory({ isLoading: false });\n\n            cold(paginationMarble, {\n                a: true,\n                b: false,\n            }).subscribe((value) => {\n                paginator.next(value);\n            });\n\n            const actions$ = cold(actionsMarble, {\n                a: setLoadingStateTrueAction,\n                b: addAction,\n                c: setLoadingStateFalseAction,\n                e: addAction2,\n            });\n            // act\n            entity.get$(filter, remote$, options, paginator).subscribe();\n            // assert\n            expect(store.actions$).toBeObservable(actions$);\n        });\n    });\n\n    describe('#getDictionary$', () => {\n        test('should return page as dictionary observable', () => {\n            // arrange\n            const storeObs = cold('a', { a: {} });\n            // act\n            const actual = entity.getDictionary$(undefined);\n            // assert\n            expect(actual).toBeObservable(storeObs);\n        });\n    });\n\n});\n"
  },
  {
    "path": "src/entity/entity.ts",
    "content": "import { Entity, EntityActionTypes, EntitySchema, EntityState } from './types';\nimport { createEntitySelectors } from './selectors';\nimport { PainlessRedux, SlotTypes } from '../painless-redux/types';\nimport { createEntityActionTypes, createIdResolver, getFullEntitySchema } from './utils';\nimport { createEntityReducer } from './reducer';\nimport { EntityActions } from './actions';\nimport { createDispatchEntityMethods } from './methods/dispatch/dispatch';\nimport { createEntityActionCreators } from './action-creators';\nimport { createSelectEntityMethods } from './methods/select/select';\nimport { createMixedEntityMethods } from './methods/mixed/mixed';\n\nexport const createEntity = <T, TPageMetadata = void>(\n    pr: PainlessRedux,\n    schema?: Partial<EntitySchema<T>>,\n): Entity<T, TPageMetadata> => {\n    const fullSchema = getFullEntitySchema<T>(schema);\n    const actionTypes = createEntityActionTypes(fullSchema.name);\n    const actionCreators = createEntityActionCreators<T, TPageMetadata>(actionTypes, fullSchema);\n    const reducer = createEntityReducer<T, TPageMetadata>(actionTypes, fullSchema);\n    const {\n        selector,\n        dispatcher,\n        selectManager,\n    } = pr.registerSlot<EntityState<T, TPageMetadata>, EntityActionTypes, EntityActions>(\n        SlotTypes.Entity,\n        fullSchema.name,\n        reducer,\n        actionCreators,\n    );\n    const selectors = createEntitySelectors<T, TPageMetadata>(selector, fullSchema.hashFn);\n    const idResolver = createIdResolver<T>(fullSchema);\n\n    const selectMethods = createSelectEntityMethods<T, TPageMetadata>(selectManager, selectors);\n    const dispatchMethods = createDispatchEntityMethods<T, TPageMetadata>(dispatcher, idResolver, selectMethods, fullSchema);\n    const mixedMethods = createMixedEntityMethods<T, TPageMetadata>(dispatchMethods, selectMethods, fullSchema, pr.schema);\n\n    const { changeWithId, resolveChange, ...publicDispatchMethods } = dispatchMethods;\n    const { get$, getDictionary$, getById$, ...publicSelectMethods } = selectMethods;\n\n    return {\n        ...publicDispatchMethods,\n        ...mixedMethods,\n        ...publicSelectMethods,\n        actionCreators,\n    };\n};\n"
  },
  {
    "path": "src/entity/methods/dispatch/dispatch.ts",
    "content": "import { Dispatcher } from '../../../dispatcher/types';\nimport {\n    EntityActionTypes,\n    EntityAddListOptions,\n    EntityAddOptions,\n    EntityInternalAddListOptions,\n    EntityInternalAddOptions,\n    EntityInternalSetLoadingStateOptions,\n    EntityRemoveListOptions,\n    EntityRemoveOptions,\n    EntitySchema,\n    EntitySetLoadingStateOptions,\n    EntityType,\n    IdPatch,\n    IdPatchRequest,\n} from '../../types';\nimport { EntityActions } from '../../actions';\nimport { DeepPartial, Id, LoadingState } from '../../../system-types';\nimport { DispatchEntityMethods } from './types';\nimport { isNil } from 'lodash-es';\nimport { affectLoadingStateFactory } from '../../../affect-loading-state/affect-loading-state';\nimport { ChangeOptions, PatchRequest } from '../../../shared/change/types';\nimport { normalizePatch } from '../../../shared/change/utils';\nimport { SelectEntityMethods } from '../select/types';\nimport { AffectLoadingStateFactory } from '../../..';\n\nexport const createDispatchEntityMethods = <T, TPageMetadata>(\n    dispatcher: Dispatcher<EntityActionTypes, EntityActions>,\n    idResolver: (data: T) => EntityType<T>,\n    selectMethods: SelectEntityMethods<T, TPageMetadata>,\n    schema: EntitySchema<T>,\n): DispatchEntityMethods<T, TPageMetadata> => {\n\n    const add = (\n        entity: T,\n        config?: unknown,\n        options?: EntityAddOptions,\n    ) => {\n        entity = idResolver(entity);\n        const internalOptions: EntityInternalAddOptions = {\n            ...options,\n            maxPagesCount: schema.maxPagesCount,\n        };\n        return dispatcher.createAndDispatch('ADD', [entity, config, undefined], internalOptions);\n    };\n\n    const addWithId = (\n        entity: T,\n        tempId: string,\n        config?: unknown,\n        options?: EntityAddOptions,\n    ) => {\n        const internalOptions: EntityInternalAddOptions = {\n            ...options,\n            maxPagesCount: schema.maxPagesCount,\n        };\n        return dispatcher.createAndDispatch('ADD', [entity, config, tempId], internalOptions);\n    };\n\n    const resolveAdd = (\n        result: T,\n        success: boolean,\n        tempId: string,\n        config?: unknown,\n        options?: EntityAddOptions,\n    ) => {\n        return dispatcher.createAndDispatch('RESOLVE_ADD', [result, success, tempId, config], options);\n    };\n\n    const addList = (\n        entities: T[],\n        config?: unknown,\n        isReplace: boolean = false,\n        hasMore: boolean = false,\n        metadata?: TPageMetadata,\n        options?: EntityAddListOptions,\n    ) => {\n        const internalOptions: EntityInternalAddListOptions = {\n            ...options,\n            maxPagesCount: schema.maxPagesCount,\n        };\n        entities = entities.map((entity) => idResolver(entity));\n        return dispatcher.createAndDispatch('ADD_LIST', [entities, config, isReplace, hasMore, metadata], internalOptions);\n    };\n\n    const changeWithId = (\n        id: Id,\n        patch: PatchRequest<T>,\n        changeId: string | undefined,\n        options?: ChangeOptions,\n    ) => {\n        const normalizedPatch = normalizePatch(patch, selectMethods.getById$(id));\n        return dispatcher.createAndDispatch('CHANGE', [id, normalizedPatch, changeId], options);\n    };\n\n    const change = (\n        id: Id,\n        patch: PatchRequest<T>,\n        options?: ChangeOptions,\n    ) => {\n        return changeWithId(id, patch, undefined, options);\n    };\n\n    const changeListWithId = (\n        patches: IdPatchRequest<T>[],\n        changeId: string | undefined,\n        options?: ChangeOptions,\n    ) => {\n        const normalizedPatches: IdPatch<T>[] = patches.map((patch) => ({\n            ...patch,\n            patch: normalizePatch(patch.patch, selectMethods.getById$(patch.id)),\n        }));\n        return dispatcher.createAndDispatch('CHANGE_LIST', [normalizedPatches, changeId], options);\n    };\n\n    const changeList = (\n        patches: IdPatchRequest<T>[],\n        options?: ChangeOptions,\n    ) => {\n        return changeListWithId(patches, undefined, options);\n    };\n\n    const resolveChange = (\n        id: Id,\n        changeId: Id,\n        success: boolean,\n        remotePatch: DeepPartial<T>,\n        options?: ChangeOptions,\n    ) => {\n        return dispatcher.createAndDispatch('RESOLVE_CHANGE', [id, changeId, success, remotePatch], options);\n    };\n\n    const resolveChangeList = (\n        patches: IdPatch<T>[],\n        changeId: string,\n        success: boolean,\n        options?: ChangeOptions,\n    ) => {\n        return dispatcher.createAndDispatch('RESOLVE_CHANGE_LIST', [patches, changeId, success], options);\n    };\n\n    const remove = (\n        id: Id,\n        options?: EntityRemoveOptions,\n    ) => {\n        return dispatcher.createAndDispatch('REMOVE', [id], options);\n    };\n\n    const resolveRemove = (\n        id: Id,\n        success: boolean,\n        options?: EntityRemoveOptions,\n    ) => {\n        return dispatcher.createAndDispatch('RESOLVE_REMOVE', [id, success], options);\n    };\n\n    const restoreRemoved = (\n        id: Id,\n    ) => {\n        return dispatcher.createAndDispatch('RESTORE_REMOVED', [id]);\n    };\n\n    const removeList = (\n        ids: Id[],\n        options?: EntityRemoveListOptions,\n    ) => {\n        return dispatcher.createAndDispatch('REMOVE_LIST', [ids], options);\n    };\n\n    const resolveRemoveList = (\n        ids: Id[],\n        success: boolean,\n        options?: EntityRemoveListOptions,\n    ) => {\n        return dispatcher.createAndDispatch('RESOLVE_REMOVE_LIST', [ids, success], options);\n    };\n\n    const restoreRemovedList = (\n        ids: Id[],\n    ) => {\n        return dispatcher.createAndDispatch('RESTORE_REMOVED_LIST', [ids]);\n    };\n\n    const setLoadingState = (\n        state: LoadingState,\n        config?: unknown,\n        options?: EntitySetLoadingStateOptions,\n    ) => {\n        const internalOptions: EntityInternalSetLoadingStateOptions = {\n            ...options,\n            maxPagesCount: schema.maxPagesCount,\n        };\n        return dispatcher.createAndDispatch('SET_LOADING_STATE', [state, config, undefined, undefined], internalOptions);\n    };\n\n    const setLoadingStateById = (\n        id: Id,\n        state: LoadingState,\n        options?: EntitySetLoadingStateOptions,\n    ) => {\n        const internalOptions: EntityInternalSetLoadingStateOptions = {\n            ...options,\n            maxPagesCount: schema.maxPagesCount,\n        };\n        return dispatcher.createAndDispatch('SET_LOADING_STATE', [state, undefined, id, undefined], internalOptions);\n    };\n\n    const setLoadingStateForKey = (\n        id: Id,\n        key: string,\n        state: LoadingState,\n        options?: EntitySetLoadingStateOptions,\n    ) => {\n        const internalOptions: EntityInternalSetLoadingStateOptions = {\n            ...options,\n            maxPagesCount: schema.maxPagesCount,\n        };\n        return dispatcher.createAndDispatch('SET_LOADING_STATE', [state, undefined, id, key], internalOptions);\n    };\n\n    const setLoadingStateByIds = (\n        ids: Id[],\n        state: LoadingState,\n        options?: EntitySetLoadingStateOptions,\n    ) => {\n        const internalOptions: EntityInternalSetLoadingStateOptions = {\n            ...options,\n            maxPagesCount: schema.maxPagesCount,\n        };\n        return dispatcher.createAndDispatch('SET_LOADING_STATES', [state, ids], internalOptions);\n    };\n\n    const clear = (config: unknown) => {\n        return dispatcher.createAndDispatch('CLEAR', [config]);\n    };\n    const clearAll = () => {\n        return dispatcher.createAndDispatch('CLEAR_ALL', []);\n    };\n\n    const setLoadingStateBus = (\n        state: LoadingState,\n        id?: Id,\n        config?: unknown,\n        key?: string,\n        options?: EntitySetLoadingStateOptions,\n    ) => {\n        if (!isNil(id)) {\n            if (!isNil(key)) {\n                return setLoadingStateForKey(id, key, state, options);\n            } else {\n                return setLoadingStateById(id, state, options);\n            }\n        } else {\n            if (state.error) {\n                console.error(state.error);\n            }\n            return setLoadingState(state, config, options);\n        }\n    };\n\n    const affectLoadingState = (\n        config?: unknown,\n        key?: string,\n        rethrow?: boolean,\n    ) => {\n        const setter = (state: LoadingState) => {\n            setLoadingStateBus(state, undefined, config, key);\n        };\n        return affectLoadingStateFactory(setter, rethrow);\n    };\n\n    const affectLoadingStateById = (\n        id?: Id,\n        key?: string,\n        rethrow?: boolean,\n    ) => {\n        const setter = (state: LoadingState) => {\n            setLoadingStateBus(state, id, undefined, key);\n        };\n        return affectLoadingStateFactory(setter, rethrow);\n    };\n\n    const affectLoadingStateByConfigOrId = (\n        config?: unknown,\n        id?: Id,\n        key?: string,\n        rethrow?: boolean,\n    ): AffectLoadingStateFactory => {\n        const setter = (state: LoadingState) => {\n            setLoadingStateBus(state, id, config, key);\n        };\n        return affectLoadingStateFactory(setter, rethrow);\n    };\n\n    const batch = (\n        actions: EntityActions[],\n    ) => {\n        return dispatcher.createAndDispatch('BATCH', [actions]);\n    };\n\n    return {\n        add,\n        addWithId,\n        addList,\n        change,\n        changeList,\n        changeListWithId,\n        changeWithId,\n        resolveChange,\n        resolveChangeList,\n        resolveAdd,\n        remove,\n        resolveRemove,\n        restoreRemoved,\n        removeList,\n        resolveRemoveList,\n        restoreRemovedList,\n        setLoadingState,\n        clear,\n        clearAll,\n        setLoadingStateBus,\n        setLoadingStateById,\n        setLoadingStateForKey,\n        affectLoadingState,\n        affectLoadingStateById,\n        affectLoadingStateByConfigOrId,\n        batch,\n        setLoadingStateByIds,\n    };\n};\n"
  },
  {
    "path": "src/entity/methods/dispatch/types.ts",
    "content": "import {\n    EntityAddListOptions,\n    EntityAddOptions,\n    EntityRemoveListOptions,\n    EntityRemoveOptions,\n    EntitySetLoadingStateOptions,\n    IdPatch,\n    IdPatchRequest,\n} from '../../types';\nimport { DeepPartial, Id, LoadingState } from '../../../system-types';\nimport { EntityActions } from '../../actions';\nimport { ChangeOptions, PatchRequest } from '../../../shared/change/types';\nimport { AffectLoadingStateFactory } from '../../..';\n\nexport interface DispatchEntityMethods<T, TPageMetadata> {\n    add(\n        data: T,\n        config?: unknown,\n        options?: EntityAddOptions,\n    ): EntityActions;\n\n    addWithId(\n        data: T,\n        tempId: string,\n        config?: unknown,\n        options?: EntityAddOptions,\n    ): EntityActions;\n\n    resolveAdd(\n        data: T | undefined,\n        success: boolean,\n        tempId: string,\n        config?: unknown,\n        options?: EntityAddOptions,\n    ): EntityActions;\n\n    addList(\n        data: T[],\n        config?: unknown,\n        isReplace?: boolean,\n        hasMore?: boolean,\n        metadata?: TPageMetadata,\n        options?: EntityAddListOptions,\n    ): EntityActions;\n\n    change(\n        id: Id,\n        patch: PatchRequest<T>,\n        options?: ChangeOptions,\n    ): EntityActions;\n\n    changeList(\n        patches: IdPatchRequest<T>[],\n        options?: ChangeOptions,\n    ): EntityActions;\n\n    changeListWithId(\n        patches: IdPatchRequest<T>[],\n        changeId: string,\n        options?: ChangeOptions,\n    ): EntityActions;\n\n    changeWithId(\n        id: Id,\n        patch: PatchRequest<T>,\n        changeId: string,\n        options?: ChangeOptions,\n    ): EntityActions;\n\n    resolveChange(\n        id: Id,\n        changeId: string,\n        success: boolean,\n        remotePatch?: DeepPartial<T>,\n        options?: ChangeOptions,\n    ): EntityActions;\n\n    resolveChangeList(\n        patches: IdPatch<T>[],\n        changeId: string,\n        success: boolean,\n        options?: ChangeOptions,\n    ): EntityActions;\n\n    remove(\n        id: Id,\n        options?: EntityRemoveOptions,\n    ): EntityActions;\n\n    resolveRemove(\n        id: Id,\n        success: boolean,\n        options?: EntityRemoveOptions,\n    ): EntityActions;\n\n    restoreRemoved(\n        id: Id,\n    ): EntityActions;\n\n    removeList(\n        ids: Id[],\n        options?: EntityRemoveListOptions,\n    ): EntityActions;\n\n    resolveRemoveList(\n        ids: Id[],\n        success: boolean,\n        options?: EntityRemoveListOptions,\n    ): EntityActions;\n\n    restoreRemovedList(\n        ids: Id[],\n    ): EntityActions;\n\n    setLoadingState(\n        state: LoadingState,\n        config?: unknown,\n        options?: EntitySetLoadingStateOptions,\n    ): EntityActions;\n\n    clear(config: unknown): EntityActions;\n\n    clearAll(): EntityActions;\n\n    setLoadingStateById(\n        id: Id,\n        state: LoadingState,\n        options?: EntitySetLoadingStateOptions,\n    ): EntityActions;\n\n    setLoadingStateByIds(\n        ids: Id[],\n        state: LoadingState,\n        options?: EntitySetLoadingStateOptions,\n    ): EntityActions;\n\n    setLoadingStateForKey(\n        id: Id,\n        key: string,\n        state: LoadingState,\n        options?: EntitySetLoadingStateOptions,\n    ): EntityActions;\n\n    setLoadingStateBus(\n        state: LoadingState,\n        id?: Id,\n        config?: unknown,\n        key?: string,\n        options?: EntitySetLoadingStateOptions,\n    ): EntityActions;\n\n    batch(\n        actions: EntityActions[],\n    ): EntityActions;\n\n    affectLoadingState(\n        config?: unknown,\n        key?: string,\n        rethrow?: boolean,\n    ): AffectLoadingStateFactory;\n\n    affectLoadingStateById(\n        id?: Id,\n        key?: string,\n        rethrow?: boolean,\n    ): AffectLoadingStateFactory;\n\n    affectLoadingStateByConfigOrId(\n        config?: unknown,\n        id?: Id,\n        key?: string,\n        rethrow?: boolean,\n    ): AffectLoadingStateFactory;\n}\n"
  },
  {
    "path": "src/entity/methods/mixed/mixed.ts",
    "content": "import {\n    EntityAddOptions,\n    EntityGetListOptions,\n    EntityGetOptions,\n    EntityLoadListOptions,\n    EntityLoadOptions,\n    EntityRemoveListOptions,\n    EntityRemoveOptions,\n    EntitySchema,\n    IdPatch,\n    IdPatchRequest,\n    PaginatedResponse,\n    Pagination,\n    Response$Factory,\n    ResponseArray,\n} from '../../types';\nimport { BehaviorSubject, EMPTY, merge, Observable, of } from 'rxjs';\nimport { DispatchEntityMethods } from '../dispatch/types';\nimport { SelectEntityMethods } from '../select/types';\nimport { getPaginated$ } from '../../utils';\nimport { DeepPartial, Dictionary, Id, LoadingState } from '../../../system-types';\nimport { createMixedEntityMethodsUtils } from './utils';\nimport { MixedEntityMethods } from './types';\nimport { v4 } from 'uuid';\nimport { PainlessReduxSchema } from '../../../painless-redux/types';\nimport { ChangeOptions, PatchRequest } from '../../../shared/change/types';\nimport { getPatchByOptions, getResolvePatchByOptions, normalizePatch } from '../../../shared/change/utils';\nimport { getRemotePipe, guardIfLoading } from '../../../shared/utils';\nimport { switchMap } from 'rxjs/operators';\nimport { typedDefaultsDeep } from '../../../utils';\n\nexport const createMixedEntityMethods = <T, TPageMetadata>(\n    dispatchMethods: DispatchEntityMethods<T, TPageMetadata>,\n    selectMethods: SelectEntityMethods<T, TPageMetadata>,\n    schema: EntitySchema<T>,\n    prSchema: PainlessReduxSchema,\n): MixedEntityMethods<T, TPageMetadata> => {\n\n    const {\n        getPaginator,\n        tryInvoke,\n    } = createMixedEntityMethodsUtils<T, TPageMetadata>(dispatchMethods, selectMethods, schema, prSchema);\n\n    const loadList$ = (\n        config: unknown,\n        dataSource: Observable<ResponseArray<T, TPageMetadata>> | Response$Factory<T, TPageMetadata>,\n        options?: EntityLoadListOptions,\n        paginatorSubj?: BehaviorSubject<boolean>,\n    ): Observable<PaginatedResponse<T, TPageMetadata>> => {\n        const store$ = selectMethods.get$(config);\n        const sourcePipe = getRemotePipe<Pagination, T[] | undefined, PaginatedResponse<T, TPageMetadata>, PaginatedResponse<T, TPageMetadata>>({\n                options,\n                store$,\n                emitOnSuccess: true,\n                remoteObsOrFactory: (pagination: Pagination) => getPaginated$(dataSource, pagination),\n                success: (result?: PaginatedResponse<T, TPageMetadata>) => {\n                    if (!result) return;\n                    const { index, size, response } = result;\n                    const data = response.data ?? [];\n                    const isReplace = index === 0;\n                    const hasMore = response.hasMore ?? data.length >= size;\n                    const metadata = response.metadata;\n                    return dispatchMethods.addList(data, config, isReplace, hasMore, metadata, options);\n                },\n                setLoadingState: (state) => dispatchMethods.setLoadingStateBus(state, undefined, config, undefined, options),\n            },\n        );\n        const paginator = getPaginator(config, paginatorSubj, options);\n        return paginator.pipe(sourcePipe);\n    };\n\n    const loadById$ = (\n        id: Id,\n        dataSource$: Observable<T>,\n        options?: EntityLoadOptions,\n    ): Observable<T> => {\n        const store$ = selectMethods.getById$(id);\n        const sourcePipe = getRemotePipe<LoadingState | undefined, T | undefined, T, T>({\n            options,\n            store$,\n            emitOnSuccess: true,\n            remoteObsOrFactory: dataSource$,\n            success: (response) => {\n                if (!response) return;\n                const entity = { ...response, id };\n                return dispatchMethods.add(entity, undefined, options);\n            },\n            setLoadingState: (state) => dispatchMethods.setLoadingStateBus(state, id, undefined, undefined, options),\n        });\n        const loadingState$ = selectMethods.getLoadingStateById$(id, prSchema.useAsapSchedulerInLoadingGuards);\n        return guardIfLoading(loadingState$).pipe(sourcePipe);\n    };\n\n    const tryInvokeList$ = <S>(\n        store$: Observable<S>,\n        config: unknown,\n        dataSource?: Observable<ResponseArray<T, TPageMetadata>> | Response$Factory<T, TPageMetadata>,\n        options?: EntityGetListOptions,\n        paginatorSubj?: BehaviorSubject<boolean>,\n    ) => {\n        const invoker = (ds: Observable<ResponseArray<T, TPageMetadata>> | Response$Factory<T, TPageMetadata>) => loadList$(\n            config,\n            ds,\n            options,\n            paginatorSubj,\n        );\n        return tryInvoke(store$, invoker, dataSource);\n    };\n\n    const get$ = (\n        config: unknown,\n        dataSource?: Observable<ResponseArray<T, TPageMetadata>> | Response$Factory<T, TPageMetadata>,\n        options?: EntityGetListOptions,\n        paginatorSubj?: BehaviorSubject<boolean>,\n    ): Observable<T[] | undefined> => {\n        const store$ = selectMethods.get$(config);\n        return tryInvokeList$(\n            store$,\n            config,\n            dataSource,\n            options,\n            paginatorSubj,\n        );\n    };\n\n    const getDictionary$ = (\n        config: unknown,\n        dataSource?: Observable<ResponseArray<T, TPageMetadata>> | Response$Factory<T, TPageMetadata>,\n        options?: EntityGetListOptions,\n        paginatorSubj?: BehaviorSubject<boolean>,\n    ): Observable<Dictionary<T>> => {\n        const store$ = selectMethods.getDictionary$(config);\n        return tryInvokeList$(\n            store$,\n            config,\n            dataSource,\n            options,\n            paginatorSubj,\n        );\n    };\n\n    const getById$ = (\n        id: Id,\n        dataSource?: Observable<T>,\n        options?: EntityGetOptions,\n    ): Observable<T | undefined> => {\n        const store$ = selectMethods.getById$(id);\n        if (dataSource) {\n            const remote$ = loadById$(id, dataSource, options).pipe(switchMap(() => EMPTY));\n            return merge(store$, remote$);\n        }\n        return store$;\n    };\n\n    const addRemote$ = (\n        entity: T,\n        config: unknown,\n        dataSource$: Observable<T>,\n        options?: EntityAddOptions,\n    ): Observable<T> => {\n        const tempId = v4();\n        options = typedDefaultsDeep(options, { rethrow: true });\n        const { addWithId, resolveAdd } = dispatchMethods;\n        const sourcePipe = getRemotePipe<null, unknown, T, T>({\n            options,\n            remoteObsOrFactory: dataSource$,\n            success: (result) => {\n                const newEntity = options?.optimistic ? entity : result;\n                if (!newEntity) return;\n                return addWithId(newEntity, tempId, config, options);\n            },\n            emitOnSuccess: true,\n            optimistic: options.optimistic,\n            optimisticResolve: (success, result) => resolveAdd(result, success, tempId, config, options),\n            setLoadingState: (state) => dispatchMethods.setLoadingStateBus(state, undefined, config, undefined, options),\n        });\n        return of(null).pipe(sourcePipe);\n    };\n\n    const changeRemote$ = (\n        id: Id,\n        patch: PatchRequest<T>,\n        dataSource$: Observable<DeepPartial<T> | undefined>,\n        options?: ChangeOptions,\n    ): Observable<DeepPartial<T> | undefined> => {\n        options = typedDefaultsDeep(options, { rethrow: true });\n        const changeId = v4();\n        const { changeWithId, resolveChange, setLoadingStateBus } = dispatchMethods;\n        const { getLoadingStateById$, getById$ } = selectMethods;\n\n        const normalizedPatch = normalizePatch(patch, getById$(id));\n\n        const sourcePipe = getRemotePipe<LoadingState | undefined, unknown, DeepPartial<T> | undefined, DeepPartial<T> | undefined>({\n            options,\n            remoteObsOrFactory: dataSource$,\n            success: (\n                response?: DeepPartial<T>,\n            ) => {\n                const patchToApply = getPatchByOptions(normalizedPatch, response, options) ?? {};\n                return changeWithId(id, patchToApply, changeId, options);\n            },\n            emitOnSuccess: true,\n            optimistic: options.optimistic,\n            optimisticResolve: (\n                success: boolean,\n                response?: DeepPartial<T>,\n            ) => {\n                const patchToApply = getResolvePatchByOptions(normalizedPatch, response, options);\n                return resolveChange(id, changeId, success, patchToApply, options);\n            },\n            setLoadingState: (state) => setLoadingStateBus(state, id, undefined, undefined, options),\n        });\n        const loadingState$ = getLoadingStateById$(id, prSchema.useAsapSchedulerInLoadingGuards);\n        return guardIfLoading(loadingState$).pipe(sourcePipe);\n    };\n\n    const changeListRemote$ = (\n        patches: IdPatchRequest<T>[],\n        dataSource$: Observable<IdPatch<T>[] | undefined>,\n        options?: ChangeOptions,\n    ): Observable<IdPatch<T>[] | undefined> => {\n        options = typedDefaultsDeep(options, { rethrow: true });\n        const changeId = v4();\n        const { changeListWithId, resolveChangeList, setLoadingStateByIds } = dispatchMethods;\n        const { getLoadingStateByIds$, getById$ } = selectMethods;\n        const normalizedPatches: IdPatch<T>[] = patches.map((patch) => ({\n            ...patch,\n            patch: normalizePatch(patch.patch, getById$(patch.id)),\n        }));\n        const ids = normalizedPatches.map((patch) => patch.id);\n\n        const sourcePipe = getRemotePipe<LoadingState | undefined, unknown, IdPatch<T>[] | undefined, IdPatch<T>[]>({\n            options,\n            remoteObsOrFactory: dataSource$,\n            success: (\n                response?: IdPatch<T>[] | undefined,\n            ) => {\n                const patchesToApply = normalizedPatches.map((idPatch) => {\n                    const responsePatch = response?.find((r) => r.id === idPatch.id);\n                    const patch = getPatchByOptions(idPatch.patch, responsePatch?.patch, options) ?? {};\n                    return { ...idPatch, patch };\n                });\n                return changeListWithId(patchesToApply, changeId, options);\n            },\n            emitOnSuccess: true,\n            optimistic: options?.optimistic,\n            optimisticResolve: (\n                success: boolean,\n                response?: IdPatch<T>[] | undefined,\n            ) => {\n                const patchesToApply = normalizedPatches.map((idPatch) => {\n                    const responsePatch = response?.find((r) => r.id === idPatch.id);\n                    const patch = getResolvePatchByOptions(idPatch.patch, responsePatch?.patch, options) ?? {};\n                    return { ...idPatch, patch };\n                });\n                return resolveChangeList(patchesToApply, changeId, success, options);\n            },\n            setLoadingState: (state) => setLoadingStateByIds(ids, state, options),\n        });\n\n        const loadingState$ = getLoadingStateByIds$(ids, prSchema.useAsapSchedulerInLoadingGuards);\n        return guardIfLoading(loadingState$).pipe(sourcePipe);\n    };\n\n    const removeRemote$ = <R>(\n        id: Id,\n        observable: Observable<R>,\n        options?: EntityRemoveOptions,\n    ): Observable<R> => {\n        options = typedDefaultsDeep(options, { rethrow: true });\n        const { remove, resolveRemove } = dispatchMethods;\n        const sourcePipe = getRemotePipe<LoadingState | undefined, unknown, R, R>({\n            options,\n            remoteObsOrFactory: observable,\n            success: () => remove(id, options),\n            emitSuccessOutsideAffectState: true,\n            emitOnSuccess: true,\n            optimistic: options?.optimistic,\n            optimisticResolve: (success: boolean) => resolveRemove(id, success, options),\n            setLoadingState: (state) => dispatchMethods.setLoadingStateBus(state, id, undefined, undefined, options),\n        });\n        const loadingState$ = selectMethods.getLoadingStateById$(id, prSchema.useAsapSchedulerInLoadingGuards);\n        return guardIfLoading(loadingState$).pipe(sourcePipe);\n    };\n\n    const removeListRemote$ = <R>(\n        ids: Id[],\n        observable: Observable<R>,\n        options?: EntityRemoveListOptions,\n    ): Observable<R> => {\n        options = typedDefaultsDeep(options, { rethrow: true });\n        const { removeList, setLoadingStateByIds, resolveRemoveList } = dispatchMethods;\n        const sourcePipe = getRemotePipe<LoadingState | undefined, unknown, R, R>({\n            options,\n            remoteObsOrFactory: observable,\n            success: () => removeList(ids, options),\n            emitSuccessOutsideAffectState: true,\n            emitOnSuccess: true,\n            optimistic: options?.optimistic,\n            optimisticResolve: (success: boolean) => resolveRemoveList(ids, success, options),\n            setLoadingState: (state) => setLoadingStateByIds(ids, state, options),\n        });\n        const loadingState$ = selectMethods.getLoadingStateByIds$(ids, prSchema.useAsapSchedulerInLoadingGuards);\n        return guardIfLoading(loadingState$).pipe(sourcePipe);\n    };\n\n    return {\n        loadList$,\n        loadById$,\n        get$,\n        getDictionary$,\n        getById$,\n        addRemote$,\n        changeRemote$,\n        removeRemote$,\n        removeListRemote$,\n        changeListRemote$,\n    };\n};\n"
  },
  {
    "path": "src/entity/methods/mixed/types.ts",
    "content": "import {\n    EntityAddOptions,\n    EntityGetListOptions,\n    EntityGetOptions,\n    EntityLoadListOptions,\n    EntityLoadOptions,\n    EntityRemoveListOptions,\n    EntityRemoveOptions,\n    IdPatch,\n    IdPatchRequest,\n    PaginatedResponse,\n    Response$Factory,\n    ResponseArray,\n} from '../../types';\nimport { BehaviorSubject, Observable } from 'rxjs';\nimport { DeepPartial, Dictionary, Id } from '../../../system-types';\nimport { ChangeOptions, PatchRequest } from '../../../shared/change/types';\n\nexport interface MixedEntityMethods<T, TPageMetadata> {\n    loadList$(\n        config: unknown,\n        dataSource: Observable<ResponseArray<T, TPageMetadata>> | Response$Factory<T, TPageMetadata>,\n        options?: EntityLoadListOptions,\n        paginatorSubj?: BehaviorSubject<boolean>,\n    ): Observable<PaginatedResponse<T, TPageMetadata>>;\n\n    loadById$(\n        id: Id,\n        dataSource$: Observable<T>,\n        options?: EntityLoadOptions,\n    ): Observable<T>;\n\n    get$(\n        config: unknown,\n        dataSource?: Observable<ResponseArray<T, TPageMetadata>> | Response$Factory<T, TPageMetadata>,\n        options?: EntityGetListOptions,\n        paginatorSubj?: BehaviorSubject<boolean>,\n    ): Observable<T[] | undefined>;\n\n    getDictionary$(\n        config: unknown,\n        dataSource?: Observable<ResponseArray<T, TPageMetadata>> | Response$Factory<T, TPageMetadata>,\n        options?: EntityGetListOptions,\n        paginatorSubj?: BehaviorSubject<boolean>,\n    ): Observable<Dictionary<T>>;\n\n    getById$(\n        id: Id,\n        dataSource?: Observable<T>,\n        options?: EntityGetOptions,\n    ): Observable<T | undefined>;\n\n    addRemote$(\n        entity: T,\n        config: unknown,\n        dataSource$: Observable<T>,\n        options?: EntityAddOptions,\n    ): Observable<T>;\n\n    changeRemote$(\n        id: Id,\n        patch: PatchRequest<T>,\n        dataSource$: Observable<DeepPartial<T> | undefined>,\n        options?: ChangeOptions,\n    ): Observable<DeepPartial<T> | undefined>;\n\n    changeListRemote$(\n        patches: IdPatchRequest<T>[],\n        dataSource$: Observable<IdPatch<T>[] | undefined>,\n        options?: ChangeOptions,\n    ): Observable<IdPatch<T>[] | undefined>;\n\n    removeRemote$<R>(\n        id: Id,\n        observable: Observable<R>,\n        options?: EntityRemoveOptions,\n    ): Observable<R>;\n\n    removeListRemote$<R>(\n        ids: Id[],\n        observable: Observable<R>,\n        options?: EntityRemoveListOptions,\n    ): Observable<R>;\n}\n"
  },
  {
    "path": "src/entity/methods/mixed/utils.ts",
    "content": "import { BehaviorSubject, EMPTY, merge, Observable, of } from 'rxjs';\nimport { EntityGetListOptions, EntitySchema, Page, Pagination } from '../../types';\nimport { map, scan, switchMap, take } from 'rxjs/operators';\nimport { DispatchEntityMethods } from '../dispatch/types';\nimport { SelectEntityMethods } from '../select/types';\nimport { PainlessReduxSchema } from '../../../painless-redux/types';\nimport { ObservableOrFactory } from '../../../shared/types';\nimport { guardIfLoading } from '../../../shared/utils';\n\nexport const createMixedEntityMethodsUtils = <T, TPageMetadata>(\n    dispatchMethods: DispatchEntityMethods<T, TPageMetadata>,\n    selectMethods: SelectEntityMethods<T, TPageMetadata>,\n    schema: EntitySchema<T>,\n    prSchema: PainlessReduxSchema,\n) => {\n    const { getPage$, getPageLoadingState$ } = selectMethods;\n\n    const getPaginator = (\n        config: unknown,\n        paginatorSubj?: BehaviorSubject<boolean>,\n        options?: EntityGetListOptions,\n    ): Observable<Pagination> => {\n        paginatorSubj = paginatorSubj ?? new BehaviorSubject<boolean>(false);\n        const page$ = getPage$(config);\n        const loadingState$ = getPageLoadingState$(config, prSchema.useAsapSchedulerInLoadingGuards);\n        return paginatorSubj.pipe(\n            switchMap((isNext) => guardIfLoading(loadingState$).pipe(map(() => isNext))),\n            scan((\n                prevIndex: number,\n                isNext: boolean,\n            ) => isNext ? prevIndex + 1 : 0, -1),\n            map((index: number) => {\n                const size = options?.pageSize ?? schema.pageSize;\n                const from = index * size;\n                const to = from + size - 1;\n                return { index, size, from, to };\n            }),\n            switchMap((paging: Pagination) => page$.pipe(\n                take(1),\n                map((page: Page<TPageMetadata> | undefined) => !page || page.hasMore !== false),\n                switchMap((hasMore: boolean) => paging.index === 0 || hasMore ? of(paging) : EMPTY),\n            )),\n        );\n    };\n\n    const tryInvoke = <T, R, S>(\n        store$: Observable<S>,\n        invoker: (dataSource: ObservableOrFactory<T, R>) => Observable<unknown>,\n        dataSource?: ObservableOrFactory<T, R>,\n    ) => {\n        if (dataSource) {\n            const result$ = invoker(dataSource).pipe(switchMap(() => EMPTY));\n            return merge(store$, result$);\n        }\n        return store$;\n    };\n\n    return { getPaginator, tryInvoke };\n};\n"
  },
  {
    "path": "src/entity/methods/select/select.ts",
    "content": "import { EntitySelectors } from '../../types';\nimport { Observable } from 'rxjs';\nimport { SelectManager } from '../../../select-manager/types';\nimport { Dictionary, Id } from '../../../system-types';\nimport { SelectEntityMethods } from './types';\nimport { toDictionary } from '../../../utils';\n\nexport const createSelectEntityMethods = <T, TPageMetadata>(\n    selectManager: SelectManager,\n    selectors: EntitySelectors<T, TPageMetadata>,\n): SelectEntityMethods<T, TPageMetadata> => {\n\n    const get$ = (config: unknown): Observable<T[] | undefined> => {\n        const selector = selectors.createPageListByConfig(config);\n        return selectManager.select$(selector);\n    };\n\n    const getAll$ = (): Observable<T[] | undefined> => {\n        const selector = selectors.all;\n        return selectManager.select$(selector);\n    };\n\n    const getDictionary$ = (config: unknown): Observable<Dictionary<T>> => {\n        return get$(config).pipe(toDictionary());\n    };\n\n    const getById$ = (id: Id): Observable<T | undefined> => {\n        const selector = selectors.createActual(id);\n        return selectManager.select$(selector);\n    };\n\n    const getPage$ = (\n        config: unknown,\n        isAsap: boolean = false,\n    ) => {\n        const selector = selectors.createPage(config);\n        return selectManager.select$(selector, isAsap);\n    };\n\n    const getPages$ = (\n        isAsap: boolean = false,\n    ) => {\n        const selector = selectors.allPages;\n        return selectManager.select$(selector, isAsap);\n    };\n\n    const getPageLoadingState$ = (\n        config: unknown,\n        isAsap: boolean = false,\n    ) => {\n        const selector = selectors.createPageLoadingState(config);\n        return selectManager.select$(selector, isAsap);\n    };\n\n    const getLoadingStateById$ = (\n        id: Id,\n        isAsap: boolean = false,\n    ) => {\n        const selector = selectors.createLoadingStateById(id);\n        return selectManager.select$(selector, isAsap);\n    };\n\n    const getLoadingState$ = () => {\n        const selector = selectors.loadingState;\n        return selectManager.select$(selector);\n    };\n\n    const getLoadingStates$ = () => {\n        const selector = selectors.loadingStates;\n        return selectManager.select$(selector);\n    };\n\n    const getLoadingStateByIds$ = (ids: Id[]) => {\n        const selector = selectors.createLoadingStateByIds(ids);\n        return selectManager.select$(selector);\n    };\n\n    return {\n        get$,\n        getById$,\n        getDictionary$,\n        getPage$,\n        getPageLoadingState$,\n        getLoadingStateById$,\n        getLoadingStateByIds$,\n        getAll$,\n        getPages$,\n        getLoadingState$,\n        getLoadingStates$,\n    };\n};\n"
  },
  {
    "path": "src/entity/methods/select/types.ts",
    "content": "import { Observable } from 'rxjs';\nimport { Dictionary, Id, LoadingState } from '../../../system-types';\nimport { Page } from '../../types';\n\nexport interface SelectEntityMethods<T, TPageMetadata> {\n    get$(config: unknown): Observable<T[] | undefined>;\n\n    getDictionary$(config: unknown): Observable<Dictionary<T>>;\n\n    getById$(id: Id): Observable<T | undefined>;\n\n    getLoadingState$(): Observable<LoadingState | undefined>;\n\n    getLoadingStates$(): Observable<Dictionary<LoadingState>>;\n\n    getLoadingStateById$(\n        id: Id,\n        isAsap?: boolean,\n    ): Observable<LoadingState | undefined>;\n\n    getLoadingStateByIds$(\n        ids: Id[],\n        isAsap?: boolean,\n    ): Observable<LoadingState | undefined>;\n\n    getPage$(\n        config: unknown,\n        isAsap?: boolean,\n    ): Observable<Page<TPageMetadata> | undefined>;\n\n    getPageLoadingState$(\n        config: unknown,\n        isAsap?: boolean,\n    ): Observable<LoadingState | undefined>;\n\n    getAll$(): Observable<T[] | undefined>;\n\n    getPages$(): Observable<Page<TPageMetadata>[]>;\n}\n"
  },
  {
    "path": "src/entity/reducer.ts",
    "content": "import { EntityActionTypes, EntitySchema, EntityState } from './types';\nimport { Reducer } from '../system-types';\nimport { combineReducers } from '../shared/utils';\nimport { createDictionaryReducer } from './reducers/dictionary';\nimport { createIdsReducer } from './reducers/ids';\nimport { createPagesReducer } from './reducers/pages';\nimport { createChange, createResolveChange, createSetLoadingState, EntityActions } from './actions';\nimport { createByIdLoadingStatesReducer } from './reducers/loading-states';\nimport { createEntityLoadingStateReducer } from './reducers/loading-state';\nimport { batchActionsReducerFactory } from '../shared/system/reducers';\n\nconst createBaseReducer = <T, TPageMetadata>(\n    actionTypes: EntityActionTypes,\n): Reducer<EntityState<T, TPageMetadata>, EntityActions> => combineReducers<EntityState<T, TPageMetadata>, EntityActions>({\n    dictionary: createDictionaryReducer(actionTypes),\n    ids: createIdsReducer(actionTypes),\n    pages: createPagesReducer(actionTypes),\n    loadingStates: createByIdLoadingStatesReducer(actionTypes),\n    loadingState: createEntityLoadingStateReducer(actionTypes),\n});\n\nconst createListReducer = <T, TPageMetadata>(\n    actionTypes: EntityActionTypes,\n    schema: EntitySchema<T>,\n): Reducer<EntityState<T, TPageMetadata>, EntityActions> => {\n    const baseReducer = createBaseReducer<T, TPageMetadata>(actionTypes);\n    return (state: EntityState<T, TPageMetadata>, action: EntityActions) => {\n        switch (action.type) {\n            case actionTypes.CHANGE_LIST: {\n                const actionCreator = createChange(actionTypes);\n                const { payload: { patches, changeId }, options } = action;\n                const actions = patches.map((patch) => actionCreator(\n                    patch.id,\n                    patch.patch,\n                    changeId,\n                    options,\n                ));\n                return actions.reduce(baseReducer, state);\n            }\n            case actionTypes.RESOLVE_CHANGE_LIST: {\n                const actionCreator = createResolveChange(actionTypes);\n                const { payload: { patches, changeId, success }, options } = action;\n                const actions = patches.map((patch) => actionCreator(\n                    patch.id,\n                    changeId,\n                    success,\n                    patch.patch,\n                    options,\n                ));\n                return actions.reduce(baseReducer, state);\n            }\n            case actionTypes.SET_LOADING_STATES: {\n                const actionCreator = createSetLoadingState(actionTypes, schema);\n                const { payload: { ids, state: loadingState }, options } = action;\n                const actions = ids.map((id) => actionCreator(\n                    loadingState,\n                    undefined,\n                    id,\n                    undefined,\n                    options,\n                ));\n                return actions.reduce(baseReducer, state);\n            }\n            default:\n                return baseReducer(state, action);\n        }\n    };\n};\n\nexport const createEntityReducer = <T, TPageMetadata>(\n    actionTypes: EntityActionTypes,\n    schema: EntitySchema<T>,\n): Reducer<EntityState<T, TPageMetadata>, EntityActions> => {\n    const listReducer = createListReducer<T, TPageMetadata>(actionTypes, schema);\n    return batchActionsReducerFactory(actionTypes, listReducer);\n};\n"
  },
  {
    "path": "src/entity/reducers/dictionary.spec.ts",
    "content": "import { Id } from '../../system-types';\nimport { createTestHelpers } from '../../testing/helpers';\nimport { createDictionaryReducer } from './dictionary';\n\ntype TestEntity = {\n    id: Id;\n    profile?: {\n        image?: string;\n        age?: number;\n        name?: string;\n    },\n}\ntype TPageMetadata = any;\n\nconst {\n    reducer,\n    actionCreators,\n} = createTestHelpers<TestEntity, TPageMetadata>(createDictionaryReducer);\n\ndescribe('dictionary', () => {\n\n    test('should return default state', () => {\n        // act\n        const actual = reducer(undefined, { type: 'INIT' } as any);\n        // assert\n        expect(actual).toEqual({});\n    });\n\n    test('should add entity', () => {\n        // arrange\n        const entity: TestEntity = { id: 1 };\n        const action = actionCreators.ADD(entity);\n        // act\n        const actual = reducer(undefined, action);\n        // assert\n        const expected = { [entity.id]: { actual: entity } };\n        expect(actual).toEqual(expected);\n    });\n\n    test('should add entities', () => {\n        // arrange\n        const entities = [{ id: 1 }, { id: 2 }];\n        const action = actionCreators.ADD_LIST(entities, null);\n        // act\n        const actual = reducer(undefined, action);\n        // assert\n        const expected = entities.reduce((\n            memo: any,\n            entity: any,\n        ) => {\n            memo[entity.id] = { actual: entity };\n            return memo;\n        }, {});\n        expect(actual).toEqual(expected);\n    });\n\n    test.each`\n\t\toptions\n\t\t${{ merge: true }}\n\t\t${{ merge: false }}\n\t`('should merge entity with the same if options.merge option passed, otherwise replace ($options)', ({ options }) => {\n        // arrange\n        const entity = { id: 1, name: 'entity 1' };\n        const action = actionCreators.ADD(entity);\n        const entity2 = { id: 1, age: 1 };\n        const action2 = actionCreators.ADD(entity2, undefined, undefined, options);\n        // act\n        const instances = [action, action2].reduce(reducer, undefined);\n        const actual = instances[entity.id].actual;\n        // assert\n        const expected = options.merge ? { id: 1, name: 'entity 1', age: 1 } : { id: 1, age: 1 };\n        expect(actual).toEqual(expected);\n    });\n\n    test('should remove entity', () => {\n        // arrange\n        const entity: TestEntity = { id: 1 };\n        const action = actionCreators.REMOVE(entity.id);\n        // act\n        const actual = reducer({\n            [entity.id]: {\n                actual: entity,\n                changes: [],\n            },\n        }, action);\n        // assert\n        const expected = {};\n        expect(actual).toEqual(expected);\n    });\n\n    test('should remove entities', () => {\n        // arrange\n        const entity1: TestEntity = { id: 1 };\n        const entity2: TestEntity = { id: 2 };\n        const action = actionCreators.REMOVE_LIST([entity1.id, entity2.id]);\n        // act\n        const actual = reducer({\n            [entity1.id]: {\n                actual: entity1,\n                changes: [],\n            },\n            [entity2.id]: {\n                actual: entity2,\n                changes: [],\n            },\n        }, action);\n        // assert\n        const expected = {};\n        expect(actual).toEqual(expected);\n    });\n\n    describe('#CHANGE', () => {\n        test.each`\n            options\n            ${{ merge: true }}\n            ${{ merge: false }}\n        `('should override entity when no merge option passed and merge otherwise ($options)', ({ options }) => {\n            // arrange\n            const entity: TestEntity = { id: 1, profile: { image: '1.png' } };\n            const patch = { profile: { age: 18 } };\n            const action = actionCreators.CHANGE(entity.id, patch, undefined, options);\n            // act\n            const instances = reducer({\n                [entity.id]: {\n                    actual: entity,\n                    changes: [],\n                },\n            }, action);\n            // assert\n            const expected = options.merge ? { image: '1.png', age: 18 } : { age: 18 };\n            const actual = instances[entity.id].actual.profile;\n            expect(actual).toEqual(expected);\n        });\n\n        test.each`\n\t\t\tifNotExist\n\t\t\t${undefined}\n\t\t\t${true}\n\t\t`(\n            'should either create entity or ignore based on options.ifNotExist=$ifNotExist',\n            ({ ifNotExist }) => {\n                // arrange\n                const id = 1;\n                const patch = { profile: { age: 18 } };\n                const action = actionCreators.CHANGE(id, patch, undefined, { ifNotExist });\n                // act\n                const actual = reducer(undefined, action);\n                // assert\n                const expected = ifNotExist ? {\n                    [id]: { actual: { id, profile: { age: 18 } } },\n                } : {};\n                const expectedKeys = ifNotExist ? [id.toString()] : [];\n                expect(Object.keys(actual)).toEqual(expectedKeys);\n                expect(actual).toEqual(expected);\n            },\n        );\n    });\n});\n"
  },
  {
    "path": "src/entity/reducers/dictionary.ts",
    "content": "import { Dictionary } from '../../system-types';\nimport { createAddByHash, createResolveChange, EntityActions } from '../actions';\nimport { EntityActionTypes, EntityInstanceState } from '../types';\nimport { keyBy } from 'lodash-es';\nimport { createInstanceReducer } from './instance';\n\nconst addInstances = <T>(\n    state: Dictionary<EntityInstanceState<T>>,\n    instances: EntityInstanceState<T>[],\n): Dictionary<EntityInstanceState<T>> => {\n    const newInstances = keyBy<EntityInstanceState<T>>(instances, 'actual.id');\n    return { ...state, ...newInstances };\n};\n\nexport const createDictionaryReducer = <T>(\n    types: EntityActionTypes,\n) => {\n    const instanceReducer = createInstanceReducer<T>(types);\n    return (\n        state: Dictionary<EntityInstanceState<T>> = {},\n        action: EntityActions,\n    ): Dictionary<EntityInstanceState<T>> => {\n        switch (action.type) {\n            case types.ADD: {\n                const entity = action.payload.entity;\n                const instanceState = state[entity.id];\n                const instance = instanceReducer(instanceState, action);\n                if (!instance) return state;\n                return addInstances(state, [instance]);\n            }\n            case types.RESOLVE_ADD: {\n                const {\n                    payload: { success, result, tempId },\n                    options,\n                } = action;\n                const optimisticCreated = state[tempId];\n                if (!optimisticCreated) return state;\n                const { [tempId]: deleted, ...rest } = state;\n                if (!success) return rest;\n                const id = result.id;\n                const patch = { ...optimisticCreated.actual, id };\n                const resolveChangeAction = createResolveChange(types)(tempId, tempId, true, patch, options);\n                const instance = instanceReducer(state[tempId], resolveChangeAction);\n                if (!instance) return state;\n                return addInstances(state, [instance]);\n            }\n            case types.ADD_LIST: {\n                const { payload: { entities, configHash }, options } = action;\n                const add = createAddByHash(types);\n                const instances = entities.map((entity) => {\n                    const action = add(entity, configHash, undefined, options);\n                    const instanceState = state[entity.id];\n                    return instanceReducer(instanceState, action) as EntityInstanceState<T>;\n                });\n                return addInstances(state, instances);\n            }\n            case types.CHANGE:\n            case types.RESOLVE_CHANGE: {\n                const { payload: { id }, options: { ifNotExist } } = action;\n                const instanceState = state[id];\n                if (instanceState === undefined && !ifNotExist) return state;\n                const instance = instanceReducer(instanceState, action) as EntityInstanceState<T>;\n                return { ...state, [id]: instance };\n            }\n            case types.REMOVE:\n            case types.RESOLVE_REMOVE:\n            case types.RESTORE_REMOVED: {\n                const { payload: { id } } = action;\n                const instanceState = state[id];\n                const instance = instanceReducer(instanceState, action);\n                if (instance) return { ...state, [id]: instance };\n                const { [id]: deleted, ...rest } = state;\n                return rest;\n            }\n            case types.REMOVE_LIST:\n            case types.RESOLVE_REMOVE_LIST:\n            case types.RESTORE_REMOVED_LIST: {\n                const { payload: { ids } } = action;\n                return ids.reduce((memo, id) => {\n                    const instanceState = state[id];\n                    const instance = instanceReducer(instanceState, action);\n                    if (instance) {\n                        memo[id] = instance;\n                    } else {\n                        delete memo[id];\n                    }\n                    return memo;\n                }, { ...state });\n            }\n            case types.CLEAR_ALL: {\n                return {};\n            }\n            default:\n                return state;\n        }\n    };\n};\n"
  },
  {
    "path": "src/entity/reducers/ids.spec.ts",
    "content": "import { createIdsReducer } from './ids';\nimport { createTestHelpers } from '../../testing/helpers';\n\nconst {\n    reducer,\n    actionCreators,\n} = createTestHelpers(createIdsReducer);\n\ndescribe('ids', () => {\n\n    describe('#ADD', () => {\n        test('should add entity id', () => {\n            // arrange\n            const entity = { id: 1 };\n            const action = actionCreators.ADD(entity);\n            // act\n            const actual = reducer(undefined, action);\n            // assert\n            const expected = [entity.id];\n            expect(actual).toEqual(expected);\n        });\n\n        test('should add entity id to options.pasteIndex', () => {\n            // arrange\n            const action = actionCreators.ADD({ id: 99 }, undefined, undefined, { pasteIndex: 2 });\n            // act\n            const actual = reducer([1, 2, 3, 4], action);\n            // assert\n            const expected = [1, 2, 99, 3, 4];\n            expect(actual).toEqual(expected);\n        });\n    });\n\n    test('should return default state', () => {\n        // act\n        const actual = reducer(undefined, { type: 'INIT' } as any);\n        // assert\n        expect(actual).toEqual([]);\n    });\n\n    test('should add entity ids from payload.$source', () => {\n        // arrange\n        const entities = [{ id: 1 }, { id: 2 }];\n        const action = actionCreators.ADD_LIST(entities);\n        // act\n        const actual = reducer(undefined, action);\n        // assert\n        const expected = entities.map((entity) => entity.id);\n        expect(actual).toEqual(expected);\n    });\n\n    test('should remove entity id', () => {\n        // arrange\n        const entity = { id: 3 };\n        const action = actionCreators.REMOVE(entity.id);// act\n        const actual = reducer([1, 2, entity.id, 4], action);\n        // assert\n        const expected = [1, 2, 4];\n        expect(actual).toEqual(expected);\n    });\n\n    test('should remove entities ids', () => {\n        // arrange\n        const entity1 = { id: 3 };\n        const entity2 = { id: 4 };\n        const action = actionCreators.REMOVE_LIST([entity1.id, entity2.id]);// act\n        const actual = reducer([1, 2, entity1.id, entity2.id], action);\n        // assert\n        const expected = [1, 2];\n        expect(actual).toEqual(expected);\n    });\n\n    test.each`\n\t\texist\n\t\t${true}\n\t\t${false}\n\t`('should add entity id when change if options.ifNotExist options passed and not exist($exist)', ({ exist }) => {\n        // arrange\n        const action = actionCreators.CHANGE(1, {}, undefined, { ifNotExist: true });\n        const initialState = exist ? [1] : [];\n        // act\n        const actual = reducer(initialState, action);\n        // assert\n        const expected = [1];\n        expect(actual).toEqual(expected);\n    });\n\n});\n"
  },
  {
    "path": "src/entity/reducers/ids.ts",
    "content": "import { EntityActionTypes, EntityInsertOptions } from '../types';\nimport { EntityActions } from '../actions';\nimport { Id } from '../../system-types';\nimport { isNil } from 'lodash-es';\nimport { removeFromArray } from '../../utils';\n\nconst getOnlyNewIds = (\n    state: Id[],\n    ids: Id[],\n): Id[] => {\n    return ids.filter((id: Id) => !state.includes(id));\n};\n\nconst addIds = (\n    state: Id[],\n    ids: Id[],\n    options?: EntityInsertOptions,\n): Id[] => {\n    const newIds = getOnlyNewIds(state, ids);\n    if (options && !isNil(options.pasteIndex)) {\n        const pre = state.slice(0, options.pasteIndex);\n        const post = state.slice(options.pasteIndex);\n        return pre.concat(newIds, post);\n    }\n    return state.concat(newIds);\n};\n\nexport const createIdsReducer = (\n    types: EntityActionTypes,\n) => (\n    state: Id[] = [],\n    action: EntityActions,\n): Id[] => {\n    switch (action.type) {\n        case types.ADD: {\n            const entity = action.payload.entity;\n            return addIds(state, [entity.id], action.options);\n        }\n        case types.CHANGE: {\n            const index = state.indexOf(action.payload.id);\n            if (index === -1) {\n                if (!action.options.ifNotExist) return state;\n                return addIds(state, [action.payload.id]);\n            }\n            return state;\n        }\n        case types.ADD_LIST: {\n            const entities = action.payload.entities;\n            const ids = entities.map((e) => e.id);\n            return addIds(state, ids, action.options);\n        }\n        case types.REMOVE: {\n            const { payload: { id }, options: { safe, optimistic } } = action;\n            if (optimistic || safe) return state;\n            return removeFromArray(state, [id]);\n        }\n        case types.REMOVE_LIST: {\n            const { payload: { ids }, options: { safe, optimistic } } = action;\n            if (optimistic || safe) return state;\n            return removeFromArray(state, ids);\n        }\n        case types.RESOLVE_REMOVE: {\n            const { payload: { success, id }, options: { safe } } = action;\n            if (!success || safe) return state;\n            return removeFromArray(state, [id]);\n        }\n        case types.RESOLVE_ADD: {\n            const { payload: { success, result, tempId } } = action;\n            if (!success) return removeFromArray(state, [tempId]);\n            return state.map((id) => {\n                if (id === tempId) return result.id;\n                return id;\n            });\n        }\n        case types.CLEAR_ALL: {\n            return [];\n        }\n        default:\n            return state;\n    }\n};\n"
  },
  {
    "path": "src/entity/reducers/instance.spec.ts",
    "content": "import { createTestHelpers } from '../../testing/helpers';\nimport { createInstanceReducer } from './instance';\nimport { EntityInstanceState, EntityType } from '../types';\nimport { Change } from '../../shared/change/types';\n\nconst {\n    reducer,\n    actionCreators,\n} = createTestHelpers(createInstanceReducer);\n\ndescribe('instance', () => {\n\n    test('should return default state', () => {\n        // act\n        const actual = reducer(undefined, { type: 'INIT' } as any);\n        // assert\n        expect(actual).toEqual(undefined);\n    });\n\n    describe('#ADD', () => {\n        test.each`\n            merge\n            ${true}\n            ${false}\n        `('should add entity as stable change (without id) and merge=$merge if there are changes', ({ merge }) => {\n            // arrange\n            const entity: EntityType<any> = { id: 1 };\n            const action = actionCreators.ADD(entity, undefined, undefined, { merge });\n            const existChange = { stable: false, patch: {}, merge: false };\n            const state: EntityInstanceState<any> = {\n                actual: entity, changes: [existChange],\n            };\n            // act\n            const actual = reducer(state, action);\n            // assert\n            const addChange: Change<any> = { stable: true, patch: entity, merge };\n            const expected = {\n                actual: entity,\n                changes: [existChange, addChange],\n            };\n            expect(actual).toEqual(expected);\n        });\n    });\n});\n"
  },
  {
    "path": "src/entity/reducers/instance.ts",
    "content": "import { EntityActionTypes, EntityInstanceState, EntityType } from '../types';\nimport { EntityActions } from '../actions';\nimport { createInstanceByChanges, getMergedChanges } from '../../shared/change/utils';\nimport { createChangeReducer } from '../../shared/change/reducer';\nimport { createChange } from '../../shared/change/actions';\n\nexport const createInstanceReducer = <T>(types: EntityActionTypes) => {\n    const changeReducer = createChangeReducer(types);\n    const createChangeAction = createChange<EntityType<T>>(types);\n    return (\n        state: EntityInstanceState<T> | undefined,\n        action: EntityActions,\n    ): EntityInstanceState<T> | undefined => {\n        switch (action.type) {\n            case types.ADD: {\n                const {\n                    options: { optimistic, merge },\n                    payload: { entity, tempId },\n                } = action;\n                const instance = createInstanceByChanges(\n                    state,\n                    entity as EntityType<T>,\n                    merge,\n                    !optimistic,\n                    tempId,\n                );\n                return getMergedChanges(instance, true);\n            }\n            case types.CHANGE: {\n                const {\n                    options,\n                    payload: { id, patch, changeId },\n                } = action;\n                const { ifNotExist } = options;\n                if (!ifNotExist && !state) return state;\n                const patchWithId = { id, ...patch };\n                const resultPatch = ifNotExist ? patchWithId : patch;\n                const changeAction = createChangeAction(resultPatch, changeId, options);\n                return changeReducer(state, changeAction) as EntityInstanceState<T>;\n            }\n            case types.RESOLVE_CHANGE: {\n                return changeReducer(state, action) as EntityInstanceState<T>;\n            }\n            case types.REMOVE:\n            case types.REMOVE_LIST: {\n                if (!state) return state;\n                const { options: { optimistic, safe } } = action;\n                if (safe || optimistic) return { ...state, removed: true };\n                return undefined;\n            }\n            case types.RESOLVE_REMOVE:\n            case types.RESOLVE_REMOVE_LIST: {\n                if (!state) return state;\n                const {\n                    payload: { success },\n                    options: { safe },\n                } = action;\n                if (safe || state.removed === false) return state;\n                if (success) return undefined;\n                return { ...state, removed: false };\n            }\n            case types.RESTORE_REMOVED:\n            case types.RESTORE_REMOVED_LIST: {\n                if (!state) return state;\n                return { ...state, removed: false };\n            }\n            default:\n                return state;\n        }\n    };\n};\n"
  },
  {
    "path": "src/entity/reducers/loading-state.ts",
    "content": "import { LoadingState } from '../../system-types';\nimport { createLoadingStateReducer } from '../../shared/loading-state/reducers';\nimport { EntityActionTypes } from '../types';\nimport { EntityActions } from '../actions';\nimport { RequestOptions } from '../../shared/types';\n\nexport const createEntityLoadingStateReducer = (\n    types: EntityActionTypes,\n) => (\n    state: LoadingState | undefined,\n    action: EntityActions,\n) => {\n    const loadingStateReducer = createLoadingStateReducer(types);\n    if (action.type !== types.SET_LOADING_STATE) return state;\n    if ((action.options as RequestOptions)?.globalLoadingState === false) return state;\n    return loadingStateReducer(state, action);\n};\n"
  },
  {
    "path": "src/entity/reducers/loading-states.ts",
    "content": "import { EntityActionTypes } from '../types';\nimport { Dictionary, Id, LoadingState } from '../../system-types';\nimport { EntityActions } from '../actions';\nimport { createLoadingStateReducer } from '../../shared/loading-state/reducers';\nimport { isNil } from 'lodash-es';\nimport { removeFromObject } from '../../utils';\n\nconst removeState = (\n    state: Dictionary<LoadingState>,\n    ids: Id[] = [],\n    condition: boolean = true,\n): Dictionary<LoadingState> => {\n    if (!condition) return state;\n    return removeFromObject(state, ids);\n};\n\nexport const createByIdLoadingStatesReducer = (\n    types: EntityActionTypes,\n) => {\n    const entityLoadingStateReducer = createLoadingStateReducer(types);\n    return (\n        state: Dictionary<LoadingState> = {},\n        action: EntityActions,\n    ): Dictionary<LoadingState> => {\n        switch (action.type) {\n            case types.SET_LOADING_STATE: {\n                const id = action.payload.id;\n                if (isNil(id)) return state;\n                const byId = entityLoadingStateReducer(state[id], action) as LoadingState;\n                return { ...state, [id]: byId };\n            }\n            case types.RESOLVE_ADD: {\n                const { payload: { tempId } } = action;\n                return removeState(state, [tempId]);\n            }\n            case types.REMOVE: {\n                const { payload: { id }, options: { optimistic } } = action;\n                return removeState(state, [id], !optimistic);\n            }\n            case types.RESOLVE_REMOVE: {\n                const { payload: { success, id } } = action;\n                return removeState(state, [id], !success);\n            }\n            case types.REMOVE_LIST: {\n                const { payload: { ids }, options: { optimistic } } = action;\n                return removeState(state, ids, !optimistic);\n            }\n            case types.RESOLVE_REMOVE_LIST: {\n                const { payload: { success, ids } } = action;\n                return removeState(state, ids, !success);\n            }\n            case types.CLEAR_ALL: {\n                return {};\n            }\n            default:\n                return state;\n        }\n\n    };\n};\n"
  },
  {
    "path": "src/entity/reducers/pages.spec.ts",
    "content": "import { createPagesReducer } from './pages';\nimport { EntityActions } from '../actions';\nimport { createTestHelpers } from '../../testing/helpers';\n\njest.mock('../utils');\n\nconst {\n    reducer,\n    actionCreators,\n} = createTestHelpers(createPagesReducer);\n\ndescribe('pages', () => {\n\n    test('should return default value', () => {\n        // act\n        const actual = reducer(undefined, { type: 'INIT' } as any);\n        // assert\n        expect(actual).toEqual({});\n    });\n\n    test('should remove id for all pages, otherwise default', () => {\n        // arrange\n        const action: EntityActions = actionCreators.REMOVE(1);\n        const initialState = {\n            1: { ids: [1, 2] },\n            2: { ids: [2, 1] },\n        };\n        // act\n        const actual = reducer(initialState, action);\n        // assert\n        const expected = {\n            1: { ids: [2] },\n            2: { ids: [2] },\n        };\n        expect(actual).toEqual(expected);\n    });\n\n    test('should remove ids for all pages, otherwise default', () => {\n        // arrange\n        const action: EntityActions = actionCreators.REMOVE_LIST([1, 2]);\n        const initialState = {\n            1: { ids: [1, 2, 3] },\n            2: { ids: [2, 1, 5] },\n        };\n        // act\n        const actual = reducer(initialState, action);\n        // assert\n        const expected = {\n            1: { ids: [3] },\n            2: { ids: [5] },\n        };\n        expect(actual).toEqual(expected);\n    });\n\n});\n"
  },
  {
    "path": "src/entity/reducers/pages.ts",
    "content": "import { EntityActionTypes, EntityType, Page } from '../types';\nimport { Dictionary } from '../../system-types';\nimport { EntityActions } from '../actions';\nimport { isNil, uniq } from 'lodash-es';\nimport { createLoadingStateReducer } from '../../shared/loading-state/reducers';\nimport { MAX_PAGES_COUNT } from '../constants';\nimport { removeFromArray } from '../../utils';\n\nconst addList = <T, TPageMetadata>(\n    state: Page<TPageMetadata> | undefined,\n    data: EntityType<T>[],\n): Page<TPageMetadata> => {\n    const newIds = data.map(entity => entity.id);\n    const oldIds = state?.ids ?? [];\n    return {\n        ...state,\n        ids: uniq(oldIds.concat(newIds)),\n    };\n};\n\nconst createPageReducer = <TPageMetadata>(\n    types: EntityActionTypes,\n) => {\n    const loadingStateReducer = createLoadingStateReducer(types);\n    return (\n        state: Page<TPageMetadata> | undefined,\n        action: EntityActions,\n    ): Page<TPageMetadata> | undefined => {\n        switch (action.type) {\n            case types.ADD_LIST: {\n                let newState = state ?? { ids: [] };\n                if (!isNil(action.payload.hasMore)) {\n                    newState = { ...newState, hasMore: action.payload.hasMore };\n                }\n                if (!isNil(action.payload.metadata)) {\n                    newState = { ...newState, metadata: action.payload.metadata as TPageMetadata }\n                }\n                if (action.payload.isReplace) {\n                    newState = { ...newState, ids: undefined };\n                }\n                return addList(newState, action.payload.entities);\n            }\n            case types.ADD: {\n                return addList(state, [action.payload.entity]);\n            }\n            case types.REMOVE: {\n                const { payload: { id }, options: { safe, optimistic } } = action;\n                if (optimistic || safe) return state;\n                // this check needs to clear immutable reference updating.\n                // It means, no state mutating if this id doesn't exist here\n                if (!state?.ids?.includes(id)) return state;\n                return {\n                    ...state,\n                    ids: removeFromArray(state.ids, [id]),\n                };\n            }\n            case types.RESOLVE_REMOVE: {\n                const { payload: { success, id }, options: { safe } } = action;\n                if (!success || safe) return state;\n                // this check needs to clear immutable reference updating.\n                // It means, no state mutating if this id doesn't exist here\n                if (!state?.ids?.includes(id)) return state;\n                return {\n                    ...state,\n                    ids: removeFromArray(state.ids, [id]),\n                };\n            }\n            case types.REMOVE_LIST: {\n                const { payload: { ids }, options: { safe, optimistic } } = action;\n                if (optimistic || safe) return state;\n                if (!state?.ids) return state;\n                // this check needs to clear immutable reference updating.\n                // It means, no state mutating if this id doesn't exist here\n                const hasIds = ids.some((id) => state.ids?.includes(id));\n                if (!hasIds) return state;\n                return {\n                    ...state,\n                    ids: removeFromArray(state.ids, ids),\n                };\n            }\n\n            case types.RESOLVE_REMOVE_LIST: {\n                const { payload: { success, ids }, options: { safe } } = action;\n                if (!success || safe) return state;\n                if (!state?.ids) return state;\n                // this check needs to clear immutable reference updating.\n                // It means, no state mutating if this id doesn't exist here\n                const hasIds = ids.some((id) => state.ids?.includes(id));\n                if (!hasIds) return state;\n                return {\n                    ...state,\n                    ids: removeFromArray(state.ids, ids),\n                };\n            }\n            case types.RESOLVE_ADD: {\n                const { payload: { success, tempId, result } } = action;\n                if (!state) return state;\n                if (success) return {\n                    ...state,\n                    ids: state.ids?.map((id) => {\n                        if (id === tempId) return result.id;\n                        return id;\n                    }),\n                };\n                return {\n                    ...state,\n                    ids: removeFromArray(state?.ids ?? [], [tempId]),\n                };\n            }\n            case types.SET_LOADING_STATE: {\n                return {\n                    ...state,\n                    ids: state?.ids,\n                    loadingState: loadingStateReducer(state?.loadingState, action),\n                };\n            }\n            default:\n                return state;\n        }\n    };\n};\n\nexport const createPagesReducer = <TPageMetadata>(\n    types: EntityActionTypes,\n) => {\n    const pageReducer = createPageReducer<TPageMetadata>(types);\n    return (\n        state: Dictionary<Page<TPageMetadata>> = {},\n        action: EntityActions,\n    ): Dictionary<Page<TPageMetadata>> => {\n        switch (action.type) {\n            case types.SET_LOADING_STATE:\n            case types.ADD_LIST:\n            case types.ADD: {\n                const {\n                    payload: { configHash },\n                    options: { maxPagesCount = MAX_PAGES_COUNT },\n                } = action;\n                if (isNil(configHash)) return state;\n                const pageExist = configHash in state;\n                const page = pageReducer(state[configHash], action);\n                if (!page) return state;\n                const newState = { ...state, [configHash]: page };\n                if (pageExist) return newState;\n                const pageHashes = Object.keys(newState);\n                const pagesCount = pageHashes.length;\n                page.order = pagesCount - 1;\n                if (pagesCount <= maxPagesCount) return newState;\n                return pageHashes.reduce((memo: Dictionary<Page<TPageMetadata>>, hash: string) => {\n                    const existPage = newState[hash];\n                    if (existPage.order === 0) return memo;\n                    memo[hash] = {\n                        ...existPage,\n                        order: (existPage.order ?? pagesCount) - 1,\n                    };\n                    return memo;\n                }, {});\n            }\n            case types.RESOLVE_ADD:\n            case types.REMOVE:\n            case types.RESOLVE_REMOVE:\n            case types.REMOVE_LIST:\n            case types.RESOLVE_REMOVE_LIST: {\n                return Object.keys(state).reduce((\n                    memo: Dictionary<Page<TPageMetadata>>,\n                    hash: string,\n                ) => {\n                    const page = pageReducer(state[hash], action);\n                    if (!page) return memo;\n                    memo[hash] = page;\n                    return memo;\n                }, {});\n            }\n            case types.CLEAR: {\n                const hash = action.payload.configHash;\n                const { [hash]: deleted, ...rest } = state;\n                return rest;\n            }\n            case types.CLEAR_ALL: {\n                return {};\n            }\n            default:\n                return state;\n        }\n    };\n};\n"
  },
  {
    "path": "src/entity/selectors.ts",
    "content": "import { createSelector, Selector } from 'reselect';\nimport {\n    ActualSelector,\n    BaseEntitySelectors,\n    DictionarySelector,\n    EntityInstanceState,\n    EntitySelectors,\n    EntityState,\n    EntityType,\n    IdsSelector,\n    ListSelector,\n    LoadingStatesSelector,\n    Page,\n    PageSelector,\n    PagesListSelector,\n    PagesSelector,\n} from './types';\nimport { PainlessReduxState } from '../painless-redux/types';\nimport { HashFn, Id, LoadingState } from '../system-types';\nimport { createLoadingStateSelector } from '../shared/loading-state/selectors';\nimport { LoadingStateSelector } from '../shared/loading-state/types';\nimport { isNil, values } from 'lodash-es';\nimport { getChangeableActual } from '../shared/change/selectors';\n\nexport const createDictionarySelector = <T, TPageMetadata>(\n    selector: Selector<PainlessReduxState, EntityState<T, TPageMetadata>>,\n): DictionarySelector<T, TPageMetadata> =>\n    createSelector(selector, (s) => s.dictionary);\n\nexport const createIdsSelector = <T, TPageMetadata>(\n    selector: Selector<PainlessReduxState, EntityState<T, TPageMetadata>>,\n): IdsSelector<T, TPageMetadata> =>\n    createSelector(selector, (s) => s.ids);\n\nexport const createPagesSelector = <T, TPageMetadata>(\n    selector: Selector<PainlessReduxState, EntityState<T, TPageMetadata>>,\n): PagesSelector<T, TPageMetadata> =>\n    createSelector(selector, (s) => s.pages);\n\nexport const createLoadingStatesSelector = <T, TPageMetadata>(\n    selector: Selector<PainlessReduxState, EntityState<T, TPageMetadata>>,\n): LoadingStatesSelector<T, TPageMetadata> =>\n    createSelector(selector, (s) => s.loadingStates);\n\nconst createCreateLoadingStateById = <T, TPageMetadata>(\n    selector: LoadingStatesSelector<T, TPageMetadata>,\n) => (id: Id) => createSelector(\n    selector,\n    (loadingStates) => loadingStates[id],\n);\nconst createCreateLoadingStateByIds = <T, TPageMetadata>(\n    selector: LoadingStatesSelector<T, TPageMetadata>,\n) => (ids: Id[]) => createSelector(\n    selector,\n    (loadingStates) => ids.reduce((memo: LoadingState, id: Id) => {\n        const loadingState = loadingStates[id];\n        memo.isLoading = memo.isLoading || (loadingState?.isLoading ?? false);\n        memo.error = memo.error || loadingState?.error;\n        return memo;\n    }, { isLoading: false }),\n);\n\nexport const createBaseEntitySelectors = <T, TPageMetadata>(\n    selector: Selector<PainlessReduxState, EntityState<T, TPageMetadata>>,\n): BaseEntitySelectors<T, TPageMetadata> => {\n    const ids = createIdsSelector(selector);\n    const dictionary = createDictionarySelector(selector);\n    const pages = createPagesSelector(selector);\n    const loadingState = createLoadingStateSelector<EntityState<T, TPageMetadata>>(selector);\n    const loadingStates = createLoadingStatesSelector(selector);\n    const createLoadingStateById = createCreateLoadingStateById(loadingStates);\n    const createLoadingStateByIds = createCreateLoadingStateByIds(loadingStates);\n\n    return {\n        ids,\n        dictionary,\n        pages,\n        loadingState,\n        loadingStates,\n        createLoadingStateById,\n        createLoadingStateByIds,\n    };\n};\n\nconst getActual = <T>(\n    instance: EntityInstanceState<T> | undefined,\n): EntityType<T> | undefined => {\n    if (!instance || instance.removed) return undefined;\n    return getChangeableActual(instance);\n};\n\nexport const createCreateActualSelector = <T, TPageMetadata>(\n    dictionarySelector: DictionarySelector<T, TPageMetadata>,\n) => (\n    id: Id,\n): ActualSelector<T, TPageMetadata> => createSelector(\n    dictionarySelector,\n    (dictionary) => getActual(dictionary[id]),\n);\nexport const createListSelectorFromPages = <T, TPageMetadata>(\n    pagesSelector: PagesSelector<T, TPageMetadata>,\n): PagesListSelector<T, TPageMetadata> => createSelector(\n    pagesSelector,\n    (pages) => values(pages),\n);\n\nexport const createListSelector = <T, TPageMetadata>(\n    dictionarySelector: DictionarySelector<T, TPageMetadata>,\n) => (\n    idsSelector: IdsSelector<T, TPageMetadata>,\n): ListSelector<T, TPageMetadata> => createSelector(\n    idsSelector,\n    dictionarySelector,\n    (ids, dict) => {\n        if (!ids) return undefined;\n        return ids.map(id => getActual(dict[id]))\n            .filter((actual) => !isNil(actual)) as T[];\n    },\n);\n\nexport const createPageSelector = <T, TPageMetadata>(\n    pagesSelector: PagesSelector<T, TPageMetadata>,\n    hash: string,\n): PageSelector<T, TPageMetadata> =>\n    createSelector(pagesSelector, (pages) => pages[hash]);\n\nexport const createCreatePageIdsSelector = <T, TPageMetadata>(pagesSelector: PagesSelector<T, TPageMetadata>) => (\n    hash: string,\n): IdsSelector<T, TPageMetadata> => {\n    const pageSelector = createPageSelector<T, TPageMetadata>(pagesSelector, hash);\n    return createSelector(\n        pageSelector,\n        (page: Page<TPageMetadata> | undefined) => {\n            if (!page) return undefined;\n            return page.ids;\n        },\n    );\n};\n\nexport const createCreatePageIdsByConfigSelector = <T, TPageMetadata>(\n    pagesSelector: PagesSelector<T, TPageMetadata>,\n    hashFn: HashFn,\n) => (\n    config: unknown,\n): IdsSelector<T, TPageMetadata> => {\n    const hash = hashFn(config);\n    return createCreatePageIdsSelector<T, TPageMetadata>(pagesSelector)(hash);\n};\n\nexport const createCreatePageByConfigSelector = <T, TPageMetadata>(\n    pagesSelector: PagesSelector<T, TPageMetadata>,\n    hashFn: HashFn,\n) => (\n    config: unknown,\n): PageSelector<T, TPageMetadata> => {\n    const hash = hashFn(config);\n    return createPageSelector<T, TPageMetadata>(pagesSelector, hash);\n};\n\nexport const createCreatePageLoadingState = <T, TPageMetadata>(\n    pagesSelector: PagesSelector<T, TPageMetadata>,\n    hashFn: HashFn,\n) => (\n    config: unknown,\n): LoadingStateSelector<EntityState<T, TPageMetadata>> => {\n    const hash = hashFn(config);\n    const pageSelector = createPageSelector<T, TPageMetadata>(pagesSelector, hash);\n    return createSelector(\n        pageSelector,\n        (page) => page?.loadingState,\n    );\n};\n\n// TODO(egorgrushin): refactor here\nexport const createEntitySelectors = <T, TPageMetadata>(\n    selector: Selector<PainlessReduxState, EntityState<T, TPageMetadata>>,\n    hashFn: HashFn,\n): EntitySelectors<T, TPageMetadata> => {\n    const {\n        dictionary,\n        loadingStates,\n        loadingState,\n        pages,\n        ids,\n        createLoadingStateById,\n        createLoadingStateByIds,\n    } = createBaseEntitySelectors<T, TPageMetadata>(selector);\n    const createListSelectorByIds = createListSelector(dictionary);\n    const all = createListSelectorByIds(ids);\n    const allPages = createListSelectorFromPages(pages);\n\n    const createActual = createCreateActualSelector(dictionary);\n    const createPageIds = createCreatePageIdsSelector(pages);\n    const createPageIdsByConfig = createCreatePageIdsByConfigSelector(pages, hashFn);\n    const createPage = createCreatePageByConfigSelector(pages, hashFn);\n    const createPageLoadingState = createCreatePageLoadingState(pages, hashFn);\n\n    const createPageListByConfig = (config: unknown) => {\n        const pageIdsSelector = createPageIdsByConfig(config);\n        return createListSelectorByIds(pageIdsSelector);\n    };\n\n    return {\n        dictionary,\n        ids,\n        pages,\n        loadingStates,\n        loadingState,\n        all,\n        createActual,\n        createPageIds,\n        createPageIdsByConfig,\n        createListSelectorByIds,\n        createPageListByConfig,\n        createPage,\n        createPageLoadingState,\n        createLoadingStateById,\n        createLoadingStateByIds,\n        allPages,\n    };\n};\n"
  },
  {
    "path": "src/entity/types.ts",
    "content": "import {Observable} from 'rxjs';\nimport {Selector} from 'reselect';\nimport {DeepPartial, Dictionary, HashFn, Id, LoadingState} from '../system-types';\nimport {ChangeableState, PatchRequest} from '../shared/change/types';\nimport {\n    LoadingStateActionTypes,\n    LoadingStateSelector,\n    LoadingStateSelectors,\n    LoadingStateSetOptions,\n    LoadingStateState,\n} from '../shared/loading-state/types';\nimport {SelectEntityMethods} from './methods/select/types';\nimport {DispatchEntityMethods} from './methods/dispatch/types';\nimport {MixedEntityMethods} from './methods/mixed/types';\nimport {SystemActionTypes} from '../shared/system/types';\nimport {EntityActionCreators} from './action-creators.types';\nimport {RequestOptions} from '../shared/types';\n\nexport type EntityType<T> = T & { id: Id };\n\nexport interface EntitySchema<T> {\n    name: string;\n    hashFn: HashFn;\n    pageSize: number;\n    maxPagesCount: number;\n\n    id?(data: T): Id;\n}\n\nexport interface EntityLoadOptions extends EntityInsertOptions, RequestOptions {\n}\n\nexport interface EntityGetOptions extends EntityAddOptions {\n}\n\nexport interface EntityGetListOptions extends EntityLoadListOptions {\n}\n\nexport interface EntityLoadListOptions extends EntityAddListOptions, RequestOptions {\n    pageSize?: number;\n}\n\nexport interface EntityAddOptions extends EntityOptimisticOptions, EntityInsertOptions, RequestOptions {\n}\n\ninterface EntityInternalOptions {\n    maxPagesCount?: number;\n}\n\nexport interface EntityInternalAddOptions extends EntityAddOptions, EntityInternalOptions {\n}\n\nexport interface EntityInternalAddListOptions extends EntityAddListOptions, EntityInternalOptions {\n}\n\nexport interface EntityOptimisticOptions {\n    optimistic?: boolean;\n}\n\nexport interface EntityRemoveOptions extends EntityOptimisticOptions, RequestOptions {\n    safe?: boolean;\n}\n\nexport interface EntityRemoveListOptions extends EntityOptimisticOptions, RequestOptions {\n    safe?: boolean;\n}\n\nexport interface EntitySetLoadingStateOptions extends LoadingStateSetOptions {\n}\n\nexport interface EntityInternalSetLoadingStateOptions extends EntitySetLoadingStateOptions, EntityInternalOptions {\n}\n\nexport interface EntityInsertOptions {\n    pasteIndex?: number;\n    merge?: boolean;\n    single?: boolean;\n}\n\nexport interface EntityAddListOptions extends EntityInsertOptions {\n}\n\nexport interface Pagination {\n    index: number;\n    size: number;\n    from: number;\n    to: number;\n}\n\nexport interface ResponseArray<T, TPageMetadata> {\n    data: T[];\n    hasMore?: boolean;\n    metadata?: TPageMetadata;\n}\n\nexport interface PaginatedResponse<T, TPageMetadata> extends Pagination {\n    response: ResponseArray<T, TPageMetadata>;\n}\n\nexport type Response$Factory<T, TPageMetadata> = (pagination: Pagination) => Observable<ResponseArray<T, TPageMetadata>>;\n\nexport interface Page<TPageMetadata> {\n    ids: Id[] | undefined;\n    order?: number;\n    hasMore?: boolean;\n    loadingState?: LoadingState;\n    metadata?: TPageMetadata;\n}\n\nexport interface IdPatchRequest<T> {\n    id: Id;\n    patch: PatchRequest<T>;\n}\n\nexport interface IdPatch<T> {\n    id: Id;\n    patch: DeepPartial<T>;\n}\n\nexport interface EntityInstanceState<T> extends ChangeableState<EntityType<T>> {\n    removed?: boolean;\n}\n\nexport interface EntityState<T, TPageMetadata> extends LoadingStateState {\n    ids: Id[];\n    dictionary: Dictionary<EntityInstanceState<T>>;\n    pages: Dictionary<Page<TPageMetadata>>;\n    loadingStates: Dictionary<LoadingState>;\n}\n\nexport type IdsSelector<T, TPageMetadata> = Selector<EntityState<T, TPageMetadata>, Id[] | undefined>;\nexport type DictionarySelector<T, TPageMetadata> = Selector<EntityState<T, TPageMetadata>, Dictionary<EntityInstanceState<T>>>;\nexport type PagesSelector<T, TPageMetadata> = Selector<EntityState<T, TPageMetadata>, Dictionary<Page<TPageMetadata>>>;\nexport type PagesListSelector<T, TPageMetadata> = Selector<EntityState<T, TPageMetadata>, Page<TPageMetadata>[]>;\nexport type PageSelector<T, TPageMetadata> = Selector<EntityState<T, TPageMetadata>, Page<TPageMetadata> | undefined>;\nexport type LoadingStatesSelector<T, TPageMetadata> = Selector<EntityState<T, TPageMetadata>, Dictionary<LoadingState>>;\nexport type ActualSelector<T, TPageMetadata> = Selector<EntityState<T, TPageMetadata>, T | undefined>;\nexport type ListSelector<T, TPageMetadata> = Selector<EntityState<T, TPageMetadata>, T[] | undefined>;\n\nexport interface BaseEntitySelectors<T, TPageMetadata> extends LoadingStateSelectors<EntityState<T, TPageMetadata>> {\n    ids: IdsSelector<T, TPageMetadata>;\n    dictionary: DictionarySelector<T, TPageMetadata>;\n    pages: PagesSelector<T, TPageMetadata>;\n    loadingStates: LoadingStatesSelector<T, TPageMetadata>;\n    createLoadingStateById: (id: Id) => LoadingStateSelector<EntityState<T, TPageMetadata>>;\n    createLoadingStateByIds: (ids: Id[]) => LoadingStateSelector<EntityState<T, TPageMetadata>>;\n}\n\nexport interface EntitySelectors<T, TPageMetadata> extends BaseEntitySelectors<T, TPageMetadata> {\n    createActual: (id: Id) => ActualSelector<T, TPageMetadata>;\n    createPage: (config: unknown) => PageSelector<T, TPageMetadata>;\n    createPageIds: (hash: string) => IdsSelector<T, TPageMetadata>;\n    createPageLoadingState: (config: unknown) => LoadingStateSelector<EntityState<T, TPageMetadata>>;\n    createPageIdsByConfig: (config: unknown) => IdsSelector<T, TPageMetadata>;\n    createListSelectorByIds: (idsSelector: IdsSelector<T, TPageMetadata>) => ListSelector<T, TPageMetadata>;\n    createPageListByConfig: (config: unknown) => ListSelector<T, TPageMetadata>;\n    allPages: PagesListSelector<T, TPageMetadata>;\n    all: ListSelector<T, TPageMetadata>;\n}\n\nexport interface EntityActionTypes extends SystemActionTypes {\n    ADD: 'ADD';\n    RESOLVE_ADD: 'RESOLVE_ADD';\n    ADD_LIST: 'ADD_LIST';\n    SET_LOADING_STATE: LoadingStateActionTypes['SET_LOADING_STATE'];\n    CHANGE: 'CHANGE';\n    RESOLVE_CHANGE: 'RESOLVE_CHANGE';\n    REMOVE: 'REMOVE';\n    RESOLVE_REMOVE: 'RESOLVE_REMOVE';\n    RESTORE_REMOVED: 'RESTORE_REMOVED';\n    REMOVE_LIST: 'REMOVE_LIST';\n    RESOLVE_REMOVE_LIST: 'RESOLVE_REMOVE_LIST';\n    RESTORE_REMOVED_LIST: 'RESTORE_REMOVED_LIST';\n    CLEAR: 'CLEAR';\n    CLEAR_ALL: 'CLEAR_ALL';\n    CHANGE_LIST: 'CHANGE_LIST';\n    RESOLVE_CHANGE_LIST: 'RESOLVE_CHANGE_LIST';\n    SET_LOADING_STATES: 'SET_LOADING_STATES';\n}\n\nexport type PublicDispatchEntityMethods<T, TPageMetadata> = Omit<DispatchEntityMethods<T, TPageMetadata>,\n    'changeWithId' | 'changeListWithId' | 'resolveChange' | 'resolveAdd' | 'resolveRemove' | 'resolveChangeList'>\nexport type PublicSelectEntityMethods<T, TPageMetadata> = Omit<SelectEntityMethods<T, TPageMetadata>, 'get$' | 'getDictionary$' | 'getById$'>\n\nexport interface Entity<T, TPageMetadata = void> extends PublicSelectEntityMethods<T, TPageMetadata>, PublicDispatchEntityMethods<T, TPageMetadata>, MixedEntityMethods<T, TPageMetadata> {\n    actionCreators: EntityActionCreators<T, TPageMetadata>;\n}\n"
  },
  {
    "path": "src/entity/utils.ts",
    "content": "import { createActionTypes, hashIt, typedDefaultsDeep } from '../utils';\nimport {\n    EntityActionTypes,\n    EntitySchema,\n    EntityType,\n    PaginatedResponse,\n    Pagination,\n    Response$Factory,\n    ResponseArray,\n} from './types';\nimport { DEFAULT_PAGE_SIZE, ENTITY_TYPE_NAMES, MAX_PAGES_COUNT } from './constants';\nimport { v4 } from 'uuid';\nimport { Observable } from 'rxjs';\nimport { map } from 'rxjs/operators';\nimport { getObservable$ } from '../shared/utils';\n\nexport const getHash = (config: unknown): string => hashIt(config);\n\nexport const getFullEntitySchema = <T>(\n    schema?: Partial<EntitySchema<T>>,\n): EntitySchema<T> => typedDefaultsDeep(schema, {\n    name: '',\n    hashFn: getHash,\n    pageSize: DEFAULT_PAGE_SIZE,\n    maxPagesCount: MAX_PAGES_COUNT,\n}) as EntitySchema<T>;\n\nexport const createIdResolver = <T>(\n    schema: EntitySchema<T>,\n) => (data: T): EntityType<T> => {\n    if (schema.id) return { ...data, id: schema.id(data) };\n    if ('id' in data) return data as EntityType<T>;\n    return { ...data, id: v4() };\n};\n\nexport const createEntityActionTypes = (\n    entityName: string,\n): EntityActionTypes => createActionTypes(ENTITY_TYPE_NAMES, entityName);\n\nexport const getPaginated$ = <T, TPageMetadata>(\n    dataSource: Observable<ResponseArray<T, TPageMetadata>> | Response$Factory<T, TPageMetadata>,\n    pagination: Pagination,\n): Observable<PaginatedResponse<T, TPageMetadata>> => getObservable$(dataSource, pagination).pipe(\n    map((response) => ({ ...pagination, response })),\n);\n"
  },
  {
    "path": "src/index.ts",
    "content": "export * from './painless-redux/painless-redux';\nexport * from './painless-redux/types';\nexport * from './shared/types';\nexport * from './shared/change/types';\nexport * from './shared/change/actions';\nexport * from './shared/loading-state/types';\nexport * from './shared/system/types';\nexport * from './workspace/workspace';\nexport * from './workspace/types';\nexport { WorkspaceActions } from './workspace/actions';\nexport * from './workspace/action-creators';\nexport * from './entity/entity';\nexport * from './entity/types';\nexport * from './entity/action-creators';\nexport * from './entity/action-creators.types';\nexport { EntityActions } from './entity/actions';\nexport * from './affect-loading-state/affect-loading-state';\nexport * from './affect-loading-state/types';\nexport * from './system-types';\nexport {\n    hashString,\n    hashIt,\n    snapshot,\n    merge,\n} from './utils';\nexport {\n    actionSanitizer,\n} from './workspace/utils';\n"
  },
  {
    "path": "src/painless-redux/action-creators.ts",
    "content": ""
  },
  {
    "path": "src/painless-redux/constants.ts",
    "content": "import { SystemActionTypes } from '../shared/system/types';\n\nexport const SYSTEM_TYPE_NAMES: Array<keyof SystemActionTypes> = [];\n"
  },
  {
    "path": "src/painless-redux/painless-redux.ts",
    "content": "import { PainlessRedux, PainlessReduxSchema, SlotTypes } from './types';\nimport { ActionCreator, AnyAction, Reducer, RxStore, SameShaped } from '../system-types';\nimport { createDispatcher } from '../dispatcher/dispatcher';\nimport { typedDefaultsDeep } from '../utils';\nimport { createSlotSelector, createSlotsSelector } from './selectors';\nimport { getWrappedReducer } from './reducers';\nimport { createSelectManager } from '../select-manager/select-manager';\nimport { createRegister } from './register';\nimport { createSelector } from 'reselect';\nimport { createSystemActionTypes } from './utils';\n\nexport const createPainlessRedux = (\n    rxStore: RxStore,\n    schema?: Partial<PainlessReduxSchema>,\n): PainlessRedux => {\n    const fullSchema = typedDefaultsDeep(schema, {\n        name: '@store',\n        entityDomainName: 'entities',\n        workspaceDomainName: 'workspaces',\n        useAsapSchedulerInLoadingGuards: true,\n        selector: (state: any) => state,\n    }) as PainlessReduxSchema;\n\n    const domainName = fullSchema.name;\n    const initialValue = {};\n\n    const selector = createSelector(fullSchema.selector, (state: any) => state[domainName]);\n    const slotsSelector = createSlotsSelector(fullSchema, selector);\n    const systemActionTypes = createSystemActionTypes(domainName);\n\n    const { value: register, checkSlotUniq, addNewSlotToRegister } = createRegister();\n    const getReducer = () => getWrappedReducer(fullSchema, register, systemActionTypes, initialValue);\n\n    const registerSlotReducer = <TState, TActions extends AnyAction>(\n        type: SlotTypes,\n        name: string,\n        reducer: Reducer<TState, TActions>,\n    ) => {\n        checkSlotUniq(type, name);\n        addNewSlotToRegister<TState, TActions>(type, name, reducer);\n        const fullReducer = getReducer();\n        rxStore.addReducer(domainName, fullReducer);\n    };\n\n    const registerSlot = <TState, TActionTypes, TActions extends AnyAction>(\n        type: SlotTypes,\n        name: string,\n        reducer: Reducer<TState, TActions>,\n        actionCreators: SameShaped<TActionTypes, ActionCreator<TActionTypes, TActions>>,\n    ) => {\n        const dispatcher = createDispatcher<TActionTypes, TActions>(rxStore, actionCreators);\n        const selectManager = createSelectManager(rxStore);\n        const currentSlotsSelector = slotsSelector<TState>(type);\n        const selector = createSlotSelector<TState>(currentSlotsSelector, name);\n        registerSlotReducer<TState, TActions>(type, name, reducer);\n        return {\n            dispatcher,\n            selector,\n            selectManager,\n        };\n    };\n\n    return {\n        registerSlot,\n        getReducer,\n        name: domainName,\n        schema: fullSchema,\n    };\n};\n"
  },
  {
    "path": "src/painless-redux/reducers.ts",
    "content": "import { PainlessReduxRegister, PainlessReduxSchema, PainlessReduxState, SlotTypes } from './types';\nimport { AnyAction, PayloadAction, Reducer } from '../system-types';\nimport { combineReducers } from '../shared/utils';\nimport { SystemActionTypes } from '../shared/system/types';\n\nexport const getSlotReducer = (\n    register: PainlessReduxRegister,\n    type: SlotTypes,\n): Reducer<any, AnyAction> => {\n    const slots = register[type];\n    return combineReducers(slots);\n};\n\nexport const createFullReducer = (\n    schema: PainlessReduxSchema,\n    register: PainlessReduxRegister,\n): Reducer<PainlessReduxState, AnyAction> => combineReducers({\n    [schema.entityDomainName]: getSlotReducer(register, SlotTypes.Entity),\n    [schema.workspaceDomainName]: getSlotReducer(register, SlotTypes.Workspace),\n});\n\nexport const getWrappedReducer = (\n    schema: PainlessReduxSchema,\n    register: PainlessReduxRegister,\n    types: SystemActionTypes,\n    initialState: PainlessReduxState,\n): Reducer<PainlessReduxState, PayloadAction> => {\n    return createFullReducer(schema, register);\n    // const reducerMiddlewareFactory = undoReducerFactory(types, initialState);\n    // return reducerMiddlewareFactory(reducer);\n};\n"
  },
  {
    "path": "src/painless-redux/register.ts",
    "content": "import { PainlessReduxRegister, SlotTypes } from './types';\nimport { isNil } from 'lodash-es';\nimport { AnyAction, Reducer } from '../system-types';\n\nexport const createRegister = () => {\n    const value: PainlessReduxRegister = {\n        [SlotTypes.Entity]: {},\n        [SlotTypes.Workspace]: {},\n    };\n\n    const checkSlotUniq = (\n        type: SlotTypes,\n        name: string,\n    ) => {\n        const existSlotReducer = value[type][name];\n        if (isNil(existSlotReducer)) return;\n        throw new Error('Slot name is not uniq');\n    };\n\n    const addNewSlotToRegister = <TState, TActions extends AnyAction>(\n        type: SlotTypes,\n        name: string,\n        reducer: Reducer<TState, TActions>,\n    ) => {\n        value[type] = {\n            ...value[type],\n            [name]: reducer as Reducer<any, AnyAction>,\n        };\n        return value;\n    };\n\n\n    return { addNewSlotToRegister, checkSlotUniq, value };\n};\n"
  },
  {
    "path": "src/painless-redux/selectors.ts",
    "content": "import { PainlessReduxSchema, PainlessReduxState, SlotTypes } from './types';\nimport { createSelector, Selector } from 'reselect';\nimport { Dictionary, StrictDictionary } from '../system-types';\n\nexport const createEntitiesSelector = (\n    schema: PainlessReduxSchema,\n    selector: Selector<any, PainlessReduxState>,\n) => createSelector(\n    selector,\n    (state: PainlessReduxState) => state[schema.entityDomainName],\n);\n\nexport const createWorkspacesSelector = (\n    schema: PainlessReduxSchema,\n    selector: Selector<any, PainlessReduxState>,\n) => createSelector(\n    selector,\n    (state: PainlessReduxState) => state[schema.workspaceDomainName],\n);\n\nconst SLOTS_SELECTOR_FACTORIES: StrictDictionary<SlotTypes, any> = {\n    [SlotTypes.Entity]: createEntitiesSelector,\n    [SlotTypes.Workspace]: createWorkspacesSelector,\n};\n\nexport const createSlotsSelector = (\n    schema: PainlessReduxSchema,\n    selector: Selector<any, PainlessReduxState>,\n) => <TState>(\n    type: SlotTypes,\n): Selector<PainlessReduxState, Dictionary<TState>> => {\n    const slotSelectorFactory = SLOTS_SELECTOR_FACTORIES[type];\n    return slotSelectorFactory(schema, selector);\n};\n\nexport const createSlotSelector = <TState>(\n    slotsSelector: Selector<PainlessReduxState, Dictionary<TState>>,\n    name: string,\n): Selector<PainlessReduxState, TState> => createSelector(\n    slotsSelector,\n    (state) => state[name],\n);\n"
  },
  {
    "path": "src/painless-redux/types.ts",
    "content": "import { Selector } from 'reselect';\nimport {\n    ActionCreator,\n    AnyAction,\n    Dictionary,\n    PayloadAction,\n    Reducer,\n    SameShaped,\n    StrictDictionary,\n} from '../system-types';\nimport { Dispatcher } from '../dispatcher/types';\nimport { SelectManager } from '../select-manager/types';\n\nexport enum SlotTypes {\n    Entity,\n    Workspace\n}\n\nexport interface PainlessReduxSchema {\n    name: string;\n    entityDomainName: string;\n    workspaceDomainName: string;\n    selector: Selector<any, any>;\n    useAsapSchedulerInLoadingGuards: boolean;\n}\n\nexport type PainlessReduxRegister = StrictDictionary<SlotTypes, Dictionary<Reducer<any, AnyAction>>, SlotTypes>;\n\nexport type PainlessReduxState = Dictionary<any>;\n\nexport type PainlessRedux = {\n    name: string;\n    schema: PainlessReduxSchema;\n    registerSlot<TState, TActionTypes, TActions extends AnyAction>(\n        type: SlotTypes,\n        name: string,\n        reducer: Reducer<TState, TActions>,\n        actionCreators: SameShaped<TActionTypes, ActionCreator<TActionTypes, TActions>>,\n    ): {\n        selector: Selector<PainlessReduxState, TState>;\n        dispatcher: Dispatcher<TActionTypes, TActions>;\n        selectManager: SelectManager;\n    };\n    getReducer(): Reducer<PainlessReduxState, PayloadAction>;\n}\n"
  },
  {
    "path": "src/painless-redux/utils.ts",
    "content": "import { SystemActionTypes } from '../shared/system/types';\nimport { createActionTypes } from '../utils';\nimport { SYSTEM_TYPE_NAMES } from './constants';\n\n\nexport const createSystemActionTypes = (\n    name: string,\n): SystemActionTypes => createActionTypes(SYSTEM_TYPE_NAMES, name);\n"
  },
  {
    "path": "src/select-manager/select-manager.ts",
    "content": "import { RxStore } from '../system-types';\nimport { asapScheduler, Observable } from 'rxjs';\nimport { subscribeOn } from 'rxjs/operators';\nimport { Selector } from 'reselect';\nimport { select, snapshot } from '../utils';\nimport { SelectManager } from './types';\n\nexport const createSelectManager = (rxStore: RxStore): SelectManager => {\n\n    const select$ = <T, R>(\n        selector: Selector<T, R>,\n        isAsap: boolean = false,\n    ): Observable<R> => {\n        const selectObs = rxStore.pipe(select(selector));\n        if (!isAsap) return selectObs;\n        return selectObs.pipe(subscribeOn(asapScheduler));\n    };\n\n    const snapshotFn = <T, R>(\n        selector: Selector<T, R>,\n    ): R | undefined => {\n        const source$ = select$<T, R>(selector);\n        return snapshot<R>(source$);\n    };\n\n    return {\n        select$,\n        snapshot: snapshotFn,\n    };\n\n};\n"
  },
  {
    "path": "src/select-manager/types.ts",
    "content": "import { Selector } from 'reselect';\nimport { Observable } from 'rxjs';\n\nexport interface SelectManager {\n    select$<T, R>(selector: Selector<T, R>, isAsap?: boolean): Observable<R>;\n    snapshot<T, R>(selector: Selector<T, R>): R | undefined;\n}\n"
  },
  {
    "path": "src/shared/change/actions.ts",
    "content": "import { ChangeActionTypes, ChangeOptions } from './types';\nimport { typedDefaultsDeep } from '../../utils';\nimport { DeepPartial } from '../../system-types';\n\nexport const createChange = <T>(types: ChangeActionTypes) => (\n    patch: DeepPartial<T>,\n    changeId?: string,\n    options?: ChangeOptions,\n) => {\n    options = typedDefaultsDeep(options, { merge: true });\n    const payload = { patch, changeId };\n    return { type: types.CHANGE, payload, options } as const;\n};\n\nexport const createResolveChange = <T>(types: ChangeActionTypes) => (\n    changeId: string,\n    success: boolean,\n    remotePatch?: DeepPartial<T>,\n    options?: ChangeOptions,\n) => {\n    options = typedDefaultsDeep(options, { merge: true });\n    const payload = { changeId, success, remotePatch };\n    return { type: types.RESOLVE_CHANGE, payload, options } as const;\n};\n\ntype SelfActionCreators = ReturnType<typeof createChange>\n    | ReturnType<typeof createResolveChange>;\n\nexport type ChangeActions = ReturnType<SelfActionCreators>\n"
  },
  {
    "path": "src/shared/change/reducer.spec.ts",
    "content": "import { ChangeActionTypes } from './types';\nimport { createChangeReducer } from './reducer';\nimport { createChange } from './actions';\n\nconst types: ChangeActionTypes = {\n    CHANGE: 'CHANGE',\n    RESOLVE_CHANGE: 'RESOLVE_CHANGE',\n};\n\ndescribe('change', () => {\n    const initialActual = { values: [0, 1, 2], key: 'test' };\n    const initialValue = { actual: initialActual };\n    const reducer = createChangeReducer(types, initialActual);\n\n    test('should init value', () => {\n        // act\n        const actual = reducer(undefined, { type: 'INIT' } as any);\n        // assert\n        expect(actual?.actual).toBe(initialActual);\n    });\n\n    test('should override arrays while changing even if new array is smaller', () => {\n        // arrange\n        const values = [3, 4];\n        const action = createChange(types)({ values });\n        // act\n        const actual = reducer(initialValue, action);\n        // assert\n        expect(actual?.actual.values).toEqual(values);\n    });\n\n    test('should override arrays while changing even if new array is bigger', () => {\n        // arrange\n        const values = [3, 4, 5, 6];\n        const action = createChange(types)({ values });\n        // act\n        const actual = reducer(initialValue, action);\n        // assert\n        expect(actual?.actual.values).toEqual(values);\n    });\n\n    test('should set only given properties to undefined', () => {\n        // arrange\n        const action = createChange(types)({ values: undefined });\n        // act\n        const actual = reducer(initialValue, action);\n        // assert\n        expect(actual?.actual).toEqual({ ...initialActual, values: undefined });\n\n    });\n\n});\n"
  },
  {
    "path": "src/shared/change/reducer.ts",
    "content": "import { ChangeableState, ChangeActionTypes } from './types';\nimport { ChangeActions } from './actions';\nimport { createInstanceByChanges, getMergedChanges, resolveChanges } from './utils';\n\nexport const createChangeReducer = <T>(\n    types: ChangeActionTypes,\n    initialActual?: T,\n) => (\n    state: ChangeableState<T> | undefined,\n    action: ChangeActions,\n): ChangeableState<T> | undefined => {\n    if (!state && initialActual) {\n        state = { actual: initialActual };\n    }\n    switch (action.type) {\n        case types.CHANGE: {\n            const {\n                payload: { patch, changeId },\n                options: { merge, optimistic },\n            } = action;\n            const instance = createInstanceByChanges<T>(\n                state,\n                patch,\n                merge,\n                !optimistic,\n                changeId,\n            );\n            return getMergedChanges(instance, true);\n        }\n        case types.RESOLVE_CHANGE: {\n            if (!state) return state;\n            const {\n                payload: { success, changeId, remotePatch },\n                options: { merge, optimistic },\n            } = action;\n            const instance = createInstanceByChanges<T>(\n                state,\n                remotePatch,\n                merge,\n                !optimistic,\n                changeId,\n            ) as ChangeableState<T>;\n            const changes = resolveChanges(instance?.changes, success, changeId);\n            const newState = { ...instance, changes };\n            return getMergedChanges(newState, true);\n        }\n        default:\n            return state;\n    }\n};\n"
  },
  {
    "path": "src/shared/change/selectors.ts",
    "content": "import { getMergedChanges } from './utils';\nimport { ChangeableState } from './types';\n\nexport const getChangeableActual = <T>(\n    instance: ChangeableState<T> | undefined,\n): T | undefined => {\n    if (!instance) return undefined;\n    return getMergedChanges(instance)?.actual;\n};\n"
  },
  {
    "path": "src/shared/change/types.ts",
    "content": "import { DeepPartial } from '../../system-types';\nimport { RequestOptions } from '../types';\n\nexport interface ChangeOptions extends RequestOptions {\n    merge?: boolean;\n    ifNotExist?: boolean;\n    optimistic?: boolean;\n    useResponsePatch?: boolean;\n}\n\nexport interface ChangeActionTypes {\n    CHANGE: 'CHANGE';\n    RESOLVE_CHANGE: 'RESOLVE_CHANGE';\n}\n\nexport interface Change<T> {\n    stable: boolean;\n    patch: DeepPartial<T>;\n    merge: boolean;\n    id?: string;\n}\n\nexport interface ChangeableState<T> {\n    actual: T;\n    changes?: Change<T>[];\n}\n\nexport type PatchRequest<T> = DeepPartial<T> | ((value: DeepPartial<T> | undefined) => DeepPartial<T>);\n"
  },
  {
    "path": "src/shared/change/utils.ts",
    "content": "import { DeepPartial } from '../../system-types';\nimport { Change, ChangeableState, ChangeOptions, PatchRequest } from './types';\nimport { merge as mergeFn, snapshot } from '../../utils';\nimport { Observable } from 'rxjs';\n\nexport const createEntityChange = <T>(\n    patch: DeepPartial<T>,\n    stable = false,\n    merge = true,\n    id?: string,\n): Change<T> => ({ patch, stable, merge, id });\n\nexport const getMergedChanges = <T>(\n    state: ChangeableState<T> | undefined,\n    onlyStable?: boolean,\n): ChangeableState<T> | undefined => {\n    if (!state) return state;\n    let { actual, changes = [] } = state;\n    if (changes.length === 0) return state;\n    let change: Change<T> | undefined;\n\n    while ((change = changes[0])) {\n        if (onlyStable && !change.stable) break;\n        changes = changes.slice(1);\n        const { merge, patch } = change;\n        actual = merge ? mergeFn(actual, patch) : patch as T;\n    }\n\n    if (changes.length === 0) return { actual };\n    return { actual, changes: changes };\n};\n\nexport const createInstanceByChanges = <T>(\n    state: ChangeableState<T> | undefined,\n    patch: DeepPartial<T> | undefined,\n    merge: boolean = true,\n    success: boolean = true,\n    id?: string,\n): ChangeableState<T> | undefined => {\n    if (!patch) return state;\n    const actual = state?.actual ?? patch as T;\n    const change = createEntityChange(patch, success, merge, id);\n    const changes = state?.changes ?? [];\n    return { actual, changes: changes.concat(change) };\n};\n\nexport const resolveChanges = <T>(\n    changes: Change<T>[] | undefined,\n    success: boolean,\n    id: string,\n): Change<T>[] | undefined => {\n    if (!changes) return;\n    if (success) {\n        return changes.map((change) => {\n            if (change.id === id) return { ...change, stable: true };\n            return change;\n        });\n    }\n    return changes.filter((change) => change.id !== id);\n};\n\nexport const getPatchByOptions = <T>(\n    patch: DeepPartial<T>,\n    response: DeepPartial<T> | undefined,\n    options?: ChangeOptions,\n): DeepPartial<T> => {\n    if (options?.optimistic) return patch;\n    if (options?.useResponsePatch) return response ?? {};\n    return patch;\n};\n\nexport const getResolvePatchByOptions = <T>(\n    patch: DeepPartial<T>,\n    response: DeepPartial<T> | undefined,\n    options?: ChangeOptions,\n): DeepPartial<T> | undefined => {\n    if (options?.useResponsePatch) return response;\n};\n\nexport const normalizePatch = <T>(\n    patch: PatchRequest<T>,\n    oldValue$: Observable<DeepPartial<T> | undefined>,\n): DeepPartial<T> => {\n    if (typeof patch === 'function') {\n        const oldValue = snapshot(oldValue$);\n        return patch(oldValue as DeepPartial<T>);\n    }\n    return patch;\n};\n\n"
  },
  {
    "path": "src/shared/loading-state/actions.ts",
    "content": "import { LoadingStateActionTypes, LoadingStateSetOptions } from './types';\nimport { LoadingState } from '../../system-types';\nimport { typedDefaultsDeep } from '../../utils';\n\nexport const createSetLoadingState = (types: LoadingStateActionTypes) => (\n    state: LoadingState,\n    key?: string,\n    options?: LoadingStateSetOptions,\n) => {\n    options = typedDefaultsDeep(options);\n    const payload = { state, key };\n    return { type: types.SET_LOADING_STATE, payload, options } as const;\n};\n\nexport type LoadingStateActions = ReturnType<ReturnType<typeof createSetLoadingState>>;\n"
  },
  {
    "path": "src/shared/loading-state/reducers.spec.ts",
    "content": "import { createLoadingStateReducer, loadingStateReducer } from './reducers';\nimport { LoadingState } from '../../system-types';\nimport { LoadingStateActionTypes } from './types';\nimport { LoadingStateActions } from './actions';\n\nconst types: LoadingStateActionTypes = {\n    SET_LOADING_STATE: 'SET_LOADING_STATE',\n};\n\ndescribe('loadingState', () => {\n    let initialState: any;\n    const newState = { isLoading: true };\n    const someKey = 'some-key';\n\n    beforeEach(() => {\n        initialState = {};\n    });\n\n    describe('#createLoadingStateReducer', () => {\n        const reducer = createLoadingStateReducer(types);\n\n        test('should return default state', () => {\n            // act\n            const actual = reducer(undefined, { type: 'INIT' } as any);\n            // assert\n            expect(actual).toEqual(undefined);\n        });\n\n        test('should correct set state', () => {\n            // arrange\n            const action: LoadingStateActions = {\n                type: types.SET_LOADING_STATE,\n                payload: {\n                    state: newState,\n                    key: undefined,\n                },\n                options: {},\n            };\n            // act\n            const actual = reducer(undefined, action);\n            // assert\n            const expected = newState;\n            expect(actual).toEqual(expected);\n        });\n    });\n\n    describe('#loadingStateReducer', () => {\n\n        test('should set state for a key', () => {\n            // act\n            const actual = loadingStateReducer(initialState, someKey, newState);\n            // assert\n            const expected: LoadingState = {\n                byKeys: { [someKey]: newState },\n                isLoading: false,\n            };\n            expect(actual).toEqual(expected);\n        });\n\n        test('should set state as is', () => {\n            // act\n            const actual = loadingStateReducer(initialState, undefined, newState);\n            // assert\n            expect(actual).toEqual(newState);\n        });\n\n    });\n\n});\n"
  },
  {
    "path": "src/shared/loading-state/reducers.ts",
    "content": "import { Dictionary, LoadingState } from '../../system-types';\nimport { LoadingStateActionTypes } from './types';\nimport { LoadingStateActions } from './actions';\n\nconst pureLoadingStateReducer = (\n    state: LoadingState | undefined,\n    newState: LoadingState,\n): LoadingState => ({ ...state, ...newState });\n\nexport const loadingStateByKeysReducer = (\n    state: Dictionary<LoadingState> = {},\n    key: string,\n    newState: LoadingState,\n): Dictionary<LoadingState> => {\n    return {\n        ...state,\n        [key]: pureLoadingStateReducer(state[key], newState),\n    };\n};\n\nexport const loadingStateReducer = (\n    state: LoadingState | undefined,\n    key: string | undefined,\n    newState: LoadingState,\n): LoadingState => {\n    if (!key) return pureLoadingStateReducer(state, newState);\n    return {\n        ...state,\n        byKeys: loadingStateByKeysReducer(state?.byKeys, key, newState),\n        isLoading: state?.isLoading ?? false,\n    };\n};\n\nexport const createLoadingStateReducer = (\n    types: LoadingStateActionTypes,\n) => (\n    state: LoadingState | undefined,\n    action: LoadingStateActions,\n) => {\n    switch (action.type) {\n        case types.SET_LOADING_STATE: {\n            const { key, state: newState } = action.payload;\n            return loadingStateReducer(state, key, newState);\n        }\n        default:\n            return state;\n    }\n};\n"
  },
  {
    "path": "src/shared/loading-state/selectors.ts",
    "content": "import { createSelector, Selector } from 'reselect';\nimport { LoadingStateState } from './types';\n\nexport const createLoadingStateSelector = <T extends LoadingStateState>(\n    selector: Selector<any, T>,\n) => createSelector(\n    selector,\n    (state: T) => state.loadingState ?? { isLoading: false },\n);\n"
  },
  {
    "path": "src/shared/loading-state/types.ts",
    "content": "import { LoadingState } from '../../system-types';\nimport { Selector } from 'reselect';\n\nexport interface LoadingStateState {\n    loadingState?: LoadingState;\n}\n\nexport interface LoadingStateSetOptions {\n\n}\n\nexport interface LoadingStateActionTypes {\n    SET_LOADING_STATE: 'SET_LOADING_STATE';\n}\n\nexport type LoadingStateSelector<T> = Selector<T, LoadingState | undefined>\n\nexport interface LoadingStateSelectors<T> {\n    loadingState: LoadingStateSelector<T>;\n}\n\n"
  },
  {
    "path": "src/shared/system/actions.ts",
    "content": "import { SystemActionTypes } from './types';\n\nexport const createBatch = <T>(types: SystemActionTypes) => (actions: T[]) => {\n    const payload = { actions };\n    return { type: types.BATCH, payload } as const;\n};\n\nexport type SystemActions = ReturnType<ReturnType<typeof createBatch>>;\n"
  },
  {
    "path": "src/shared/system/reducers.ts",
    "content": "import { AnyAction, Reducer } from '../../system-types';\nimport { SystemActionTypes } from './types';\n\nexport const batchActionsReducerFactory = <TState, TAction extends AnyAction>(\n    types: SystemActionTypes,\n    reducer: Reducer<TState, TAction>,\n) => (state: TState, action: TAction): TState => {\n    if (action.type === types.BATCH) {\n        const { actions } = (action as any).payload;\n        return actions.reduce(reducer, state);\n    }\n    return reducer(state, action);\n};\n"
  },
  {
    "path": "src/shared/system/system.ts",
    "content": ""
  },
  {
    "path": "src/shared/system/types.ts",
    "content": "export interface SystemActionTypes {\n    BATCH: 'BATCH';\n}\n"
  },
  {
    "path": "src/shared/types.ts",
    "content": "import { AnyAction, LoadingState } from '../system-types';\nimport { Observable } from 'rxjs';\n\nexport type ObservableOrFactory<S, R> = (Observable<R>) | ((value: S) => Observable<R>);\n\nexport interface RemotePipeConfig<TSource, TStore, TResponse> {\n    store$?: Observable<TStore>;\n    remoteObsOrFactory: ObservableOrFactory<TSource, TResponse>;\n    options?: RemoteOptions;\n    success: (result?: TResponse) => AnyAction | undefined;\n    emitSuccessOutsideAffectState?: boolean;\n    emitOnSuccess?: boolean;\n    optimistic?: boolean;\n    optimisticResolve?: (\n        success: boolean,\n        result?: TResponse,\n    ) => AnyAction | undefined;\n    setLoadingState?: (loadingState: LoadingState) => void;\n}\n\nexport interface OptimisticOptions {\n    optimistic?: boolean;\n}\n\nexport interface RequestOptions {\n    rethrow?: boolean;\n    globalLoadingState?: boolean;\n}\n\nexport interface RemoteOptions extends OptimisticOptions, RequestOptions {\n    single?: boolean;\n}\n"
  },
  {
    "path": "src/shared/utils.ts",
    "content": "import { EMPTY, MonoTypeOperatorFunction, Observable, of, OperatorFunction } from 'rxjs';\nimport { catchError, filter, first, map, switchMap, tap } from 'rxjs/operators';\nimport { ObservableOrFactory, RemoteOptions, RemotePipeConfig } from './types';\nimport { isFunction, isNil } from 'lodash-es';\nimport { AnyAction, CombinedReducers, LoadingState, Reducer } from '../system-types';\nimport { affectLoadingStateFactory } from '../affect-loading-state/affect-loading-state';\n\nexport const getObservable$ = <S, R>(\n    observableOrFactory: ObservableOrFactory<S, R>,\n    value: S,\n): Observable<R> => isFunction(observableOrFactory)\n    ? observableOrFactory(value)\n    : observableOrFactory;\n\nexport const guardByOptions = <T>(\n    options?: RemoteOptions,\n): MonoTypeOperatorFunction<T | T[]> => (\n    source: Observable<T | T[]>,\n): Observable<T | T[]> => source.pipe(\n    filter((storeValue) => !options?.single || isNil(storeValue)),\n);\n\nexport const getRemotePipe = <TSource, TStore, TResponse, TOutput>(\n    {\n        store$,\n        remoteObsOrFactory,\n        options,\n        success,\n        emitSuccessOutsideAffectState,\n        emitOnSuccess,\n        optimistic,\n        optimisticResolve,\n        setLoadingState,\n    }: RemotePipeConfig<TSource, TStore, TResponse>,\n): OperatorFunction<TSource, TOutput> => {\n    const trailPipe: OperatorFunction<TResponse, TOutput> = emitOnSuccess\n        ? map((result: TResponse) => result as unknown as TOutput)\n        : switchMap(() => EMPTY);\n\n    return (source: Observable<TSource>): Observable<TOutput> => source.pipe(\n        switchMap((value: TSource) => {\n            const remote$ = getObservable$(remoteObsOrFactory, value);\n            if (optimistic) {\n                success();\n                return remote$.pipe(\n                    tap((response) => optimisticResolve?.(true, response)),\n                    catchError((error: any) => {\n                        optimisticResolve?.(false);\n                        setLoadingState?.({ error, isLoading: false });\n                        return EMPTY;\n                    }),\n                );\n            }\n            const successPipe = tap((result: TResponse) => success(result));\n            const pipesToAffect: OperatorFunction<any, any>[] = [\n                switchMap(() => remote$),\n            ];\n            const resultPipes: OperatorFunction<any, any>[] = [];\n\n            if (emitSuccessOutsideAffectState) {\n                resultPipes.push(successPipe);\n            } else {\n                pipesToAffect.push(successPipe);\n            }\n            if (setLoadingState) {\n                const rethrow = options?.rethrow ?? false;\n                const affectStateObsFactory = affectLoadingStateFactory(setLoadingState, rethrow);\n                const affectPipe = affectStateObsFactory(...pipesToAffect);\n                resultPipes.unshift(affectPipe);\n            } else {\n                resultPipes.unshift(...pipesToAffect);\n            }\n            if (store$) {\n                return (store$ as any).pipe(\n                    first(),\n                    guardByOptions<TSource>(options),\n                    ...resultPipes,\n                ) as Observable<TResponse>;\n            }\n            return (of(value) as any).pipe(...resultPipes) as Observable<TResponse>;\n\n        }),\n        trailPipe,\n    );\n};\n\nexport const guardIfLoading = (\n    loadingStateObs: Observable<LoadingState | undefined>,\n): Observable<LoadingState | undefined> => loadingStateObs.pipe(\n    first(),\n    filter((loadingState: LoadingState | undefined) => !loadingState?.isLoading),\n);\n\nexport const combineReducers = <TState extends object, TAction extends AnyAction>(\n    reducers: CombinedReducers<TState>,\n): Reducer<TState, TAction> => {\n    const reducerKeys = Object.keys(reducers) as (keyof TState)[];\n\n    return (state: TState, action: any) => {\n        state = state ?? {};\n        let hasChanged = false;\n        const nextState: Partial<TState> = {};\n\n        reducerKeys.forEach((reducerKey) => {\n            const subReducer = reducers[reducerKey];\n            const subState = state[reducerKey];\n            nextState[reducerKey] = subReducer(subState, action);\n            hasChanged = hasChanged || nextState[reducerKey] !== subState;\n        })\n        return hasChanged ? nextState as TState : state;\n    };\n}\n"
  },
  {
    "path": "src/system-types.ts",
    "content": "import { Observable } from 'rxjs';\n\nexport type Dictionary<T> = Record<string, T>;\n\nexport type Id = string | number;\n\nexport interface LoadingState<E = string> {\n    byKeys?: Dictionary<LoadingState>;\n    isLoading: boolean;\n    error?: E;\n}\n\nexport interface RxStore<T = any> extends Observable<T> {\n    dispatch(action: { type: any }): void;\n\n    addReducer(\n        key: string,\n        reducer: any,\n    ): void;\n}\n\nexport interface AnyAction {\n    type: string;\n}\n\nexport interface PayloadAction<T = any> {\n    type: string;\n    payload: T;\n}\n\nexport type Reducer<S, A extends AnyAction> = (\n    state: S,\n    action: A,\n) => S;\n\nexport type CombinedReducers<TState> = {\n    [K in keyof TState]: Reducer<TState[K], any>;\n}\n\nexport type ActionCreator<TActionTypes, TActions> = (...args: any) => TActions;\n\nexport type HashFn = (ob: any) => string;\n\nexport type SameShaped<T, V> = {\n    [K in keyof T]: V\n}\n\ntype Key = string | number | symbol;\nexport type StrictDictionary<K extends Key,\n    V,\n    K2 extends Key = string,\n    > = Record<K, V> & Record<K2, V>;\n\nexport type DeepPartial<T> = {\n    [P in keyof T]?: T[P] extends Array<infer U>\n        ? Array<DeepPartial<U>>\n        : T[P] extends ReadonlyArray<infer U>\n            ? ReadonlyArray<DeepPartial<U>>\n            : DeepPartial<T[P]>\n};\n\n\n\n\n\n\n\n"
  },
  {
    "path": "src/testing/helpers.ts",
    "content": "import { cold } from 'jest-marbles';\nimport { TestStore } from './store';\nimport { combineReducers } from '../shared/utils';\nimport { PainlessRedux } from '../painless-redux/types';\nimport { EntityActionTypes } from '../entity/types';\nimport { createEntityActionCreators } from '../entity/action-creators';\nimport { Reducer } from '../system-types';\nimport { EntityActions } from '../entity/actions';\nimport { getHash } from '../entity/utils';\n\nexport const initStoreWithPr = (\n    store: TestStore<any>,\n    pr: PainlessRedux,\n) => {\n    const reducer = pr.getReducer();\n    const globalReducer = combineReducers({ [pr.name]: reducer });\n    const state = globalReducer({}, { type: 'any' });\n    store.setState(state);\n    return globalReducer;\n};\n\nexport const getOrderedMarbleStream = (...items: any) => {\n    const { marble, values } = items.reduce((\n        memo: any,\n        item: any,\n        index: number,\n    ) => {\n        const isFrames = typeof item === 'string';\n        const isArray = Array.isArray(item);\n        let marblePart: string = index.toString();\n        if (isFrames) {\n            marblePart = item;\n        }\n        if (isArray) {\n            marblePart = `(${item.map((\n                subItem: any,\n                subIndex: number,\n            ) => `${index}${subIndex}`)})`;\n        }\n        memo.marble += marblePart;\n        if (!isFrames) {\n            if (isArray) {\n                memo.values = item.reduce((\n                    subMemo: any,\n                    subItem: any,\n                    subIndex: number,\n                ) => {\n                    subMemo[`${index}${subIndex}`] = subItem;\n                    return subMemo;\n                }, memo.values);\n            } else {\n                memo.values[index] = item;\n            }\n        }\n        return memo;\n    }, { marble: '', values: {} });\n    return cold(marble, values);\n};\n\nexport const createTestHelpers = <T, TPageMetadata>(\n    reducerFactory: <T>(types: EntityActionTypes) => Reducer<any, EntityActions>,\n) => {\n    const types: EntityActionTypes = {\n        ADD: 'ADD',\n        RESOLVE_ADD: 'RESOLVE_ADD',\n        ADD_LIST: 'ADD_LIST',\n        SET_LOADING_STATE: 'SET_LOADING_STATE',\n        CHANGE: 'CHANGE',\n        RESOLVE_CHANGE: 'RESOLVE_CHANGE',\n        REMOVE: 'REMOVE',\n        RESOLVE_REMOVE: 'RESOLVE_REMOVE',\n        RESTORE_REMOVED: 'RESTORE_REMOVED',\n        REMOVE_LIST: 'REMOVE_LIST',\n        RESTORE_REMOVED_LIST: 'RESTORE_REMOVED_LIST',\n        RESOLVE_REMOVE_LIST: 'RESOLVE_REMOVE_LIST',\n        CLEAR: 'CLEAR',\n        CLEAR_ALL: 'CLEAR_ALL',\n        BATCH: 'BATCH',\n        SET_LOADING_STATES: 'SET_LOADING_STATES',\n        CHANGE_LIST: 'CHANGE_LIST',\n        RESOLVE_CHANGE_LIST: 'RESOLVE_CHANGE_LIST',\n    };\n\n    const actionCreators = createEntityActionCreators<T, TPageMetadata>(\n        types,\n        { hashFn: getHash, name: 'test', pageSize: 10, maxPagesCount: 10 },\n    );\n    const reducer = reducerFactory<T>(types);\n    return { actionCreators, reducer };\n};\n"
  },
  {
    "path": "src/testing/store.ts",
    "content": "import { BehaviorSubject, ReplaySubject } from 'rxjs';\r\nimport { AnyAction, Reducer, RxStore } from '../system-types';\r\nimport { combineReducers } from '../shared/utils';\r\n\r\nexport class TestStore<T = any> extends BehaviorSubject<T> implements RxStore<T> {\r\n    actions$: ReplaySubject<AnyAction> = new ReplaySubject();\r\n    state: T;\r\n\r\n    constructor(\r\n        private initialState: T,\r\n        private reducer: Reducer<T, AnyAction>,\r\n    ) {\r\n        super(initialState);\r\n        this.state = initialState;\r\n    }\r\n\r\n    setState(data: T) {\r\n        this.next(data);\r\n    }\r\n\r\n    dispatch(\r\n        action: AnyAction,\r\n    ) {\r\n        this.actions$.next(action);\r\n        this.performDispatch(action);\r\n    }\r\n\r\n    addReducer(\r\n        key: string,\r\n        reducer: any,\r\n    ) {\r\n        this.reducer = combineReducers<any, AnyAction>({ [key]: reducer });\r\n        this.performDispatch({ type: 'ADD_REDUCER' });\r\n    }\r\n\r\n    clear() {\r\n        this.performDispatch({ type: 'TEST_CLEAR' }, this.initialState);\r\n    }\r\n\r\n    private performDispatch(\r\n        action: AnyAction,\r\n        state = this.state,\r\n    ) {\r\n        this.state = this.reducer(state, action);\r\n        this.setState(this.state);\r\n    }\r\n}\r\n"
  },
  {
    "path": "src/utils.ts",
    "content": "import { MD5 } from 'object-hash';\nimport { capitalize, defaultsDeep, isNil, isObject, keyBy, lowerCase } from 'lodash-es';\nimport { Observable, OperatorFunction } from 'rxjs';\nimport { distinctUntilChanged, map, take } from 'rxjs/operators';\nimport { DeepPartial, Dictionary, Id } from './system-types';\n\nexport const capitalizeAll = (str: string | number | symbol): string =>\n    lowerCase(str.toString()).split(' ').map((part: string) => capitalize(part)).join(' ');\n\nexport const hashString = (value: string): string => MD5(value).toString();\n\nexport const hashIt = (value?: unknown): string => {\n    if (typeof value === 'string') return hashString(value);\n    return MD5(value ?? {});\n};\n\nexport const getHeadedActionName = (\n    header: string,\n    ...rest: string[]\n) => {\n    const actionString = capitalizeAll(rest.join(' '));\n    const typeString = capitalizeAll(header);\n    return `[${typeString}] ${actionString}`;\n};\n\nexport const createActionTypes = <T>(\n    actionTypeNames: (keyof T)[],\n    entityName: string,\n): T => {\n    return actionTypeNames.reduce((\n        memo: any,\n        actionName: keyof T,\n    ) => {\n        memo[actionName] = getHeadedActionName(entityName, actionName as string);\n        return memo;\n    }, {});\n};\n\nexport const merge = <T>(\n    src: T,\n    patch: DeepPartial<T>,\n): T => {\n    if (isNil(src)) return { ...patch } as T;\n    const newObject: T = { ...src };\n    for (const key in patch) {\n        const srcValue = src[key];\n        const patchValue = patch[key];\n        if (patchValue === undefined) {\n            delete newObject[key];\n        } else if (isObject(patchValue) && !Array.isArray(patchValue)) {\n            newObject[key] = merge(srcValue, patchValue as any);\n        } else {\n            (newObject[key] as unknown) = patchValue;\n        }\n    }\n    return newObject;\n};\n\nexport const typedDefaultsDeep = <T>(\n    obj: Partial<T> | undefined,\n    ...args: Partial<T>[]\n): Partial<T> => defaultsDeep({}, obj, ...args) as Partial<T>;\n\nexport const snapshot = <T>(obs$: Observable<T>): T | undefined => {\n    let value;\n    obs$.pipe(take(1)).subscribe((v) => value = v);\n    return value;\n};\n\nexport const select = <T, R>(\n    selector: (\n        value: T,\n        index: number,\n    ) => R,\n): OperatorFunction<T, R> => (\n    source: Observable<T>,\n): Observable<R> => source.pipe(\n    map<T, R>((\n        value: T,\n        index: number,\n    ) => selector(value, index)),\n    distinctUntilChanged(),\n);\n\nexport const toDictionary = <T>(\n    key: string = 'id',\n): OperatorFunction<T[] | undefined, Dictionary<T>> => (\n    source: Observable<T[] | undefined>,\n): Observable<Dictionary<T>> => source.pipe(\n    map((values: T[] | undefined) => keyBy(values, key)),\n);\n\nexport const removeFromArray = <T>(\n    array: T[],\n    itemsToRemove: T[],\n): T[] => array.filter((item) => !itemsToRemove.includes(item));\n\nexport const removeFromObject = <T>(\n    obj: Record<Id, T>,\n    keysToRemove: Id[],\n): Record<string, T> => keysToRemove.reduce((memo, key: Id) => {\n    delete memo[key];\n    return memo;\n}, { ...obj });\n"
  },
  {
    "path": "src/workspace/action-creators.ts",
    "content": "import { WorkspaceActionTypes } from './types';\nimport { createChange, createResolveChange } from './actions';\nimport { createSetLoadingState } from '../shared/loading-state/actions';\nimport { createBatch } from '../shared/system/actions';\n\nexport const createWorkspaceActionCreators = <T>(\n    actionTypes: WorkspaceActionTypes,\n) => ({\n    CHANGE: createChange<T>(actionTypes),\n    RESOLVE_CHANGE: createResolveChange<T>(actionTypes),\n    SET_LOADING_STATE: createSetLoadingState(actionTypes),\n    BATCH: createBatch(actionTypes),\n});\n\nexport type WorkspaceActionCreators = ReturnType<typeof createWorkspaceActionCreators>;\n"
  },
  {
    "path": "src/workspace/actions.ts",
    "content": "import * as changeActions from '../shared/change/actions';\nimport { LoadingStateActions } from '../shared/loading-state/actions';\nimport { ChangeOptions } from '../shared/change/types';\nimport { WorkspaceActionTypes } from './types';\nimport { DeepPartial } from '../system-types';\nimport { SystemActions } from '../shared/system/actions';\n\nexport const createChange = <T>(types: WorkspaceActionTypes) => (\n    patch: DeepPartial<T>,\n    label: string,\n    changeId?: string,\n    options?: ChangeOptions,\n) => {\n    const actionCreator = changeActions.createChange(types);\n    const action = actionCreator(patch, changeId, options);\n    return { ...action, label } as const;\n};\n\nexport const createResolveChange = <T>(types: WorkspaceActionTypes) => (\n    label: string,\n    changeId: string,\n    success: boolean,\n    remotePatch?: DeepPartial<T>,\n    options?: ChangeOptions,\n) => {\n    const actionCreator = changeActions.createResolveChange(types);\n    const action = actionCreator(changeId, success, remotePatch, options);\n    return { ...action, label } as const;\n};\n\ntype SelfActions = ReturnType<typeof createChange>\n    | ReturnType<typeof createResolveChange>;\n\nexport type WorkspaceChangeAction = ReturnType<SelfActions>\n\nexport type WorkspaceActions = ReturnType<SelfActions> | LoadingStateActions | SystemActions\n\n"
  },
  {
    "path": "src/workspace/constants.ts",
    "content": "import { WorkspaceActionTypes } from './types';\n\nexport const WORKSPACE_TYPE_NAMES: (keyof WorkspaceActionTypes)[] = [\n    'CHANGE',\n    'SET_LOADING_STATE',\n    'RESOLVE_CHANGE',\n    'BATCH',\n];\n"
  },
  {
    "path": "src/workspace/methods/dispatch/dispatch.ts",
    "content": "import { Dispatcher } from '../../../dispatcher/types';\nimport { WorkspaceActions } from '../../actions';\nimport { WorkspaceActionTypes } from '../../types';\nimport { DispatchWorkspaceMethods } from './types';\nimport { DeepPartial, Id, LoadingState } from '../../../system-types';\nimport { SelectWorkspaceMethods } from '../select/types';\nimport { getHeadedActionName } from '../../../utils';\nimport { ChangeOptions, PatchRequest } from '../../../shared/change/types';\nimport { LoadingStateSetOptions } from '../../../shared/loading-state/types';\nimport { normalizePatch } from '../../../shared/change/utils';\n\nexport const createDispatchWorkspaceMethods = <T>(\n    dispatcher: Dispatcher<WorkspaceActionTypes, WorkspaceActions>,\n    selectMethods: SelectWorkspaceMethods<T>,\n    name: string,\n): DispatchWorkspaceMethods<T> => {\n\n    const changeWithId = (\n        patch: PatchRequest<T>,\n        label: string,\n        changeId?: string,\n        options?: ChangeOptions,\n    ) => {\n        const normalizedPatch = normalizePatch(patch, selectMethods.get$());\n        label = getHeadedActionName(name, label);\n        return dispatcher.createAndDispatch('CHANGE', [normalizedPatch, label, changeId, options]);\n    };\n\n    const resolveChange = (\n        label: string,\n        changeId: Id,\n        success: boolean,\n        remotePatch?: DeepPartial<T>,\n        options?: ChangeOptions,\n    ) => {\n        return dispatcher.createAndDispatch('RESOLVE_CHANGE', [label, changeId, success, remotePatch], options);\n    };\n\n    const change = (\n        patch: PatchRequest<T>,\n        label: string,\n        options?: ChangeOptions,\n    ) => {\n        return changeWithId(patch, label, undefined, options);\n    };\n\n    const setLoadingState = (\n        state: LoadingState,\n        key?: string,\n        options?: LoadingStateSetOptions,\n    ) => {\n        return dispatcher.createAndDispatch('SET_LOADING_STATE', [state, key], options);\n    };\n\n    const batch = (\n        actions: WorkspaceActions[],\n    ) => {\n        return dispatcher.createAndDispatch('BATCH', [actions]);\n    };\n\n    return { changeWithId, setLoadingState: setLoadingState, change, resolveChange, batch };\n};\n"
  },
  {
    "path": "src/workspace/methods/dispatch/types.ts",
    "content": "import { DeepPartial, Id, LoadingState } from '../../../system-types';\nimport { ChangeOptions, PatchRequest } from '../../../shared/change/types';\nimport { WorkspaceActions } from '../../actions';\nimport { LoadingStateSetOptions } from '../../../shared/loading-state/types';\n\nexport interface DispatchWorkspaceMethods<T> {\n    change: (\n        patch: PatchRequest<T>,\n        label: string,\n        options?: ChangeOptions,\n    ) => WorkspaceActions;\n    changeWithId: (\n        patch: PatchRequest<T>,\n        label: string,\n        changeId?: string,\n        options?: ChangeOptions,\n    ) => WorkspaceActions;\n    resolveChange: (\n        label: string,\n        changeId: Id,\n        success: boolean,\n        remotePatch?: DeepPartial<T>,\n        options?: ChangeOptions,\n    ) => WorkspaceActions;\n    setLoadingState: (\n        state: LoadingState,\n        key?: string,\n        options?: LoadingStateSetOptions,\n    ) => WorkspaceActions;\n    batch: (\n        actions: WorkspaceActions[],\n    ) => WorkspaceActions;\n}\n"
  },
  {
    "path": "src/workspace/methods/mixed/mixed.ts",
    "content": "import { DeepPartial, LoadingState } from '../../../system-types';\nimport { Observable } from 'rxjs';\nimport { ChangeOptions, PatchRequest } from '../../../shared/change/types';\nimport { v4 } from 'uuid';\nimport { getPatchByOptions, getResolvePatchByOptions, normalizePatch } from '../../../shared/change/utils';\nimport { PainlessReduxSchema } from '../../../painless-redux/types';\nimport { getRemotePipe, guardIfLoading } from '../../../shared/utils';\nimport { SelectWorkspaceMethods } from '../select/types';\nimport { DispatchWorkspaceMethods } from '../dispatch/types';\nimport { MixedWorkspaceMethods } from './types';\nimport { typedDefaultsDeep } from '../../../utils';\n\nexport const createWorkspaceMixedMethods = <T>(\n    prSchema: PainlessReduxSchema,\n    selectMethods: SelectWorkspaceMethods<T>,\n    dispatchMethods: DispatchWorkspaceMethods<T>,\n): MixedWorkspaceMethods<T> => {\n\n    const changeRemote$ = (\n        patch: PatchRequest<T>,\n        dataSource$: Observable<DeepPartial<T> | undefined>,\n        label: string,\n        options?: ChangeOptions,\n    ): Observable<DeepPartial<T>> => {\n        options = typedDefaultsDeep(options, { rethrow: true });\n        const changeId = v4();\n        const { changeWithId, resolveChange, setLoadingState } = dispatchMethods;\n        const { getLoadingState$, get$ } = selectMethods;\n        const normalizedPatch = normalizePatch(patch, get$());\n\n        const sourcePipe = getRemotePipe<LoadingState | undefined, unknown, DeepPartial<T> | undefined, DeepPartial<T>>({\n            options,\n            remoteObsOrFactory: dataSource$,\n            success: (\n                response?: DeepPartial<T>,\n            ) => {\n                const patchToApply = getPatchByOptions(normalizedPatch, response, options) ?? {};\n                return changeWithId(patchToApply, label, changeId, options);\n            },\n            emitOnSuccess: true,\n            optimistic: options?.optimistic,\n            optimisticResolve: (\n                success: boolean,\n                response?: DeepPartial<T>,\n            ) => {\n                const patchToApply = getResolvePatchByOptions(normalizedPatch, response, options);\n                return resolveChange(label, changeId, success, patchToApply, options);\n            },\n            setLoadingState: (state) => setLoadingState(state),\n        });\n        const loadingState$ = getLoadingState$(prSchema.useAsapSchedulerInLoadingGuards);\n        return guardIfLoading(loadingState$).pipe(sourcePipe);\n    };\n\n    return { changeRemote$ };\n\n};\n"
  },
  {
    "path": "src/workspace/methods/mixed/types.ts",
    "content": "import { DeepPartial } from '../../../system-types';\nimport { ChangeOptions, PatchRequest } from '../../../shared/change/types';\nimport { Observable } from 'rxjs';\n\nexport interface MixedWorkspaceMethods<T> {\n    changeRemote$: (\n        patch: PatchRequest<T>,\n        dataSource$: Observable<DeepPartial<T> | undefined>,\n        label: string,\n        options?: ChangeOptions,\n    ) => Observable<DeepPartial<T>>;\n}\n"
  },
  {
    "path": "src/workspace/methods/select/select.ts",
    "content": "import { SelectManager } from '../../../select-manager/types';\nimport { WorkspaceSelectors } from '../../types';\nimport { Observable } from 'rxjs';\nimport { SelectWorkspaceMethods } from './types';\nimport { LoadingState } from '../../../system-types';\n\nexport const createSelectWorkspaceMethods = <T>(\n    { select$ }: SelectManager,\n    selectors: WorkspaceSelectors<T>,\n): SelectWorkspaceMethods<T> => {\n\n    const get$ = (): Observable<T | undefined> => {\n        const selector = selectors.actual;\n        return select$(selector);\n    };\n\n    const getLoadingState$ = (isAsap?: boolean): Observable<LoadingState | undefined> => {\n        const selector = selectors.loadingState;\n        return select$(selector, isAsap);\n    };\n\n    // const getByMap$ = <M extends BooleanMap<Partial<T>>>(\n    //     selectMap?: M,\n    // ): Observable<SelectResult<Partial<T>, M>> | Observable<Partial<T>> => {\n    //     const value$ = get$();\n    //     if (!selectMap) return value$;\n    //     return value$.pipe(\n    //         map((value: Partial<T>) => maskObject(value, selectMap)),\n    //     );\n    // };\n\n    return {\n        get$,\n        getLoadingState$,\n        // getByMap$,\n    };\n};\n"
  },
  {
    "path": "src/workspace/methods/select/types.ts",
    "content": "import { Observable } from 'rxjs';\nimport { LoadingState } from '../../../system-types';\n\nexport interface SelectWorkspaceMethods<T> {\n    get$: () => Observable<T | undefined>;\n    getLoadingState$: (isAsap?: boolean) => Observable<LoadingState | undefined>;\n    // getByMap$: <M extends BooleanMap<Partial<T>>>(\n    //     selectMap?: M\n    // ) => Observable<SelectResult<Partial<T>, M>> | Observable<Partial<T>>;\n}\n"
  },
  {
    "path": "src/workspace/reducer.ts",
    "content": "import { WorkspaceActionTypes, WorkspaceState } from './types';\nimport { Reducer } from '../system-types';\nimport { combineReducers } from '../shared/utils';\nimport { WorkspaceActions } from './actions';\nimport { createLoadingStateReducer } from '../shared/loading-state/reducers';\nimport { createChangeReducer } from '../shared/change/reducer';\nimport { batchActionsReducerFactory } from '../shared/system/reducers';\n\nexport const createBaseReducer = <T>(\n    actionTypes: WorkspaceActionTypes,\n    initialValue?: Partial<T>,\n): Reducer<WorkspaceState<T>, WorkspaceActions> => combineReducers<WorkspaceState<T>, WorkspaceActions>({\n    value: createChangeReducer<T>(actionTypes, initialValue as T),\n    loadingState: createLoadingStateReducer(actionTypes),\n});\n\nexport const createWorkspaceReducer = <T>(\n    actionTypes: WorkspaceActionTypes,\n    initialValue?: Partial<T>,\n): Reducer<WorkspaceState<T>, WorkspaceActions> => {\n    const baseReducer = createBaseReducer<T>(actionTypes, initialValue);\n    return batchActionsReducerFactory(actionTypes, baseReducer);\n};\n"
  },
  {
    "path": "src/workspace/selectors.ts",
    "content": "import { createSelector, Selector } from 'reselect';\nimport { PainlessReduxState } from '../painless-redux/types';\nimport { WorkspaceSelectors, WorkspaceState } from './types';\nimport { createLoadingStateSelector } from '../shared/loading-state/selectors';\nimport { getChangeableActual } from '../shared/change/selectors';\n\nexport const createWorkspaceValueSelector = <T>(\n    selector: Selector<PainlessReduxState, WorkspaceState<T>>,\n) => createSelector(\n    selector,\n    (workspace: WorkspaceState<T>) => getChangeableActual(workspace.value),\n);\n\nexport const createWorkspaceSelectors = <T>(\n    selector: Selector<PainlessReduxState, WorkspaceState<T>>,\n): WorkspaceSelectors<T> => {\n    const actual = createWorkspaceValueSelector(selector);\n    const loadingState = createLoadingStateSelector<WorkspaceState<T>>(selector);\n\n    return {\n        actual,\n        loadingState,\n    };\n};\n"
  },
  {
    "path": "src/workspace/types.ts",
    "content": "import { ChangeableState } from '../shared/change/types';\nimport { LoadingStateActionTypes, LoadingStateSelectors, LoadingStateState } from '../shared/loading-state/types';\nimport { Selector } from 'reselect';\nimport { SelectWorkspaceMethods } from './methods/select/types';\nimport { DispatchWorkspaceMethods } from './methods/dispatch/types';\nimport { WorkspaceActionCreators } from './action-creators';\nimport { MixedWorkspaceMethods } from './methods/mixed/types';\nimport { SystemActionTypes } from '../shared/system/types';\n\nexport type PublicDispatchWorkspaceMethods<T> = Omit<DispatchWorkspaceMethods<T>, 'changeWithId' | 'resolveChange'>\n\nexport interface Workspace<T> extends PublicDispatchWorkspaceMethods<T>, SelectWorkspaceMethods<T>, MixedWorkspaceMethods<T> {\n    actionCreators: WorkspaceActionCreators;\n}\n\nexport interface WorkspaceState<T> extends LoadingStateState {\n    value: ChangeableState<T> | undefined;\n}\n\nexport interface WorkspaceSchema<T> {\n    name: string;\n    initialValue?: T;\n}\n\nexport interface WorkspaceActionTypes extends SystemActionTypes {\n    SET_LOADING_STATE: LoadingStateActionTypes['SET_LOADING_STATE'];\n    CHANGE: 'CHANGE';\n    RESOLVE_CHANGE: 'RESOLVE_CHANGE';\n}\n\nexport type ValueSelector<T> = Selector<WorkspaceState<T>, T | undefined>;\n\nexport interface WorkspaceSelectors<T> extends LoadingStateSelectors<WorkspaceState<T>> {\n    actual: ValueSelector<T>;\n}\n\nexport type BooleanMap<T> = {\n    [K in keyof T]?: T[K] extends object\n        ? boolean | BooleanMap<T[K]>\n        : boolean\n};\nexport type SelectValue<T, M> = M extends boolean ? T : SelectResult<T, M>;\nexport type SelectResult<T, M extends BooleanMap<T>> = { [K in (keyof M & keyof T)]: SelectValue<T[K], M[K]> };\n"
  },
  {
    "path": "src/workspace/utils.ts",
    "content": "import { createActionTypes, typedDefaultsDeep } from '../utils';\nimport { WORKSPACE_TYPE_NAMES } from './constants';\nimport { WorkspaceActionTypes, WorkspaceSchema } from './types';\nimport { WorkspaceChangeAction } from './actions';\n\nexport const getFullWorkspaceSchema = <T>(\n    schema?: Partial<WorkspaceSchema<T>>,\n): WorkspaceSchema<T> => typedDefaultsDeep(schema, {\n    initialValue: undefined,\n}) as WorkspaceSchema<T>;\n\nexport const createWorkspaceActionTypes = (workspaceName: string): WorkspaceActionTypes => {\n    return createActionTypes(WORKSPACE_TYPE_NAMES, workspaceName);\n};\n\n// export const maskObject = <T extends any, M extends any>(obj: T, mask: M): SelectResult<T, M> => {\n//     if (isNil(obj || isNil(mask))) return obj as unknown as SelectResult<T, M>;\n//     return Object.keys(obj).reduce((memo: any, key: string) => {\n//         let value = obj[key];\n//         const maskValue = mask[key];\n//         if (!maskValue) return memo;\n//         if (typeof maskValue === 'object') {\n//             value = maskObject(value, maskValue);\n//         }\n//         memo[key] = value;\n//         return memo;\n//     }, {}) as SelectResult<T, M>;\n// };\n\nexport const actionSanitizer = (action: WorkspaceChangeAction) => ({\n    ...action,\n    _type: action.type,\n    type: action.label || action.type,\n});\n"
  },
  {
    "path": "src/workspace/workspace.spec.ts",
    "content": "import 'jest';\nimport { cold } from 'jest-marbles';\nimport { Workspace } from './types';\nimport { TestStore } from '../testing/store';\nimport { createPainlessRedux } from '../painless-redux/painless-redux';\nimport { createWorkspace } from '../workspace/workspace';\nimport { PainlessRedux } from '../painless-redux/types';\nimport { initStoreWithPr } from '../testing/helpers';\n\ninterface TestWorkspace {\n    fill?: boolean;\n    color?: {\n        red?: number;\n        green?: number;\n        blue?: number;\n    };\n    values?: number[];\n}\n\ndescribe('Workspace', () => {\n    let pr: PainlessRedux;\n    let workspace: Workspace<TestWorkspace>;\n    let store: TestStore<any>;\n    let initialValue: TestWorkspace;\n    let noLabel: string;\n\n    beforeEach(() => {\n        store = new TestStore(undefined, (state) => state);\n        pr = createPainlessRedux(store, { useAsapSchedulerInLoadingGuards: false });\n        initialValue = {\n            fill: true,\n            color: { red: 0, green: 0, blue: 0 },\n            values: [1, 2, 3],\n        };\n        workspace = createWorkspace(pr, { name: 'test', initialValue });\n        noLabel = '[Test] ';\n        initStoreWithPr(store, pr);\n    });\n\n    describe('#get$', () => {\n        test('should return observable with origin value by empty selector', () => {\n            // arrange\n            const expected$ = cold('a', { a: initialValue });\n            // act\n            const actual = workspace.get$();\n            // assert\n            expect(actual).toBeObservable(expected$);\n        });\n    });\n\n    describe('#getLoadingState$', () => {\n        test('should return observable with loadingState', () => {\n            // act\n            const actual = workspace.getLoadingState$();\n            // assert\n            const expected$ = cold('a', { a: { isLoading: false } });\n            expect(actual).toBeObservable(expected$);\n        });\n    });\n\n    xdescribe('#getByMap$', () => {\n        test('should return observable to workspace', () => {\n            // arrange\n            // const expected$ = cold('a', { a: initialValue });\n            // // act\n            // const actual = workspace.getByMap$();\n            // // assert\n            // expect(actual).toBeObservable(expected$);\n        });\n\n        test('should return observable to masked workspace', () => {\n            // arrange\n            // const expected$ = cold('a', {\n            //     a: { color: { red: initialValue.color?.red } },\n            // });\n            // // act\n            // const actual = workspace.getByMap$({ color: { red: true } });\n            // // assert\n            // expect(actual).toBeObservable(expected$);\n        });\n    });\n\n    describe('#change', () => {\n        test('should change workspace value', () => {\n            // arrange\n            const patch = { color: { blue: 255 } };\n            const action = workspace.actionCreators.CHANGE(patch, noLabel);\n            const expected$ = cold('a', { a: action });\n            // act\n            workspace.change(patch, '');\n            // assert\n            expect(store.actions$).toBeObservable(expected$);\n        });\n\n        test('should change workspace value based on previous', () => {\n            // arrange\n            const action = workspace.actionCreators.CHANGE({ color: { blue: 1 } }, noLabel);\n            const expected$ = cold('a', {\n                a: action,\n            });\n            // act\n            workspace.change((previous) => ({\n                color: { blue: (previous?.color?.blue ?? 0) + 1 },\n            }), '');\n            // assert\n            expect(store.actions$).toBeObservable(expected$);\n        });\n\n        test('should set upper cased label for action', () => {\n            // arrange\n            const patch = {};\n            const action = workspace.actionCreators.CHANGE(patch, `[Test] Some Label`);\n            const expected$ = cold('a', { a: action });\n            // act\n            workspace.change(patch, 'some label');\n            // assert\n            expect(store.actions$).toBeObservable(expected$);\n        });\n    });\n\n});\n"
  },
  {
    "path": "src/workspace/workspace.ts",
    "content": "import { PainlessRedux, SlotTypes } from '../painless-redux/types';\nimport { Workspace, WorkspaceActionTypes, WorkspaceSchema, WorkspaceState } from './types';\nimport { WorkspaceActions } from './actions';\nimport { createWorkspaceActionTypes, getFullWorkspaceSchema } from './utils';\nimport { createWorkspaceActionCreators } from './action-creators';\nimport { createWorkspaceReducer } from './reducer';\nimport { createWorkspaceSelectors } from './selectors';\nimport { createSelectWorkspaceMethods } from './methods/select/select';\nimport { createDispatchWorkspaceMethods } from './methods/dispatch/dispatch';\nimport { createWorkspaceMixedMethods } from './methods/mixed/mixed';\n\nexport const createWorkspace = <T>(\n    pr: PainlessRedux,\n    schema?: Partial<WorkspaceSchema<T>>,\n): Workspace<T> => {\n    const fullSchema = getFullWorkspaceSchema<T>(schema);\n    const name = fullSchema.name;\n    const actionTypes = createWorkspaceActionTypes(name);\n    const actionCreators = createWorkspaceActionCreators<T>(actionTypes);\n    const reducer = createWorkspaceReducer<T>(actionTypes, fullSchema.initialValue);\n    const {\n        selector,\n        dispatcher,\n        selectManager,\n    } = pr.registerSlot<WorkspaceState<T>, WorkspaceActionTypes, WorkspaceActions>(\n        SlotTypes.Workspace,\n        name,\n        reducer,\n        actionCreators,\n    );\n    const selectors = createWorkspaceSelectors<T>(selector);\n\n    const selectMethods = createSelectWorkspaceMethods<T>(selectManager, selectors);\n    const dispatchMethods = createDispatchWorkspaceMethods<T>(dispatcher, selectMethods, name);\n    const mixedMethods = createWorkspaceMixedMethods<T>(pr.schema, selectMethods, dispatchMethods);\n\n    const { changeWithId, resolveChange, ...publicDispatchMethods } = dispatchMethods;\n\n    return {\n        ...publicDispatchMethods,\n        ...selectMethods,\n        ...mixedMethods,\n        actionCreators,\n    };\n};\n"
  },
  {
    "path": "tsconfig.json",
    "content": "{\r\n  \"compilerOptions\": {\r\n    \"target\": \"ES2015\",\r\n    \"module\": \"ES2020\",\r\n    \"declaration\": true,\r\n    \"outDir\": \"./dist\",\r\n    \"strict\": true,\r\n    \"allowSyntheticDefaultImports\": true,\r\n    \"moduleResolution\": \"Node\",\r\n    \"esModuleInterop\": true,\r\n    \"lib\": [\r\n      \"dom\",\r\n      \"es2015\",\r\n      \"es2016.array.include\"\r\n    ]\r\n  },\r\n  \"exclude\": [\r\n    \"node_modules\",\r\n    \"**/*.spec.*\"\r\n  ]\r\n}\r\n"
  }
]