[
  {
    "path": ".gitignore",
    "content": "/node_modules\r\n/package-lock.json\r\n/lib"
  },
  {
    "path": ".prettierrc",
    "content": "{\n  \"bracketSpacing\": false,\n  \"trailingComma\": \"all\",\n  \"tabWidth\": 4\n}\n"
  },
  {
    "path": ".travis.yml",
    "content": "language: node_js\nnode_js:\n- 10\n- 8\nscript: npm test\n"
  },
  {
    "path": ".vscode/settings.json",
    "content": "{\n    \"typescript.tsdk\": \"node_modules/typescript/lib\"\n}"
  },
  {
    "path": "CHANGELOG.md",
    "content": "See <https://github.com/epeli/immer-reducer/releases>\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2018 Esa-Matti Suuronen\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": "# immer-reducer\n\nType-safe and terse reducers with Typescript for React Hooks and Redux using [Immer](https://immerjs.github.io/immer/)!\n\n## 📦 Install\n\n    npm install immer-reducer\n\nYou can also install [eslint-plugin-immer-reducer](https://github.com/skoshy/eslint-plugin-immer-reducer) to help you avoid errors when writing your reducer.\n\n## 💪 Motivation\n\nTurn this 💩 💩 💩\n\n```ts\ninterface SetFirstNameAction {\n    type: \"SET_FIRST_NAME\";\n    firstName: string;\n}\n\ninterface SetLastNameAction {\n    type: \"SET_LAST_NAME\";\n    lastName: string;\n}\n\ntype Action = SetFirstNameAction | SetLastNameAction;\n\nfunction reducer(action: Action, state: State): State {\n    switch (action.type) {\n        case \"SET_FIRST_NAME\":\n            return {\n                ...state,\n                user: {\n                    ...state.user,\n                    firstName: action.firstName,\n                },\n            };\n        case \"SET_LAST_NAME\":\n            return {\n                ...state,\n                user: {\n                    ...state.user,\n                    lastName: action.lastName,\n                },\n            };\n        default:\n            return state;\n    }\n}\n```\n\n✨✨ Into this! ✨✨\n\n```ts\nimport {ImmerReducer} from \"immer-reducer\";\n\nclass MyImmerReducer extends ImmerReducer<State> {\n    setFirstName(firstName: string) {\n        this.draftState.user.firstName = firstName;\n    }\n\n    setLastName(lastName: string) {\n        this.draftState.user.lastName = lastName;\n    }\n}\n```\n\n🔥🔥 **Without losing type-safety!** 🔥🔥\n\nOh, and you get the action creators for free! 🤗 🎂\n\n## 📖 Usage\n\nGenerate Action Creators and the actual reducer function for Redux from the class with\n\n```ts\nimport {createStore} from \"redux\";\nimport {createActionCreators, createReducerFunction} from \"immer-reducer\";\n\nconst initialState: State = {\n    user: {\n        firstName: \"\",\n        lastName: \"\",\n    },\n};\n\nconst ActionCreators = createActionCreators(MyImmerReducer);\nconst reducerFunction = createReducerFunction(MyImmerReducer, initialState);\n\nconst store = createStore(reducerFunction);\n```\n\nDispatch some actions\n\n```ts\nstore.dispatch(ActionCreators.setFirstName(\"Charlie\"));\nstore.dispatch(ActionCreators.setLastName(\"Brown\"));\n\nexpect(store.getState().user.firstName).toEqual(\"Charlie\");\nexpect(store.getState().user.lastName).toEqual(\"Brown\");\n```\n\n## 🌟 Typed Action Creators!\n\nThe generated `ActionCreator` object respect the types used in the class\n\n```ts\nconst action = ActionCreators.setFirstName(\"Charlie\");\naction.payload; // Has the type of string\n\nActionCreators.setFirstName(1); // Type error. Needs string.\nActionCreators.setWAT(\"Charlie\"); // Type error. Unknown method\n```\n\nIf the reducer class where to have a method which takes more than one argument\nthe payload would be array of the arguments\n\n```ts\n// In the Reducer class:\n// setName(firstName: string, lastName: string) {}\nconst action = ActionCreators.setName(\"Charlie\", \"Brown\");\naction.payload; // will have value [\"Charlie\", \"Brown\"] and type [string, string]\n```\n\nThe reducer function is also typed properly\n\n```ts\nconst reducer = createReducerFunction(MyImmerReducer);\n\nreducer(initialState, ActionCreators.setFirstName(\"Charlie\")); // OK\nreducer(initialState, {type: \"WAT\"}); // Type error\nreducer({wat: \"bad state\"}, ActionCreators.setFirstName(\"Charlie\")); // Type error\n```\n\n## ⚓ React Hooks\n\nBecause the `useReducer()` API in React Hooks is the same as with Redux\nReducers immer-reducer can be used with as is.\n\n```tsx\nconst initialState = {message: \"\"};\n\nclass ReducerClass extends ImmerReducer<typeof initialState> {\n    setMessage(message: string) {\n        this.draftState.message = message;\n    }\n}\n\nconst ActionCreators = createActionCreators(ReducerClass);\nconst reducerFunction = createReducerFunction(ReducerClass);\n\nfunction Hello() {\n    const [state, dispatch] = React.useReducer(reducerFunction, initialState);\n\n    return (\n        <button\n            data-testid=\"button\"\n            onClick={() => {\n                dispatch(ActionCreators.setMessage(\"Hello!\"));\n            }}\n        >\n            {state.message}\n        </button>\n    );\n}\n```\n\nThe returned state and dispatch functions will be typed as you would expect.\n\n## 🤔 How\n\nUnder the hood the class is deconstructed to following actions:\n\n```js\n{\n    type: \"IMMER_REDUCER:MyImmerReducer#setFirstName\",\n    payload: \"Charlie\",\n}\n{\n    type: \"IMMER_REDUCER:MyImmerReducer#setLastName\",\n    payload: \"Brown\",\n}\n{\n    type: \"IMMER_REDUCER:MyImmerReducer#setName\",\n    payload: [\"Charlie\", \"Brown\"],\n    args: true\n}\n```\n\nSo the class and method names become the Redux Action Types and the method\narguments become the action payloads. The reducer function will then match\nthese actions against the class and calls the appropriate methods with the\npayload array spread to the arguments.\n\n🚫 The format of the `action.type` string is internal to immer-reducer. If\nyou need to detect the actions use the provided type guards.\n\nThe generated reducer function executes the methods inside the `produce()`\nfunction of Immer enabling the terse mutatable style updates.\n\n## 🔄 Integrating with the Redux ecosystem\n\nTo integrate for example with the side effects libraries such as\n[redux-observable](https://github.com/redux-observable/redux-observable/) and\n[redux-saga](https://github.com/redux-saga/redux-saga), you can access the\ngenerated action type using the `type` property of the action creator\nfunction.\n\nWith redux-observable\n\n```ts\n// Get the action name to subscribe to\nconst setFirstNameActionTypeName = ActionCreators.setFirstName.type;\n\n// Get the action type to have a type safe Epic\ntype SetFirstNameAction = ReturnType<typeof ActionCreators.setFirstName>;\n\nconst setFirstNameEpic: Epic<SetFirstNameAction> = action$ =>\n  action$\n    .ofType(setFirstNameActionTypeName)\n    .pipe(\n      // action.payload - recognized as string\n      map(action => action.payload.toUpperCase()),\n      ...\n    );\n```\n\nWith redux-saga\n\n```ts\nfunction* watchFirstNameChanges() {\n    yield takeEvery(ActionCreators.setFirstName.type, doStuff);\n}\n\n// or use the isActionFrom() to get all actions from a specific ImmerReducer\n// action creators object\nfunction* watchImmerActions() {\n    yield takeEvery(\n        (action: Action) => isActionFrom(action, MyImmerReducer),\n        handleImmerReducerAction,\n    );\n}\n\nfunction* handleImmerReducerAction(action: Actions<typeof MyImmerReducer>) {\n    // `action` is a union of action types\n    if (isAction(action, ActionCreators.setFirstName)) {\n        // with action of setFirstName\n    }\n}\n```\n\n**Warning:** Due to how immer-reducers action generation works, adding default\nparameters to the methods will NOT pass it to the action payload, which can\nmake your reducer impure and the values will not be available in middlewares.\n\n```ts\nclass MyImmerReducer extends ImmerReducer<State> {\n    addItem (id: string = uuid()) {\n        this.draftState.ids.push([id])\n    }\n}\n\nimmerActions.addItem() // generates empty payload { payload: [] }\n```\n\nAs a workaround, create custom action creator wrappers that pass the default parameters instead.\n\n```ts\nclass MyImmerReducer extends ImmerReducer<State> {\n    addItem (id) {\n        this.draftState.ids.push([id])\n    }\n}\n\nconst actions = {\n  addItem: () => immerActions.addItem(id)\n}\n```\n\nIt is also recommended to install the ESLint plugin in the \"Install\" section\nto alert you if you accidentally encounter this issue.\n\n## 📚 Examples\n\nHere's a more complete example with redux-saga and [redux-render-prop](https://github.com/epeli/redux-render-prop):\n\n<https://github.com/epeli/typescript-redux-todoapp>\n\n## 🃏 Tips and Tricks\n\nYou can replace the whole `draftState` with a new state if you'd like. This could be useful if you'd like to reset back to your initial state.\n\n```ts\nimport {ImmerReducer} from \"immer-reducer\";\n\nconst initialState: State = {\n    user: {\n        firstName: \"\",\n        lastName: \"\",\n    },\n};\n\nclass MyImmerReducer extends ImmerReducer<State> {\n    // omitting other reducer methods\n    \n    reset() {\n        this.draftState = initialState;\n    }\n}\n```\n\n## 📓 Helpers\n\nThe module exports following helpers\n\n### `function isActionFrom(action, ReducerClass)`\n\nType guard for detecting whether the given action is generated by the given\nreducer class. The detected type will be union of actions the class\ngenerates.\n\nExample\n\n```ts\nif (isActionFrom(someAction, ActionCreators)) {\n    // someAction now has type of\n    // {\n    //     type: \"setFirstName\";\n    //     payload: string;\n    // } | {\n    //     type: \"setLastName\";\n    //     payload: string;\n    // };\n}\n```\n\n### `function isAction(action, actionCreator)`\n\nType guard for detecting specific actions generated by immer-reducer.\n\nExample\n\n```ts\nif (isAction(someAction, ActionCreators.setFirstName)) {\n    someAction.payload; // Type checks to `string`\n}\n```\n\n### `type Actions<ImmerReducerClass>`\n\nGet union of the action types generated by the ImmerReducer class\n\nExample\n\n```ts\ntype MyActions = Actions<typeof MyImmerReducer>;\n\n// Is the same as\ntype MyActions =\n    | {\n          type: \"setFirstName\";\n          payload: string;\n      }\n    | {\n          type: \"setLastName\";\n          payload: string;\n      };\n```\n\n### `function setPrefix(prefix: string)`\n\nThe default prefix in the generated action types is `IMMER_REDUCER`. Call\nthis customize it for your app.\n\nExample\n\n```ts\nsetPrefix(\"MY_APP\");\n```\n\n### `function composeReducers<State>(...reducers)`\n\nUtility that reduces actions by applying them through multiple reducers.\nThis helps in allowing you to split up your reducer logic to multiple `ImmerReducer`s\nif they affect the same part of your state\n\nExample\n\n```ts\nclass MyNameReducer extends ImmerReducer<NamesState> {\n    setFirstName(firstName: string) {\n        this.draftState.firstName = firstName;\n    }\n\n    setLastName(lastName: string) {\n        this.draftState.lastName = lastName;\n    }\n}\n\nclass MyAgeReducer extends ImmerReducer<AgeState> {\n    setAge(age: number) {\n        this.draftState.age = 8;\n    }\n}\n\nexport const reducer = composeReducers(\n  createReducerFunction(MyNameReducer, initialState),\n  createReducerFunction(MyAgeReducer, initialState)\n)\n```\n"
  },
  {
    "path": "__dtslint__/generic-reducers.dtslint.ts",
    "content": "import {\r\n    ImmerReducer,\r\n    createReducerFunction,\r\n    createActionCreators,\r\n    ImmerReducerState,\r\n} from \"../src/immer-reducer\";\r\n\r\ninterface AssignFail {\r\n    ___: \"it should not be possible to assign to me\";\r\n}\r\n\r\ninterface State {\r\n    foo: {\r\n        fooField1: string;\r\n        fooField2: number;\r\n    };\r\n\r\n    bar: {\r\n        barField1: number[];\r\n        barField2: RegExp;\r\n    };\r\n}\r\n\r\nconst initialState: State = {\r\n    foo: {\r\n        fooField1: \"a\",\r\n        fooField2: 1,\r\n    },\r\n    bar: {\r\n        barField1: [1, 2],\r\n        barField2: /re/,\r\n    },\r\n};\r\n\r\nfunction createGenericReducer<T extends {[key: string]: unknown}>() {\r\n    return class GenericReducer extends ImmerReducer<T> {\r\n        set(part: Partial<T>) {\r\n            Object.assign(this.draftState, part);\r\n        }\r\n    };\r\n}\r\nconst ReducerClassFoo = createGenericReducer<State[\"foo\"]>();\r\nconst ReducerClassBar = createGenericReducer<State[\"bar\"]>();\r\n\r\n////////////////////\r\n// Instance tests //\r\n////////////////////\r\n\r\nconst ins = new ReducerClassFoo(initialState.foo, initialState.foo);\r\n\r\nconst state_test_1: State[\"foo\"] = ins.state;\r\nconst state_test_2: State[\"foo\"] = ins.draftState;\r\n\r\n// cannot assign to wrong state (ie. was not any)\r\n// $ExpectError\r\nconst state_test_3: AssignFail = ins.state;\r\n// $ExpectError\r\nconst state_test_4: AssignFail = ins.draftState;\r\n\r\n//////////////////////////\r\n// Action Creator tests //\r\n//////////////////////////\r\n\r\nconst ActionCreatorsFoo = createActionCreators(ReducerClassFoo);\r\nconst ActionCreatorsBar = createActionCreators(ReducerClassBar);\r\n\r\nActionCreatorsFoo.set({fooField1: \"b\"});\r\nActionCreatorsFoo.set({fooField2: 2});\r\n\r\nActionCreatorsBar.set({barField1: [8]});\r\nActionCreatorsBar.set({barField2: /ding/});\r\n\r\n// Cannot set bad values\r\n// $ExpectError\r\nActionCreatorsFoo.set({fooField1: 2});\r\n\r\n// Cannot set unknown fields\r\n// $ExpectError\r\nActionCreatorsFoo.set({bad: 2});\r\n\r\n// Cannot set bar fields\r\n// $ExpectError\r\nActionCreatorsFoo.set({barField1: [8]});\r\n\r\n////////////////////////////\r\n// Reducer function tests //\r\n////////////////////////////\r\n\r\nconst reducerFoo = createReducerFunction(ReducerClassFoo, initialState.foo);\r\n\r\nreducerFoo(initialState, ActionCreatorsFoo.set({fooField1: \"c\"}));\r\n\r\n// no bad actions allowed\r\n// $ExpectError\r\nreducerFoo(initialState, {type: \"BAD_ACTION\"});\r\n\r\n// XXX bug! :( State is any here. This should fail!\r\nreducerFoo({bad: \"state\"}, ActionCreatorsFoo.set({fooField1: \"c\"}));\r\n\r\n// For some reason ImmerReducerState cannot infer state\r\n// from a generic class. Maybe this is a limitation in Typescript?\r\n\r\ntype InferredState = ImmerReducerState<typeof ReducerClassFoo>;\r\ndeclare const inferredState: InferredState;\r\n\r\n// XXX! Should fail too!\r\nconst anumber: AssignFail = inferredState;\r\n"
  },
  {
    "path": "__dtslint__/immer-reducer.dtslint.ts",
    "content": "import {Action, createStore, bindActionCreators} from \"redux\";\r\n\r\nimport {\r\n    ImmerReducer,\r\n    createActionCreators,\r\n    createReducerFunction,\r\n    isAction,\r\n    Actions,\r\n    isActionFrom,\r\n} from \"../src/immer-reducer\";\r\nimport {Dispatch} from \"react\";\r\nimport React from \"react\";\r\n\r\ninterface AssertNotAny {\r\n    ___: \"it should not be possible to assign to me\";\r\n}\r\n\r\ninterface State {\r\n    readonly foo: string;\r\n    readonly bar: number;\r\n}\r\n\r\nclass MyReducer extends ImmerReducer<State> {\r\n    setBoth(newFoo: string, newBar: number) {\r\n        this.setBar(newBar);\r\n        this.setFoo(newFoo);\r\n    }\r\n\r\n    setFoo(newFoo: string) {\r\n        this.draftState.foo = newFoo;\r\n    }\r\n\r\n    setBar(newBar: number) {\r\n        this.draftState.bar = newBar;\r\n    }\r\n\r\n    setFooStatic() {\r\n        this.draftState.foo = \"static\";\r\n    }\r\n}\r\n\r\n////////////////////\r\n// Test action types\r\n////////////////////\r\n\r\nconst ActionCreators = createActionCreators(MyReducer);\r\n\r\n// Action creator return Action Object\r\nconst action: {\r\n    type: \"setBar\";\r\n    payload: number;\r\n} = ActionCreators.setBar(3);\r\n\r\n// the action creator does no return any\r\n// $ExpectError\r\nconst is_not_any: AssertNotAny = ActionCreators.setBar(3);\r\n\r\n// actions without payload\r\nconst staticAction = ActionCreators.setFooStatic();\r\nconst staticPayload: [] = staticAction.payload;\r\n\r\n// Actions with multiple items in the payload\r\nconst bothAction = ActionCreators.setBoth(\"foo\", 1);\r\n\r\nconst bothPayload: [string, number] = bothAction.payload;\r\n\r\n// Only function properties are picked\r\n// $ExpectError\r\nActionCreators.draftState;\r\n// $ExpectError\r\nActionCreators.state;\r\n\r\n// Do not allow bad argument types\r\n// $ExpectError\r\nActionCreators.setBar(\"sdf\");\r\n\r\n// Do not allow bad method names\r\n// $ExpectError\r\nActionCreators.setBad(3);\r\n\r\n//////////////////////\r\n// Test reducer types\r\n//////////////////////\r\n\r\nclass BadReducer {\r\n    dong() {}\r\n}\r\n\r\n// Cannot create action creators from random classes\r\n// $ExpectError\r\ncreateActionCreators(BadReducer);\r\n\r\nconst reducer = createReducerFunction(MyReducer);\r\n\r\n// can create with proper initial state\r\ncreateReducerFunction(MyReducer, {foo: \"\", bar: 0});\r\n\r\n// Bad state argument is not allowed\r\n// $ExpectError\r\ncreateReducerFunction(MyReducer, {bad: \"state\"});\r\n\r\nconst newState: State = reducer(\r\n    {foo: \"sdf\", bar: 2},\r\n    {\r\n        type: \"setBar\",\r\n        payload: 3,\r\n    },\r\n);\r\n\r\n// reducer does not return any\r\n// $ExpectError\r\nconst no_any_state: AssertNotAny = reducer(\r\n    {foo: \"f\", bar: 2},\r\n    {\r\n        type: \"setBar\",\r\n        payload: 3,\r\n    },\r\n);\r\n\r\n// bad state for the reducer\r\nreducer(\r\n    // $ExpectError\r\n    {foo: \"sdf\", bar: \"should be number\"},\r\n    {\r\n        type: \"setBar\",\r\n        payload: 3,\r\n    },\r\n);\r\n\r\n// Bad action object\r\n// $ExpectError\r\nreducer({foo: \"sdf\", bar: 2}, {});\r\n\r\n// Bad payload type\r\nreducer(\r\n    {foo: \"sdf\", bar: 2},\r\n    // $ExpectError\r\n    {\r\n        type: \"setBar\",\r\n        payload: \"should be number here\",\r\n    },\r\n);\r\n\r\n// Bad action type\r\nreducer(\r\n    {foo: \"sdf\", bar: 2},\r\n    {\r\n        // $ExpectError\r\n        type: \"bad\",\r\n        payload: 3,\r\n    },\r\n);\r\n\r\nreducer({foo: \"sdf\", bar: 2}, ActionCreators.setBar(3));\r\n\r\nclass OtherReducer extends ImmerReducer<State> {\r\n    setDing(dong: string) {\r\n        this.draftState.foo = dong;\r\n    }\r\n}\r\n\r\nconst OtherActionCreators = createActionCreators(OtherReducer);\r\n\r\n// Mixed reducer and action creators from different ImmerReducer classes\r\n// $ExpectError\r\nreducer({foo: \"sdf\", bar: 2}, OtherActionCreators.setDing(\"sdf\"));\r\n\r\n// Action creator provides action type\r\nconst actionType: \"setBar\" = ActionCreators.setBar.type;\r\n\r\n// $ExpectError\r\nconst actionType_not_any: AssertNotAny = ActionCreators.setBar.type;\r\n\r\n//////////////////////\r\n// Test isAction types\r\n//////////////////////\r\n\r\ndeclare const unknownAction: {type: string};\r\n\r\nif (isAction(unknownAction, ActionCreators.setBar)) {\r\n    // $ExpectError\r\n    const actione_not_any: AssertNotAny = unknownAction;\r\n\r\n    const knownAction: {\r\n        type: \"setBar\";\r\n        payload: number;\r\n    } = unknownAction;\r\n\r\n    // $ExpectError\r\n    const nope: string = unknownAction.payload;\r\n}\r\n\r\n/////////////////////////////\r\n// Test Actions<> type helper\r\n/////////////////////////////\r\n\r\nclass Reducer1 extends ImmerReducer<State> {\r\n    setFoo(newFoo: string) {\r\n        this.draftState.foo = newFoo;\r\n    }\r\n\r\n    setBar(newBar: number) {\r\n        this.draftState.bar = newBar;\r\n    }\r\n}\r\n\r\ntype MyActions = Actions<typeof Reducer1>;\r\n\r\ndeclare const someActions: MyActions;\r\n\r\n// $ExpectError\r\nconst someActionsNotAny: AssertNotAny = someActions;\r\n\r\nconst someActionsTest:\r\n    | {\r\n          type: \"setFoo\";\r\n          payload: string;\r\n      }\r\n    | {\r\n          type: \"setBar\";\r\n          payload: number;\r\n      } = someActions;\r\n\r\ntype MyReducerActions = Actions<typeof Reducer1>;\r\ndeclare const myReducerActions: MyReducerActions;\r\n\r\n// $ExpectError\r\nconst actions_not_any: AssertNotAny = myReducerActions;\r\n\r\nconst actions_manual:\r\n    | {\r\n          type: \"setFoo\";\r\n          payload: string;\r\n      }\r\n    | {\r\n          type: \"setBar\";\r\n          payload: number;\r\n      } = myReducerActions;\r\n\r\n//////////////////////////\r\n// Test isActionFrom types\r\n//////////////////////////\r\n\r\ndeclare const someAction: Action;\r\n\r\nconst ActionCreators1 = createActionCreators(Reducer1);\r\n\r\nif (isActionFrom(someAction, Reducer1)) {\r\n    // $ExpectError\r\n    const notany: AssertNotAny = someAction;\r\n\r\n    const actions_manual:\r\n        | {\r\n              type: \"setFoo\";\r\n              payload: string;\r\n          }\r\n        | {\r\n              type: \"setBar\";\r\n              payload: number;\r\n          } = someAction;\r\n}\r\n\r\ntest(\"Can work with bindActionCreators\", () => {\r\n    const initialState = {foo: \"\"};\r\n    const store = createStore(s => initialState);\r\n\r\n    class Reducer extends ImmerReducer<typeof initialState> {\r\n        setFoo(foo: string) {}\r\n    }\r\n\r\n    const ActionCreators = createActionCreators(Reducer);\r\n\r\n    const boundActionCreators = bindActionCreators(\r\n        ActionCreators,\r\n        store.dispatch,\r\n    );\r\n});\r\n\r\ntest(\"can use with React.useReducer()\", () => {\r\n    const initialState = {foo: \"\"};\r\n\r\n    class Reducer extends ImmerReducer<typeof initialState> {\r\n        setFoo(foo: string) {}\r\n    }\r\n\r\n    const ActionCreators = createActionCreators(Reducer);\r\n    const reducerFuntion = createReducerFunction(Reducer);\r\n\r\n    function Component1() {\r\n        const [state, dispatch] = React.useReducer(\r\n            reducerFuntion,\r\n            initialState,\r\n        );\r\n\r\n        const callback = () => {\r\n            dispatch(ActionCreators.setFoo(\"test\"));\r\n\r\n            // $ExpectError\r\n            dispatch(\"bad\");\r\n\r\n            const foo: string = state.foo;\r\n\r\n            // $ExpectError\r\n            const bar: AssertNotAny = state.foo;\r\n        };\r\n\r\n        return null;\r\n    }\r\n});\r\n"
  },
  {
    "path": "__tests__/immer-reducer.test.tsx",
    "content": "import {\n    ImmerReducer,\n    createReducerFunction,\n    createActionCreators,\n    composeReducers,\n    setPrefix,\n    _clearKnownClasses,\n    isAction,\n    isActionFrom,\n} from \"../src/immer-reducer\";\n\nimport {createStore, combineReducers, Action} from \"redux\";\n\nbeforeEach(_clearKnownClasses);\n\nafterEach(() => {\n    setPrefix(\"IMMER_REDUCER\");\n});\n\ntest(\"can detect inherited actions\", () => {\n    class Parent extends ImmerReducer<any> {\n        setFoo(foo: string) {}\n    }\n\n    class Child extends Parent {\n        setFoo2(foo: string) {}\n    }\n\n    const actions = createActionCreators(Child);\n    expect(actions.setFoo).toBeTruthy();\n    expect(actions.setFoo2).toBeTruthy();\n});\n\ntest(\"can create reducers\", () => {\n    const initialState = {foo: \"bar\"};\n\n    class TestReducer extends ImmerReducer<typeof initialState> {\n        setFoo(foo: string) {\n            this.draftState.foo = foo;\n        }\n    }\n\n    const reducer = createReducerFunction(TestReducer);\n    const store = createStore(reducer, initialState);\n\n    expect(store.getState()).toEqual({foo: \"bar\"});\n});\n\ntest(\"the reducer can return the initial state\", () => {\n    const initialState = {foo: \"bar\"};\n\n    class TestReducer extends ImmerReducer<typeof initialState> {\n        setFoo(foo: string) {\n            this.draftState.foo = foo;\n        }\n    }\n\n    const reducer = createReducerFunction(TestReducer, initialState);\n    const store = createStore(reducer);\n\n    expect(store.getState()).toEqual({foo: \"bar\"});\n});\n\ntest(\"can dispatch actions\", () => {\n    const initialState = {foo: \"bar\"};\n\n    class TestReducer extends ImmerReducer<typeof initialState> {\n        noop() {}\n    }\n\n    const ActionCreators = createActionCreators(TestReducer);\n    const reducer = createReducerFunction(TestReducer, initialState);\n    const store = createStore(reducer);\n\n    store.dispatch(ActionCreators.noop());\n\n    expect(store.getState()).toEqual({foo: \"bar\"});\n});\n\ntest(\"can update state\", () => {\n    const initialState = {foo: \"bar\"};\n\n    class TestReducer extends ImmerReducer<typeof initialState> {\n        setFoo(foo: string) {\n            this.draftState.foo = foo;\n        }\n    }\n\n    const ActionCreators = createActionCreators(TestReducer);\n    const reducer = createReducerFunction(TestReducer, initialState);\n    const store = createStore(reducer);\n\n    store.dispatch(ActionCreators.setFoo(\"next\"));\n\n    expect(store.getState()).toEqual({foo: \"next\"});\n});\n\ntest(\"can update state using multiple methods\", () => {\n    const initialState = {foo: \"bar\", bar: 1};\n\n    class TestReducer extends ImmerReducer<typeof initialState> {\n        setFoo(foo: string) {\n            this.draftState.foo = foo;\n        }\n\n        setBar(bar: number) {\n            this.draftState.bar = bar;\n        }\n\n        setBoth(foo: string, bar: number) {\n            this.setFoo(foo);\n            this.setBar(bar);\n        }\n    }\n\n    const ActionCreators = createActionCreators(TestReducer);\n    const reducer = createReducerFunction(TestReducer, initialState);\n    const store = createStore(reducer);\n\n    store.dispatch(ActionCreators.setBoth(\"next\", 2));\n\n    expect(store.getState()).toEqual({foo: \"next\", bar: 2});\n});\n\ntest(\"the actual action type name is prefixed\", () => {\n    const initialState = {foo: \"bar\"};\n\n    class TestReducer extends ImmerReducer<typeof initialState> {\n        setFoo(foo: string) {\n            this.draftState.foo = foo;\n        }\n    }\n\n    const ActionCreators = createActionCreators(TestReducer);\n\n    const reducer = createReducerFunction(TestReducer, initialState);\n    const reducerSpy: typeof reducer = jest.fn(reducer);\n\n    const store = createStore(reducerSpy);\n\n    store.dispatch(ActionCreators.setFoo(\"next\"));\n\n    expect(reducerSpy).toHaveBeenLastCalledWith(\n        {foo: \"bar\"},\n        {\n            payload: \"next\",\n            type: \"IMMER_REDUCER:TestReducer#setFoo\",\n        },\n    );\n});\n\ntest(\"can add helpers to the class\", () => {\n    const initialState = {foo: 1, bar: 1};\n\n    class Helper {\n        state: typeof initialState;\n\n        constructor(state: typeof initialState) {\n            this.state = state;\n        }\n\n        getCombined() {\n            return this.state.foo + this.state.bar;\n        }\n    }\n\n    class TestReducer extends ImmerReducer<typeof initialState> {\n        helper = new Helper(this.state);\n\n        combineToBar() {\n            this.draftState.bar = this.helper.getCombined();\n        }\n    }\n\n    const ActionCreators = createActionCreators(TestReducer);\n    const reducer = createReducerFunction(TestReducer, initialState);\n    const store = createStore(reducer);\n\n    store.dispatch(ActionCreators.combineToBar());\n\n    expect(store.getState()).toEqual({foo: 1, bar: 2});\n});\n\ntest(\"can use combineReducers\", () => {\n    interface State1 {\n        foo: number;\n    }\n\n    interface State2 {\n        bar: string;\n    }\n\n    class TestReducer1 extends ImmerReducer<State1> {\n        setFoo(foo: number) {\n            this.draftState.foo = foo;\n        }\n    }\n\n    class TestReducer2 extends ImmerReducer<State2> {\n        setBar(bar: string) {\n            this.draftState.bar = bar;\n        }\n    }\n\n    const ActionCreators1 = createActionCreators(TestReducer1);\n    const ActionCreators2 = createActionCreators(TestReducer2);\n\n    const slice1 = createReducerFunction(TestReducer1, {foo: 0});\n    const slice2 = createReducerFunction(TestReducer2, {bar: \"\"});\n\n    const combined = combineReducers({slice1, slice2});\n\n    const store = createStore(combined);\n\n    store.dispatch(ActionCreators1.setFoo(1));\n    store.dispatch(ActionCreators2.setBar(\"barval\"));\n\n    const state: {\n        slice1: State1;\n        slice2: State2;\n    } = store.getState();\n\n    expect(state).toEqual({slice1: {foo: 1}, slice2: {bar: \"barval\"}});\n});\n\ntest(\"cannot collide reducers\", () => {\n    const initialState = {foo: \"bar\"};\n\n    class TestReducer1 extends ImmerReducer<typeof initialState> {\n        setFoo() {\n            this.draftState.foo = \"1\";\n        }\n    }\n\n    class TestReducer2 extends ImmerReducer<typeof initialState> {\n        setFoo() {\n            this.draftState.foo = \"2\";\n        }\n    }\n\n    const reducer = composeReducers(\n        createReducerFunction(TestReducer1),\n        createReducerFunction(TestReducer2),\n    );\n\n    const store = createStore(reducer, initialState);\n\n    const ActionCreators1 = createActionCreators(TestReducer1);\n    const ActionCreators2 = createActionCreators(TestReducer2);\n\n    store.dispatch(ActionCreators1.setFoo());\n    expect(store.getState()).toEqual({foo: \"1\"});\n\n    store.dispatch(ActionCreators2.setFoo());\n    expect(store.getState()).toEqual({foo: \"2\"});\n});\n\ntest(\"dynamically generated reducers do not collide\", () => {\n    const initialState = {\n        foo: \"\",\n    };\n\n    function createGenericReducer<T extends {[key: string]: unknown}>(\n        value: string,\n    ) {\n        return class GenericReducer extends ImmerReducer<T> {\n            set() {\n                Object.assign(this.draftState, {foo: value});\n            }\n        };\n    }\n    const ReducerClass1 = createGenericReducer<typeof initialState>(\"1\");\n    const ReducerClass2 = createGenericReducer<typeof initialState>(\"2\");\n\n    const reducer1 = createReducerFunction(ReducerClass1, initialState);\n    const reducer2 = createReducerFunction(ReducerClass2, initialState);\n\n    const reducer = composeReducers(reducer1, reducer2);\n\n    const ActionCreators1 = createActionCreators(ReducerClass1);\n    const ActionCreators2 = createActionCreators(ReducerClass2);\n\n    const store = createStore(reducer);\n\n    store.dispatch(ActionCreators1.set());\n    expect(store.getState().foo).toEqual(\"1\");\n\n    store.dispatch(ActionCreators2.set());\n    expect(store.getState().foo).toEqual(\"2\");\n});\n\ntest(\"can create dynamic reducers after creating actions\", () => {\n    const initialState = {\n        foo: \"\",\n    };\n\n    function createGenericReducer<T extends {[key: string]: unknown}>(\n        value: string,\n    ) {\n        return class GenericReducer extends ImmerReducer<T> {\n            set() {\n                Object.assign(this.draftState, {foo: value});\n            }\n        };\n    }\n    const ReducerClass1 = createGenericReducer<typeof initialState>(\"1\");\n    const ReducerClass2 = createGenericReducer<typeof initialState>(\"2\");\n\n    const ActionCreators1 = createActionCreators(ReducerClass1);\n    const ActionCreators2 = createActionCreators(ReducerClass2);\n\n    const reducer1 = createReducerFunction(ReducerClass1, initialState);\n    const reducer2 = createReducerFunction(ReducerClass2, initialState);\n\n    const reducer = composeReducers(reducer1, reducer2);\n\n    const store = createStore(reducer);\n\n    store.dispatch(ActionCreators1.set());\n    expect(store.getState().foo).toEqual(\"1\");\n\n    store.dispatch(ActionCreators2.set());\n    expect(store.getState().foo).toEqual(\"2\");\n});\n\ntest(\"throw error when using duplicate customNames\", () => {\n    class Reducer1 extends ImmerReducer<{foo: string}> {\n        static customName = \"dup\";\n        set() {\n            this.draftState.foo = \"foo\";\n        }\n    }\n\n    class Reducer2 extends ImmerReducer<{foo: string}> {\n        static customName = \"dup\";\n        set() {\n            this.draftState.foo = \"foo\";\n        }\n    }\n\n    createReducerFunction(Reducer1);\n\n    expect(() => {\n        createReducerFunction(Reducer2);\n    }).toThrow();\n});\n\ntest(\"action creators expose the actual action type name\", () => {\n    const initialState = {foo: \"bar\"};\n\n    class TestReducer extends ImmerReducer<typeof initialState> {\n        setBar(foo: string) {\n            this.draftState.foo = foo;\n        }\n    }\n\n    const ActionCreators = createActionCreators(TestReducer);\n\n    expect(ActionCreators.setBar.type).toEqual(\n        \"IMMER_REDUCER:TestReducer#setBar\",\n    );\n});\n\ntest(\"can customize prefix of action type name what is returned by action creator.\", () => {\n    const initialState = {foo: \"bar\"};\n\n    class TestReducer extends ImmerReducer<typeof initialState> {\n        setBar(foo: string) {\n            this.draftState.foo = foo;\n        }\n    }\n\n    setPrefix(\"AWESOME_LIBRARY\");\n    const ActionCreators = createActionCreators(TestReducer);\n\n    expect(ActionCreators.setBar.type).toEqual(\n        \"AWESOME_LIBRARY:TestReducer#setBar\",\n    );\n\n    const reducer = createReducerFunction(TestReducer);\n    const store = createStore(reducer, initialState);\n\n    store.dispatch(ActionCreators.setBar(\"ding\"));\n\n    expect(store.getState()).toEqual({foo: \"ding\"});\n});\n\ntest(\"isActionFrom can detect actions\", () => {\n    class TestReducer extends ImmerReducer<{foo: string}> {\n        setFoo(foo: string) {\n            this.draftState.foo = foo;\n        }\n    }\n    const ActionCreators = createActionCreators(TestReducer);\n\n    const action1: Action = ActionCreators.setFoo(\"foo\");\n\n    const action2: Action = {\n        type: \"other\",\n    };\n\n    expect(isActionFrom(action1, TestReducer)).toBe(true);\n    expect(isActionFrom(action2, TestReducer)).toBe(false);\n});\n\ntest(\"isAction can detect actions\", () => {\n    class TestReducer extends ImmerReducer<{foo: string}> {\n        setFoo(foo: string) {\n            this.draftState.foo = foo;\n        }\n    }\n    const ActionCreators = createActionCreators(TestReducer);\n\n    const action1: Action = ActionCreators.setFoo(\"foo\");\n\n    const action2: Action = {\n        type: \"other\",\n    };\n\n    expect(isAction(action1, ActionCreators.setFoo)).toBe(true);\n    expect(isAction(action2, ActionCreators.setFoo)).toBe(false);\n});\n\ntest(\"single argument is the payload value\", () => {\n    class TestReducer extends ImmerReducer<{}> {\n        singleArg(arg: string) {}\n    }\n    const action = createActionCreators(TestReducer).singleArg(\"foo\");\n    expect(action.payload).toEqual(\"foo\");\n});\n\ntest(\"multiple arguments are as an array in the payload\", () => {\n    class TestReducer extends ImmerReducer<{}> {\n        multiple(arg1: string, arg2: number) {}\n    }\n    const action = createActionCreators(TestReducer).multiple(\"foo\", 2);\n    expect(action.payload).toEqual([\"foo\", 2]);\n});\n\ntest(\"single argument can be an array\", () => {\n    class TestReducer extends ImmerReducer<{}> {\n        singleArg(arg: string[]) {}\n    }\n    const action = createActionCreators(TestReducer).singleArg([\"foo\"]);\n    expect(action.payload).toEqual([\"foo\"]);\n});\n\ntest(\"single array argument is dispatched correctly\", () => {\n    expect.assertions(1);\n\n    class TestReducer extends ImmerReducer<{}> {\n        arrayArg(arr: string[]) {\n            expect(arr).toEqual([\"foo\", \"bar\"]);\n        }\n    }\n\n    const store = createStore(createReducerFunction(TestReducer, {}));\n    store.dispatch(createActionCreators(TestReducer).arrayArg([\"foo\", \"bar\"]));\n});\n\ntest(\"puts only defined arguments to the action object\", () => {\n    class TestReducer extends ImmerReducer<{}> {\n        doIt() {}\n    }\n\n    // Simulate click handler type\n    let onClick = (arg: string): any => {};\n\n    // \"Pass action the event handler\"\n    onClick = createActionCreators(TestReducer).doIt;\n\n    const action = onClick(\"nope\");\n\n    expect(action.payload).toEqual([]);\n});\n\ntest(\"puts only defined arguments to the action object\", () => {\n    class TestReducer extends ImmerReducer<{}> {\n        doIt(oneArg: string) {}\n    }\n\n    // Simulate click handler type\n    let onClick = (first: string, second: string): any => {};\n\n    // \"Pass action the event handler\"\n    onClick = createActionCreators(TestReducer).doIt;\n\n    const action = onClick(\"yes\", \"nope\");\n\n    expect(action.payload).toEqual(\"yes\");\n});\n\ntest(\"can replace the draft state with completely new state\", () => {\n    const initialState = {foo: \"bar\", ding: \"ding\"};\n\n    class TestReducer extends ImmerReducer<typeof initialState> {\n        resetState() {\n            this.draftState = {\n                foo: \"new\",\n                ding: \"new\",\n            };\n        }\n    }\n\n    const ActionCreators = createActionCreators(TestReducer);\n\n    const reducer = createReducerFunction(TestReducer);\n    const store = createStore(reducer, initialState);\n\n    store.dispatch(ActionCreators.resetState());\n\n    expect(store.getState()).toEqual({\n        foo: \"new\",\n        ding: \"new\",\n    });\n});\n"
  },
  {
    "path": "__tests__/use-reducer-integration.test.tsx",
    "content": "import React from \"react\";\nimport {render, fireEvent, cleanup} from \"@testing-library/react\";\nimport {\n    ImmerReducer,\n    createActionCreators,\n    createReducerFunction,\n} from \"../src/immer-reducer\";\n\nafterEach(cleanup);\n\ntest(\"can use with React.useReducer()\", () => {\n    const initialState = {foo: \"\"};\n\n    class Reducer extends ImmerReducer<typeof initialState> {\n        setFoo(foo: string) {\n            this.draftState.foo = foo;\n        }\n    }\n\n    const ActionCreators = createActionCreators(Reducer);\n    const reducerFuntion = createReducerFunction(Reducer);\n\n    function Foo() {\n        const [state, dispatch] = React.useReducer(\n            reducerFuntion,\n            initialState,\n        );\n\n        return (\n            <button\n                data-testid=\"button\"\n                onClick={() => {\n                    dispatch(ActionCreators.setFoo(\"clicked\"));\n                }}\n            >\n                {state.foo}\n            </button>\n        );\n    }\n\n    const rtl = render(<Foo />);\n    const button = rtl.getByTestId(\"button\");\n\n    fireEvent.click(button);\n\n    expect(button.innerHTML).toBe(\"clicked\");\n});\n"
  },
  {
    "path": "jest.config.js",
    "content": "module.exports = {\r\n    moduleFileExtensions: [\"ts\", \"tsx\", \"js\"],\r\n    transform: {\r\n        \"^.+\\\\.(ts|tsx)$\": \"ts-jest\",\r\n    },\r\n    globals: {\r\n        \"ts-jest\": {\r\n            tsconfig: \"tsconfig.json\",\r\n        },\r\n    },\r\n    testMatch: [\"**/?(*.)+(spec|test).ts?(x)\"],\r\n};\r\n"
  },
  {
    "path": "package.json",
    "content": "{\n    \"name\": \"immer-reducer\",\n    \"version\": \"0.7.13\",\n    \"description\": \"\",\n    \"main\": \"lib/immer-reducer.js\",\n    \"types\": \"lib/immer-reducer.d.ts\",\n    \"repository\": {\n        \"url\": \"https://github.com/epeli/immer-reducer\"\n    },\n    \"scripts\": {\n        \"test\": \"npm run dtslint && jest\",\n        \"build\": \"tsc --project tsconfig.build.json && rm -rf lib && mv build/src lib && rm -rf build\",\n        \"clean\": \"rm -rf lib build\",\n        \"dtslint\": \"tslint --project tsconfig.dtslint.json\",\n        \"prepublishOnly\": \"npm run test && npm run build\"\n    },\n    \"keywords\": [\n        \"typescript\",\n        \"immer\"\n    ],\n    \"author\": \"\",\n    \"license\": \"MIT\",\n    \"files\": [\n        \"lib\"\n    ],\n    \"devDependencies\": {\n        \"@testing-library/react\": \"^8.0.4\",\n        \"@types/jest\": \"^24.0.15\",\n        \"@types/react\": \"^16.8.22\",\n        \"@types/react-dom\": \"^16.8.4\",\n        \"@types/redux\": \"^3.6.0\",\n        \"dtslint\": \"^4.0.7\",\n        \"jest\": \"^26.6.3\",\n        \"prettier\": \"^1.18.2\",\n        \"react\": \"^16.8.6\",\n        \"react-dom\": \"^16.8.6\",\n        \"redux\": \"^4.0.1\",\n        \"ts-jest\": \"^26.5.1\",\n        \"typescript\": \"^3.9.9\"\n    },\n    \"dependencies\": {\n        \"immer\": \"^1.4.0 || ^2.0.0 || ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^8.0.0 || ^9.0.0\"\n    }\n}\n"
  },
  {
    "path": "src/immer-reducer.ts",
    "content": "import produce, {Draft} from \"immer\";\r\n\r\nlet actionTypePrefix = \"IMMER_REDUCER\";\r\n\r\n/** get function arguments as tuple type */\r\ntype ArgumentsType<T> = T extends (...args: infer V) => any ? V : never;\r\n\r\n/**\r\n * Get the first value of tuple when the tuple length is 1 otherwise return the\r\n * whole tuple\r\n */\r\ntype FirstOrAll<T> = T extends [infer V] ? V : T;\r\n\r\n/** Get union of function property names */\r\ntype FunctionPropertyNames<T> = {\r\n    [K in keyof T]: T[K] extends Function ? K : never;\r\n}[keyof T];\r\n\r\ntype MethodObject = {[key: string]: () => any};\r\n\r\n/** Pick only methods from object */\r\ntype Methods<T> = Pick<T, FunctionPropertyNames<T>>;\r\n\r\n/** flatten functions in an object to their return values */\r\ntype FlattenToReturnTypes<T extends MethodObject> = {\r\n    [K in keyof T]: ReturnType<T[K]>;\r\n};\r\n\r\n/** get union of object value types */\r\ntype ObjectValueTypes<T> = T[keyof T];\r\n\r\n/** get union of object method return types */\r\ntype ReturnTypeUnion<T extends MethodObject> = ObjectValueTypes<\r\n    FlattenToReturnTypes<T>\r\n>;\r\n\r\n/**\r\n * Get union of actions types from a ImmerReducer class\r\n */\r\nexport type Actions<T extends ImmerReducerClass> = ReturnTypeUnion<\r\n    ActionCreators<T>\r\n>;\r\n\r\n/** type constraint for the ImmerReducer class  */\r\nexport interface ImmerReducerClass {\r\n    customName?: string;\r\n    new (...args: any[]): ImmerReducer<any>;\r\n}\r\n\r\n/** get state type from a ImmerReducer subclass */\r\nexport type ImmerReducerState<T> = T extends {\r\n    prototype: {\r\n        state: infer V;\r\n    };\r\n}\r\n    ? V\r\n    : never;\r\n\r\n/** generate reducer function type from the ImmerReducer class */\r\nexport interface ImmerReducerFunction<T extends ImmerReducerClass> {\r\n    (\r\n        state: ImmerReducerState<T> | undefined,\r\n        action: ReturnTypeUnion<ActionCreators<T>>,\r\n    ): ImmerReducerState<T>;\r\n}\r\n\r\n/** ActionCreator function interface with actual action type name */\r\ninterface ImmerActionCreator<ActionTypeType, Payload extends any[]> {\r\n    readonly type: ActionTypeType;\r\n\r\n    (...args: Payload): {\r\n        type: ActionTypeType;\r\n        payload: FirstOrAll<Payload>;\r\n    };\r\n}\r\n\r\n/** generate ActionCreators types from the ImmerReducer class */\r\nexport type ActionCreators<ClassActions extends ImmerReducerClass> = {\r\n    [K in keyof Methods<InstanceType<ClassActions>>]: ImmerActionCreator<\r\n        K,\r\n        ArgumentsType<InstanceType<ClassActions>[K]>\r\n    >;\r\n};\r\n\r\n/**\r\n * Internal type for the action\r\n */\r\ntype ImmerAction =\r\n    | {\r\n          type: string;\r\n          payload: unknown;\r\n          args?: false;\r\n      }\r\n    | {\r\n          type: string;\r\n          payload: unknown[];\r\n          args: true;\r\n      };\r\n\r\n/**\r\n * Type guard for detecting actions created by immer reducer\r\n *\r\n * @param action any redux action\r\n * @param immerActionCreator method from a ImmerReducer class\r\n */\r\nexport function isAction<A extends ImmerActionCreator<any, any>>(\r\n    action: {type: any},\r\n    immerActionCreator: A,\r\n): action is ReturnType<A> {\r\n    return action.type === immerActionCreator.type;\r\n}\r\n\r\nfunction isActionFromClass<T extends ImmerReducerClass>(\r\n    action: {type: any},\r\n    immerReducerClass: T,\r\n): action is Actions<T> {\r\n    if (typeof action.type !== \"string\") {\r\n        return false;\r\n    }\r\n\r\n    if (!action.type.startsWith(actionTypePrefix + \":\")) {\r\n        return false;\r\n    }\r\n\r\n    const [className, methodName] = removePrefix(action.type).split(\"#\");\r\n\r\n    if (className !== getReducerName(immerReducerClass)) {\r\n        return false;\r\n    }\r\n\r\n    if (typeof immerReducerClass.prototype[methodName] !== \"function\") {\r\n        return false;\r\n    }\r\n\r\n    return true;\r\n}\r\n\r\nexport function isActionFrom<T extends ImmerReducerClass>(\r\n    action: {type: any},\r\n    immerReducerClass: T,\r\n): action is Actions<T> {\r\n    return isActionFromClass(action, immerReducerClass);\r\n}\r\n\r\ninterface Reducer<State> {\r\n    (state: State | undefined, action: any): State;\r\n}\r\n\r\n/**\r\n * Combine multiple reducers into a single one\r\n *\r\n * @param reducers two or more reducer\r\n */\r\nexport function composeReducers<State>(\r\n    ...reducers: Reducer<State | undefined>[]\r\n): Reducer<State> {\r\n    return (state: any, action: any) => {\r\n        return (\r\n            reducers.reduce((state, subReducer) => {\r\n                if (typeof subReducer === \"function\") {\r\n                    return subReducer(state, action);\r\n                }\r\n\r\n                return state;\r\n            }, state) || state\r\n        );\r\n    };\r\n}\r\n\r\n/** The actual ImmerReducer class */\r\nexport class ImmerReducer<T> {\r\n    static customName?: string;\r\n    readonly state: T;\r\n    draftState: Draft<T>; // Make read only states mutable using Draft\r\n\r\n    constructor(draftState: Draft<T>, state: T) {\r\n        this.state = state;\r\n        this.draftState = draftState;\r\n    }\r\n}\r\n\r\nfunction removePrefix(actionType: string) {\r\n    return actionType\r\n        .split(\":\")\r\n        .slice(1)\r\n        .join(\":\");\r\n}\r\n\r\nlet KNOWN_REDUCER_CLASSES: typeof ImmerReducer[] = [];\r\n\r\nconst DUPLICATE_INCREMENTS: {[name: string]: number | undefined} = {};\r\n\r\n/**\r\n * Set customName for classes automatically if there is multiple reducers\r\n * classes defined with the same name. This can occur accidentaly when using\r\n * name mangling with minifiers.\r\n *\r\n * @param immerReducerClass\r\n */\r\nfunction setCustomNameForDuplicates(immerReducerClass: typeof ImmerReducer) {\r\n    const hasSetCustomName = KNOWN_REDUCER_CLASSES.find(klass =>\r\n        Boolean(klass === immerReducerClass),\r\n    );\r\n\r\n    if (hasSetCustomName) {\r\n        return;\r\n    }\r\n\r\n    const duplicateCustomName =\r\n        immerReducerClass.customName &&\r\n        KNOWN_REDUCER_CLASSES.find(klass =>\r\n            Boolean(\r\n                klass.customName &&\r\n                    klass.customName === immerReducerClass.customName,\r\n            ),\r\n        );\r\n\r\n    if (duplicateCustomName) {\r\n        throw new Error(\r\n            `There is already customName ${immerReducerClass.customName} defined for ${duplicateCustomName.name}`,\r\n        );\r\n    }\r\n\r\n    const duplicate = KNOWN_REDUCER_CLASSES.find(\r\n        klass => klass.name === immerReducerClass.name,\r\n    );\r\n\r\n    if (duplicate && !duplicate.customName) {\r\n        let number = DUPLICATE_INCREMENTS[immerReducerClass.name];\r\n\r\n        if (number) {\r\n            number++;\r\n        } else {\r\n            number = 1;\r\n        }\r\n\r\n        DUPLICATE_INCREMENTS[immerReducerClass.name] = number;\r\n\r\n        immerReducerClass.customName = immerReducerClass.name + \"_\" + number;\r\n    }\r\n\r\n    KNOWN_REDUCER_CLASSES.push(immerReducerClass);\r\n}\r\n\r\n/**\r\n * Convert function arguments to ImmerAction object\r\n */\r\nfunction createImmerAction(type: string, args: unknown[]): ImmerAction {\r\n    if (args.length === 1) {\r\n        return {type, payload: args[0]};\r\n    }\r\n\r\n    return {\r\n        type,\r\n        payload: args,\r\n        args: true,\r\n    };\r\n}\r\n\r\n/**\r\n * Get function arguments from the ImmerAction object\r\n */\r\nfunction getArgsFromImmerAction(action: ImmerAction): unknown[] {\r\n    if (action.args) {\r\n        return action.payload;\r\n    }\r\n\r\n    return [action.payload];\r\n}\r\n\r\nfunction getAllPropertyNames(obj: object) {\r\n    const proto = Object.getPrototypeOf(obj);\r\n    const inherited: string[] = proto ? getAllPropertyNames(proto) : [];\r\n    return Object.getOwnPropertyNames(obj)\r\n        .concat(inherited)\r\n        .filter(\r\n            (propertyName, index, uniqueList) =>\r\n                uniqueList.indexOf(propertyName) === index,\r\n        );\r\n}\r\n\r\nexport function createActionCreators<T extends ImmerReducerClass>(\r\n    immerReducerClass: T,\r\n): ActionCreators<T> {\r\n    setCustomNameForDuplicates(immerReducerClass);\r\n\r\n    const actionCreators: {[key: string]: Function} = {};\r\n    const immerReducerProperties = getAllPropertyNames(ImmerReducer.prototype);\r\n    getAllPropertyNames(immerReducerClass.prototype).forEach(key => {\r\n        if (immerReducerProperties.includes(key)) {\r\n            return;\r\n        }\r\n        const method = immerReducerClass.prototype[key];\r\n\r\n        if (typeof method !== \"function\") {\r\n            return;\r\n        }\r\n\r\n        const type = `${actionTypePrefix}:${getReducerName(\r\n            immerReducerClass,\r\n        )}#${key}`;\r\n\r\n        const actionCreator = (...args: any[]) => {\r\n            // Make sure only the arguments are passed to the action object that\r\n            // are defined in the method\r\n            return createImmerAction(type, args.slice(0, method.length));\r\n        };\r\n        actionCreator.type = type;\r\n        actionCreators[key] = actionCreator;\r\n    });\r\n\r\n    return actionCreators as any; // typed in the function signature\r\n}\r\n\r\nfunction getReducerName(klass: {name: string; customName?: string}) {\r\n    const name = klass.customName || klass.name;\r\n    if (!name) {\r\n        throw new Error(\r\n            `immer-reducer failed to get reducer name for a class. Try adding 'static customName = \"name\"'`,\r\n        );\r\n    }\r\n    return name;\r\n}\r\n\r\nexport function createReducerFunction<T extends ImmerReducerClass>(\r\n    immerReducerClass: T,\r\n    initialState?: ImmerReducerState<T>,\r\n): ImmerReducerFunction<T> {\r\n    setCustomNameForDuplicates(immerReducerClass);\r\n\r\n    return function immerReducerFunction(state, action) {\r\n        if (state === undefined) {\r\n            state = initialState;\r\n        }\r\n\r\n        if (!isActionFromClass(action, immerReducerClass)) {\r\n            return state;\r\n        }\r\n\r\n        if (!state) {\r\n            throw new Error(\r\n                \"ImmerReducer does not support undefined state. Pass initial state to createReducerFunction() or createStore()\",\r\n            );\r\n        }\r\n\r\n        const [_, methodName] = removePrefix(action.type as string).split(\"#\");\r\n\r\n        return produce(state, draftState => {\r\n            const reducers: any = new immerReducerClass(draftState, state);\r\n\r\n            reducers[methodName](...getArgsFromImmerAction(action as any));\r\n\r\n            // The reducer replaced the instance with completely new state so\r\n            // make that to be the next state\r\n            if (reducers.draftState !== draftState) {\r\n                return reducers.draftState;\r\n            }\r\n\r\n            return draftState;\r\n\r\n            // Workaround typing changes in Immer 9.x. This does not actually\r\n            // affect the exposed types by immer-reducer itself.\r\n\r\n            // Also using immer internally with anys like this allow us to\r\n            // support multiple versions of immer.\r\n        }) as any;\r\n    };\r\n}\r\n\r\nexport function setPrefix(prefix: string): void {\r\n    actionTypePrefix = prefix;\r\n}\r\n\r\n/**\r\n * INTERNAL! This is only for tests!\r\n */\r\nexport function _clearKnownClasses() {\r\n    KNOWN_REDUCER_CLASSES = [];\r\n}\r\n\r\n/**\r\n * https://webpack.js.org/api/hot-module-replacement/#module-api\r\n */\r\ninterface WebpackModule {\r\n    hot?: {\r\n        status(): string;\r\n        addStatusHandler?: (handler: (status: string) => void) => void;\r\n    };\r\n}\r\n\r\n/**\r\n * Webpack Module global if using Wepback\r\n */\r\ndeclare const module: WebpackModule | undefined;\r\n\r\nif (typeof module !== \"undefined\") {\r\n    // Clear classes on Webpack Hot Module replacement as it will mess up the\r\n    // duplicate checks appear\r\n    module.hot?.addStatusHandler?.(status => {\r\n        if (status === \"prepare\") {\r\n            _clearKnownClasses();\r\n        }\r\n    });\r\n}\r\n"
  },
  {
    "path": "tsconfig.build.json",
    "content": "{\n    \"extends\": \"./tsconfig.json\",\n    \"exclude\": [\"__dtslint__\"],\n    \"compilerOptions\": {\n        \"sourceMap\": true,\n        \"noEmit\": false,\n        \"outDir\": \"./build\",\n        \"declaration\": true,\n        \"declarationDir\": \"./build\"\n    }\n}\n"
  },
  {
    "path": "tsconfig.dtslint.json",
    "content": "{\r\n    \"extends\": \"./tsconfig.json\",\r\n    \"exclude\": []\r\n}\r\n"
  },
  {
    "path": "tsconfig.json",
    "content": "{\n    \"compilerOptions\": {\n        \"module\": \"commonjs\",\n        \"target\": \"es5\",\n        \"noEmit\": true,\n        \"jsx\": \"react\",\n        \"lib\": [\"esnext\", \"dom\"],\n        \"moduleResolution\": \"node\",\n        \"forceConsistentCasingInFileNames\": true,\n        \"strict\": true,\n        \"esModuleInterop\": true\n    }\n}\n"
  },
  {
    "path": "tslint.json",
    "content": "{\n    \"rulesDirectory\": \"./node_modules/dtslint/bin/rules\",\n    \"rules\": {\n        \"expect\": true\n    }\n}\n"
  }
]