reducer-composer
Advanced tools
Comparing version 0.0.2 to 0.0.4
@@ -1,2 +0,3 @@ | ||
export declare function createReducer<State>(): <Handlers extends Record<string, (state: State, payload: any) => State>>(handlers: Handlers) => (state: State, action: { [Type in keyof Handlers]: Parameters<Handlers[Type]>[1] extends undefined ? { | ||
import { DeepReadonly } from "./utility"; | ||
export declare function createReducer<State>(): <Handlers extends Record<string, (state: DeepReadonly<State>, payload: any) => DeepReadonly<State>>>(handlers: Handlers) => (state: DeepReadonly<State>, action: { [Type in keyof Handlers]: Parameters<Handlers[Type]>[1] extends undefined ? { | ||
type: Type; | ||
@@ -7,2 +8,2 @@ payload?: undefined; | ||
payload: Parameters<Handlers[Type]>[1]; | ||
}; }[keyof Handlers]) => State; | ||
}; }[keyof Handlers]) => DeepReadonly<State>; |
@@ -6,3 +6,3 @@ "use strict"; | ||
return function (state, action) { | ||
return action.type in handlers | ||
return Object.prototype.hasOwnProperty.call(handlers, action.type) | ||
? handlers[action.type](state, action.payload) | ||
@@ -9,0 +9,0 @@ : state; |
export declare function createReducerCrudHandlers<Entity, EntityPayload, KeyPayload>(entitySelector: (payload: EntityPayload) => [string, Entity], keySelector: (payload: KeyPayload) => string): { | ||
create: (record: Record<string, Entity>, payload: EntityPayload) => Record<string, Entity>; | ||
update: (record: Record<string, Entity>, payload: EntityPayload) => Record<string, Entity>; | ||
delete: (record: Record<string, Entity>, payload: KeyPayload) => Record<string, Entity>; | ||
upsert: (record: Record<string, Entity>, payload: EntityPayload) => Record<string, Entity>; | ||
discard: (record: Record<string, Entity>, payload: KeyPayload) => Record<string, Entity>; | ||
create: (record: import("./utility").DeepReadonlyObject<Record<string, Entity>>, payload: EntityPayload) => import("./utility").DeepReadonlyObject<Record<string, Entity>>; | ||
update: (record: import("./utility").DeepReadonlyObject<Record<string, Entity>>, payload: EntityPayload) => import("./utility").DeepReadonlyObject<Record<string, Entity>>; | ||
delete: (record: import("./utility").DeepReadonlyObject<Record<string, Entity>>, payload: KeyPayload) => import("./utility").DeepReadonlyObject<Record<string, Entity>>; | ||
upsert: (record: import("./utility").DeepReadonlyObject<Record<string, Entity>>, payload: EntityPayload) => import("./utility").DeepReadonlyObject<Record<string, Entity>>; | ||
discard: (record: import("./utility").DeepReadonlyObject<Record<string, Entity>>, payload: KeyPayload) => import("./utility").DeepReadonlyObject<Record<string, Entity>>; | ||
}; |
@@ -25,3 +25,4 @@ "use strict"; | ||
var withEntity = function (f) { return function (record, payload) { | ||
return f.apply(void 0, [record].concat(entitySelector(payload))); | ||
var _a = entitySelector(payload), key = _a[0], entity = _a[1]; | ||
return f(record, key, entity); | ||
}; }; | ||
@@ -34,3 +35,3 @@ var withKey = function (f) { return function (record, payload) { | ||
var _a; | ||
if (key in state) { | ||
if (Object.prototype.hasOwnProperty.call(state, key)) { | ||
throw new Error(); | ||
@@ -42,3 +43,3 @@ } | ||
var _a; | ||
if (key in state) { | ||
if (Object.prototype.hasOwnProperty.call(state, key)) { | ||
return __assign({}, state, (_a = {}, _a[key] = entity, _a)); | ||
@@ -49,3 +50,3 @@ } | ||
delete: withKey(function (state, key) { | ||
if (key in state) { | ||
if (Object.prototype.hasOwnProperty.call(state, key)) { | ||
var _a = key, removed = state[_a], rest = __rest(state, [typeof _a === "symbol" ? _a : _a + ""]); | ||
@@ -52,0 +53,0 @@ return rest; |
@@ -0,11 +1,12 @@ | ||
import { DeepReadonly } from "./utility"; | ||
export declare function createReducerOnAction<State, Action extends { | ||
type: string; | ||
payload?: any; | ||
}>(): <Handlers extends { [Type in Action["type"]]: (state: State, payload: Extract<Action, { | ||
}>(): <Handlers extends { [Type in Action["type"]]: (state: DeepReadonly<State>, payload: Extract<Action, { | ||
type: Type; | ||
}>["payload"]) => State; }>(handlers: Handlers) => (state: State, action: { [Type in keyof { [Type in keyof Handlers]: (state: State, payload: Extract<Action, { | ||
}>["payload"]) => DeepReadonly<State>; }>(handlers: Handlers) => (state: DeepReadonly<State>, action: { [Type in keyof { [Type in keyof Handlers]: (state: DeepReadonly<State>, payload: Extract<Action, { | ||
type: Type; | ||
}>["payload"]) => State; }]: Parameters<{ [Type in keyof Handlers]: (state: State, payload: Extract<Action, { | ||
}>["payload"]) => DeepReadonly<State>; }]: Parameters<{ [Type in keyof Handlers]: (state: DeepReadonly<State>, payload: Extract<Action, { | ||
type: Type; | ||
}>["payload"]) => State; }[Type]>[1] extends undefined ? { | ||
}>["payload"]) => DeepReadonly<State>; }[Type]>[1] extends undefined ? { | ||
type: Type; | ||
@@ -15,5 +16,5 @@ payload?: undefined; | ||
type: Type; | ||
payload: Parameters<{ [Type in keyof Handlers]: (state: State, payload: Extract<Action, { | ||
payload: Parameters<{ [Type in keyof Handlers]: (state: DeepReadonly<State>, payload: Extract<Action, { | ||
type: Type; | ||
}>["payload"]) => State; }[Type]>[1]; | ||
}; }[keyof Handlers]) => State; | ||
}>["payload"]) => DeepReadonly<State>; }[Type]>[1]; | ||
}; }[keyof Handlers]) => DeepReadonly<State>; |
@@ -14,4 +14,3 @@ "use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
var _1 = require("./"); | ||
var createReducerCrudHandlers_1 = require("./createReducerCrudHandlers"); | ||
var reducer_composer_1 = require("reducer-composer"); | ||
var userInitialState = { | ||
@@ -22,3 +21,3 @@ id: "", | ||
}; | ||
var userReducer = _1.createReducer()({ | ||
var userReducer = reducer_composer_1.createReducer()({ | ||
USER_CHANGE_NAME: function (user, _a) { | ||
@@ -42,3 +41,3 @@ var name = _a.name; | ||
var clickInitialState = 0; | ||
var clickReducer = _1.createReducer()({ | ||
var clickReducer = reducer_composer_1.createReducer()({ | ||
CLICK: function (clicks) { | ||
@@ -55,3 +54,3 @@ return clicks + 1; | ||
// createCombinedReducer | ||
var userClickReducer = _1.createCombinedReducer({ | ||
var userClickReducer = reducer_composer_1.createCombinedReducer({ | ||
user: userReducer, | ||
@@ -72,7 +71,7 @@ click: clickReducer | ||
}); | ||
var nestedUserClickReducer = _1.createCombinedReducer({ | ||
var nestedUserClickReducer = reducer_composer_1.createCombinedReducer({ | ||
nested: userClickReducer | ||
}); | ||
// createBatchReducer | ||
var userClickBatchReducer = _1.createBatchReducer("CLICKS", userClickReducer); | ||
var userClickBatchReducer = reducer_composer_1.createBatchReducer("CLICKS", userClickReducer); | ||
userClickBatchReducer(userClickInitialState, { type: "CLICK" }); | ||
@@ -87,7 +86,7 @@ userClickBatchReducer(userClickInitialState, { | ||
// keyedReducer | ||
var usersReducer = _1.createKeyedReducer("id", userReducer); | ||
var usersReducer = reducer_composer_1.createKeyedReducer("id", userReducer); | ||
usersReducer({}, { type: "USER_CHANGE_NAME", payload: { id: "42", name: "John Doe" } }); | ||
// createReducerSequence | ||
var usersInitialState = {}; | ||
var usersReducerWithAdd = _1.createReducerSequence(usersReducer, _1.createReducer()({ | ||
var usersReducerWithAdd = reducer_composer_1.createReducerSequence(usersReducer, reducer_composer_1.createReducer()({ | ||
USER_ADD: function (state, user) { | ||
@@ -100,3 +99,3 @@ var _a; | ||
usersReducerWithAdd({}, { type: "USER_ADD", payload: { id: "42", name: "John", birth: new Date() } }); | ||
var multitenantUsersReducer = _1.createKeyedReducer("company", usersReducerWithAdd); | ||
var multitenantUsersReducer = reducer_composer_1.createKeyedReducer("company", usersReducerWithAdd); | ||
multitenantUsersReducer({}, { | ||
@@ -110,8 +109,8 @@ type: "USER_ADD", | ||
}); | ||
var usersActionCounterReducer = _1.createReducerOnAction()({ | ||
var usersActionCounterReducer = reducer_composer_1.createReducerOnAction()({ | ||
USER_ADD: function (count) { | ||
return count + 1; | ||
}, | ||
USER_CHANGE_BIRTH: _1.ignore, | ||
USER_CHANGE_NAME: _1.ignore | ||
USER_CHANGE_BIRTH: reducer_composer_1.ignore, | ||
USER_CHANGE_NAME: reducer_composer_1.ignore | ||
}); | ||
@@ -122,4 +121,4 @@ usersActionCounterReducer(0, { | ||
}); | ||
var carCrud = createReducerCrudHandlers_1.createReducerCrudHandlers(function (payload) { return [payload.id, payload]; }, function (payload) { return payload.id; }); | ||
var carReducer = _1.createReducer()({ | ||
var carCrud = reducer_composer_1.createReducerCrudHandlers(function (payload) { return [payload.id, payload]; }, function (payload) { return payload.id; }); | ||
var carReducer = reducer_composer_1.createReducer()({ | ||
CAR_ADD: carCrud.create, | ||
@@ -129,3 +128,3 @@ CAR_UPDATE: carCrud.update, | ||
}); | ||
var rentReducer = _1.createReducer()({ | ||
var rentReducer = reducer_composer_1.createReducer()({ | ||
RENT_CHANGE_USER: function (state, _a) { | ||
@@ -136,13 +135,13 @@ var user = _a.user; | ||
}); | ||
var rentKeyedReducer = _1.createKeyedReducer("id", rentReducer); | ||
var rentsCrud = createReducerCrudHandlers_1.createReducerCrudHandlers(function (rent) { return [rent.id, rent]; }, function (_a) { | ||
var rentKeyedReducer = reducer_composer_1.createKeyedReducer("id", rentReducer); | ||
var rentsCrud = reducer_composer_1.createReducerCrudHandlers(function (rent) { return [rent.id, rent]; }, function (_a) { | ||
var id = _a.id; | ||
return id; | ||
}); | ||
var rentsCrudReducer = _1.createReducer()({ | ||
var rentsCrudReducer = reducer_composer_1.createReducer()({ | ||
RENT: rentsCrud.create, | ||
RENT_CANCEL: rentsCrud.delete | ||
}); | ||
var rentsReducer = _1.createReducerSequence(rentKeyedReducer, rentsCrudReducer); | ||
var carRentCompanyReducer = _1.createCombinedReducer({ | ||
var rentsReducer = reducer_composer_1.createReducerSequence(rentKeyedReducer, rentsCrudReducer); | ||
var carRentCompanyReducer = reducer_composer_1.createCombinedReducer({ | ||
users: usersReducerWithAdd, | ||
@@ -198,3 +197,3 @@ cars: carReducer, | ||
} | ||
var precondition = _1.createReducerOnAction()({ | ||
var precondition = reducer_composer_1.createReducerOnAction()({ | ||
CAR_DESTROY: function (state, _a) { | ||
@@ -214,13 +213,13 @@ var id = _a.id; | ||
}, | ||
CAR_ADD: _1.ignore, | ||
CAR_UPDATE: _1.ignore, | ||
RENT: _1.ignore, | ||
RENT_CANCEL: _1.ignore, | ||
RENT_CHANGE_USER: _1.ignore, | ||
USER_ADD: _1.ignore, | ||
USER_CHANGE_BIRTH: _1.ignore, | ||
USER_CHANGE_NAME: _1.ignore | ||
CAR_ADD: reducer_composer_1.ignore, | ||
CAR_UPDATE: reducer_composer_1.ignore, | ||
RENT: reducer_composer_1.ignore, | ||
RENT_CANCEL: reducer_composer_1.ignore, | ||
RENT_CHANGE_USER: reducer_composer_1.ignore, | ||
USER_ADD: reducer_composer_1.ignore, | ||
USER_CHANGE_BIRTH: reducer_composer_1.ignore, | ||
USER_CHANGE_NAME: reducer_composer_1.ignore | ||
}); | ||
var safeCarRentCompanyReducer = _1.createReducerSequence(precondition, carRentCompanyReducer, postCondition); | ||
var transactionalSafeCarRentCompanyReducer = _1.createBatchReducer("CAR_TRANSACTION", safeCarRentCompanyReducer); | ||
var safeCarRentCompanyReducer = reducer_composer_1.createReducerSequence(precondition, carRentCompanyReducer, postCondition); | ||
var transactionalSafeCarRentCompanyReducer = reducer_composer_1.createBatchReducer("CAR_TRANSACTION", safeCarRentCompanyReducer); | ||
transactionalSafeCarRentCompanyReducer(carRentCompanyInitialState, { | ||
@@ -227,0 +226,0 @@ type: "CAR_TRANSACTION", |
@@ -10,1 +10,7 @@ export declare type Values<O> = O[keyof O]; | ||
export declare function get<Attribute extends string>(attribute: Attribute): <Value>(object: { [K in Attribute]: Value; }) => { [K in Attribute]: Value; }[Attribute]; | ||
export interface DeepReadonlyArray<A> extends ReadonlyArray<DeepReadonly<A>> { | ||
} | ||
export declare type DeepReadonlyObject<A> = { | ||
readonly [K in keyof A]: DeepReadonly<A[K]>; | ||
}; | ||
export declare type DeepReadonly<A> = A extends Array<infer B> ? DeepReadonlyArray<B> : DeepReadonlyObject<A>; |
@@ -11,3 +11,3 @@ { | ||
], | ||
"version": "0.0.2", | ||
"version": "0.0.4", | ||
"license": "MIT", | ||
@@ -14,0 +14,0 @@ "author": "Frederik Batuna", |
# reducer-composer | ||
compose your reducers effortlessly, with intellisense and strict type-checking | ||
Compose your reducers **effortlessly**, with **intellisense** and **strict type-checking**. | ||
## [Usage](src/usage.ts) | ||
## Usage | ||
[![Edit reducer-composer](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/s/52zq6r02ln?hidenavigation=1&view=editor) | ||
```typescript | ||
import { | ||
createReducer, | ||
createCombinedReducer, | ||
createReducerSequence, | ||
createBatchReducer, | ||
createKeyedReducer, | ||
createReducerCrudHandlers, | ||
createReducerOnAction, | ||
ActionOfReducer, | ||
ignore | ||
} from "reducer-composer"; | ||
interface User { | ||
id: string; | ||
name: string; | ||
birth: Date; | ||
} | ||
const user = createReducer<User>()({ | ||
USER_CHANGE_NAME(user, { name }: { name: string }) { | ||
return { ...user, name }; | ||
}, | ||
USER_CHANGE_BIRTH(user, { birth }: { birth: Date }) { | ||
return { ...user, birth }; | ||
} | ||
}); | ||
const click = createReducer<number>()({ | ||
CLICK(clicks) { | ||
return clicks + 1; | ||
}, | ||
CLICK_SET(_, { clicks }: { clicks: number }) { | ||
return clicks; | ||
} | ||
}); | ||
const userClick = createCombinedReducer({ | ||
user, | ||
click | ||
}); | ||
const userClickBatchReducer = createBatchReducer("CLICKS", userClick); | ||
const usersReducer = createKeyedReducer("id", user); | ||
const companyUsers = createReducerSequence( | ||
usersReducer, | ||
createReducer<Record<string, User>>()({ | ||
USER_ADD(state, user: User) { | ||
return { ...state, [user.id]: user }; | ||
} | ||
}) | ||
); | ||
const usersCounter = createReducerOnAction< | ||
number, | ||
ActionOfReducer<typeof companyUsers> | ||
>()({ | ||
USER_ADD(count) { | ||
return count + 1; | ||
}, | ||
USER_CHANGE_BIRTH: ignore, | ||
USER_CHANGE_NAME: ignore | ||
}); | ||
interface Car { | ||
id: string; | ||
name: string; | ||
km: number; | ||
} | ||
const carCrud = createReducerCrudHandlers( | ||
(payload: Car) => [payload.id, payload], | ||
(payload: { id: string }) => payload.id | ||
); | ||
const car = createReducer<Record<string, Car>>()({ | ||
CAR_ADD: carCrud.create, | ||
CAR_UPDATE: carCrud.update, | ||
CAR_DESTROY: carCrud.discard | ||
}); | ||
``` | ||
[more usage](src/usage.ts) | ||
## License MIT |
@@ -1,6 +0,9 @@ | ||
import { Values } from "./utility"; | ||
import { Values, DeepReadonly } from "./utility"; | ||
export function createReducer<State>() { | ||
return < | ||
Handlers extends Record<string, (state: State, payload: any) => State> | ||
Handlers extends Record< | ||
string, | ||
(state: DeepReadonly<State>, payload: any) => DeepReadonly<State> | ||
> | ||
>( | ||
@@ -10,3 +13,3 @@ handlers: Handlers | ||
return ( | ||
state: State, | ||
state: DeepReadonly<State>, | ||
action: Values< | ||
@@ -22,3 +25,3 @@ { | ||
) => | ||
action.type in handlers | ||
Object.prototype.hasOwnProperty.call(handlers, action.type) | ||
? handlers[action.type](state, action.payload) | ||
@@ -25,0 +28,0 @@ : state; |
@@ -0,1 +1,3 @@ | ||
import { DeepReadonly } from "./utility"; | ||
export function createReducerCrudHandlers<Entity, EntityPayload, KeyPayload>( | ||
@@ -7,15 +9,23 @@ entitySelector: (payload: EntityPayload) => [string, Entity], | ||
f: ( | ||
record: Record<string, Entity>, | ||
record: DeepReadonly<Record<string, Entity>>, | ||
key: string, | ||
entity: Entity | ||
) => Record<string, Entity> | ||
) => (record: Record<string, Entity>, payload: EntityPayload) => | ||
f(record, ...entitySelector(payload)); | ||
entity: DeepReadonly<Entity> | ||
) => DeepReadonly<Record<string, Entity>> | ||
) => ( | ||
record: DeepReadonly<Record<string, Entity>>, | ||
payload: EntityPayload | ||
) => { | ||
const [key, entity] = entitySelector(payload); | ||
return f(record, key, (entity as unknown) as DeepReadonly<Entity>); | ||
}; | ||
const withKey = ( | ||
f: (record: Record<string, Entity>, key: string) => Record<string, Entity> | ||
) => (record: Record<string, Entity>, payload: KeyPayload) => | ||
f: ( | ||
record: DeepReadonly<Record<string, Entity>>, | ||
key: string | ||
) => DeepReadonly<Record<string, Entity>> | ||
) => (record: DeepReadonly<Record<string, Entity>>, payload: KeyPayload) => | ||
f(record, keySelector(payload)); | ||
return { | ||
create: withEntity((state, key, entity) => { | ||
if (key in state) { | ||
if (Object.prototype.hasOwnProperty.call(state, key)) { | ||
throw new Error(); | ||
@@ -26,3 +36,3 @@ } | ||
update: withEntity((state, key, entity) => { | ||
if (key in state) { | ||
if (Object.prototype.hasOwnProperty.call(state, key)) { | ||
return { ...state, [key]: entity }; | ||
@@ -33,5 +43,5 @@ } | ||
delete: withKey((state, key) => { | ||
if (key in state) { | ||
if (Object.prototype.hasOwnProperty.call(state, key)) { | ||
const { [key]: removed, ...rest } = state; | ||
return rest as Record<string, Entity>; | ||
return rest as DeepReadonly<Record<string, Entity>>; | ||
} | ||
@@ -45,5 +55,5 @@ throw new Error(); | ||
const { [key]: removed, ...rest } = state; | ||
return rest as Record<string, Entity>; | ||
return rest as DeepReadonly<Record<string, Entity>>; | ||
}) | ||
}; | ||
} |
import { createReducer } from "./createReducer"; | ||
import { DeepReadonly } from "./utility"; | ||
@@ -10,5 +11,5 @@ export function createReducerOnAction< | ||
[Type in Action["type"]]: ( | ||
state: State, | ||
state: DeepReadonly<State>, | ||
payload: Extract<Action, { type: Type }>["payload"] | ||
) => State | ||
) => DeepReadonly<State> | ||
} | ||
@@ -20,6 +21,6 @@ >( | ||
[Type in keyof Handlers]: ( | ||
state: State, | ||
state: DeepReadonly<State>, | ||
payload: Extract<Action, { type: Type }>["payload"] | ||
) => State | ||
) => DeepReadonly<State> | ||
}); | ||
} |
@@ -11,5 +11,5 @@ import { | ||
createBatchReducer, | ||
ignore | ||
} from "./"; | ||
import { createReducerCrudHandlers } from "./createReducerCrudHandlers"; | ||
ignore, | ||
createReducerCrudHandlers | ||
} from "reducer-composer"; | ||
@@ -188,5 +188,5 @@ // createReducer | ||
const rentKeyedReducer = createKeyedReducer("id", rentReducer); | ||
const rentsCrud = createReducerCrudHandlers( | ||
(rent: Rent) => [rent.id, rent], | ||
({ id }: { id: string }) => id | ||
const rentsCrud = createReducerCrudHandlers<Rent, Rent, { id: string }>( | ||
rent => [rent.id, rent], | ||
({ id }) => id | ||
); | ||
@@ -193,0 +193,0 @@ const rentsCrudReducer = createReducer<Record<string, Rent>>()({ |
@@ -17,1 +17,11 @@ export type Values<O> = O[keyof O]; | ||
} | ||
export interface DeepReadonlyArray<A> extends ReadonlyArray<DeepReadonly<A>> {} | ||
export type DeepReadonlyObject<A> = { | ||
readonly [K in keyof A]: DeepReadonly<A[K]> | ||
}; | ||
export type DeepReadonly<A> = A extends Array<infer B> | ||
? DeepReadonlyArray<B> | ||
: DeepReadonlyObject<A>; |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
51099
41
1350
94