redux-sacala 0.3.0 → 0.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +88 -0
- package/build/redux-sacala.cjs +1 -1
- package/build/redux-sacala.cjs.map +1 -1
- package/build/redux-sacala.d.ts +49 -22
- package/build/redux-sacala.js +67 -46
- package/build/redux-sacala.js.map +1 -1
- package/package.json +9 -7
- package/.github/workflows/test.yml +0 -30
package/README.md
CHANGED
|
@@ -7,6 +7,7 @@ A library for creating composable Redux blocks with state, actions, and effects.
|
|
|
7
7
|
- **ReduxBlock**: A composable unit of Redux logic that encapsulates state, action creators, and effect handlers. It provides a structured way to define how state changes and how side effects are handled.
|
|
8
8
|
- **Action**: A pure function that describes how the state changes in response to an event. In `redux-sacala`, actions are defined using `.action()` and they also serve as action creators.
|
|
9
9
|
- **Effect**: A non-pure handler that can perform side effects such as asynchronous API calls, logging, or dispatching other actions. Effects are defined using `.effects()` and have access to a context object providing necessary dependencies.
|
|
10
|
+
- **Selector**: A pure function that takes the state and returns a derived value. Selectors are defined using `.selectors()` and can be accessed via `.select`. For memoization, it is recommended to use `createSelector` from `@reduxjs/toolkit`. In compositions, selectors from child blocks are automatically "lifted" and available under the block's name.
|
|
10
11
|
|
|
11
12
|
## Examples
|
|
12
13
|
|
|
@@ -79,6 +80,93 @@ const rootBlock = ReduxBlock.composition("root")
|
|
|
79
80
|
// rootBlock.actions.logAndIncrement()
|
|
80
81
|
```
|
|
81
82
|
|
|
83
|
+
### Selectors
|
|
84
|
+
|
|
85
|
+
Selectors allow you to extract and derive data from the state. They are defined using `.selectors()` and are available on the `.select` property of the built block.
|
|
86
|
+
|
|
87
|
+
```typescript
|
|
88
|
+
const counterBlock = ReduxBlock.builder("counter", { count: 0 })
|
|
89
|
+
.action("inc", (state) => ({ count: state.count + 1 }))
|
|
90
|
+
.selectors({
|
|
91
|
+
count: (state) => state.count,
|
|
92
|
+
doubleCount: (state) => state.count * 2,
|
|
93
|
+
})
|
|
94
|
+
.build();
|
|
95
|
+
|
|
96
|
+
// Usage:
|
|
97
|
+
// counterBlock.select.count({ count: 5 }) -> 5
|
|
98
|
+
// counterBlock.select.doubleCount({ count: 5 }) -> 10
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
For complex or expensive derivations, it is recommended to use `createSelector` from `@reduxjs/toolkit` (or `reselect`) to enable memoization:
|
|
102
|
+
|
|
103
|
+
```typescript
|
|
104
|
+
import { createSelector } from '@reduxjs/toolkit';
|
|
105
|
+
|
|
106
|
+
const counterBlock = ReduxBlock.builder("counter", { count: 0 })
|
|
107
|
+
.selectors({
|
|
108
|
+
count: (state) => state.count,
|
|
109
|
+
// Memoized selector using createSelector
|
|
110
|
+
tripleCount: createSelector(
|
|
111
|
+
[(state: { count: number }) => state.count],
|
|
112
|
+
(count) => count * 3
|
|
113
|
+
),
|
|
114
|
+
})
|
|
115
|
+
.build();
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
When blocks are composed, their selectors are automatically "lifted" to work with the composition's state.
|
|
119
|
+
|
|
120
|
+
```typescript
|
|
121
|
+
const rootBlock = ReduxBlock.composition("root")
|
|
122
|
+
.block("counter", counterBlock)
|
|
123
|
+
.selectors({
|
|
124
|
+
isPositive: (state) => state.counter.count > 0,
|
|
125
|
+
})
|
|
126
|
+
.build();
|
|
127
|
+
|
|
128
|
+
// Lifted selector from counterBlock:
|
|
129
|
+
// rootBlock.select.counter.count({ counter: { count: 5 } }) -> 5
|
|
130
|
+
|
|
131
|
+
// Composition selector:
|
|
132
|
+
// rootBlock.select.isPositive({ counter: { count: 5 } }) -> true
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
### Context Mapping
|
|
136
|
+
|
|
137
|
+
You can change the context shape of a block using `ReduxBlock.mapContext`. This is useful when you want to adapt a block to a different environment or use a more convenient context structure.
|
|
138
|
+
|
|
139
|
+
```typescript
|
|
140
|
+
interface OldContext {
|
|
141
|
+
log: {
|
|
142
|
+
error: (msg: string) => void;
|
|
143
|
+
info: (msg: string) => void;
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const block = ReduxBlock.builder("test", { message: "" })
|
|
148
|
+
.effects((ctx: OldContext) => ({
|
|
149
|
+
logError: (msg: string) => ctx.log.error(msg),
|
|
150
|
+
}))
|
|
151
|
+
.build();
|
|
152
|
+
|
|
153
|
+
interface NewContext {
|
|
154
|
+
log: (level: "error" | "info", msg: string) => void;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const mappedBlock = ReduxBlock.mapContext(
|
|
158
|
+
block,
|
|
159
|
+
(ctx: NewContext): OldContext => ({
|
|
160
|
+
log: {
|
|
161
|
+
error: (msg) => ctx.log("error", msg),
|
|
162
|
+
info: (msg) => ctx.log("info", msg),
|
|
163
|
+
},
|
|
164
|
+
}),
|
|
165
|
+
);
|
|
166
|
+
|
|
167
|
+
// Now mappedBlock expects NewContext
|
|
168
|
+
```
|
|
169
|
+
|
|
82
170
|
### Minimal Redux Toolkit Example
|
|
83
171
|
|
|
84
172
|
```typescript
|
package/build/redux-sacala.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});function
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});function f(c,e){return typeof c=="object"&&c!==null&&Object.hasOwn(c,e)}const d=c=>new Proxy({},{get(e,t){return f(e,t)?e[t]:(...r)=>{const i=`${c}/${t}`;return r.length?{type:i,payload:r}:{type:i}}}});function b(c,e){return typeof c=="function"?(t=>c(e(t))):Object.fromEntries(Object.entries(c).map(([t,r])=>[t,b(r,e)]))}class h{constructor(e,t,r,i,s){this.name=e,this.initial=t,this.reducers=r,this.handlers=i,this.select=s}static init(e,t){return new h(e,t,{},[],{})}action(e,t){return this.reducers[`${this.name}/${e}`]=t,this}effects(e){return this.handlers.push(e),this}selectors(e){return Object.assign(this.select,e),this}build(){const e=this.initial,t=this.name;return{actions:d(this.name),effects:r=>this.handlers.reduce((i,s)=>Object.assign(i,Object.fromEntries(Object.entries(s(r)).map(([n,o])=>[`${t}/${n}`,o]))),{}),reducer:(r=e,i)=>{const s=this.reducers[i.type];if(!s)return r;const n="payload"in i?i.payload:void 0;return n&&n.length?s(r,...n):s(r)},select:this.select}}}class a{constructor(e){this.name=e,this.creators=d(e)}blocks={};handlers=[];creators;select={};static init(e){return new a(e)}block(e,t){return this.blocks[e]=t,this.creators[e]=t.actions,this.select[e]=b(t.select,r=>r[e]),this}selectors(e){return Object.assign(this.select,e),this}effects(e){return this.handlers.push(e),this}build(){const e=Object.entries(this.blocks).map(([t,r])=>[t,r.reducer]);return{actions:this.creators,effects:t=>{const r=this.name,i=this.handlers.reduce((s,n)=>Object.assign(s,Object.fromEntries(Object.entries(n(t)).map(([o,l])=>[`${r}/${o}`,l]))),{});return Object.values(this.blocks).forEach(s=>Object.assign(i,s.effects(t))),i},reducer:(t,r)=>{let i=t,s=!1;return e.forEach(([n,o])=>{const l=t?.[n],u=o(l,r);u!==l&&(s||(s=!0,i={...t}),i[n]=u)}),i},select:this.select}}}exports.ReduxBlock=void 0;(c=>{function e(s,n){return h.init(s,n)}c.builder=e;function t(s){return a.init(s)}c.composition=t;function r(s,n){const o=s.effects(n);return()=>l=>u=>{u&&typeof u=="object"&&"type"in u&&f(o,u.type)&&o[u.type](..."payload"in u?u.payload:[]),l(u)}}c.middleware=r;function i(s,n){return{actions:s.actions,reducer:s.reducer,effects:o=>s.effects(n(o)),select:s.select}}c.mapContext=i})(exports.ReduxBlock||(exports.ReduxBlock={}));
|
|
2
2
|
//# sourceMappingURL=redux-sacala.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"redux-sacala.cjs","sources":["../src/redux-sacala.ts"],"sourcesContent":["import { Middleware, Reducer, UnknownAction } from \"redux\";\n\n/**\n * Composable Redux block with state description, action creators, and effects handlers.\n * Use `ReduxBlock.builder` to start building a new block.\n */\nexport interface ReduxBlock<State, Creators, Context> {\n /**\n * Action creators for this block.\n * When composed, action creators can form a folder tree structure.\n */\n actions: Creators;\n /**\n * Reducer that can be used directly in Redux store configuration.\n */\n reducer: Reducer<State, UnknownAction>;\n /**\n * Effects to be called on effects actions.\n * Use `ReduxBlock.middleware` to create middleware for effects processing.\n */\n effects: Effects<Context>;\n}\n\ntype PayloadAction<Type extends string, Payload extends unknown[]> = Payload extends never[]\n ? { type: Type }\n : { type: Type; payload: Payload };\n\n/**\n * Checks if the given key exists directly on the provided object.\n */\nfunction has<K extends string | symbol>(v: unknown, k: K): v is Record<K, unknown> {\n return typeof v === \"object\" && v !== null && Object.hasOwn(v, k);\n}\n\nconst creator = (scope: string) =>\n new Proxy(\n {},\n {\n get(target, property) {\n if (has(target, property)) {\n return target[property];\n }\n\n return (...payload: unknown[]) => {\n const type = `${scope}/${property as string}`;\n return payload.length ? { type, payload } : { type };\n };\n },\n },\n ) as Record<string, (...payload: unknown[]) => UnknownAction>;\n\n/**\n * Effects creator. It receives context with side effect APIs and returns non-pure handlers.\n */\ntype Effects<Context> = (context: Context) => Record<string, (...payload: any[]) => void>;\n\n/**\n * Convert effects type to action creators.\n */\ntype EffectsToCreators<Name extends string, E extends Effects<any>> = {\n [K in keyof ReturnType<E>]: (\n ...parameters: Parameters<ReturnType<E>[K]>\n ) => PayloadAction<`${Name}/${K extends string ? K : never}`, Parameters<ReturnType<E>[K]>>;\n};\n\nclass BlockBuilder<\n Name extends string,\n State,\n Creators extends Record<string, (...parameters: unknown[]) => PayloadAction<any, any>>,\n Context,\n> {\n private constructor(\n readonly name: Name,\n readonly initial: State,\n /**\n * Per-message action reducers for this block.\n */\n private readonly reducers: Record<string, (state: State, ...payload: unknown[]) => State>,\n /**\n * Effects handlers for this block.\n */\n private readonly handlers: Effects<Context>[],\n ) {}\n\n static init<Name extends string, State>(name: Name, initial: State): BlockBuilder<Name, State, {}, {}> {\n return new BlockBuilder(name, initial, {}, []);\n }\n\n /**\n * Append an action handler to the block.\n * Action is a pure function that takes the state + arguments and returns a new state.\n */\n action<Action extends string, Payload extends unknown[] = []>(\n action: Action,\n handler: (state: State, ...payload: Payload) => State,\n ): BlockBuilder<\n Name,\n State,\n Creators & Record<Action, (...payload: Payload) => PayloadAction<`${Name}/${Action}`, Payload>>,\n Context\n > {\n this.reducers[`${this.name}/${action}`] = handler as (state: State, ...payload: unknown[]) => State;\n return this as any;\n }\n\n /**\n * Append effect handlers to the block.\n * Effects can call any side effects provided from the context.\n */\n effects<E extends Effects<any>>(\n effects: E,\n ): BlockBuilder<\n Name,\n State,\n Creators & EffectsToCreators<Name, E>,\n Context & (E extends Effects<infer C> ? C : never)\n > {\n this.handlers.push(effects);\n return this as any;\n }\n\n build(): ReduxBlock<State, Creators, Context> {\n const initialState = this.initial;\n const blockName = this.name;\n return {\n actions: creator(this.name),\n effects: (context: Context) =>\n this.handlers.reduce(\n (acc, effect) =>\n Object.assign(\n acc,\n Object.fromEntries(\n Object.entries(effect(context)).map(([effectName, handler]) => [\n `${blockName}/${effectName}`,\n handler,\n ]),\n ),\n ),\n {} as Record<string, (...payload: unknown[]) => void>,\n ),\n reducer: (state = initialState, action: UnknownAction) => {\n const handler = this.reducers[action.type];\n if (!handler) return state;\n const payload = \"payload\" in action ? (action.payload as unknown[]) : undefined;\n return payload && payload.length ? handler(state, ...payload) : handler(state);\n },\n } as any;\n }\n}\n\nclass CompositionBuilder<\n Name extends string,\n BlockMap extends Record<string, ReduxBlock<any, any, any>>,\n Creators,\n Context,\n> {\n private blocks: BlockMap = {} as BlockMap;\n private handlers: Effects<Context>[] = [];\n private creators: Creators;\n\n private constructor(private name: Name) {\n this.creators = creator(name) as Creators;\n }\n\n static init<Name extends string>(name: Name): CompositionBuilder<Name, {}, {}, {}> {\n return new CompositionBuilder(name);\n }\n\n block<Name extends string, Block extends ReduxBlock<any, any, any>>(\n name: Name,\n block: Block,\n ): CompositionBuilder<\n Name,\n BlockMap & Record<Name, Block>,\n Creators & Record<Name, ReduxBlock.TakeCreators<Block>>,\n Context & ReduxBlock.TakeContext<Block>\n > {\n (this.blocks as Record<string, ReduxBlock<any, any, any>>)[name] = block;\n (this.creators as Record<string, unknown>)[name] = block.actions;\n return this as any;\n }\n\n effects<E extends Effects<any>>(\n effects: E,\n ): CompositionBuilder<\n Name,\n BlockMap,\n Creators & EffectsToCreators<Name, E>,\n Context & (E extends Effects<infer ExtraContext> ? ExtraContext : never)\n > {\n (this.handlers as (Effects<Context> | E)[]).push(effects);\n return this as any;\n }\n\n build(): ReduxBlock<{ [K in keyof BlockMap]: ReduxBlock.TakeState<BlockMap[K]> }, Creators, Context> {\n const reducers = Object.entries(this.blocks).map(([name, block]) => [name, block.reducer] as const);\n return {\n actions: this.creators,\n effects: (context: Context) => {\n const blockName = this.name;\n const result = this.handlers.reduce(\n (acc, effect) =>\n Object.assign(\n acc,\n Object.fromEntries(\n Object.entries(effect(context)).map(([effectName, handler]) => [\n `${blockName}/${effectName}`,\n handler,\n ]),\n ),\n ),\n {} as Record<string, (...payload: unknown[]) => void>,\n );\n Object.values(this.blocks).forEach((block) => Object.assign(result, block.effects(context)));\n return result;\n },\n reducer: (state: any, action: UnknownAction) => {\n let result = state;\n let changed = false;\n reducers.forEach(([name, reducer]) => {\n const original = state?.[name];\n const updated = reducer(original, action);\n if (updated !== original) {\n if (!changed) {\n changed = true;\n result = { ...state };\n }\n result[name] = updated;\n }\n });\n return result;\n },\n } as any;\n }\n}\n\nexport namespace ReduxBlock {\n type AnyBlock = ReduxBlock<any, any, any>;\n\n export type TakeState<Block extends AnyBlock> = Block extends ReduxBlock<infer State, any, any> ? State : never;\n export type TakeCreators<Block extends AnyBlock> =\n Block extends ReduxBlock<any, infer Creators, any> ? Creators : never;\n export type TakeContext<Block extends AnyBlock> =\n Block extends ReduxBlock<any, any, infer Context> ? Context : never;\n\n /**\n * Create a block builder.\n * It's a starting point for creating a block.\n */\n export function builder<Name extends string, State>(name: Name, initial: State): BlockBuilder<Name, State, {}, {}> {\n return BlockBuilder.init(name, initial);\n }\n\n /**\n * Create a composition builder.\n */\n export function composition<Name extends string>(name: Name): CompositionBuilder<Name, {}, {}, {}> {\n return CompositionBuilder.init(name);\n }\n\n /**\n * Create middleware for effects processing.\n * It expects to receive a context object that provides necessary dependencies for effect handlers.\n */\n export function middleware<Block extends AnyBlock>(block: Block, context: TakeContext<Block>): Middleware {\n const effects = block.effects(context);\n return () => (next) => (action) => {\n if (action && typeof action === \"object\" && \"type\" in action && has(effects, action.type as string)) {\n effects[action.type as string](...(\"payload\" in action ? (action.payload as unknown[]) : []));\n }\n next(action);\n };\n }\n}\n"],"names":["has","v","k","creator","scope","target","property","payload","type","BlockBuilder","name","initial","reducers","handlers","action","handler","effects","initialState","blockName","context","acc","effect","effectName","state","CompositionBuilder","block","result","changed","reducer","original","updated","ReduxBlock","builder","composition","middleware","next"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"redux-sacala.cjs","sources":["../src/redux-sacala.ts"],"sourcesContent":["import { Middleware, Reducer, UnknownAction } from \"redux\";\nimport { Selector } from \"@reduxjs/toolkit\";\n\n/**\n * Composable Redux block with state description, action creators, and effects handlers.\n * Use `ReduxBlock.builder` to start building a new block.\n */\nexport interface ReduxBlock<State, Creators, Context, Selectors> {\n /**\n * Action creators for this block.\n * When composed, action creators can form a folder tree structure.\n */\n actions: Creators;\n /**\n * Reducer that can be used directly in Redux store configuration.\n */\n reducer: Reducer<State>;\n /**\n * Effects to be called on effects actions.\n * Use `ReduxBlock.middleware` to create middleware for effects processing.\n */\n effects: Effects<Context>;\n /**\n * Selectors for derived state properties.\n */\n select: Selectors;\n}\n\ntype PayloadAction<Type extends string, Payload extends unknown[]> = Payload extends never[]\n ? { type: Type }\n : { type: Type; payload: Payload };\n\n/**\n * Checks if the given key exists directly on the provided object.\n */\nfunction has<K extends string | symbol>(v: unknown, k: K): v is Record<K, unknown> {\n return typeof v === \"object\" && v !== null && Object.hasOwn(v, k);\n}\n\nconst creator = (scope: string) =>\n new Proxy(\n {},\n {\n get(target, property) {\n if (has(target, property)) {\n return target[property];\n }\n\n return (...payload: unknown[]) => {\n const type = `${scope}/${property as string}`;\n return payload.length ? { type, payload } : { type };\n };\n },\n },\n ) as Record<string, (...payload: unknown[]) => UnknownAction>;\n\n/**\n * Effects creator. It receives context with side effect APIs and returns non-pure handlers.\n */\ntype Effects<Context> = (context: Context) => Record<string, (...payload: any[]) => void>;\n\n/**\n * Convert effects type to action creators.\n */\ntype EffectsToCreators<Name extends string, E extends Effects<any>> = {\n [K in keyof ReturnType<E>]: (\n ...parameters: Parameters<ReturnType<E>[K]>\n ) => PayloadAction<`${Name}/${K extends string ? K : never}`, Parameters<ReturnType<E>[K]>>;\n};\n\ntype LiftSelectors<Tree, NewState> = Tree extends (...args: any) => infer Value\n ? Selector<NewState, Value>\n : {\n [K in keyof Tree]: LiftSelectors<Tree[K], NewState>;\n };\n\nfunction lift<Tree, RootState, State>(\n tree: Tree,\n selectState: (root: RootState) => State,\n): LiftSelectors<Tree, RootState> {\n if (typeof tree === \"function\") {\n return ((state: RootState) => tree(selectState(state))) as any;\n } else {\n return Object.fromEntries(Object.entries(tree as any).map(([k, v]) => [k, lift(v, selectState)])) as any;\n }\n}\n\nclass BlockBuilder<\n Name extends string,\n State,\n Creators extends Record<string, (...parameters: unknown[]) => PayloadAction<any, any>>,\n Context,\n Selectors extends Record<string, Selector<State>>,\n> {\n private constructor(\n readonly name: Name,\n readonly initial: State,\n /**\n * Per-message action reducers for this block.\n */\n private readonly reducers: Record<string, (state: State, ...payload: unknown[]) => State>,\n /**\n * Effects handlers for this block.\n */\n private readonly handlers: Effects<Context>[],\n private readonly select: Selectors,\n ) {}\n\n static init<Name extends string, State>(name: Name, initial: State): BlockBuilder<Name, State, {}, {}, {}> {\n return new BlockBuilder(name, initial, {}, [], {});\n }\n\n /**\n * Append an action handler to the block.\n * Action is a pure function that takes the state + arguments and returns a new state.\n */\n action<Action extends string, Payload extends unknown[] = []>(\n action: Action,\n handler: (state: State, ...payload: Payload) => State,\n ): BlockBuilder<\n Name,\n State,\n Creators & Record<Action, (...payload: Payload) => PayloadAction<`${Name}/${Action}`, Payload>>,\n Context,\n Selectors\n > {\n this.reducers[`${this.name}/${action}`] = handler as (state: State, ...payload: unknown[]) => State;\n return this as any;\n }\n\n /**\n * Append effect handlers to the block.\n * Effects can call any side effects provided from the context.\n */\n effects<E extends Effects<any>>(\n effects: E,\n ): BlockBuilder<\n Name,\n State,\n Creators & EffectsToCreators<Name, E>,\n Context & (E extends Effects<infer C> ? C : never),\n Selectors\n > {\n this.handlers.push(effects);\n return this as any;\n }\n\n selectors<SelectorsToAdd extends Record<string, Selector<State>>>(\n selectors: SelectorsToAdd,\n ): BlockBuilder<Name, State, Creators, Context, Selectors & SelectorsToAdd> {\n Object.assign(this.select as any, selectors);\n return this as any;\n }\n\n build(): ReduxBlock<State, Creators, Context, Selectors> {\n const initialState = this.initial;\n const blockName = this.name;\n return {\n actions: creator(this.name),\n effects: (context: Context) =>\n this.handlers.reduce(\n (acc, effect) =>\n Object.assign(\n acc,\n Object.fromEntries(\n Object.entries(effect(context)).map(([effectName, handler]) => [\n `${blockName}/${effectName}`,\n handler,\n ]),\n ),\n ),\n {} as Record<string, (...payload: unknown[]) => void>,\n ),\n reducer: (state = initialState, action: UnknownAction) => {\n const handler = this.reducers[action.type];\n if (!handler) return state;\n const payload = \"payload\" in action ? (action.payload as unknown[]) : undefined;\n return payload && payload.length ? handler(state, ...payload) : handler(state);\n },\n select: this.select,\n } as any;\n }\n}\n\nclass CompositionBuilder<\n Name extends string,\n BlockMap extends Record<string, ReduxBlock<any, any, any, any>>,\n Creators,\n Context,\n Selectors,\n> {\n private readonly blocks: BlockMap = {} as BlockMap;\n private readonly handlers: Effects<Context>[] = [];\n private readonly creators: Creators;\n private readonly select: Selectors = {} as Selectors;\n\n private constructor(private name: Name) {\n this.creators = creator(name) as Creators;\n }\n\n static init<Name extends string>(name: Name): CompositionBuilder<Name, {}, {}, {}, {}> {\n return new CompositionBuilder(name);\n }\n\n block<Name extends string, Block extends ReduxBlock<any, any, any, any>>(\n name: Name,\n block: Block,\n ): CompositionBuilder<\n Name,\n BlockMap & { [name in Name]: Block },\n Creators & { [name in Name]: ReduxBlock.TakeCreators<Block> },\n Context & ReduxBlock.TakeContext<Block>,\n Selectors & {\n [key in Name]: LiftSelectors<\n ReduxBlock.TakeSelectors<Block>,\n { [name in Name]: ReduxBlock.TakeState<Block> }\n >;\n }\n > {\n (this.blocks as Record<string, ReduxBlock<any, any, any, any>>)[name] = block;\n (this.creators as Record<string, unknown>)[name] = block.actions;\n (this.select as Record<string, unknown>)[name] = lift(block.select, (rootState: any) => rootState[name]);\n return this as any;\n }\n\n selectors<\n SelectorsToAdd extends Record<string, Selector<{ [K in keyof BlockMap]: ReduxBlock.TakeState<BlockMap[K]> }>>,\n >(selectors: SelectorsToAdd): CompositionBuilder<Name, BlockMap, Creators, Context, Selectors & SelectorsToAdd> {\n Object.assign(this.select as any, selectors);\n return this as any;\n }\n\n effects<E extends Effects<any>>(\n effects: E,\n ): CompositionBuilder<\n Name,\n BlockMap,\n Creators & EffectsToCreators<Name, E>,\n Context & (E extends Effects<infer ExtraContext> ? ExtraContext : never),\n Selectors\n > {\n (this.handlers as (Effects<Context> | E)[]).push(effects);\n return this as any;\n }\n\n build(): ReduxBlock<{ [K in keyof BlockMap]: ReduxBlock.TakeState<BlockMap[K]> }, Creators, Context, Selectors> {\n const reducers = Object.entries(this.blocks).map(([name, block]) => [name, block.reducer] as const);\n return {\n actions: this.creators,\n effects: (context: Context) => {\n const blockName = this.name;\n const result = this.handlers.reduce(\n (acc, effect) =>\n Object.assign(\n acc,\n Object.fromEntries(\n Object.entries(effect(context)).map(([effectName, handler]) => [\n `${blockName}/${effectName}`,\n handler,\n ]),\n ),\n ),\n {} as Record<string, (...payload: unknown[]) => void>,\n );\n Object.values(this.blocks).forEach((block) => Object.assign(result, block.effects(context)));\n return result;\n },\n reducer: (state: any, action: UnknownAction) => {\n let result = state;\n let changed = false;\n reducers.forEach(([name, reducer]) => {\n const original = state?.[name];\n const updated = reducer(original, action);\n if (updated !== original) {\n if (!changed) {\n changed = true;\n result = { ...state };\n }\n result[name] = updated;\n }\n });\n return result;\n },\n select: this.select,\n } as any;\n }\n}\n\nexport namespace ReduxBlock {\n type AnyBlock = ReduxBlock<any, any, any, any>;\n\n export type TakeState<Block extends AnyBlock> =\n Block extends ReduxBlock<infer State, any, any, any> ? State : never;\n export type TakeCreators<Block extends AnyBlock> =\n Block extends ReduxBlock<any, infer Creators, any, any> ? Creators : never;\n export type TakeContext<Block extends AnyBlock> =\n Block extends ReduxBlock<any, any, infer Context, any> ? Context : never;\n export type TakeSelectors<Block extends AnyBlock> =\n Block extends ReduxBlock<any, any, any, infer Selectors> ? Selectors : never;\n\n /**\n * Create a block builder.\n * It's a starting point for creating a block.\n */\n export function builder<Name extends string, State>(\n name: Name,\n initial: State,\n ): BlockBuilder<Name, State, {}, {}, {}> {\n return BlockBuilder.init(name, initial);\n }\n\n /**\n * Create a composition builder.\n */\n export function composition<Name extends string>(name: Name): CompositionBuilder<Name, {}, {}, {}, {}> {\n return CompositionBuilder.init(name);\n }\n\n /**\n * Create middleware for effects processing.\n * It expects to receive a context object that provides necessary dependencies for effect handlers.\n */\n export function middleware<Block extends AnyBlock>(block: Block, context: TakeContext<Block>): Middleware {\n const effects = block.effects(context);\n return () => (next) => (action) => {\n if (action && typeof action === \"object\" && \"type\" in action && has(effects, action.type as string)) {\n effects[action.type as string](...(\"payload\" in action ? (action.payload as unknown[]) : []));\n }\n next(action);\n };\n }\n\n /**\n * Create a new block with a different context shape.\n */\n export function mapContext<Block extends AnyBlock, NewContext>(\n block: Block,\n mapper: (context: NewContext) => TakeContext<Block>,\n ): ReduxBlock<TakeState<Block>, TakeCreators<Block>, NewContext, TakeSelectors<Block>> {\n return {\n actions: block.actions,\n reducer: block.reducer,\n effects: (ctx) => block.effects(mapper(ctx)),\n select: block.select,\n };\n }\n}\n"],"names":["has","v","k","creator","scope","target","property","payload","type","lift","tree","selectState","state","BlockBuilder","name","initial","reducers","handlers","select","action","handler","effects","selectors","initialState","blockName","context","acc","effect","effectName","CompositionBuilder","block","rootState","result","changed","reducer","original","updated","ReduxBlock","builder","composition","middleware","next","mapContext","mapper","ctx"],"mappings":"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"}
|
package/build/redux-sacala.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { Middleware } from 'redux';
|
|
2
2
|
import { Reducer } from 'redux';
|
|
3
|
-
import {
|
|
3
|
+
import { Selector } from '@reduxjs/toolkit';
|
|
4
4
|
|
|
5
|
-
declare class BlockBuilder<Name extends string, State, Creators extends Record<string, (...parameters: unknown[]) => PayloadAction<any, any>>, Context
|
|
5
|
+
declare class BlockBuilder<Name extends string, State, Creators extends Record<string, (...parameters: unknown[]) => PayloadAction<any, any>>, Context, Selectors extends Record<string, Selector<State>>> {
|
|
6
6
|
readonly name: Name;
|
|
7
7
|
readonly initial: State;
|
|
8
8
|
/**
|
|
@@ -13,33 +13,47 @@ declare class BlockBuilder<Name extends string, State, Creators extends Record<s
|
|
|
13
13
|
* Effects handlers for this block.
|
|
14
14
|
*/
|
|
15
15
|
private readonly handlers;
|
|
16
|
+
private readonly select;
|
|
16
17
|
private constructor();
|
|
17
|
-
static init<Name extends string, State>(name: Name, initial: State): BlockBuilder<Name, State, {}, {}>;
|
|
18
|
+
static init<Name extends string, State>(name: Name, initial: State): BlockBuilder<Name, State, {}, {}, {}>;
|
|
18
19
|
/**
|
|
19
20
|
* Append an action handler to the block.
|
|
20
21
|
* Action is a pure function that takes the state + arguments and returns a new state.
|
|
21
22
|
*/
|
|
22
|
-
action<Action extends string, Payload extends unknown[] = []>(action: Action, handler: (state: State, ...payload: Payload) => State): BlockBuilder<Name, State, Creators & Record<Action, (...payload: Payload) => PayloadAction<`${Name}/${Action}`, Payload>>, Context>;
|
|
23
|
+
action<Action extends string, Payload extends unknown[] = []>(action: Action, handler: (state: State, ...payload: Payload) => State): BlockBuilder<Name, State, Creators & Record<Action, (...payload: Payload) => PayloadAction<`${Name}/${Action}`, Payload>>, Context, Selectors>;
|
|
23
24
|
/**
|
|
24
25
|
* Append effect handlers to the block.
|
|
25
26
|
* Effects can call any side effects provided from the context.
|
|
26
27
|
*/
|
|
27
|
-
effects<E extends Effects<any>>(effects: E): BlockBuilder<Name, State, Creators & EffectsToCreators<Name, E>, Context & (E extends Effects<infer C> ? C : never)>;
|
|
28
|
-
|
|
28
|
+
effects<E extends Effects<any>>(effects: E): BlockBuilder<Name, State, Creators & EffectsToCreators<Name, E>, Context & (E extends Effects<infer C> ? C : never), Selectors>;
|
|
29
|
+
selectors<SelectorsToAdd extends Record<string, Selector<State>>>(selectors: SelectorsToAdd): BlockBuilder<Name, State, Creators, Context, Selectors & SelectorsToAdd>;
|
|
30
|
+
build(): ReduxBlock<State, Creators, Context, Selectors>;
|
|
29
31
|
}
|
|
30
32
|
|
|
31
|
-
declare class CompositionBuilder<Name extends string, BlockMap extends Record<string, ReduxBlock<any, any, any>>, Creators, Context> {
|
|
33
|
+
declare class CompositionBuilder<Name extends string, BlockMap extends Record<string, ReduxBlock<any, any, any, any>>, Creators, Context, Selectors> {
|
|
32
34
|
private name;
|
|
33
|
-
private blocks;
|
|
34
|
-
private handlers;
|
|
35
|
-
private creators;
|
|
35
|
+
private readonly blocks;
|
|
36
|
+
private readonly handlers;
|
|
37
|
+
private readonly creators;
|
|
38
|
+
private readonly select;
|
|
36
39
|
private constructor();
|
|
37
|
-
static init<Name extends string>(name: Name): CompositionBuilder<Name, {}, {}, {}>;
|
|
38
|
-
block<Name extends string, Block extends ReduxBlock<any, any, any>>(name: Name, block: Block): CompositionBuilder<Name, BlockMap &
|
|
39
|
-
|
|
40
|
+
static init<Name extends string>(name: Name): CompositionBuilder<Name, {}, {}, {}, {}>;
|
|
41
|
+
block<Name extends string, Block extends ReduxBlock<any, any, any, any>>(name: Name, block: Block): CompositionBuilder<Name, BlockMap & {
|
|
42
|
+
[name in Name]: Block;
|
|
43
|
+
}, Creators & {
|
|
44
|
+
[name in Name]: ReduxBlock.TakeCreators<Block>;
|
|
45
|
+
}, Context & ReduxBlock.TakeContext<Block>, Selectors & {
|
|
46
|
+
[key in Name]: LiftSelectors<ReduxBlock.TakeSelectors<Block>, {
|
|
47
|
+
[name in Name]: ReduxBlock.TakeState<Block>;
|
|
48
|
+
}>;
|
|
49
|
+
}>;
|
|
50
|
+
selectors<SelectorsToAdd extends Record<string, Selector<{
|
|
51
|
+
[K in keyof BlockMap]: ReduxBlock.TakeState<BlockMap[K]>;
|
|
52
|
+
}>>>(selectors: SelectorsToAdd): CompositionBuilder<Name, BlockMap, Creators, Context, Selectors & SelectorsToAdd>;
|
|
53
|
+
effects<E extends Effects<any>>(effects: E): CompositionBuilder<Name, BlockMap, Creators & EffectsToCreators<Name, E>, Context & (E extends Effects<infer ExtraContext> ? ExtraContext : never), Selectors>;
|
|
40
54
|
build(): ReduxBlock<{
|
|
41
55
|
[K in keyof BlockMap]: ReduxBlock.TakeState<BlockMap[K]>;
|
|
42
|
-
}, Creators, Context>;
|
|
56
|
+
}, Creators, Context, Selectors>;
|
|
43
57
|
}
|
|
44
58
|
|
|
45
59
|
/**
|
|
@@ -54,6 +68,10 @@ declare type EffectsToCreators<Name extends string, E extends Effects<any>> = {
|
|
|
54
68
|
[K in keyof ReturnType<E>]: (...parameters: Parameters<ReturnType<E>[K]>) => PayloadAction<`${Name}/${K extends string ? K : never}`, Parameters<ReturnType<E>[K]>>;
|
|
55
69
|
};
|
|
56
70
|
|
|
71
|
+
declare type LiftSelectors<Tree, NewState> = Tree extends (...args: any) => infer Value ? Selector<NewState, Value> : {
|
|
72
|
+
[K in keyof Tree]: LiftSelectors<Tree[K], NewState>;
|
|
73
|
+
};
|
|
74
|
+
|
|
57
75
|
declare type PayloadAction<Type extends string, Payload extends unknown[]> = Payload extends never[] ? {
|
|
58
76
|
type: Type;
|
|
59
77
|
} : {
|
|
@@ -65,7 +83,7 @@ declare type PayloadAction<Type extends string, Payload extends unknown[]> = Pay
|
|
|
65
83
|
* Composable Redux block with state description, action creators, and effects handlers.
|
|
66
84
|
* Use `ReduxBlock.builder` to start building a new block.
|
|
67
85
|
*/
|
|
68
|
-
export declare interface ReduxBlock<State, Creators, Context> {
|
|
86
|
+
export declare interface ReduxBlock<State, Creators, Context, Selectors> {
|
|
69
87
|
/**
|
|
70
88
|
* Action creators for this block.
|
|
71
89
|
* When composed, action creators can form a folder tree structure.
|
|
@@ -74,33 +92,42 @@ export declare interface ReduxBlock<State, Creators, Context> {
|
|
|
74
92
|
/**
|
|
75
93
|
* Reducer that can be used directly in Redux store configuration.
|
|
76
94
|
*/
|
|
77
|
-
reducer: Reducer<State
|
|
95
|
+
reducer: Reducer<State>;
|
|
78
96
|
/**
|
|
79
97
|
* Effects to be called on effects actions.
|
|
80
98
|
* Use `ReduxBlock.middleware` to create middleware for effects processing.
|
|
81
99
|
*/
|
|
82
100
|
effects: Effects<Context>;
|
|
101
|
+
/**
|
|
102
|
+
* Selectors for derived state properties.
|
|
103
|
+
*/
|
|
104
|
+
select: Selectors;
|
|
83
105
|
}
|
|
84
106
|
|
|
85
107
|
export declare namespace ReduxBlock {
|
|
86
|
-
export type AnyBlock = ReduxBlock<any, any, any>;
|
|
87
|
-
export type TakeState<Block extends AnyBlock> = Block extends ReduxBlock<infer State, any, any> ? State : never;
|
|
88
|
-
export type TakeCreators<Block extends AnyBlock> = Block extends ReduxBlock<any, infer Creators, any> ? Creators : never;
|
|
89
|
-
export type TakeContext<Block extends AnyBlock> = Block extends ReduxBlock<any, any, infer Context> ? Context : never;
|
|
108
|
+
export type AnyBlock = ReduxBlock<any, any, any, any>;
|
|
109
|
+
export type TakeState<Block extends AnyBlock> = Block extends ReduxBlock<infer State, any, any, any> ? State : never;
|
|
110
|
+
export type TakeCreators<Block extends AnyBlock> = Block extends ReduxBlock<any, infer Creators, any, any> ? Creators : never;
|
|
111
|
+
export type TakeContext<Block extends AnyBlock> = Block extends ReduxBlock<any, any, infer Context, any> ? Context : never;
|
|
112
|
+
export type TakeSelectors<Block extends AnyBlock> = Block extends ReduxBlock<any, any, any, infer Selectors> ? Selectors : never;
|
|
90
113
|
/**
|
|
91
114
|
* Create a block builder.
|
|
92
115
|
* It's a starting point for creating a block.
|
|
93
116
|
*/
|
|
94
|
-
export function builder<Name extends string, State>(name: Name, initial: State): BlockBuilder<Name, State, {}, {}>;
|
|
117
|
+
export function builder<Name extends string, State>(name: Name, initial: State): BlockBuilder<Name, State, {}, {}, {}>;
|
|
95
118
|
/**
|
|
96
119
|
* Create a composition builder.
|
|
97
120
|
*/
|
|
98
|
-
export function composition<Name extends string>(name: Name): CompositionBuilder<Name, {}, {}, {}>;
|
|
121
|
+
export function composition<Name extends string>(name: Name): CompositionBuilder<Name, {}, {}, {}, {}>;
|
|
99
122
|
/**
|
|
100
123
|
* Create middleware for effects processing.
|
|
101
124
|
* It expects to receive a context object that provides necessary dependencies for effect handlers.
|
|
102
125
|
*/
|
|
103
126
|
export function middleware<Block extends AnyBlock>(block: Block, context: TakeContext<Block>): Middleware;
|
|
127
|
+
/**
|
|
128
|
+
* Create a new block with a different context shape.
|
|
129
|
+
*/
|
|
130
|
+
export function mapContext<Block extends AnyBlock, NewContext>(block: Block, mapper: (context: NewContext) => TakeContext<Block>): ReduxBlock<TakeState<Block>, TakeCreators<Block>, NewContext, TakeSelectors<Block>>;
|
|
104
131
|
{};
|
|
105
132
|
}
|
|
106
133
|
|
package/build/redux-sacala.js
CHANGED
|
@@ -1,23 +1,26 @@
|
|
|
1
|
-
function
|
|
2
|
-
return typeof
|
|
1
|
+
function d(c, e) {
|
|
2
|
+
return typeof c == "object" && c !== null && Object.hasOwn(c, e);
|
|
3
3
|
}
|
|
4
|
-
const b = (
|
|
4
|
+
const b = (c) => new Proxy(
|
|
5
5
|
{},
|
|
6
6
|
{
|
|
7
7
|
get(e, t) {
|
|
8
|
-
return
|
|
9
|
-
const
|
|
10
|
-
return r.length ? { type:
|
|
8
|
+
return d(e, t) ? e[t] : (...r) => {
|
|
9
|
+
const i = `${c}/${t}`;
|
|
10
|
+
return r.length ? { type: i, payload: r } : { type: i };
|
|
11
11
|
};
|
|
12
12
|
}
|
|
13
13
|
}
|
|
14
14
|
);
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
15
|
+
function p(c, e) {
|
|
16
|
+
return typeof c == "function" ? ((t) => c(e(t))) : Object.fromEntries(Object.entries(c).map(([t, r]) => [t, p(r, e)]));
|
|
17
|
+
}
|
|
18
|
+
class l {
|
|
19
|
+
constructor(e, t, r, i, s) {
|
|
20
|
+
this.name = e, this.initial = t, this.reducers = r, this.handlers = i, this.select = s;
|
|
18
21
|
}
|
|
19
22
|
static init(e, t) {
|
|
20
|
-
return new
|
|
23
|
+
return new l(e, t, {}, [], {});
|
|
21
24
|
}
|
|
22
25
|
/**
|
|
23
26
|
* Append an action handler to the block.
|
|
@@ -33,28 +36,32 @@ class h {
|
|
|
33
36
|
effects(e) {
|
|
34
37
|
return this.handlers.push(e), this;
|
|
35
38
|
}
|
|
39
|
+
selectors(e) {
|
|
40
|
+
return Object.assign(this.select, e), this;
|
|
41
|
+
}
|
|
36
42
|
build() {
|
|
37
43
|
const e = this.initial, t = this.name;
|
|
38
44
|
return {
|
|
39
45
|
actions: b(this.name),
|
|
40
46
|
effects: (r) => this.handlers.reduce(
|
|
41
|
-
(
|
|
42
|
-
|
|
47
|
+
(i, s) => Object.assign(
|
|
48
|
+
i,
|
|
43
49
|
Object.fromEntries(
|
|
44
|
-
Object.entries(
|
|
45
|
-
`${t}/${
|
|
46
|
-
|
|
50
|
+
Object.entries(s(r)).map(([n, h]) => [
|
|
51
|
+
`${t}/${n}`,
|
|
52
|
+
h
|
|
47
53
|
])
|
|
48
54
|
)
|
|
49
55
|
),
|
|
50
56
|
{}
|
|
51
57
|
),
|
|
52
|
-
reducer: (r = e,
|
|
53
|
-
const
|
|
54
|
-
if (!
|
|
55
|
-
const
|
|
56
|
-
return
|
|
57
|
-
}
|
|
58
|
+
reducer: (r = e, i) => {
|
|
59
|
+
const s = this.reducers[i.type];
|
|
60
|
+
if (!s) return r;
|
|
61
|
+
const n = "payload" in i ? i.payload : void 0;
|
|
62
|
+
return n && n.length ? s(r, ...n) : s(r);
|
|
63
|
+
},
|
|
64
|
+
select: this.select
|
|
58
65
|
};
|
|
59
66
|
}
|
|
60
67
|
}
|
|
@@ -65,11 +72,15 @@ class a {
|
|
|
65
72
|
blocks = {};
|
|
66
73
|
handlers = [];
|
|
67
74
|
creators;
|
|
75
|
+
select = {};
|
|
68
76
|
static init(e) {
|
|
69
77
|
return new a(e);
|
|
70
78
|
}
|
|
71
79
|
block(e, t) {
|
|
72
|
-
return this.blocks[e] = t, this.creators[e] = t.actions, this;
|
|
80
|
+
return this.blocks[e] = t, this.creators[e] = t.actions, this.select[e] = p(t.select, (r) => r[e]), this;
|
|
81
|
+
}
|
|
82
|
+
selectors(e) {
|
|
83
|
+
return Object.assign(this.select, e), this;
|
|
73
84
|
}
|
|
74
85
|
effects(e) {
|
|
75
86
|
return this.handlers.push(e), this;
|
|
@@ -79,49 +90,59 @@ class a {
|
|
|
79
90
|
return {
|
|
80
91
|
actions: this.creators,
|
|
81
92
|
effects: (t) => {
|
|
82
|
-
const r = this.name,
|
|
83
|
-
(
|
|
84
|
-
|
|
93
|
+
const r = this.name, i = this.handlers.reduce(
|
|
94
|
+
(s, n) => Object.assign(
|
|
95
|
+
s,
|
|
85
96
|
Object.fromEntries(
|
|
86
|
-
Object.entries(
|
|
87
|
-
`${r}/${
|
|
88
|
-
|
|
97
|
+
Object.entries(n(t)).map(([h, o]) => [
|
|
98
|
+
`${r}/${h}`,
|
|
99
|
+
o
|
|
89
100
|
])
|
|
90
101
|
)
|
|
91
102
|
),
|
|
92
103
|
{}
|
|
93
104
|
);
|
|
94
|
-
return Object.values(this.blocks).forEach((
|
|
105
|
+
return Object.values(this.blocks).forEach((s) => Object.assign(i, s.effects(t))), i;
|
|
95
106
|
},
|
|
96
107
|
reducer: (t, r) => {
|
|
97
|
-
let
|
|
98
|
-
return e.forEach(([
|
|
99
|
-
const
|
|
100
|
-
|
|
101
|
-
}),
|
|
102
|
-
}
|
|
108
|
+
let i = t, s = !1;
|
|
109
|
+
return e.forEach(([n, h]) => {
|
|
110
|
+
const o = t?.[n], u = h(o, r);
|
|
111
|
+
u !== o && (s || (s = !0, i = { ...t }), i[n] = u);
|
|
112
|
+
}), i;
|
|
113
|
+
},
|
|
114
|
+
select: this.select
|
|
103
115
|
};
|
|
104
116
|
}
|
|
105
117
|
}
|
|
106
|
-
var
|
|
107
|
-
((
|
|
118
|
+
var f;
|
|
119
|
+
((c) => {
|
|
108
120
|
function e(s, n) {
|
|
109
|
-
return
|
|
121
|
+
return l.init(s, n);
|
|
110
122
|
}
|
|
111
|
-
|
|
123
|
+
c.builder = e;
|
|
112
124
|
function t(s) {
|
|
113
125
|
return a.init(s);
|
|
114
126
|
}
|
|
115
|
-
|
|
127
|
+
c.composition = t;
|
|
116
128
|
function r(s, n) {
|
|
117
|
-
const
|
|
118
|
-
return () => (o) => (
|
|
119
|
-
|
|
129
|
+
const h = s.effects(n);
|
|
130
|
+
return () => (o) => (u) => {
|
|
131
|
+
u && typeof u == "object" && "type" in u && d(h, u.type) && h[u.type](..."payload" in u ? u.payload : []), o(u);
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
c.middleware = r;
|
|
135
|
+
function i(s, n) {
|
|
136
|
+
return {
|
|
137
|
+
actions: s.actions,
|
|
138
|
+
reducer: s.reducer,
|
|
139
|
+
effects: (h) => s.effects(n(h)),
|
|
140
|
+
select: s.select
|
|
120
141
|
};
|
|
121
142
|
}
|
|
122
|
-
|
|
123
|
-
})(
|
|
143
|
+
c.mapContext = i;
|
|
144
|
+
})(f || (f = {}));
|
|
124
145
|
export {
|
|
125
|
-
|
|
146
|
+
f as ReduxBlock
|
|
126
147
|
};
|
|
127
148
|
//# sourceMappingURL=redux-sacala.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"redux-sacala.js","sources":["../src/redux-sacala.ts"],"sourcesContent":["import { Middleware, Reducer, UnknownAction } from \"redux\";\n\n/**\n * Composable Redux block with state description, action creators, and effects handlers.\n * Use `ReduxBlock.builder` to start building a new block.\n */\nexport interface ReduxBlock<State, Creators, Context> {\n /**\n * Action creators for this block.\n * When composed, action creators can form a folder tree structure.\n */\n actions: Creators;\n /**\n * Reducer that can be used directly in Redux store configuration.\n */\n reducer: Reducer<State, UnknownAction>;\n /**\n * Effects to be called on effects actions.\n * Use `ReduxBlock.middleware` to create middleware for effects processing.\n */\n effects: Effects<Context>;\n}\n\ntype PayloadAction<Type extends string, Payload extends unknown[]> = Payload extends never[]\n ? { type: Type }\n : { type: Type; payload: Payload };\n\n/**\n * Checks if the given key exists directly on the provided object.\n */\nfunction has<K extends string | symbol>(v: unknown, k: K): v is Record<K, unknown> {\n return typeof v === \"object\" && v !== null && Object.hasOwn(v, k);\n}\n\nconst creator = (scope: string) =>\n new Proxy(\n {},\n {\n get(target, property) {\n if (has(target, property)) {\n return target[property];\n }\n\n return (...payload: unknown[]) => {\n const type = `${scope}/${property as string}`;\n return payload.length ? { type, payload } : { type };\n };\n },\n },\n ) as Record<string, (...payload: unknown[]) => UnknownAction>;\n\n/**\n * Effects creator. It receives context with side effect APIs and returns non-pure handlers.\n */\ntype Effects<Context> = (context: Context) => Record<string, (...payload: any[]) => void>;\n\n/**\n * Convert effects type to action creators.\n */\ntype EffectsToCreators<Name extends string, E extends Effects<any>> = {\n [K in keyof ReturnType<E>]: (\n ...parameters: Parameters<ReturnType<E>[K]>\n ) => PayloadAction<`${Name}/${K extends string ? K : never}`, Parameters<ReturnType<E>[K]>>;\n};\n\nclass BlockBuilder<\n Name extends string,\n State,\n Creators extends Record<string, (...parameters: unknown[]) => PayloadAction<any, any>>,\n Context,\n> {\n private constructor(\n readonly name: Name,\n readonly initial: State,\n /**\n * Per-message action reducers for this block.\n */\n private readonly reducers: Record<string, (state: State, ...payload: unknown[]) => State>,\n /**\n * Effects handlers for this block.\n */\n private readonly handlers: Effects<Context>[],\n ) {}\n\n static init<Name extends string, State>(name: Name, initial: State): BlockBuilder<Name, State, {}, {}> {\n return new BlockBuilder(name, initial, {}, []);\n }\n\n /**\n * Append an action handler to the block.\n * Action is a pure function that takes the state + arguments and returns a new state.\n */\n action<Action extends string, Payload extends unknown[] = []>(\n action: Action,\n handler: (state: State, ...payload: Payload) => State,\n ): BlockBuilder<\n Name,\n State,\n Creators & Record<Action, (...payload: Payload) => PayloadAction<`${Name}/${Action}`, Payload>>,\n Context\n > {\n this.reducers[`${this.name}/${action}`] = handler as (state: State, ...payload: unknown[]) => State;\n return this as any;\n }\n\n /**\n * Append effect handlers to the block.\n * Effects can call any side effects provided from the context.\n */\n effects<E extends Effects<any>>(\n effects: E,\n ): BlockBuilder<\n Name,\n State,\n Creators & EffectsToCreators<Name, E>,\n Context & (E extends Effects<infer C> ? C : never)\n > {\n this.handlers.push(effects);\n return this as any;\n }\n\n build(): ReduxBlock<State, Creators, Context> {\n const initialState = this.initial;\n const blockName = this.name;\n return {\n actions: creator(this.name),\n effects: (context: Context) =>\n this.handlers.reduce(\n (acc, effect) =>\n Object.assign(\n acc,\n Object.fromEntries(\n Object.entries(effect(context)).map(([effectName, handler]) => [\n `${blockName}/${effectName}`,\n handler,\n ]),\n ),\n ),\n {} as Record<string, (...payload: unknown[]) => void>,\n ),\n reducer: (state = initialState, action: UnknownAction) => {\n const handler = this.reducers[action.type];\n if (!handler) return state;\n const payload = \"payload\" in action ? (action.payload as unknown[]) : undefined;\n return payload && payload.length ? handler(state, ...payload) : handler(state);\n },\n } as any;\n }\n}\n\nclass CompositionBuilder<\n Name extends string,\n BlockMap extends Record<string, ReduxBlock<any, any, any>>,\n Creators,\n Context,\n> {\n private blocks: BlockMap = {} as BlockMap;\n private handlers: Effects<Context>[] = [];\n private creators: Creators;\n\n private constructor(private name: Name) {\n this.creators = creator(name) as Creators;\n }\n\n static init<Name extends string>(name: Name): CompositionBuilder<Name, {}, {}, {}> {\n return new CompositionBuilder(name);\n }\n\n block<Name extends string, Block extends ReduxBlock<any, any, any>>(\n name: Name,\n block: Block,\n ): CompositionBuilder<\n Name,\n BlockMap & Record<Name, Block>,\n Creators & Record<Name, ReduxBlock.TakeCreators<Block>>,\n Context & ReduxBlock.TakeContext<Block>\n > {\n (this.blocks as Record<string, ReduxBlock<any, any, any>>)[name] = block;\n (this.creators as Record<string, unknown>)[name] = block.actions;\n return this as any;\n }\n\n effects<E extends Effects<any>>(\n effects: E,\n ): CompositionBuilder<\n Name,\n BlockMap,\n Creators & EffectsToCreators<Name, E>,\n Context & (E extends Effects<infer ExtraContext> ? ExtraContext : never)\n > {\n (this.handlers as (Effects<Context> | E)[]).push(effects);\n return this as any;\n }\n\n build(): ReduxBlock<{ [K in keyof BlockMap]: ReduxBlock.TakeState<BlockMap[K]> }, Creators, Context> {\n const reducers = Object.entries(this.blocks).map(([name, block]) => [name, block.reducer] as const);\n return {\n actions: this.creators,\n effects: (context: Context) => {\n const blockName = this.name;\n const result = this.handlers.reduce(\n (acc, effect) =>\n Object.assign(\n acc,\n Object.fromEntries(\n Object.entries(effect(context)).map(([effectName, handler]) => [\n `${blockName}/${effectName}`,\n handler,\n ]),\n ),\n ),\n {} as Record<string, (...payload: unknown[]) => void>,\n );\n Object.values(this.blocks).forEach((block) => Object.assign(result, block.effects(context)));\n return result;\n },\n reducer: (state: any, action: UnknownAction) => {\n let result = state;\n let changed = false;\n reducers.forEach(([name, reducer]) => {\n const original = state?.[name];\n const updated = reducer(original, action);\n if (updated !== original) {\n if (!changed) {\n changed = true;\n result = { ...state };\n }\n result[name] = updated;\n }\n });\n return result;\n },\n } as any;\n }\n}\n\nexport namespace ReduxBlock {\n type AnyBlock = ReduxBlock<any, any, any>;\n\n export type TakeState<Block extends AnyBlock> = Block extends ReduxBlock<infer State, any, any> ? State : never;\n export type TakeCreators<Block extends AnyBlock> =\n Block extends ReduxBlock<any, infer Creators, any> ? Creators : never;\n export type TakeContext<Block extends AnyBlock> =\n Block extends ReduxBlock<any, any, infer Context> ? Context : never;\n\n /**\n * Create a block builder.\n * It's a starting point for creating a block.\n */\n export function builder<Name extends string, State>(name: Name, initial: State): BlockBuilder<Name, State, {}, {}> {\n return BlockBuilder.init(name, initial);\n }\n\n /**\n * Create a composition builder.\n */\n export function composition<Name extends string>(name: Name): CompositionBuilder<Name, {}, {}, {}> {\n return CompositionBuilder.init(name);\n }\n\n /**\n * Create middleware for effects processing.\n * It expects to receive a context object that provides necessary dependencies for effect handlers.\n */\n export function middleware<Block extends AnyBlock>(block: Block, context: TakeContext<Block>): Middleware {\n const effects = block.effects(context);\n return () => (next) => (action) => {\n if (action && typeof action === \"object\" && \"type\" in action && has(effects, action.type as string)) {\n effects[action.type as string](...(\"payload\" in action ? (action.payload as unknown[]) : []));\n }\n next(action);\n };\n }\n}\n"],"names":["has","v","k","creator","scope","target","property","payload","type","BlockBuilder","name","initial","reducers","handlers","action","handler","effects","initialState","blockName","context","acc","effect","effectName","state","CompositionBuilder","block","result","changed","reducer","original","updated","ReduxBlock","builder","composition","middleware","next"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"redux-sacala.js","sources":["../src/redux-sacala.ts"],"sourcesContent":["import { Middleware, Reducer, UnknownAction } from \"redux\";\nimport { Selector } from \"@reduxjs/toolkit\";\n\n/**\n * Composable Redux block with state description, action creators, and effects handlers.\n * Use `ReduxBlock.builder` to start building a new block.\n */\nexport interface ReduxBlock<State, Creators, Context, Selectors> {\n /**\n * Action creators for this block.\n * When composed, action creators can form a folder tree structure.\n */\n actions: Creators;\n /**\n * Reducer that can be used directly in Redux store configuration.\n */\n reducer: Reducer<State>;\n /**\n * Effects to be called on effects actions.\n * Use `ReduxBlock.middleware` to create middleware for effects processing.\n */\n effects: Effects<Context>;\n /**\n * Selectors for derived state properties.\n */\n select: Selectors;\n}\n\ntype PayloadAction<Type extends string, Payload extends unknown[]> = Payload extends never[]\n ? { type: Type }\n : { type: Type; payload: Payload };\n\n/**\n * Checks if the given key exists directly on the provided object.\n */\nfunction has<K extends string | symbol>(v: unknown, k: K): v is Record<K, unknown> {\n return typeof v === \"object\" && v !== null && Object.hasOwn(v, k);\n}\n\nconst creator = (scope: string) =>\n new Proxy(\n {},\n {\n get(target, property) {\n if (has(target, property)) {\n return target[property];\n }\n\n return (...payload: unknown[]) => {\n const type = `${scope}/${property as string}`;\n return payload.length ? { type, payload } : { type };\n };\n },\n },\n ) as Record<string, (...payload: unknown[]) => UnknownAction>;\n\n/**\n * Effects creator. It receives context with side effect APIs and returns non-pure handlers.\n */\ntype Effects<Context> = (context: Context) => Record<string, (...payload: any[]) => void>;\n\n/**\n * Convert effects type to action creators.\n */\ntype EffectsToCreators<Name extends string, E extends Effects<any>> = {\n [K in keyof ReturnType<E>]: (\n ...parameters: Parameters<ReturnType<E>[K]>\n ) => PayloadAction<`${Name}/${K extends string ? K : never}`, Parameters<ReturnType<E>[K]>>;\n};\n\ntype LiftSelectors<Tree, NewState> = Tree extends (...args: any) => infer Value\n ? Selector<NewState, Value>\n : {\n [K in keyof Tree]: LiftSelectors<Tree[K], NewState>;\n };\n\nfunction lift<Tree, RootState, State>(\n tree: Tree,\n selectState: (root: RootState) => State,\n): LiftSelectors<Tree, RootState> {\n if (typeof tree === \"function\") {\n return ((state: RootState) => tree(selectState(state))) as any;\n } else {\n return Object.fromEntries(Object.entries(tree as any).map(([k, v]) => [k, lift(v, selectState)])) as any;\n }\n}\n\nclass BlockBuilder<\n Name extends string,\n State,\n Creators extends Record<string, (...parameters: unknown[]) => PayloadAction<any, any>>,\n Context,\n Selectors extends Record<string, Selector<State>>,\n> {\n private constructor(\n readonly name: Name,\n readonly initial: State,\n /**\n * Per-message action reducers for this block.\n */\n private readonly reducers: Record<string, (state: State, ...payload: unknown[]) => State>,\n /**\n * Effects handlers for this block.\n */\n private readonly handlers: Effects<Context>[],\n private readonly select: Selectors,\n ) {}\n\n static init<Name extends string, State>(name: Name, initial: State): BlockBuilder<Name, State, {}, {}, {}> {\n return new BlockBuilder(name, initial, {}, [], {});\n }\n\n /**\n * Append an action handler to the block.\n * Action is a pure function that takes the state + arguments and returns a new state.\n */\n action<Action extends string, Payload extends unknown[] = []>(\n action: Action,\n handler: (state: State, ...payload: Payload) => State,\n ): BlockBuilder<\n Name,\n State,\n Creators & Record<Action, (...payload: Payload) => PayloadAction<`${Name}/${Action}`, Payload>>,\n Context,\n Selectors\n > {\n this.reducers[`${this.name}/${action}`] = handler as (state: State, ...payload: unknown[]) => State;\n return this as any;\n }\n\n /**\n * Append effect handlers to the block.\n * Effects can call any side effects provided from the context.\n */\n effects<E extends Effects<any>>(\n effects: E,\n ): BlockBuilder<\n Name,\n State,\n Creators & EffectsToCreators<Name, E>,\n Context & (E extends Effects<infer C> ? C : never),\n Selectors\n > {\n this.handlers.push(effects);\n return this as any;\n }\n\n selectors<SelectorsToAdd extends Record<string, Selector<State>>>(\n selectors: SelectorsToAdd,\n ): BlockBuilder<Name, State, Creators, Context, Selectors & SelectorsToAdd> {\n Object.assign(this.select as any, selectors);\n return this as any;\n }\n\n build(): ReduxBlock<State, Creators, Context, Selectors> {\n const initialState = this.initial;\n const blockName = this.name;\n return {\n actions: creator(this.name),\n effects: (context: Context) =>\n this.handlers.reduce(\n (acc, effect) =>\n Object.assign(\n acc,\n Object.fromEntries(\n Object.entries(effect(context)).map(([effectName, handler]) => [\n `${blockName}/${effectName}`,\n handler,\n ]),\n ),\n ),\n {} as Record<string, (...payload: unknown[]) => void>,\n ),\n reducer: (state = initialState, action: UnknownAction) => {\n const handler = this.reducers[action.type];\n if (!handler) return state;\n const payload = \"payload\" in action ? (action.payload as unknown[]) : undefined;\n return payload && payload.length ? handler(state, ...payload) : handler(state);\n },\n select: this.select,\n } as any;\n }\n}\n\nclass CompositionBuilder<\n Name extends string,\n BlockMap extends Record<string, ReduxBlock<any, any, any, any>>,\n Creators,\n Context,\n Selectors,\n> {\n private readonly blocks: BlockMap = {} as BlockMap;\n private readonly handlers: Effects<Context>[] = [];\n private readonly creators: Creators;\n private readonly select: Selectors = {} as Selectors;\n\n private constructor(private name: Name) {\n this.creators = creator(name) as Creators;\n }\n\n static init<Name extends string>(name: Name): CompositionBuilder<Name, {}, {}, {}, {}> {\n return new CompositionBuilder(name);\n }\n\n block<Name extends string, Block extends ReduxBlock<any, any, any, any>>(\n name: Name,\n block: Block,\n ): CompositionBuilder<\n Name,\n BlockMap & { [name in Name]: Block },\n Creators & { [name in Name]: ReduxBlock.TakeCreators<Block> },\n Context & ReduxBlock.TakeContext<Block>,\n Selectors & {\n [key in Name]: LiftSelectors<\n ReduxBlock.TakeSelectors<Block>,\n { [name in Name]: ReduxBlock.TakeState<Block> }\n >;\n }\n > {\n (this.blocks as Record<string, ReduxBlock<any, any, any, any>>)[name] = block;\n (this.creators as Record<string, unknown>)[name] = block.actions;\n (this.select as Record<string, unknown>)[name] = lift(block.select, (rootState: any) => rootState[name]);\n return this as any;\n }\n\n selectors<\n SelectorsToAdd extends Record<string, Selector<{ [K in keyof BlockMap]: ReduxBlock.TakeState<BlockMap[K]> }>>,\n >(selectors: SelectorsToAdd): CompositionBuilder<Name, BlockMap, Creators, Context, Selectors & SelectorsToAdd> {\n Object.assign(this.select as any, selectors);\n return this as any;\n }\n\n effects<E extends Effects<any>>(\n effects: E,\n ): CompositionBuilder<\n Name,\n BlockMap,\n Creators & EffectsToCreators<Name, E>,\n Context & (E extends Effects<infer ExtraContext> ? ExtraContext : never),\n Selectors\n > {\n (this.handlers as (Effects<Context> | E)[]).push(effects);\n return this as any;\n }\n\n build(): ReduxBlock<{ [K in keyof BlockMap]: ReduxBlock.TakeState<BlockMap[K]> }, Creators, Context, Selectors> {\n const reducers = Object.entries(this.blocks).map(([name, block]) => [name, block.reducer] as const);\n return {\n actions: this.creators,\n effects: (context: Context) => {\n const blockName = this.name;\n const result = this.handlers.reduce(\n (acc, effect) =>\n Object.assign(\n acc,\n Object.fromEntries(\n Object.entries(effect(context)).map(([effectName, handler]) => [\n `${blockName}/${effectName}`,\n handler,\n ]),\n ),\n ),\n {} as Record<string, (...payload: unknown[]) => void>,\n );\n Object.values(this.blocks).forEach((block) => Object.assign(result, block.effects(context)));\n return result;\n },\n reducer: (state: any, action: UnknownAction) => {\n let result = state;\n let changed = false;\n reducers.forEach(([name, reducer]) => {\n const original = state?.[name];\n const updated = reducer(original, action);\n if (updated !== original) {\n if (!changed) {\n changed = true;\n result = { ...state };\n }\n result[name] = updated;\n }\n });\n return result;\n },\n select: this.select,\n } as any;\n }\n}\n\nexport namespace ReduxBlock {\n type AnyBlock = ReduxBlock<any, any, any, any>;\n\n export type TakeState<Block extends AnyBlock> =\n Block extends ReduxBlock<infer State, any, any, any> ? State : never;\n export type TakeCreators<Block extends AnyBlock> =\n Block extends ReduxBlock<any, infer Creators, any, any> ? Creators : never;\n export type TakeContext<Block extends AnyBlock> =\n Block extends ReduxBlock<any, any, infer Context, any> ? Context : never;\n export type TakeSelectors<Block extends AnyBlock> =\n Block extends ReduxBlock<any, any, any, infer Selectors> ? Selectors : never;\n\n /**\n * Create a block builder.\n * It's a starting point for creating a block.\n */\n export function builder<Name extends string, State>(\n name: Name,\n initial: State,\n ): BlockBuilder<Name, State, {}, {}, {}> {\n return BlockBuilder.init(name, initial);\n }\n\n /**\n * Create a composition builder.\n */\n export function composition<Name extends string>(name: Name): CompositionBuilder<Name, {}, {}, {}, {}> {\n return CompositionBuilder.init(name);\n }\n\n /**\n * Create middleware for effects processing.\n * It expects to receive a context object that provides necessary dependencies for effect handlers.\n */\n export function middleware<Block extends AnyBlock>(block: Block, context: TakeContext<Block>): Middleware {\n const effects = block.effects(context);\n return () => (next) => (action) => {\n if (action && typeof action === \"object\" && \"type\" in action && has(effects, action.type as string)) {\n effects[action.type as string](...(\"payload\" in action ? (action.payload as unknown[]) : []));\n }\n next(action);\n };\n }\n\n /**\n * Create a new block with a different context shape.\n */\n export function mapContext<Block extends AnyBlock, NewContext>(\n block: Block,\n mapper: (context: NewContext) => TakeContext<Block>,\n ): ReduxBlock<TakeState<Block>, TakeCreators<Block>, NewContext, TakeSelectors<Block>> {\n return {\n actions: block.actions,\n reducer: block.reducer,\n effects: (ctx) => block.effects(mapper(ctx)),\n select: block.select,\n };\n }\n}\n"],"names":["has","v","k","creator","scope","target","property","payload","type","lift","tree","selectState","state","BlockBuilder","name","initial","reducers","handlers","select","action","handler","effects","selectors","initialState","blockName","context","acc","effect","effectName","CompositionBuilder","block","rootState","result","changed","reducer","original","updated","ReduxBlock","builder","composition","middleware","next","mapContext","mapper","ctx"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "redux-sacala",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.2",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Andrey Monkin",
|
|
@@ -17,21 +17,23 @@
|
|
|
17
17
|
}
|
|
18
18
|
},
|
|
19
19
|
"peerDependencies": {
|
|
20
|
-
"
|
|
20
|
+
"@reduxjs/toolkit": "^2.0.0",
|
|
21
|
+
"redux": "^5.0.0"
|
|
21
22
|
},
|
|
22
23
|
"devDependencies": {
|
|
23
24
|
"@eslint/js": "9.39.2",
|
|
24
|
-
"@
|
|
25
|
+
"@reduxjs/toolkit": "2.11.2",
|
|
26
|
+
"@types/node": "25.0.8",
|
|
25
27
|
"@typescript-eslint/eslint-plugin": "8.51.0",
|
|
26
|
-
"@typescript-eslint/parser": "8.
|
|
28
|
+
"@typescript-eslint/parser": "8.53.0",
|
|
27
29
|
"eslint": "9.39.2",
|
|
28
30
|
"eslint-config-prettier": "10.1.8",
|
|
29
31
|
"prettier": "3.7.4",
|
|
30
32
|
"typescript": "5.9.3",
|
|
31
|
-
"typescript-eslint": "8.
|
|
32
|
-
"vite": "7.3.
|
|
33
|
+
"typescript-eslint": "8.53.0",
|
|
34
|
+
"vite": "7.3.1",
|
|
33
35
|
"vite-plugin-dts": "4.5.4",
|
|
34
|
-
"vitest": "4.0.
|
|
36
|
+
"vitest": "4.0.17"
|
|
35
37
|
},
|
|
36
38
|
"scripts": {
|
|
37
39
|
"build": "vite build",
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
name: CI
|
|
2
|
-
|
|
3
|
-
on:
|
|
4
|
-
push:
|
|
5
|
-
pull_request:
|
|
6
|
-
|
|
7
|
-
jobs:
|
|
8
|
-
test:
|
|
9
|
-
runs-on: ubuntu-latest
|
|
10
|
-
|
|
11
|
-
steps:
|
|
12
|
-
- uses: actions/checkout@v4
|
|
13
|
-
|
|
14
|
-
- name: Use Node.js
|
|
15
|
-
uses: actions/setup-node@v4
|
|
16
|
-
with:
|
|
17
|
-
node-version: '20'
|
|
18
|
-
cache: 'npm'
|
|
19
|
-
|
|
20
|
-
- name: Install dependencies
|
|
21
|
-
run: npm ci
|
|
22
|
-
|
|
23
|
-
- name: Lint
|
|
24
|
-
run: npm run lint
|
|
25
|
-
|
|
26
|
-
- name: Build
|
|
27
|
-
run: npm run build
|
|
28
|
-
|
|
29
|
-
- name: Test
|
|
30
|
-
run: npm test
|