Skip to content

Commit

Permalink
Merge pull request #4 from edvincandon/core-improvements
Browse files Browse the repository at this point in the history
Core improvements
  • Loading branch information
edvincandon authored Feb 17, 2024
2 parents eef0b83 + 10f0eeb commit 68a4d95
Show file tree
Hide file tree
Showing 26 changed files with 913 additions and 431 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ Next, create an optimistic reducer :
export const todosReducer = optimistron(
'todos',
initial,
recordHandlerFactory<Todo>({ itemIdKey: 'id', compare, eq }) // see section about state handlers
indexedStateFactory<Todo>({ itemIdKey: 'id', compare, eq }) // see section about state handlers
({ getState, create, update, remove }, action) => {
if (createTodo.match(action)) return create(action.payload.todo);
if (editTodo.match(action)) return update(action.payload.id, action.payload.update);
Expand Down
Binary file modified bun.lockb
Binary file not shown.
5 changes: 5 additions & 0 deletions bunfig.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
[test]

coverage = true
coverageThreshold = 0.9
coverageSkipTestFiles = true
6 changes: 0 additions & 6 deletions jest.config.js

This file was deleted.

86 changes: 41 additions & 45 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,47 +1,43 @@
{
"name": "optimistron",
"packageManager": "yarn@1.22.19",
"devDependencies": {
"@types/jest": "^29.5.5",
"@types/lodash": "^4.14.202",
"@types/react-dom": "^18.2.14",
"@typescript-eslint/eslint-plugin": "^6.19.1",
"@typescript-eslint/parser": "^6.19.1",
"clsx": "^2.0.0",
"eslint": "^8.56.0",
"formik": "^2.4.5",
"idb": "^8.0.0",
"imask": "^7.3.0",
"lodash": "^4.17.21",
"loglevel": "^1.9.1",
"mermaid": "^10.7.0",
"otpauth": "^9.2.2",
"papaparse": "^5.4.1",
"prettier": "^3.2.4",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-redux": "^9.1.0",
"react-router-dom": "^6.21.1",
"redux-saga": "^1.3.0",
"redux-thunk": "^3.1.0",
"serve": "^14.2.1",
"ts-jest": "^29.1.1",
"typescript": "^5.2.2"
},
"peerDependencies": {
"@reduxjs/toolkit": "^2.1.0",
"redux": "^5.0.1"
},
"scripts": {
"build:usecases": "bun build ./usecases/index.tsx --outdir ./usecases/dist --entry-naming [dir]/[name].[ext] --asset-naming [name].[ext] --target browser",
"serve:usecases": "bun serve ./usecases/",
"watch:usecases": "bun build:usecases --watch"
},
"version": "1.0.0",
"main": "index.js",
"author": "Edvin CANDON <edvin.candon@proton.ch>",
"license": "MIT",
"dependencies": {
"jest": "^29.7.0"
}
"name": "optimistron",
"packageManager": "yarn@1.22.19",
"devDependencies": {
"@types/lodash": "^4.14.202",
"@types/react-dom": "^18.2.14",
"@typescript-eslint/eslint-plugin": "^6.19.1",
"@typescript-eslint/parser": "^6.19.1",
"bun-types": "^1.0.26",
"clsx": "^2.0.0",
"eslint": "^8.56.0",
"formik": "^2.4.5",
"idb": "^8.0.0",
"imask": "^7.3.0",
"lodash": "^4.17.21",
"loglevel": "^1.9.1",
"mermaid": "^10.7.0",
"otpauth": "^9.2.2",
"papaparse": "^5.4.1",
"prettier": "^3.2.4",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-redux": "^9.1.0",
"react-router-dom": "^6.21.1",
"redux-saga": "^1.3.0",
"redux-thunk": "^3.1.0",
"serve": "^14.2.1",
"typescript": "^5.2.2"
},
"peerDependencies": {
"@reduxjs/toolkit": "^2.1.0",
"redux": "^5.0.1"
},
"scripts": {
"build:usecases": "bun build ./usecases/index.tsx --outdir ./usecases/dist --entry-naming [dir]/[name].[ext] --asset-naming [name].[ext] --target browser",
"serve:usecases": "bun serve ./usecases/",
"watch:usecases": "bun build:usecases --watch"
},
"version": "1.0.0",
"main": "index.js",
"author": "Edvin CANDON <edvin.candon@proton.ch>",
"license": "MIT"
}
47 changes: 19 additions & 28 deletions src/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,33 +3,30 @@ import { createAction } from '@reduxjs/toolkit';

import type { MetaKey } from '~constants';
import type { TransitionMeta, TransitionNamespace } from '~transitions';
import {
TransitionDedupeMode,
TransitionOperation,
getTransitionMeta,
isTransitionForNamespace,
withTransitionMeta,
} from '~transitions';
import { DedupeMode, Operation, getTransitionMeta, isTransitionForNamespace, prepareTransition } from '~transitions';

type EmptyPayload = { payload: never };
type PA_Empty = () => EmptyPayload;
type PA_Error = (error: unknown) => EmptyPayload & { error: Error };

/** Helper action matcher function that will match the supplied
* namespace when the transition operation is of type COMMIT */
const createMatcher =
<NS extends TransitionNamespace, PA extends PrepareAction<any>>(namespace: NS) =>
<NS extends string, PA extends PrepareAction<any>>(namespace: NS) =>
<
Result extends ReturnType<PA>,
Error = Result extends { error: infer Err } ? Err : never,
Meta = { [MetaKey]: TransitionMeta } & (Result extends { meta: infer Meta } ? Meta : object),
>(
action: Action,
): action is PayloadAction<Result['payload'], NS, Meta, Error> =>
isTransitionForNamespace(action, namespace) &&
getTransitionMeta(action).operation === TransitionOperation.COMMIT;
isTransitionForNamespace(action, namespace) && getTransitionMeta(action).operation === Operation.COMMIT;

export const createTransition =
<Type extends TransitionNamespace>(
const createTransition =
<Type extends TransitionNamespace, Op extends Operation>(
type: Type,
operation: TransitionOperation,
dedupe: TransitionDedupeMode = TransitionDedupeMode.OVERWRITE,
operation: Op,
dedupe: DedupeMode = DedupeMode.OVERWRITE,
) =>
<
PA extends PrepareAction<any>,
Expand All @@ -41,24 +38,18 @@ export const createTransition =
prepare: PA,
): ActionCreatorWithPreparedPayload<[transitionId: string, ...Params], Action['payload'], Type, Err, Meta> =>
createAction(type, (transitionId, ...params) =>
withTransitionMeta(prepare(...params), {
conflict: false,
failed: false,
prepareTransition(prepare(...params), {
id: transitionId,
operation,
dedupe,
}),
);

type EmptyPayload = { payload: never };
type PA_Empty = () => EmptyPayload;
type PA_Error = (error: unknown) => EmptyPayload & { error: Error };

export const createTransitions =
<Type extends TransitionNamespace>(type: Type, dedupe: TransitionDedupeMode = TransitionDedupeMode.OVERWRITE) =>
<Type extends string>(type: Type, dedupe: DedupeMode = DedupeMode.OVERWRITE) =>
<
PA_Stage extends PrepareAction<any>,
PA_Commit extends PA_Stage | PA_Empty = PA_Empty,
PA_Commit extends PrepareAction<any> = PA_Empty,
PA_Stash extends PrepareAction<any> = PA_Empty,
PA_Fail extends PrepareAction<any> = PA_Error,
>(
Expand All @@ -85,11 +76,11 @@ export const createTransitions =
const stashPA = noOptions ? emptyPA : options.stash ?? emptyPA;

return {
amend: createTransition(`${type}::amend`, TransitionOperation.AMEND, dedupe)(stagePA),
stage: createTransition(`${type}::stage`, TransitionOperation.STAGE, dedupe)(stagePA),
commit: createTransition(`${type}::commit`, TransitionOperation.COMMIT, dedupe)(commitPA as PA_Commit),
fail: createTransition(`${type}::fail`, TransitionOperation.FAIL, dedupe)(failPA as PA_Fail),
stash: createTransition(`${type}::stash`, TransitionOperation.STASH, dedupe)(stashPA as PA_Stash),
amend: createTransition(`${type}::amend`, Operation.AMEND, dedupe)(stagePA),
stage: createTransition(`${type}::stage`, Operation.STAGE, dedupe)(stagePA),
commit: createTransition(`${type}::commit`, Operation.COMMIT, dedupe)(commitPA as PA_Commit),
fail: createTransition(`${type}::fail`, Operation.FAIL, dedupe)(failPA as PA_Fail),
stash: createTransition(`${type}::stash`, Operation.STASH, dedupe)(stashPA as PA_Stash),
match: createMatcher<Type, PA_Stage>(type),
};
};
1 change: 0 additions & 1 deletion src/constants.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,2 @@
export const MetaKey = '__OPTIMISTRON_META__' as const;
export const ReducerIdKey = '__OPTIMISTRON_REF_ID__' as const;
export const InitAction = { type: '__OPTIMISTRON_INIT__' } as const;
115 changes: 0 additions & 115 deletions src/optimistron.spec.ts

This file was deleted.

32 changes: 14 additions & 18 deletions src/optimistron.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@ import { ReducerMap, bindReducer, type HandlerReducer } from '~reducer';
import type { StateHandler, TransitionState } from '~state';
import { bindStateFactory, buildTransitionState, transitionStateFactory } from '~state';
import {
TransitionOperation,
Operation,
getTransitionID,
getTransitionMeta,
isTransitionForNamespace,
processTransition,
sanitizeTransitions,
updateTransition,
toCommit,
} from '~transitions';

export const optimistron = <S, C extends any[], U extends any[], D extends any[]>(
Expand Down Expand Up @@ -40,24 +40,20 @@ export const optimistron = <S, C extends any[], U extends any[], D extends any[]
const nextTransitions = processTransition(options?.sanitizeAction?.(action) ?? action, transitions);
const { operation, id } = getTransitionMeta(action);

switch (operation) {
case TransitionOperation.COMMIT: {
/* Find the matching staged action in the transition list. If
* it does not exist, do nothing else treat it as a commit */
const staged = transitions.find((entry) => id === getTransitionID(entry));
if (!staged) return next(state, nextTransitions);
if (operation === Operation.COMMIT) {
/* Find the matching staged action in the transition list.
* Treat it as a commit if it exists - noop otherwise */
const staged = transitions.find((entry) => id === getTransitionID(entry));
if (!staged) return next(state, nextTransitions);

/* Comitting will apply the action to the reducer */
const commit = updateTransition(staged, { operation: TransitionOperation.COMMIT });
return next(boundReducer(transitionState, commit), nextTransitions);
}
default: {
/* Every other transition actions will not be applied.
* If you need to get the optimistic state use the provided
* selectors which will apply the optimistic transitions */
return next(state, nextTransitions);
}
/* Comitting will apply the action to the reducer */
return next(boundReducer(transitionState, toCommit(staged)), nextTransitions);
}

/* Every other transition actions will not be applied.
* If you need to get the optimistic state use the provided
* selectors which will apply the optimistic transitions */
return next(state, nextTransitions);
}

return next(boundReducer(transitionState, action), transitions);
Expand Down
Loading

0 comments on commit 68a4d95

Please sign in to comment.