Compare commits
166 Commits
v0.2.0
...
typescript
Author | SHA1 | Date |
---|---|---|
Yanick Champoux | ace7368b1b | |
Yanick Champoux | 624a60cb7f | |
Yanick Champoux | 82f5d53df2 | |
Yanick Champoux | f206026087 | |
Yanick Champoux | 7f1bcbddef | |
Yanick Champoux | 0b0ea7ea66 | |
Yanick Champoux | dd0dda0970 | |
Yanick Champoux | 23724931e9 | |
Yanick Champoux | 13c9603251 | |
Yanick Champoux | 82e8ba7385 | |
Yanick Champoux | 27e27aa70d | |
Yanick Champoux | 1255876c38 | |
Yanick Champoux | 6b508c9db2 | |
Yanick Champoux | 16397ce3ee | |
Yanick Champoux | fbe624a14c | |
Yanick Champoux | 702a3e76d4 | |
Yanick Champoux | 0f3aeff396 | |
Yanick Champoux | 3e6d2dae55 | |
Yanick Champoux | 51421c1052 | |
Yanick Champoux | 63d8235346 | |
Yanick Champoux | 141d959d28 | |
Yanick Champoux | c5ca566c12 | |
Yanick Champoux | 7583f9e98b | |
Yanick Champoux | e3dce45f50 | |
Yanick Champoux | 80a356ff1a | |
Yanick Champoux | af6db3403c | |
Yanick Champoux | c8bb1e5096 | |
Yanick Champoux | 9255548326 | |
Yanick Champoux | b4a96b67ea | |
Yanick Champoux | fa3300004d | |
Yanick Champoux | 378d61337f | |
Yanick Champoux | d12d114af8 | |
Yanick Champoux | 36b19316ba | |
Yanick Champoux | 6a4525f507 | |
Yanick Champoux | d44986e990 | |
Yanick Champoux | 34acc63bef | |
Yanick Champoux | 43702fe096 | |
Yanick Champoux | 3bb9221c27 | |
Yanick Champoux | 6cb7f14407 | |
Yanick Champoux | 6a6bb1636a | |
Yanick Champoux | 7494fe553e | |
Yanick Champoux | 325ad47731 | |
Yanick Champoux | b96f5e72ac | |
Yanick Champoux | fa837a345e | |
Yanick Champoux | e0ffaef07a | |
Yanick Champoux | c609a19ef8 | |
Yanick Champoux | 46b0565819 | |
Yanick Champoux | 3c78f34d5b | |
Yanick Champoux | 9772ce4bec | |
Yanick Champoux | 4d4a5dde50 | |
Yanick Champoux | 64782096df | |
Yanick Champoux | d20f50e156 | |
Yanick Champoux | 38c91d435f | |
Yanick Champoux | f2468e3b82 | |
Yanick Champoux | ef5751eb69 | |
Yanick Champoux | 3aac2e092e | |
Yanick Champoux | bb5218d2f6 | |
Yanick Champoux | ce666b75bb | |
Yanick Champoux | a272ee0329 | |
Yanick Champoux | f51c8158fc | |
Yanick Champoux | 541790425a | |
Yanick Champoux | f1b7677f0f | |
Yanick Champoux | 8314ff94ca | |
Yanick Champoux | 88d62536ad | |
Yanick Champoux | 707d9ec923 | |
Yanick Champoux | 4e4fa13d90 | |
Yanick Champoux | 27958a6d14 | |
Yanick Champoux | 85e478c02f | |
Yanick Champoux | a8f97940e8 | |
Yanick Champoux | 55812ed6d8 | |
Yanick Champoux | 7cd7e27805 | |
Yanick Champoux | c5a9a7397a | |
Yanick Champoux | 69a8781b4b | |
Yanick Champoux | a657813415 | |
Yanick Champoux | 440c76d408 | |
Yanick Champoux | 3394a00419 | |
Yanick Champoux | 83f28a1720 | |
Yanick Champoux | 6fb14a26d0 | |
Yanick Champoux | 33ded1448e | |
Yanick Champoux | 8c026314f5 | |
Yanick Champoux | d75da07d3f | |
Yanick Champoux | 6dd8b1af9e | |
Yanick Champoux | 9778e04a8d | |
Yanick Champoux | 2e357b71e2 | |
Yanick Champoux | a5e8410768 | |
Yanick Champoux | d14eb08bf0 | |
Yanick Champoux | 912ea85edc | |
Yanick Champoux | c1c1edf588 | |
Yanick Champoux | 5eeb4d4ab7 | |
Yanick Champoux | d61c9478a2 | |
Yanick Champoux | 5247aa2255 | |
Yanick Champoux | 04159ec7cc | |
Yanick Champoux | 2c26f9652b | |
Yanick Champoux | e4eff8a113 | |
Yanick Champoux | b7ada06e3c | |
Yanick Champoux | 0ecb1059ee | |
Yanick Champoux | 1759ce16c6 | |
Yanick Champoux | 1a653fac5b | |
Yanick Champoux | 0870767994 | |
Yanick Champoux | fd064f5996 | |
Yanick Champoux | 003a1309bd | |
Yanick Champoux | c96a6ea07c | |
Yanick Champoux | 1f62028e4e | |
Yanick Champoux | 74c29ce2d0 | |
Yanick Champoux | 99ba091a51 | |
Yanick Champoux | a39715f0a8 | |
Yanick Champoux | 7eab50ff60 | |
Yanick Champoux | 0ebf32c71b | |
Yanick Champoux | afc1a14f56 | |
Yanick Champoux | 9c45ee7efc | |
Yanick Champoux | 86dd272603 | |
Yanick Champoux | f0c1d6f015 | |
Yanick Champoux | 38199a5ec7 | |
Yanick Champoux | 97f21aac2a | |
Trey Bianchini | 84a9c4f77f | |
Yanick Champoux | 26bf0c962a | |
Trey Bianchini | f8edbcaa49 | |
Yanick Champoux | f0653442f3 | |
Yanick Champoux | e3c5aad399 | |
Yanick Champoux | dce52c56d0 | |
Yanick Champoux | 6349d720b8 | |
Yanick Champoux | f6b7c2b15a | |
Yanick Champoux | a71d3f8781 | |
Yanick Champoux | b7339c25e5 | |
Yanick Champoux | 4665ee39f7 | |
Yanick Champoux | 91923bfd49 | |
Yanick Champoux | 64a36c4088 | |
Yanick Champoux | e7fbd582fc | |
Yanick Champoux | 4c0d7b366f | |
Yanick Champoux | 931377b584 | |
Yanick Champoux | f744616cb2 | |
Yanick Champoux | 1825e8d269 | |
Yanick Champoux | 499e987219 | |
Yanick Champoux | 27ae46dbab | |
Yanick Champoux | 7ddc187f2b | |
Yanick Champoux | c8f497f5e9 | |
Yanick Champoux | 1388282d81 | |
Yanick Champoux | 4acfe5b17e | |
Yanick Champoux | 0d0890ffff | |
Yanick Champoux | fa55762efc | |
Yanick Champoux | 93bebc5acf | |
Yanick Champoux | fe34d01a41 | |
Yanick Champoux | 54ad7f2512 | |
Yanick Champoux | d90d72148c | |
Yanick Champoux | 73c2776826 | |
Yanick Champoux | 5465c53cb5 | |
Yanick Champoux | e78568b3cb | |
Yanick Champoux | 057226dfd1 | |
Yanick Champoux | 5c5e614f4b | |
Yanick Champoux | 8d4542fffa | |
Yanick Champoux | 9b5a7981d5 | |
Yanick Champoux | 74d29d3126 | |
Yanick Champoux | 1379055264 | |
Yanick Champoux | da7ac2ef6e | |
Yanick Champoux | 47b78f43cc | |
Yanick Champoux | 0d298cb285 | |
Yanick Champoux | 15e154b6ec | |
Yanick Champoux | 0e03628502 | |
Yanick Champoux | 73973a9588 | |
Yanick Champoux | 82373634ed | |
Yanick Champoux | 122ff71260 | |
Yanick Champoux | 1642bab09a | |
Yanick Champoux | a6b83bbfa0 | |
Yanick Champoux | fb61c9bc1e | |
Yanick Champoux | 87d8a4de38 | |
Yanick Champoux | 23312d2f15 |
|
@ -0,0 +1 @@
|
|||
out/
|
|
@ -0,0 +1,26 @@
|
|||
module.exports = {
|
||||
parserOptions: {
|
||||
ecmaVersion: 'latest',
|
||||
sourceType: 'module',
|
||||
},
|
||||
env: {
|
||||
es6: true,
|
||||
browser: true,
|
||||
},
|
||||
plugins: ['todo-plz', 'no-only-tests'],
|
||||
overrides: [],
|
||||
rules: {
|
||||
'no-console': ['error'],
|
||||
'todo-plz/ticket-ref': ['error', { pattern: 'GT[0-9]+' }],
|
||||
'no-only-tests/no-only-tests': [
|
||||
'error',
|
||||
{
|
||||
block: ['test'],
|
||||
focus: ['only'],
|
||||
},
|
||||
],
|
||||
},
|
||||
settings: {
|
||||
// ...
|
||||
},
|
||||
};
|
|
@ -1,2 +1,11 @@
|
|||
node_modules/
|
||||
tsconfig.tsbuildinfo
|
||||
**/*.orig
|
||||
dist
|
||||
package-lock.json
|
||||
yarn.lock
|
||||
.nyc_output/
|
||||
pnpm-debug.log
|
||||
yarn-error.log
|
||||
GPUCache/
|
||||
updux-2.0.0.tgz
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
*.test.*
|
||||
test.*
|
||||
docs
|
||||
node_modules/
|
||||
tsconfig.tsbuildinfo
|
||||
**/*.orig
|
||||
dist
|
||||
package-lock.json
|
||||
yarn.lock
|
||||
.nyc_output/
|
||||
pnpm-debug.log
|
||||
pnpm-lock.yaml
|
||||
yarn-error.log
|
||||
GPUCache/
|
||||
updux-2.0.0.tgz
|
||||
.travis.yml
|
||||
.prettierignore
|
||||
tools/gen_sidebar.pl
|
|
@ -0,0 +1,22 @@
|
|||
exclude: static/fontawesome|^build
|
||||
|
||||
default_stages:
|
||||
- merge-commit
|
||||
- push
|
||||
|
||||
repos:
|
||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||
rev: v4.3.0
|
||||
hooks:
|
||||
- id: end-of-file-fixer
|
||||
stages: [merge-commit, push]
|
||||
- id: trailing-whitespace
|
||||
stages: [merge-commit, push]
|
||||
- id: check-merge-conflict
|
||||
- repo: local
|
||||
hooks:
|
||||
- id: lint
|
||||
name: lint
|
||||
entry: task lint:fix --
|
||||
language: system
|
||||
files: ''
|
|
@ -0,0 +1,7 @@
|
|||
dist
|
||||
out
|
||||
pnpm-lock.yaml
|
||||
types
|
||||
docs
|
||||
Changes
|
||||
.prettierignore
|
|
@ -0,0 +1,9 @@
|
|||
module.exports = {
|
||||
semi: true,
|
||||
trailingComma: 'all',
|
||||
singleQuote: true,
|
||||
printWidth: 80,
|
||||
tabWidth: 4,
|
||||
useTabs: false,
|
||||
plugins: [],
|
||||
};
|
14
.travis.yml
14
.travis.yml
|
@ -1,16 +1,16 @@
|
|||
language: node_js
|
||||
|
||||
node_js:
|
||||
- 'node'
|
||||
- 'lts/*'
|
||||
- 'node'
|
||||
- 'lts/*'
|
||||
|
||||
install:
|
||||
- npm uninstall typescript --no-save
|
||||
- npm install
|
||||
- npm uninstall typescript --no-save
|
||||
- npm install
|
||||
|
||||
cache:
|
||||
directories:
|
||||
- node_modules
|
||||
directories:
|
||||
- node_modules
|
||||
|
||||
git:
|
||||
depth: 1
|
||||
depth: 1
|
||||
|
|
|
@ -0,0 +1,69 @@
|
|||
# Changelog
|
||||
|
||||
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
|
||||
|
||||
## 4.0.0 (2022-08-30)
|
||||
|
||||
### ⚠ BREAKING CHANGES
|
||||
|
||||
- Return to a JavaScript core (sorry Typescript).
|
||||
|
||||
## 3.0.0 (2021-05-02)
|
||||
|
||||
### ⚠ BREAKING CHANGES
|
||||
|
||||
- Upgrade to Typescript 4.
|
||||
- Switch from 'updeep' to '@yanick/updeep'.
|
||||
|
||||
## [2.1.0](https://github.com/yanick/updux/compare/v2.0.0...v2.1.0) (2020-06-19)
|
||||
|
||||
### Features
|
||||
|
||||
- add support for subscriptions ([9c45ee7](https://github.com/yanick/updux/commit/9c45ee7efcb623defb9da5d01165fbad0e4424f9))
|
||||
|
||||
## [2.0.0](https://github.com/yanick/updux/compare/v1.2.0...v2.0.0) (2020-06-13)
|
||||
|
||||
### ⚠ BREAKING CHANGES
|
||||
|
||||
- use ts-action for action creation
|
||||
- middleware support refined
|
||||
|
||||
### Features
|
||||
|
||||
- allow adding actionCreators via addAction() ([27ae46d](https://github.com/yanick/updux/commit/27ae46dbab289b27ea99aca149aaa3b7c90ee7d0))
|
||||
- middleware support refined ([d90d721](https://github.com/yanick/updux/commit/d90d72148c2d4ba186a19650d961c64df5791c55))
|
||||
- moving documentation to docsify ([fa55762](https://github.com/yanick/updux/commit/fa55762efcbd4db356150f6022fd62750adc27a9))
|
||||
- use ts-action for action creation ([6349d72](https://github.com/yanick/updux/commit/6349d720b8aba4b443a7225d6a377c5c929a3021))
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- state is a PreloadedState<S> ([93bebc5](https://github.com/yanick/updux/commit/93bebc5acf193752aa6b4857507f05d52b1b7665))
|
||||
|
||||
## 1.2.0 2019-11-06
|
||||
|
||||
- The middleware's 'getState' returns the local state of its updux,
|
||||
instead of the root state. Plus we add `getRootState` to get
|
||||
the root state.
|
||||
|
||||
## 1.1.0 2019-11-05
|
||||
|
||||
- Document mapping behavior of the '*' subdux.
|
||||
- add subduxUpreducer.
|
||||
- add sink mutations.
|
||||
|
||||
## 1.0.0 2019-11-04
|
||||
|
||||
- Pretty big rework.
|
||||
- Better documentation.
|
||||
|
||||
## 0.2.0 2019-10-24
|
||||
|
||||
- Converted everything to Typescript.
|
||||
|
||||
## 0.1.0 2019-10-22
|
||||
|
||||
- Add 'actions' in the config.
|
||||
|
||||
## 0.0.1 2019-10-22
|
||||
|
||||
- Initial release.
|
|
@ -0,0 +1,128 @@
|
|||
# Contributor Covenant Code of Conduct
|
||||
|
||||
## Our Pledge
|
||||
|
||||
We as members, contributors, and leaders pledge to make participation in our
|
||||
community a harassment-free experience for everyone, regardless of age, body
|
||||
size, visible or invisible disability, ethnicity, sex characteristics, gender
|
||||
identity and expression, level of experience, education, socio-economic status,
|
||||
nationality, personal appearance, race, religion, or sexual identity
|
||||
and orientation.
|
||||
|
||||
We pledge to act and interact in ways that contribute to an open, welcoming,
|
||||
diverse, inclusive, and healthy community.
|
||||
|
||||
## Our Standards
|
||||
|
||||
Examples of behavior that contributes to a positive environment for our
|
||||
community include:
|
||||
|
||||
- Demonstrating empathy and kindness toward other people
|
||||
- Being respectful of differing opinions, viewpoints, and experiences
|
||||
- Giving and gracefully accepting constructive feedback
|
||||
- Accepting responsibility and apologizing to those affected by our mistakes,
|
||||
and learning from the experience
|
||||
- Focusing on what is best not just for us as individuals, but for the
|
||||
overall community
|
||||
|
||||
Examples of unacceptable behavior include:
|
||||
|
||||
- The use of sexualized language or imagery, and sexual attention or
|
||||
advances of any kind
|
||||
- Trolling, insulting or derogatory comments, and personal or political attacks
|
||||
- Public or private harassment
|
||||
- Publishing others' private information, such as a physical or email
|
||||
address, without their explicit permission
|
||||
- Other conduct which could reasonably be considered inappropriate in a
|
||||
professional setting
|
||||
|
||||
## Enforcement Responsibilities
|
||||
|
||||
Community leaders are responsible for clarifying and enforcing our standards of
|
||||
acceptable behavior and will take appropriate and fair corrective action in
|
||||
response to any behavior that they deem inappropriate, threatening, offensive,
|
||||
or harmful.
|
||||
|
||||
Community leaders have the right and responsibility to remove, edit, or reject
|
||||
comments, commits, code, wiki edits, issues, and other contributions that are
|
||||
not aligned to this Code of Conduct, and will communicate reasons for moderation
|
||||
decisions when appropriate.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies within all community spaces, and also applies when
|
||||
an individual is officially representing the community in public spaces.
|
||||
Examples of representing our community include using an official e-mail address,
|
||||
posting via an official social media account, or acting as an appointed
|
||||
representative at an online or offline event.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||
reported to the community leaders responsible for enforcement at
|
||||
[yanick@babyl.ca](mailto://yanick@babyl.ca).
|
||||
All complaints will be reviewed and investigated promptly and fairly.
|
||||
|
||||
All community leaders are obligated to respect the privacy and security of the
|
||||
reporter of any incident.
|
||||
|
||||
## Enforcement Guidelines
|
||||
|
||||
Community leaders will follow these Community Impact Guidelines in determining
|
||||
the consequences for any action they deem in violation of this Code of Conduct:
|
||||
|
||||
### 1. Correction
|
||||
|
||||
**Community Impact**: Use of inappropriate language or other behavior deemed
|
||||
unprofessional or unwelcome in the community.
|
||||
|
||||
**Consequence**: A private, written warning from community leaders, providing
|
||||
clarity around the nature of the violation and an explanation of why the
|
||||
behavior was inappropriate. A public apology may be requested.
|
||||
|
||||
### 2. Warning
|
||||
|
||||
**Community Impact**: A violation through a single incident or series
|
||||
of actions.
|
||||
|
||||
**Consequence**: A warning with consequences for continued behavior. No
|
||||
interaction with the people involved, including unsolicited interaction with
|
||||
those enforcing the Code of Conduct, for a specified period of time. This
|
||||
includes avoiding interactions in community spaces as well as external channels
|
||||
like social media. Violating these terms may lead to a temporary or
|
||||
permanent ban.
|
||||
|
||||
### 3. Temporary Ban
|
||||
|
||||
**Community Impact**: A serious violation of community standards, including
|
||||
sustained inappropriate behavior.
|
||||
|
||||
**Consequence**: A temporary ban from any sort of interaction or public
|
||||
communication with the community for a specified period of time. No public or
|
||||
private interaction with the people involved, including unsolicited interaction
|
||||
with those enforcing the Code of Conduct, is allowed during this period.
|
||||
Violating these terms may lead to a permanent ban.
|
||||
|
||||
### 4. Permanent Ban
|
||||
|
||||
**Community Impact**: Demonstrating a pattern of violation of community
|
||||
standards, including sustained inappropriate behavior, harassment of an
|
||||
individual, or aggression toward or disparagement of classes of individuals.
|
||||
|
||||
**Consequence**: A permanent ban from any sort of public interaction within
|
||||
the community.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
|
||||
version 2.0, available at
|
||||
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
|
||||
|
||||
Community Impact Guidelines were inspired by [Mozilla's code of conduct
|
||||
enforcement ladder](https://github.com/mozilla/diversity).
|
||||
|
||||
[homepage]: https://www.contributor-covenant.org
|
||||
|
||||
For answers to common questions about this code of conduct, see the FAQ at
|
||||
https://www.contributor-covenant.org/faq. Translations are available at
|
||||
https://www.contributor-covenant.org/translations.
|
10
Changes
10
Changes
|
@ -1,10 +0,0 @@
|
|||
# Revision history for Updux
|
||||
|
||||
0.2.0 2019-10-24
|
||||
- Converted everything to Typescript.
|
||||
|
||||
0.1.0 2019-10-22
|
||||
- Add 'actions' in the config.
|
||||
|
||||
0.0.1 2019-10-22
|
||||
- Initial release.
|
438
README.md
438
README.md
|
@ -1,23 +1,37 @@
|
|||
|
||||
# What's Updux?
|
||||
|
||||
So, I'm a fan of [Redux](https://redux.js.org). Two days ago I discovered
|
||||
[rematch](https://rematch.github.io/rematch) alonside a few other frameworks built atop Redux.
|
||||
[rematch](https://rematch.github.io/rematch) alonside a few other frameworks built atop Redux.
|
||||
|
||||
It has a couple of pretty good ideas that removes some of the
|
||||
boilerplate. Keeping mutations and asynchronous effects close to the
|
||||
reducer definition, à la [VueX][]? Nice. Automatically infering the
|
||||
It has a couple of pretty good ideas that removes some of the
|
||||
boilerplate. Keeping mutations and asynchronous effects close to the
|
||||
reducer definition? Nice. Automatically infering the
|
||||
actions from the said mutations and effects? Genius!
|
||||
|
||||
But it also enforces a flat hierarchy of reducers -- where
|
||||
is the fun in that? And I'm also having a strong love for
|
||||
[Updeep](https://github.com/substantial/updeep), so I want reducer state updates to leverage the heck out of it.
|
||||
|
||||
All that to say, I had some fun yesterday and hacked a proto-lovechild
|
||||
of `Rematch` and `Updeep`, with a dash of [VueX](https://vuex.vuejs.org/) inspiration.
|
||||
I call it... `Updux`.
|
||||
All that to say, say hello to `Updux`. Heavily inspired by `rematch`, but twisted
|
||||
to work with `updeep` and to fit my peculiar needs. It offers features such as
|
||||
|
||||
- Mimic the way VueX has mutations (reducer reactions to specific actions) and
|
||||
effects (middleware reacting to actions that can be asynchronous and/or
|
||||
have side-effects), so everything pertaining to a store are all defined
|
||||
in the space place.
|
||||
- Automatically gather all actions used by the updux's effects and mutations,
|
||||
and makes then accessible as attributes to the `dispatch` object of the
|
||||
store.
|
||||
- Mutations have a signature that is friendly to Updux and Immer.
|
||||
- Also, the mutation signature auto-unwrap the payload of the actions for you.
|
||||
- TypeScript types.
|
||||
|
||||
Fair warning: this package is still very new, probably very buggy,
|
||||
definitively very badly documented, and very subject to changes. Caveat
|
||||
Maxima Emptor.
|
||||
|
||||
# Synopsis
|
||||
|
||||
```
|
||||
import updux from 'updux';
|
||||
|
||||
|
@ -29,7 +43,7 @@ const {
|
|||
actions,
|
||||
middleware,
|
||||
createStore,
|
||||
} = updux({
|
||||
} = new Updux({
|
||||
initial: {
|
||||
counter: 0,
|
||||
},
|
||||
|
@ -46,7 +60,10 @@ const {
|
|||
};
|
||||
},
|
||||
actions: {
|
||||
customAction: ( someArg ) => ({ someProp: someArg }),
|
||||
customAction: ( someArg ) => ({
|
||||
type: "custom",
|
||||
payload: { someProp: someArg }
|
||||
}),
|
||||
},
|
||||
|
||||
});
|
||||
|
@ -58,329 +75,152 @@ store.dispatch.inc(3);
|
|||
|
||||
# Description
|
||||
|
||||
`Updux` exports one function, `updux`, both as a named export and as
|
||||
its default export.
|
||||
Full documentation can be [found here](https://yanick.github.io/updux/).
|
||||
Right now the best way to understand the whole thing is to go
|
||||
through the [tutorial](https://yanick.github.io/updux/#/tutorial)
|
||||
|
||||
## helpers = updux(config);
|
||||
## Exporting upduxes
|
||||
|
||||
`updux` is a way to minimize and simplify the boilerplate associated with the
|
||||
creation of a `Redux` store. It takes a shorthand configuration
|
||||
object, and generates the appropriate reducer, actions, middleware, etc.
|
||||
In true `Redux`-like fashion, just like reducers can be composed
|
||||
of sub-reducers, upduxs can be made of sub-upduxs.
|
||||
|
||||
### config
|
||||
|
||||
The config object recognize following properties.
|
||||
|
||||
#### initial
|
||||
|
||||
The default initial state of the reducer. Can be anything your
|
||||
heart desires.
|
||||
|
||||
#### subduxes
|
||||
|
||||
Object mapping slices of the state to sub-upduxs.
|
||||
|
||||
For example, if in plain Redux you would do
|
||||
If you are creating upduxes that will be used as subduxes
|
||||
by other upduxes, or as
|
||||
[ducks](https://github.com/erikras/ducks-modular-redux)-like containers, I
|
||||
recommend that you export the Updux instance as the default export:
|
||||
|
||||
```
|
||||
import { combineReducers } from 'redux';
|
||||
import todosReducer from './todos';
|
||||
import statisticsReducer from './statistics';
|
||||
import Updux from 'updux';
|
||||
|
||||
const rootReducer = combineReducers({
|
||||
todos: todosReducer,
|
||||
stats: statisticsReducer,
|
||||
});
|
||||
const updux = new Updux({ ... });
|
||||
|
||||
export default updux;
|
||||
```
|
||||
|
||||
then with Updux you'd do
|
||||
Then you can use them as subduxes like this:
|
||||
|
||||
```
|
||||
import { updux } from 'updux';
|
||||
import todos from './todos';
|
||||
import statistics from './statistics';
|
||||
import Updux from 'updux';
|
||||
import foo from './foo'; // foo is an Updux
|
||||
import bar from './bar'; // bar is an Updux as well
|
||||
|
||||
const rootUpdux = updux({
|
||||
const updux = new Updux({
|
||||
subduxes: {
|
||||
todos, statistics
|
||||
foo, bar
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
#### mutations
|
||||
|
||||
Object mapping actions to the associated state mutation.
|
||||
|
||||
For example, in `Redux` you'd do
|
||||
Or if you want to use it:
|
||||
|
||||
```
|
||||
function todosReducer(state=[],action) {
|
||||
import updux from './myUpdux';
|
||||
|
||||
switch(action.type) {
|
||||
case 'ADD': return [ ...state, action.payload ];
|
||||
|
||||
case 'DONE': return state.map( todo => todo.id === action.payload
|
||||
? { ...todo, done: true } : todo )
|
||||
|
||||
default: return state;
|
||||
}
|
||||
}
|
||||
const {
|
||||
reducer,
|
||||
actions: { doTheThing },
|
||||
createStore,
|
||||
middleware,
|
||||
} = updux;
|
||||
```
|
||||
|
||||
With Updux:
|
||||
## Mapping a mutation to all values of a state
|
||||
|
||||
Say you have a `todos` state that is an array of `todo` sub-states. It's easy
|
||||
enough to have the main reducer maps away all items to the sub-reducer:
|
||||
|
||||
```
|
||||
const todosUpdux = updux({
|
||||
const todo = new Updux({
|
||||
mutations: {
|
||||
add: todo => state => [ ...state, todo ],
|
||||
done: done_id => u.map( u.if( ({id} => id === done_id), {done: true} ) )
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
The signature of the mutations is `(payload,action) => state => newState`.
|
||||
It is designed to play well with `Updeep`. This way, instead of doing
|
||||
|
||||
```
|
||||
mutation: {
|
||||
renameTodo: newName => state => { ...state, name: newName }
|
||||
}
|
||||
```
|
||||
|
||||
we can do
|
||||
|
||||
```
|
||||
mutation: {
|
||||
renameTodo: newName => u({ name: newName })
|
||||
}
|
||||
```
|
||||
|
||||
Also, the special key `*` can be used to match any
|
||||
action not explicitly matched by other mutations.
|
||||
|
||||
```
|
||||
const todosUpdux = updux({
|
||||
mutations: {
|
||||
add: todo => state => [ ...state, todo ],
|
||||
done: done_id => u.map( u.if( ({id} => id === done_id), {done: true} ) ),
|
||||
'*' (payload,action) => state => {
|
||||
console.warn( "unexpected action ", action.type );
|
||||
return state;
|
||||
},
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
#### effects
|
||||
|
||||
Plain object defining asynchronous actions and side-effects triggered by actions.
|
||||
The effects themselves are Redux middleware, expect with the `dispatch`
|
||||
property of the first argument augmented with all the available actions.
|
||||
|
||||
```
|
||||
updux({
|
||||
effects: {
|
||||
fetch: ({dispatch}) => next => async (action) => {
|
||||
next(action);
|
||||
|
||||
let result = await fetch(action.payload.url).then( result => result.json() );
|
||||
dispatch.fetchSuccess(result);
|
||||
}
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
### actions
|
||||
|
||||
Generic action creations are automatically created from the mutations and effects, but you can
|
||||
also define custom action creator here. The object's values are function that
|
||||
transform the arguments of the creator to the action's payload.
|
||||
|
||||
```
|
||||
const { actions } = updox({
|
||||
mutations: {
|
||||
foo: () => state => state,
|
||||
}
|
||||
actions: {
|
||||
bar: (x,y) => ({x,y})
|
||||
}
|
||||
});
|
||||
|
||||
actions.foo({ x: 1, y: 2 }); // => { type: foo, payload: { x:1, y:2 } }
|
||||
actions.bar(1,2); // => { type: bar, payload: { x:1, y:2 } }
|
||||
```
|
||||
|
||||
## return value
|
||||
|
||||
`updux` returns an object with the following properties:
|
||||
|
||||
### initial
|
||||
|
||||
Default initial state of the reducer. If applicable, merge
|
||||
the initial states of `config` and `subduxes`, with
|
||||
`config` having precedence over `subduxes`.
|
||||
|
||||
If nothing was given, defaults to an empty object.
|
||||
|
||||
### reducer
|
||||
|
||||
A Redux reducer generated using the computed initial state and
|
||||
mutations.
|
||||
|
||||
### mutations
|
||||
|
||||
Merge of the config and subduxes mutations. If an action trigger
|
||||
mutations in both the main updux and its subduxes, the subduxes
|
||||
mutations will be performed first.
|
||||
|
||||
### actions
|
||||
|
||||
Action creators for all actions defined or used in the actions, mutations, effects and subduxes
|
||||
of the updox config.
|
||||
|
||||
Non-custom action creators defined in `actions` have the signature `(payload={},meta={}) => ({type,
|
||||
payload,meta})` (with the extra sugar that if `meta` or `payload` are not
|
||||
specified, the key is not present in the produced action).
|
||||
|
||||
If the same action appears in multiple locations, the precedence order
|
||||
determining which one will prevail is
|
||||
|
||||
actions generated from mutations/effects < non-custom subduxes actions <
|
||||
custom subduxes actions < custom actions
|
||||
|
||||
### middleware
|
||||
|
||||
A middleware aggregating all the effects defined in the
|
||||
updox and its subduxes. Effects of the updox itself are
|
||||
done before the subdoxes effects.
|
||||
|
||||
### createStore
|
||||
|
||||
Same as doing
|
||||
|
||||
```
|
||||
import { createStore, applyMiddleware } from 'redux';
|
||||
|
||||
const { initial, reducer, middleware, actions } = updox(...);
|
||||
|
||||
const store = createStore( initial, reducer, applyMiddleware(middleware) );
|
||||
|
||||
for ( let type in actions ) {
|
||||
store.dispatch[type] = (...args) => {
|
||||
store.dispatch(actions[type](...args))
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
So that later on you can do
|
||||
|
||||
```
|
||||
store.dispatch.addTodo(...);
|
||||
|
||||
// still work
|
||||
store.dispatch( actions.addTodo(...) );
|
||||
```
|
||||
|
||||
# Example
|
||||
|
||||
#### battle.js
|
||||
|
||||
```
|
||||
import { updux } from 'updux';
|
||||
|
||||
import game from './game';
|
||||
import log from './log';
|
||||
import bogeys from './bogeys';
|
||||
|
||||
const { createStore } = updux({
|
||||
subduxes: { game, log, bogeys }
|
||||
})
|
||||
|
||||
export default createStore;
|
||||
```
|
||||
|
||||
#### game.js
|
||||
|
||||
|
||||
```
|
||||
import { updux } from 'updux';
|
||||
import _ from 'lodash';
|
||||
import u from 'updeep';
|
||||
|
||||
import { calculateMovement } from 'game/rules';
|
||||
|
||||
export default updux({
|
||||
initial: { game: "", players: [], turn: 0, },
|
||||
mutations: {
|
||||
init_game: ({game: { name, players }}) => {name, players},
|
||||
play_turn: () => u({ turn: x => x+1 }),
|
||||
},
|
||||
effects: {
|
||||
play_turn: ({getState,dispatch}) => next => action => {
|
||||
|
||||
const bogeys = api.getState().bogeys;
|
||||
|
||||
// only allow the turn to be played if
|
||||
// all ships have their orders in
|
||||
if( bogeys.any( bogey => ! bogey.orders ) ) return;
|
||||
|
||||
bogeys.forEach( bogey => {
|
||||
dispatch.move( calculateMovement(bogey) )
|
||||
} );
|
||||
|
||||
next(action);
|
||||
},
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
|
||||
#### log.js
|
||||
|
||||
```
|
||||
import { updux } from 'updux';
|
||||
|
||||
export default updux({
|
||||
initial: [],
|
||||
mutations: {
|
||||
'*': (payload,action) => state => [ ...state, action ],
|
||||
review: () => u({ reviewed: true}),
|
||||
done: () => u({done: true}),
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
#### bogeys.js
|
||||
const todos = new Updux({ initial: [] });
|
||||
|
||||
todos.addMutation(
|
||||
todo.actions.review,
|
||||
(_,action) => state => state.map( todo.upreducer(action) )
|
||||
);
|
||||
todos.addMutation(
|
||||
todo.actions.done,
|
||||
(id,action) => u.map(u.if(u.is('id',id), todo.upreducer(action))),
|
||||
);
|
||||
|
||||
```
|
||||
import { updux } from 'updux';
|
||||
import _ from 'lodash';
|
||||
|
||||
export default updux({
|
||||
initial: [],
|
||||
But `updeep` can iterate through all the items of an array (or the values of
|
||||
an object) via the special key `*`. So the todos updux above could also be
|
||||
written:
|
||||
|
||||
```
|
||||
const todo = new Updux({
|
||||
mutations: {
|
||||
init_game: ({bogeys}) => () => _.keyBy( bogeys, 'id' ),
|
||||
move: ({position}) => u({ position }),
|
||||
review: () => u({ reviewed: true}),
|
||||
done: () => u({done: true}),
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
|
||||
#### myGame.js
|
||||
|
||||
```
|
||||
import Battle from './battle';
|
||||
|
||||
const battle = Battle();
|
||||
|
||||
battle.dispatch.init_game({
|
||||
name: 'Gemini Prime',
|
||||
players: [ 'yenzie' ],
|
||||
bogeys: [ { id: 'Enkidu' } ]
|
||||
const todos = new Updux({
|
||||
subduxes: { '*': todo },
|
||||
});
|
||||
|
||||
battle.dispatch.play_game();
|
||||
|
||||
....
|
||||
todos.addMutation(
|
||||
todo.actions.done,
|
||||
(id,action) => u.map(u.if(u.is('id',id), todo.upreducer(action))),
|
||||
true
|
||||
);
|
||||
```
|
||||
|
||||
The advantages being that the actions/mutations/effects of the subdux will be
|
||||
imported by the root updux as usual, and all actions that aren't being
|
||||
overridden by a sink mutation will trickle down automatically.
|
||||
|
||||
## Usage with Immer
|
||||
|
||||
While Updux was created with Updeep in mind, it also plays very
|
||||
well with [Immer](https://immerjs.github.io/immer/docs/introduction).
|
||||
|
||||
For example, taking this basic updux:
|
||||
|
||||
```
|
||||
import Updux from 'updux';
|
||||
|
||||
const updux = new Updux({
|
||||
initial: { counter: 0 },
|
||||
mutations: {
|
||||
add: (inc=1) => state => { counter: counter + inc }
|
||||
}
|
||||
});
|
||||
|
||||
```
|
||||
|
||||
Converting it to Immer would look like:
|
||||
|
||||
```
|
||||
import Updux from 'updux';
|
||||
import { produce } from 'Immer';
|
||||
|
||||
const updux = new Updux({
|
||||
initial: { counter: 0 },
|
||||
mutations: {
|
||||
add: (inc=1) => produce( draft => draft.counter += inc ) }
|
||||
}
|
||||
});
|
||||
|
||||
```
|
||||
|
||||
But since typing `produce` over and over is no fun, `groomMutations`
|
||||
can be used to wrap all mutations with it:
|
||||
|
||||
```
|
||||
import Updux from 'updux';
|
||||
import { produce } from 'Immer';
|
||||
|
||||
const updux = new Updux({
|
||||
initial: { counter: 0 },
|
||||
groomMutations: mutation => (...args) => produce( mutation(...args) ),
|
||||
mutations: {
|
||||
add: (inc=1) => draft => draft.counter += inc
|
||||
}
|
||||
});
|
||||
|
||||
```
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
- documentation generator (mkdocs + jsdoc-to-markdown)
|
||||
- createStore
|
|
@ -0,0 +1,46 @@
|
|||
# https://taskfile.dev
|
||||
|
||||
version: '3'
|
||||
|
||||
tasks:
|
||||
lint:fix:delta:
|
||||
vars:
|
||||
FILES:
|
||||
sh: git diff-ls --diff-filter=d main | grep -v .vitebook
|
||||
deps:
|
||||
- task: 'lint:fix'
|
||||
vars:
|
||||
CLI_ARGS: '{{.FILES | catLines }}'
|
||||
|
||||
lint:
|
||||
cmds:
|
||||
- npx prettier --check {{.CLI_ARGS | default "." }}
|
||||
- npx eslint {{.CLI_ARGS | default "." }}
|
||||
|
||||
lint:fix:
|
||||
cmds:
|
||||
- npx prettier --write {{.CLI_ARGS | default "." }}
|
||||
- npx eslint --fix --quiet {{.CLI_ARGS | default "." }}
|
||||
|
||||
lint:delta:
|
||||
cmds:
|
||||
- task: 'lint:prettier:delta'
|
||||
- task: 'lint:eslint:delta'
|
||||
|
||||
lint:prettier:delta:
|
||||
vars:
|
||||
FILES:
|
||||
sh: git diff-ls --diff-filter=d {{.ROOT_BRANCH | default "main"}} | grep -v .vitebook
|
||||
cmds:
|
||||
- npx prettier --check {{.FILES | catLines }}
|
||||
|
||||
lint:eslint:delta:
|
||||
vars:
|
||||
FILES:
|
||||
sh: git diff-ls --diff-filter=d {{.ROOT_BRANCH | default "main"}} | grep -v .vitebook
|
||||
cmds:
|
||||
- npx eslint --format=unix {{.FILES | catLines }}
|
||||
|
||||
lint:eslint:
|
||||
cmds:
|
||||
- npx eslint {{.FILES | default "src/**" }}
|
|
@ -1,12 +0,0 @@
|
|||
module.exports = {
|
||||
presets: [
|
||||
[
|
||||
'@babel/preset-env',
|
||||
{
|
||||
targets: {
|
||||
node: 'current',
|
||||
},
|
||||
},
|
||||
],
|
||||
],
|
||||
};
|
|
@ -1,2 +0,0 @@
|
|||
export {};
|
||||
//# sourceMappingURL=actions.test.d.ts.map
|
|
@ -1 +0,0 @@
|
|||
{"version":3,"file":"actions.test.d.ts","sourceRoot":"","sources":["../src/actions.test.ts"],"names":[],"mappings":""}
|
|
@ -1,37 +0,0 @@
|
|||
"use strict";
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const _1 = __importDefault(require("."));
|
||||
const noopEffect = () => () => () => { };
|
||||
test('actions defined in effects and mutations, multi-level', () => {
|
||||
const { actions } = _1.default({
|
||||
effects: {
|
||||
foo: noopEffect,
|
||||
},
|
||||
mutations: { bar: () => () => null },
|
||||
subduxes: {
|
||||
mysub: {
|
||||
effects: { baz: noopEffect },
|
||||
mutations: { quux: () => () => null },
|
||||
actions: {
|
||||
foo: (limit) => ({ limit }),
|
||||
},
|
||||
},
|
||||
myothersub: {
|
||||
effects: {
|
||||
foo: noopEffect,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
const types = Object.keys(actions);
|
||||
types.sort();
|
||||
expect(types).toEqual(['bar', 'baz', 'foo', 'quux']);
|
||||
expect(actions.bar()).toEqual({ type: 'bar' });
|
||||
expect(actions.bar('xxx')).toEqual({ type: 'bar', payload: 'xxx' });
|
||||
expect(actions.bar(undefined, 'yyy')).toEqual({ type: 'bar', meta: 'yyy' });
|
||||
expect(actions.foo(12)).toEqual({ type: 'foo', payload: { limit: 12 } });
|
||||
});
|
||||
//# sourceMappingURL=actions.test.js.map
|
|
@ -1 +0,0 @@
|
|||
{"version":3,"file":"actions.test.js","sourceRoot":"","sources":["../src/actions.test.ts"],"names":[],"mappings":";;;;;AAAA,yCAAsB;AAGtB,MAAM,UAAU,GAAG,GAAG,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC;AAExC,IAAI,CAAC,uDAAuD,EAAE,GAAG,EAAE;IACjE,MAAM,EAAC,OAAO,EAAC,GAAG,UAAK,CAAC;QACtB,OAAO,EAAE;YACP,GAAG,EAAE,UAAU;SAChB;QACD,SAAS,EAAE,EAAC,GAAG,EAAE,GAAG,EAAE,CAAC,GAAG,EAAE,CAAC,IAAI,EAAC;QAClC,QAAQ,EAAE;YACR,KAAK,EAAE;gBACL,OAAO,EAAE,EAAC,GAAG,EAAE,UAAU,EAAE;gBAC3B,SAAS,EAAE,EAAC,IAAI,EAAE,GAAG,EAAE,CAAC,GAAG,EAAE,CAAC,IAAI,EAAC;gBACnC,OAAO,EAAE;oBACP,GAAG,EAAE,CAAC,KAAY,EAAE,EAAE,CAAC,CAAC,EAAC,KAAK,EAAC,CAAC;iBACjC;aACF;YACD,UAAU,EAAE;gBACV,OAAO,EAAE;oBACP,GAAG,EAAE,UAAU;iBAChB;aACF;SACF;KACF,CAAC,CAAC;IAEH,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACnC,KAAK,CAAC,IAAI,EAAE,CAAC;IAEb,MAAM,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC;IAErD,MAAM,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC,OAAO,CAAC,EAAC,IAAI,EAAE,KAAK,EAAC,CAAC,CAAC;IAC7C,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,EAAC,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAC,CAAC,CAAC;IAClE,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,EAAC,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAC,CAAC,CAAC;IAE1E,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,EAAC,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE,EAAC,KAAK,EAAE,EAAE,EAAC,EAAC,CAAC,CAAC;AACvE,CAAC,CAAC,CAAC"}
|
|
@ -1,9 +0,0 @@
|
|||
import { Action, ActionPayloadGenerator, Dictionary } from '../types';
|
||||
interface ActionCreator {
|
||||
(...args: any[]): Action;
|
||||
_genericAction?: boolean;
|
||||
}
|
||||
declare type ActionPair = [string, ActionCreator];
|
||||
declare function buildActions(generators?: Dictionary<ActionPayloadGenerator>, actionNames?: string[], subActions?: ActionPair[]): Dictionary<ActionCreator>;
|
||||
export default buildActions;
|
||||
//# sourceMappingURL=index.d.ts.map
|
|
@ -1 +0,0 @@
|
|||
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/buildActions/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,sBAAsB,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAEtE,UAAU,aAAa;IACnB,CAAE,GAAG,IAAI,EAAE,GAAG,EAAE,GAAI,MAAM,CAAC;IAC3B,cAAc,CAAC,EAAE,OAAO,CAAA;CAC3B;AAYD,aAAK,UAAU,GAAG,CAAE,MAAM,EAAE,aAAa,CAAE,CAAC;AAE5C,iBAAS,YAAY,CACnB,UAAU,GAAG,UAAU,CAAC,sBAAsB,CAAM,EACpD,WAAW,GAAE,MAAM,EAAO,EAC1B,UAAU,GAAG,UAAU,EAAO,GAC9B,UAAU,CAAC,aAAa,CAAC,CAmB1B;AAED,eAAe,YAAY,CAAC"}
|
|
@ -1,23 +0,0 @@
|
|||
"use strict";
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const fp_1 = __importDefault(require("lodash/fp"));
|
||||
function actionFor(type) {
|
||||
const creator = ((payload = undefined, meta = undefined) => fp_1.default.pickBy(v => v !== undefined)({ type, payload, meta }));
|
||||
creator._genericAction = true;
|
||||
return creator;
|
||||
}
|
||||
function buildActions(generators = {}, actionNames = [], subActions = []) {
|
||||
const [crafted, generic] = fp_1.default.partition(([type, f]) => !f._genericAction)(subActions);
|
||||
const actions = [
|
||||
...(actionNames.map(type => [type, actionFor(type)])),
|
||||
...generic,
|
||||
...crafted,
|
||||
...Object.entries(generators).map(([type, payload]) => [type, (...args) => ({ type, payload: payload(...args) })]),
|
||||
];
|
||||
return fp_1.default.fromPairs(actions);
|
||||
}
|
||||
exports.default = buildActions;
|
||||
//# sourceMappingURL=index.js.map
|
|
@ -1 +0,0 @@
|
|||
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/buildActions/index.ts"],"names":[],"mappings":";;;;;AAAA,mDAA2B;AAQ3B,SAAS,SAAS,CAAC,IAAW;IAC5B,MAAM,OAAO,GAAmB,CAAE,CAAC,OAAO,GAAG,SAAS,EAAE,IAAI,GAAG,SAAS,EAAE,EAAE,CAC1E,YAAE,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,KAAK,SAAS,CAAC,CAAC,EAAC,IAAI,EAAE,OAAO,EAAE,IAAI,EAAC,CAAW,CACjE,CAAC;IAEF,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC;IAE9B,OAAO,OAAO,CAAC;AACjB,CAAC;AAID,SAAS,YAAY,CACnB,aAAkD,EAAE,EACpD,cAAwB,EAAE,EAC1B,aAA4B,EAAE;IAK9B,MAAM,CAAE,OAAO,EAAE,OAAO,CAAE,GAAG,YAAE,CAAC,SAAS,CACrC,CAAC,CAAC,IAAI,EAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,cAAc,CAClC,CAAE,UAAU,CAAE,CAAC;IAEd,MAAM,OAAO,GAAG;QACZ,GAAG,CAAC,WAAW,CAAC,GAAG,CAAE,IAAI,CAAC,EAAE,CAAC,CAAE,IAAI,EAAE,SAAS,CAAC,IAAI,CAAC,CAAE,CAAE,CAAC;QACzD,GAAG,OAAO;QACV,GAAG,OAAO;QACV,GAAG,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,GAAG,CAC7B,CAAC,CAAC,IAAI,EAAE,OAAO,CAAuB,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,GAAG,IAAS,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC,EAAE,CAAC,CAAC,CAC7G;KACJ,CAAC;IAEF,OAAO,YAAE,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;AAEjC,CAAC;AAED,kBAAe,YAAY,CAAC"}
|
|
@ -1,24 +0,0 @@
|
|||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.default = buildCreateStore;
|
||||
|
||||
var _redux = require("redux");
|
||||
|
||||
function buildCreateStore(reducer, initial, middleware, actions) {
|
||||
return () => {
|
||||
const store = (0, _redux.createStore)(reducer, initial, (0, _redux.applyMiddleware)(middleware));
|
||||
|
||||
for (let a in actions) {
|
||||
store.dispatch[a] = (...args) => {
|
||||
store.dispatch(actions[a](...args));
|
||||
};
|
||||
}
|
||||
|
||||
return store;
|
||||
};
|
||||
}
|
||||
|
||||
;
|
|
@ -1 +0,0 @@
|
|||
{"version":3,"file":"buildCreateStore.js","sourceRoot":"","sources":["../src/buildCreateStore.js"],"names":[],"mappings":";;AAAA,iCAAyE;AAEzE,SAAwB,gBAAgB,CAAE,OAAO,EAAE,OAAO,EAAE,UAAU,EAC9B,OAAO;IAC3C,OAAO,GAAG,EAAE;QACV,MAAM,KAAK,GAAI,mBAAgB,CAAE,OAAO,EAAE,OAAO,EAC7C,uBAAe,CAAE,UAAU,CAAC,CAC7B,CAAC;QACJ,KAAM,IAAI,CAAC,IAAI,OAAO,EAAG;YACrB,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,IAAI,EAAE,EAAE;gBAC5B,KAAK,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,CAAA;YACvC,CAAC,CAAC;SACL;QAED,OAAO,KAAK,CAAC;IACjB,CAAC,CAAA;AACH,CAAC;AAdD,mCAcC;AAAA,CAAC"}
|
|
@ -1,7 +0,0 @@
|
|||
import { Middleware, Reducer } from 'redux';
|
||||
import { ActionCreator, Dictionary } from '../types';
|
||||
declare function buildCreateStore<S>(reducer: Reducer<S>, initial: S, middleware: Middleware, actions: Dictionary<ActionCreator>): () => import("redux").Store<S, import("redux").AnyAction> & {
|
||||
dispatch: {};
|
||||
};
|
||||
export default buildCreateStore;
|
||||
//# sourceMappingURL=index.d.ts.map
|
|
@ -1 +0,0 @@
|
|||
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/buildCreateStore/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,UAAU,EACV,OAAO,EACR,MAAM,OAAO,CAAC;AACf,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAErD,iBAAS,gBAAgB,CAAC,CAAC,EACzB,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,EACnB,OAAO,EAAE,CAAC,EACV,UAAU,EAAE,UAAU,EACtB,OAAO,EAAE,UAAU,CAAC,aAAa,CAAC;;EAgBnC;AAED,eAAe,gBAAgB,CAAC"}
|
|
@ -1,16 +0,0 @@
|
|||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const redux_1 = require("redux");
|
||||
function buildCreateStore(reducer, initial, middleware, actions) {
|
||||
return () => {
|
||||
const store = redux_1.createStore(reducer, initial, redux_1.applyMiddleware(middleware));
|
||||
for (let a in actions) {
|
||||
store.dispatch[a] = (...args) => {
|
||||
store.dispatch(actions[a](...args));
|
||||
};
|
||||
}
|
||||
return store;
|
||||
};
|
||||
}
|
||||
exports.default = buildCreateStore;
|
||||
//# sourceMappingURL=index.js.map
|
|
@ -1 +0,0 @@
|
|||
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/buildCreateStore/index.ts"],"names":[],"mappings":";;AAAA,iCAKe;AAGf,SAAS,gBAAgB,CACvB,OAAmB,EACnB,OAAU,EACV,UAAsB,EACtB,OAAkC;IAElC,OAAO,GAAG,EAAE;QACV,MAAM,KAAK,GAAG,mBAAgB,CAC5B,OAAO,EACP,OAAO,EACP,uBAAe,CAAC,UAAU,CAAC,CAC5B,CAAC;QACF,KAAK,IAAI,CAAC,IAAI,OAAO,EAAE;YACnB,KAAK,CAAC,QAAgB,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,IAAW,EAAE,EAAE;gBAC/C,KAAK,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC;YACtC,CAAC,CAAC;SACH;QAED,OAAO,KAAK,CAAC;IACf,CAAC,CAAC;AACJ,CAAC;AAED,kBAAe,gBAAgB,CAAC"}
|
|
@ -1,5 +0,0 @@
|
|||
import { Dictionary } from '../types';
|
||||
declare function buildInitial<S extends number | string | boolean>(initial: S, subduxes?: Dictionary<undefined>): S;
|
||||
declare function buildInitial<S extends object>(initial?: Partial<S>, subduxes?: Partial<S>): S extends object ? S : never;
|
||||
export default buildInitial;
|
||||
//# sourceMappingURL=index.d.ts.map
|
|
@ -1 +0,0 @@
|
|||
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/buildInitial/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAEtC,iBAAS,YAAY,CAAC,CAAC,SAAS,MAAM,GAAC,MAAM,GAAC,OAAO,EAAG,OAAO,EAAE,CAAC,EAAE,QAAQ,CAAC,EAAE,UAAU,CAAC,SAAS,CAAC,GAAI,CAAC,CAAC;AAC1G,iBAAS,YAAY,CAAC,CAAC,SAAS,MAAM,EAAG,OAAO,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,QAAQ,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC,GAAI,CAAC,SAAS,MAAM,GAAG,CAAC,GAAG,KAAK,CAAC;AAQrH,eAAe,YAAY,CAAC"}
|
|
@ -1,11 +0,0 @@
|
|||
"use strict";
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const fp_1 = __importDefault(require("lodash/fp"));
|
||||
function buildInitial(initial = {}, subduxes = {}) {
|
||||
return fp_1.default.isPlainObject(initial) ? fp_1.default.mergeAll([subduxes, initial]) : initial;
|
||||
}
|
||||
exports.default = buildInitial;
|
||||
//# sourceMappingURL=index.js.map
|
|
@ -1 +0,0 @@
|
|||
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/buildInitial/index.ts"],"names":[],"mappings":";;;;;AAAA,mDAA2B;AAK3B,SAAS,YAAY,CACnB,UAAgB,EAAE,EAClB,WAAiB,EAAE;IAEnB,OAAO,YAAE,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,YAAE,CAAC,QAAQ,CAAC,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC;AAChF,CAAC;AAED,kBAAe,YAAY,CAAC"}
|
|
@ -1 +0,0 @@
|
|||
"use strict";
|
|
@ -1 +0,0 @@
|
|||
{"version":3,"file":"index.test-d.js","sourceRoot":"","sources":["../../src/buildInitial/index.test-d.js"],"names":[],"mappings":""}
|
|
@ -1 +0,0 @@
|
|||
"use strict";
|
|
@ -1 +0,0 @@
|
|||
{"version":3,"file":"test-d.js","sourceRoot":"","sources":["../../src/buildInitial/test-d.js"],"names":[],"mappings":""}
|
|
@ -1,27 +0,0 @@
|
|||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.default = buildMiddleware;
|
||||
|
||||
var _fp = _interopRequireDefault(require("lodash/fp"));
|
||||
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||||
|
||||
const MiddlewareFor = (type, mw) => api => next => action => {
|
||||
if (type !== '*' && action.type !== type) return next(action);
|
||||
return mw(api)(next)(action);
|
||||
};
|
||||
|
||||
function buildMiddleware(effects = {}, actions = {}, subduxes = {}) {
|
||||
return api => {
|
||||
for (let type in actions) {
|
||||
api.dispatch[type] = (...args) => api.dispatch(actions[type](...args));
|
||||
}
|
||||
|
||||
return original_next => {
|
||||
return [..._fp.default.toPairs(effects).map(([type, effect]) => MiddlewareFor(type, effect)), ..._fp.default.map('middleware', subduxes)].filter(x => x).reduceRight((next, mw) => mw(api)(next), original_next);
|
||||
};
|
||||
};
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
{"version":3,"file":"buildMiddleware.js","sourceRoot":"","sources":["../src/buildMiddleware.js"],"names":[],"mappings":";;;;;AAAA,mDAA2B;AAE3B,MAAM,aAAa,GAAG,CAAC,IAAI,EAAC,EAAE,EAAE,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,MAAM,CAAC,EAAE;IACvD,IAAI,IAAI,KAAK,GAAG,IAAI,MAAM,CAAC,IAAI,KAAK,IAAI;QAAE,OAAO,IAAI,CAAC,MAAM,CAAC,CAAC;IAE9D,OAAO,EAAE,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,CAAC;AACjC,CAAC,CAAC;AAEF,SAAwB,eAAe,CACnC,OAAO,GAAG,EAAE,EACZ,OAAO,GAAG,EAAE,EACZ,QAAQ,GAAG,EAAE;IAEf,OAAO,GAAG,CAAC,EAAE;QACX,KAAK,IAAI,IAAI,IAAI,OAAO,EAAE;YACxB,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,IAAI,EAAE,EAAE,CAAC,GAAG,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC;SACxE;QAED,OAAO,aAAa,CAAC,EAAE;YACrB,OAAO;gBACL,GAAG,YAAE,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,MAAM,CAAC,EAAE,EAAE,CAC1C,aAAa,CAAC,IAAI,EAAC,MAAM,CAAC,CAC7B;gBACD,GAAG,YAAE,CAAC,GAAG,CAAC,YAAY,EAAE,QAAQ,CAAC;aAClC;iBACE,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;iBACd,WAAW,CAAC,CAAC,IAAI,EAAE,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,aAAa,CAAC,CAAC;QAC7D,CAAC,CAAC;IACJ,CAAC,CAAC;AACJ,CAAC;AArBD,kCAqBC"}
|
|
@ -1,5 +0,0 @@
|
|||
import { Middleware } from 'redux';
|
||||
import { Dictionary, ActionCreator, UpduxDispatch } from '../types';
|
||||
declare function buildMiddleware<S = any>(effects?: Dictionary<Middleware<{}, S, UpduxDispatch>>, actions?: Dictionary<ActionCreator>, subMiddlewares?: Middleware<{}, S, UpduxDispatch>[]): Middleware<{}, S, UpduxDispatch>;
|
||||
export default buildMiddleware;
|
||||
//# sourceMappingURL=index.d.ts.map
|
|
@ -1 +0,0 @@
|
|||
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/buildMiddleware/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,UAAU,EAA2B,MAAM,OAAO,CAAC;AAC5D,OAAO,EAAE,UAAU,EAAE,aAAa,EAAU,aAAa,EAAE,MAAM,UAAU,CAAC;AAU5E,iBAAS,eAAe,CAAC,CAAC,GAAC,GAAG,EAC1B,OAAO,GAAG,UAAU,CAAC,UAAU,CAAC,EAAE,EAAC,CAAC,EAAC,aAAa,CAAC,CAAK,EACxD,OAAO,GAAG,UAAU,CAAC,aAAa,CAAK,EACvC,cAAc,GAAE,UAAU,CAAC,EAAE,EAAC,CAAC,EAAC,aAAa,CAAC,EAAO,GACtD,UAAU,CAAC,EAAE,EAAC,CAAC,EAAC,aAAa,CAAC,CAkBhC;AAED,eAAe,eAAe,CAAC"}
|
|
@ -1,28 +0,0 @@
|
|||
"use strict";
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const fp_1 = __importDefault(require("lodash/fp"));
|
||||
const MiddlewareFor = (type, mw) => api => next => action => {
|
||||
if (type !== '*' && action.type !== type)
|
||||
return next(action);
|
||||
return mw(api)(next)(action);
|
||||
};
|
||||
function buildMiddleware(effects = {}, actions = {}, subMiddlewares = []) {
|
||||
return (api) => {
|
||||
for (let type in actions) {
|
||||
api.dispatch[type] = (...args) => api.dispatch(actions[type](...args));
|
||||
}
|
||||
return (original_next) => {
|
||||
return [
|
||||
...fp_1.default.toPairs(effects).map(([type, effect]) => MiddlewareFor(type, effect)),
|
||||
...subMiddlewares
|
||||
]
|
||||
.filter(x => x)
|
||||
.reduceRight((next, mw) => mw(api)(next), original_next);
|
||||
};
|
||||
};
|
||||
}
|
||||
exports.default = buildMiddleware;
|
||||
//# sourceMappingURL=index.js.map
|
|
@ -1 +0,0 @@
|
|||
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/buildMiddleware/index.ts"],"names":[],"mappings":";;;;;AAAA,mDAA2B;AAK3B,MAAM,aAAa,GAAG,CAAC,IAAS,EAAE,EAAc,EAAe,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,MAAM,CAAC,EAAE;IACtF,IAAI,IAAI,KAAK,GAAG,IAAI,MAAM,CAAC,IAAI,KAAK,IAAI;QAAE,OAAO,IAAI,CAAC,MAAM,CAAC,CAAC;IAE9D,OAAO,EAAE,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,CAAC;AACjC,CAAC,CAAC;AAIF,SAAS,eAAe,CACpB,UAAsD,EAAE,EACxD,UAAqC,EAAE,EACvC,iBAAmD,EAAE;IAGvD,OAAO,CAAC,GAAmC,EAAE,EAAE;QAC7C,KAAK,IAAI,IAAI,IAAI,OAAO,EAAE;YACxB,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,IAAU,EAAE,EAAE,CAAC,GAAG,CAAC,QAAQ,CAAG,OAAe,CAAC,IAAI,CAAS,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC;SAChG;QAED,OAAO,CAAC,aAAmB,EAAC,EAAE;YAC5B,OAAO;gBACL,GAAG,YAAE,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,MAAM,CAAC,EAAE,EAAE,CAC1C,aAAa,CAAC,IAAI,EAAC,MAAoB,CAAC,CAC3C;gBACD,GAAG,cAAc;aAClB;iBACE,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;iBACd,WAAW,CAAC,CAAC,IAAI,EAAE,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,aAAa,CAAC,CAAC;QAC7D,CAAC,CAAC;IACJ,CAAC,CAAC;AACJ,CAAC;AAED,kBAAe,eAAe,CAAC"}
|
|
@ -1,5 +0,0 @@
|
|||
/// <reference types="lodash" />
|
||||
import { Mutation, Dictionary } from '../types';
|
||||
declare function buildMutations(mutations?: Dictionary<Mutation>, subduxes?: {}): import("lodash").Dictionary<Mutation<any>>;
|
||||
export default buildMutations;
|
||||
//# sourceMappingURL=index.d.ts.map
|
|
@ -1 +0,0 @@
|
|||
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/buildMutations/index.ts"],"names":[],"mappings":";AAEA,OAAO,EAAC,QAAQ,EAAU,UAAU,EAAC,MAAM,UAAU,CAAC;AAWtD,iBAAS,cAAc,CACnB,SAAS,GAAE,UAAU,CAAC,QAAQ,CAAM,EACpC,QAAQ,KAAK,8CAgDhB;AAED,eAAe,cAAc,CAAC"}
|
|
@ -1,33 +0,0 @@
|
|||
"use strict";
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const fp_1 = __importDefault(require("lodash/fp"));
|
||||
const updeep_1 = __importDefault(require("updeep"));
|
||||
const composeMutations = (mutations) => mutations.reduce((m1, m2) => (payload = null, action) => state => m2(payload, action)(m1(payload, action)(state)));
|
||||
function buildMutations(mutations = {}, subduxes = {}) {
|
||||
const actions = fp_1.default.uniq(Object.keys(mutations).concat(...Object.values(subduxes).map(({ mutations = {} }) => Object.keys(mutations))));
|
||||
let mergedMutations = {};
|
||||
let [globby, nonGlobby] = fp_1.default.partition(([_, { mutations = {} }]) => mutations['*'], Object.entries(subduxes));
|
||||
globby = fp_1.default.flow([
|
||||
fp_1.default.fromPairs,
|
||||
fp_1.default.mapValues(({ reducer }) => (_, action) => (state) => reducer(state, action)),
|
||||
])(globby);
|
||||
const globbyMutation = (payload, action) => updeep_1.default(fp_1.default.mapValues((mut) => mut(payload, action))(globby));
|
||||
actions.forEach(action => {
|
||||
mergedMutations[action] = [globbyMutation];
|
||||
});
|
||||
nonGlobby.forEach(([slice, { mutations = {}, reducer = {} }]) => {
|
||||
Object.entries(mutations).forEach(([type, mutation]) => {
|
||||
const localized = (payload = null, action) => updeep_1.default.updateIn(slice)(mutation(payload, action));
|
||||
mergedMutations[type].push(localized);
|
||||
});
|
||||
});
|
||||
Object.entries(mutations).forEach(([type, mutation]) => {
|
||||
mergedMutations[type].push(mutation);
|
||||
});
|
||||
return fp_1.default.mapValues(composeMutations)(mergedMutations);
|
||||
}
|
||||
exports.default = buildMutations;
|
||||
//# sourceMappingURL=index.js.map
|
|
@ -1 +0,0 @@
|
|||
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/buildMutations/index.ts"],"names":[],"mappings":";;;;;AAAA,mDAA2B;AAC3B,oDAAuB;AAGvB,MAAM,gBAAgB,GAAG,CAAC,SAAqB,EAAE,EAAE,CACjD,SAAS,CAAC,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC,UAAe,IAAI,EAAE,MAAc,EAAE,EAAE,CAAC,KAAK,CAAC,EAAE,CAC5E,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC,KAAK,CAAC,CAAC,CAChD,CAAC;AAMJ,SAAS,cAAc,CACnB,YAAkC,EAAE,EACpC,QAAQ,GAAG,EAAE;IAKf,MAAM,OAAO,GAAG,YAAE,CAAC,IAAI,CACrB,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,MAAM,CAC3B,GAAG,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,GAAG,CAAC,CAAC,EAAC,SAAS,GAAG,EAAE,EAAK,EAAE,EAAE,CACtD,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CACvB,CACF,CACF,CAAC;IAEF,IAAI,eAAe,GAA2B,EAAE,CAAC;IAEjD,IAAI,CAAC,MAAM,EAAE,SAAS,CAAC,GAAG,YAAE,CAAC,SAAS,CACpC,CAAC,CAAC,CAAC,EAAE,EAAC,SAAS,GAAG,EAAE,EAAC,CAAK,EAAE,EAAE,CAAC,SAAS,CAAC,GAAG,CAAC,EAC7C,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,CACzB,CAAC;IAEF,MAAM,GAAG,YAAE,CAAC,IAAI,CAAC;QACf,YAAE,CAAC,SAAS;QACZ,YAAE,CAAC,SAAS,CAAC,CAAC,EAAC,OAAO,EAAC,EAAE,EAAE,CAAC,CAAC,CAAK,EAAE,MAAc,EAAE,EAAE,CAAC,CAAE,KAAU,EAAG,EAAE,CACtE,OAAO,CAAC,KAAK,EAAE,MAAM,CAAC,CACvB;KACF,CAAC,CAAC,MAAM,CAAC,CAAC;IAEX,MAAM,cAAc,GAAG,CAAC,OAAW,EAAE,MAAa,EAAE,EAAE,CACpD,gBAAC,CAAC,YAAE,CAAC,SAAS,CAAC,CAAC,GAAO,EAAE,EAAE,CAAC,GAAG,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC;IAE7D,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE;QACvB,eAAe,CAAC,MAAM,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;IAC7C,CAAC,CAAC,CAAC;IAEH,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,EAAE,EAAC,SAAS,GAAG,EAAE,EAAE,OAAO,GAAG,EAAE,EAAC,CAAO,EAAE,EAAE;QAClE,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,EAAE,QAAQ,CAAC,EAAE,EAAE;YACrD,MAAM,SAAS,GAAG,CAAC,OAAO,GAAG,IAAI,EAAE,MAAc,EAAE,EAAE,CACnD,gBAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAE,QAAqB,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC;YAE7D,eAAe,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QACxC,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,EAAE,QAAQ,CAAC,EAAE,EAAE;QACrD,eAAe,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IACvC,CAAC,CAAC,CAAC;IAEH,OAAO,YAAE,CAAC,SAAS,CAAC,gBAAgB,CAAC,CAAC,eAAe,CAAC,CAAC;AACzD,CAAC;AAED,kBAAe,cAAc,CAAC"}
|
|
@ -1,4 +0,0 @@
|
|||
import { Dictionary, Mutation, Upreducer } from '../types';
|
||||
declare function buildUpreducer<S>(initial: S, mutations: Dictionary<Mutation<S>>): Upreducer<S>;
|
||||
export default buildUpreducer;
|
||||
//# sourceMappingURL=index.d.ts.map
|
|
@ -1 +0,0 @@
|
|||
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/buildUpreducer/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAU,SAAS,EAAE,MAAM,UAAU,CAAC;AAEnE,iBAAS,cAAc,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,SAAS,EAAE,UAAU,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,GAAI,SAAS,CAAC,CAAC,CAAC,CAYxF;AAED,eAAe,cAAc,CAAC"}
|
|
@ -1,15 +0,0 @@
|
|||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
function buildUpreducer(initial, mutations) {
|
||||
return (action) => (state) => {
|
||||
if (state === null)
|
||||
state = initial;
|
||||
const a = mutations[action.type] ||
|
||||
mutations['*'];
|
||||
if (!a)
|
||||
return state;
|
||||
return a(action.payload, action)(state);
|
||||
};
|
||||
}
|
||||
exports.default = buildUpreducer;
|
||||
//# sourceMappingURL=index.js.map
|
|
@ -1 +0,0 @@
|
|||
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/buildUpreducer/index.ts"],"names":[],"mappings":";;AAIA,SAAS,cAAc,CAAI,OAAU,EAAE,SAAkC;IACvE,OAAO,CAAC,MAAc,EAAE,EAAE,CAAC,CAAC,KAAQ,EAAE,EAAE;QACtC,IAAI,KAAK,KAAK,IAAI;YAAE,KAAK,GAAG,OAAO,CAAC;QAEpC,MAAM,CAAC,GACL,SAAS,CAAC,MAAM,CAAC,IAAI,CAAC;YACtB,SAAS,CAAC,GAAG,CAAC,CAAC;QAEjB,IAAG,CAAC,CAAC;YAAE,OAAO,KAAK,CAAC;QAEpB,OAAO,CAAC,CAAC,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC,KAAK,CAAC,CAAC;IAC1C,CAAC,CAAC;AACJ,CAAC;AAED,kBAAe,cAAc,CAAC"}
|
|
@ -1,4 +0,0 @@
|
|||
import Updux from './updux';
|
||||
import { UpduxConfig } from './types';
|
||||
export default function updux(config: UpduxConfig): Updux<any>;
|
||||
//# sourceMappingURL=index.d.ts.map
|
|
@ -1 +0,0 @@
|
|||
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,MAAM,SAAS,CAAC;AAE5B,OAAO,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAEtC,MAAM,CAAC,OAAO,UAAU,KAAK,CAAC,MAAM,EAAE,WAAW,cAEhD"}
|
|
@ -1,11 +0,0 @@
|
|||
"use strict";
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const updux_1 = __importDefault(require("./updux"));
|
||||
function updux(config) {
|
||||
return new updux_1.default(config);
|
||||
}
|
||||
exports.default = updux;
|
||||
//# sourceMappingURL=index.js.map
|
|
@ -1 +0,0 @@
|
|||
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;AAGA,oDAA4B;AAI5B,SAAwB,KAAK,CAAC,MAAmB;IAC/C,OAAO,IAAI,eAAK,CAAC,MAAM,CAAC,CAAC;AAC3B,CAAC;AAFD,wBAEC"}
|
|
@ -1,2 +0,0 @@
|
|||
export {};
|
||||
//# sourceMappingURL=middleware.test.d.ts.map
|
|
@ -1 +0,0 @@
|
|||
{"version":3,"file":"middleware.test.d.ts","sourceRoot":"","sources":["../src/middleware.test.ts"],"names":[],"mappings":""}
|
|
@ -1,80 +0,0 @@
|
|||
"use strict";
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const _1 = __importDefault(require("."));
|
||||
test('simple effect', () => {
|
||||
const tracer = jest.fn();
|
||||
const store = _1.default({
|
||||
effects: {
|
||||
foo: (api) => (next) => (action) => {
|
||||
tracer();
|
||||
next(action);
|
||||
},
|
||||
},
|
||||
}).createStore();
|
||||
expect(tracer).not.toHaveBeenCalled();
|
||||
store.dispatch({ type: 'bar' });
|
||||
expect(tracer).not.toHaveBeenCalled();
|
||||
store.dispatch.foo();
|
||||
expect(tracer).toHaveBeenCalled();
|
||||
});
|
||||
test('effect and sub-effect', () => {
|
||||
const tracer = jest.fn();
|
||||
const tracerEffect = (signature) => (api) => (next) => (action) => {
|
||||
tracer(signature);
|
||||
next(action);
|
||||
};
|
||||
const store = _1.default({
|
||||
effects: {
|
||||
foo: tracerEffect('root'),
|
||||
},
|
||||
subduxes: {
|
||||
zzz: _1.default({ effects: {
|
||||
foo: tracerEffect('child'),
|
||||
} })
|
||||
},
|
||||
}).createStore();
|
||||
expect(tracer).not.toHaveBeenCalled();
|
||||
store.dispatch({ type: 'bar' });
|
||||
expect(tracer).not.toHaveBeenCalled();
|
||||
store.dispatch.foo();
|
||||
expect(tracer).toHaveBeenNthCalledWith(1, 'root');
|
||||
expect(tracer).toHaveBeenNthCalledWith(2, 'child');
|
||||
});
|
||||
test('"*" effect', () => {
|
||||
const tracer = jest.fn();
|
||||
const store = _1.default({
|
||||
effects: {
|
||||
'*': api => next => action => {
|
||||
tracer();
|
||||
next(action);
|
||||
},
|
||||
},
|
||||
}).createStore();
|
||||
expect(tracer).not.toHaveBeenCalled();
|
||||
store.dispatch({ type: 'bar' });
|
||||
expect(tracer).toHaveBeenCalled();
|
||||
});
|
||||
test('async effect', async () => {
|
||||
function timeout(ms) {
|
||||
return new Promise(resolve => setTimeout(resolve, ms));
|
||||
}
|
||||
const tracer = jest.fn();
|
||||
const store = _1.default({
|
||||
effects: {
|
||||
foo: api => next => async (action) => {
|
||||
next(action);
|
||||
await timeout(1000);
|
||||
tracer();
|
||||
},
|
||||
},
|
||||
}).createStore();
|
||||
expect(tracer).not.toHaveBeenCalled();
|
||||
store.dispatch.foo();
|
||||
expect(tracer).not.toHaveBeenCalled();
|
||||
await timeout(1000);
|
||||
expect(tracer).toHaveBeenCalled();
|
||||
});
|
||||
//# sourceMappingURL=middleware.test.js.map
|
|
@ -1 +0,0 @@
|
|||
{"version":3,"file":"middleware.test.js","sourceRoot":"","sources":["../src/middleware.test.ts"],"names":[],"mappings":";;;;;AAAA,yCAAsB;AAGtB,IAAI,CAAE,eAAe,EAAE,GAAG,EAAE;IAExB,MAAM,MAAM,GAAG,IAAI,CAAC,EAAE,EAAE,CAAC;IAEzB,MAAM,KAAK,GAAG,UAAK,CAAC;QAChB,OAAO,EAAE;YACL,GAAG,EAAE,CAAC,GAAO,EAAE,EAAE,CAAC,CAAC,IAAQ,EAAE,EAAE,CAAC,CAAC,MAAU,EAAE,EAAE;gBAC3C,MAAM,EAAE,CAAC;gBACT,IAAI,CAAC,MAAM,CAAC,CAAC;YACjB,CAAC;SACJ;KACJ,CAAC,CAAC,WAAW,EAAE,CAAC;IAEjB,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,gBAAgB,EAAE,CAAC;IAEtC,KAAK,CAAC,QAAQ,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IAEhC,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,gBAAgB,EAAE,CAAC;IAEtC,KAAK,CAAC,QAAQ,CAAC,GAAG,EAAE,CAAC;IAErB,MAAM,CAAC,MAAM,CAAC,CAAC,gBAAgB,EAAE,CAAC;AAEtC,CAAC,CAAC,CAAC;AAEH,IAAI,CAAE,uBAAuB,EAAE,GAAG,EAAE;IAEhC,MAAM,MAAM,GAAG,IAAI,CAAC,EAAE,EAAE,CAAC;IAEzB,MAAM,YAAY,GAAG,CAAE,SAAiB,EAAG,EAAE,CAAC,CAAE,GAAO,EAAG,EAAE,CAAC,CAAC,IAAQ,EAAE,EAAE,CAAC,CAAE,MAAW,EAAG,EAAE;QACzF,MAAM,CAAC,SAAS,CAAC,CAAC;QAClB,IAAI,CAAC,MAAM,CAAC,CAAC;IACjB,CAAC,CAAC;IAEF,MAAM,KAAK,GAAG,UAAK,CAAC;QAChB,OAAO,EAAE;YACL,GAAG,EAAE,YAAY,CAAC,MAAM,CAAC;SAC5B;QACD,QAAQ,EAAE;YACN,GAAG,EAAE,UAAK,CAAC,EAAC,OAAO,EAAE;oBACjB,GAAG,EAAE,YAAY,CAAC,OAAO,CAAC;iBAC7B,EAAC,CAAC;SACN;KACJ,CAAC,CAAC,WAAW,EAAE,CAAC;IAEjB,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,gBAAgB,EAAE,CAAC;IAEtC,KAAK,CAAC,QAAQ,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IAEhC,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,gBAAgB,EAAE,CAAC;IAEtC,KAAK,CAAC,QAAQ,CAAC,GAAG,EAAE,CAAC;IAErB,MAAM,CAAC,MAAM,CAAC,CAAC,uBAAuB,CAAC,CAAC,EAAC,MAAM,CAAC,CAAC;IACjD,MAAM,CAAC,MAAM,CAAC,CAAC,uBAAuB,CAAC,CAAC,EAAC,OAAO,CAAC,CAAC;AAItD,CAAC,CAAC,CAAC;AAEH,IAAI,CAAE,YAAY,EAAE,GAAG,EAAE;IAErB,MAAM,MAAM,GAAG,IAAI,CAAC,EAAE,EAAE,CAAC;IAEzB,MAAM,KAAK,GAAG,UAAK,CAAC;QAChB,OAAO,EAAE;YACL,GAAG,EAAE,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,MAAM,CAAC,EAAE;gBACzB,MAAM,EAAE,CAAC;gBACT,IAAI,CAAC,MAAM,CAAC,CAAC;YACjB,CAAC;SACJ;KACJ,CAAC,CAAC,WAAW,EAAE,CAAC;IAEjB,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,gBAAgB,EAAE,CAAC;IAEtC,KAAK,CAAC,QAAQ,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IAEhC,MAAM,CAAC,MAAM,CAAC,CAAC,gBAAgB,EAAE,CAAC;AACtC,CAAC,CAAC,CAAC;AAEH,IAAI,CAAE,cAAc,EAAE,KAAK,IAAI,EAAE;IAE7B,SAAS,OAAO,CAAC,EAAS;QACtB,OAAO,IAAI,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,CAAC;IAC3D,CAAC;IAED,MAAM,MAAM,GAAG,IAAI,CAAC,EAAE,EAAE,CAAC;IAEzB,MAAM,KAAK,GAAG,UAAK,CAAC;QAChB,OAAO,EAAE;YACL,GAAG,EAAE,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,KAAK,EAAC,MAAM,EAAC,EAAE;gBAC/B,IAAI,CAAC,MAAM,CAAC,CAAC;gBACb,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;gBACpB,MAAM,EAAE,CAAC;YACb,CAAC;SACJ;KACJ,CAAC,CAAC,WAAW,EAAE,CAAC;IAEjB,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,gBAAgB,EAAE,CAAC;IAEtC,KAAK,CAAC,QAAQ,CAAC,GAAG,EAAE,CAAC;IAErB,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,gBAAgB,EAAE,CAAC;IAEtC,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAEpB,MAAM,CAAC,MAAM,CAAC,CAAC,gBAAgB,EAAE,CAAC;AACtC,CAAC,CAAC,CAAC"}
|
|
@ -1,2 +0,0 @@
|
|||
export {};
|
||||
//# sourceMappingURL=splat.test.d.ts.map
|
|
@ -1 +0,0 @@
|
|||
{"version":3,"file":"splat.test.d.ts","sourceRoot":"","sources":["../src/splat.test.ts"],"names":[],"mappings":""}
|
|
@ -1,55 +0,0 @@
|
|||
"use strict";
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const _1 = __importDefault(require("."));
|
||||
const updeep_1 = __importDefault(require("updeep"));
|
||||
const tracer = (chr) => updeep_1.default({ tracer: (s = '') => s + chr });
|
||||
test('mutations, simple', () => {
|
||||
const dux = _1.default({
|
||||
mutations: {
|
||||
foo: () => tracer('a'),
|
||||
'*': () => tracer('b'),
|
||||
},
|
||||
});
|
||||
const store = dux.createStore();
|
||||
expect(store.getState()).toEqual({ tracer: 'b' });
|
||||
store.dispatch.foo();
|
||||
expect(store.getState()).toEqual({ tracer: 'ba', });
|
||||
store.dispatch({ type: 'bar' });
|
||||
expect(store.getState()).toEqual({ tracer: 'bab', });
|
||||
});
|
||||
test('with subduxes', () => {
|
||||
const dux = _1.default({
|
||||
mutations: {
|
||||
foo: () => tracer('a'),
|
||||
'*': () => tracer('b'),
|
||||
bar: () => ({ bar }) => ({ bar, tracer: bar.tracer })
|
||||
},
|
||||
subduxes: {
|
||||
bar: _1.default({
|
||||
mutations: {
|
||||
foo: () => tracer('d'),
|
||||
'*': () => tracer('e'),
|
||||
},
|
||||
}),
|
||||
},
|
||||
});
|
||||
const store = dux.createStore();
|
||||
expect(store.getState()).toEqual({
|
||||
tracer: 'b',
|
||||
bar: { tracer: 'e' }
|
||||
});
|
||||
store.dispatch.foo();
|
||||
expect(store.getState()).toEqual({
|
||||
tracer: 'ba',
|
||||
bar: { tracer: 'ed' }
|
||||
});
|
||||
store.dispatch({ type: 'bar' });
|
||||
expect(store.getState()).toEqual({
|
||||
tracer: 'ede',
|
||||
bar: { tracer: 'ede' }
|
||||
});
|
||||
});
|
||||
//# sourceMappingURL=splat.test.js.map
|
|
@ -1 +0,0 @@
|
|||
{"version":3,"file":"splat.test.js","sourceRoot":"","sources":["../src/splat.test.ts"],"names":[],"mappings":";;;;;AAAA,yCAAsB;AACtB,oDAAuB;AAEvB,MAAM,MAAM,GAAG,CAAC,GAAU,EAAE,EAAE,CAAC,gBAAC,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,GAAC,EAAE,EAAE,EAAE,CAAC,CAAC,GAAG,GAAG,EAAE,CAAC,CAAC;AAEhE,IAAI,CAAE,mBAAmB,EAAE,GAAG,EAAE;IAC5B,MAAM,GAAG,GAAG,UAAK,CAAC;QACd,SAAS,EAAE;YACP,GAAG,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC;YACtB,GAAG,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC;SACzB;KACJ,CAAC,CAAC;IAEJ,MAAM,KAAK,GAAG,GAAG,CAAC,WAAW,EAAE,CAAC;IAE/B,MAAM,CAAC,KAAK,CAAC,QAAQ,EAAE,CAAC,CAAC,OAAO,CAAC,EAAE,MAAM,EAAE,GAAG,EAAC,CAAC,CAAC;IAEjD,KAAK,CAAC,QAAQ,CAAC,GAAG,EAAE,CAAC;IAErB,MAAM,CAAC,KAAK,CAAC,QAAQ,EAAE,CAAC,CAAC,OAAO,CAAC,EAAE,MAAM,EAAE,IAAI,GAAG,CAAC,CAAC;IAEpD,KAAK,CAAC,QAAQ,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IAEhC,MAAM,CAAC,KAAK,CAAC,QAAQ,EAAE,CAAC,CAAC,OAAO,CAAC,EAAE,MAAM,EAAE,KAAK,GAAG,CAAC,CAAC;AACzD,CAAC,CAAC,CAAC;AAEH,IAAI,CAAE,eAAe,EAAE,GAAG,EAAE;IACxB,MAAM,GAAG,GAAG,UAAK,CAAC;QACd,SAAS,EAAE;YACP,GAAG,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC;YACtB,GAAG,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC;YACtB,GAAG,EAAE,GAAG,EAAE,CAAC,CAAC,EAAC,GAAG,EAAK,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,CAAC;SAC1D;QACD,QAAQ,EAAE;YACN,GAAG,EAAE,UAAK,CAAC;gBACP,SAAS,EAAE;oBACP,GAAG,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC;oBACtB,GAAG,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC;iBACzB;aACJ,CAAC;SACL;KACJ,CAAC,CAAC;IAEJ,MAAM,KAAK,GAAG,GAAG,CAAC,WAAW,EAAE,CAAC;IAE/B,MAAM,CAAC,KAAK,CAAC,QAAQ,EAAE,CAAC,CAAC,OAAO,CAAC;QAC7B,MAAM,EAAE,GAAG;QACX,GAAG,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE;KAAE,CAAC,CAAC;IAE5B,KAAK,CAAC,QAAQ,CAAC,GAAG,EAAE,CAAC;IAErB,MAAM,CAAC,KAAK,CAAC,QAAQ,EAAE,CAAC,CAAC,OAAO,CAAC;QAC7B,MAAM,EAAE,IAAI;QACZ,GAAG,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE;KAAE,CAAC,CAAC;IAE7B,KAAK,CAAC,QAAQ,CAAC,EAAC,IAAI,EAAE,KAAK,EAAC,CAAC,CAAC;IAE9B,MAAM,CAAC,KAAK,CAAC,QAAQ,EAAE,CAAC,CAAC,OAAO,CAAC;QAC7B,MAAM,EAAE,KAAK;QACb,GAAG,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE;KAAE,CAAC,CAAC;AAGlC,CAAC,CAAC,CAAC"}
|
|
@ -1,2 +0,0 @@
|
|||
export {};
|
||||
//# sourceMappingURL=test.d.ts.map
|
|
@ -1 +0,0 @@
|
|||
{"version":3,"file":"test.d.ts","sourceRoot":"","sources":["../src/test.ts"],"names":[],"mappings":""}
|
|
@ -1,133 +0,0 @@
|
|||
"use strict";
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const _1 = __importDefault(require("."));
|
||||
test('actions from mutations', () => {
|
||||
const { actions: { foo, bar }, } = _1.default({
|
||||
mutations: {
|
||||
foo: () => (x) => x,
|
||||
},
|
||||
});
|
||||
expect(foo()).toEqual({ type: 'foo' });
|
||||
expect(foo(true)).toEqual({ type: 'foo', payload: true });
|
||||
expect(foo({ bar: 2 }, { timestamp: 613 })).toEqual({
|
||||
type: 'foo',
|
||||
payload: { bar: 2 },
|
||||
meta: { timestamp: 613 },
|
||||
});
|
||||
});
|
||||
test('reducer', () => {
|
||||
const { actions, reducer } = _1.default({
|
||||
initial: { counter: 1 },
|
||||
mutations: {
|
||||
inc: () => ({ counter }) => ({ counter: counter + 1 }),
|
||||
},
|
||||
});
|
||||
let state = reducer(null, { type: 'noop' });
|
||||
expect(state).toEqual({ counter: 1 });
|
||||
state = reducer(state, actions.inc());
|
||||
expect(state).toEqual({ counter: 2 });
|
||||
});
|
||||
test('sub reducers', () => {
|
||||
const foo = _1.default({
|
||||
initial: 1,
|
||||
mutations: {
|
||||
doFoo: () => (x) => x + 1,
|
||||
doAll: () => (x) => x + 10,
|
||||
},
|
||||
});
|
||||
const bar = _1.default({
|
||||
initial: 'a',
|
||||
mutations: {
|
||||
doBar: () => (x) => x + 'a',
|
||||
doAll: () => (x) => x + 'b',
|
||||
}
|
||||
});
|
||||
const { initial, actions, reducer } = _1.default({
|
||||
subduxes: {
|
||||
foo, bar
|
||||
}
|
||||
});
|
||||
expect(initial).toEqual({ foo: 1, bar: 'a' });
|
||||
expect(Object.keys(actions)).toHaveLength(3);
|
||||
let state = reducer(null, { type: 'noop' });
|
||||
expect(state).toEqual({ foo: 1, bar: 'a' });
|
||||
state = reducer(state, actions.doFoo());
|
||||
expect(state).toEqual({ foo: 2, bar: 'a' });
|
||||
state = reducer(state, actions.doBar());
|
||||
expect(state).toEqual({ foo: 2, bar: 'aa' });
|
||||
state = reducer(state, actions.doAll());
|
||||
expect(state).toEqual({ foo: 12, bar: 'aab' });
|
||||
});
|
||||
test('precedence between root and sub-reducers', () => {
|
||||
const { initial, reducer, actions, } = _1.default({
|
||||
initial: {
|
||||
foo: { bar: 4 },
|
||||
},
|
||||
mutations: {
|
||||
inc: () => (state) => {
|
||||
return {
|
||||
...state,
|
||||
surprise: state.foo.bar
|
||||
};
|
||||
}
|
||||
},
|
||||
subduxes: {
|
||||
foo: _1.default({
|
||||
initial: {
|
||||
bar: 2,
|
||||
quux: 3,
|
||||
},
|
||||
mutations: {
|
||||
inc: () => (state) => ({ ...state, bar: state.bar + 1 })
|
||||
},
|
||||
}),
|
||||
}
|
||||
});
|
||||
expect(initial).toEqual({
|
||||
foo: { bar: 4, quux: 3 }
|
||||
});
|
||||
expect(reducer(null, actions.inc())).toEqual({
|
||||
foo: { bar: 5, quux: 3 }, surprise: 5
|
||||
});
|
||||
});
|
||||
function timeout(ms) {
|
||||
return new Promise(resolve => setTimeout(resolve, ms));
|
||||
}
|
||||
test('middleware', async () => {
|
||||
const { middleware, createStore } = _1.default({
|
||||
initial: "",
|
||||
mutations: {
|
||||
inc: (addition) => (state) => state + addition,
|
||||
doEeet: () => (state) => {
|
||||
return state + 'Z';
|
||||
},
|
||||
},
|
||||
effects: {
|
||||
doEeet: api => next => async (action) => {
|
||||
api.dispatch.inc('a');
|
||||
next(action);
|
||||
await timeout(1000);
|
||||
api.dispatch.inc('c');
|
||||
}
|
||||
},
|
||||
subduxes: {
|
||||
foo: _1.default({
|
||||
effects: {
|
||||
doEeet: (api) => next => action => {
|
||||
api.dispatch({ type: 'inc', payload: 'b' });
|
||||
next(action);
|
||||
}
|
||||
}
|
||||
}),
|
||||
}
|
||||
});
|
||||
const store = createStore();
|
||||
store.dispatch.doEeet();
|
||||
expect(store.getState()).toEqual('abZ');
|
||||
await timeout(1000);
|
||||
expect(store.getState()).toEqual('abZc');
|
||||
});
|
||||
//# sourceMappingURL=test.js.map
|
File diff suppressed because one or more lines are too long
|
@ -1,24 +0,0 @@
|
|||
import { Dispatch, Middleware } from 'redux';
|
||||
export declare type Action = {
|
||||
type: string;
|
||||
payload?: any;
|
||||
meta?: any;
|
||||
};
|
||||
export declare type Dictionary<T> = {
|
||||
[key: string]: T;
|
||||
};
|
||||
export declare type Mutation<S = any> = (payload: any, action: Action) => (state: S) => S;
|
||||
export declare type ActionPayloadGenerator = (...args: any[]) => any;
|
||||
export declare type ActionCreator = (...args: any[]) => Action;
|
||||
export declare type UpduxDispatch = Dispatch & Dictionary<ActionCreator>;
|
||||
export declare type UpduxConfig<S = any> = Partial<{
|
||||
initial: S;
|
||||
subduxes: {};
|
||||
actions: {
|
||||
[type: string]: ActionPayloadGenerator;
|
||||
};
|
||||
mutations: any;
|
||||
effects: Dictionary<Middleware<{}, S, UpduxDispatch>>;
|
||||
}>;
|
||||
export declare type Upreducer<S = any> = (action: Action) => (state: S) => S;
|
||||
//# sourceMappingURL=types.d.ts.map
|
|
@ -1 +0,0 @@
|
|||
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,OAAO,CAAC;AAE7C,oBAAY,MAAM,GAAG;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,GAAG,CAAC;IACd,IAAI,CAAC,EAAE,GAAG,CAAC;CACd,CAAA;AAED,oBAAY,UAAU,CAAC,CAAC,IAAI;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,CAAC,CAAA;CAAE,CAAC;AAEjD,oBAAY,QAAQ,CAAC,CAAC,GAAC,GAAG,IAAI,CAAC,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,KAAK,CAAC,KAAK,EAAE,CAAC,KAAK,CAAC,CAAE;AAEjF,oBAAY,sBAAsB,GAAG,CAAC,GAAG,IAAI,EAAC,GAAG,EAAE,KAAK,GAAG,CAAC;AAE5D,oBAAY,aAAa,GAAG,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAM,MAAM,CAAC;AAExD,oBAAY,aAAa,GAAG,QAAQ,GAAG,UAAU,CAAC,aAAa,CAAC,CAAC;AAEjE,oBAAY,WAAW,CAAC,CAAC,GAAC,GAAG,IAAI,OAAO,CAAC;IACrC,OAAO,EAAE,CAAC,CAAC;IACX,QAAQ,EAAE,EAAE,CAAC;IACb,OAAO,EAAE;QACL,CAAE,IAAI,EAAE,MAAM,GAAI,sBAAsB,CAAA;KAC3C,CAAC;IACF,SAAS,EAAE,GAAG,CAAC;IACf,OAAO,EAAE,UAAU,CAAC,UAAU,CAAC,EAAE,EAAC,CAAC,EAAC,aAAa,CAAC,CAAC,CAAC;CACvD,CAAC,CAAC;AAEH,oBAAY,SAAS,CAAC,CAAC,GAAC,GAAG,IAAI,CAAC,MAAM,EAAC,MAAM,KAAK,CAAC,KAAK,EAAC,CAAC,KAAK,CAAC,CAAC"}
|
|
@ -1,3 +0,0 @@
|
|||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
//# sourceMappingURL=types.js.map
|
|
@ -1 +0,0 @@
|
|||
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
|
|
@ -1,22 +0,0 @@
|
|||
import { UpduxConfig, Dictionary, Action, ActionCreator, Mutation, Upreducer, UpduxDispatch } from './types';
|
||||
import { Middleware, Store } from 'redux';
|
||||
declare type StoreWithDispatchActions<S = any, Actions = {
|
||||
[action: string]: (...args: any) => Action;
|
||||
}> = Store<S> & {
|
||||
dispatch: {
|
||||
[type in keyof Actions]: (...args: any) => void;
|
||||
};
|
||||
};
|
||||
export declare class Updux<S = any> {
|
||||
subduxes: Dictionary<Updux>;
|
||||
actions: Dictionary<ActionCreator>;
|
||||
initial: S;
|
||||
mutations: Dictionary<Mutation>;
|
||||
upreducer: Upreducer<S>;
|
||||
reducer: (state: S | undefined, action: Action) => S;
|
||||
middleware: Middleware<{}, S, UpduxDispatch>;
|
||||
createStore: () => StoreWithDispatchActions<S>;
|
||||
constructor(config: UpduxConfig);
|
||||
}
|
||||
export default Updux;
|
||||
//# sourceMappingURL=updux.d.ts.map
|
|
@ -1 +0,0 @@
|
|||
{"version":3,"file":"updux.d.ts","sourceRoot":"","sources":["../src/updux.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,EAAE,aAAa,EAAE,QAAQ,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAE7G,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,OAAO,CAAC;AAE1C,aAAK,wBAAwB,CAAC,CAAC,GAAC,GAAG,EAAC,OAAO,GAAC;IAAE,CAAC,MAAM,EAAE,MAAM,GAAG,CAAC,GAAG,IAAI,EAAC,GAAG,KAAK,MAAM,CAAA;CAAE,IAAI,KAAK,CAAC,CAAC,CAAC,GAAG;IACpG,QAAQ,EAAE;SAAI,IAAI,IAAI,MAAM,OAAO,GAAI,CAAC,GAAG,IAAI,EAAC,GAAG,KAAK,IAAI;KAAE,CAAA;CACjE,CAAC;AAEF,qBAAa,KAAK,CAAC,CAAC,GAAC,GAAG;IAEpB,QAAQ,EAAE,UAAU,CAAC,KAAK,CAAC,CAAC;IAE5B,OAAO,EAAE,UAAU,CAAC,aAAa,CAAC,CAAA;IAElC,OAAO,EAAE,CAAC,CAAC;IAEX,SAAS,EAAE,UAAU,CAAC,QAAQ,CAAC,CAAC;IAEhC,SAAS,EAAE,SAAS,CAAC,CAAC,CAAC,CAAC;IAExB,OAAO,EAAE,CAAC,KAAK,EAAC,CAAC,GAAC,SAAS,EAAC,MAAM,EAAC,MAAM,KAAK,CAAC,CAAC;IAEhD,UAAU,EAAE,UAAU,CAAC,EAAE,EAAC,CAAC,EAAC,aAAa,CAAC,CAAC;IAE3C,WAAW,EAAE,MAAM,wBAAwB,CAAC,CAAC,CAAC,CAAC;gBAEnC,MAAM,EAAE,WAAW;CAwClC;AAED,eAAe,KAAK,CAAC"}
|
|
@ -1,30 +0,0 @@
|
|||
"use strict";
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const fp_1 = __importDefault(require("lodash/fp"));
|
||||
const buildActions_1 = __importDefault(require("./buildActions"));
|
||||
const buildInitial_1 = __importDefault(require("./buildInitial"));
|
||||
const buildMutations_1 = __importDefault(require("./buildMutations"));
|
||||
const buildCreateStore_1 = __importDefault(require("./buildCreateStore"));
|
||||
const buildMiddleware_1 = __importDefault(require("./buildMiddleware"));
|
||||
const buildUpreducer_1 = __importDefault(require("./buildUpreducer"));
|
||||
class Updux {
|
||||
constructor(config) {
|
||||
this.subduxes = fp_1.default.mapValues((value) => fp_1.default.isPlainObject(value) ? new Updux(value) : value)(fp_1.default.getOr({}, 'subduxes', config));
|
||||
this.actions = buildActions_1.default(config.actions, [...Object.keys(config.mutations || {}), ...Object.keys(config.effects || {})], fp_1.default.flatten(Object.values(this.subduxes).map(({ actions }) => Object.entries(actions))));
|
||||
this.initial = buildInitial_1.default(config.initial, fp_1.default.mapValues(({ initial }) => initial)(this.subduxes));
|
||||
this.mutations = buildMutations_1.default(config.mutations, this.subduxes);
|
||||
this.upreducer = buildUpreducer_1.default(this.initial, this.mutations);
|
||||
this.reducer = (state, action) => {
|
||||
return this.upreducer(action)(state);
|
||||
};
|
||||
this.middleware = buildMiddleware_1.default(config.effects, this.actions, Object.values(this.subduxes).map(sd => sd.middleware));
|
||||
const actions = this.actions;
|
||||
this.createStore = buildCreateStore_1.default(this.reducer, this.initial, this.middleware, this.actions);
|
||||
}
|
||||
}
|
||||
exports.Updux = Updux;
|
||||
exports.default = Updux;
|
||||
//# sourceMappingURL=updux.js.map
|
|
@ -1 +0,0 @@
|
|||
{"version":3,"file":"updux.js","sourceRoot":"","sources":["../src/updux.ts"],"names":[],"mappings":";;;;;AAAA,mDAA2B;AAC3B,kEAA0C;AAC1C,kEAA0C;AAC1C,sEAA8C;AAE9C,0EAAkD;AAClD,wEAAgD;AAChD,sEAA8C;AAS9C,MAAa,KAAK;IAkBd,YAAY,MAAmB;QAE3B,IAAI,CAAC,QAAQ,GAAG,YAAE,CAAC,SAAS,CACxB,CAAC,KAAuB,EAAE,EAAE,CAAC,YAAE,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAE,CAAC,YAAE,CAAC,KAAK,CAAC,EAAE,EAAC,UAAU,EAAC,MAAM,CAAC,CAC9F,CAAC;QAGvB,IAAI,CAAC,OAAO,GAAG,sBAAY,CACvB,MAAM,CAAC,OAAO,EACd,CAAE,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,SAAS,IAAE,EAAE,CAAC,EAAE,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,IAAE,EAAE,CAAE,CAAE,EAC7E,YAAE,CAAC,OAAO,CAAE,MAAM,CAAC,MAAM,CAAE,IAAI,CAAC,QAAQ,CAAE,CAAC,GAAG,CAAE,CAAC,EAAC,OAAO,EAAO,EAAE,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,CAAE,CAAE,CACnG,CAAA;QAED,IAAI,CAAC,OAAO,GAAG,sBAAY,CACvB,MAAM,CAAC,OAAO,EAAE,YAAE,CAAC,SAAS,CAAE,CAAC,EAAC,OAAO,EAAC,EAAE,EAAE,CAAC,OAAO,CAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,CACxE,CAAC;QAEF,IAAI,CAAC,SAAS,GAAG,wBAAc,CAC3B,MAAM,CAAC,SAAS,EAAE,IAAI,CAAC,QAAQ,CAClC,CAAC;QAEF,IAAI,CAAC,SAAS,GAAG,wBAAc,CAC3B,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,SAAS,CAC/B,CAAC;QAEF,IAAI,CAAC,OAAO,GAAG,CAAC,KAAK,EAAC,MAAM,EAAE,EAAE;YAC5B,OAAO,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,KAAU,CAAC,CAAC;QAC9C,CAAC,CAAA;QAED,IAAI,CAAC,UAAU,GAAG,yBAAe,CAC7B,MAAM,CAAC,OAAO,EACd,IAAI,CAAC,OAAO,EACZ,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,GAAG,CAAE,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,UAAU,CAAE,CAC1D,CAAC;QAEF,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC;QAC7B,IAAI,CAAC,WAAW,GAAG,0BAAgB,CAAI,IAAI,CAAC,OAAO,EAAC,IAAI,CAAC,OAAO,EAAC,IAAI,CAAC,UAAwB,EAAC,IAAI,CAAC,OAAO,CACvD,CAAC;IACzD,CAAC;CAEJ;AA1DD,sBA0DC;AAED,kBAAe,KAAK,CAAC"}
|
|
@ -0,0 +1,226 @@
|
|||
# What's Updux?
|
||||
|
||||
So, I'm a fan of [Redux](https://redux.js.org). Two days ago I discovered
|
||||
[rematch](https://rematch.github.io/rematch) alonside a few other frameworks built atop Redux.
|
||||
|
||||
It has a couple of pretty good ideas that removes some of the
|
||||
boilerplate. Keeping mutations and asynchronous effects close to the
|
||||
reducer definition? Nice. Automatically infering the
|
||||
actions from the said mutations and effects? Genius!
|
||||
|
||||
But it also enforces a flat hierarchy of reducers -- where
|
||||
is the fun in that? And I'm also having a strong love for
|
||||
[Updeep](https://github.com/substantial/updeep), so I want reducer state updates to leverage the heck out of it.
|
||||
|
||||
All that to say, say hello to `Updux`. Heavily inspired by `rematch`, but twisted
|
||||
to work with `updeep` and to fit my peculiar needs. It offers features such as
|
||||
|
||||
- Mimic the way VueX has mutations (reducer reactions to specific actions) and
|
||||
effects (middleware reacting to actions that can be asynchronous and/or
|
||||
have side-effects), so everything pertaining to a store are all defined
|
||||
in the space place.
|
||||
- Automatically gather all actions used by the updux's effects and mutations,
|
||||
and makes then accessible as attributes to the `dispatch` object of the
|
||||
store.
|
||||
- Mutations have a signature that is friendly to Updux and Immer.
|
||||
- Also, the mutation signature auto-unwrap the payload of the actions for you.
|
||||
- TypeScript types.
|
||||
|
||||
Fair warning: this package is still very new, probably very buggy,
|
||||
definitively very badly documented, and very subject to changes. Caveat
|
||||
Maxima Emptor.
|
||||
|
||||
# Synopsis
|
||||
|
||||
```
|
||||
import updux from 'updux';
|
||||
|
||||
import otherUpdux from './otherUpdux';
|
||||
|
||||
const {
|
||||
initial,
|
||||
reducer,
|
||||
actions,
|
||||
middleware,
|
||||
createStore,
|
||||
} = new Updux({
|
||||
initial: {
|
||||
counter: 0,
|
||||
},
|
||||
subduxes: {
|
||||
otherUpdux,
|
||||
},
|
||||
mutations: {
|
||||
inc: ( increment = 1 ) => u({counter: s => s + increment })
|
||||
},
|
||||
effects: {
|
||||
'*' => api => next => action => {
|
||||
console.log( "hey, look, an action zoomed by!", action );
|
||||
next(action);
|
||||
};
|
||||
},
|
||||
actions: {
|
||||
customAction: ( someArg ) => ({
|
||||
type: "custom",
|
||||
payload: { someProp: someArg }
|
||||
}),
|
||||
},
|
||||
|
||||
});
|
||||
|
||||
const store = createStore();
|
||||
|
||||
store.dispatch.inc(3);
|
||||
```
|
||||
|
||||
# Description
|
||||
|
||||
Full documentation can be [found here](https://yanick.github.io/updux/).
|
||||
Right now the best way to understand the whole thing is to go
|
||||
through the [tutorial](https://yanick.github.io/updux/#/tutorial)
|
||||
|
||||
## Exporting upduxes
|
||||
|
||||
If you are creating upduxes that will be used as subduxes
|
||||
by other upduxes, or as
|
||||
[ducks](https://github.com/erikras/ducks-modular-redux)-like containers, I
|
||||
recommend that you export the Updux instance as the default export:
|
||||
|
||||
```
|
||||
import Updux from 'updux';
|
||||
|
||||
const updux = new Updux({ ... });
|
||||
|
||||
export default updux;
|
||||
```
|
||||
|
||||
Then you can use them as subduxes like this:
|
||||
|
||||
```
|
||||
import Updux from 'updux';
|
||||
import foo from './foo'; // foo is an Updux
|
||||
import bar from './bar'; // bar is an Updux as well
|
||||
|
||||
const updux = new Updux({
|
||||
subduxes: {
|
||||
foo, bar
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
Or if you want to use it:
|
||||
|
||||
```
|
||||
import updux from './myUpdux';
|
||||
|
||||
const {
|
||||
reducer,
|
||||
actions: { doTheThing },
|
||||
createStore,
|
||||
middleware,
|
||||
} = updux;
|
||||
```
|
||||
|
||||
## Mapping a mutation to all values of a state
|
||||
|
||||
Say you have a `todos` state that is an array of `todo` sub-states. It's easy
|
||||
enough to have the main reducer maps away all items to the sub-reducer:
|
||||
|
||||
```
|
||||
const todo = new Updux({
|
||||
mutations: {
|
||||
review: () => u({ reviewed: true}),
|
||||
done: () => u({done: true}),
|
||||
},
|
||||
});
|
||||
|
||||
const todos = new Updux({ initial: [] });
|
||||
|
||||
todos.addMutation(
|
||||
todo.actions.review,
|
||||
(_,action) => state => state.map( todo.upreducer(action) )
|
||||
);
|
||||
todos.addMutation(
|
||||
todo.actions.done,
|
||||
(id,action) => u.map(u.if(u.is('id',id), todo.upreducer(action))),
|
||||
);
|
||||
|
||||
```
|
||||
|
||||
But `updeep` can iterate through all the items of an array (or the values of
|
||||
an object) via the special key `*`. So the todos updux above could also be
|
||||
written:
|
||||
|
||||
```
|
||||
const todo = new Updux({
|
||||
mutations: {
|
||||
review: () => u({ reviewed: true}),
|
||||
done: () => u({done: true}),
|
||||
},
|
||||
});
|
||||
|
||||
const todos = new Updux({
|
||||
subduxes: { '*': todo },
|
||||
});
|
||||
|
||||
todos.addMutation(
|
||||
todo.actions.done,
|
||||
(id,action) => u.map(u.if(u.is('id',id), todo.upreducer(action))),
|
||||
true
|
||||
);
|
||||
```
|
||||
|
||||
The advantages being that the actions/mutations/effects of the subdux will be
|
||||
imported by the root updux as usual, and all actions that aren't being
|
||||
overridden by a sink mutation will trickle down automatically.
|
||||
|
||||
## Usage with Immer
|
||||
|
||||
While Updux was created with Updeep in mind, it also plays very
|
||||
well with [Immer](https://immerjs.github.io/immer/docs/introduction).
|
||||
|
||||
For example, taking this basic updux:
|
||||
|
||||
```
|
||||
import Updux from 'updux';
|
||||
|
||||
const updux = new Updux({
|
||||
initial: { counter: 0 },
|
||||
mutations: {
|
||||
add: (inc=1) => state => { counter: counter + inc }
|
||||
}
|
||||
});
|
||||
|
||||
```
|
||||
|
||||
Converting it to Immer would look like:
|
||||
|
||||
```
|
||||
import Updux from 'updux';
|
||||
import { produce } from 'Immer';
|
||||
|
||||
const updux = new Updux({
|
||||
initial: { counter: 0 },
|
||||
mutations: {
|
||||
add: (inc=1) => produce( draft => draft.counter += inc ) }
|
||||
}
|
||||
});
|
||||
|
||||
```
|
||||
|
||||
But since typing `produce` over and over is no fun, `groomMutations`
|
||||
can be used to wrap all mutations with it:
|
||||
|
||||
```
|
||||
import Updux from 'updux';
|
||||
import { produce } from 'Immer';
|
||||
|
||||
const updux = new Updux({
|
||||
initial: { counter: 0 },
|
||||
groomMutations: mutation => (...args) => produce( mutation(...args) ),
|
||||
mutations: {
|
||||
add: (inc=1) => draft => draft.counter += inc
|
||||
}
|
||||
});
|
||||
|
||||
```
|
|
@ -0,0 +1,3 @@
|
|||
* [Home](/)
|
||||
* [ Tutorial ](tutorial.md)
|
||||
* [ Recipes ](recipes.md)
|
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,24 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Document</title>
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
|
||||
<meta name="description" content="Description">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0">
|
||||
<link rel="stylesheet" href="//cdn.jsdelivr.net/npm/docsify@4/lib/themes/vue.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script>
|
||||
window.$docsify = {
|
||||
loadSidebar: true,
|
||||
name: 'updux',
|
||||
repo: '',
|
||||
subMaxLevel: 3,
|
||||
}
|
||||
</script>
|
||||
<!-- Docsify v4 -->
|
||||
<script src="docsify.js"></script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,96 @@
|
|||
# Recipes
|
||||
|
||||
## Mapping a mutation to all values of a state
|
||||
|
||||
Say you have a `todos` state that is an array of `todo` sub-states, with some
|
||||
actions that should percolate to all todos, and some that should only
|
||||
percolate to one. One way to model this is via updux's splat subduxes
|
||||
(backed by `updeep`'s own '*'-key behavior).
|
||||
|
||||
```
|
||||
const done = () => (state) => ({...state, done: true});
|
||||
|
||||
const todo = new Updux({
|
||||
initial: { id: 0, done: false },
|
||||
actions: {
|
||||
done: null,
|
||||
doneAll: null,
|
||||
},
|
||||
mutations: {
|
||||
done,
|
||||
doneAll: done,
|
||||
},
|
||||
});
|
||||
|
||||
const todos = new Updux({
|
||||
initial: [],
|
||||
subduxes: { '*': todo },
|
||||
actions: { addTodo: null },
|
||||
mutations: {
|
||||
addTodo: text => state => [ ...state, { text } ]
|
||||
}
|
||||
});
|
||||
|
||||
todos.setMutation(
|
||||
todo.actions.done,
|
||||
(text,action) => u.map(u.if(u.is('text',text), todo.upreducer(action))),
|
||||
true // prevents the subduxes mutations to run automatically
|
||||
);
|
||||
|
||||
```
|
||||
|
||||
## Usage with Immer
|
||||
|
||||
While Updux was created with Updeep in mind, it also plays very
|
||||
well with [Immer](https://immerjs.github.io/immer/docs/introduction).
|
||||
|
||||
For example, taking this basic updux:
|
||||
|
||||
```
|
||||
import Updux from 'updux';
|
||||
|
||||
const updux = new Updux({
|
||||
initial: { counter: 0 },
|
||||
mutations: {
|
||||
add: (inc=1) => state => ({ counter: state.counter + inc })
|
||||
}
|
||||
});
|
||||
|
||||
```
|
||||
|
||||
Converting it to Immer would look like:
|
||||
|
||||
|
||||
```
|
||||
import Updux from 'updux';
|
||||
import { produce } from 'immer';
|
||||
|
||||
const updux = new Updux({
|
||||
initial: { counter: 0 },
|
||||
mutations: {
|
||||
add: (inc=1) => produce( draft => draft.counter += inc ) }
|
||||
}
|
||||
});
|
||||
|
||||
```
|
||||
|
||||
But since typing `produce` over and over is no fun, `groomMutations`
|
||||
can be used to wrap all mutations with it:
|
||||
|
||||
|
||||
```
|
||||
import Updux from 'updux';
|
||||
import { produce } from 'immer';
|
||||
|
||||
const updux = new Updux({
|
||||
initial: { counter: 0 },
|
||||
groomMutations: mutation => (...args) => produce( mutation(...args) ),
|
||||
mutations: {
|
||||
add: (inc=1) => draft => draft.counter += inc
|
||||
}
|
||||
});
|
||||
|
||||
```
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
import { test, expect } from 'vitest';
|
||||
|
||||
import u from 'updeep';
|
||||
import { Updux } from '../src/index.js';
|
||||
|
||||
const done = () => (state) => ({...state, done: true});
|
||||
|
||||
const todo = new Updux({
|
||||
initial: { id: 0, done: false },
|
||||
actions: {
|
||||
done: null,
|
||||
doneAll: null,
|
||||
},
|
||||
mutations: {
|
||||
done,
|
||||
doneAll: done,
|
||||
},
|
||||
});
|
||||
|
||||
const todos = new Updux({
|
||||
initial: [],
|
||||
subduxes: { '*': todo },
|
||||
actions: { addTodo: null },
|
||||
mutations: {
|
||||
addTodo: text => state => [ ...state, { text } ]
|
||||
}
|
||||
});
|
||||
|
||||
todos.setMutation(
|
||||
todo.actions.done,
|
||||
(text,action) => u.map(u.if(u.is('text',text), todo.upreducer(action))),
|
||||
true // prevents the subduxes mutations to run automatically
|
||||
);
|
||||
|
||||
test( "tutorial", async () => {
|
||||
const store = todos.createStore();
|
||||
|
||||
store.dispatch.addTodo('one');
|
||||
store.dispatch.addTodo('two');
|
||||
store.dispatch.addTodo('three');
|
||||
|
||||
store.dispatch.done( 'two' );
|
||||
|
||||
expect( store.getState()[1].done ).toBeTruthy();
|
||||
expect( store.getState()[2].done ).toBeFalsy();
|
||||
|
||||
store.dispatch.doneAll();
|
||||
|
||||
expect( store.getState().map( ({done}) => done ) ).toEqual([
|
||||
true, true, true
|
||||
]);
|
||||
|
||||
});
|
||||
|
|
@ -0,0 +1,69 @@
|
|||
import { test, expect } from 'vitest';
|
||||
|
||||
import u from 'updeep';
|
||||
import { action, Updux, dux } from '../src/index.js';
|
||||
|
||||
const addTodoWithId = action('addTodoWithId');
|
||||
const incNextId = action('incNextId');
|
||||
const addTodo = action('addTodo');
|
||||
|
||||
const addTodoEffect = ({ getState, dispatch }) => next => action => {
|
||||
const id = getState.nextId();
|
||||
|
||||
dispatch.incNextId();
|
||||
|
||||
next(action);
|
||||
|
||||
dispatch.addTodoWithId({ description: action.payload, id });
|
||||
}
|
||||
|
||||
const todosDux = new Updux({
|
||||
initial: { nextId: 1, todos: [] },
|
||||
actions: { addTodo, incNextId, addTodoWithId },
|
||||
selectors: {
|
||||
nextId: ({nextId}) => nextId,
|
||||
},
|
||||
mutations: {
|
||||
addTodoWithId: (todo) => u({ todos: (todos) => [...todos, todo] }),
|
||||
incNextId: () => u({ nextId: id => id+1 }),
|
||||
},
|
||||
effects: {
|
||||
'addTodo': addTodoEffect
|
||||
}
|
||||
});
|
||||
|
||||
const store = todosDux.createStore();
|
||||
|
||||
test( "tutorial example", async () => {
|
||||
store.dispatch.addTodo('Do the thing');
|
||||
|
||||
expect( store.getState() ).toMatchObject({
|
||||
nextId:2, todos: [ { description: 'Do the thing', id: 1 } ]
|
||||
})
|
||||
|
||||
});
|
||||
|
||||
test( "catch-all effect", () => {
|
||||
|
||||
let seen = [];
|
||||
|
||||
const foo = new Updux({
|
||||
actions: {
|
||||
one: null,
|
||||
two: null,
|
||||
},
|
||||
effects: {
|
||||
'*': (api) => next => action => {
|
||||
seen.push(action.type);
|
||||
next(action);
|
||||
}
|
||||
}
|
||||
} );
|
||||
|
||||
const store = foo.createStore();
|
||||
|
||||
store.dispatch.one();
|
||||
store.dispatch.two();
|
||||
|
||||
expect(seen).toEqual([ 'one', 'two' ]);
|
||||
} )
|
|
@ -0,0 +1,40 @@
|
|||
import { test, expect } from 'vitest';
|
||||
import u from 'updeep';
|
||||
|
||||
import { Updux } from '../src/index.js';
|
||||
|
||||
const todos = new Updux({
|
||||
initial: [],
|
||||
actions: {
|
||||
setNbrTodos: null,
|
||||
addTodo: null,
|
||||
},
|
||||
mutations: {
|
||||
addTodo: todo => todos => [ ...todos, todo ],
|
||||
},
|
||||
reactions: [
|
||||
({dispatch}) => todos => dispatch.setNbrTodos(todos.length)
|
||||
],
|
||||
});
|
||||
|
||||
const myDux = new Updux({
|
||||
initial: {
|
||||
nbrTodos: 0
|
||||
},
|
||||
subduxes: {
|
||||
todos,
|
||||
},
|
||||
mutations: {
|
||||
setNbrTodos: nbrTodos => u({ nbrTodos })
|
||||
}
|
||||
});
|
||||
|
||||
test( "basic tests", async () => {
|
||||
const store = myDux.createStore();
|
||||
|
||||
store.dispatch.addTodo('one');
|
||||
store.dispatch.addTodo('two');
|
||||
|
||||
expect(store.getState().nbrTodos).toEqual(2);
|
||||
});
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
import { test, expect } from 'vitest';
|
||||
|
||||
import { Updux } from '../src/index.js';
|
||||
|
||||
test( 'selectors', () => {
|
||||
const dux = new Updux({
|
||||
initial: { a: 1, b: 2 },
|
||||
selectors: {
|
||||
getA: ({a}) => a,
|
||||
getBPlus: ({b}) => addition => b + addition,
|
||||
},
|
||||
subduxes: {
|
||||
subbie: new Updux({
|
||||
initial: { d: 3 },
|
||||
selectors: {
|
||||
getD: ({d}) => d
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
const store = dux.createStore();
|
||||
|
||||
expect( store.getState.getA() ).toEqual(1);
|
||||
expect( store.getState.getBPlus(7) ).toEqual(9);
|
||||
expect( store.getState.getD() ).toEqual(3);
|
||||
} );
|
|
@ -0,0 +1,91 @@
|
|||
import { test, expect } from 'vitest';
|
||||
|
||||
import u from 'updeep';
|
||||
import R from 'remeda';
|
||||
|
||||
import { Updux } from '../src/index.js';
|
||||
|
||||
const nextIdDux = new Updux({
|
||||
initial: 1,
|
||||
actions: {
|
||||
incrementNextId: null,
|
||||
},
|
||||
selectors: {
|
||||
getNextId: state => state
|
||||
},
|
||||
mutations: {
|
||||
incrementNextId: () => state => state + 1,
|
||||
}
|
||||
});
|
||||
|
||||
const matches = conditions => target => Object.entries(conditions).every(
|
||||
([key,value]) => typeof value === 'function' ? value(target[key]) : target[key] === value
|
||||
);
|
||||
|
||||
const todoDux = new Updux({
|
||||
initial: {
|
||||
id: 0,
|
||||
description: "",
|
||||
done: false,
|
||||
},
|
||||
actions: {
|
||||
todoDone: null,
|
||||
},
|
||||
mutations: {
|
||||
todoDone: id => u.if( matches({id}), { done: true })
|
||||
},
|
||||
selectors: {
|
||||
desc: R.prop('description'),
|
||||
}
|
||||
});
|
||||
|
||||
const todosDux = new Updux({
|
||||
initial: [],
|
||||
subduxes: {
|
||||
'*': todoDux
|
||||
},
|
||||
actions: {
|
||||
addTodoWithId: (description, id) => ({description, id} )
|
||||
},
|
||||
findSelectors: {
|
||||
getTodoById: state => id => state.find(matches({id}))
|
||||
},
|
||||
mutations: {
|
||||
addTodoWithId: todo => todos => [...todos, todo]
|
||||
}
|
||||
});
|
||||
|
||||
const mainDux = new Updux({
|
||||
subduxes: {
|
||||
nextId: nextIdDux,
|
||||
todos: todosDux,
|
||||
},
|
||||
actions: {
|
||||
addTodo: null
|
||||
},
|
||||
effects: {
|
||||
addTodo: ({ getState, dispatch }) => next => action => {
|
||||
const id = getState.getNextId();
|
||||
|
||||
dispatch.incrementNextId()
|
||||
|
||||
next(action);
|
||||
|
||||
dispatch.addTodoWithId( action.payload, id );
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const store = mainDux.createStore();
|
||||
|
||||
|
||||
test( "basic tests", () => {
|
||||
|
||||
const myDesc = 'do the thing';
|
||||
store.dispatch.addTodo(myDesc);
|
||||
|
||||
expect( store.getState.getTodoById(1).desc() ).toEqual(myDesc);
|
||||
|
||||
|
||||
|
||||
});
|
|
@ -0,0 +1,407 @@
|
|||
# Tutorial
|
||||
|
||||
This tutorial walks you through the features of `Updux` using the
|
||||
time-honored example of the implementation of Todo list store.
|
||||
|
||||
We'll be using
|
||||
[updeep](https://www.npmjs.com/package/updeep) to
|
||||
help with immutability and deep merging,
|
||||
but that's totally optional. If `updeep` is not your bag,
|
||||
it can easily be substitued with, say, [immer][], [lodash][], or even
|
||||
plain JavaScript.
|
||||
|
||||
## Definition of the state
|
||||
|
||||
To begin with, let's define that has nothing but an initial state.
|
||||
|
||||
```js
|
||||
import { Updux } from 'updux';
|
||||
|
||||
const todosDux = new Updux({
|
||||
initial: {
|
||||
next_id: 1,
|
||||
todos: [],
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
Congrats! You have written your first Updux object. It
|
||||
doesn't do a lot, but you can already create a store out of it, and its
|
||||
initial state will be automatically set:
|
||||
|
||||
```js
|
||||
const store = todosDux.createStore();
|
||||
|
||||
console.log(store.getState()); // prints { next_id: 1, todos: [] }
|
||||
```
|
||||
|
||||
## Add actions
|
||||
|
||||
This is all good, but a little static. Let's add actions!
|
||||
|
||||
```js
|
||||
const todosDux = new Updux({
|
||||
initial: {
|
||||
next_id: 1,
|
||||
todos: [],
|
||||
},
|
||||
{
|
||||
addTodo: null,
|
||||
todoDone: null,
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
### Accessing actions
|
||||
|
||||
Once an action is defined, its creator is accessible via the `actions` accessor.
|
||||
|
||||
```js
|
||||
console.log( todosDux.actions.addTodo('write tutorial') );
|
||||
// prints { type: 'addTodo', payload: 'write tutorial' }
|
||||
```
|
||||
### Adding a mutation
|
||||
|
||||
Mutations are the reducing functions associated to actions. They
|
||||
are defined via the `setMutation` method:
|
||||
|
||||
|
||||
```js
|
||||
todosDux.setMutation( 'addTodo', description => ({next_id: id, todos}) => ({
|
||||
next_id: 1 + id,
|
||||
todos: [...todos, { description, id, done: false }]
|
||||
}));
|
||||
```
|
||||
## Effects
|
||||
|
||||
In addition to mutations, Updux also provides action-specific middleware, here
|
||||
called effects.
|
||||
|
||||
Effects use the usual Redux middleware signature, plus a few goodies.
|
||||
The `getState` and `dispatch` functions are augmented with the dux selectors,
|
||||
and actions, respectively. The selectors and actions are also available
|
||||
from the api object.
|
||||
|
||||
```js
|
||||
import u from 'updeep';
|
||||
import { action, Updux } from 'updux';
|
||||
|
||||
// we want to decouple the increment of next_id and the creation of
|
||||
// a new todo. So let's use a new version of the action 'addTodo'.
|
||||
|
||||
const addTodoWithId = action('addTodoWithId');
|
||||
const incNextId = action('incNextId');
|
||||
const addTodo = action('addTodo');
|
||||
|
||||
const addTodoEffect = ({ getState, dispatch }) => next => action => {
|
||||
const id = getState.nextId();
|
||||
|
||||
dispatch.incNextId();
|
||||
|
||||
next(action);
|
||||
|
||||
dispatch.addTodoWithId({ description: action.payload, id });
|
||||
}
|
||||
|
||||
const todosDux = new Updux({
|
||||
initial: { nextId: 1, todos: [] },
|
||||
actions: { addTodo, incNextId, addTodoWithId },
|
||||
selectors: {
|
||||
nextId: ({nextId}) => nextId,
|
||||
},
|
||||
mutations: {
|
||||
addTodoWithId: (todo) => u({ todos: (todos) => [...todos, todo] }),
|
||||
incNextId: () => u({ nextId: id => id+1 }),
|
||||
},
|
||||
effects: {
|
||||
'addTodo': addTodoEffect
|
||||
}
|
||||
});
|
||||
|
||||
const store = todosDux.createStore();
|
||||
|
||||
store.dispatch.addTodo('Do the thing');
|
||||
```
|
||||
|
||||
### Catch-all effect
|
||||
|
||||
It is possible to have an effect match all actions via the special `*` token.
|
||||
|
||||
```
|
||||
todosUpdux.addEffect('*', () => next => action => {
|
||||
console.log( 'seeing action fly by:', action );
|
||||
next(action);
|
||||
});
|
||||
```
|
||||
|
||||
## Adding selectors
|
||||
|
||||
Selectors can be defined to get data derived from the state.
|
||||
From now you should know the drill: selectors can be defined at construction
|
||||
time or via `setSelector`.
|
||||
|
||||
```
|
||||
const getTodoById = ({todos}) => targetId => todos.find(({id}) => id === targetId);
|
||||
|
||||
const todosUpdux = new Updux({
|
||||
selectors: {
|
||||
getTodoById
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
or
|
||||
|
||||
```
|
||||
todosDux.setSelector('getTodoById', getTodoById);
|
||||
```
|
||||
|
||||
### Accessing selectors
|
||||
|
||||
The `getState` method of a dux store is augmented
|
||||
with its selectors, with the first call for the state already
|
||||
called in for you.
|
||||
|
||||
```js
|
||||
const store = todosDux.createStore();
|
||||
|
||||
console.log(
|
||||
todosUpdux.getState.getTodoById(1)
|
||||
);
|
||||
```
|
||||
|
||||
## Subduxes
|
||||
|
||||
Now that we have all the building blocks, we can embark on the last and
|
||||
funkiest part of Updux: its recursive nature.
|
||||
|
||||
### Recap: the Todos dux, undivided
|
||||
|
||||
Upduxes can be divided into sub-upduxes that deal with the various parts of
|
||||
the global state. This is better understood by working out an example, so
|
||||
let's recap on the Todos dux we have so far:
|
||||
|
||||
```js
|
||||
import Updux from 'updux';
|
||||
import u from 'updeep';
|
||||
import fp from 'lodash/fp';
|
||||
|
||||
const todosDux = new Updux({
|
||||
initial: {
|
||||
nextId: 1,
|
||||
todos: [],
|
||||
},
|
||||
actions: {
|
||||
addTodo: null,
|
||||
addTodoWithId: (description, id) => ({description, id, done: false}),
|
||||
todoDone: null,
|
||||
incNextId: null,
|
||||
},
|
||||
selectors: {
|
||||
getTodoById: ({todos}) => id => fp.find({id},todos)
|
||||
},
|
||||
mutations: {
|
||||
addTodoWithId: todo =>
|
||||
u.updateIn( 'todos', todos => [ ...todos, todo] ),
|
||||
incrementNextId: () => u({ nextId: fp.add(1) }),
|
||||
todoDone: (id) => u.updateIn('todos',
|
||||
u.map( u.if( fp.matches({id}), todo => u({done: true}, todo) ) )
|
||||
),
|
||||
},
|
||||
effects: {
|
||||
addTodo: ({ getState, dispatch }) => next => action => {
|
||||
const { nextId: id } = getState();
|
||||
|
||||
dispatch.incNextId();
|
||||
|
||||
next(action);
|
||||
|
||||
dispatch.addTodoWithId(action.payload, id);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
```
|
||||
|
||||
This store has two main components: the `nextId`, and the `todos` collection.
|
||||
The `todos` collection is itself composed of the individual `todo`s. Let's
|
||||
create upduxes for each of those.
|
||||
|
||||
### NextId dux
|
||||
|
||||
```
|
||||
// dux/nextId.js
|
||||
|
||||
import { Updux } from 'updux';
|
||||
import u from 'updeep';
|
||||
|
||||
export default new Updux({
|
||||
initial: 1,
|
||||
actions: {
|
||||
incrementNextId: null,
|
||||
},
|
||||
selectors: {
|
||||
getNextId: state => state
|
||||
},
|
||||
mutations: {
|
||||
incrementNextId: () => state => state + 1,
|
||||
}
|
||||
});
|
||||
|
||||
```
|
||||
|
||||
### Todo updux
|
||||
|
||||
```
|
||||
// dux/todos/todo/index.ts
|
||||
|
||||
import { Updux } from 'updux';
|
||||
import u from 'updeep';
|
||||
import fp from 'lodash/fp';
|
||||
|
||||
export default new Updux({
|
||||
initial: {
|
||||
id: 0,
|
||||
description: "",
|
||||
done: false,
|
||||
},
|
||||
actions: {
|
||||
todoDone: null,
|
||||
},
|
||||
mutations: {
|
||||
todoDone: id => u.if( fp.matches({id}), { done: true }) )
|
||||
},
|
||||
selectors: {
|
||||
desc: ({description}) => description,
|
||||
}
|
||||
});
|
||||
|
||||
```
|
||||
|
||||
### Todos updux
|
||||
|
||||
```
|
||||
// dux/todos/index.js
|
||||
|
||||
import { Updux } from 'updux';
|
||||
import u from 'updeep';
|
||||
import fp from 'lodash/fp';
|
||||
|
||||
import todo from './todo/index.js';
|
||||
|
||||
export default new Updux({
|
||||
initial: [],
|
||||
subduxes: {
|
||||
'*': todoDux
|
||||
},
|
||||
actions: {
|
||||
addTodoWithId: (description, id) => ({description, id} )
|
||||
},
|
||||
findSelectors: {
|
||||
getTodoById: state => id => fp.find({id},state)
|
||||
},
|
||||
mutations: {
|
||||
addTodoWithId: todo =>
|
||||
todos => [ ...todos, todo ]
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
Note the special '\*' subdux key used here. This
|
||||
allows the updux to map every item present in its
|
||||
state to a `todo` updux. See [this recipe](/recipes?id=mapping-a-mutation-to-all-values-of-a-state) for details.
|
||||
|
||||
### Main store
|
||||
|
||||
```
|
||||
// dux/index.js
|
||||
|
||||
import Updux from 'updux';
|
||||
|
||||
import todos from './todos';
|
||||
import nextId from './next_id';
|
||||
|
||||
export new Updux({
|
||||
subduxes: {
|
||||
nextId,
|
||||
todos,
|
||||
},
|
||||
actions: {
|
||||
addTodo: null
|
||||
},
|
||||
effects: {
|
||||
addTodo: ({ getState, dispatch }) => next => action => {
|
||||
const id = getState.getNextId();
|
||||
|
||||
dispatch.incrementNextId()
|
||||
|
||||
next(action);
|
||||
|
||||
dispatch.addTodoWithId( action.payload, id );
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
```
|
||||
|
||||
Tadah! We had to define the `addTodo` effect at the top level as it needs to
|
||||
access the `getNextId` selector from `nextId` and the `addTodoWithId`
|
||||
action from the `todos`.
|
||||
|
||||
Note that the `getNextId` selector still gets the
|
||||
right value; when aggregating subduxes selectors Updux auto-wraps them to
|
||||
access the right slice of the top object. ```
|
||||
|
||||
## Reactions
|
||||
|
||||
Reactions -- aka Redux's subscriptions -- can be added to a updux store via the initial config
|
||||
or the method `addSubscription`. The signature of a reaction is:
|
||||
|
||||
```
|
||||
(storeApi) => (state, previousState, unsubscribe) => {
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
Subscriptions registered for an updux and its subduxes are automatically
|
||||
subscribed to the store when calling `createStore`.
|
||||
|
||||
The `state` passed to the subscriptions of the subduxes is the local state.
|
||||
|
||||
Also, all subscriptions are wrapped such that they are called only if the
|
||||
local `state` changed since their last invocation.
|
||||
|
||||
Example:
|
||||
|
||||
```
|
||||
const todos = new Updux({
|
||||
initial: [],
|
||||
actions: {
|
||||
setNbrTodos: null,
|
||||
addTodo: null,
|
||||
},
|
||||
mutations: {
|
||||
addTodo: todo => todos => [ ...todos, todo ],
|
||||
},
|
||||
reactions: [
|
||||
({dispatch}) => todos => dispatch.setNbrTodos(todos.length)
|
||||
],
|
||||
});
|
||||
|
||||
const myDux = new Updux({
|
||||
initial: {
|
||||
nbrTodos: 0
|
||||
},
|
||||
subduxes: {
|
||||
todos,
|
||||
},
|
||||
mutations: {
|
||||
setNbrTodos: nbrTodos => u({ nbrTodos })
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
[immer]: https://www.npmjs.com/package/immer
|
||||
[lodash]: https://www.npmjs.com/package/lodash
|
||||
[ts-action]: https://www.npmjs.com/package/ts-action
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
import { test, expect } from 'vitest';
|
||||
|
||||
import { Updux } from '../src/index.js';
|
||||
|
||||
test( "basic checks", async () => {
|
||||
|
||||
|
||||
const todosDux = new Updux({
|
||||
initial: {
|
||||
next_id: 1,
|
||||
todos: [],
|
||||
},
|
||||
actions: {
|
||||
addTodo: null,
|
||||
todoDone: null,
|
||||
}
|
||||
});
|
||||
|
||||
const store = todosDux.createStore();
|
||||
|
||||
expect(store.getState()).toEqual({ next_id: 1, todos: [] });
|
||||
|
||||
expect(store.actions.addTodo("learn updux")).toMatchObject({
|
||||
type: 'addTodo', payload: 'learn updux'
|
||||
})
|
||||
});
|
|
@ -1 +0,0 @@
|
|||
|
|
@ -1,17 +0,0 @@
|
|||
import { expectType, expectError } from 'tsd';
|
||||
|
||||
import buildInitial from './src/buildInitial';
|
||||
|
||||
expectType<{}>(buildInitial());
|
||||
|
||||
type MyState = {
|
||||
foo: {
|
||||
bar: number
|
||||
},
|
||||
baz: string,
|
||||
}
|
||||
|
||||
expectType<MyState>(buildInitial<MyState>());
|
||||
|
||||
expectError( buildInitial<MyState>({ foo: { bar: "potato" } }) );
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
module.exports = {
|
||||
"roots": [
|
||||
"./src"
|
||||
],
|
||||
"transform": {
|
||||
"^.+\\.ts$": "ts-jest",
|
||||
"^.+\\.js$": "babel-jest",
|
||||
},
|
||||
}
|
|
@ -0,0 +1,293 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>JSDoc: Class: Updux</title>
|
||||
|
||||
<script src="scripts/prettify/prettify.js"> </script>
|
||||
<script src="scripts/prettify/lang-css.js"> </script>
|
||||
<!--[if lt IE 9]>
|
||||
<script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
|
||||
<![endif]-->
|
||||
<link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css">
|
||||
<link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div id="main">
|
||||
|
||||
<h1 class="page-title">Class: Updux</h1>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<section>
|
||||
|
||||
<header>
|
||||
|
||||
<h2><span class="attribs"><span class="type-signature"></span></span>Updux<span class="signature">()</span><span class="type-signature"></span></h2>
|
||||
|
||||
|
||||
</header>
|
||||
|
||||
<article>
|
||||
<div class="container-overview">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<h4 class="name" id="Updux"><span class="type-signature"></span>new Updux<span class="signature">()</span><span class="type-signature"></span></h4>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<dl class="details">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<dt class="tag-source">Source:</dt>
|
||||
<dd class="tag-source"><ul class="dummy"><li>
|
||||
<a href="Updux.js.html">Updux.js</a>, <a href="Updux.js.html#line24">line 24</a>
|
||||
</li></ul></dd>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</dl>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<h3 class="subsection-title">Classes</h3>
|
||||
|
||||
<dl>
|
||||
<dt><a href="Updux.html">Updux</a></dt>
|
||||
<dd></dd>
|
||||
</dl>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<h3 class="subsection-title">Members</h3>
|
||||
|
||||
|
||||
|
||||
<h4 class="name" id="actions"><span class="type-signature"></span>actions<span class="type-signature"></span></h4>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<dl class="details">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<dt class="tag-source">Source:</dt>
|
||||
<dd class="tag-source"><ul class="dummy"><li>
|
||||
<a href="Updux.js.html">Updux.js</a>, <a href="Updux.js.html#line111">line 111</a>
|
||||
</li></ul></dd>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</dl>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<h4 class="name" id="initial"><span class="type-signature"></span>initial<span class="type-signature"></span></h4>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<dl class="details">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<dt class="tag-source">Source:</dt>
|
||||
<dd class="tag-source"><ul class="dummy"><li>
|
||||
<a href="Updux.js.html">Updux.js</a>, <a href="Updux.js.html#line104">line 104</a>
|
||||
</li></ul></dd>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</dl>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</article>
|
||||
|
||||
</section>
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<nav>
|
||||
<h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="Updux.html">Updux</a></li></ul>
|
||||
</nav>
|
||||
|
||||
<br class="clear">
|
||||
|
||||
<footer>
|
||||
Documentation generated by <a href="https://github.com/jsdoc/jsdoc">JSDoc 3.6.7</a> on Tue Oct 12 2021 10:16:59 GMT-0400 (Eastern Daylight Time)
|
||||
</footer>
|
||||
|
||||
<script> prettyPrint(); </script>
|
||||
<script src="scripts/linenumber.js"> </script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,368 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>JSDoc: Source: Updux.js</title>
|
||||
|
||||
<script src="scripts/prettify/prettify.js"> </script>
|
||||
<script src="scripts/prettify/lang-css.js"> </script>
|
||||
<!--[if lt IE 9]>
|
||||
<script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
|
||||
<![endif]-->
|
||||
<link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css">
|
||||
<link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div id="main">
|
||||
|
||||
<h1 class="page-title">Source: Updux.js</h1>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<section>
|
||||
<article>
|
||||
<pre class="prettyprint source linenums"><code>/* TODO change * for leftovers to +, change subscriptions to reactions */
|
||||
import moize from 'moize';
|
||||
import u from '@yanick/updeep';
|
||||
import { createStore as reduxCreateStore, applyMiddleware } from 'redux';
|
||||
import { get, map, mapValues, merge, difference } from 'lodash-es';
|
||||
|
||||
import { buildInitial } from './buildInitial/index.js';
|
||||
import { buildActions } from './buildActions/index.js';
|
||||
import { buildSelectors } from './buildSelectors/index.js';
|
||||
import { action } from './actions.js';
|
||||
import { buildUpreducer } from './buildUpreducer.js';
|
||||
import {
|
||||
buildMiddleware,
|
||||
augmentMiddlewareApi,
|
||||
} from './buildMiddleware/index.js';
|
||||
|
||||
/**
|
||||
* @public
|
||||
* `Updux` is a way to minimize and simplify the boilerplate associated with the
|
||||
* creation of a `Redux` store. It takes a shorthand configuration
|
||||
* object, and generates the appropriate reducer, actions, middleware, etc.
|
||||
* In true `Redux`-like fashion, upduxes can be made of sub-upduxes (`subduxes` for short) for different slices of the root state.
|
||||
*/
|
||||
export class Updux {
|
||||
/** @type { unknown } */
|
||||
#initial = {};
|
||||
#subduxes = {};
|
||||
|
||||
/** @type Record<string,Function> */
|
||||
#actions = {};
|
||||
#selectors = {};
|
||||
#mutations = {};
|
||||
#effects = [];
|
||||
#subscriptions = [];
|
||||
#splatSelector = undefined;
|
||||
#splatReaction = undefined;
|
||||
|
||||
constructor(config) {
|
||||
this.#initial = config.initial ?? {};
|
||||
this.#subduxes = config.subduxes ?? {};
|
||||
|
||||
if (config.subduxes) {
|
||||
this.#subduxes = mapValues(config.subduxes, (sub) =>
|
||||
sub instanceof Updux ? sub : new Updux(sub)
|
||||
);
|
||||
}
|
||||
|
||||
if (config.actions) {
|
||||
for (const [type, actionArg] of Object.entries(config.actions)) {
|
||||
if (typeof actionArg === 'function' && actionArg.type) {
|
||||
this.#actions[type] = actionArg;
|
||||
} else {
|
||||
this.#actions[type] = action(type, actionArg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.#selectors = config.selectors ?? {};
|
||||
|
||||
this.#mutations = config.mutations ?? {};
|
||||
|
||||
this.#splatSelector = config.splatSelector;
|
||||
|
||||
Object.keys(this.#mutations)
|
||||
.filter((action) => action !== '*')
|
||||
.filter((action) => !this.actions.hasOwnProperty(action))
|
||||
.forEach((action) => {
|
||||
throw new Error(`action '${action}' is not defined`);
|
||||
});
|
||||
|
||||
if (config.effects) {
|
||||
this.#effects = Object.entries(config.effects);
|
||||
}
|
||||
|
||||
this.#subscriptions = config.subscriptions ?? [];
|
||||
|
||||
this.#splatReaction = config.splatReaction;
|
||||
}
|
||||
|
||||
#memoInitial = moize(buildInitial);
|
||||
#memoActions = moize(buildActions);
|
||||
#memoSelectors = moize(buildSelectors);
|
||||
#memoUpreducer = moize(buildUpreducer);
|
||||
#memoMiddleware = moize(buildMiddleware);
|
||||
|
||||
get subscriptions() {
|
||||
return this.#subscriptions;
|
||||
}
|
||||
|
||||
setSplatSelector(name, f) {
|
||||
this.#splatSelector = [name, f];
|
||||
}
|
||||
|
||||
get middleware() {
|
||||
return this.#memoMiddleware(
|
||||
this.#effects,
|
||||
this.actions,
|
||||
this.selectors,
|
||||
this.#subduxes
|
||||
);
|
||||
}
|
||||
|
||||
/** @return { import('./Updux').What } */
|
||||
get initial() {
|
||||
return this.#memoInitial(this.#initial, this.#subduxes);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {Record<string,Function>}
|
||||
*/
|
||||
get actions() {
|
||||
return this.#memoActions(this.#actions, this.#subduxes);
|
||||
}
|
||||
|
||||
get selectors() {
|
||||
return this.#memoSelectors(
|
||||
this.#selectors,
|
||||
this.#splatSelector,
|
||||
this.#subduxes
|
||||
);
|
||||
}
|
||||
|
||||
get upreducer() {
|
||||
return this.#memoUpreducer(
|
||||
this.initial,
|
||||
this.#mutations,
|
||||
this.#subduxes
|
||||
);
|
||||
}
|
||||
|
||||
get reducer() {
|
||||
return (state, action) => this.upreducer(action)(state);
|
||||
}
|
||||
|
||||
addSubscription(subscription) {
|
||||
this.#subscriptions = [...this.#subscriptions, subscription];
|
||||
}
|
||||
|
||||
addAction(type, payloadFunc) {
|
||||
const theAction = action(type, payloadFunc);
|
||||
|
||||
this.#actions = { ...this.#actions, [type]: theAction };
|
||||
|
||||
return theAction;
|
||||
}
|
||||
|
||||
addSelector(name, func) {
|
||||
this.#selectors = {
|
||||
...this.#selectors,
|
||||
[name]: func,
|
||||
};
|
||||
return func;
|
||||
}
|
||||
|
||||
addMutation(name, mutation) {
|
||||
if (typeof name === 'function') name = name.type;
|
||||
|
||||
this.#mutations = {
|
||||
...this.#mutations,
|
||||
[name]: mutation,
|
||||
};
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
addEffect(action, effect) {
|
||||
this.#effects = [...this.#effects, [action, effect]];
|
||||
}
|
||||
|
||||
splatSubscriber(store, inner, splatReaction) {
|
||||
const cache = {};
|
||||
|
||||
return () => (state, previous, unsub) => {
|
||||
const cacheKeys = Object.keys(cache);
|
||||
|
||||
const newKeys = difference(Object.keys(state), cacheKeys);
|
||||
|
||||
for (const slice of newKeys) {
|
||||
let localStore = {
|
||||
...store,
|
||||
getState: () => store.getState()[slice],
|
||||
};
|
||||
|
||||
cache[slice] = [];
|
||||
|
||||
if (typeof splatReaction === 'function') {
|
||||
localStore = {
|
||||
...localStore,
|
||||
...splatReaction(localStore, slice),
|
||||
};
|
||||
}
|
||||
|
||||
const { unsub, subscriber, subscriberRaw } =
|
||||
inner.subscribeAll(localStore);
|
||||
|
||||
cache[slice].push({ unsub, subscriber, subscriberRaw });
|
||||
subscriber();
|
||||
}
|
||||
|
||||
const deletedKeys = difference(cacheKeys, Object.keys(state));
|
||||
|
||||
for (const deleted of deletedKeys) {
|
||||
for (const inner of cache[deleted]) {
|
||||
inner.subscriber();
|
||||
inner.unsub();
|
||||
}
|
||||
delete cache[deleted];
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
subscribeTo(store, subscription, setupArgs = []) {
|
||||
const localStore = augmentMiddlewareApi(
|
||||
{
|
||||
...store,
|
||||
subscribe: (subscriber) =>
|
||||
this.subscribeTo(store, () => subscriber),
|
||||
},
|
||||
this.actions,
|
||||
this.selectors
|
||||
);
|
||||
|
||||
const subscriber = subscription(localStore, ...setupArgs);
|
||||
|
||||
let previous;
|
||||
|
||||
const memoSub = () => {
|
||||
const state = store.getState();
|
||||
if (state === previous) return;
|
||||
let p = previous;
|
||||
previous = state;
|
||||
subscriber(state, p, unsub);
|
||||
};
|
||||
|
||||
let ret = store.subscribe(memoSub);
|
||||
const unsub = typeof ret === 'function' ? ret : ret.unsub;
|
||||
return {
|
||||
unsub,
|
||||
subscriber: memoSub,
|
||||
subscriberRaw: subscriber,
|
||||
};
|
||||
}
|
||||
|
||||
subscribeAll(store) {
|
||||
let results = this.#subscriptions.map((sub) =>
|
||||
this.subscribeTo(store, sub)
|
||||
);
|
||||
|
||||
for (const subdux in this.#subduxes) {
|
||||
if (subdux !== '*') {
|
||||
const localStore = {
|
||||
...store,
|
||||
getState: () => get(store.getState(), subdux),
|
||||
};
|
||||
results.push(this.#subduxes[subdux].subscribeAll(localStore));
|
||||
}
|
||||
}
|
||||
|
||||
if (this.#splatReaction) {
|
||||
results.push(
|
||||
this.subscribeTo(
|
||||
store,
|
||||
this.splatSubscriber(
|
||||
store,
|
||||
this.#subduxes['*'],
|
||||
this.#splatReaction
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
unsub: () => results.forEach(({ unsub }) => unsub()),
|
||||
subscriber: () => results.forEach(({ subscriber }) => subscriber()),
|
||||
subscriberRaw: (...args) =>
|
||||
results.forEach(({ subscriberRaw }) => subscriberRaw(...args)),
|
||||
};
|
||||
}
|
||||
|
||||
createStore(initial) {
|
||||
const store = reduxCreateStore(
|
||||
this.reducer,
|
||||
initial ?? this.initial,
|
||||
applyMiddleware(this.middleware)
|
||||
);
|
||||
|
||||
store.actions = this.actions;
|
||||
|
||||
store.selectors = this.selectors;
|
||||
|
||||
merge(
|
||||
store.getState,
|
||||
mapValues(this.selectors, (selector) => {
|
||||
return (...args) => {
|
||||
let result = selector(store.getState());
|
||||
|
||||
if (typeof result === 'function') return result(...args);
|
||||
|
||||
return result;
|
||||
};
|
||||
})
|
||||
);
|
||||
|
||||
for (const action in this.actions) {
|
||||
store.dispatch[action] = (...args) => {
|
||||
return store.dispatch(this.actions[action](...args));
|
||||
};
|
||||
}
|
||||
|
||||
this.subscribeAll(store);
|
||||
|
||||
return store;
|
||||
}
|
||||
}
|
||||
|
||||
const x = new Updux();
|
||||
x.selectors;
|
||||
</code></pre>
|
||||
</article>
|
||||
</section>
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<nav>
|
||||
<h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="Updux.html">Updux</a></li></ul>
|
||||
</nav>
|
||||
|
||||
<br class="clear">
|
||||
|
||||
<footer>
|
||||
Documentation generated by <a href="https://github.com/jsdoc/jsdoc">JSDoc 3.6.7</a> on Tue Oct 12 2021 10:16:59 GMT-0400 (Eastern Daylight Time)
|
||||
</footer>
|
||||
|
||||
<script> prettyPrint(); </script>
|
||||
<script src="scripts/linenumber.js"> </script>
|
||||
</body>
|
||||
</html>
|
Binary file not shown.
File diff suppressed because it is too large
Load Diff
After Width: | Height: | Size: 116 KiB |
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large
Load Diff
After Width: | Height: | Size: 118 KiB |
Binary file not shown.
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue