Compare commits

..

No commits in common. "760263b55559d7850346363f797eaa17bc26b6b7" and "90e90779e1b6d568e2a81338a3a7e8349ce3fb32" have entirely different histories.

7 changed files with 20 additions and 136 deletions

View File

@ -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]

View File

@ -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",

View File

@ -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;
}
} }

View File

@ -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);
});

View File

@ -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();
});

View File

@ -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),

View File

@ -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, unsubscribe: () => void) => void;
) => void;