Compare commits
No commits in common. "760263b55559d7850346363f797eaa17bc26b6b7" and "90e90779e1b6d568e2a81338a3a7e8349ce3fb32" have entirely different histories.
760263b555
...
90e90779e1
@ -7,21 +7,6 @@ vars:
|
|||||||
PARENT_BRANCH: main
|
PARENT_BRANCH: main
|
||||||
|
|
||||||
tasks:
|
tasks:
|
||||||
release:gitea:
|
|
||||||
cmds:
|
|
||||||
- tea releases create -asset releases/updux-{{.VERSION}}.tgz -p --title {{.VERSION}} --tag {{.VERSION}}
|
|
||||||
vars:
|
|
||||||
VERSION: { sh: 'npm version --json | jq -r .updux' }
|
|
||||||
prerelease:
|
|
||||||
desc: deploy a prerelease
|
|
||||||
deps:
|
|
||||||
- build
|
|
||||||
- checks
|
|
||||||
cmds:
|
|
||||||
- npm version prerelease
|
|
||||||
- npm pack --pack-destination releases
|
|
||||||
- { task: 'release:gitea' }
|
|
||||||
|
|
||||||
pack:
|
pack:
|
||||||
desc: bundle the distro
|
desc: bundle the distro
|
||||||
deps: [build, checks]
|
deps: [build, checks]
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"version": "4.0.0-alpha.5",
|
"version": "4.0.0-alpha.4",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@mobily/ts-belt": "4.0.0-rc.5",
|
"@mobily/ts-belt": "4.0.0-rc.5",
|
||||||
|
45
src/Updux.ts
45
src/Updux.ts
@ -11,7 +11,6 @@ import {
|
|||||||
DuxSelectors,
|
DuxSelectors,
|
||||||
DuxState,
|
DuxState,
|
||||||
Mutation,
|
Mutation,
|
||||||
MutationEntry,
|
|
||||||
} from './types.js';
|
} from './types.js';
|
||||||
import baseMoize from 'moize/mjs/index.mjs';
|
import baseMoize from 'moize/mjs/index.mjs';
|
||||||
import { buildInitialState } from './initialState.js';
|
import { buildInitialState } from './initialState.js';
|
||||||
@ -37,7 +36,6 @@ interface ActionCreator<T extends string, P, A extends Array<any>> {
|
|||||||
|
|
||||||
const moize = (func) => baseMoize(func, { maxSize: 1 });
|
const moize = (func) => baseMoize(func, { maxSize: 1 });
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description main Updux class
|
* @description main Updux class
|
||||||
*/
|
*/
|
||||||
@ -70,7 +68,7 @@ export default class Updux<D extends DuxConfig> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/** @internal */
|
/** @internal */
|
||||||
#effects: EffectMiddleware<D>[] = [];
|
#effects = [];
|
||||||
|
|
||||||
/** @internal */
|
/** @internal */
|
||||||
#reactions: DuxReaction<D>[] = [];
|
#reactions: DuxReaction<D>[] = [];
|
||||||
@ -78,21 +76,12 @@ export default class Updux<D extends DuxConfig> {
|
|||||||
/** @internal */
|
/** @internal */
|
||||||
#mutations: any[] = [];
|
#mutations: any[] = [];
|
||||||
|
|
||||||
/** @internal */
|
|
||||||
#inheritedReducer: (state: DuxState<D> | undefined, action: AnyAction) => DuxState<D>;
|
|
||||||
|
|
||||||
constructor(private readonly duxConfig: D) {
|
constructor(private readonly duxConfig: D) {
|
||||||
if (duxConfig.subduxes)
|
if (duxConfig.subduxes)
|
||||||
this.#subduxes = D.map(duxConfig.subduxes, (s) =>
|
this.#subduxes = D.map(duxConfig.subduxes, (s) =>
|
||||||
s instanceof Updux ? s : new Updux(s),
|
s instanceof Updux ? s : new Updux(s),
|
||||||
);
|
);
|
||||||
|
|
||||||
this.#inheritedReducer = duxConfig.reducer;
|
|
||||||
|
|
||||||
this.#effects = duxConfig.effects ?? [];
|
|
||||||
|
|
||||||
this.#reactions = (duxConfig.reactions as any) ?? [];
|
|
||||||
|
|
||||||
this.#actions = buildActions(duxConfig.actions, this.#subduxes) as any;
|
this.#actions = buildActions(duxConfig.actions, this.#subduxes) as any;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -221,7 +210,7 @@ export default class Updux<D extends DuxConfig> {
|
|||||||
setDefaultMutation(
|
setDefaultMutation(
|
||||||
mutation: Mutation<any, DuxState<D>>,
|
mutation: Mutation<any, DuxState<D>>,
|
||||||
terminal?: boolean,
|
terminal?: boolean,
|
||||||
): Updux<D>;
|
);
|
||||||
setDefaultMutation(mutation, terminal = false) {
|
setDefaultMutation(mutation, terminal = false) {
|
||||||
this.#defaultMutation = { terminal, mutation };
|
this.#defaultMutation = { terminal, mutation };
|
||||||
return this;
|
return this;
|
||||||
@ -234,7 +223,6 @@ export default class Updux<D extends DuxConfig> {
|
|||||||
this.#mutations,
|
this.#mutations,
|
||||||
this.#defaultMutation,
|
this.#defaultMutation,
|
||||||
this.#subduxes,
|
this.#subduxes,
|
||||||
this.#inheritedReducer,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -294,11 +282,11 @@ export default class Updux<D extends DuxConfig> {
|
|||||||
return this as any;
|
return this as any;
|
||||||
}
|
}
|
||||||
|
|
||||||
get effects(): any[] {
|
get effects(): any {
|
||||||
return this.#memoBuildEffects(this.#effects, this.#subduxes);
|
return this.#memoBuildEffects(this.#effects, this.#subduxes);
|
||||||
}
|
}
|
||||||
|
|
||||||
get upreducer(): (action: AnyAction) => (state?: DuxState<D>) => DuxState<D> {
|
get upreducer() {
|
||||||
return (action: AnyAction) => (state?: DuxState<D>) => this.reducer(state, action);
|
return (action: AnyAction) => (state?: DuxState<D>) => this.reducer(state, action);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -330,29 +318,4 @@ export default class Updux<D extends DuxConfig> {
|
|||||||
return this.#memoBuildReactions(this.#reactions, this.#subduxes);
|
return this.#memoBuildReactions(this.#reactions, this.#subduxes);
|
||||||
}
|
}
|
||||||
|
|
||||||
get defaultMutation() {
|
|
||||||
return this.#defaultMutation;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @description Returns an object holding the Updux reducer and all its
|
|
||||||
* paraphernalia.
|
|
||||||
*/
|
|
||||||
get asDux(): {
|
|
||||||
initialState: DuxState<D>,
|
|
||||||
actions: DuxActions<D>,
|
|
||||||
reducer: (state: DuxState<D>, action: AnyAction) => DuxState<D>,
|
|
||||||
effects: EffectMiddleware<D>[],
|
|
||||||
reactions: DuxReaction<D>[],
|
|
||||||
} {
|
|
||||||
return {
|
|
||||||
initialState: this.initialState,
|
|
||||||
actions: this.actions,
|
|
||||||
effects: this.effects,
|
|
||||||
reactions: this.reactions,
|
|
||||||
reducer: this.reducer,
|
|
||||||
} as any;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,37 +0,0 @@
|
|||||||
import Updux, { createAction } from './index.js';
|
|
||||||
|
|
||||||
test('asDux', () => {
|
|
||||||
const actionA = createAction('actionA');
|
|
||||||
const defaultMutation = () => (state: number) => state + 1;
|
|
||||||
const dux = new Updux({
|
|
||||||
initialState: 13,
|
|
||||||
actions: { actionA },
|
|
||||||
})
|
|
||||||
.setDefaultMutation(defaultMutation)
|
|
||||||
.addReaction((api) => (state) => { })
|
|
||||||
.addEffect((api) => (next) => (action) => next(action));
|
|
||||||
|
|
||||||
const asDux = dux.asDux;
|
|
||||||
|
|
||||||
expect(asDux.initialState).toEqual(13);
|
|
||||||
|
|
||||||
expect(asDux.reducer).toBeTypeOf('function');
|
|
||||||
|
|
||||||
expect(asDux.actions.actionA()).toMatchObject({ type: 'actionA' });
|
|
||||||
|
|
||||||
expect(asDux.effects).toHaveLength(1);
|
|
||||||
|
|
||||||
expect(asDux.reactions).toHaveLength(1);
|
|
||||||
|
|
||||||
const newDux = new Updux(asDux);
|
|
||||||
|
|
||||||
expect(newDux.initialState).toEqual(13);
|
|
||||||
|
|
||||||
expect(newDux.reducer).toBeTypeOf('function');
|
|
||||||
|
|
||||||
expect(newDux.actions.actionA()).toMatchObject({ type: 'actionA' });
|
|
||||||
|
|
||||||
expect(newDux.effects).toHaveLength(1);
|
|
||||||
|
|
||||||
expect(newDux.reactions).toHaveLength(1);
|
|
||||||
});
|
|
@ -98,15 +98,3 @@ test('actionType as string', () => {
|
|||||||
dux.addMutation('unknown', () => (x) => x);
|
dux.addMutation('unknown', () => (x) => x);
|
||||||
}).toThrow();
|
}).toThrow();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('setDefaultMutation return value', () => {
|
|
||||||
const dux = new Updux({
|
|
||||||
initialState: 13,
|
|
||||||
});
|
|
||||||
|
|
||||||
let withDM = dux.setDefaultMutation(() => (state) => state);
|
|
||||||
|
|
||||||
expect(withDM).toEqual(dux);
|
|
||||||
|
|
||||||
expectTypeOf(withDM.initialState).toBeNumber();
|
|
||||||
});
|
|
||||||
|
@ -4,7 +4,6 @@ import * as rtk from '@reduxjs/toolkit';
|
|||||||
import { DuxConfig, Mutation } from './types.js';
|
import { DuxConfig, Mutation } from './types.js';
|
||||||
import { D } from '@mobily/ts-belt';
|
import { D } from '@mobily/ts-belt';
|
||||||
import Updux from './Updux.js';
|
import Updux from './Updux.js';
|
||||||
import { AnyAction } from '@reduxjs/toolkit';
|
|
||||||
|
|
||||||
export type MutationCase = {
|
export type MutationCase = {
|
||||||
matcher: (action: rtk.AnyAction) => boolean;
|
matcher: (action: rtk.AnyAction) => boolean;
|
||||||
@ -17,10 +16,12 @@ export function buildReducer(
|
|||||||
mutations: MutationCase[] = [],
|
mutations: MutationCase[] = [],
|
||||||
defaultMutation?: Omit<MutationCase, 'matcher'>,
|
defaultMutation?: Omit<MutationCase, 'matcher'>,
|
||||||
subduxes: Record<string, Updux<any>> = {},
|
subduxes: Record<string, Updux<any>> = {},
|
||||||
inheritedReducer?: (state: any, action: AnyAction) => any,
|
|
||||||
) {
|
) {
|
||||||
const subReducers = D.map(subduxes, D.getUnsafe('reducer'));
|
const subReducers = D.map(subduxes, D.getUnsafe('reducer'));
|
||||||
|
|
||||||
|
// TODO matcherMutation
|
||||||
|
// TODO defaultMutation
|
||||||
|
//
|
||||||
const reducer = (state = initialStateState, action: rtk.AnyAction) => {
|
const reducer = (state = initialStateState, action: rtk.AnyAction) => {
|
||||||
if (!action?.type) throw new Error('reducer called with a bad action');
|
if (!action?.type) throw new Error('reducer called with a bad action');
|
||||||
|
|
||||||
@ -29,13 +30,6 @@ export function buildReducer(
|
|||||||
if (active.length === 0 && defaultMutation)
|
if (active.length === 0 && defaultMutation)
|
||||||
active.push(defaultMutation as any);
|
active.push(defaultMutation as any);
|
||||||
|
|
||||||
if (!active.some(R.prop<any, any>('terminal')) && inheritedReducer) {
|
|
||||||
active.push({
|
|
||||||
mutation: (_payload, action) => (state) => {
|
|
||||||
return u(state, inheritedReducer(state, action));
|
|
||||||
},
|
|
||||||
} as any);
|
|
||||||
}
|
|
||||||
if (
|
if (
|
||||||
!active.some(R.prop<any, any>('terminal')) &&
|
!active.some(R.prop<any, any>('terminal')) &&
|
||||||
D.values(subReducers).length > 0
|
D.values(subReducers).length > 0
|
||||||
@ -55,6 +49,10 @@ export function buildReducer(
|
|||||||
} as any);
|
} as any);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// frozen objects don't play well with immer
|
||||||
|
// if (Object.isFrozen(state)) {
|
||||||
|
// state = { ...(state as any) };
|
||||||
|
// }
|
||||||
return active.reduce(
|
return active.reduce(
|
||||||
(state, { mutation }) =>
|
(state, { mutation }) =>
|
||||||
mutation((action as any).payload, action)(state),
|
mutation((action as any).payload, action)(state),
|
||||||
|
29
src/types.ts
29
src/types.ts
@ -5,7 +5,6 @@ import {
|
|||||||
Dispatch,
|
Dispatch,
|
||||||
MiddlewareAPI,
|
MiddlewareAPI,
|
||||||
} from '@reduxjs/toolkit';
|
} from '@reduxjs/toolkit';
|
||||||
import { EffectMiddleware } from './effects.js';
|
|
||||||
import Updux from './Updux.js';
|
import Updux from './Updux.js';
|
||||||
|
|
||||||
export type DuxConfig = Partial<{
|
export type DuxConfig = Partial<{
|
||||||
@ -13,9 +12,6 @@ export type DuxConfig = Partial<{
|
|||||||
subduxes: Record<string, DuxConfig>;
|
subduxes: Record<string, DuxConfig>;
|
||||||
actions: Record<string, ActionCreator<string> | Function>;
|
actions: Record<string, ActionCreator<string> | Function>;
|
||||||
selectors: Record<string, Function>;
|
selectors: Record<string, Function>;
|
||||||
effects: EffectMiddleware<any>[];
|
|
||||||
reactions: DuxReaction<any>[];
|
|
||||||
reducer: (state: any, action: AnyAction) => any;
|
|
||||||
}>;
|
}>;
|
||||||
|
|
||||||
type UpduxConfig<D> = D extends Updux<infer T> ? T : D;
|
type UpduxConfig<D> = D extends Updux<infer T> ? T : D;
|
||||||
@ -66,12 +62,6 @@ export type DuxActions<D> = ResolveActions<
|
|||||||
|
|
||||||
export type Subduxes = Record<string, Updux<any> | DuxConfig>;
|
export type Subduxes = Record<string, Updux<any> | DuxConfig>;
|
||||||
|
|
||||||
export type MutationEntry<S = any> = {
|
|
||||||
terminal: boolean;
|
|
||||||
matcher?: (action: AnyAction) => boolean;
|
|
||||||
mutation: Mutation<AnyAction, S>;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type Mutation<A = AnyAction, S = any> = (
|
export type Mutation<A = AnyAction, S = any> = (
|
||||||
payload: A extends {
|
payload: A extends {
|
||||||
payload: infer P;
|
payload: infer P;
|
||||||
@ -99,16 +89,14 @@ export type AugmentedMiddlewareAPI<D> = MiddlewareAPI<
|
|||||||
selectors: DuxSelectors<D>;
|
selectors: DuxSelectors<D>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type DuxSelectors<D> = ForceResolveObject<
|
export type DuxSelectors<D> = ForceResolveObject<(D extends { selectors: infer S } ? S : {}) &
|
||||||
(D extends { selectors: infer S } ? S : {}) &
|
|
||||||
(D extends { subduxes: infer SUB }
|
(D extends { subduxes: infer SUB }
|
||||||
? UnionToIntersection<
|
? UnionToIntersection<
|
||||||
Values<{
|
Values<{
|
||||||
[key in keyof SUB]: RebaseSelectors<key, SUB[key]>;
|
[key in keyof SUB]: RebaseSelectors<key, SUB[key]>;
|
||||||
}>
|
}>
|
||||||
>
|
>
|
||||||
: {})
|
: {})>;
|
||||||
>;
|
|
||||||
|
|
||||||
export type UnionToIntersection<U> = (
|
export type UnionToIntersection<U> = (
|
||||||
U extends any ? (k: U) => void : never
|
U extends any ? (k: U) => void : never
|
||||||
@ -129,10 +117,9 @@ type RebaseSelector<SLICE, S> = SLICE extends string
|
|||||||
: never;
|
: never;
|
||||||
type Values<X> = X[keyof X];
|
type Values<X> = X[keyof X];
|
||||||
|
|
||||||
export type DuxReaction<D extends DuxConfig> = (
|
|
||||||
api: AugmentedMiddlewareAPI<D>,
|
export type DuxReaction<D extends DuxConfig> =
|
||||||
) => (
|
(api: AugmentedMiddlewareAPI<D>) => (state: DuxState<D>,
|
||||||
state: DuxState<D>,
|
previousState: DuxState<D> | undefined,
|
||||||
previousState: DuxState<D> | undefined,
|
unsubscribe: () => void) => void;
|
||||||
unsubscribe: () => void,
|
|
||||||
) => void;
|
|
||||||
|
Loading…
Reference in New Issue
Block a user