[
  {
    "path": ".gitignore",
    "content": "# Created by https://www.gitignore.io/api/visualstudiocode\n\n### VisualStudioCode ###\n.vscode/\n\ndist/\nnode_modules/\nnpm-debug.log\n"
  },
  {
    "path": ".prettierrc",
    "content": "{\n    \"tabWidth\": 4,\n    \"trailingComma\": \"all\"\n}\n"
  },
  {
    "path": ".travis.yml",
    "content": "language: node_js\nnode_js:\n  - \"node\"\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2017 David Philipson\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": "# TypeScript FSA Reducers\n\nFluent syntax for defining typesafe Redux reducers on top of\n[typescript-fsa](https://github.com/aikoven/typescript-fsa).\n\n[![Build\nStatus](https://travis-ci.org/dphilipson/typescript-fsa-reducers.svg?branch=master)](https://travis-ci.org/dphilipson/typescript-fsa-reducers)\n\n## Introduction\n\nThis library will allow you to write typesafe reducers that look like this:\n\n```ts\nconst reducer = reducerWithInitialState(INITIAL_STATE)\n    .case(setName, setNameHandler)\n    .case(addBalance, addBalanceHandler)\n    .case(setIsFrozen, setIsFrozenHandler);\n```\n\nIt removes the boilerplate normally associated with writing reducers, including\nif-else chains, the default case, and the need to pull the payload field off of\nthe action.\n\n## Table of Contents\n\n<!-- toc -->\n\n- [Usage](#usage)\n- [Installation](#installation)\n- [API](#api)\n  * [Starting a reducer chain](#starting-a-reducer-chain)\n    + [`reducerWithInitialState(initialState)`](#reducerwithinitialstateinitialstate)\n    + [`reducerWithoutInitialState()`](#reducerwithoutinitialstate)\n    + [`upcastingReducer()`](#upcastingreducer)\n  * [Reducer chain methods](#reducer-chain-methods)\n    + [`.case(actionCreator, handler(state, payload) => newState)`](#caseactioncreator-handlerstate-payload--newstate)\n    + [`.caseWithAction(actionCreator, handler(state, action) => newState)`](#casewithactionactioncreator-handlerstate-action--newstate)\n    + [`.cases(actionCreators, handler(state, payload) => newState)`](#casesactioncreators-handlerstate-payload--newstate)\n    + [`.casesWithAction(actionCreators, handler(state, action) => newState)`](#caseswithactionactioncreators-handlerstate-action--newstate)\n    + [`.withHandling(updateBuilder(builder) => builder)`](#withhandlingupdatebuilderbuilder--builder)\n    + [`.default(handler(state, action) => newState)`](#defaulthandlerstate-action--newstate)\n    + [`.build()`](#build)\n\n<!-- tocstop -->\n\n## Usage\n\nThis library allows you to define reducers by chaining a series of handlers for\ndifferent action types and optionally providing an initial value. It builds on\ntop of and assumes familiarity with the excellent\n[typescript-fsa](https://github.com/aikoven/typescript-fsa).\n\nSuppose we have used [typescript-fsa](https://github.com/aikoven/typescript-fsa)\nto define our state and some actions:\n\n```ts\nimport actionCreatorFactory from \"typescript-fsa\";\nconst actionCreator = actionCreatorFactory();\n\ninterface State {\n    name: string;\n    balance: number;\n    isFrozen: boolean;\n}\n\nconst INITIAL_STATE: State = {\n    name: \"Untitled\",\n    balance: 0,\n    isFrozen: false,\n};\n\nconst setName = actionCreator<string>(\"SET_NAME\");\nconst addBalance = actionCreator<number>(\"ADD_BALANCE\");\nconst setIsFrozen = actionCreator<boolean>(\"SET_IS_FROZEN\");\n```\n\nUsing vanilla `typescript-fsa`, we might define a reducer as follows:\n\n```ts\nimport { Action } from \"redux\";\nimport { isType } from \"typescript-fsa\";\n\nfunction reducer(state = INITIAL_STATE, action: Action): State {\n    if (isType(action, setName)) {\n        return { ...state, name: action.payload };\n    } else if (isType(action, addBalance)) {\n        return {\n            ...state,\n            balance: state.balance + action.payload,\n        };\n    } else if (isType(action, setIsFrozen)) {\n        return { ...state, isFrozen: action.payload };\n    } else {\n        return state;\n    }\n}\n```\n\nUsing this library, the above is exactly equivalent to the following code:\n\n```ts\nimport { reducerWithInitialState } from \"typescript-fsa-reducers\";\n\nconst reducer = reducerWithInitialState(INITIAL_STATE)\n    .case(setName, (state, name) => ({ ...state, name }))\n    .case(addBalance, (state, amount) => ({\n        ...state,\n        balance: state.balance + amount,\n    }))\n    .case(setIsFrozen, (state, isFrozen) => ({ ...state, isFrozen }));\n```\n\nNote that unlike the vanilla case, there is no need to pull the payload off of\nthe action, as it is passed directly to the handler, nor is it necessary to\nspecify a default case which returns `state` unmodified.\n\nEverything is typesafe. If the types of the action payload and handler don't\nline up, then TypeScript will complain. If you find it easier to read, you can\nof course pull out the handlers into separate functions, as shown in the\n[Introduction](#introduction).\n\nIf the full action is needed rather than just the payload, `.caseWithAction()`\nmay be used in place of `.case()`. This may be useful if you intend to pass the\naction unchanged to a different reducer, or if you need to read the `meta` field\nof the action. For example:\n\n```ts\nimport { Action } from \"typescript-fsa\";\n\nconst setText = actionCreator<string>(\"SET_TEXT\");\n\nconst reducer = reducerWithInitialState({\n    text: \"\",\n    lastEditBy: \"\",\n}).caseWithAction(incrementCount, (state, { payload, meta }) => ({\n    text: payload,\n    lastEditBy: meta.author,\n}));\n\n// Returns { text: \"hello\", lastEditBy: \"cbrontë\" }.\nreducer(undefined, setText(\"hello\", { author: \"cbrontë\" }));\n```\n\nFurther, a single handler may be assigned to multiple action types at once using\n`.cases()` or `.casesWithAction()`:\n\n```ts\nconst reducer = reducerWithInitialState(initialState).cases(\n    [setName, addBalance],\n    (state, payload) => {\n        // Payload has type SetNamePayload | AddBalancePayload.\n        // ...\n\n        // Make sure to return the updated state, or TypeScript will give you a\n        // rather unhelpful error message.\n        return state;\n    },\n);\n```\n\nThe reducer builder chains are mutable. Each call to `.case()` modifies the\ncallee to respond to the specified action type. If this is undesirable, see the\n[`.build()`](#build) method below.\n\n## Installation\n\nFor this library to be useful, you will also need\n[typescript-fsa](https://github.com/aikoven/typescript-fsa) to define your\nactions.\n\nWith Yarn:\n\n```\nyarn add typescript-fsa-reducers typescript-fsa\n```\n\nOr with NPM:\n\n```\nnpm install --save typescript-fsa-reducers typescript-fsa\n```\n\n## API\n\n### Starting a reducer chain\n\n#### `reducerWithInitialState(initialState)`\n\nStarts a reducer builder-chain which uses the provided initial state if passed\n`undefined` as its state. For example usage, see the [Usage](#usage) section\nabove.\n\n#### `reducerWithoutInitialState()`\n\nStarts a reducer builder-chain without special logic for an initial state.\n`undefined` will be treated like any other value for the state.\n\nRedux seems to really want you to provide an initial state for your reducers.\nIts `createStore` API encourages it and `combineReducers` function enforces it.\nFor the Redux author's reasoning behind this, see [this\nthread](https://github.com/reactjs/redux/issues/514). For this reason,\n`reducerWithInitialState` will likely be the more common choice, but the option\nto not provide an initial state is there in case you have some means of\ncomposing reducers for which initial state is unnecessary.\n\nNote that since the type of the state cannot be inferred from the initial state,\nit must be provided as a type parameter:\n\n```ts\nconst reducer = reducerWithoutInitialState<State>()\n    .case(setName, setNameHandler)\n    .case(addBalance, addBalanceHandler)\n    .case(setIsFrozen, setIsFrozenHandler);\n```\n\n#### `upcastingReducer()`\n\nStarts a builder-chain which produces a \"reducer\" whose return type is a\nsupertype of the input state. This is most useful for handling a state which may\nbe in one of several \"modes\", each of which responds differently to actions and\ncan transition to the other modes. Many applications will not have a use for\nthis.\n\nNote that the function produced is technically not a reducer because the initial\nand updated states are different types.\n\nExample usage:\n\n```javascript\ntype State = StoppedState | StartedState;\n\ninterface StoppedState {\n    type: \"STOPPED\";\n}\n\ninterface StartedState {\n    type: \"STARTED\";\n    count: number;\n}\n\nconst INITIAL_STATE: State = { type: \"STOPPED\" };\n\nconst startWithCount = actionCreator<number>(\"START_WITH_COUNT\");\nconst addToCount = actionCreator<number>(\"ADD_TO_COUNT\");\nconst stop = actionCreator<void>(\"STOP\");\n\nfunction startWithCountHandler(state: StoppedState, count: number): State {\n    return { type: \"STARTED\", count };\n}\n\nfunction addToCountHandler(state: StartedState, count: number): State {\n    return { ...state, count: state.count + count };\n}\n\nfunction stopHandler(state: StartedState): State {\n    return { type: \"STOPPED\" };\n}\n\nconst stoppedReducer = upcastingReducer<StoppedState, State>()\n    .case(startWithCount, startWithCountHandler);\n\nconst startedReducer = upcastingReducer<StartedState, State>()\n    .case(addToCount, addToCountHandler)\n    .case(stop, stopHandler);\n\nfunction reducer(state = INITIAL_STATE, action: Redux.Action): State {\n    if (state.type === \"STOPPED\") {\n        return stoppedReducer(state, action);\n    } else if (state.type === \"STARTED\") {\n        return startedReducer(state, action);\n    } else {\n        throw new Error(\"Unknown state\");\n    }\n}\n```\n\n### Reducer chain methods\n\n#### `.case(actionCreator, handler(state, payload) => newState)`\n\nMutates the reducer such that it applies `handler` when passed actions matching\nthe type of `actionCreator`. For examples, see [Usage](#usage).\n\n#### `.caseWithAction(actionCreator, handler(state, action) => newState)`\n\nLike `.case()`, except that `handler` receives the entire action as its second\nargument rather than just the payload. This is useful if you want to read other\nproperties of the action, such as `meta` or `error`, or if you want to pass the\nentire action unmodified to some other function. For an example, see\n[Usage](#usage).\n\n#### `.cases(actionCreators, handler(state, payload) => newState)`\n\nLike `.case()`, except that multiple action creators may be provided and the\nsame handler is applied to all of them. That is,\n\n```javascript\nreducerWithInitialState(initialState).cases(\n    [setName, addBalance, setIsFrozen],\n    handler,\n);\n```\n\nis equivalent to\n\n```javascript\nreducerWithInitialState(initialState)\n    .case(setName, handler)\n    .case(addBalance, handler)\n    .case(setIsFrozen, handler);\n```\n\nNote that the payload passed to the handler may be of the type of any of the\nlisted action types' payloads. In TypeScript terms, this means it has type `P1 | P2 | ...`, where `P1, P2, ...` are the payload types of the listed action\ncreators.\n\nThe payload type is inferred automatically for up to four action types. After\nthat, it must be supplied as a type annotation, for example:\n\n```javascript\nreducerWithInitialState(initialState).cases <\n    { documentId: number } >\n    ([\n        selectDocument,\n        editDocument,\n        deleteDocument,\n        sendDocument,\n        archiveDocument,\n    ],\n    handler);\n```\n\n#### `.casesWithAction(actionCreators, handler(state, action) => newState)`\n\nLike `.cases()`, except that the handler receives the entire action as its\nsecond argument rather than just the payload.\n\n#### `.withHandling(updateBuilder(builder) => builder)`\n\nConvenience method which applies the provided function to the current builder\nand returns the result. Useful if you have a sequence of builder updates (calls\nto `.case()`, etc.) which you want to reuse across several reducers.\n\n#### `.default(handler(state, action) => newState)`\n\nProduces a reducer which applies `handler` when no previously added `.case()`,\n`.caseWithAction()`, etc. matched. The handler is similar to the one in\n`.caseWithAction()`. Note that `.default()` ends the chain and internally does\nthe same as [`.build()`](#build), because it is not intended that the chain be\nmutated after calling `.default()`.\n\nThis is useful if you have a \"delegate\" reducer that should be called on any\naction after handling a few specific actions in the parent.\n\n```ts\nconst NESTED_STATE = {\n    someProp: \"hello\",\n};\n\nconst nestedReducer = reducerWithInitialState(NESTED_STATE)\n    .case(...);\n\nconst INITIAL_STATE = {\n    someOtherProp: \"world\"\n    nested: NESTED_STATE\n};\n\nconst reducer = reducerWithInitialState(INITIAL_STATE)\n    .case(...)\n    .default((state, action) => ({\n        ...state,\n        nested: nestedReducer(state.nested, action),\n    }));\n```\n\n#### `.build()`\n\nReturns a plain reducer function whose behavior matches the current state of the\nreducer chain. Further updates to the chain (through calls to `.case()`) will\nhave no effect on this function.\n\nThere are two reasons you may want to do this:\n\n1.  **You want to ensure that the reducer is not modified further**\n\n    Calling `.build()` is an example of defensive coding. It prevents someone\n    from causing confusing behavior by importing your reducer in an unrelated\n    file and adding cases to it.\n\n2.  **You want your package to export a reducer, but not have its types depend\n    on `typescript-fsa-reducers`**\n\n    If the code that defines a reducer and the code that uses it reside in\n    separate NPM packages, you may run into type errors since the exported\n    reducer has type `ReducerBuilder`, which the consuming package does not\n    recognize unless it also depends on `typescript-fsa-reducers`. This is\n    avoided by calling `.build()`, whose return type is a plain function\n    instead.\n\nExample usage:\n\n```javascript\nconst reducer = reducerWithInitialState(INITIAL_STATE)\n    .case(setName, setNameHandler)\n    .case(addBalance, addBalanceHandler)\n    .case(setIsFrozen, setIsFrozenHandler)\n    .build();\n```\n\nCopyright © 2017 David Philipson\n"
  },
  {
    "path": "__tests__/test.ts",
    "content": "import actionCreatorFactory from \"typescript-fsa\";\nimport {\n    ReducerBuilder,\n    reducerWithInitialState,\n    reducerWithoutInitialState,\n    upcastingReducer,\n} from \"../src/index\";\n\nconst actionCreator = actionCreatorFactory();\n\ninterface State {\n    data: string;\n}\n\ninterface StateWithCount extends State {\n    count: number;\n}\n\nconst initialState: State = { data: \"hello\" };\n\nconst defaultHandlerResult: State = { ...initialState, data: \"world\" };\n\nfunction defaultHandler(state: State): State {\n    return {\n        ...state,\n        data: \"world\",\n    };\n}\n\nconst sliceData = actionCreator<number>(\"SLICE_DATA\");\nfunction sliceDataHandler(state: State, fromIndex: number): State {\n    return { data: state.data.slice(fromIndex) };\n}\n\nconst dataToUpperCase = actionCreator<void>(\"DATA_TO_UPPERCASE\");\nfunction dataToUpperCaseHandler(state: State): State {\n    return { data: state.data.toUpperCase() };\n}\n\nconst toBasicState = actionCreator<void>(\"TO_BASIC_STATE\");\nfunction toBasicStateHandler(state: StateWithCount): State {\n    return { data: state.data };\n}\n\ndescribe(\"reducer builder\", () => {\n    it(\"should return a no-op reducer if no cases provided\", () => {\n        const reducer = reducerWithoutInitialState<State>();\n        expect(reducer(initialState, { type: \"UNKNOWN\" })).toBe(initialState);\n    });\n\n    it(\"should execute the default handler if no cases provided\", () => {\n        const reducer = reducerWithoutInitialState<State>().default(\n            defaultHandler,\n        );\n        expect(reducer(initialState, { type: \"UNKNOWN\" })).toEqual(\n            defaultHandlerResult,\n        );\n    });\n\n    it(\"should no-op on unknown actions if cases provided\", () => {\n        const reducer = reducerWithoutInitialState<State>()\n            .case(sliceData, sliceDataHandler)\n            .case(dataToUpperCase, dataToUpperCaseHandler);\n        expect(reducer(initialState, { type: \"UNKNOWN\" })).toBe(initialState);\n    });\n\n    it(\"should execute the default handler on unknown actions if cases provided\", () => {\n        const reducer = reducerWithoutInitialState<State>()\n            .case(sliceData, sliceDataHandler)\n            .case(dataToUpperCase, dataToUpperCaseHandler)\n            .default(defaultHandler);\n        expect(reducer(initialState, { type: \"UNKNOWN\" })).toEqual(\n            defaultHandlerResult,\n        );\n    });\n\n    it(\"should return an initial value if state is undefined if no cases provided\", () => {\n        const reducer = reducerWithInitialState(initialState);\n        expect(reducer(undefined, { type: \"UNKNOWN\" })).toBe(initialState);\n    });\n\n    it(\"should return default handler result if state is undefined if only default handler provided\", () => {\n        const reducer = reducerWithInitialState(initialState).default(\n            defaultHandler,\n        );\n        expect(reducer(undefined, { type: \"UNKNOWN\" })).toEqual(\n            defaultHandlerResult,\n        );\n    });\n\n    it(\"should return an initial value if state is undefined if cases provided\", () => {\n        const reducer = reducerWithInitialState(initialState)\n            .case(sliceData, sliceDataHandler)\n            .case(dataToUpperCase, dataToUpperCaseHandler);\n        expect(reducer(undefined, { type: \"UNKNOWN\" })).toBe(initialState);\n    });\n\n    it(\"should return default handler result if state is undefined if cases and default handler provided\", () => {\n        const reducer = reducerWithInitialState(initialState)\n            .case(sliceData, sliceDataHandler)\n            .case(dataToUpperCase, dataToUpperCaseHandler)\n            .default(defaultHandler);\n        expect(reducer(undefined, { type: \"UNKNOWN\" })).toEqual(\n            defaultHandlerResult,\n        );\n    });\n\n    it(\"should call handler on matching action with single handler\", () => {\n        const reducer = reducerWithoutInitialState<State>().case(\n            sliceData,\n            sliceDataHandler,\n        );\n        expect(reducer(initialState, sliceData(1))).toEqual({ data: \"ello\" });\n    });\n\n    it(\"should call handler on matching action with multiple handlers\", () => {\n        const reducer = reducerWithoutInitialState<State>()\n            .case(sliceData, sliceDataHandler)\n            .case(dataToUpperCase, dataToUpperCaseHandler);\n        expect(reducer(initialState, dataToUpperCase)).toEqual({\n            data: \"HELLO\",\n        });\n    });\n\n    it(\"should call handler on matching action with multiple handlers and default handler\", () => {\n        const reducer = reducerWithoutInitialState<State>()\n            .case(sliceData, sliceDataHandler)\n            .case(dataToUpperCase, dataToUpperCaseHandler)\n            .default(defaultHandler);\n        expect(reducer(initialState, dataToUpperCase)).toEqual({\n            data: \"HELLO\",\n        });\n    });\n\n    it(\"should call full-action handler when using .caseWithAction()\", () => {\n        const reducer = reducerWithInitialState(initialState).caseWithAction(\n            sliceData,\n            (state, action) => ({\n                ...state,\n                data: state.data.slice(action.payload),\n                meta: { author: action.meta && action.meta.author },\n            }),\n        );\n        expect(reducer(undefined, sliceData(1, { author: \"cbrontë\" }))).toEqual(\n            {\n                data: \"ello\",\n                meta: { author: \"cbrontë\" },\n            },\n        );\n    });\n\n    it(\"should call upcasting handler on matching action\", () => {\n        const reducer = upcastingReducer<StateWithCount, State>().case(\n            toBasicState,\n            toBasicStateHandler,\n        );\n        expect(reducer({ data: \"hello\", count: 2 }, toBasicState)).toEqual({\n            data: \"hello\",\n        });\n    });\n\n    it(\"should be able to call nested reducer when using default handler\", () => {\n        const nestedReducer = reducerWithoutInitialState<State>()\n            .case(sliceData, sliceDataHandler)\n            .case(dataToUpperCase, dataToUpperCaseHandler);\n\n        const reducer = reducerWithoutInitialState<{ nested: State }>().default(\n            (state, action) => ({\n                nested: nestedReducer(state.nested, action),\n            }),\n        );\n        expect(reducer({ nested: initialState }, dataToUpperCase)).toEqual({\n            nested: { data: \"HELLO\" },\n        });\n    });\n\n    (() => {\n        // Scope for shared testing values for .cases() and .casesWithAction().\n\n        interface PayloadA {\n            data: string;\n            x: number;\n        }\n        interface PayloadB {\n            data: string;\n            y: number;\n        }\n        interface PayloadC {\n            data: string;\n            z: number;\n        }\n        const actionA = actionCreator<PayloadA>(\"ACTION_A\");\n        const actionB = actionCreator<PayloadB>(\"ACTION_B\");\n        const actionC = actionCreator<PayloadC>(\"ACTION_C\");\n\n        it(\"should call handler on any matching action when using .cases()\", () => {\n            const reducer = reducerWithInitialState(initialState).cases(\n                [actionA, actionB, actionC],\n                (state, payload) => {\n                    return { ...state, data: payload.data };\n                },\n            );\n            expect(\n                reducer(initialState, actionA({ data: \"from A\", x: 0 })),\n            ).toEqual({\n                data: \"from A\",\n            });\n            expect(\n                reducer(initialState, actionB({ data: \"from B\", y: 1 })),\n            ).toEqual({\n                data: \"from B\",\n            });\n            expect(\n                reducer(initialState, actionC({ data: \"from C\", z: 2 })),\n            ).toEqual({\n                data: \"from C\",\n            });\n        });\n\n        it(\"should call handler on any matching action when using .casesWithAction()\", () => {\n            const reducer = reducerWithInitialState(\n                initialState,\n            ).casesWithAction([actionA, actionB, actionC], (state, action) => {\n                return { ...state, data: action.payload.data };\n            });\n            expect(\n                reducer(initialState, actionA({ data: \"from A\", x: 0 })),\n            ).toEqual({\n                data: \"from A\",\n            });\n            expect(\n                reducer(initialState, actionB({ data: \"from B\", y: 1 })),\n            ).toEqual({\n                data: \"from B\",\n            });\n            expect(\n                reducer(initialState, actionC({ data: \"from C\", z: 2 })),\n            ).toEqual({\n                data: \"from C\",\n            });\n        });\n    })();\n\n    it(\"should be mutated by .case()\", () => {\n        const reducer = reducerWithInitialState(initialState);\n        reducer.case(sliceData, sliceDataHandler);\n        reducer.case(dataToUpperCase, dataToUpperCaseHandler);\n        expect(reducer(undefined, sliceData(1))).toEqual({\n            data: \"ello\",\n        });\n    });\n\n    it(\"should apply handling function to itself in .withHandling()\", () => {\n        const handling = (\n            builder: ReducerBuilder<State>,\n        ): ReducerBuilder<State> => builder.case(sliceData, sliceDataHandler);\n        const reducer = reducerWithInitialState(initialState).withHandling(\n            handling,\n        );\n        expect(reducer(undefined, sliceData(1))).toEqual({\n            data: \"ello\",\n        });\n    });\n\n    describe(\".build()\", () => {\n        const reducer = reducerWithInitialState(initialState)\n            .case(sliceData, sliceDataHandler)\n            .case(dataToUpperCase, dataToUpperCaseHandler)\n            .build();\n\n        it(\"should return a function with no extra keys\", () => {\n            expect(Object.keys(reducer)).toEqual([]);\n        });\n\n        it(\"should return a function which behaves like the reducer\", () => {\n            expect(reducer(undefined, sliceData(1))).toEqual({\n                data: \"ello\",\n            });\n        });\n\n        it(\"should return a function that does not mutate if parent builder mutates\", () => {\n            const builder = reducerWithInitialState(initialState);\n            const reducer1 = builder.build();\n            builder.case(sliceData, sliceDataHandler);\n            const reducer2 = builder.build();\n            expect(reducer1(undefined, sliceData(1))).toEqual({\n                data: \"hello\",\n            });\n            expect(reducer2(undefined, sliceData(1))).toEqual({\n                data: \"ello\",\n            });\n        });\n    });\n});\n"
  },
  {
    "path": "jest.config.js",
    "content": "module.exports = {\n    preset: \"ts-jest\",\n    testEnvironment: \"node\",\n};\n"
  },
  {
    "path": "package.json",
    "content": "{\n    \"name\": \"typescript-fsa-reducers\",\n    \"version\": \"1.2.2\",\n    \"description\": \"Fluent syntax for defining typesafe Redux reducers on top of typescript-fsa.\",\n    \"main\": \"dist/index.js\",\n    \"types\": \"dist/index\",\n    \"files\": [\n        \"dist/\"\n    ],\n    \"repository\": {\n        \"type\": \"git\",\n        \"url\": \"git://github.com/dphilipson/typescript-fsa-reducers.git\"\n    },\n    \"keywords\": [\n        \"redux\",\n        \"typescript\",\n        \"action\",\n        \"reducer\",\n        \"builder\"\n    ],\n    \"author\": \"David Philipson <david.philipson@gmail.com> (http://dphil.me)\",\n    \"license\": \"MIT\",\n    \"bugs\": {\n        \"url\": \"https://github.com/dphilipson/typescript-fsa-reducers/issues\"\n    },\n    \"homepage\": \"https://github.com/dphilipson/typescript-fsa-reducers#readme\",\n    \"scripts\": {\n        \"build\": \"yarn run clean && tsc -p tsconfig.build.json\",\n        \"clean\": \"rm -rf dist/*\",\n        \"format-file\": \"prettier --write\",\n        \"format\": \"git ls-files | egrep '\\\\.(js(on)?|scss|tsx?)?$' | xargs yarn run format-file\",\n        \"generate-toc\": \"git ls-files | egrep '\\\\.md$' | xargs scripts/markdown-toc-all.sh\",\n        \"jest\": \"jest\",\n        \"lint-file\": \"tslint\",\n        \"lint\": \"tslint --project .\",\n        \"prepublishOnly\": \"yarn run test && yarn run build\",\n        \"test\": \"yarn run lint && tsc && yarn run jest\"\n    },\n    \"husky\": {\n        \"hooks\": {\n            \"pre-commit\": \"lint-staged\"\n        }\n    },\n    \"lint-staged\": {\n        \"**/*.{js,json}\": [\n            \"yarn run format-file\",\n            \"git add\"\n        ],\n        \"**/*.ts\": [\n            \"yarn run format-file\",\n            \"yarn run lint-file --fix\",\n            \"git add\"\n        ],\n        \"*.md\": [\n            \"./scripts/markdown-toc-all.sh\",\n            \"git add\"\n        ]\n    },\n    \"devDependencies\": {\n        \"@types/jest\": \"^24.0.20\",\n        \"husky\": \"^3.0.9\",\n        \"jest\": \"^24.9.0\",\n        \"lint-staged\": \"^9.4.2\",\n        \"markdown-toc\": \"^1.2.0\",\n        \"prettier\": \"^1.18.2\",\n        \"ts-jest\": \"^24.1.0\",\n        \"tslint\": \"^5.20.0\",\n        \"tslint-config-prettier\": \"^1.18.0\",\n        \"typescript\": \"^3.6.4\",\n        \"typescript-fsa\": \"^3.0.0\"\n    },\n    \"peerDependencies\": {\n        \"typescript-fsa\": \"*\"\n    }\n}\n"
  },
  {
    "path": "scripts/markdown-toc-all.sh",
    "content": "#!/bin/sh\n\n# Needed for lint-staged if multiple .md files are changed since markdown-toc\n# only accepts a single file argument.\nfor var in \"$@\"\ndo\n    yarn markdown-toc -i \"$var\"\ndone\n"
  },
  {
    "path": "src/index.ts",
    "content": "import { Action, ActionCreator, AnyAction } from \"typescript-fsa\";\n\nexport interface ReducerBuilder<InS, OutS = InS, PassedS = InS | undefined> {\n    case<P>(\n        actionCreator: ActionCreator<P>,\n        handler: Handler<InS, OutS, P>,\n    ): ReducerBuilder<InS, OutS, PassedS>;\n    caseWithAction<P>(\n        actionCreator: ActionCreator<P>,\n        handler: Handler<InS, OutS, Action<P>>,\n    ): ReducerBuilder<InS, OutS, PassedS>;\n\n    // cases variadic overloads\n    cases<P1, P2>(\n        actionCreators: [ActionCreator<P1>, ActionCreator<P2>],\n        handler: Handler<InS, OutS, P1 | P2>,\n    ): ReducerBuilder<InS, OutS, PassedS>;\n    cases<P1, P2, P3>(\n        actionCreators: [\n            ActionCreator<P1>,\n            ActionCreator<P2>,\n            ActionCreator<P3>,\n        ],\n        handler: Handler<InS, OutS, P1 | P2 | P3>,\n    ): ReducerBuilder<InS, OutS, PassedS>;\n    cases<P1, P2, P3, P4>(\n        actionCreators: [\n            ActionCreator<P1>,\n            ActionCreator<P2>,\n            ActionCreator<P3>,\n            ActionCreator<P4>,\n        ],\n        handler: Handler<InS, OutS, P1 | P2 | P3 | P4>,\n    ): ReducerBuilder<InS, OutS, PassedS>;\n    cases<P>(\n        actionCreators: Array<ActionCreator<P>>,\n        handler: Handler<InS, OutS, P>,\n    ): ReducerBuilder<InS, OutS, PassedS>;\n\n    // casesWithAction variadic overloads\n    casesWithAction<P1, P2>(\n        actionCreators: [ActionCreator<P1>, ActionCreator<P2>],\n        handler: Handler<InS, OutS, Action<P1 | P2>>,\n    ): ReducerBuilder<InS, OutS, PassedS>;\n    casesWithAction<P1, P2, P3>(\n        actionCreators: [\n            ActionCreator<P1>,\n            ActionCreator<P2>,\n            ActionCreator<P3>,\n        ],\n        handler: Handler<InS, OutS, Action<P1 | P2 | P3>>,\n    ): ReducerBuilder<InS, OutS, PassedS>;\n    casesWithAction<P1, P2, P3, P4>(\n        actionCreators: [\n            ActionCreator<P1>,\n            ActionCreator<P2>,\n            ActionCreator<P3>,\n            ActionCreator<P4>,\n        ],\n        handler: Handler<InS, OutS, Action<P1 | P2 | P3 | P4>>,\n    ): ReducerBuilder<InS, OutS, PassedS>;\n    casesWithAction<P>(\n        actionCreators: Array<ActionCreator<P>>,\n        handler: Handler<InS, OutS, Action<P>>,\n    ): ReducerBuilder<InS, OutS, PassedS>;\n\n    withHandling(\n        updateBuilder: (\n            builder: ReducerBuilder<InS, OutS, PassedS>,\n        ) => ReducerBuilder<InS, OutS, PassedS>,\n    ): ReducerBuilder<InS, OutS, PassedS>;\n\n    // Intentionally avoid AnyAction in return type so packages can export\n    // reducers created using .default() or .build() without consumers requiring\n    // a dependency on typescript-fsa.\n    default(\n        defaultHandler: Handler<InS, OutS, AnyAction>,\n    ): (state: PassedS, action: { type: any }) => OutS;\n    build(): (state: PassedS, action: { type: any }) => OutS;\n    (state: PassedS, action: AnyAction): OutS;\n}\n\nexport type Handler<InS, OutS, P> = (state: InS, payload: P) => OutS;\n\nexport function reducerWithInitialState<S>(initialState: S): ReducerBuilder<S> {\n    return makeReducer<S, S, S | undefined>(initialState);\n}\n\nexport function reducerWithoutInitialState<S>(): ReducerBuilder<S, S, S> {\n    return makeReducer<S, S, S>();\n}\n\nexport function upcastingReducer<InS extends OutS, OutS>(): ReducerBuilder<\n    InS,\n    OutS,\n    InS\n> {\n    return makeReducer<InS, OutS, InS>();\n}\n\nfunction makeReducer<InS, OutS, PassedS>(\n    initialState?: InS,\n): ReducerBuilder<InS, OutS, PassedS> {\n    const handlersByActionType: {\n        [actionType: string]: Handler<InS, OutS, any>;\n    } = {};\n    const reducer = getReducerFunction(\n        initialState,\n        handlersByActionType,\n    ) as ReducerBuilder<InS, OutS, PassedS>;\n\n    reducer.caseWithAction = <P>(\n        actionCreator: ActionCreator<P>,\n        handler: Handler<InS, OutS, Action<P>>,\n    ) => {\n        handlersByActionType[actionCreator.type] = handler;\n        return reducer;\n    };\n\n    reducer.case = <P>(\n        actionCreator: ActionCreator<P>,\n        handler: Handler<InS, OutS, P>,\n    ) =>\n        reducer.caseWithAction(actionCreator, (state, action) =>\n            handler(state, action.payload),\n        );\n\n    reducer.casesWithAction = <P>(\n        actionCreators: Array<ActionCreator<P>>,\n        handler: Handler<InS, OutS, Action<P>>,\n    ) => {\n        for (const actionCreator of actionCreators) {\n            reducer.caseWithAction(actionCreator, handler);\n        }\n        return reducer;\n    };\n\n    reducer.cases = <P>(\n        actionCreators: Array<ActionCreator<P>>,\n        handler: Handler<InS, OutS, P>,\n    ) =>\n        reducer.casesWithAction(actionCreators, (state, action) =>\n            handler(state, action.payload),\n        );\n\n    reducer.withHandling = (\n        updateBuilder: (\n            builder: ReducerBuilder<InS, OutS, PassedS>,\n        ) => ReducerBuilder<InS, OutS, PassedS>,\n    ) => updateBuilder(reducer);\n\n    reducer.default = (defaultHandler: Handler<InS, OutS, AnyAction>) =>\n        getReducerFunction<InS, OutS, PassedS>(\n            initialState,\n            { ...handlersByActionType },\n            defaultHandler,\n        );\n\n    reducer.build = () =>\n        getReducerFunction(initialState, { ...handlersByActionType });\n\n    return reducer;\n}\n\nfunction getReducerFunction<InS, OutS, PassedS>(\n    initialState: InS | undefined,\n    handlersByActionType: { [actionType: string]: Handler<InS, OutS, any> },\n    defaultHandler?: Handler<InS, OutS, AnyAction>,\n) {\n    return (passedState: PassedS, action: AnyAction) => {\n        const state = passedState !== undefined ? passedState : initialState;\n        const handler = handlersByActionType[action.type] || defaultHandler;\n        return handler\n            ? handler(state as InS, action)\n            : ((state as unknown) as OutS);\n    };\n}\n"
  },
  {
    "path": "tsconfig.build.json",
    "content": "{\n    \"compilerOptions\": {\n        \"declaration\": true,\n        \"inlineSources\": true,\n        \"noEmit\": false,\n        \"outDir\": \"dist\",\n        \"sourceMap\": true\n    },\n    \"extends\": \"./tsconfig.json\",\n    \"include\": [\"src/**/*\"]\n}\n"
  },
  {
    "path": "tsconfig.json",
    "content": "{\n    \"compilerOptions\": {\n        \"module\": \"commonjs\",\n        \"noEmit\": true,\n        \"noFallthroughCasesInSwitch\": true,\n        \"noImplicitReturns\": true,\n        \"noUnusedLocals\": true,\n        \"noUnusedParameters\": true,\n        \"skipLibCheck\": true,\n        \"strict\": true,\n        \"target\": \"es5\"\n    },\n    \"include\": [\"src/**/*\", \"__tests__/**/*\"]\n}\n"
  },
  {
    "path": "tslint.json",
    "content": "{\n    \"extends\": [\"tslint:latest\", \"tslint-config-prettier\"],\n    \"rules\": {\n        \"interface-name\": [true, \"never-prefix\"],\n        \"no-bitwise\": false,\n        \"no-submodule-imports\": false,\n        \"object-literal-sort-keys\": false\n    }\n}\n"
  }
]