Merge branch 'definitions-ts' into going-javascript

typescript
Yanick Champoux 2021-10-13 19:37:24 -04:00
commit 8c026314f5
102 changed files with 29160 additions and 2193 deletions

12
Taskfile.yml Normal file
View File

@ -0,0 +1,12 @@
# https://taskfile.dev
version: '3'
vars:
GREETING: Hello, World!
tasks:
'test:types': tsd
docs:
cmds:
- jsdoc -c jsdoc.json -t ./node_modules/better-docs src/index.js src/Updux.js

1
docs/API/.nojekyll Normal file
View File

@ -0,0 +1 @@
TypeDoc added this file to prevent GitHub Pages from using Jekyll. You can turn off this behavior by setting the `githubPages` option to false.

View File

@ -1,228 +0,0 @@
[updux - v1.2.0](README.md) [Globals](globals.md)
# updux - v1.2.0
# 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/docs/).
## 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
}
});
```

View File

@ -0,0 +1,92 @@
:root {
--light-hl-0: #AF00DB;
--dark-hl-0: #C586C0;
--light-hl-1: #000000;
--dark-hl-1: #D4D4D4;
--light-hl-2: #001080;
--dark-hl-2: #9CDCFE;
--light-hl-3: #A31515;
--dark-hl-3: #CE9178;
--light-hl-4: #0000FF;
--dark-hl-4: #569CD6;
--light-hl-5: #0070C1;
--dark-hl-5: #4FC1FF;
--light-hl-6: #795E26;
--dark-hl-6: #DCDCAA;
--light-hl-7: #098658;
--dark-hl-7: #B5CEA8;
--light-hl-8: #008000;
--dark-hl-8: #6A9955;
--light-hl-9: #000000;
--dark-hl-9: #C8C8C8;
--light-code-background: #FFFFFF;
--dark-code-background: #1E1E1E;
}
@media (prefers-color-scheme: light) { :root {
--hl-0: var(--light-hl-0);
--hl-1: var(--light-hl-1);
--hl-2: var(--light-hl-2);
--hl-3: var(--light-hl-3);
--hl-4: var(--light-hl-4);
--hl-5: var(--light-hl-5);
--hl-6: var(--light-hl-6);
--hl-7: var(--light-hl-7);
--hl-8: var(--light-hl-8);
--hl-9: var(--light-hl-9);
--code-background: var(--light-code-background);
} }
@media (prefers-color-scheme: dark) { :root {
--hl-0: var(--dark-hl-0);
--hl-1: var(--dark-hl-1);
--hl-2: var(--dark-hl-2);
--hl-3: var(--dark-hl-3);
--hl-4: var(--dark-hl-4);
--hl-5: var(--dark-hl-5);
--hl-6: var(--dark-hl-6);
--hl-7: var(--dark-hl-7);
--hl-8: var(--dark-hl-8);
--hl-9: var(--dark-hl-9);
--code-background: var(--dark-code-background);
} }
body.light {
--hl-0: var(--light-hl-0);
--hl-1: var(--light-hl-1);
--hl-2: var(--light-hl-2);
--hl-3: var(--light-hl-3);
--hl-4: var(--light-hl-4);
--hl-5: var(--light-hl-5);
--hl-6: var(--light-hl-6);
--hl-7: var(--light-hl-7);
--hl-8: var(--light-hl-8);
--hl-9: var(--light-hl-9);
--code-background: var(--light-code-background);
}
body.dark {
--hl-0: var(--dark-hl-0);
--hl-1: var(--dark-hl-1);
--hl-2: var(--dark-hl-2);
--hl-3: var(--dark-hl-3);
--hl-4: var(--dark-hl-4);
--hl-5: var(--dark-hl-5);
--hl-6: var(--dark-hl-6);
--hl-7: var(--dark-hl-7);
--hl-8: var(--dark-hl-8);
--hl-9: var(--dark-hl-9);
--code-background: var(--dark-code-background);
}
.hl-0 { color: var(--hl-0); }
.hl-1 { color: var(--hl-1); }
.hl-2 { color: var(--hl-2); }
.hl-3 { color: var(--hl-3); }
.hl-4 { color: var(--hl-4); }
.hl-5 { color: var(--hl-5); }
.hl-6 { color: var(--hl-6); }
.hl-7 { color: var(--hl-7); }
.hl-8 { color: var(--hl-8); }
.hl-9 { color: var(--hl-9); }
pre, code { background: var(--code-background); }

1043
docs/API/assets/icons.css Normal file

File diff suppressed because it is too large Load Diff

BIN
docs/API/assets/icons.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

52
docs/API/assets/main.js Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

1384
docs/API/assets/style.css Normal file

File diff suppressed because it is too large Load Diff

BIN
docs/API/assets/widgets.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 480 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 855 B

File diff suppressed because one or more lines are too long

View File

@ -1,491 +0,0 @@
[updux - v1.2.0](../README.md) [Globals](../globals.md) [Updux](updux.md)
# Class: Updux <**S, A, X, C**>
## Type parameters
▪ **S**
▪ **A**
▪ **X**
**C**: *[UpduxConfig](../globals.md#upduxconfig)*
## Hierarchy
* **Updux**
## Index
### Constructors
* [constructor](updux.md#constructor)
### Properties
* [coduxes](updux.md#coduxes)
* [groomMutations](updux.md#groommutations)
* [subduxes](updux.md#subduxes)
### Accessors
* [_middlewareEntries](updux.md#_middlewareentries)
* [actions](updux.md#actions)
* [asDux](updux.md#asdux)
* [createStore](updux.md#createstore)
* [initial](updux.md#initial)
* [middleware](updux.md#middleware)
* [mutations](updux.md#mutations)
* [reducer](updux.md#reducer)
* [selectors](updux.md#selectors)
* [subduxUpreducer](updux.md#subduxupreducer)
* [upreducer](updux.md#upreducer)
### Methods
* [addAction](updux.md#addaction)
* [addEffect](updux.md#addeffect)
* [addMutation](updux.md#addmutation)
* [addSelector](updux.md#addselector)
## Constructors
### constructor
\+ **new Updux**(`config`: C): *[Updux](updux.md)*
**Parameters:**
Name | Type | Default | Description |
------ | ------ | ------ | ------ |
`config` | C | {} as C | an [UpduxConfig](../globals.md#upduxconfig) plain object |
**Returns:** *[Updux](updux.md)*
## Properties
### coduxes
**coduxes**: *[Dux](../globals.md#dux)[]*
___
### groomMutations
**groomMutations**: *function*
#### Type declaration:
▸ (`mutation`: [Mutation](../globals.md#mutation)S): *[Mutation](../globals.md#mutation)S*
**Parameters:**
Name | Type |
------ | ------ |
`mutation` | [Mutation](../globals.md#mutation)S |
___
### subduxes
**subduxes**: *[Dictionary](../globals.md#dictionary)[Dux](../globals.md#dux)*
## Accessors
### _middlewareEntries
**get _middlewareEntries**(): *any[]*
**Returns:** *any[]*
___
### actions
**get actions**(): *[DuxActions](../globals.md#duxactions)A, C*
Action creators for all actions defined or used in the actions, mutations, effects and subduxes
of the updux 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, that key won't be present in the produced action).
The same action creator can be included
in multiple subduxes. However, if two different creators
are included for the same action, an error will be thrown.
**`example`**
```
const actions = updux.actions;
```
**Returns:** *[DuxActions](../globals.md#duxactions)A, C*
___
### asDux
**get asDux**(): *object*
Returns a <a href="https://github.com/erikras/ducks-modular-redux">ducks</a>-like
plain object holding the reducer from the Updux object and all
its trimmings.
**`example`**
```
const {
createStore,
upreducer,
subduxes,
coduxes,
middleware,
actions,
reducer,
mutations,
initial,
selectors,
} = myUpdux.asDux;
```
**Returns:** *object*
* **actions**: = this.actions
* **coduxes**: *object[]* = this.coduxes
* **createStore**(): *function*
* (`initial?`: S, `injectEnhancer?`: Function): *StoreS & object*
* **initial**: = this.initial
* **middleware**(): *function*
* (`api`: UpduxMiddlewareAPIS, X): *function*
* (`next`: Function): *function*
* (`action`: A): *any*
* **mutations**(): *object*
* **reducer**(): *function*
* (`state`: S | undefined, `action`: [Action](../globals.md#action)): *S*
* **selectors**: = this.selectors
* **subduxes**(): *object*
* **upreducer**(): *function*
* (`action`: [Action](../globals.md#action)): *function*
* (`state`: S): *S*
___
### createStore
**get createStore**(): *function*
Returns a `createStore` function that takes two argument:
`initial` and `injectEnhancer`. `initial` is a custom
initial state for the store, and `injectEnhancer` is a function
taking in the middleware built by the updux object and allowing
you to wrap it in any enhancer you want.
**`example`**
```
const createStore = updux.createStore;
const store = createStore(initial);
```
**Returns:** *function*
▸ (`initial?`: S, `injectEnhancer?`: Function): *StoreS & object*
**Parameters:**
Name | Type |
------ | ------ |
`initial?` | S |
`injectEnhancer?` | Function |
___
### initial
**get initial**(): *AggDuxStateS, C*
**Returns:** *AggDuxStateS, C*
___
### middleware
**get middleware**(): *[UpduxMiddleware](../globals.md#upduxmiddleware)AggDuxStateS, C, [DuxSelectors](../globals.md#duxselectors)AggDuxStateS, C, X, C*
Array of middlewares aggregating all the effects defined in the
updux and its subduxes. Effects of the updux itself are
done before the subduxes effects.
Note that `getState` will always return the state of the
local updux.
**`example`**
```
const middleware = updux.middleware;
```
**Returns:** *[UpduxMiddleware](../globals.md#upduxmiddleware)AggDuxStateS, C, [DuxSelectors](../globals.md#duxselectors)AggDuxStateS, C, X, C*
___
### mutations
**get mutations**(): *[Dictionary](../globals.md#dictionary)[Mutation](../globals.md#mutation)S*
Merge of the updux and subduxes mutations. If an action triggers
mutations in both the main updux and its subduxes, the subduxes
mutations will be performed first.
**Returns:** *[Dictionary](../globals.md#dictionary)[Mutation](../globals.md#mutation)S*
___
### reducer
**get reducer**(): *function*
A Redux reducer generated using the computed initial state and
mutations.
**Returns:** *function*
▸ (`state`: S | undefined, `action`: [Action](../globals.md#action)): *S*
**Parameters:**
Name | Type |
------ | ------ |
`state` | S &#124; undefined |
`action` | [Action](../globals.md#action) |
___
### selectors
**get selectors**(): *[DuxSelectors](../globals.md#duxselectors)AggDuxStateS, C, X, C*
A dictionary of the updux's selectors. Subduxes'
selectors are included as well (with the mapping to the
sub-state already taken care of you).
**Returns:** *[DuxSelectors](../globals.md#duxselectors)AggDuxStateS, C, X, C*
___
### subduxUpreducer
**get subduxUpreducer**(): *function*
Returns the upreducer made of the merge of all sudbuxes reducers, without
the local mutations. Useful, for example, for sink mutations.
**`example`**
```
import todo from './todo'; // updux for a single todo
import Updux from 'updux';
import u from 'updeep';
const todos = new Updux({ initial: [], subduxes: { '*': todo } });
todos.addMutation(
todo.actions.done,
({todo_id},action) => u.map( u.if( u.is('id',todo_id) ), todos.subduxUpreducer(action) )
true
);
```
**Returns:** *function*
▸ (`action`: [Action](../globals.md#action)): *function*
**Parameters:**
Name | Type |
------ | ------ |
`action` | [Action](../globals.md#action) |
▸ (`state`: S): *S*
**Parameters:**
Name | Type |
------ | ------ |
`state` | S |
___
### upreducer
**get upreducer**(): *[Upreducer](../globals.md#upreducer)S*
**Returns:** *[Upreducer](../globals.md#upreducer)S*
## Methods
### addAction
**addAction**(`theaction`: string, `transform?`: any): *ActionCreatorstring, any*
Adds an action to the updux. It can take an already defined action
creator, or any arguments that can be passed to `actionCreator`.
**`example`**
```
const action = updux.addAction( name, ...creatorArgs );
const action = updux.addAction( otherActionCreator );
```
**`example`**
```
import {actionCreator, Updux} from 'updux';
const updux = new Updux();
const foo = updux.addAction('foo');
const bar = updux.addAction( 'bar', (x) => ({stuff: x+1}) );
const baz = actionCreator( 'baz' );
foo({ a: 1}); // => { type: 'foo', payload: { a: 1 } }
bar(2); // => { type: 'bar', payload: { stuff: 3 } }
baz(); // => { type: 'baz', payload: undefined }
```
**Parameters:**
Name | Type |
------ | ------ |
`theaction` | string |
`transform?` | any |
**Returns:** *ActionCreatorstring, any*
**addAction**(`theaction`: string | ActionCreatorany, `transform?`: undefined): *ActionCreatorstring, any*
**Parameters:**
Name | Type |
------ | ------ |
`theaction` | string &#124; ActionCreatorany |
`transform?` | undefined |
**Returns:** *ActionCreatorstring, any*
___
### addEffect
**addEffect**<**AC**>(`creator`: AC, `middleware`: [UpduxMiddleware](../globals.md#upduxmiddleware)AggDuxStateS, C, [DuxSelectors](../globals.md#duxselectors)AggDuxStateS, C, X, C, ReturnTypeAC, `isGenerator?`: undefined | false | true): *any*
**Type parameters:**
**AC**: *ActionCreator*
**Parameters:**
Name | Type |
------ | ------ |
`creator` | AC |
`middleware` | [UpduxMiddleware](../globals.md#upduxmiddleware)AggDuxStateS, C, [DuxSelectors](../globals.md#duxselectors)AggDuxStateS, C, X, C, ReturnTypeAC |
`isGenerator?` | undefined &#124; false &#124; true |
**Returns:** *any*
**addEffect**(`creator`: string, `middleware`: [UpduxMiddleware](../globals.md#upduxmiddleware)AggDuxStateS, C, [DuxSelectors](../globals.md#duxselectors)AggDuxStateS, C, X, C, `isGenerator?`: undefined | false | true): *any*
**Parameters:**
Name | Type |
------ | ------ |
`creator` | string |
`middleware` | [UpduxMiddleware](../globals.md#upduxmiddleware)AggDuxStateS, C, [DuxSelectors](../globals.md#duxselectors)AggDuxStateS, C, X, C |
`isGenerator?` | undefined &#124; false &#124; true |
**Returns:** *any*
___
### addMutation
**addMutation**<**A**>(`creator`: A, `mutation`: [Mutation](../globals.md#mutation)S, ActionTypeA, `isSink?`: undefined | false | true): *any*
Adds a mutation and its associated action to the updux.
**`remarks`**
If a local mutation was already associated to the action,
it will be replaced by the new one.
**`example`**
```js
updux.addMutation(
action('ADD', payload<int>() ),
inc => state => state + in
);
```
**Type parameters:**
**A**: *ActionCreator*
**Parameters:**
Name | Type | Description |
------ | ------ | ------ |
`creator` | A | - |
`mutation` | [Mutation](../globals.md#mutation)S, ActionTypeA | - |
`isSink?` | undefined &#124; false &#124; true | If `true`, disables the subduxes mutations for this action. To conditionally run the subduxes mutations, check out [subduxUpreducer](updux.md#subduxupreducer). Defaults to `false`. |
**Returns:** *any*
**addMutation**<**A**>(`creator`: string, `mutation`: [Mutation](../globals.md#mutation)S, any, `isSink?`: undefined | false | true): *any*
**Type parameters:**
**A**: *ActionCreator*
**Parameters:**
Name | Type |
------ | ------ |
`creator` | string |
`mutation` | [Mutation](../globals.md#mutation)S, any |
`isSink?` | undefined &#124; false &#124; true |
**Returns:** *any*
___
### addSelector
**addSelector**(`name`: string, `selector`: [Selector](../globals.md#selector)): *void*
**Parameters:**
Name | Type |
------ | ------ |
`name` | string |
`selector` | [Selector](../globals.md#selector) |
**Returns:** *void*

View File

@ -1,980 +0,0 @@
[updux - v1.2.0](README.md) [Globals](globals.md)
# updux - v1.2.0
## Index
### Classes
* [Updux](classes/updux.md)
### Type aliases
* [Action](globals.md#action)
* [ActionPair](globals.md#actionpair)
* [ActionPayloadGenerator](globals.md#actionpayloadgenerator)
* [ActionsOf](globals.md#actionsof)
* [CoduxesOf](globals.md#coduxesof)
* [Dictionary](globals.md#dictionary)
* [Dux](globals.md#dux)
* [DuxActions](globals.md#duxactions)
* [DuxActionsCoduxes](globals.md#duxactionscoduxes)
* [DuxActionsSubduxes](globals.md#duxactionssubduxes)
* [DuxSelectors](globals.md#duxselectors)
* [DuxState](globals.md#duxstate)
* [DuxStateCoduxes](globals.md#duxstatecoduxes)
* [DuxStateGlobSub](globals.md#duxstateglobsub)
* [DuxStateSubduxes](globals.md#duxstatesubduxes)
* [Effect](globals.md#effect)
* [GenericActions](globals.md#genericactions)
* [ItemsOf](globals.md#itemsof)
* [LocalDuxState](globals.md#localduxstate)
* [MaybePayload](globals.md#maybepayload)
* [MaybeReturnType](globals.md#maybereturntype)
* [Merge](globals.md#merge)
* [Mutation](globals.md#mutation)
* [MutationEntry](globals.md#mutationentry)
* [MwGen](globals.md#mwgen)
* [Next](globals.md#next)
* [RebaseSelector](globals.md#rebaseselector)
* [Selector](globals.md#selector)
* [SelectorsOf](globals.md#selectorsof)
* [StateOf](globals.md#stateof)
* [StoreWithDispatchActions](globals.md#storewithdispatchactions)
* [SubMutations](globals.md#submutations)
* [Submws](globals.md#submws)
* [UnionToIntersection](globals.md#uniontointersection)
* [UpduxActions](globals.md#upduxactions)
* [UpduxConfig](globals.md#upduxconfig)
* [UpduxLocalActions](globals.md#upduxlocalactions)
* [UpduxMiddleware](globals.md#upduxmiddleware)
* [Upreducer](globals.md#upreducer)
### Variables
* [subEffects](globals.md#const-subeffects)
* [updux](globals.md#const-updux)
### Functions
* [MiddlewareFor](globals.md#const-middlewarefor)
* [buildActions](globals.md#buildactions)
* [buildCreateStore](globals.md#buildcreatestore)
* [buildInitial](globals.md#buildinitial)
* [buildMiddleware](globals.md#buildmiddleware)
* [buildMutations](globals.md#buildmutations)
* [buildSelectors](globals.md#buildselectors)
* [buildUpreducer](globals.md#buildupreducer)
* [coduxes](globals.md#const-coduxes)
* [composeMutations](globals.md#const-composemutations)
* [composeMw](globals.md#const-composemw)
* [dux](globals.md#const-dux)
* [effectToMw](globals.md#const-effecttomw)
* [sliceMw](globals.md#slicemw)
* [subMiddleware](globals.md#const-submiddleware)
* [subSelectors](globals.md#subselectors)
## Type aliases
### Action
Ƭ **Action**: *object & [MaybePayload](globals.md#maybepayload)P*
___
### ActionPair
Ƭ **ActionPair**: *[string, ActionCreator]*
___
### ActionPayloadGenerator
Ƭ **ActionPayloadGenerator**: *function*
#### Type declaration:
▸ (...`args`: any[]): *any*
**Parameters:**
Name | Type |
------ | ------ |
`...args` | any[] |
___
### ActionsOf
Ƭ **ActionsOf**: *U extends Updux ? U["actions"] : object*
___
### CoduxesOf
Ƭ **CoduxesOf**: *U extends Updux<any, any, any, infer S> ? S : []*
___
### Dictionary
Ƭ **Dictionary**: *object*
#### Type declaration:
* \[ **key**: *string*\]: T
___
### Dux
Ƭ **Dux**: *object*
#### Type declaration:
* **actions**: *A*
* **coduxes**: *[Dux](globals.md#dux)[]*
* **initial**: *AggDuxStateS, C*
* **subduxes**: *[Dictionary](globals.md#dictionary)[Dux](globals.md#dux)*
___
### DuxActions
Ƭ **DuxActions**:
___
### DuxActionsCoduxes
Ƭ **DuxActionsCoduxes**: *C extends Array<infer I> ? UnionToIntersection<ActionsOf<I>> : object*
___
### DuxActionsSubduxes
Ƭ **DuxActionsSubduxes**: *C extends object ? ActionsOf<C[keyof C]> : unknown*
___
### DuxSelectors
Ƭ **DuxSelectors**: *unknown extends X ? object : X*
___
### DuxState
Ƭ **DuxState**: *D extends object ? S : unknown*
___
### DuxStateCoduxes
Ƭ **DuxStateCoduxes**: *C extends Array<infer U> ? UnionToIntersection<StateOf<U>> : unknown*
___
### DuxStateGlobSub
Ƭ **DuxStateGlobSub**: *S extends object ? StateOf<I> : unknown*
___
### DuxStateSubduxes
Ƭ **DuxStateSubduxes**: *C extends object ? object : C extends object ? object : unknown*
___
### Effect
Ƭ **Effect**: *[string, [UpduxMiddleware](globals.md#upduxmiddleware) | [MwGen](globals.md#mwgen), undefined | false | true]*
___
### GenericActions
Ƭ **GenericActions**: *[Dictionary](globals.md#dictionary)ActionCreatorstring, function*
___
### ItemsOf
Ƭ **ItemsOf**: *C extends object ? C[keyof C] : unknown*
___
### LocalDuxState
Ƭ **LocalDuxState**: *S extends never[] ? unknown[] : S*
___
### MaybePayload
Ƭ **MaybePayload**: *P extends object | string | boolean | number ? object : object*
___
### MaybeReturnType
Ƭ **MaybeReturnType**: *X extends function ? ReturnType<X> : unknown*
___
### Merge
Ƭ **Merge**: *[UnionToIntersection](globals.md#uniontointersection)T[keyof T]*
___
### Mutation
Ƭ **Mutation**: *function*
#### Type declaration:
▸ (`payload`: A["payload"], `action`: A): *function*
**Parameters:**
Name | Type |
------ | ------ |
`payload` | A["payload"] |
`action` | A |
▸ (`state`: S): *S*
**Parameters:**
Name | Type |
------ | ------ |
`state` | S |
___
### MutationEntry
Ƭ **MutationEntry**: *[ActionCreator | string, [Mutation](globals.md#mutation)any, [Action](globals.md#action)string, any, undefined | false | true]*
___
### MwGen
Ƭ **MwGen**: *function*
#### Type declaration:
▸ (): *[UpduxMiddleware](globals.md#upduxmiddleware)*
___
### Next
Ƭ **Next**: *function*
#### Type declaration:
▸ (`action`: [Action](globals.md#action)): *any*
**Parameters:**
Name | Type |
------ | ------ |
`action` | [Action](globals.md#action) |
___
### RebaseSelector
Ƭ **RebaseSelector**: *object*
#### Type declaration:
___
### Selector
Ƭ **Selector**: *function*
#### Type declaration:
▸ (`state`: S): *unknown*
**Parameters:**
Name | Type |
------ | ------ |
`state` | S |
___
### SelectorsOf
Ƭ **SelectorsOf**: *C extends object ? S : unknown*
___
### StateOf
Ƭ **StateOf**: *D extends object ? I : unknown*
___
### StoreWithDispatchActions
Ƭ **StoreWithDispatchActions**: *StoreS & object*
___
### SubMutations
Ƭ **SubMutations**: *object*
#### Type declaration:
* \[ **slice**: *string*\]: [Dictionary](globals.md#dictionary)[Mutation](globals.md#mutation)
___
### Submws
Ƭ **Submws**: *[Dictionary](globals.md#dictionary)[UpduxMiddleware](globals.md#upduxmiddleware)*
___
### UnionToIntersection
Ƭ **UnionToIntersection**: *U extends any ? function : never extends function ? I : never*
___
### UpduxActions
Ƭ **UpduxActions**: *U extends Updux ? UnionToIntersection<UpduxLocalActions<U> | ActionsOf<CoduxesOf<U>[keyof CoduxesOf<U>]>> : object*
___
### UpduxConfig
Ƭ **UpduxConfig**: *Partialobject*
Configuration object given to Updux's constructor.
#### arguments
##### initial
Default initial state of the reducer. If applicable, is merged with
the subduxes initial states, with the parent having precedence.
If not provided, defaults to an empty object.
##### actions
[Actions](/concepts/Actions) used by the updux.
```js
import { dux } from 'updux';
import { action, payload } from 'ts-action';
const bar = action('BAR', payload<int>());
const foo = action('FOO');
const myDux = dux({
actions: {
bar
},
mutations: [
[ foo, () => state => state ]
]
});
myDux.actions.foo({ x: 1, y: 2 }); // => { type: foo, x:1, y:2 }
myDux.actions.bar(2); // => { type: bar, payload: 2 }
```
New actions used directly in mutations and effects will be added to the
dux actions -- that is, they will be accessible via `dux.actions` -- but will
not appear as part of its Typescript type.
##### selectors
Dictionary of selectors for the current updux. The updux also
inherit its subduxes' selectors.
The selectors are available via the class' getter.
##### mutations
mutations: [
[ action, mutation, isSink ],
...
]
or
mutations: {
action: mutation,
...
}
List of mutations for assign to the dux. If you want Typescript goodness, you
probably want to use `addMutation()` instead.
In its generic array-of-array form,
each mutation tuple contains: the action, the mutation,
and boolean indicating if this is a sink mutation.
The action can be an action creator function or a string. If it's a string, it's considered to be the
action type and a generic `action( actionName, payload() )` creator will be
generated for it. If an action is not already defined in the `actions`
parameter, it'll be automatically added.
The pseudo-action type `*` can be used to match any action not explicitly matched by other mutations.
```js
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;
},
}
});
```
The signature of the mutations is `(payload,action) => state => newState`.
It is designed to play well with `Updeep` (and [Immer](https://immerjs.github.io/immer/docs/introduction)). This way, instead of doing
```js
mutation: {
renameTodo: newName => state => { ...state, name: newName }
}
```
we can do
```js
mutation: {
renameTodo: newName => u({ name: newName })
}
```
The final argument is the optional boolean `isSink`. If it is true, it'll
prevent subduxes' mutations on the same action. It defaults to `false`.
The object version of the argument can be used as a shortcut when all actions
are strings. In that case, `isSink` is `false` for all mutations.
##### groomMutations
Function that can be provided to alter all local mutations of the updux
(the mutations of subduxes are left untouched).
Can be used, for example, for Immer integration:
```js
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
}
});
```
Or perhaps for debugging:
```js
import Updux from 'updux';
const updux = new Updux({
initial: { counter: 0 },
groomMutations: mutation => (...args) => state => {
console.log( "got action ", args[1] );
return mutation(...args)(state);
}
});
```
##### subduxes
Object mapping slices of the state to sub-upduxes. In addition to creating
sub-reducers for those slices, it'll make the parend updux inherit all the
actions and middleware from its subduxes.
For example, if in plain Redux you would do
```js
import { combineReducers } from 'redux';
import todosReducer from './todos';
import statisticsReducer from './statistics';
const rootReducer = combineReducers({
todos: todosReducer,
stats: statisticsReducer,
});
```
then with Updux you'd do
```js
import { updux } from 'updux';
import todos from './todos';
import statistics from './statistics';
const rootUpdux = updux({
subduxes: {
todos,
statistics
}
});
```
##### effects
Array of arrays or plain object defining asynchronous actions and side-effects triggered by actions.
The effects themselves are Redux middleware, 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);
}
}
});
```
**`example`**
```
import Updux from 'updux';
import { actions, payload } from 'ts-action';
import u from 'updeep';
const todoUpdux = new Updux({
initial: {
done: false,
note: "",
},
actions: {
finish: action('FINISH', payload()),
edit: action('EDIT', payload()),
},
mutations: [
[ edit, note => u({note}) ]
],
selectors: {
getNote: state => state.note
},
groomMutations: mutation => transform(mutation),
subduxes: {
foo
},
effects: {
finish: () => next => action => {
console.log( "Woo! one more bites the dust" );
}
}
})
```
___
### UpduxLocalActions
Ƭ **UpduxLocalActions**: *S extends Updux<any, null> ? object : S extends Updux<any, infer A> ? A : object*
___
### UpduxMiddleware
Ƭ **UpduxMiddleware**: *function*
#### Type declaration:
▸ (`api`: UpduxMiddlewareAPIS, X): *function*
**Parameters:**
Name | Type |
------ | ------ |
`api` | UpduxMiddlewareAPIS, X |
▸ (`next`: Function): *function*
**Parameters:**
Name | Type |
------ | ------ |
`next` | Function |
▸ (`action`: A): *any*
**Parameters:**
Name | Type |
------ | ------ |
`action` | A |
___
### Upreducer
Ƭ **Upreducer**: *function*
#### Type declaration:
▸ (`action`: [Action](globals.md#action)): *function*
**Parameters:**
Name | Type |
------ | ------ |
`action` | [Action](globals.md#action) |
▸ (`state`: S): *S*
**Parameters:**
Name | Type |
------ | ------ |
`state` | S |
## Variables
### `Const` subEffects
**subEffects**: *[Effect](globals.md#effect)* = [ '*', subMiddleware ] as any
___
### `Const` updux
**updux**: *[Updux](classes/updux.md)unknown, null, unknown, object* = new Updux({
subduxes: {
foo: dux({ initial: "banana" })
}
})
## Functions
### `Const` MiddlewareFor
**MiddlewareFor**(`type`: any, `mw`: Middleware): *Middleware*
**Parameters:**
Name | Type |
------ | ------ |
`type` | any |
`mw` | Middleware |
**Returns:** *Middleware*
___
### buildActions
**buildActions**(`actions`: [ActionPair](globals.md#actionpair)[]): *[Dictionary](globals.md#dictionary)ActionCreatorstring, function*
**Parameters:**
Name | Type | Default |
------ | ------ | ------ |
`actions` | [ActionPair](globals.md#actionpair)[] | [] |
**Returns:** *[Dictionary](globals.md#dictionary)ActionCreatorstring, function*
___
### buildCreateStore
**buildCreateStore**<**S**, **A**>(`reducer`: ReducerS, `middleware`: Middleware, `actions`: A): *function*
**Type parameters:**
▪ **S**
▪ **A**
**Parameters:**
Name | Type | Default |
------ | ------ | ------ |
`reducer` | ReducerS | - |
`middleware` | Middleware | - |
`actions` | A | {} as A |
**Returns:** *function*
▸ (`initial?`: S, `injectEnhancer?`: Function): *StoreS & object*
**Parameters:**
Name | Type |
------ | ------ |
`initial?` | S |
`injectEnhancer?` | Function |
___
### buildInitial
**buildInitial**(`initial`: any, `coduxes`: any, `subduxes`: any): *any*
**Parameters:**
Name | Type | Default |
------ | ------ | ------ |
`initial` | any | - |
`coduxes` | any | [] |
`subduxes` | any | {} |
**Returns:** *any*
___
### buildMiddleware
**buildMiddleware**<**S**>(`local`: [UpduxMiddleware](globals.md#upduxmiddleware)[], `co`: [UpduxMiddleware](globals.md#upduxmiddleware)[], `sub`: [Submws](globals.md#submws)): *[UpduxMiddleware](globals.md#upduxmiddleware)S*
**Type parameters:**
▪ **S**
**Parameters:**
Name | Type | Default |
------ | ------ | ------ |
`local` | [UpduxMiddleware](globals.md#upduxmiddleware)[] | [] |
`co` | [UpduxMiddleware](globals.md#upduxmiddleware)[] | [] |
`sub` | [Submws](globals.md#submws) | {} |
**Returns:** *[UpduxMiddleware](globals.md#upduxmiddleware)S*
___
### buildMutations
**buildMutations**(`mutations`: [Dictionary](globals.md#dictionary)[Mutation](globals.md#mutation) | [[Mutation](globals.md#mutation), boolean | undefined], `subduxes`: object, `coduxes`: [Upreducer](globals.md#upreducer)[]): *object*
**Parameters:**
Name | Type | Default |
------ | ------ | ------ |
`mutations` | [Dictionary](globals.md#dictionary)[Mutation](globals.md#mutation) &#124; [[Mutation](globals.md#mutation), boolean &#124; undefined] | {} |
`subduxes` | object | {} |
`coduxes` | [Upreducer](globals.md#upreducer)[] | [] |
**Returns:** *object*
___
### buildSelectors
**buildSelectors**(`localSelectors`: [Dictionary](globals.md#dictionary)[Selector](globals.md#selector), `coduxes`: [Dictionary](globals.md#dictionary)[Selector](globals.md#selector)[], `subduxes`: [Dictionary](globals.md#dictionary)[Selector](globals.md#selector)): *object*
**Parameters:**
Name | Type | Default |
------ | ------ | ------ |
`localSelectors` | [Dictionary](globals.md#dictionary)[Selector](globals.md#selector) | {} |
`coduxes` | [Dictionary](globals.md#dictionary)[Selector](globals.md#selector)[] | [] |
`subduxes` | [Dictionary](globals.md#dictionary)[Selector](globals.md#selector) | {} |
**Returns:** *object*
___
### buildUpreducer
**buildUpreducer**<**S**>(`initial`: S, `mutations`: [Dictionary](globals.md#dictionary)[Mutation](globals.md#mutation)S): *[Upreducer](globals.md#upreducer)S*
**Type parameters:**
▪ **S**
**Parameters:**
Name | Type |
------ | ------ |
`initial` | S |
`mutations` | [Dictionary](globals.md#dictionary)[Mutation](globals.md#mutation)S |
**Returns:** *[Upreducer](globals.md#upreducer)S*
___
### `Const` coduxes
**coduxes**<**C**, **U**>(...`coduxes`: U): *object*
**Type parameters:**
**C**: *[Dux](globals.md#dux)*
**U**: *[C]*
**Parameters:**
Name | Type |
------ | ------ |
`...coduxes` | U |
**Returns:** *object*
* **coduxes**: *U*
___
### `Const` composeMutations
**composeMutations**(`mutations`: [Mutation](globals.md#mutation)[]): *function | (Anonymous function)*
**Parameters:**
Name | Type |
------ | ------ |
`mutations` | [Mutation](globals.md#mutation)[] |
**Returns:** *function | (Anonymous function)*
___
### `Const` composeMw
**composeMw**(`mws`: [UpduxMiddleware](globals.md#upduxmiddleware)[]): *(Anonymous function)*
**Parameters:**
Name | Type |
------ | ------ |
`mws` | [UpduxMiddleware](globals.md#upduxmiddleware)[] |
**Returns:** *(Anonymous function)*
___
### `Const` dux
**dux**<**S**, **A**, **X**, **C**>(`config`: C): *object*
**Type parameters:**
▪ **S**
▪ **A**
▪ **X**
**C**: *[UpduxConfig](globals.md#upduxconfig)*
**Parameters:**
Name | Type |
------ | ------ |
`config` | C |
**Returns:** *object*
* **actions**: = this.actions
* **coduxes**: *object[]* = this.coduxes
* **createStore**(): *function*
* (`initial?`: S, `injectEnhancer?`: Function): *StoreS & object*
* **initial**: = this.initial
* **middleware**(): *function*
* (`api`: UpduxMiddlewareAPIS, X): *function*
* (`next`: Function): *function*
* (`action`: A): *any*
* **mutations**(): *object*
* **reducer**(): *function*
* (`state`: S | undefined, `action`: [Action](globals.md#action)): *S*
* **selectors**: = this.selectors
* **subduxes**(): *object*
* **upreducer**(): *function*
* (`action`: [Action](globals.md#action)): *function*
* (`state`: S): *S*
___
### `Const` effectToMw
**effectToMw**(`effect`: [Effect](globals.md#effect), `actions`: [Dictionary](globals.md#dictionary)ActionCreator, `selectors`: [Dictionary](globals.md#dictionary)[Selector](globals.md#selector)): *subMiddleware | augmented*
**Parameters:**
Name | Type |
------ | ------ |
`effect` | [Effect](globals.md#effect) |
`actions` | [Dictionary](globals.md#dictionary)ActionCreator |
`selectors` | [Dictionary](globals.md#dictionary)[Selector](globals.md#selector) |
**Returns:** *subMiddleware | augmented*
___
### sliceMw
**sliceMw**(`slice`: string, `mw`: [UpduxMiddleware](globals.md#upduxmiddleware)): *[UpduxMiddleware](globals.md#upduxmiddleware)*
**Parameters:**
Name | Type |
------ | ------ |
`slice` | string |
`mw` | [UpduxMiddleware](globals.md#upduxmiddleware) |
**Returns:** *[UpduxMiddleware](globals.md#upduxmiddleware)*
___
### `Const` subMiddleware
**subMiddleware**(): *(Anonymous function)*
**Returns:** *(Anonymous function)*
___
### subSelectors
**subSelectors**(`__namedParameters`: [string, Function]): *[string, [Selector](globals.md#selector)][]*
**Parameters:**
Name | Type |
------ | ------ |
`__namedParameters` | [string, Function] |
**Returns:** *[string, [Selector](globals.md#selector)][]*

92
docs/API/index.html Normal file
View File

@ -0,0 +1,92 @@
<!DOCTYPE html><html class="default no-js"><head><meta charSet="utf-8"/><meta http-equiv="X-UA-Compatible" content="IE=edge"/><title>Updux</title><meta name="description" content="Documentation for Updux"/><meta name="viewport" content="width=device-width, initial-scale=1"/><link rel="stylesheet" href="assets/style.css"/><link rel="stylesheet" href="assets/highlight.css"/><script async src="assets/search.js" id="search-script"></script></head><body><header><div class="tsd-page-toolbar"><div class="container"><div class="table-wrap"><div class="table-cell" id="tsd-search" data-base="."><div class="field"><label for="tsd-search-field" class="tsd-widget search no-caption">Search</label><input type="text" id="tsd-search-field"/></div><ul class="results"><li class="state loading">Preparing search index...</li><li class="state failure">The search index is not available</li></ul><a href="index.html" class="title">Updux</a></div><div class="table-cell" id="tsd-widgets"><div id="tsd-filter"><a href="#" class="tsd-widget options no-caption" data-toggle="options">Options</a><div class="tsd-filter-group"><div class="tsd-select" id="tsd-filter-visibility"><span class="tsd-select-label">All</span><ul class="tsd-select-list"><li data-value="public">Public</li><li data-value="protected">Public/Protected</li><li data-value="private" class="selected">All</li></ul></div> <input type="checkbox" id="tsd-filter-inherited" checked/><label class="tsd-widget" for="tsd-filter-inherited">Inherited</label></div></div><a href="#" class="tsd-widget menu no-caption" data-toggle="menu">Menu</a></div></div></div></div><div class="tsd-page-title"><div class="container"><h1>Updux</h1></div></div></header><div class="container container-main"><div class="row"><div class="col-8 col-content"><div class="tsd-panel tsd-typography">
<a href="#what39s-updux" id="what39s-updux" style="color: inherit; text-decoration: none;">
<h1>What&#39;s Updux?</h1>
</a>
<p>So, I&#39;m a fan of <a href="https://redux.js.org">Redux</a>. Two days ago I discovered
<a href="https://rematch.github.io/rematch">rematch</a> alonside a few other frameworks built atop Redux.</p>
<p>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!</p>
<p>But it also enforces a flat hierarchy of reducers -- where
is the fun in that? And I&#39;m also having a strong love for
<a href="https://github.com/substantial/updeep">Updeep</a>, so I want reducer state updates to leverage the heck out of it.</p>
<p>All that to say, say hello to <code>Updux</code>. Heavily inspired by <code>rematch</code>, but twisted
to work with <code>updeep</code> and to fit my peculiar needs. It offers features such as</p>
<ul>
<li>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.</li>
<li>Automatically gather all actions used by the updux&#39;s effects and mutations,
and makes then accessible as attributes to the <code>dispatch</code> object of the
store.</li>
<li>Mutations have a signature that is friendly to Updux and Immer.</li>
<li>Also, the mutation signature auto-unwrap the payload of the actions for you.</li>
<li>TypeScript types.</li>
</ul>
<p>Fair warning: this package is still very new, probably very buggy,
definitively very badly documented, and very subject to changes. Caveat
Maxima Emptor.</p>
<a href="#synopsis" id="synopsis" style="color: inherit; text-decoration: none;">
<h1>Synopsis</h1>
</a>
<pre><code><span class="hl-0">import</span><span class="hl-1"> </span><span class="hl-2">updux</span><span class="hl-1"> </span><span class="hl-0">from</span><span class="hl-1"> </span><span class="hl-3">&#39;updux&#39;</span><span class="hl-1">;</span><br/><br/><span class="hl-0">import</span><span class="hl-1"> </span><span class="hl-2">otherUpdux</span><span class="hl-1"> </span><span class="hl-0">from</span><span class="hl-1"> </span><span class="hl-3">&#39;./otherUpdux&#39;</span><span class="hl-1">;</span><br/><br/><span class="hl-4">const</span><span class="hl-1"> {</span><br/><span class="hl-1"> </span><span class="hl-5">initial</span><span class="hl-1">,</span><br/><span class="hl-1"> </span><span class="hl-5">reducer</span><span class="hl-1">,</span><br/><span class="hl-1"> </span><span class="hl-5">actions</span><span class="hl-1">,</span><br/><span class="hl-1"> </span><span class="hl-5">middleware</span><span class="hl-1">,</span><br/><span class="hl-1"> </span><span class="hl-5">createStore</span><span class="hl-1">,</span><br/><span class="hl-1">} = </span><span class="hl-4">new</span><span class="hl-1"> </span><span class="hl-6">Updux</span><span class="hl-1">({</span><br/><span class="hl-1"> </span><span class="hl-2">initial:</span><span class="hl-1"> {</span><br/><span class="hl-1"> </span><span class="hl-2">counter:</span><span class="hl-1"> </span><span class="hl-7">0</span><span class="hl-1">,</span><br/><span class="hl-1"> },</span><br/><span class="hl-1"> </span><span class="hl-2">subduxes:</span><span class="hl-1"> {</span><br/><span class="hl-1"> </span><span class="hl-2">otherUpdux</span><span class="hl-1">,</span><br/><span class="hl-1"> },</span><br/><span class="hl-1"> </span><span class="hl-2">mutations:</span><span class="hl-1"> {</span><br/><span class="hl-1"> </span><span class="hl-6">inc</span><span class="hl-2">:</span><span class="hl-1"> ( </span><span class="hl-2">increment</span><span class="hl-1"> = </span><span class="hl-7">1</span><span class="hl-1"> ) </span><span class="hl-4">=&gt;</span><span class="hl-1"> </span><span class="hl-6">u</span><span class="hl-1">({</span><span class="hl-6">counter</span><span class="hl-2">:</span><span class="hl-1"> </span><span class="hl-2">s</span><span class="hl-1"> </span><span class="hl-4">=&gt;</span><span class="hl-1"> </span><span class="hl-2">s</span><span class="hl-1"> + </span><span class="hl-2">increment</span><span class="hl-1"> })</span><br/><span class="hl-1"> },</span><br/><span class="hl-1"> </span><span class="hl-2">effects:</span><span class="hl-1"> {</span><br/><span class="hl-1"> </span><span class="hl-3">&#39;*&#39;</span><span class="hl-2"> =&gt; api =&gt; next =&gt; action =&gt; {</span><br/><span class="hl-2"> console.log( </span><span class="hl-3">&quot;hey, look, an action zoomed by!&quot;</span><span class="hl-1">, action );</span><br/><span class="hl-1"> </span><span class="hl-6">next</span><span class="hl-1">(</span><span class="hl-2">action</span><span class="hl-1">);</span><br/><span class="hl-1"> };</span><br/><span class="hl-1"> },</span><br/><span class="hl-1"> </span><span class="hl-2">actions</span><span class="hl-1">: {</span><br/><span class="hl-1"> </span><span class="hl-6">customAction</span><span class="hl-2">:</span><span class="hl-1"> ( </span><span class="hl-2">someArg</span><span class="hl-1"> ) </span><span class="hl-4">=&gt;</span><span class="hl-1"> ({</span><br/><span class="hl-1"> </span><span class="hl-2">type:</span><span class="hl-1"> </span><span class="hl-3">&quot;custom&quot;</span><span class="hl-1">,</span><br/><span class="hl-1"> </span><span class="hl-2">payload:</span><span class="hl-1"> { </span><span class="hl-2">someProp:</span><span class="hl-1"> </span><span class="hl-2">someArg</span><span class="hl-1"> }</span><br/><span class="hl-1"> }),</span><br/><span class="hl-1"> },</span><br/><br/><span class="hl-1">});</span><br/><br/><span class="hl-4">const</span><span class="hl-1"> </span><span class="hl-5">store</span><span class="hl-1"> = </span><span class="hl-6">createStore</span><span class="hl-1">();</span><br/><br/><span class="hl-2">store</span><span class="hl-1">.</span><span class="hl-2">dispatch</span><span class="hl-1">.</span><span class="hl-6">inc</span><span class="hl-1">(</span><span class="hl-7">3</span><span class="hl-1">);</span>
</code></pre>
<a href="#description" id="description" style="color: inherit; text-decoration: none;">
<h1>Description</h1>
</a>
<p>Full documentation can be <a href="https://yanick.github.io/updux/">found here</a>.
Right now the best way to understand the whole thing is to go
through the <a href="https://yanick.github.io/updux/#/tutorial">tutorial</a></p>
<a href="#exporting-upduxes" id="exporting-upduxes" style="color: inherit; text-decoration: none;">
<h2>Exporting upduxes</h2>
</a>
<p>If you are creating upduxes that will be used as subduxes
by other upduxes, or as
<a href="https://github.com/erikras/ducks-modular-redux">ducks</a>-like containers, I
recommend that you export the Updux instance as the default export:</p>
<pre><code><span class="hl-0">import</span><span class="hl-1"> </span><span class="hl-2">Updux</span><span class="hl-1"> </span><span class="hl-0">from</span><span class="hl-1"> </span><span class="hl-3">&#39;updux&#39;</span><span class="hl-1">;</span><br/><br/><span class="hl-4">const</span><span class="hl-1"> </span><span class="hl-5">updux</span><span class="hl-1"> = </span><span class="hl-4">new</span><span class="hl-1"> </span><span class="hl-6">Updux</span><span class="hl-1">({ ... });</span><br/><br/><span class="hl-0">export</span><span class="hl-1"> </span><span class="hl-0">default</span><span class="hl-1"> </span><span class="hl-2">updux</span><span class="hl-1">;</span>
</code></pre>
<p>Then you can use them as subduxes like this:</p>
<pre><code><span class="hl-0">import</span><span class="hl-1"> </span><span class="hl-2">Updux</span><span class="hl-1"> </span><span class="hl-0">from</span><span class="hl-1"> </span><span class="hl-3">&#39;updux&#39;</span><span class="hl-1">;</span><br/><span class="hl-0">import</span><span class="hl-1"> </span><span class="hl-2">foo</span><span class="hl-1"> </span><span class="hl-0">from</span><span class="hl-1"> </span><span class="hl-3">&#39;./foo&#39;</span><span class="hl-1">; </span><span class="hl-8">// foo is an Updux</span><br/><span class="hl-0">import</span><span class="hl-1"> </span><span class="hl-2">bar</span><span class="hl-1"> </span><span class="hl-0">from</span><span class="hl-1"> </span><span class="hl-3">&#39;./bar&#39;</span><span class="hl-1">; </span><span class="hl-8">// bar is an Updux as well</span><br/><br/><span class="hl-4">const</span><span class="hl-1"> </span><span class="hl-5">updux</span><span class="hl-1"> = </span><span class="hl-4">new</span><span class="hl-1"> </span><span class="hl-6">Updux</span><span class="hl-1">({</span><br/><span class="hl-1"> </span><span class="hl-2">subduxes:</span><span class="hl-1"> {</span><br/><span class="hl-1"> </span><span class="hl-2">foo</span><span class="hl-1">, </span><span class="hl-2">bar</span><br/><span class="hl-1"> }</span><br/><span class="hl-1">});</span>
</code></pre>
<p>Or if you want to use it:</p>
<pre><code><span class="hl-0">import</span><span class="hl-1"> </span><span class="hl-2">updux</span><span class="hl-1"> </span><span class="hl-0">from</span><span class="hl-1"> </span><span class="hl-3">&#39;./myUpdux&#39;</span><span class="hl-1">;</span><br/><br/><span class="hl-4">const</span><span class="hl-1"> {</span><br/><span class="hl-1"> </span><span class="hl-5">reducer</span><span class="hl-1">,</span><br/><span class="hl-1"> </span><span class="hl-2">actions</span><span class="hl-1">: { </span><span class="hl-5">doTheThing</span><span class="hl-1"> },</span><br/><span class="hl-1"> </span><span class="hl-5">createStore</span><span class="hl-1">,</span><br/><span class="hl-1"> </span><span class="hl-5">middleware</span><span class="hl-1">,</span><br/><span class="hl-1">} = </span><span class="hl-2">updux</span><span class="hl-1">;</span>
</code></pre>
<a href="#mapping-a-mutation-to-all-values-of-a-state" id="mapping-a-mutation-to-all-values-of-a-state" style="color: inherit; text-decoration: none;">
<h2>Mapping a mutation to all values of a state</h2>
</a>
<p>Say you have a <code>todos</code> state that is an array of <code>todo</code> sub-states. It&#39;s easy
enough to have the main reducer maps away all items to the sub-reducer:</p>
<pre><code><span class="hl-4">const</span><span class="hl-1"> </span><span class="hl-5">todo</span><span class="hl-1"> = </span><span class="hl-4">new</span><span class="hl-1"> </span><span class="hl-6">Updux</span><span class="hl-1">({</span><br/><span class="hl-1"> </span><span class="hl-2">mutations:</span><span class="hl-1"> {</span><br/><span class="hl-1"> </span><span class="hl-6">review</span><span class="hl-2">:</span><span class="hl-1"> () </span><span class="hl-4">=&gt;</span><span class="hl-1"> </span><span class="hl-6">u</span><span class="hl-1">({ </span><span class="hl-2">reviewed:</span><span class="hl-1"> </span><span class="hl-4">true</span><span class="hl-1">}),</span><br/><span class="hl-1"> </span><span class="hl-6">done</span><span class="hl-2">:</span><span class="hl-1"> () </span><span class="hl-4">=&gt;</span><span class="hl-1"> </span><span class="hl-6">u</span><span class="hl-1">({</span><span class="hl-2">done:</span><span class="hl-1"> </span><span class="hl-4">true</span><span class="hl-1">}),</span><br/><span class="hl-1"> },</span><br/><span class="hl-1">});</span><br/><br/><span class="hl-4">const</span><span class="hl-1"> </span><span class="hl-5">todos</span><span class="hl-1"> = </span><span class="hl-4">new</span><span class="hl-1"> </span><span class="hl-6">Updux</span><span class="hl-1">({ </span><span class="hl-2">initial:</span><span class="hl-1"> [] });</span><br/><br/><span class="hl-2">todos</span><span class="hl-1">.</span><span class="hl-6">addMutation</span><span class="hl-1">(</span><br/><span class="hl-1"> </span><span class="hl-2">todo</span><span class="hl-1">.</span><span class="hl-2">actions</span><span class="hl-1">.</span><span class="hl-2">review</span><span class="hl-1">,</span><br/><span class="hl-1"> (</span><span class="hl-2">_</span><span class="hl-1">,</span><span class="hl-2">action</span><span class="hl-1">) </span><span class="hl-4">=&gt;</span><span class="hl-1"> </span><span class="hl-2">state</span><span class="hl-1"> </span><span class="hl-4">=&gt;</span><span class="hl-1"> </span><span class="hl-2">state</span><span class="hl-1">.</span><span class="hl-6">map</span><span class="hl-1">( </span><span class="hl-2">todo</span><span class="hl-1">.</span><span class="hl-6">upreducer</span><span class="hl-1">(</span><span class="hl-2">action</span><span class="hl-1">) )</span><br/><span class="hl-1">);</span><br/><span class="hl-2">todos</span><span class="hl-1">.</span><span class="hl-6">addMutation</span><span class="hl-1">(</span><br/><span class="hl-1"> </span><span class="hl-2">todo</span><span class="hl-1">.</span><span class="hl-2">actions</span><span class="hl-1">.</span><span class="hl-2">done</span><span class="hl-1">,</span><br/><span class="hl-1"> (</span><span class="hl-2">id</span><span class="hl-1">,</span><span class="hl-2">action</span><span class="hl-1">) </span><span class="hl-4">=&gt;</span><span class="hl-1"> </span><span class="hl-2">u</span><span class="hl-1">.</span><span class="hl-6">map</span><span class="hl-1">(</span><span class="hl-2">u</span><span class="hl-1">.</span><span class="hl-6">if</span><span class="hl-1">(</span><span class="hl-2">u</span><span class="hl-1">.</span><span class="hl-6">is</span><span class="hl-1">(</span><span class="hl-3">&#39;id&#39;</span><span class="hl-1">,</span><span class="hl-2">id</span><span class="hl-1">), </span><span class="hl-2">todo</span><span class="hl-1">.</span><span class="hl-6">upreducer</span><span class="hl-1">(</span><span class="hl-2">action</span><span class="hl-1">))),</span><br/><span class="hl-1">);</span><br/>
</code></pre>
<p>But <code>updeep</code> can iterate through all the items of an array (or the values of
an object) via the special key <code>*</code>. So the todos updux above could also be
written:</p>
<pre><code><span class="hl-4">const</span><span class="hl-1"> </span><span class="hl-5">todo</span><span class="hl-1"> = </span><span class="hl-4">new</span><span class="hl-1"> </span><span class="hl-6">Updux</span><span class="hl-1">({</span><br/><span class="hl-1"> </span><span class="hl-2">mutations:</span><span class="hl-1"> {</span><br/><span class="hl-1"> </span><span class="hl-6">review</span><span class="hl-2">:</span><span class="hl-1"> () </span><span class="hl-4">=&gt;</span><span class="hl-1"> </span><span class="hl-6">u</span><span class="hl-1">({ </span><span class="hl-2">reviewed:</span><span class="hl-1"> </span><span class="hl-4">true</span><span class="hl-1">}),</span><br/><span class="hl-1"> </span><span class="hl-6">done</span><span class="hl-2">:</span><span class="hl-1"> () </span><span class="hl-4">=&gt;</span><span class="hl-1"> </span><span class="hl-6">u</span><span class="hl-1">({</span><span class="hl-2">done:</span><span class="hl-1"> </span><span class="hl-4">true</span><span class="hl-1">}),</span><br/><span class="hl-1"> },</span><br/><span class="hl-1">});</span><br/><br/><span class="hl-4">const</span><span class="hl-1"> </span><span class="hl-5">todos</span><span class="hl-1"> = </span><span class="hl-4">new</span><span class="hl-1"> </span><span class="hl-6">Updux</span><span class="hl-1">({</span><br/><span class="hl-1"> </span><span class="hl-2">subduxes:</span><span class="hl-1"> { </span><span class="hl-3">&#39;*&#39;</span><span class="hl-2">:</span><span class="hl-1"> </span><span class="hl-2">todo</span><span class="hl-1"> },</span><br/><span class="hl-1">});</span><br/><br/><span class="hl-2">todos</span><span class="hl-1">.</span><span class="hl-6">addMutation</span><span class="hl-1">(</span><br/><span class="hl-1"> </span><span class="hl-2">todo</span><span class="hl-1">.</span><span class="hl-2">actions</span><span class="hl-1">.</span><span class="hl-2">done</span><span class="hl-1">,</span><br/><span class="hl-1"> (</span><span class="hl-2">id</span><span class="hl-1">,</span><span class="hl-2">action</span><span class="hl-1">) </span><span class="hl-4">=&gt;</span><span class="hl-1"> </span><span class="hl-2">u</span><span class="hl-1">.</span><span class="hl-6">map</span><span class="hl-1">(</span><span class="hl-2">u</span><span class="hl-1">.</span><span class="hl-6">if</span><span class="hl-1">(</span><span class="hl-2">u</span><span class="hl-1">.</span><span class="hl-6">is</span><span class="hl-1">(</span><span class="hl-3">&#39;id&#39;</span><span class="hl-1">,</span><span class="hl-2">id</span><span class="hl-1">), </span><span class="hl-2">todo</span><span class="hl-1">.</span><span class="hl-6">upreducer</span><span class="hl-1">(</span><span class="hl-2">action</span><span class="hl-1">))),</span><br/><span class="hl-1"> </span><span class="hl-4">true</span><br/><span class="hl-1">);</span>
</code></pre>
<p>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&#39;t being
overridden by a sink mutation will trickle down automatically.</p>
<a href="#usage-with-immer" id="usage-with-immer" style="color: inherit; text-decoration: none;">
<h2>Usage with Immer</h2>
</a>
<p>While Updux was created with Updeep in mind, it also plays very
well with <a href="https://immerjs.github.io/immer/docs/introduction">Immer</a>.</p>
<p>For example, taking this basic updux:</p>
<pre><code><span class="hl-0">import</span><span class="hl-1"> </span><span class="hl-2">Updux</span><span class="hl-1"> </span><span class="hl-0">from</span><span class="hl-1"> </span><span class="hl-3">&#39;updux&#39;</span><span class="hl-1">;</span><br/><br/><span class="hl-4">const</span><span class="hl-1"> </span><span class="hl-5">updux</span><span class="hl-1"> = </span><span class="hl-4">new</span><span class="hl-1"> </span><span class="hl-6">Updux</span><span class="hl-1">({</span><br/><span class="hl-1"> </span><span class="hl-2">initial:</span><span class="hl-1"> { </span><span class="hl-2">counter:</span><span class="hl-1"> </span><span class="hl-7">0</span><span class="hl-1"> },</span><br/><span class="hl-1"> </span><span class="hl-2">mutations:</span><span class="hl-1"> {</span><br/><span class="hl-1"> </span><span class="hl-6">add</span><span class="hl-2">:</span><span class="hl-1"> (</span><span class="hl-2">inc</span><span class="hl-1">=</span><span class="hl-7">1</span><span class="hl-1">) </span><span class="hl-4">=&gt;</span><span class="hl-1"> </span><span class="hl-2">state</span><span class="hl-1"> </span><span class="hl-4">=&gt;</span><span class="hl-1"> { </span><span class="hl-9">counter</span><span class="hl-1">: </span><span class="hl-2">counter</span><span class="hl-1"> + </span><span class="hl-2">inc</span><span class="hl-1"> }</span><br/><span class="hl-1"> }</span><br/><span class="hl-1">});</span><br/>
</code></pre>
<p>Converting it to Immer would look like:</p>
<pre><code><span class="hl-0">import</span><span class="hl-1"> </span><span class="hl-2">Updux</span><span class="hl-1"> </span><span class="hl-0">from</span><span class="hl-1"> </span><span class="hl-3">&#39;updux&#39;</span><span class="hl-1">;</span><br/><span class="hl-0">import</span><span class="hl-1"> { </span><span class="hl-2">produce</span><span class="hl-1"> } </span><span class="hl-0">from</span><span class="hl-1"> </span><span class="hl-3">&#39;Immer&#39;</span><span class="hl-1">;</span><br/><br/><span class="hl-4">const</span><span class="hl-1"> </span><span class="hl-5">updux</span><span class="hl-1"> = </span><span class="hl-4">new</span><span class="hl-1"> </span><span class="hl-6">Updux</span><span class="hl-1">({</span><br/><span class="hl-1"> </span><span class="hl-2">initial:</span><span class="hl-1"> { </span><span class="hl-2">counter:</span><span class="hl-1"> </span><span class="hl-7">0</span><span class="hl-1"> },</span><br/><span class="hl-1"> </span><span class="hl-2">mutations:</span><span class="hl-1"> {</span><br/><span class="hl-1"> </span><span class="hl-6">add</span><span class="hl-2">:</span><span class="hl-1"> (</span><span class="hl-2">inc</span><span class="hl-1">=</span><span class="hl-7">1</span><span class="hl-1">) </span><span class="hl-4">=&gt;</span><span class="hl-1"> </span><span class="hl-6">produce</span><span class="hl-1">( </span><span class="hl-2">draft</span><span class="hl-1"> </span><span class="hl-4">=&gt;</span><span class="hl-1"> </span><span class="hl-2">draft</span><span class="hl-1">.</span><span class="hl-2">counter</span><span class="hl-1"> += </span><span class="hl-2">inc</span><span class="hl-1"> ) }</span><br/><span class="hl-1"> }</span><br/><span class="hl-1">});</span><br/>
</code></pre>
<p>But since typing <code>produce</code> over and over is no fun, <code>groomMutations</code>
can be used to wrap all mutations with it:</p>
<pre><code><span class="hl-0">import</span><span class="hl-1"> </span><span class="hl-2">Updux</span><span class="hl-1"> </span><span class="hl-0">from</span><span class="hl-1"> </span><span class="hl-3">&#39;updux&#39;</span><span class="hl-1">;</span><br/><span class="hl-0">import</span><span class="hl-1"> { </span><span class="hl-2">produce</span><span class="hl-1"> } </span><span class="hl-0">from</span><span class="hl-1"> </span><span class="hl-3">&#39;Immer&#39;</span><span class="hl-1">;</span><br/><br/><span class="hl-4">const</span><span class="hl-1"> </span><span class="hl-5">updux</span><span class="hl-1"> = </span><span class="hl-4">new</span><span class="hl-1"> </span><span class="hl-6">Updux</span><span class="hl-1">({</span><br/><span class="hl-1"> </span><span class="hl-2">initial:</span><span class="hl-1"> { </span><span class="hl-2">counter:</span><span class="hl-1"> </span><span class="hl-7">0</span><span class="hl-1"> },</span><br/><span class="hl-1"> </span><span class="hl-6">groomMutations</span><span class="hl-2">:</span><span class="hl-1"> </span><span class="hl-2">mutation</span><span class="hl-1"> </span><span class="hl-4">=&gt;</span><span class="hl-1"> (...</span><span class="hl-2">args</span><span class="hl-1">) </span><span class="hl-4">=&gt;</span><span class="hl-1"> </span><span class="hl-6">produce</span><span class="hl-1">( </span><span class="hl-6">mutation</span><span class="hl-1">(...</span><span class="hl-2">args</span><span class="hl-1">) ),</span><br/><span class="hl-1"> </span><span class="hl-2">mutations:</span><span class="hl-1"> {</span><br/><span class="hl-1"> </span><span class="hl-6">add</span><span class="hl-2">:</span><span class="hl-1"> (</span><span class="hl-2">inc</span><span class="hl-1">=</span><span class="hl-7">1</span><span class="hl-1">) </span><span class="hl-4">=&gt;</span><span class="hl-1"> </span><span class="hl-2">draft</span><span class="hl-1"> </span><span class="hl-4">=&gt;</span><span class="hl-1"> </span><span class="hl-2">draft</span><span class="hl-1">.</span><span class="hl-2">counter</span><span class="hl-1"> += </span><span class="hl-2">inc</span><br/><span class="hl-1"> }</span><br/><span class="hl-1">});</span><br/>
</code></pre>
</div></div><div class="col-4 col-menu menu-sticky-wrap menu-highlight"><nav class="tsd-navigation primary"><ul><li class="current"><a href="modules.html">Exports</a></li><li class=" tsd-kind-namespace"><a href="modules/_updux_.html">&quot;updux&quot;</a></li></ul></nav><nav class="tsd-navigation secondary menu-sticky"><ul><li class="tsd-kind-type-alias"><a href="modules.html#ActionGenerator" class="tsd-kind-icon">Action<wbr/>Generator</a></li><li class="tsd-kind-type-alias tsd-has-type-parameter"><a href="modules.html#Dict" class="tsd-kind-icon">Dict</a></li><li class="tsd-kind-type-alias tsd-has-type-parameter"><a href="modules.html#Mutation" class="tsd-kind-icon">Mutation</a></li></ul></nav></div></div></div><footer class="with-border-bottom"><div class="container"><h2>Settings</h2><p>Theme <select id="theme"><option value="os">OS</option><option value="light">Light</option><option value="dark">Dark</option></select></p></div></footer><div class="container tsd-generator"><p>Generated using <a href="https://typedoc.org/" target="_blank">TypeDoc</a></p></div><div class="overlay"></div><script src="assets/main.js"></script></body></html>

View File

@ -0,0 +1,25 @@
<!DOCTYPE html><html class="default no-js"><head><meta charSet="utf-8"/><meta http-equiv="X-UA-Compatible" content="IE=edge"/><title>UpduxConfig | Updux</title><meta name="description" content="Documentation for Updux"/><meta name="viewport" content="width=device-width, initial-scale=1"/><link rel="stylesheet" href="../assets/style.css"/><link rel="stylesheet" href="../assets/highlight.css"/><script async src="../assets/search.js" id="search-script"></script></head><body><header><div class="tsd-page-toolbar"><div class="container"><div class="table-wrap"><div class="table-cell" id="tsd-search" data-base=".."><div class="field"><label for="tsd-search-field" class="tsd-widget search no-caption">Search</label><input type="text" id="tsd-search-field"/></div><ul class="results"><li class="state loading">Preparing search index...</li><li class="state failure">The search index is not available</li></ul><a href="../index.html" class="title">Updux</a></div><div class="table-cell" id="tsd-widgets"><div id="tsd-filter"><a href="#" class="tsd-widget options no-caption" data-toggle="options">Options</a><div class="tsd-filter-group"><div class="tsd-select" id="tsd-filter-visibility"><span class="tsd-select-label">All</span><ul class="tsd-select-list"><li data-value="public">Public</li><li data-value="protected">Public/Protected</li><li data-value="private" class="selected">All</li></ul></div> <input type="checkbox" id="tsd-filter-inherited" checked/><label class="tsd-widget" for="tsd-filter-inherited">Inherited</label></div></div><a href="#" class="tsd-widget menu no-caption" data-toggle="menu">Menu</a></div></div></div></div><div class="tsd-page-title"><div class="container"><ul class="tsd-breadcrumb"><li><a href="../modules.html">Updux</a></li><li><a href="../modules/_updux_.html">&quot;updux&quot;</a></li><li><a href="_updux_.UpduxConfig.html">UpduxConfig</a></li></ul><h1>Interface UpduxConfig&lt;TState&gt;</h1></div></div></header><div class="container container-main"><div class="row"><div class="col-8 col-content"><section class="tsd-panel tsd-comment"><div class="tsd-comment tsd-typography"><div class="lead">
<p>Configuration object typically passed to the constructor of the class Updux.</p>
</div></div></section><section class="tsd-panel tsd-type-parameters"><h3>Type parameters</h3><ul class="tsd-type-parameters"><li><h4>TState = <span class="tsd-signature-type">unknown</span></h4></li></ul></section><section class="tsd-panel tsd-hierarchy"><h3>Hierarchy</h3><ul class="tsd-hierarchy"><li><span class="target">UpduxConfig</span></li></ul></section><section class="tsd-panel-group tsd-index-group"><h2>Index</h2><section class="tsd-panel tsd-index-panel"><div class="tsd-index-content"><section class="tsd-index-section "><h3>Properties</h3><ul class="tsd-index-list"><li class="tsd-kind-property tsd-parent-kind-interface"><a href="_updux_.UpduxConfig.html#actions" class="tsd-kind-icon">actions</a></li><li class="tsd-kind-property tsd-parent-kind-interface"><a href="_updux_.UpduxConfig.html#effects" class="tsd-kind-icon">effects</a></li><li class="tsd-kind-property tsd-parent-kind-interface"><a href="_updux_.UpduxConfig.html#initial" class="tsd-kind-icon">initial</a></li><li class="tsd-kind-property tsd-parent-kind-interface"><a href="_updux_.UpduxConfig.html#mappedReaction" class="tsd-kind-icon">mapped<wbr/>Reaction</a></li><li class="tsd-kind-property tsd-parent-kind-interface"><a href="_updux_.UpduxConfig.html#mappedSelectors" class="tsd-kind-icon">mapped<wbr/>Selectors</a></li><li class="tsd-kind-property tsd-parent-kind-interface"><a href="_updux_.UpduxConfig.html#mutations" class="tsd-kind-icon">mutations</a></li><li class="tsd-kind-property tsd-parent-kind-interface"><a href="_updux_.UpduxConfig.html#reactions" class="tsd-kind-icon">reactions</a></li><li class="tsd-kind-property tsd-parent-kind-interface"><a href="_updux_.UpduxConfig.html#selectors" class="tsd-kind-icon">selectors</a></li><li class="tsd-kind-property tsd-parent-kind-interface"><a href="_updux_.UpduxConfig.html#subduxes" class="tsd-kind-icon">subduxes</a></li></ul></section></div></section></section><section class="tsd-panel-group tsd-member-group "><h2>Properties</h2><section class="tsd-panel tsd-member tsd-kind-property tsd-parent-kind-interface"><a name="actions" class="tsd-anchor"></a><h3><span class="tsd-flag ts-flagOptional">Optional</span> actions</h3><div class="tsd-signature tsd-kind-icon">actions<span class="tsd-signature-symbol">?:</span> <span class="tsd-signature-type">Record</span><span class="tsd-signature-symbol">&lt;</span><span class="tsd-signature-type">string</span><span class="tsd-signature-symbol">, </span><span class="tsd-signature-type">any</span><span class="tsd-signature-symbol">&gt;</span></div><aside class="tsd-sources"></aside><div class="tsd-comment tsd-typography"><div class="lead">
<p>Local actions.</p>
</div></div></section><section class="tsd-panel tsd-member tsd-kind-property tsd-parent-kind-interface"><a name="effects" class="tsd-anchor"></a><h3><span class="tsd-flag ts-flagOptional">Optional</span> effects</h3><div class="tsd-signature tsd-kind-icon">effects<span class="tsd-signature-symbol">?:</span> <span class="tsd-signature-type">Record</span><span class="tsd-signature-symbol">&lt;</span><span class="tsd-signature-type">string</span><span class="tsd-signature-symbol">, </span><span class="tsd-signature-type">Function</span><span class="tsd-signature-symbol">&gt;</span></div><aside class="tsd-sources"></aside><div class="tsd-comment tsd-typography"><div class="lead">
<p>Local effects.</p>
</div></div></section><section class="tsd-panel tsd-member tsd-kind-property tsd-parent-kind-interface"><a name="initial" class="tsd-anchor"></a><h3><span class="tsd-flag ts-flagOptional">Optional</span> initial</h3><div class="tsd-signature tsd-kind-icon">initial<span class="tsd-signature-symbol">?:</span> <span class="tsd-signature-type" data-tsd-kind="Type parameter">TState</span></div><aside class="tsd-sources"></aside><div class="tsd-comment tsd-typography"><div class="lead">
<p>Local initial state.</p>
</div><dl class="tsd-comment-tags"><dt>default</dt><dd><p>{}</p>
</dd></dl></div></section><section class="tsd-panel tsd-member tsd-kind-property tsd-parent-kind-interface"><a name="mappedReaction" class="tsd-anchor"></a><h3><span class="tsd-flag ts-flagOptional">Optional</span> mapped<wbr/>Reaction</h3><div class="tsd-signature tsd-kind-icon">mapped<wbr/>Reaction<span class="tsd-signature-symbol">?:</span> <span class="tsd-signature-type">boolean</span><span class="tsd-signature-symbol"> | </span><span class="tsd-signature-type">Function</span></div><aside class="tsd-sources"></aside><div class="tsd-comment tsd-typography"><div class="lead">
<p>If true, enables mapped reactions. Additionally, it can be
a reaction function, which will treated as a regular
reaction for the mapped dux.</p>
</div></div></section><section class="tsd-panel tsd-member tsd-kind-property tsd-parent-kind-interface"><a name="mappedSelectors" class="tsd-anchor"></a><h3><span class="tsd-flag ts-flagOptional">Optional</span> mapped<wbr/>Selectors</h3><div class="tsd-signature tsd-kind-icon">mapped<wbr/>Selectors<span class="tsd-signature-symbol">?:</span> <span class="tsd-signature-type">Record</span><span class="tsd-signature-symbol">&lt;</span><span class="tsd-signature-type">string</span><span class="tsd-signature-symbol">, </span><span class="tsd-signature-type">Function</span><span class="tsd-signature-symbol">&gt;</span></div><aside class="tsd-sources"></aside><div class="tsd-comment tsd-typography"><div class="lead">
<p> Selectors to apply to the mapped subduxes. Only
applicable if the dux is a mapping dux.</p>
</div></div></section><section class="tsd-panel tsd-member tsd-kind-property tsd-parent-kind-interface"><a name="mutations" class="tsd-anchor"></a><h3><span class="tsd-flag ts-flagOptional">Optional</span> mutations</h3><div class="tsd-signature tsd-kind-icon">mutations<span class="tsd-signature-symbol">?:</span> <span class="tsd-signature-type">Record</span><span class="tsd-signature-symbol">&lt;</span><span class="tsd-signature-type">string</span><span class="tsd-signature-symbol">, </span><span class="tsd-signature-type">Function</span><span class="tsd-signature-symbol">&gt;</span></div><aside class="tsd-sources"></aside><div class="tsd-comment tsd-typography"><div class="lead">
<p>Local mutations</p>
</div></div></section><section class="tsd-panel tsd-member tsd-kind-property tsd-parent-kind-interface"><a name="reactions" class="tsd-anchor"></a><h3><span class="tsd-flag ts-flagOptional">Optional</span> reactions</h3><div class="tsd-signature tsd-kind-icon">reactions<span class="tsd-signature-symbol">?:</span> <span class="tsd-signature-type">Record</span><span class="tsd-signature-symbol">&lt;</span><span class="tsd-signature-type">string</span><span class="tsd-signature-symbol">, </span><span class="tsd-signature-type">Function</span><span class="tsd-signature-symbol">&gt;</span></div><aside class="tsd-sources"></aside><div class="tsd-comment tsd-typography"><div class="lead">
<p>Local reactions.</p>
</div></div></section><section class="tsd-panel tsd-member tsd-kind-property tsd-parent-kind-interface"><a name="selectors" class="tsd-anchor"></a><h3><span class="tsd-flag ts-flagOptional">Optional</span> selectors</h3><div class="tsd-signature tsd-kind-icon">selectors<span class="tsd-signature-symbol">?:</span> <span class="tsd-signature-type">Record</span><span class="tsd-signature-symbol">&lt;</span><span class="tsd-signature-type">string</span><span class="tsd-signature-symbol">, </span><span class="tsd-signature-type">Function</span><span class="tsd-signature-symbol">&gt;</span></div><aside class="tsd-sources"></aside><div class="tsd-comment tsd-typography"><div class="lead">
<p>Local selectors.</p>
</div></div></section><section class="tsd-panel tsd-member tsd-kind-property tsd-parent-kind-interface"><a name="subduxes" class="tsd-anchor"></a><h3><span class="tsd-flag ts-flagOptional">Optional</span> subduxes</h3><div class="tsd-signature tsd-kind-icon">subduxes<span class="tsd-signature-symbol">?:</span> <a href="../modules.html#Dict" class="tsd-signature-type" data-tsd-kind="Type alias">Dict</a><span class="tsd-signature-symbol">&lt;</span><a href="../classes/_updux_.Updux.html" class="tsd-signature-type" data-tsd-kind="Class">Updux</a><span class="tsd-signature-symbol">&lt;</span><span class="tsd-signature-type">unknown</span><span class="tsd-signature-symbol">&gt;</span><span class="tsd-signature-symbol"> | </span><a href="_updux_.UpduxConfig.html" class="tsd-signature-type" data-tsd-kind="Interface">UpduxConfig</a><span class="tsd-signature-symbol">&lt;</span><span class="tsd-signature-type">unknown</span><span class="tsd-signature-symbol">&gt;</span><span class="tsd-signature-symbol">&gt;</span></div><aside class="tsd-sources"></aside><div class="tsd-comment tsd-typography"><div class="lead">
<p>Subduxes to be merged to this dux.</p>
</div></div></section></section></div><div class="col-4 col-menu menu-sticky-wrap menu-highlight"><nav class="tsd-navigation primary"><ul><li class=""><a href="../modules.html">Exports</a></li><li class="current tsd-kind-namespace"><a href="../modules/_updux_.html">&quot;updux&quot;</a></li></ul></nav><nav class="tsd-navigation secondary menu-sticky"><ul><li class="current tsd-kind-interface tsd-parent-kind-namespace tsd-has-type-parameter"><a href="_updux_.UpduxConfig.html" class="tsd-kind-icon">Updux<wbr/>Config</a><ul><li class="tsd-kind-property tsd-parent-kind-interface"><a href="_updux_.UpduxConfig.html#actions" class="tsd-kind-icon">actions</a></li><li class="tsd-kind-property tsd-parent-kind-interface"><a href="_updux_.UpduxConfig.html#effects" class="tsd-kind-icon">effects</a></li><li class="tsd-kind-property tsd-parent-kind-interface"><a href="_updux_.UpduxConfig.html#initial" class="tsd-kind-icon">initial</a></li><li class="tsd-kind-property tsd-parent-kind-interface"><a href="_updux_.UpduxConfig.html#mappedReaction" class="tsd-kind-icon">mapped<wbr/>Reaction</a></li><li class="tsd-kind-property tsd-parent-kind-interface"><a href="_updux_.UpduxConfig.html#mappedSelectors" class="tsd-kind-icon">mapped<wbr/>Selectors</a></li><li class="tsd-kind-property tsd-parent-kind-interface"><a href="_updux_.UpduxConfig.html#mutations" class="tsd-kind-icon">mutations</a></li><li class="tsd-kind-property tsd-parent-kind-interface"><a href="_updux_.UpduxConfig.html#reactions" class="tsd-kind-icon">reactions</a></li><li class="tsd-kind-property tsd-parent-kind-interface"><a href="_updux_.UpduxConfig.html#selectors" class="tsd-kind-icon">selectors</a></li><li class="tsd-kind-property tsd-parent-kind-interface"><a href="_updux_.UpduxConfig.html#subduxes" class="tsd-kind-icon">subduxes</a></li></ul></li></ul></nav></div></div></div><footer class="with-border-bottom"><div class="container"><h2>Legend</h2><div class="tsd-legend-group"><ul class="tsd-legend"><li class="tsd-kind-property tsd-parent-kind-interface"><span class="tsd-kind-icon">Property</span></li></ul></div><h2>Settings</h2><p>Theme <select id="theme"><option value="os">OS</option><option value="light">Light</option><option value="dark">Dark</option></select></p></div></footer><div class="container tsd-generator"><p>Generated using <a href="https://typedoc.org/" target="_blank">TypeDoc</a></p></div><div class="overlay"></div><script src="../assets/main.js"></script></body></html>

1
docs/API/modules.html Normal file

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,3 @@
<!DOCTYPE html><html class="default no-js"><head><meta charSet="utf-8"/><meta http-equiv="X-UA-Compatible" content="IE=edge"/><title>&quot;updux&quot; | Updux</title><meta name="description" content="Documentation for Updux"/><meta name="viewport" content="width=device-width, initial-scale=1"/><link rel="stylesheet" href="../assets/style.css"/><link rel="stylesheet" href="../assets/highlight.css"/><script async src="../assets/search.js" id="search-script"></script></head><body><header><div class="tsd-page-toolbar"><div class="container"><div class="table-wrap"><div class="table-cell" id="tsd-search" data-base=".."><div class="field"><label for="tsd-search-field" class="tsd-widget search no-caption">Search</label><input type="text" id="tsd-search-field"/></div><ul class="results"><li class="state loading">Preparing search index...</li><li class="state failure">The search index is not available</li></ul><a href="../index.html" class="title">Updux</a></div><div class="table-cell" id="tsd-widgets"><div id="tsd-filter"><a href="#" class="tsd-widget options no-caption" data-toggle="options">Options</a><div class="tsd-filter-group"><div class="tsd-select" id="tsd-filter-visibility"><span class="tsd-select-label">All</span><ul class="tsd-select-list"><li data-value="public">Public</li><li data-value="protected">Public/Protected</li><li data-value="private" class="selected">All</li></ul></div> <input type="checkbox" id="tsd-filter-inherited" checked/><label class="tsd-widget" for="tsd-filter-inherited">Inherited</label></div></div><a href="#" class="tsd-widget menu no-caption" data-toggle="menu">Menu</a></div></div></div></div><div class="tsd-page-title"><div class="container"><ul class="tsd-breadcrumb"><li><a href="../modules.html">Updux</a></li><li><a href="_updux_.html">&quot;updux&quot;</a></li></ul><h1>Namespace &quot;updux&quot;</h1></div></div></header><div class="container container-main"><div class="row"><div class="col-8 col-content"><section class="tsd-panel-group tsd-index-group"><h2>Index</h2><section class="tsd-panel tsd-index-panel"><div class="tsd-index-content"><section class="tsd-index-section "><h3>Classes</h3><ul class="tsd-index-list"><li class="tsd-kind-class tsd-parent-kind-namespace tsd-has-type-parameter"><a href="../classes/_updux_.Updux.html" class="tsd-kind-icon">Updux</a></li></ul></section><section class="tsd-index-section "><h3>Interfaces</h3><ul class="tsd-index-list"><li class="tsd-kind-interface tsd-parent-kind-namespace tsd-has-type-parameter"><a href="../interfaces/_updux_.UpduxConfig.html" class="tsd-kind-icon">Updux<wbr/>Config</a></li></ul></section><section class="tsd-index-section "><h3>Functions</h3><ul class="tsd-index-list"><li class="tsd-kind-function tsd-parent-kind-namespace"><a href="_updux_.html#action" class="tsd-kind-icon">action</a></li></ul></section></div></section></section><section class="tsd-panel-group tsd-member-group "><h2>Functions</h2><section class="tsd-panel tsd-member tsd-kind-function tsd-parent-kind-namespace"><a name="action" class="tsd-anchor"></a><h3>action</h3><ul class="tsd-signatures tsd-kind-function tsd-parent-kind-namespace"><li class="tsd-signature tsd-kind-icon">action<span class="tsd-signature-symbol">(</span>type<span class="tsd-signature-symbol">: </span><span class="tsd-signature-type">string</span>, payloadFunction<span class="tsd-signature-symbol">?: </span><span class="tsd-signature-type">Function</span><span class="tsd-signature-symbol">)</span><span class="tsd-signature-symbol">: </span><a href="../modules.html#ActionGenerator" class="tsd-signature-type" data-tsd-kind="Type alias">ActionGenerator</a></li></ul><ul class="tsd-descriptions"><li class="tsd-description"><aside class="tsd-sources"></aside><div class="tsd-comment tsd-typography"><div class="lead">
<p>Creates an action generator.</p>
</div></div><h4 class="tsd-parameters-title">Parameters</h4><ul class="tsd-parameters"><li><h5>type: <span class="tsd-signature-type">string</span></h5></li><li><h5><span class="tsd-flag ts-flagOptional">Optional</span> payloadFunction: <span class="tsd-signature-type">Function</span></h5></li></ul><h4 class="tsd-returns-title">Returns <a href="../modules.html#ActionGenerator" class="tsd-signature-type" data-tsd-kind="Type alias">ActionGenerator</a></h4></li></ul></section></section></div><div class="col-4 col-menu menu-sticky-wrap menu-highlight"><nav class="tsd-navigation primary"><ul><li class=""><a href="../modules.html">Exports</a></li><li class="current tsd-kind-namespace"><a href="_updux_.html">&quot;updux&quot;</a></li></ul></nav><nav class="tsd-navigation secondary menu-sticky"><ul><li class="tsd-kind-class tsd-parent-kind-namespace tsd-has-type-parameter"><a href="../classes/_updux_.Updux.html" class="tsd-kind-icon">Updux</a></li><li class="tsd-kind-interface tsd-parent-kind-namespace tsd-has-type-parameter"><a href="../interfaces/_updux_.UpduxConfig.html" class="tsd-kind-icon">Updux<wbr/>Config</a></li><li class="tsd-kind-function tsd-parent-kind-namespace"><a href="_updux_.html#action" class="tsd-kind-icon">action</a></li></ul></nav></div></div></div><footer class="with-border-bottom"><div class="container"><h2>Settings</h2><p>Theme <select id="theme"><option value="os">OS</option><option value="light">Light</option><option value="dark">Dark</option></select></p></div></footer><div class="container tsd-generator"><p>Generated using <a href="https://typedoc.org/" target="_blank">TypeDoc</a></p></div><div class="overlay"></div><script src="../assets/main.js"></script></body></html>

View File

@ -1,23 +1,21 @@
# What's Updux?
## What's Updux?
So, I'm a fan of [Redux](https://redux.js.org).
As I was looking into tools to help cut on its boilerplate,
I came across [rematch](https://rematch.github.io/rematch).
It has some pretty darn good ideas.
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.
is the fun in that?
I'm also having a strong love for
[Updeep](https://github.com/substantial/updeep), so I wanted a framework where
I could use it to define reducer state updates.
Hence: `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
* Mimic the way VueX has mutations (per-action reducer logic) and
effects (middleware reacting to actions that can be asynchronous and/or
have side-effects), so all things pertaining to a store are defined
in the space place.
@ -26,9 +24,7 @@ to work with `updeep` and to fit my peculiar needs. It offers features such as
store.
* Mutations have a signature that is friendly to Updux and Immer.
* Mutations auto-unwrapping the payload of actions for you.
* TypeScript types.
* Leverage [ts-action](https://www.npmjs.com/package/ts-action) for action
creation.
* TypeScript support.
**Fair warning**: this package is still very new, likely to go through
big changes before I find the perfect balance between ease of use and sanity.
@ -36,70 +32,36 @@ Caveat Emptor.
# Synopsis
```
import Updux from 'updux';
import { action, payload } from 'ts-action';
```js
import { Updux } from 'updux';
import u from 'updeep';
import add from 'lodash/fp/add.js';
import otherDux from './otherUpdux';
const inc = action( 'INC', payload<int>() );
const updux = new Updux({
const dux = new Updux({
initial: {
counter: 0,
},
actions: {
inc
inc: null
},
subduxes: {
otherDux,
}
});
updux.addMutation( inc, increment => u({counter: s => s + increment }));
dux.setMutation('inc', (increment) => u({ counter: add(increment) }));
updux.addEffect( '*', api => next => action => {
dux.addEffect( '*', api => next => action => {
console.log( "hey, look, an action zoomed by!", action );
next(action);
} );
const myDux = updux.asDux;
const store = dux.createStore();
const store = myDux.createStore();
store.dispatch.inc(1);
store.dispatch( myDux.actions.inc(3) );
console.log( store.getState().counter ); // prints 1
```
# Description
The formal documentation of the class Updux and its associated functions and
types can be found over [here](https://yanick.github.io/updux/docs/classes/updux.html).
## 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 "compiled" (as in, no more editable and with all its properties resolved) output of the Updux instance via its `asDux()` getter:
```
import Updux from 'updux';
const updux = new Updux({ ... });
export default updux.asDux;
```
Then you can use them as subduxes like this:
```
import Updux from 'updux';
import foo from './foo'; // foo is a dux
import bar from './bar'; // bar is a dux as well
const updux = new Updux({
subduxes: {
foo, bar
}
});
```

406
docs/Updux.html Normal file
View File

@ -0,0 +1,406 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title> Updux</title>
<script src="https://cdn.jsdelivr.net/gh/google/code-prettify@master/loader/run_prettify.js"></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
<script src="./build/entry.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<!--[if lt IE 9]>
<script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
<link href="https://fonts.googleapis.com/css?family=Roboto:100,400,700|Inconsolata,700" rel="stylesheet">
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.6.3/css/all.css" integrity="sha384-UHRtZLI+pbxtHCWp1t77Bi1L4ZtiqrqD80Kn4Z8NTSRyMA2Fd33n5dQ8lWUE00s/" crossorigin="anonymous">
<link type="text/css" rel="stylesheet" href="https://jmblog.github.io/color-themes-for-google-code-prettify/themes/tomorrow-night.min.css">
<link type="text/css" rel="stylesheet" href="styles/app.min.css">
<link type="text/css" rel="stylesheet" href="styles/iframe.css">
<link type="text/css" rel="stylesheet" href="">
<script async defer src="https://buttons.github.io/buttons.js"></script>
</head>
<body class="layout small-header">
<div id="stickyNavbarOverlay"></div>
<div class="top-nav">
<div class="inner">
<a id="hamburger" role="button" class="navbar-burger" aria-label="menu" aria-expanded="false">
<span aria-hidden="true"></span>
<span aria-hidden="true"></span>
<span aria-hidden="true"></span>
</a>
<div class="logo">
</div>
<div class="menu">
<div class="navigation">
<a
href="index.html"
class="link"
>
API Documentation
</a>
</div>
</div>
</div>
</div>
<div id="main">
<div
class="sidebar "
id="sidebarNav"
>
<nav>
<h2><a href="index.html">Documentation</a></h2><div class="category"><h3>Modules</h3><ul><li><a href="module-updux.html">updux</a></li></ul><h3>Classes</h3><ul><li><a href="Updux.html">Updux</a></li></ul></div>
</nav>
</div>
<div class="core" id="main-content-wrapper">
<div class="content">
<header class="page-title">
<p>Class</p>
<h1>Updux</h1>
</header>
<section>
<header>
<h2><span class="attribs"><span class="type-signature"></span></span>Updux<span class="signature">()</span><span class="type-signature"></span></h2>
<div class="class-description"><p><code>Updux</code> is a way to minimize and simplify the boilerplate associated with the
creation of a <code>Redux</code> store. It takes a shorthand configuration
object, and generates the appropriate reducer, actions, middleware, etc.
In true <code>Redux</code>-like fashion, upduxes can be made of sub-upduxes (<code>subduxes</code> for short) for different slices of the root state.</p></div>
</header>
<article>
<div class="container-overview">
<div class='vertical-section'>
<div class="members">
<div class="member">
<div class=name>
<span class="tag">Constructor</span>
</div>
<h4 class="name" id="Updux">
<a class="href-link" href="#Updux">#</a>
<span class="code-name">
new Updux<span class="signature">()</span><span class="type-signature"></span>
</span>
</h4>
<dl class="details">
<p class="tag-source">
<a href="Updux.js.html" class="button">View Source</a>
<span>
<a href="Updux.js.html">Updux.js</a>, <a href="Updux.js.html#line23">line 23</a>
</span>
</p>
</dl>
</div>
</div>
</div>
</div>
<h3 class="subsection-title">Classes</h3>
<dl>
<dt><a href="Updux.html">Updux</a></dt>
<dd></dd>
</dl>
<div class='vertical-section'>
<h1>Members</h1>
<div class="members">
<div class="member">
<h4 class="name" id="actions">
<a class="href-link" href="#actions">#</a>
<span class="code-name">
actions
</span>
</h4>
<dl class="details">
<p class="tag-source">
<a href="Updux.js.html" class="button">View Source</a>
<span>
<a href="Updux.js.html">Updux.js</a>, <a href="Updux.js.html#line110">line 110</a>
</span>
</p>
</dl>
</div>
<div class="member">
<span class="method-parameter is-pulled-right">
<label>Type:</label>
<span class="param-type">unknown</span>
</span>
<h4 class="name" id="initial">
<a class="href-link" href="#initial">#</a>
<span class="code-name">
initial
</span>
</h4>
<dl class="details">
<p class="tag-source">
<a href="Updux.js.html" class="button">View Source</a>
<span>
<a href="Updux.js.html">Updux.js</a>, <a href="Updux.js.html#line103">line 103</a>
</span>
</p>
</dl>
</div>
</div>
</div>
</article>
</section>
</div>
<footer class="footer">
<div class="content has-text-centered">
<p>Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 3.6.7</a></p>
<p class="sidebar-created-by">
<a href="https://github.com/SoftwareBrothers/better-docs" target="_blank">BetterDocs theme</a> provided with <i class="fas fa-heart"></i> by
<a href="http://softwarebrothers.co" target="_blank">SoftwareBrothers - JavaScript Development Agency</a>
</p>
</div>
</footer>
</div>
<div id="side-nav" class="side-nav">
</div>
</div>
<script src="scripts/app.min.js"></script>
<script>PR.prettyPrint();</script>
<script src="scripts/linenumber.js"> </script>
</body>
</html>

432
docs/Updux.js.html Normal file
View File

@ -0,0 +1,432 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title> Updux.js</title>
<script src="https://cdn.jsdelivr.net/gh/google/code-prettify@master/loader/run_prettify.js"></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
<script src="./build/entry.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<!--[if lt IE 9]>
<script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
<link href="https://fonts.googleapis.com/css?family=Roboto:100,400,700|Inconsolata,700" rel="stylesheet">
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.6.3/css/all.css" integrity="sha384-UHRtZLI+pbxtHCWp1t77Bi1L4ZtiqrqD80Kn4Z8NTSRyMA2Fd33n5dQ8lWUE00s/" crossorigin="anonymous">
<link type="text/css" rel="stylesheet" href="https://jmblog.github.io/color-themes-for-google-code-prettify/themes/tomorrow-night.min.css">
<link type="text/css" rel="stylesheet" href="styles/app.min.css">
<link type="text/css" rel="stylesheet" href="styles/iframe.css">
<link type="text/css" rel="stylesheet" href="">
<script async defer src="https://buttons.github.io/buttons.js"></script>
</head>
<body class="layout small-header">
<div id="stickyNavbarOverlay"></div>
<div class="top-nav">
<div class="inner">
<a id="hamburger" role="button" class="navbar-burger" aria-label="menu" aria-expanded="false">
<span aria-hidden="true"></span>
<span aria-hidden="true"></span>
<span aria-hidden="true"></span>
</a>
<div class="logo">
</div>
<div class="menu">
<div class="navigation">
<a
href="index.html"
class="link"
>
API Documentation
</a>
</div>
</div>
</div>
</div>
<div id="main">
<div
class="sidebar "
id="sidebarNav"
>
<nav>
<h2><a href="index.html">Documentation</a></h2><div class="category"><h3>Modules</h3><ul><li><a href="module-updux.html">updux</a></li></ul><h3>Classes</h3><ul><li><a href="Updux.html">Updux</a></li></ul></div>
</nav>
</div>
<div class="core" id="main-content-wrapper">
<div class="content">
<header class="page-title">
<p>Source</p>
<h1>Updux.js</h1>
</header>
<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';
/**
* `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&lt;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' &amp;&amp; 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
);
}
/** @member { unknown } */
get initial() {
return this.#memoInitial(this.#initial, this.#subduxes);
}
/**
* @return {Record&lt;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>
<footer class="footer">
<div class="content has-text-centered">
<p>Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 3.6.7</a></p>
<p class="sidebar-created-by">
<a href="https://github.com/SoftwareBrothers/better-docs" target="_blank">BetterDocs theme</a> provided with <i class="fas fa-heart"></i> by
<a href="http://softwarebrothers.co" target="_blank">SoftwareBrothers - JavaScript Development Agency</a>
</p>
</div>
</footer>
</div>
<div id="side-nav" class="side-nav">
</div>
</div>
<script src="scripts/app.min.js"></script>
<script>PR.prettyPrint();</script>
<script src="scripts/linenumber.js"> </script>
</body>
</html>

View File

@ -1,4 +1,4 @@
- [README](/README.md)
- [Intro](/README.md)
- [Tutorial](/tutorial.md)
- [Concepts](/concepts.md)
- [Recipes](/recipes.md)

View File

@ -1,12 +1,32 @@
# Updux concepts
## actions
## Actions and action generators
Updux internally uses the package `ts-action` to create action creator
functions. Even if you don't use Typescript, I recommend that you use it,
as it does what it does very well. But if you don't want to, no big deal.
Updux will recognize a function as an action creator if it has a `type`
property. So a homegrown creator could be as simple as:
Updux assumes actions following the format
```js
{
type: 'theTypeName',
payload: {
...
}
}
```
The important part is having action parameters in the `payload` property. The
actions can also have other properties, like `meta`. They are mostly ignored
by Updux.
Updux provides an action generator, but you can roll your own if you do wish.
All it needs to do to be recognized as an action generator by Updux is
1. To be a function returning an action (a plain object with at least a
`type` property).
2. The function also needs to have a `type` property itself.
For example, the following function would work:
```js
function action(type) {
@ -14,7 +34,36 @@ function action(type) {
}
```
## effects
### Mutations
The transformations typically
done by a Redux's reducer are called 'mutations' in Updux. A mutation is a
function with the following signature:
```
( payload, action ) => state => {
// ... stuff done here
return new_state;
}
```
The inversion and chaining of parameters from the usual Redux reducer's
signature is there to work with updeep's curried nature. The expansion of
the usual `action` into `(payload, action)` is present because in most cases
`payload` is what we're interested in. So why not make it easily available?
### Leftover mutation
A mutation associated to the special action `+` will match any action that haven't been
explicitly dealt with with any other defined mutation.
```
todosUpdux.addMutation( '+', (payload,action) => state => {
console.log("hey, action has no mutation! ", action.type);
});
```
## Effects
Updux effects are redux middlewares. I kept that format, and the
use of `next` mostly because I wanted to give myself a way to alter
@ -26,3 +75,37 @@ An effect has the signature
```js
const effect = ({ getState, dispatch }) => next => action => { ... }
```
## Selectors
Selectors are used to get data derived from the store's state.
The signature of a selector function is either `(state) => result`
or `(state) => (...args) => result`. The selector instances attached
to a store's `getState` already do the first call on `state`, so you don't have to.
```js
const dux = new Updux({
initial: {
foo: 1,
},
selectors: {
getFoo: ({foo}) => foo,
getFooPlus: ({foo}) => increment => foo + increment,
}
});
console.log(dux.selectors.getFoo({foo: 2})); // prints 2
console.log(dux.selectors.getFooPlus({foo: 2})(3)); // prints 5
const store = dux.createStore();
console.log(store.selectors.getFoo({foo: 2})); // prints 2
console.log(store.selectors.getFooPlus({foo: 2})(3)); // prints 5
console.log(store.getState.getFoo()); // prints 2
console.log(store.getState.getFooPlus(3)); // prints 5
```

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.

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 120 KiB

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 114 KiB

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 120 KiB

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 117 KiB

Binary file not shown.

132
docs/index.js.html Normal file
View File

@ -0,0 +1,132 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title> index.js</title>
<script src="https://cdn.jsdelivr.net/gh/google/code-prettify@master/loader/run_prettify.js"></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
<script src="./build/entry.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<!--[if lt IE 9]>
<script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
<link href="https://fonts.googleapis.com/css?family=Roboto:100,400,700|Inconsolata,700" rel="stylesheet">
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.6.3/css/all.css" integrity="sha384-UHRtZLI+pbxtHCWp1t77Bi1L4ZtiqrqD80Kn4Z8NTSRyMA2Fd33n5dQ8lWUE00s/" crossorigin="anonymous">
<link type="text/css" rel="stylesheet" href="https://jmblog.github.io/color-themes-for-google-code-prettify/themes/tomorrow-night.min.css">
<link type="text/css" rel="stylesheet" href="styles/app.min.css">
<link type="text/css" rel="stylesheet" href="styles/iframe.css">
<link type="text/css" rel="stylesheet" href="">
<script async defer src="https://buttons.github.io/buttons.js"></script>
</head>
<body class="layout small-header">
<div id="stickyNavbarOverlay"></div>
<div class="top-nav">
<div class="inner">
<a id="hamburger" role="button" class="navbar-burger" aria-label="menu" aria-expanded="false">
<span aria-hidden="true"></span>
<span aria-hidden="true"></span>
<span aria-hidden="true"></span>
</a>
<div class="logo">
</div>
<div class="menu">
<div class="navigation">
<a
href="index.html"
class="link"
>
API Documentation
</a>
</div>
</div>
</div>
</div>
<div id="main">
<div
class="sidebar "
id="sidebarNav"
>
<nav>
<h2><a href="index.html">Documentation</a></h2><div class="category"><h3>Modules</h3><ul><li><a href="module-updux.html">updux</a></li></ul><h3>Classes</h3><ul><li><a href="Updux.html">Updux</a></li></ul></div>
</nav>
</div>
<div class="core" id="main-content-wrapper">
<div class="content">
<header class="page-title">
<p>Source</p>
<h1>index.js</h1>
</header>
<section>
<article>
<pre class="prettyprint source linenums"><code>import Updux from './Updux.js';
export {
/**
* The {@link Updux} class
*/
Updux
};
/**
* `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.
* @module updux
*/
</code></pre>
</article>
</section>
</div>
<footer class="footer">
<div class="content has-text-centered">
<p>Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 3.6.7</a></p>
<p class="sidebar-created-by">
<a href="https://github.com/SoftwareBrothers/better-docs" target="_blank">BetterDocs theme</a> provided with <i class="fas fa-heart"></i> by
<a href="http://softwarebrothers.co" target="_blank">SoftwareBrothers - JavaScript Development Agency</a>
</p>
</div>
</footer>
</div>
<div id="side-nav" class="side-nav">
</div>
</div>
<script src="scripts/app.min.js"></script>
<script>PR.prettyPrint();</script>
<script src="scripts/linenumber.js"> </script>
</body>
</html>

313
docs/module-updux.html Normal file
View File

@ -0,0 +1,313 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title> updux</title>
<script src="https://cdn.jsdelivr.net/gh/google/code-prettify@master/loader/run_prettify.js"></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
<script src="./build/entry.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<!--[if lt IE 9]>
<script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
<link href="https://fonts.googleapis.com/css?family=Roboto:100,400,700|Inconsolata,700" rel="stylesheet">
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.6.3/css/all.css" integrity="sha384-UHRtZLI+pbxtHCWp1t77Bi1L4ZtiqrqD80Kn4Z8NTSRyMA2Fd33n5dQ8lWUE00s/" crossorigin="anonymous">
<link type="text/css" rel="stylesheet" href="https://jmblog.github.io/color-themes-for-google-code-prettify/themes/tomorrow-night.min.css">
<link type="text/css" rel="stylesheet" href="styles/app.min.css">
<link type="text/css" rel="stylesheet" href="styles/iframe.css">
<link type="text/css" rel="stylesheet" href="">
<script async defer src="https://buttons.github.io/buttons.js"></script>
</head>
<body class="layout small-header">
<div id="stickyNavbarOverlay"></div>
<div class="top-nav">
<div class="inner">
<a id="hamburger" role="button" class="navbar-burger" aria-label="menu" aria-expanded="false">
<span aria-hidden="true"></span>
<span aria-hidden="true"></span>
<span aria-hidden="true"></span>
</a>
<div class="logo">
</div>
<div class="menu">
<div class="navigation">
<a
href="index.html"
class="link"
>
API Documentation
</a>
</div>
</div>
</div>
</div>
<div id="main">
<div
class="sidebar "
id="sidebarNav"
>
<nav>
<h2><a href="index.html">Documentation</a></h2><div class="category"><h3>Modules</h3><ul><li><a href="module-updux.html">updux</a></li></ul><h3>Classes</h3><ul><li><a href="Updux.html">Updux</a></li></ul></div>
</nav>
</div>
<div class="core" id="main-content-wrapper">
<div class="content">
<header class="page-title">
<p>Module</p>
<h1>updux</h1>
</header>
<section>
<header>
</header>
<article>
<div class="container-overview">
<div class="description"><p><code>Updux</code> is a way to minimize and simplify the boilerplate associated with the
creation of a <code>Redux</code> store. It takes a shorthand configuration
object, and generates the appropriate reducer, actions, middleware, etc.
In true <code>Redux</code>-like fashion, upduxes can be made of sub-upduxes (<code>subduxes</code> for short) for different slices of the root state.</p></div>
<dl class="details">
<p class="tag-source">
<a href="index.js.html" class="button">View Source</a>
<span>
<a href="index.js.html">index.js</a>, <a href="index.js.html#line10">line 10</a>
</span>
</p>
</dl>
</div>
<div class='vertical-section'>
<h1>Members</h1>
<div class="members">
<div class="member">
<h4 class="name" id=".Updux">
<a class="href-link" href="#.Updux">#</a>
<span class='tag'>static</span>
<span class="code-name">
Updux
</span>
</h4>
<div class="description">
<p>The <a href="Updux.html">Updux</a> class</p>
</div>
<dl class="details">
<p class="tag-source">
<a href="index.js.html" class="button">View Source</a>
<span>
<a href="index.js.html">index.js</a>, <a href="index.js.html#line7">line 7</a>
</span>
</p>
</dl>
</div>
</div>
</div>
</article>
</section>
</div>
<footer class="footer">
<div class="content has-text-centered">
<p>Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 3.6.7</a></p>
<p class="sidebar-created-by">
<a href="https://github.com/SoftwareBrothers/better-docs" target="_blank">BetterDocs theme</a> provided with <i class="fas fa-heart"></i> by
<a href="http://softwarebrothers.co" target="_blank">SoftwareBrothers - JavaScript Development Agency</a>
</p>
</div>
</footer>
</div>
<div id="side-nav" class="side-nav">
</div>
</div>
<script src="scripts/app.min.js"></script>
<script>PR.prettyPrint();</script>
<script src="scripts/linenumber.js"> </script>
</body>
</html>

1
docs/scripts/app.min.js vendored Normal file
View File

@ -0,0 +1 @@
"use strict";$().ready(function(){});var sidebarIsVisible=!1,toggleSidebar=function(e){var a=!(0<arguments.length&&void 0!==e)||e;$("#sidebarNav").toggleClass("sticky",a),$("#stickyNavbarOverlay").toggleClass("active",a),$("#hamburger").toggleClass("is-active"),sidebarIsVisible=a};$().ready(function(){$("#hamburger").click(function(){toggleSidebar(!sidebarIsVisible)}),$("#stickyNavbarOverlay").click(function(){sidebarIsVisible&&toggleSidebar(!1)})});var OFFSET=150;$().ready(function(){var o=$("#side-nav"),c=[];if($(".vertical-section").length||o.hide(),$(".vertical-section").each(function(e,a){var i=$(a),t=i.find("> h1").text();if(t){o.append($("<h3/>").text(t));var s=$("<ul></ul>");i.find(".members h4.name").each(function(e,a){var i=$(a),t=i.find(".code-name").clone().children().remove().end().text(),n=i.find("a").attr("href"),r=$('<a href="'.concat(n,'" />')).text(t);s.append($("<li></li>").append(r)),c.push({link:r,offset:i.offset().top})}),o.append(s)}else i.find(".members h4.name").each(function(e,a){var i=$(a),t=i.find(".code-name").clone().children().remove().end().text(),n=i.find("a").attr("href"),r=$('<a href="'.concat(n,'" />')).text(t);o.append(r),c.push({link:r,offset:i.offset().top})})}),!$.trim(o.text()))return o.hide();function e(){for(var e=n.scrollTop(),a=!1,i=c.length-1;0<=i;i--){var t=c[i];t.link.removeClass("is-active"),e+OFFSET>=t.offset?a?t.link.addClass("is-past"):(t.link.addClass("is-active"),a=!0):t.link.removeClass("is-past")}}var n=$("#main-content-wrapper");n.on("scroll",e),e(),c.forEach(function(e){e.link.click(function(){n.animate({scrollTop:e.offset-OFFSET+1},500)})})}),$().ready(function(){$("#sidebarNav a").each(function(e,a){var i=$(a).attr("href");window.location.pathname.match("/"+i)&&($(a).addClass("active"),$("#sidebarNav").scrollTop($(a).offset().top-150))})});

View File

@ -0,0 +1,26 @@
/*global document */
(function() {
var source = document.getElementsByClassName('prettyprint source linenums');
var i = 0;
var lineNumber = 0;
var lineId;
var lines;
var totalLines;
var anchorHash;
if (source && source[0]) {
anchorHash = document.location.hash.substring(1);
lines = source[0].getElementsByTagName('li');
totalLines = lines.length;
for (; i < totalLines; i++) {
lineNumber++;
lineId = 'line' + lineNumber;
lines[i].id = lineId;
if (lineId === anchorHash) {
lines[i].className += ' selected';
}
}
}
})();

View File

@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@ -0,0 +1,2 @@
PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t\n\f\r ]+/,null," \t\r\n "]],[["str",/^"(?:[^\n\f\r"\\]|\\(?:\r\n?|\n|\f)|\\[\S\s])*"/,null],["str",/^'(?:[^\n\f\r'\\]|\\(?:\r\n?|\n|\f)|\\[\S\s])*'/,null],["lang-css-str",/^url\(([^"')]*)\)/i],["kwd",/^(?:url|rgb|!important|@import|@page|@media|@charset|inherit)(?=[^\w-]|$)/i,null],["lang-css-kw",/^(-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*)\s*:/i],["com",/^\/\*[^*]*\*+(?:[^*/][^*]*\*+)*\//],["com",
/^(?:<\!--|--\>)/],["lit",/^(?:\d+|\d*\.\d+)(?:%|[a-z]+)?/i],["lit",/^#[\da-f]{3,6}/i],["pln",/^-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*/i],["pun",/^[^\s\w"']+/]]),["css"]);PR.registerLangHandler(PR.createSimpleLexer([],[["kwd",/^-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*/i]]),["css-kw"]);PR.registerLangHandler(PR.createSimpleLexer([],[["str",/^[^"')]+/]]),["css-str"]);

View File

@ -0,0 +1,28 @@
var q=null;window.PR_SHOULD_USE_CONTINUATION=!0;
(function(){function L(a){function m(a){var f=a.charCodeAt(0);if(f!==92)return f;var b=a.charAt(1);return(f=r[b])?f:"0"<=b&&b<="7"?parseInt(a.substring(1),8):b==="u"||b==="x"?parseInt(a.substring(2),16):a.charCodeAt(1)}function e(a){if(a<32)return(a<16?"\\x0":"\\x")+a.toString(16);a=String.fromCharCode(a);if(a==="\\"||a==="-"||a==="["||a==="]")a="\\"+a;return a}function h(a){for(var f=a.substring(1,a.length-1).match(/\\u[\dA-Fa-f]{4}|\\x[\dA-Fa-f]{2}|\\[0-3][0-7]{0,2}|\\[0-7]{1,2}|\\[\S\s]|[^\\]/g),a=
[],b=[],o=f[0]==="^",c=o?1:0,i=f.length;c<i;++c){var j=f[c];if(/\\[bdsw]/i.test(j))a.push(j);else{var j=m(j),d;c+2<i&&"-"===f[c+1]?(d=m(f[c+2]),c+=2):d=j;b.push([j,d]);d<65||j>122||(d<65||j>90||b.push([Math.max(65,j)|32,Math.min(d,90)|32]),d<97||j>122||b.push([Math.max(97,j)&-33,Math.min(d,122)&-33]))}}b.sort(function(a,f){return a[0]-f[0]||f[1]-a[1]});f=[];j=[NaN,NaN];for(c=0;c<b.length;++c)i=b[c],i[0]<=j[1]+1?j[1]=Math.max(j[1],i[1]):f.push(j=i);b=["["];o&&b.push("^");b.push.apply(b,a);for(c=0;c<
f.length;++c)i=f[c],b.push(e(i[0])),i[1]>i[0]&&(i[1]+1>i[0]&&b.push("-"),b.push(e(i[1])));b.push("]");return b.join("")}function y(a){for(var f=a.source.match(/\[(?:[^\\\]]|\\[\S\s])*]|\\u[\dA-Fa-f]{4}|\\x[\dA-Fa-f]{2}|\\\d+|\\[^\dux]|\(\?[!:=]|[()^]|[^()[\\^]+/g),b=f.length,d=[],c=0,i=0;c<b;++c){var j=f[c];j==="("?++i:"\\"===j.charAt(0)&&(j=+j.substring(1))&&j<=i&&(d[j]=-1)}for(c=1;c<d.length;++c)-1===d[c]&&(d[c]=++t);for(i=c=0;c<b;++c)j=f[c],j==="("?(++i,d[i]===void 0&&(f[c]="(?:")):"\\"===j.charAt(0)&&
(j=+j.substring(1))&&j<=i&&(f[c]="\\"+d[i]);for(i=c=0;c<b;++c)"^"===f[c]&&"^"!==f[c+1]&&(f[c]="");if(a.ignoreCase&&s)for(c=0;c<b;++c)j=f[c],a=j.charAt(0),j.length>=2&&a==="["?f[c]=h(j):a!=="\\"&&(f[c]=j.replace(/[A-Za-z]/g,function(a){a=a.charCodeAt(0);return"["+String.fromCharCode(a&-33,a|32)+"]"}));return f.join("")}for(var t=0,s=!1,l=!1,p=0,d=a.length;p<d;++p){var g=a[p];if(g.ignoreCase)l=!0;else if(/[a-z]/i.test(g.source.replace(/\\u[\da-f]{4}|\\x[\da-f]{2}|\\[^UXux]/gi,""))){s=!0;l=!1;break}}for(var r=
{b:8,t:9,n:10,v:11,f:12,r:13},n=[],p=0,d=a.length;p<d;++p){g=a[p];if(g.global||g.multiline)throw Error(""+g);n.push("(?:"+y(g)+")")}return RegExp(n.join("|"),l?"gi":"g")}function M(a){function m(a){switch(a.nodeType){case 1:if(e.test(a.className))break;for(var g=a.firstChild;g;g=g.nextSibling)m(g);g=a.nodeName;if("BR"===g||"LI"===g)h[s]="\n",t[s<<1]=y++,t[s++<<1|1]=a;break;case 3:case 4:g=a.nodeValue,g.length&&(g=p?g.replace(/\r\n?/g,"\n"):g.replace(/[\t\n\r ]+/g," "),h[s]=g,t[s<<1]=y,y+=g.length,
t[s++<<1|1]=a)}}var e=/(?:^|\s)nocode(?:\s|$)/,h=[],y=0,t=[],s=0,l;a.currentStyle?l=a.currentStyle.whiteSpace:window.getComputedStyle&&(l=document.defaultView.getComputedStyle(a,q).getPropertyValue("white-space"));var p=l&&"pre"===l.substring(0,3);m(a);return{a:h.join("").replace(/\n$/,""),c:t}}function B(a,m,e,h){m&&(a={a:m,d:a},e(a),h.push.apply(h,a.e))}function x(a,m){function e(a){for(var l=a.d,p=[l,"pln"],d=0,g=a.a.match(y)||[],r={},n=0,z=g.length;n<z;++n){var f=g[n],b=r[f],o=void 0,c;if(typeof b===
"string")c=!1;else{var i=h[f.charAt(0)];if(i)o=f.match(i[1]),b=i[0];else{for(c=0;c<t;++c)if(i=m[c],o=f.match(i[1])){b=i[0];break}o||(b="pln")}if((c=b.length>=5&&"lang-"===b.substring(0,5))&&!(o&&typeof o[1]==="string"))c=!1,b="src";c||(r[f]=b)}i=d;d+=f.length;if(c){c=o[1];var j=f.indexOf(c),k=j+c.length;o[2]&&(k=f.length-o[2].length,j=k-c.length);b=b.substring(5);B(l+i,f.substring(0,j),e,p);B(l+i+j,c,C(b,c),p);B(l+i+k,f.substring(k),e,p)}else p.push(l+i,b)}a.e=p}var h={},y;(function(){for(var e=a.concat(m),
l=[],p={},d=0,g=e.length;d<g;++d){var r=e[d],n=r[3];if(n)for(var k=n.length;--k>=0;)h[n.charAt(k)]=r;r=r[1];n=""+r;p.hasOwnProperty(n)||(l.push(r),p[n]=q)}l.push(/[\S\s]/);y=L(l)})();var t=m.length;return e}function u(a){var m=[],e=[];a.tripleQuotedStrings?m.push(["str",/^(?:'''(?:[^'\\]|\\[\S\s]|''?(?=[^']))*(?:'''|$)|"""(?:[^"\\]|\\[\S\s]|""?(?=[^"]))*(?:"""|$)|'(?:[^'\\]|\\[\S\s])*(?:'|$)|"(?:[^"\\]|\\[\S\s])*(?:"|$))/,q,"'\""]):a.multiLineStrings?m.push(["str",/^(?:'(?:[^'\\]|\\[\S\s])*(?:'|$)|"(?:[^"\\]|\\[\S\s])*(?:"|$)|`(?:[^\\`]|\\[\S\s])*(?:`|$))/,
q,"'\"`"]):m.push(["str",/^(?:'(?:[^\n\r'\\]|\\.)*(?:'|$)|"(?:[^\n\r"\\]|\\.)*(?:"|$))/,q,"\"'"]);a.verbatimStrings&&e.push(["str",/^@"(?:[^"]|"")*(?:"|$)/,q]);var h=a.hashComments;h&&(a.cStyleComments?(h>1?m.push(["com",/^#(?:##(?:[^#]|#(?!##))*(?:###|$)|.*)/,q,"#"]):m.push(["com",/^#(?:(?:define|elif|else|endif|error|ifdef|include|ifndef|line|pragma|undef|warning)\b|[^\n\r]*)/,q,"#"]),e.push(["str",/^<(?:(?:(?:\.\.\/)*|\/?)(?:[\w-]+(?:\/[\w-]+)+)?[\w-]+\.h|[a-z]\w*)>/,q])):m.push(["com",/^#[^\n\r]*/,
q,"#"]));a.cStyleComments&&(e.push(["com",/^\/\/[^\n\r]*/,q]),e.push(["com",/^\/\*[\S\s]*?(?:\*\/|$)/,q]));a.regexLiterals&&e.push(["lang-regex",/^(?:^^\.?|[!+-]|!=|!==|#|%|%=|&|&&|&&=|&=|\(|\*|\*=|\+=|,|-=|->|\/|\/=|:|::|;|<|<<|<<=|<=|=|==|===|>|>=|>>|>>=|>>>|>>>=|[?@[^]|\^=|\^\^|\^\^=|{|\||\|=|\|\||\|\|=|~|break|case|continue|delete|do|else|finally|instanceof|return|throw|try|typeof)\s*(\/(?=[^*/])(?:[^/[\\]|\\[\S\s]|\[(?:[^\\\]]|\\[\S\s])*(?:]|$))+\/)/]);(h=a.types)&&e.push(["typ",h]);a=(""+a.keywords).replace(/^ | $/g,
"");a.length&&e.push(["kwd",RegExp("^(?:"+a.replace(/[\s,]+/g,"|")+")\\b"),q]);m.push(["pln",/^\s+/,q," \r\n\t\xa0"]);e.push(["lit",/^@[$_a-z][\w$@]*/i,q],["typ",/^(?:[@_]?[A-Z]+[a-z][\w$@]*|\w+_t\b)/,q],["pln",/^[$_a-z][\w$@]*/i,q],["lit",/^(?:0x[\da-f]+|(?:\d(?:_\d+)*\d*(?:\.\d*)?|\.\d\+)(?:e[+-]?\d+)?)[a-z]*/i,q,"0123456789"],["pln",/^\\[\S\s]?/,q],["pun",/^.[^\s\w"-$'./@\\`]*/,q]);return x(m,e)}function D(a,m){function e(a){switch(a.nodeType){case 1:if(k.test(a.className))break;if("BR"===a.nodeName)h(a),
a.parentNode&&a.parentNode.removeChild(a);else for(a=a.firstChild;a;a=a.nextSibling)e(a);break;case 3:case 4:if(p){var b=a.nodeValue,d=b.match(t);if(d){var c=b.substring(0,d.index);a.nodeValue=c;(b=b.substring(d.index+d[0].length))&&a.parentNode.insertBefore(s.createTextNode(b),a.nextSibling);h(a);c||a.parentNode.removeChild(a)}}}}function h(a){function b(a,d){var e=d?a.cloneNode(!1):a,f=a.parentNode;if(f){var f=b(f,1),g=a.nextSibling;f.appendChild(e);for(var h=g;h;h=g)g=h.nextSibling,f.appendChild(h)}return e}
for(;!a.nextSibling;)if(a=a.parentNode,!a)return;for(var a=b(a.nextSibling,0),e;(e=a.parentNode)&&e.nodeType===1;)a=e;d.push(a)}var k=/(?:^|\s)nocode(?:\s|$)/,t=/\r\n?|\n/,s=a.ownerDocument,l;a.currentStyle?l=a.currentStyle.whiteSpace:window.getComputedStyle&&(l=s.defaultView.getComputedStyle(a,q).getPropertyValue("white-space"));var p=l&&"pre"===l.substring(0,3);for(l=s.createElement("LI");a.firstChild;)l.appendChild(a.firstChild);for(var d=[l],g=0;g<d.length;++g)e(d[g]);m===(m|0)&&d[0].setAttribute("value",
m);var r=s.createElement("OL");r.className="linenums";for(var n=Math.max(0,m-1|0)||0,g=0,z=d.length;g<z;++g)l=d[g],l.className="L"+(g+n)%10,l.firstChild||l.appendChild(s.createTextNode("\xa0")),r.appendChild(l);a.appendChild(r)}function k(a,m){for(var e=m.length;--e>=0;){var h=m[e];A.hasOwnProperty(h)?window.console&&console.warn("cannot override language handler %s",h):A[h]=a}}function C(a,m){if(!a||!A.hasOwnProperty(a))a=/^\s*</.test(m)?"default-markup":"default-code";return A[a]}function E(a){var m=
a.g;try{var e=M(a.h),h=e.a;a.a=h;a.c=e.c;a.d=0;C(m,h)(a);var k=/\bMSIE\b/.test(navigator.userAgent),m=/\n/g,t=a.a,s=t.length,e=0,l=a.c,p=l.length,h=0,d=a.e,g=d.length,a=0;d[g]=s;var r,n;for(n=r=0;n<g;)d[n]!==d[n+2]?(d[r++]=d[n++],d[r++]=d[n++]):n+=2;g=r;for(n=r=0;n<g;){for(var z=d[n],f=d[n+1],b=n+2;b+2<=g&&d[b+1]===f;)b+=2;d[r++]=z;d[r++]=f;n=b}for(d.length=r;h<p;){var o=l[h+2]||s,c=d[a+2]||s,b=Math.min(o,c),i=l[h+1],j;if(i.nodeType!==1&&(j=t.substring(e,b))){k&&(j=j.replace(m,"\r"));i.nodeValue=
j;var u=i.ownerDocument,v=u.createElement("SPAN");v.className=d[a+1];var x=i.parentNode;x.replaceChild(v,i);v.appendChild(i);e<o&&(l[h+1]=i=u.createTextNode(t.substring(b,o)),x.insertBefore(i,v.nextSibling))}e=b;e>=o&&(h+=2);e>=c&&(a+=2)}}catch(w){"console"in window&&console.log(w&&w.stack?w.stack:w)}}var v=["break,continue,do,else,for,if,return,while"],w=[[v,"auto,case,char,const,default,double,enum,extern,float,goto,int,long,register,short,signed,sizeof,static,struct,switch,typedef,union,unsigned,void,volatile"],
"catch,class,delete,false,import,new,operator,private,protected,public,this,throw,true,try,typeof"],F=[w,"alignof,align_union,asm,axiom,bool,concept,concept_map,const_cast,constexpr,decltype,dynamic_cast,explicit,export,friend,inline,late_check,mutable,namespace,nullptr,reinterpret_cast,static_assert,static_cast,template,typeid,typename,using,virtual,where"],G=[w,"abstract,boolean,byte,extends,final,finally,implements,import,instanceof,null,native,package,strictfp,super,synchronized,throws,transient"],
H=[G,"as,base,by,checked,decimal,delegate,descending,dynamic,event,fixed,foreach,from,group,implicit,in,interface,internal,into,is,lock,object,out,override,orderby,params,partial,readonly,ref,sbyte,sealed,stackalloc,string,select,uint,ulong,unchecked,unsafe,ushort,var"],w=[w,"debugger,eval,export,function,get,null,set,undefined,var,with,Infinity,NaN"],I=[v,"and,as,assert,class,def,del,elif,except,exec,finally,from,global,import,in,is,lambda,nonlocal,not,or,pass,print,raise,try,with,yield,False,True,None"],
J=[v,"alias,and,begin,case,class,def,defined,elsif,end,ensure,false,in,module,next,nil,not,or,redo,rescue,retry,self,super,then,true,undef,unless,until,when,yield,BEGIN,END"],v=[v,"case,done,elif,esac,eval,fi,function,in,local,set,then,until"],K=/^(DIR|FILE|vector|(de|priority_)?queue|list|stack|(const_)?iterator|(multi)?(set|map)|bitset|u?(int|float)\d*)/,N=/\S/,O=u({keywords:[F,H,w,"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END"+
I,J,v],hashComments:!0,cStyleComments:!0,multiLineStrings:!0,regexLiterals:!0}),A={};k(O,["default-code"]);k(x([],[["pln",/^[^<?]+/],["dec",/^<!\w[^>]*(?:>|$)/],["com",/^<\!--[\S\s]*?(?:--\>|$)/],["lang-",/^<\?([\S\s]+?)(?:\?>|$)/],["lang-",/^<%([\S\s]+?)(?:%>|$)/],["pun",/^(?:<[%?]|[%?]>)/],["lang-",/^<xmp\b[^>]*>([\S\s]+?)<\/xmp\b[^>]*>/i],["lang-js",/^<script\b[^>]*>([\S\s]*?)(<\/script\b[^>]*>)/i],["lang-css",/^<style\b[^>]*>([\S\s]*?)(<\/style\b[^>]*>)/i],["lang-in.tag",/^(<\/?[a-z][^<>]*>)/i]]),
["default-markup","htm","html","mxml","xhtml","xml","xsl"]);k(x([["pln",/^\s+/,q," \t\r\n"],["atv",/^(?:"[^"]*"?|'[^']*'?)/,q,"\"'"]],[["tag",/^^<\/?[a-z](?:[\w-.:]*\w)?|\/?>$/i],["atn",/^(?!style[\s=]|on)[a-z](?:[\w:-]*\w)?/i],["lang-uq.val",/^=\s*([^\s"'>]*(?:[^\s"'/>]|\/(?=\s)))/],["pun",/^[/<->]+/],["lang-js",/^on\w+\s*=\s*"([^"]+)"/i],["lang-js",/^on\w+\s*=\s*'([^']+)'/i],["lang-js",/^on\w+\s*=\s*([^\s"'>]+)/i],["lang-css",/^style\s*=\s*"([^"]+)"/i],["lang-css",/^style\s*=\s*'([^']+)'/i],["lang-css",
/^style\s*=\s*([^\s"'>]+)/i]]),["in.tag"]);k(x([],[["atv",/^[\S\s]+/]]),["uq.val"]);k(u({keywords:F,hashComments:!0,cStyleComments:!0,types:K}),["c","cc","cpp","cxx","cyc","m"]);k(u({keywords:"null,true,false"}),["json"]);k(u({keywords:H,hashComments:!0,cStyleComments:!0,verbatimStrings:!0,types:K}),["cs"]);k(u({keywords:G,cStyleComments:!0}),["java"]);k(u({keywords:v,hashComments:!0,multiLineStrings:!0}),["bsh","csh","sh"]);k(u({keywords:I,hashComments:!0,multiLineStrings:!0,tripleQuotedStrings:!0}),
["cv","py"]);k(u({keywords:"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END",hashComments:!0,multiLineStrings:!0,regexLiterals:!0}),["perl","pl","pm"]);k(u({keywords:J,hashComments:!0,multiLineStrings:!0,regexLiterals:!0}),["rb"]);k(u({keywords:w,cStyleComments:!0,regexLiterals:!0}),["js"]);k(u({keywords:"all,and,by,catch,class,else,extends,false,finally,for,if,in,is,isnt,loop,new,no,not,null,of,off,on,or,return,super,then,true,try,unless,until,when,while,yes",
hashComments:3,cStyleComments:!0,multilineStrings:!0,tripleQuotedStrings:!0,regexLiterals:!0}),["coffee"]);k(x([],[["str",/^[\S\s]+/]]),["regex"]);window.prettyPrintOne=function(a,m,e){var h=document.createElement("PRE");h.innerHTML=a;e&&D(h,e);E({g:m,i:e,h:h});return h.innerHTML};window.prettyPrint=function(a){function m(){for(var e=window.PR_SHOULD_USE_CONTINUATION?l.now()+250:Infinity;p<h.length&&l.now()<e;p++){var n=h[p],k=n.className;if(k.indexOf("prettyprint")>=0){var k=k.match(g),f,b;if(b=
!k){b=n;for(var o=void 0,c=b.firstChild;c;c=c.nextSibling)var i=c.nodeType,o=i===1?o?b:c:i===3?N.test(c.nodeValue)?b:o:o;b=(f=o===b?void 0:o)&&"CODE"===f.tagName}b&&(k=f.className.match(g));k&&(k=k[1]);b=!1;for(o=n.parentNode;o;o=o.parentNode)if((o.tagName==="pre"||o.tagName==="code"||o.tagName==="xmp")&&o.className&&o.className.indexOf("prettyprint")>=0){b=!0;break}b||((b=(b=n.className.match(/\blinenums\b(?::(\d+))?/))?b[1]&&b[1].length?+b[1]:!0:!1)&&D(n,b),d={g:k,h:n,i:b},E(d))}}p<h.length?setTimeout(m,
250):a&&a()}for(var e=[document.getElementsByTagName("pre"),document.getElementsByTagName("code"),document.getElementsByTagName("xmp")],h=[],k=0;k<e.length;++k)for(var t=0,s=e[k].length;t<s;++t)h.push(e[k][t]);var e=q,l=Date;l.now||(l={now:function(){return+new Date}});var p=0,d,g=/\blang(?:uage)?-([\w.]+)(?!\S)/;m()};window.PR={createSimpleLexer:x,registerLangHandler:k,sourceDecorator:u,PR_ATTRIB_NAME:"atn",PR_ATTRIB_VALUE:"atv",PR_COMMENT:"com",PR_DECLARATION:"dec",PR_KEYWORD:"kwd",PR_LITERAL:"lit",
PR_NOCODE:"nocode",PR_PLAIN:"pln",PR_PUNCTUATION:"pun",PR_SOURCE:"src",PR_STRING:"str",PR_TAG:"tag",PR_TYPE:"typ"}})();

39
docs/scripts/search.js Normal file
View File

@ -0,0 +1,39 @@
(function() {
const input = document.querySelector('#search')
const targets = [ ...document.querySelectorAll('#sidebarNav li')]
input.addEventListener('keyup', () => {
// loop over each targets and hide the not corresponding ones
targets.forEach(target => {
if (!target.innerText.toLowerCase().includes(input.value.toLowerCase())) {
target.style.display = 'none'
/**
* Detects an empty list
* Remove the list and the list's title if the list is not displayed
*/
const list = [...target.parentNode.childNodes].filter( elem => elem.style.display !== 'none')
if (!list.length) {
target.parentNode.style.display = 'none'
target.parentNode.previousSibling.style.display = 'none'
}
/**
* Detects empty category
* Remove the entire category if no item is displayed
*/
const category = [...target.parentNode.parentNode.childNodes]
.filter( elem => elem.tagName !== 'H2' && elem.style.display !== 'none')
if (!category.length) {
target.parentNode.parentNode.style.display = 'none'
}
} else {
target.parentNode.style.display = 'block'
target.parentNode.previousSibling.style.display = 'block'
target.parentNode.parentNode.style.display = 'block'
target.style.display = 'block'
}
})
})
})()

1
docs/styles/app.min.css vendored Normal file

File diff suppressed because one or more lines are too long

13
docs/styles/iframe.css Normal file
View File

@ -0,0 +1,13 @@
.bd__button {
padding: 10px 0;
text-align: right;
}
.bd__button > a{
font-weight: 100;
text-decoration: none;
color: #BDC3CB;
font-family: sans-serif;
}
.bd__button > a:hover {
color: #798897;
}

View File

@ -0,0 +1,358 @@
@font-face {
font-family: 'Open Sans';
font-weight: normal;
font-style: normal;
src: url('../fonts/OpenSans-Regular-webfont.eot');
src:
local('Open Sans'),
local('OpenSans'),
url('../fonts/OpenSans-Regular-webfont.eot?#iefix') format('embedded-opentype'),
url('../fonts/OpenSans-Regular-webfont.woff') format('woff'),
url('../fonts/OpenSans-Regular-webfont.svg#open_sansregular') format('svg');
}
@font-face {
font-family: 'Open Sans Light';
font-weight: normal;
font-style: normal;
src: url('../fonts/OpenSans-Light-webfont.eot');
src:
local('Open Sans Light'),
local('OpenSans Light'),
url('../fonts/OpenSans-Light-webfont.eot?#iefix') format('embedded-opentype'),
url('../fonts/OpenSans-Light-webfont.woff') format('woff'),
url('../fonts/OpenSans-Light-webfont.svg#open_sanslight') format('svg');
}
html
{
overflow: auto;
background-color: #fff;
font-size: 14px;
}
body
{
font-family: 'Open Sans', sans-serif;
line-height: 1.5;
color: #4d4e53;
background-color: white;
}
a, a:visited, a:active {
color: #0095dd;
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
header
{
display: block;
padding: 0px 4px;
}
tt, code, kbd, samp {
font-family: Consolas, Monaco, 'Andale Mono', monospace;
}
.class-description {
font-size: 130%;
line-height: 140%;
margin-bottom: 1em;
margin-top: 1em;
}
.class-description:empty {
margin: 0;
}
#main {
float: left;
width: 70%;
}
article dl {
margin-bottom: 40px;
}
article img {
max-width: 100%;
}
section
{
display: block;
background-color: #fff;
padding: 12px 24px;
border-bottom: 1px solid #ccc;
margin-right: 30px;
}
.variation {
display: none;
}
.signature-attributes {
font-size: 60%;
color: #aaa;
font-style: italic;
font-weight: lighter;
}
nav
{
display: block;
float: right;
margin-top: 28px;
width: 30%;
box-sizing: border-box;
border-left: 1px solid #ccc;
padding-left: 16px;
}
nav ul {
font-family: 'Lucida Grande', 'Lucida Sans Unicode', arial, sans-serif;
font-size: 100%;
line-height: 17px;
padding: 0;
margin: 0;
list-style-type: none;
}
nav ul a, nav ul a:visited, nav ul a:active {
font-family: Consolas, Monaco, 'Andale Mono', monospace;
line-height: 18px;
color: #4D4E53;
}
nav h3 {
margin-top: 12px;
}
nav li {
margin-top: 6px;
}
footer {
display: block;
padding: 6px;
margin-top: 12px;
font-style: italic;
font-size: 90%;
}
h1, h2, h3, h4 {
font-weight: 200;
margin: 0;
}
h1
{
font-family: 'Open Sans Light', sans-serif;
font-size: 48px;
letter-spacing: -2px;
margin: 12px 24px 20px;
}
h2, h3.subsection-title
{
font-size: 30px;
font-weight: 700;
letter-spacing: -1px;
margin-bottom: 12px;
}
h3
{
font-size: 24px;
letter-spacing: -0.5px;
margin-bottom: 12px;
}
h4
{
font-size: 18px;
letter-spacing: -0.33px;
margin-bottom: 12px;
color: #4d4e53;
}
h5, .container-overview .subsection-title
{
font-size: 120%;
font-weight: bold;
letter-spacing: -0.01em;
margin: 8px 0 3px 0;
}
h6
{
font-size: 100%;
letter-spacing: -0.01em;
margin: 6px 0 3px 0;
font-style: italic;
}
table
{
border-spacing: 0;
border: 0;
border-collapse: collapse;
}
td, th
{
border: 1px solid #ddd;
margin: 0px;
text-align: left;
vertical-align: top;
padding: 4px 6px;
display: table-cell;
}
thead tr
{
background-color: #ddd;
font-weight: bold;
}
th { border-right: 1px solid #aaa; }
tr > th:last-child { border-right: 1px solid #ddd; }
.ancestors, .attribs { color: #999; }
.ancestors a, .attribs a
{
color: #999 !important;
text-decoration: none;
}
.clear
{
clear: both;
}
.important
{
font-weight: bold;
color: #950B02;
}
.yes-def {
text-indent: -1000px;
}
.type-signature {
color: #aaa;
}
.name, .signature {
font-family: Consolas, Monaco, 'Andale Mono', monospace;
}
.details { margin-top: 14px; border-left: 2px solid #DDD; }
.details dt { width: 120px; float: left; padding-left: 10px; padding-top: 6px; }
.details dd { margin-left: 70px; }
.details ul { margin: 0; }
.details ul { list-style-type: none; }
.details li { margin-left: 30px; padding-top: 6px; }
.details pre.prettyprint { margin: 0 }
.details .object-value { padding-top: 0; }
.description {
margin-bottom: 1em;
margin-top: 1em;
}
.code-caption
{
font-style: italic;
font-size: 107%;
margin: 0;
}
.source
{
border: 1px solid #ddd;
width: 80%;
overflow: auto;
}
.prettyprint.source {
width: inherit;
}
.source code
{
font-size: 100%;
line-height: 18px;
display: block;
padding: 4px 12px;
margin: 0;
background-color: #fff;
color: #4D4E53;
}
.prettyprint code span.line
{
display: inline-block;
}
.prettyprint.linenums
{
padding-left: 70px;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
.prettyprint.linenums ol
{
padding-left: 0;
}
.prettyprint.linenums li
{
border-left: 3px #ddd solid;
}
.prettyprint.linenums li.selected,
.prettyprint.linenums li.selected *
{
background-color: lightyellow;
}
.prettyprint.linenums li *
{
-webkit-user-select: text;
-moz-user-select: text;
-ms-user-select: text;
user-select: text;
}
.params .name, .props .name, .name code {
color: #4D4E53;
font-family: Consolas, Monaco, 'Andale Mono', monospace;
font-size: 100%;
}
.params td.description > p:first-child,
.props td.description > p:first-child
{
margin-top: 0;
padding-top: 0;
}
.params td.description > p:last-child,
.props td.description > p:last-child
{
margin-bottom: 0;
padding-bottom: 0;
}
.disabled {
color: #454545;
}

View File

@ -0,0 +1,111 @@
/* JSDoc prettify.js theme */
/* plain text */
.pln {
color: #000000;
font-weight: normal;
font-style: normal;
}
/* string content */
.str {
color: #006400;
font-weight: normal;
font-style: normal;
}
/* a keyword */
.kwd {
color: #000000;
font-weight: bold;
font-style: normal;
}
/* a comment */
.com {
font-weight: normal;
font-style: italic;
}
/* a type name */
.typ {
color: #000000;
font-weight: normal;
font-style: normal;
}
/* a literal value */
.lit {
color: #006400;
font-weight: normal;
font-style: normal;
}
/* punctuation */
.pun {
color: #000000;
font-weight: bold;
font-style: normal;
}
/* lisp open bracket */
.opn {
color: #000000;
font-weight: bold;
font-style: normal;
}
/* lisp close bracket */
.clo {
color: #000000;
font-weight: bold;
font-style: normal;
}
/* a markup tag name */
.tag {
color: #006400;
font-weight: normal;
font-style: normal;
}
/* a markup attribute name */
.atn {
color: #006400;
font-weight: normal;
font-style: normal;
}
/* a markup attribute value */
.atv {
color: #006400;
font-weight: normal;
font-style: normal;
}
/* a declaration */
.dec {
color: #000000;
font-weight: bold;
font-style: normal;
}
/* a variable name */
.var {
color: #000000;
font-weight: normal;
font-style: normal;
}
/* a function name */
.fun {
color: #000000;
font-weight: bold;
font-style: normal;
}
/* Specify class=linenums on a pre to get line numbering */
ol.linenums {
margin-top: 0;
margin-bottom: 0;
}

View File

@ -0,0 +1,132 @@
/* Tomorrow Theme */
/* Original theme - https://github.com/chriskempson/tomorrow-theme */
/* Pretty printing styles. Used with prettify.js. */
/* SPAN elements with the classes below are added by prettyprint. */
/* plain text */
.pln {
color: #4d4d4c; }
@media screen {
/* string content */
.str {
color: #718c00; }
/* a keyword */
.kwd {
color: #8959a8; }
/* a comment */
.com {
color: #8e908c; }
/* a type name */
.typ {
color: #4271ae; }
/* a literal value */
.lit {
color: #f5871f; }
/* punctuation */
.pun {
color: #4d4d4c; }
/* lisp open bracket */
.opn {
color: #4d4d4c; }
/* lisp close bracket */
.clo {
color: #4d4d4c; }
/* a markup tag name */
.tag {
color: #c82829; }
/* a markup attribute name */
.atn {
color: #f5871f; }
/* a markup attribute value */
.atv {
color: #3e999f; }
/* a declaration */
.dec {
color: #f5871f; }
/* a variable name */
.var {
color: #c82829; }
/* a function name */
.fun {
color: #4271ae; } }
/* Use higher contrast and text-weight for printable form. */
@media print, projection {
.str {
color: #060; }
.kwd {
color: #006;
font-weight: bold; }
.com {
color: #600;
font-style: italic; }
.typ {
color: #404;
font-weight: bold; }
.lit {
color: #044; }
.pun, .opn, .clo {
color: #440; }
.tag {
color: #006;
font-weight: bold; }
.atn {
color: #404; }
.atv {
color: #060; } }
/* Style */
/*
pre.prettyprint {
background: white;
font-family: Consolas, Monaco, 'Andale Mono', monospace;
font-size: 12px;
line-height: 1.5;
border: 1px solid #ccc;
padding: 10px; }
*/
/* Specify class=linenums on a pre to get line numbering */
ol.linenums {
margin-top: 0;
margin-bottom: 0; }
/* IE indents via margin-left */
li.L0,
li.L1,
li.L2,
li.L3,
li.L4,
li.L5,
li.L6,
li.L7,
li.L8,
li.L9 {
/* */ }
/* Alternate shading for lines */
li.L1,
li.L3,
li.L5,
li.L7,
li.L9 {
/* */ }

44
docs/styles/reset.css Normal file
View File

@ -0,0 +1,44 @@
/* reset css */
html, body, div, span, applet, object, iframe,
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
a, abbr, acronym, address, big, cite, code,
del, dfn, em, img, ins, kbd, q, s, samp,
small, strike, strong, sub, sup, tt, var,
b, u, i, center,
dl, dt, dd, ol, ul, li,
fieldset, form, label, legend,
table, caption, tbody, tfoot, thead, tr, th, td,
article, aside, canvas, details, embed,
figure, figcaption, footer, header, hgroup,
menu, nav, output, ruby, section, summary,
time, mark, audio, video {
margin: 0;
padding: 0;
border: 0;
font-size: 100%;
font: inherit;
vertical-align: baseline;
}
/* HTML5 display-role reset for older browsers */
article, aside, details, figcaption, figure,
footer, header, hgroup, menu, nav, section {
display: block;
}
body {
line-height: 1;
}
ol, ul {
list-style: none;
}
blockquote, q {
quotes: none;
}
blockquote:before, blockquote:after,
q:before, q:after {
content: '';
content: none;
}
table {
border-collapse: collapse;
border-spacing: 0;
}

View File

@ -3,210 +3,81 @@
This tutorial walks you through the features of `Updux` using the
time-honored example of the implementation of Todo list store.
This tutorial assumes that our project is written in TypeScript, and
that we are using [updeep](https://www.npmjs.com/package/updeep) to
help with immutability and deep merging and [ts-action][] to manage our
actions. This is the recommended setup, but
neither of those two architecture
decisions are mandatory; Updux is equally usable in a pure-JavaScript setting,
and `updeep` can easily be substitued with, say, [immer][], [lodash][], or even
just plain JavaScript. Eventually, I plan to write a version of this tutorial
with all those different configurations.
Also, the code used here is also available in the project repository, in the
`src/tutorial` directory.
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
First thing first: let's define the type of our store:
To begin with, let's define that has nothing but an initial state.
```
type Todo = {
id: number;
description: string;
done: boolean;
};
```js
import { Updux } from 'updux';
type TodoStore = {
next_id: number;
todos: Todo[];
};
```
With that, let's create our very first Updux:
```
import Updux from 'updux';
const todosUpdux = new Updux({
const todosDux = new Updux({
initial: {
next_id: 1,
todos: [],
} as TodoStore
}
});
```
Note that we explicitly cast the initial state as `as TodoStore`. This lets
Updux know what is the store's state.
This being said, congrats! You have written your first Updux object. It
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:
```
const store = todosUpdux.createStore();
```js
const store = todosDux.createStore();
console.log(store.getState());
// { next_id: 1, todos: [] }
console.log(store.getState()); // prints { next_id: 1, todos: [] }
```
## Add actions
This is all good, but a little static. Let's add actions!
```
import { action, payload } from 'ts-action';
const add_todo = action('add_todo', payload<string>() );
const todo_done = action('todo_done', payload<number>() );
```
Now, there is a lot of ways to add actions to a Updux object.
It can be defined when the object is created:
```
const todosUpdux = new Updux({
actions: {
add_todo,
todo_done,
}
});
```
It can be done via the method `addAction`:
```
todosUpdux.addAction(add_todo);
```
Or it can be directly used in the definition of a mutation or effect, and will
be automatically added to the Updux.
```
todosUpdux.addMutation( add_todo, todoMutation );
```
For TypeScript projects I recommend declaring the actions as part of the
configuration passed to the constructors, as it makes them accessible to the class
at compile-time, and allows Updux to auto-add them to its aggregated `actions` type.
```
const todosUpdux = new Updux({
actions: {
add_todo,
}
});
todosUpdux.addAction(todo_done);
// only `add_todo` is visible to the type
type MyActions = typeof todosUpdux.actions;
// { add_todo: Function }
// but both actions are accessible at runtime
const myAction = ( todosUpdux.actions as any).todo_done(1);
```js
todosDux.setAction( 'addTodo' );
todosDux.setAction( 'todoDone' );
```
### 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' }
```
console.log( todosUpdux.actions.add_todo('write tutorial') );
// { type: 'add_todo', payload: 'write tutorial' }
```
### What is an action?
In this tutorial we use `ts-action` for all the work, but under the hood Updux defines actions via
their creators, which are expected to be:
1. Functions,
2. returning a plain object of the format `{ type: string; payload?: unknown }`.
3. with an additional property `type`, which is also the action type.
For example, this is a perfectly cromulent action:
```
const add_todo = description => ({ type: 'add_todo', payload: description});
add_todo.type = 'add_todo';
```
## Mutations
Actions that don't do anything are not fun. The transformations typically
done by a Redux's reducer are called 'mutations' in Updux. A mutation is a
function with the following signature:
```
( payload, action ) => state => {
// ... stuff done here
return new_state;
}
```
The inversion and chaining of parameters from the usual Redux reducer's
signature is there to work with `updeep`'s curried nature. The expansion of
the usual `action` into `(payload, action)` is present because in most cases
`payload` is what we're interested in. So why not make it easily available?
### Adding a mutation
As for the actions, a mutation can be defined as part of the Updux
Like actions, a mutation can be defined as part of the Updux
init arguments:
```
const add_todo_mutation = description => ({next_id: id, todos}) => {
return {
next_id: 1 + id,
todos: [...todos, { description, id, done: false }]
}
};
const todosUpdux = new Updux({
actions: { add_todo },
mutations: [
[ add_todo, add_todo_mutation ]
]
});
```
or via the method `addMutation`:
```
todos.addMutation( add_todo, description => ({next_id: id, todos}) => {
return {
next_id: 1 + id,
todos: [...todos, { description, id, done: false }]
```js
const todosDux = new Updux({
actions: {
addTodo: null
},
mutations: {
addTodo: description => ({next_id: id, todos}) => ({
next_id: 1 + id,
todos: [...todos, { description, id, done: false }]
})
}
});
```
This time around, if the project is using TypeScript then the addition of
mutations via `addMutation` is encouraged, as the method signature
has visibility of the types of the action and state.
or via the method `setMutation`:
### Leftover mutation
A mutation with the special action `*` will match any action that haven't been
explicitly dealt with with any other defined mutation.
```
todosUpdux.addMutation( '*', (payload,action) => state => {
console.log("hey, action has no mutation! ", action.type);
});
```js
todosDux.setMutation( 'addTodo', description => ({next_id: id, todos}) => ({
next_id: 1 + id,
todos: [...todos, { description, id, done: false }]
}));
```
## Effects
@ -214,23 +85,29 @@ todosUpdux.addMutation( '*', (payload,action) => state => {
In addition to mutations, Updux also provides action-specific middleware, here
called effects.
Effects use the usual Redux middleware signature:
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 by
themselves in the api object too.
```
```js
import u from 'updeep';
// 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 'add_todo'.
// a new todo. So let's use a new version of the action 'addTodo'.
const add_todo_with_id = action('add_todo_with_id', payload<{description: string; id?: number}>() );
const inc_next_id = action('inc_next_id');
const addTodoWithId = action('addTodoWithId');
const incNextId = action('incNextId');
const addTodo = action('addTodo');
const populate_next_id = ({ getState, dispatch }) => next => action => {
const { next_id: id } = getState();
const addTodoEffect = ({ getState, dispatch }) => next => action => {
const id = getState.nextId();
dispatch.incNextId();
dispatch(inc_next_id());
next(action);
dispatch( add_todo_with_id({ description: action.payload, id }) );
dispatch.addTodoWithId({ description: action.payload, id }) );
}
```
@ -238,28 +115,33 @@ And just like mutations, they can be defined as part of the init
configuration, or after via the method `addEffect`:
```
const todosUpdux = new Updux({
actions: { add_todo, inc_next_id },
effects: [
[ add_todo, populate_next_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');
```
or
```
const todosUpdux = new Updux({
actions: { add_todo, inc_next_id },
});
todosUpdux.addEffect( add_todo, populate_next_id );
todosDux.addEffect( 'addTodo', addTodoEffect );
```
As for the mutations, for TypeScript projects
the use of `addEffect` is prefered, as the method gives visibility to the
action and state types.
### Catch-all effect
It is possible to have an effect match all actions via the special `*` token.
@ -271,14 +153,11 @@ todosUpdux.addEffect('*', () => next => action => {
});
```
## Selectors
## Adding selectors
Selectors can be defined to get data derived from the state.
### Adding selectors
From now you should know the drill: selectors can be defined at construction
time or via `addSelector`.
time or via `setSelector`.
```
import fp from 'lodash/fp';
@ -295,287 +174,210 @@ const todosUpdux = new Updux({
or
```
todosUpdux.addSelector('getTodoById', ({todos}) => id => fp.find({id},todos));
todosDux.setSelector('getTodoById', ({todos}) => id => fp.find({id},todos));
```
Here the declaration as part of the constructor configuration is prefered.
Whereas the `addSelector` will provide the state's type as part of its
signature, declaring the selectors via the constructors will make them visible
via the type of the accessors `selectors`.
### Accessing selectors
Selectors are available via the accessor `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.
```
const store = todosUpdux.createStore();
```js
const store = todosDux.createStore();
console.log(
todosUpdux.selectors.getTodoById( store.getState() )(1)
todosUpdux.getState.getTodoById(1)
);
```
## Subduxes
Now that we have all the building blocks, we can embark on the last, and best,
part of Updux: its recursive nature.
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 updux, undivided
### 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 Updux we have so far:
let's recap on the Todos dux we have so far:
```
```js
import Updux from 'updux';
import { action, payload } from 'ts-action';
import u from 'updeep';
import fp from 'lodash/fp';
type Todo = {
id: number;
description: string;
done: boolean;
};
type TodoStore = {
next_id: number;
todos: Todo[];
};
const add_todo = action('add_todo', payload<string>() );
const add_todo_with_id = action('add_todo_with_id',
payload<{ description: string; id: number }>() );
const todo_done = action('todo_done', payload<number>() );
const increment_next_id = action('increment_next_id');
const todosUpdux = new Updux({
const todosDux = new Updux({
initial: {
next_id: 1,
nextId: 1,
todos: [],
} as TodoStore,
},
actions: {
add_todo,
add_todo_with_id,
todo_done,
increment_next_id,
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);
}
}
});
todosUpdux.addMutation( add_todo_with_id, payload =>
u.updateIn( 'todos', todos => [ ...todos, { ...payload, done: false }] )
);
todosUpdux.addMutation( increment_next_id, () => u({ next_id: i => i + 1 }) );
todosUpdux.addMutation( todo_done, id => u.updateIn(
'todos', u.map( u.if( fp.matches({id}), todo => u({done: true}, todo) ) )
) );
todosUpdux.addEffect( add_todo, ({ getState, dispatch }) => next => action => {
const { next_id: id } = getState();
dispatch(inc_next_id());
next(u.updateIn('payload', {id}, action))
});
```
This store has two main components: the `next_id`, and the `todos` collection.
The `todos` collection is itself composed of the individual `todo`s. So let's
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.
### Next_id updux
### NextId dux
```
// dux/next_id.ts
// dux/nextId.js
import Updux from 'updux';
import { action, payload } from 'ts-action';
import { Updux } from 'updux';
import u from 'updeep';
import fp from 'lodash/fp';
const increment_next_id = action('increment_next_id');
const updux = new Updux({
export default new Updux({
initial: 1,
actions: {
increment_next_id,
incNextId: null,
},
selectors: {
getNextId: state => state
},
mutations: {
incrementNextId: () => fp.add(1)
}
});
updux.addMutation( increment_next_id, () => fp.add(1) );
export default updux.asDux;
```
Notice that we didn't have to specify the type of `initial`;
TypeScript figures by itself that it's a number.
Also, note that we're exporting the output of the accessor `asDux` instead of
the updux object itself. See the upcoming section 'Exporting upduxes' for the rationale.
### Todo updux
```
// dux/todos/todo/index.ts
import Updux from 'updux';
import { action, payload } from 'ts-action';
import { Updux } from 'updux';
import u from 'updeep';
import fp from 'lodash/fp';
type Todo = {
id: number;
description: string;
done: boolean;
};
const todo_done = action('todo_done', payload<number>() );
const updux = new Updux({
export default new Updux({
initial: {
next_id: 0,
id: 0,
description: "",
done: false,
} as Todo,
},
actions: {
todo_done
todoDone: null,
},
mutations: {
todoDone: id => u.if( fp.matches({id}), { done: true }) )
}
});
updux.addMutation( todo_done, id => u.if( fp.matches({id}), { done: true }) );
export default updux.asDux;
```
### Todos updux
```
// dux/todos/index.ts
// dux/todos/index.js
import Updux, { DuxState } from 'updux';
import { action, payload } from 'ts-action';
import { Updux } from 'updux';
import u from 'updeep';
import fp from 'lodash/fp';
import todo from './todo';
import todo from './todo/index.js';
type TodoState = DuxState<typeof todo>;
const add_todo_with_id = action('add_todo_with_id',
payload<{ description: string; id: number }>()
);
const updux = new Updux({
initial: [] as Todo[],
export default new Updux({
initial: [],
subduxes: {
'*': todo.upreducer
'*': todo
},
actions: {
add_todo_with_id,
addTodoWithId,
},
selectors: {
mappedSelectors: {
getTodoById: state => id => fp.find({id},state)
},
mutations: {
addTodoWithId: todo =>
todos => [ ...todos, todo ]
}
});
todosUpdux.addMutation( add_todo_with_id, payload =>
todos => [ ...todos, { ...payload, done: false }]
);
export default updux.asDux;
```
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.
We could also have written the updux as:
```
const updux = new Updux({
initial: [] as Todo[],
actions: {
add_todo_with_id,
},
selectors: {
getTodoById: state => id => fp.find({id},state)
},
mutations: {
'*': (payload,action) => state => u.map( todo.reducer(state, action) )
}
});
```
Note how we are using the `upreducer` accessor in the first case (which yields
a reducer for the dux using the signature `(payload,action) => state => new_state`) and `reducer` in the second case (which yield an equivalent
reducer using the classic signature `(state,action) => new_state`).
### Main store
```
// dux/index.ts
// dux/index.js
import Updux from 'updux';
import todos from './todos';
import next_id from './next_id';
import nextId from './next_id';
const add_todo = action('add_todo', payload<string>() );
const updux = new Updux({
export new Updux({
subduxes: {
next_id,
nextId,
todos,
},
actions: {
add_todo
addTodo: null
},
effects: {
addTodo: ({ getState, dispatch }) => next => action => {
const id = getState.getNextId();
dispatch.incNextId()
next(action);
dispatch.addTodoWithId( action.payload, id );
}
}
});
todos.addEffect( add_todo, ({ getState, dispatch }) => next => action => {
const id = updux.selectors.getNextId( getState() );
dispatch(updux.actions.inc_next_id());
next(action);
dispatch( updux.actions.add_todo_with_id({ description: action.payload, id }) );
});
export default updux.asDux;
```
Tadah! We had to define the `add_todo` effect at the top level as it needs to
access the `getNextId` selector from `next_id` and the `add_todo_with_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. i.e., the `getNextId` selector
at the main level is actually defined as:
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:
```
const getNextId = state => next_id.selectors.getNextId(state.next_id);
```
## Subscriptions
Subscriptions can be added by default to a updux store via the initial config
or the method `addSubscription`. The signature of a subscription is:
```
(store) => (state,unsubscribe) => {
(storeApi) => (state, previousState, unsubscribe) => {
...
}
```
@ -591,46 +393,29 @@ local `state` changed since their last invocation.
Example:
```
const set_nbr_todos = action('set_nbr_todos', payload() );
const todos = dux({
const todos = new Updux({
initial: [],
subscriptions: [
({dispatch}) => todos => dispatch(set_nbr_todos(todos.length))
actions: {
setNbrTodos: null,
}
reactions: [
({dispatch}) => todos => dispatch.setNbrTodos(todos.length);
],
});
const myDux = dux({
const myDux = new Updux({
initial: {
nbr_todos: 0
},
subduxes: {
todos,
},
mutations: [
[ set_nbr_todos, nbr_todos => u({nbr_todos}) ]
]
mutations: {
setNbrTodos: nbrTodos => u({ nbrTodos })
}
})
```
## Exporting upduxes
As a general rule, don't directly export your upduxes, but rather use the accessor `asDux`.
```
const updux = new Updux({ ... });
...
export default updux.asDux;
```
`asDux` returns an immutable copy of the attributes of the updux. Exporting
this instead of the updux itself prevents unexpected modifications done
outside of the updux declaration file. More importantly, the output of
`asDux` has more precise typing, which in result results in better typing of
parent upduxes using the dux as one of its subduxes.
[immer]: https://www.npmjs.com/package/immer
[lodash]: https://www.npmjs.com/package/lodash
[ts-action]: https://www.npmjs.com/package/ts-action

22
jsdoc.json Normal file
View File

@ -0,0 +1,22 @@
{
"plugins": ["plugins/markdown"],
"recurseDepth": 10,
"source": {
"includePattern": ".+\\.js(doc|x)?$",
"excludePattern": "(^|\\/|\\\\)_"
},
"sourceType": "module",
"tags": {
"allowUnknownTags": true,
"dictionaries": ["jsdoc","closure"]
},
"templates": {
"cleverLinks": false,
"monospaceLinks": false
},
"opts": {
"destination": "./docs",
"tutorials": "./tutorials"
}
}

293
out/Updux.html Normal file
View File

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

368
out/Updux.js.html Normal file
View File

@ -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&lt;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' &amp;&amp; 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&lt;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.

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 120 KiB

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 114 KiB

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 120 KiB

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 117 KiB

Binary file not shown.

65
out/index.html Normal file
View File

@ -0,0 +1,65 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>JSDoc: Home</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">Home</h1>
<h3> </h3>
</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>

25
out/scripts/linenumber.js Normal file
View File

@ -0,0 +1,25 @@
/*global document */
(() => {
const source = document.getElementsByClassName('prettyprint source linenums');
let i = 0;
let lineNumber = 0;
let lineId;
let lines;
let totalLines;
let anchorHash;
if (source && source[0]) {
anchorHash = document.location.hash.substring(1);
lines = source[0].getElementsByTagName('li');
totalLines = lines.length;
for (; i < totalLines; i++) {
lineNumber++;
lineId = `line${lineNumber}`;
lines[i].id = lineId;
if (lineId === anchorHash) {
lines[i].className += ' selected';
}
}
}
})();

View File

@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@ -0,0 +1,2 @@
PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t\n\f\r ]+/,null," \t\r\n "]],[["str",/^"(?:[^\n\f\r"\\]|\\(?:\r\n?|\n|\f)|\\[\S\s])*"/,null],["str",/^'(?:[^\n\f\r'\\]|\\(?:\r\n?|\n|\f)|\\[\S\s])*'/,null],["lang-css-str",/^url\(([^"')]*)\)/i],["kwd",/^(?:url|rgb|!important|@import|@page|@media|@charset|inherit)(?=[^\w-]|$)/i,null],["lang-css-kw",/^(-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*)\s*:/i],["com",/^\/\*[^*]*\*+(?:[^*/][^*]*\*+)*\//],["com",
/^(?:<\!--|--\>)/],["lit",/^(?:\d+|\d*\.\d+)(?:%|[a-z]+)?/i],["lit",/^#[\da-f]{3,6}/i],["pln",/^-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*/i],["pun",/^[^\s\w"']+/]]),["css"]);PR.registerLangHandler(PR.createSimpleLexer([],[["kwd",/^-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*/i]]),["css-kw"]);PR.registerLangHandler(PR.createSimpleLexer([],[["str",/^[^"')]+/]]),["css-str"]);

View File

@ -0,0 +1,28 @@
var q=null;window.PR_SHOULD_USE_CONTINUATION=!0;
(function(){function L(a){function m(a){var f=a.charCodeAt(0);if(f!==92)return f;var b=a.charAt(1);return(f=r[b])?f:"0"<=b&&b<="7"?parseInt(a.substring(1),8):b==="u"||b==="x"?parseInt(a.substring(2),16):a.charCodeAt(1)}function e(a){if(a<32)return(a<16?"\\x0":"\\x")+a.toString(16);a=String.fromCharCode(a);if(a==="\\"||a==="-"||a==="["||a==="]")a="\\"+a;return a}function h(a){for(var f=a.substring(1,a.length-1).match(/\\u[\dA-Fa-f]{4}|\\x[\dA-Fa-f]{2}|\\[0-3][0-7]{0,2}|\\[0-7]{1,2}|\\[\S\s]|[^\\]/g),a=
[],b=[],o=f[0]==="^",c=o?1:0,i=f.length;c<i;++c){var j=f[c];if(/\\[bdsw]/i.test(j))a.push(j);else{var j=m(j),d;c+2<i&&"-"===f[c+1]?(d=m(f[c+2]),c+=2):d=j;b.push([j,d]);d<65||j>122||(d<65||j>90||b.push([Math.max(65,j)|32,Math.min(d,90)|32]),d<97||j>122||b.push([Math.max(97,j)&-33,Math.min(d,122)&-33]))}}b.sort(function(a,f){return a[0]-f[0]||f[1]-a[1]});f=[];j=[NaN,NaN];for(c=0;c<b.length;++c)i=b[c],i[0]<=j[1]+1?j[1]=Math.max(j[1],i[1]):f.push(j=i);b=["["];o&&b.push("^");b.push.apply(b,a);for(c=0;c<
f.length;++c)i=f[c],b.push(e(i[0])),i[1]>i[0]&&(i[1]+1>i[0]&&b.push("-"),b.push(e(i[1])));b.push("]");return b.join("")}function y(a){for(var f=a.source.match(/\[(?:[^\\\]]|\\[\S\s])*]|\\u[\dA-Fa-f]{4}|\\x[\dA-Fa-f]{2}|\\\d+|\\[^\dux]|\(\?[!:=]|[()^]|[^()[\\^]+/g),b=f.length,d=[],c=0,i=0;c<b;++c){var j=f[c];j==="("?++i:"\\"===j.charAt(0)&&(j=+j.substring(1))&&j<=i&&(d[j]=-1)}for(c=1;c<d.length;++c)-1===d[c]&&(d[c]=++t);for(i=c=0;c<b;++c)j=f[c],j==="("?(++i,d[i]===void 0&&(f[c]="(?:")):"\\"===j.charAt(0)&&
(j=+j.substring(1))&&j<=i&&(f[c]="\\"+d[i]);for(i=c=0;c<b;++c)"^"===f[c]&&"^"!==f[c+1]&&(f[c]="");if(a.ignoreCase&&s)for(c=0;c<b;++c)j=f[c],a=j.charAt(0),j.length>=2&&a==="["?f[c]=h(j):a!=="\\"&&(f[c]=j.replace(/[A-Za-z]/g,function(a){a=a.charCodeAt(0);return"["+String.fromCharCode(a&-33,a|32)+"]"}));return f.join("")}for(var t=0,s=!1,l=!1,p=0,d=a.length;p<d;++p){var g=a[p];if(g.ignoreCase)l=!0;else if(/[a-z]/i.test(g.source.replace(/\\u[\da-f]{4}|\\x[\da-f]{2}|\\[^UXux]/gi,""))){s=!0;l=!1;break}}for(var r=
{b:8,t:9,n:10,v:11,f:12,r:13},n=[],p=0,d=a.length;p<d;++p){g=a[p];if(g.global||g.multiline)throw Error(""+g);n.push("(?:"+y(g)+")")}return RegExp(n.join("|"),l?"gi":"g")}function M(a){function m(a){switch(a.nodeType){case 1:if(e.test(a.className))break;for(var g=a.firstChild;g;g=g.nextSibling)m(g);g=a.nodeName;if("BR"===g||"LI"===g)h[s]="\n",t[s<<1]=y++,t[s++<<1|1]=a;break;case 3:case 4:g=a.nodeValue,g.length&&(g=p?g.replace(/\r\n?/g,"\n"):g.replace(/[\t\n\r ]+/g," "),h[s]=g,t[s<<1]=y,y+=g.length,
t[s++<<1|1]=a)}}var e=/(?:^|\s)nocode(?:\s|$)/,h=[],y=0,t=[],s=0,l;a.currentStyle?l=a.currentStyle.whiteSpace:window.getComputedStyle&&(l=document.defaultView.getComputedStyle(a,q).getPropertyValue("white-space"));var p=l&&"pre"===l.substring(0,3);m(a);return{a:h.join("").replace(/\n$/,""),c:t}}function B(a,m,e,h){m&&(a={a:m,d:a},e(a),h.push.apply(h,a.e))}function x(a,m){function e(a){for(var l=a.d,p=[l,"pln"],d=0,g=a.a.match(y)||[],r={},n=0,z=g.length;n<z;++n){var f=g[n],b=r[f],o=void 0,c;if(typeof b===
"string")c=!1;else{var i=h[f.charAt(0)];if(i)o=f.match(i[1]),b=i[0];else{for(c=0;c<t;++c)if(i=m[c],o=f.match(i[1])){b=i[0];break}o||(b="pln")}if((c=b.length>=5&&"lang-"===b.substring(0,5))&&!(o&&typeof o[1]==="string"))c=!1,b="src";c||(r[f]=b)}i=d;d+=f.length;if(c){c=o[1];var j=f.indexOf(c),k=j+c.length;o[2]&&(k=f.length-o[2].length,j=k-c.length);b=b.substring(5);B(l+i,f.substring(0,j),e,p);B(l+i+j,c,C(b,c),p);B(l+i+k,f.substring(k),e,p)}else p.push(l+i,b)}a.e=p}var h={},y;(function(){for(var e=a.concat(m),
l=[],p={},d=0,g=e.length;d<g;++d){var r=e[d],n=r[3];if(n)for(var k=n.length;--k>=0;)h[n.charAt(k)]=r;r=r[1];n=""+r;p.hasOwnProperty(n)||(l.push(r),p[n]=q)}l.push(/[\S\s]/);y=L(l)})();var t=m.length;return e}function u(a){var m=[],e=[];a.tripleQuotedStrings?m.push(["str",/^(?:'''(?:[^'\\]|\\[\S\s]|''?(?=[^']))*(?:'''|$)|"""(?:[^"\\]|\\[\S\s]|""?(?=[^"]))*(?:"""|$)|'(?:[^'\\]|\\[\S\s])*(?:'|$)|"(?:[^"\\]|\\[\S\s])*(?:"|$))/,q,"'\""]):a.multiLineStrings?m.push(["str",/^(?:'(?:[^'\\]|\\[\S\s])*(?:'|$)|"(?:[^"\\]|\\[\S\s])*(?:"|$)|`(?:[^\\`]|\\[\S\s])*(?:`|$))/,
q,"'\"`"]):m.push(["str",/^(?:'(?:[^\n\r'\\]|\\.)*(?:'|$)|"(?:[^\n\r"\\]|\\.)*(?:"|$))/,q,"\"'"]);a.verbatimStrings&&e.push(["str",/^@"(?:[^"]|"")*(?:"|$)/,q]);var h=a.hashComments;h&&(a.cStyleComments?(h>1?m.push(["com",/^#(?:##(?:[^#]|#(?!##))*(?:###|$)|.*)/,q,"#"]):m.push(["com",/^#(?:(?:define|elif|else|endif|error|ifdef|include|ifndef|line|pragma|undef|warning)\b|[^\n\r]*)/,q,"#"]),e.push(["str",/^<(?:(?:(?:\.\.\/)*|\/?)(?:[\w-]+(?:\/[\w-]+)+)?[\w-]+\.h|[a-z]\w*)>/,q])):m.push(["com",/^#[^\n\r]*/,
q,"#"]));a.cStyleComments&&(e.push(["com",/^\/\/[^\n\r]*/,q]),e.push(["com",/^\/\*[\S\s]*?(?:\*\/|$)/,q]));a.regexLiterals&&e.push(["lang-regex",/^(?:^^\.?|[!+-]|!=|!==|#|%|%=|&|&&|&&=|&=|\(|\*|\*=|\+=|,|-=|->|\/|\/=|:|::|;|<|<<|<<=|<=|=|==|===|>|>=|>>|>>=|>>>|>>>=|[?@[^]|\^=|\^\^|\^\^=|{|\||\|=|\|\||\|\|=|~|break|case|continue|delete|do|else|finally|instanceof|return|throw|try|typeof)\s*(\/(?=[^*/])(?:[^/[\\]|\\[\S\s]|\[(?:[^\\\]]|\\[\S\s])*(?:]|$))+\/)/]);(h=a.types)&&e.push(["typ",h]);a=(""+a.keywords).replace(/^ | $/g,
"");a.length&&e.push(["kwd",RegExp("^(?:"+a.replace(/[\s,]+/g,"|")+")\\b"),q]);m.push(["pln",/^\s+/,q," \r\n\t\xa0"]);e.push(["lit",/^@[$_a-z][\w$@]*/i,q],["typ",/^(?:[@_]?[A-Z]+[a-z][\w$@]*|\w+_t\b)/,q],["pln",/^[$_a-z][\w$@]*/i,q],["lit",/^(?:0x[\da-f]+|(?:\d(?:_\d+)*\d*(?:\.\d*)?|\.\d\+)(?:e[+-]?\d+)?)[a-z]*/i,q,"0123456789"],["pln",/^\\[\S\s]?/,q],["pun",/^.[^\s\w"-$'./@\\`]*/,q]);return x(m,e)}function D(a,m){function e(a){switch(a.nodeType){case 1:if(k.test(a.className))break;if("BR"===a.nodeName)h(a),
a.parentNode&&a.parentNode.removeChild(a);else for(a=a.firstChild;a;a=a.nextSibling)e(a);break;case 3:case 4:if(p){var b=a.nodeValue,d=b.match(t);if(d){var c=b.substring(0,d.index);a.nodeValue=c;(b=b.substring(d.index+d[0].length))&&a.parentNode.insertBefore(s.createTextNode(b),a.nextSibling);h(a);c||a.parentNode.removeChild(a)}}}}function h(a){function b(a,d){var e=d?a.cloneNode(!1):a,f=a.parentNode;if(f){var f=b(f,1),g=a.nextSibling;f.appendChild(e);for(var h=g;h;h=g)g=h.nextSibling,f.appendChild(h)}return e}
for(;!a.nextSibling;)if(a=a.parentNode,!a)return;for(var a=b(a.nextSibling,0),e;(e=a.parentNode)&&e.nodeType===1;)a=e;d.push(a)}var k=/(?:^|\s)nocode(?:\s|$)/,t=/\r\n?|\n/,s=a.ownerDocument,l;a.currentStyle?l=a.currentStyle.whiteSpace:window.getComputedStyle&&(l=s.defaultView.getComputedStyle(a,q).getPropertyValue("white-space"));var p=l&&"pre"===l.substring(0,3);for(l=s.createElement("LI");a.firstChild;)l.appendChild(a.firstChild);for(var d=[l],g=0;g<d.length;++g)e(d[g]);m===(m|0)&&d[0].setAttribute("value",
m);var r=s.createElement("OL");r.className="linenums";for(var n=Math.max(0,m-1|0)||0,g=0,z=d.length;g<z;++g)l=d[g],l.className="L"+(g+n)%10,l.firstChild||l.appendChild(s.createTextNode("\xa0")),r.appendChild(l);a.appendChild(r)}function k(a,m){for(var e=m.length;--e>=0;){var h=m[e];A.hasOwnProperty(h)?window.console&&console.warn("cannot override language handler %s",h):A[h]=a}}function C(a,m){if(!a||!A.hasOwnProperty(a))a=/^\s*</.test(m)?"default-markup":"default-code";return A[a]}function E(a){var m=
a.g;try{var e=M(a.h),h=e.a;a.a=h;a.c=e.c;a.d=0;C(m,h)(a);var k=/\bMSIE\b/.test(navigator.userAgent),m=/\n/g,t=a.a,s=t.length,e=0,l=a.c,p=l.length,h=0,d=a.e,g=d.length,a=0;d[g]=s;var r,n;for(n=r=0;n<g;)d[n]!==d[n+2]?(d[r++]=d[n++],d[r++]=d[n++]):n+=2;g=r;for(n=r=0;n<g;){for(var z=d[n],f=d[n+1],b=n+2;b+2<=g&&d[b+1]===f;)b+=2;d[r++]=z;d[r++]=f;n=b}for(d.length=r;h<p;){var o=l[h+2]||s,c=d[a+2]||s,b=Math.min(o,c),i=l[h+1],j;if(i.nodeType!==1&&(j=t.substring(e,b))){k&&(j=j.replace(m,"\r"));i.nodeValue=
j;var u=i.ownerDocument,v=u.createElement("SPAN");v.className=d[a+1];var x=i.parentNode;x.replaceChild(v,i);v.appendChild(i);e<o&&(l[h+1]=i=u.createTextNode(t.substring(b,o)),x.insertBefore(i,v.nextSibling))}e=b;e>=o&&(h+=2);e>=c&&(a+=2)}}catch(w){"console"in window&&console.log(w&&w.stack?w.stack:w)}}var v=["break,continue,do,else,for,if,return,while"],w=[[v,"auto,case,char,const,default,double,enum,extern,float,goto,int,long,register,short,signed,sizeof,static,struct,switch,typedef,union,unsigned,void,volatile"],
"catch,class,delete,false,import,new,operator,private,protected,public,this,throw,true,try,typeof"],F=[w,"alignof,align_union,asm,axiom,bool,concept,concept_map,const_cast,constexpr,decltype,dynamic_cast,explicit,export,friend,inline,late_check,mutable,namespace,nullptr,reinterpret_cast,static_assert,static_cast,template,typeid,typename,using,virtual,where"],G=[w,"abstract,boolean,byte,extends,final,finally,implements,import,instanceof,null,native,package,strictfp,super,synchronized,throws,transient"],
H=[G,"as,base,by,checked,decimal,delegate,descending,dynamic,event,fixed,foreach,from,group,implicit,in,interface,internal,into,is,lock,object,out,override,orderby,params,partial,readonly,ref,sbyte,sealed,stackalloc,string,select,uint,ulong,unchecked,unsafe,ushort,var"],w=[w,"debugger,eval,export,function,get,null,set,undefined,var,with,Infinity,NaN"],I=[v,"and,as,assert,class,def,del,elif,except,exec,finally,from,global,import,in,is,lambda,nonlocal,not,or,pass,print,raise,try,with,yield,False,True,None"],
J=[v,"alias,and,begin,case,class,def,defined,elsif,end,ensure,false,in,module,next,nil,not,or,redo,rescue,retry,self,super,then,true,undef,unless,until,when,yield,BEGIN,END"],v=[v,"case,done,elif,esac,eval,fi,function,in,local,set,then,until"],K=/^(DIR|FILE|vector|(de|priority_)?queue|list|stack|(const_)?iterator|(multi)?(set|map)|bitset|u?(int|float)\d*)/,N=/\S/,O=u({keywords:[F,H,w,"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END"+
I,J,v],hashComments:!0,cStyleComments:!0,multiLineStrings:!0,regexLiterals:!0}),A={};k(O,["default-code"]);k(x([],[["pln",/^[^<?]+/],["dec",/^<!\w[^>]*(?:>|$)/],["com",/^<\!--[\S\s]*?(?:--\>|$)/],["lang-",/^<\?([\S\s]+?)(?:\?>|$)/],["lang-",/^<%([\S\s]+?)(?:%>|$)/],["pun",/^(?:<[%?]|[%?]>)/],["lang-",/^<xmp\b[^>]*>([\S\s]+?)<\/xmp\b[^>]*>/i],["lang-js",/^<script\b[^>]*>([\S\s]*?)(<\/script\b[^>]*>)/i],["lang-css",/^<style\b[^>]*>([\S\s]*?)(<\/style\b[^>]*>)/i],["lang-in.tag",/^(<\/?[a-z][^<>]*>)/i]]),
["default-markup","htm","html","mxml","xhtml","xml","xsl"]);k(x([["pln",/^\s+/,q," \t\r\n"],["atv",/^(?:"[^"]*"?|'[^']*'?)/,q,"\"'"]],[["tag",/^^<\/?[a-z](?:[\w-.:]*\w)?|\/?>$/i],["atn",/^(?!style[\s=]|on)[a-z](?:[\w:-]*\w)?/i],["lang-uq.val",/^=\s*([^\s"'>]*(?:[^\s"'/>]|\/(?=\s)))/],["pun",/^[/<->]+/],["lang-js",/^on\w+\s*=\s*"([^"]+)"/i],["lang-js",/^on\w+\s*=\s*'([^']+)'/i],["lang-js",/^on\w+\s*=\s*([^\s"'>]+)/i],["lang-css",/^style\s*=\s*"([^"]+)"/i],["lang-css",/^style\s*=\s*'([^']+)'/i],["lang-css",
/^style\s*=\s*([^\s"'>]+)/i]]),["in.tag"]);k(x([],[["atv",/^[\S\s]+/]]),["uq.val"]);k(u({keywords:F,hashComments:!0,cStyleComments:!0,types:K}),["c","cc","cpp","cxx","cyc","m"]);k(u({keywords:"null,true,false"}),["json"]);k(u({keywords:H,hashComments:!0,cStyleComments:!0,verbatimStrings:!0,types:K}),["cs"]);k(u({keywords:G,cStyleComments:!0}),["java"]);k(u({keywords:v,hashComments:!0,multiLineStrings:!0}),["bsh","csh","sh"]);k(u({keywords:I,hashComments:!0,multiLineStrings:!0,tripleQuotedStrings:!0}),
["cv","py"]);k(u({keywords:"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END",hashComments:!0,multiLineStrings:!0,regexLiterals:!0}),["perl","pl","pm"]);k(u({keywords:J,hashComments:!0,multiLineStrings:!0,regexLiterals:!0}),["rb"]);k(u({keywords:w,cStyleComments:!0,regexLiterals:!0}),["js"]);k(u({keywords:"all,and,by,catch,class,else,extends,false,finally,for,if,in,is,isnt,loop,new,no,not,null,of,off,on,or,return,super,then,true,try,unless,until,when,while,yes",
hashComments:3,cStyleComments:!0,multilineStrings:!0,tripleQuotedStrings:!0,regexLiterals:!0}),["coffee"]);k(x([],[["str",/^[\S\s]+/]]),["regex"]);window.prettyPrintOne=function(a,m,e){var h=document.createElement("PRE");h.innerHTML=a;e&&D(h,e);E({g:m,i:e,h:h});return h.innerHTML};window.prettyPrint=function(a){function m(){for(var e=window.PR_SHOULD_USE_CONTINUATION?l.now()+250:Infinity;p<h.length&&l.now()<e;p++){var n=h[p],k=n.className;if(k.indexOf("prettyprint")>=0){var k=k.match(g),f,b;if(b=
!k){b=n;for(var o=void 0,c=b.firstChild;c;c=c.nextSibling)var i=c.nodeType,o=i===1?o?b:c:i===3?N.test(c.nodeValue)?b:o:o;b=(f=o===b?void 0:o)&&"CODE"===f.tagName}b&&(k=f.className.match(g));k&&(k=k[1]);b=!1;for(o=n.parentNode;o;o=o.parentNode)if((o.tagName==="pre"||o.tagName==="code"||o.tagName==="xmp")&&o.className&&o.className.indexOf("prettyprint")>=0){b=!0;break}b||((b=(b=n.className.match(/\blinenums\b(?::(\d+))?/))?b[1]&&b[1].length?+b[1]:!0:!1)&&D(n,b),d={g:k,h:n,i:b},E(d))}}p<h.length?setTimeout(m,
250):a&&a()}for(var e=[document.getElementsByTagName("pre"),document.getElementsByTagName("code"),document.getElementsByTagName("xmp")],h=[],k=0;k<e.length;++k)for(var t=0,s=e[k].length;t<s;++t)h.push(e[k][t]);var e=q,l=Date;l.now||(l={now:function(){return+new Date}});var p=0,d,g=/\blang(?:uage)?-([\w.]+)(?!\S)/;m()};window.PR={createSimpleLexer:x,registerLangHandler:k,sourceDecorator:u,PR_ATTRIB_NAME:"atn",PR_ATTRIB_VALUE:"atv",PR_COMMENT:"com",PR_DECLARATION:"dec",PR_KEYWORD:"kwd",PR_LITERAL:"lit",
PR_NOCODE:"nocode",PR_PLAIN:"pln",PR_PUNCTUATION:"pun",PR_SOURCE:"src",PR_STRING:"str",PR_TAG:"tag",PR_TYPE:"typ"}})();

View File

@ -0,0 +1,358 @@
@font-face {
font-family: 'Open Sans';
font-weight: normal;
font-style: normal;
src: url('../fonts/OpenSans-Regular-webfont.eot');
src:
local('Open Sans'),
local('OpenSans'),
url('../fonts/OpenSans-Regular-webfont.eot?#iefix') format('embedded-opentype'),
url('../fonts/OpenSans-Regular-webfont.woff') format('woff'),
url('../fonts/OpenSans-Regular-webfont.svg#open_sansregular') format('svg');
}
@font-face {
font-family: 'Open Sans Light';
font-weight: normal;
font-style: normal;
src: url('../fonts/OpenSans-Light-webfont.eot');
src:
local('Open Sans Light'),
local('OpenSans Light'),
url('../fonts/OpenSans-Light-webfont.eot?#iefix') format('embedded-opentype'),
url('../fonts/OpenSans-Light-webfont.woff') format('woff'),
url('../fonts/OpenSans-Light-webfont.svg#open_sanslight') format('svg');
}
html
{
overflow: auto;
background-color: #fff;
font-size: 14px;
}
body
{
font-family: 'Open Sans', sans-serif;
line-height: 1.5;
color: #4d4e53;
background-color: white;
}
a, a:visited, a:active {
color: #0095dd;
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
header
{
display: block;
padding: 0px 4px;
}
tt, code, kbd, samp {
font-family: Consolas, Monaco, 'Andale Mono', monospace;
}
.class-description {
font-size: 130%;
line-height: 140%;
margin-bottom: 1em;
margin-top: 1em;
}
.class-description:empty {
margin: 0;
}
#main {
float: left;
width: 70%;
}
article dl {
margin-bottom: 40px;
}
article img {
max-width: 100%;
}
section
{
display: block;
background-color: #fff;
padding: 12px 24px;
border-bottom: 1px solid #ccc;
margin-right: 30px;
}
.variation {
display: none;
}
.signature-attributes {
font-size: 60%;
color: #aaa;
font-style: italic;
font-weight: lighter;
}
nav
{
display: block;
float: right;
margin-top: 28px;
width: 30%;
box-sizing: border-box;
border-left: 1px solid #ccc;
padding-left: 16px;
}
nav ul {
font-family: 'Lucida Grande', 'Lucida Sans Unicode', arial, sans-serif;
font-size: 100%;
line-height: 17px;
padding: 0;
margin: 0;
list-style-type: none;
}
nav ul a, nav ul a:visited, nav ul a:active {
font-family: Consolas, Monaco, 'Andale Mono', monospace;
line-height: 18px;
color: #4D4E53;
}
nav h3 {
margin-top: 12px;
}
nav li {
margin-top: 6px;
}
footer {
display: block;
padding: 6px;
margin-top: 12px;
font-style: italic;
font-size: 90%;
}
h1, h2, h3, h4 {
font-weight: 200;
margin: 0;
}
h1
{
font-family: 'Open Sans Light', sans-serif;
font-size: 48px;
letter-spacing: -2px;
margin: 12px 24px 20px;
}
h2, h3.subsection-title
{
font-size: 30px;
font-weight: 700;
letter-spacing: -1px;
margin-bottom: 12px;
}
h3
{
font-size: 24px;
letter-spacing: -0.5px;
margin-bottom: 12px;
}
h4
{
font-size: 18px;
letter-spacing: -0.33px;
margin-bottom: 12px;
color: #4d4e53;
}
h5, .container-overview .subsection-title
{
font-size: 120%;
font-weight: bold;
letter-spacing: -0.01em;
margin: 8px 0 3px 0;
}
h6
{
font-size: 100%;
letter-spacing: -0.01em;
margin: 6px 0 3px 0;
font-style: italic;
}
table
{
border-spacing: 0;
border: 0;
border-collapse: collapse;
}
td, th
{
border: 1px solid #ddd;
margin: 0px;
text-align: left;
vertical-align: top;
padding: 4px 6px;
display: table-cell;
}
thead tr
{
background-color: #ddd;
font-weight: bold;
}
th { border-right: 1px solid #aaa; }
tr > th:last-child { border-right: 1px solid #ddd; }
.ancestors, .attribs { color: #999; }
.ancestors a, .attribs a
{
color: #999 !important;
text-decoration: none;
}
.clear
{
clear: both;
}
.important
{
font-weight: bold;
color: #950B02;
}
.yes-def {
text-indent: -1000px;
}
.type-signature {
color: #aaa;
}
.name, .signature {
font-family: Consolas, Monaco, 'Andale Mono', monospace;
}
.details { margin-top: 14px; border-left: 2px solid #DDD; }
.details dt { width: 120px; float: left; padding-left: 10px; padding-top: 6px; }
.details dd { margin-left: 70px; }
.details ul { margin: 0; }
.details ul { list-style-type: none; }
.details li { margin-left: 30px; padding-top: 6px; }
.details pre.prettyprint { margin: 0 }
.details .object-value { padding-top: 0; }
.description {
margin-bottom: 1em;
margin-top: 1em;
}
.code-caption
{
font-style: italic;
font-size: 107%;
margin: 0;
}
.source
{
border: 1px solid #ddd;
width: 80%;
overflow: auto;
}
.prettyprint.source {
width: inherit;
}
.source code
{
font-size: 100%;
line-height: 18px;
display: block;
padding: 4px 12px;
margin: 0;
background-color: #fff;
color: #4D4E53;
}
.prettyprint code span.line
{
display: inline-block;
}
.prettyprint.linenums
{
padding-left: 70px;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
.prettyprint.linenums ol
{
padding-left: 0;
}
.prettyprint.linenums li
{
border-left: 3px #ddd solid;
}
.prettyprint.linenums li.selected,
.prettyprint.linenums li.selected *
{
background-color: lightyellow;
}
.prettyprint.linenums li *
{
-webkit-user-select: text;
-moz-user-select: text;
-ms-user-select: text;
user-select: text;
}
.params .name, .props .name, .name code {
color: #4D4E53;
font-family: Consolas, Monaco, 'Andale Mono', monospace;
font-size: 100%;
}
.params td.description > p:first-child,
.props td.description > p:first-child
{
margin-top: 0;
padding-top: 0;
}
.params td.description > p:last-child,
.props td.description > p:last-child
{
margin-bottom: 0;
padding-bottom: 0;
}
.disabled {
color: #454545;
}

View File

@ -0,0 +1,111 @@
/* JSDoc prettify.js theme */
/* plain text */
.pln {
color: #000000;
font-weight: normal;
font-style: normal;
}
/* string content */
.str {
color: #006400;
font-weight: normal;
font-style: normal;
}
/* a keyword */
.kwd {
color: #000000;
font-weight: bold;
font-style: normal;
}
/* a comment */
.com {
font-weight: normal;
font-style: italic;
}
/* a type name */
.typ {
color: #000000;
font-weight: normal;
font-style: normal;
}
/* a literal value */
.lit {
color: #006400;
font-weight: normal;
font-style: normal;
}
/* punctuation */
.pun {
color: #000000;
font-weight: bold;
font-style: normal;
}
/* lisp open bracket */
.opn {
color: #000000;
font-weight: bold;
font-style: normal;
}
/* lisp close bracket */
.clo {
color: #000000;
font-weight: bold;
font-style: normal;
}
/* a markup tag name */
.tag {
color: #006400;
font-weight: normal;
font-style: normal;
}
/* a markup attribute name */
.atn {
color: #006400;
font-weight: normal;
font-style: normal;
}
/* a markup attribute value */
.atv {
color: #006400;
font-weight: normal;
font-style: normal;
}
/* a declaration */
.dec {
color: #000000;
font-weight: bold;
font-style: normal;
}
/* a variable name */
.var {
color: #000000;
font-weight: normal;
font-style: normal;
}
/* a function name */
.fun {
color: #000000;
font-weight: bold;
font-style: normal;
}
/* Specify class=linenums on a pre to get line numbering */
ol.linenums {
margin-top: 0;
margin-bottom: 0;
}

View File

@ -0,0 +1,132 @@
/* Tomorrow Theme */
/* Original theme - https://github.com/chriskempson/tomorrow-theme */
/* Pretty printing styles. Used with prettify.js. */
/* SPAN elements with the classes below are added by prettyprint. */
/* plain text */
.pln {
color: #4d4d4c; }
@media screen {
/* string content */
.str {
color: #718c00; }
/* a keyword */
.kwd {
color: #8959a8; }
/* a comment */
.com {
color: #8e908c; }
/* a type name */
.typ {
color: #4271ae; }
/* a literal value */
.lit {
color: #f5871f; }
/* punctuation */
.pun {
color: #4d4d4c; }
/* lisp open bracket */
.opn {
color: #4d4d4c; }
/* lisp close bracket */
.clo {
color: #4d4d4c; }
/* a markup tag name */
.tag {
color: #c82829; }
/* a markup attribute name */
.atn {
color: #f5871f; }
/* a markup attribute value */
.atv {
color: #3e999f; }
/* a declaration */
.dec {
color: #f5871f; }
/* a variable name */
.var {
color: #c82829; }
/* a function name */
.fun {
color: #4271ae; } }
/* Use higher contrast and text-weight for printable form. */
@media print, projection {
.str {
color: #060; }
.kwd {
color: #006;
font-weight: bold; }
.com {
color: #600;
font-style: italic; }
.typ {
color: #404;
font-weight: bold; }
.lit {
color: #044; }
.pun, .opn, .clo {
color: #440; }
.tag {
color: #006;
font-weight: bold; }
.atn {
color: #404; }
.atv {
color: #060; } }
/* Style */
/*
pre.prettyprint {
background: white;
font-family: Consolas, Monaco, 'Andale Mono', monospace;
font-size: 12px;
line-height: 1.5;
border: 1px solid #ccc;
padding: 10px; }
*/
/* Specify class=linenums on a pre to get line numbering */
ol.linenums {
margin-top: 0;
margin-bottom: 0; }
/* IE indents via margin-left */
li.L0,
li.L1,
li.L2,
li.L3,
li.L4,
li.L5,
li.L6,
li.L7,
li.L8,
li.L9 {
/* */ }
/* Alternate shading for lines */
li.L1,
li.L3,
li.L5,
li.L7,
li.L9 {
/* */ }

View File

@ -19,6 +19,7 @@
"@types/sinon": "^7.5.2",
"@typescript-eslint/eslint-plugin": "^2.23.0",
"@typescript-eslint/parser": "^2.23.0",
"better-docs": "^2.3.2",
"docsify": "^4.11.2",
"docsify-cli": "^4.4.0",
"docsify-tools": "^1.0.20",
@ -28,16 +29,15 @@
"eslint-plugin-import": "^2.20.1",
"eslint-plugin-prettier": "^3.1.2",
"glob": "^7.1.6",
"jsdoc": "^3.6.7",
"prettier": "^2.4.1",
"promake": "^3.1.3",
"sinon": "^9.0.1",
"standard-version": "^8.0.0",
"tap": "15",
"tsd": "^0.11.0",
"typedoc": "0.17.7",
"typedoc-plugin-markdown": "^2.2.17",
"typescript": "^4.2.0",
"uvu": "^0.5.1"
"tsd": "^0.17.0",
"typedoc": "0.22.5",
"typescript": "^4.4.3"
},
"license": "MIT",
"main": "dist/index.js",

13
pagesconfig.json Normal file
View File

@ -0,0 +1,13 @@
{
"groups": [
{
"title": "Documentation",
"pages": [
{
"title": "Blah",
"source": "./tutorials/tutorial.md"
}
]
}
]
}

20
src/Updux.d.ts vendored Normal file
View File

@ -0,0 +1,20 @@
type UpduxConfig<TState> = Partial<{
initial: TState;
subduxes: Record<string,any>;
actions: Record<string, any>;
selectors: Record<string, Function>;
mutations: Record<string, Function>;
mappedSelectors: Record<string,Function>;
effects: Record<string,Function>;
reactions: Record<string,Function>;
mappedReaction: Function;
}>
export class Updux<TState=unknown> {
constructor( config: UpduxConfig<TState> );
get initial(): TState;
get selectors(): unknown;
}

View File

@ -14,13 +14,6 @@ import {
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 = {};
@ -56,13 +49,12 @@ export class Updux {
}
this.#selectors = config.selectors ?? {};
this.#splatSelector = config.splatSelector;
this.#mutations = config.mutations ?? {};
this.#splatSelector = config.splatSelector;
Object.keys(this.#mutations)
.filter((action) => action !== '*')
.filter((action) => action !== '+')
.filter((action) => !this.actions.hasOwnProperty(action))
.forEach((action) => {
throw new Error(`action '${action}' is not defined`);
@ -100,7 +92,7 @@ export class Updux {
);
}
/** @return { import('./Updux').What } */
/** @member { unknown } */
get initial() {
return this.#memoInitial(this.#initial, this.#subduxes);
}
@ -136,7 +128,7 @@ export class Updux {
this.#subscriptions = [...this.#subscriptions, subscription];
}
addAction(type, payloadFunc) {
setAction(type, payloadFunc) {
const theAction = action(type, payloadFunc);
this.#actions = { ...this.#actions, [type]: theAction };
@ -144,7 +136,8 @@ export class Updux {
return theAction;
}
addSelector(name, func) {
setSelector(name, func) {
// TODO selector already exists? Complain!
this.#selectors = {
...this.#selectors,
[name]: func,
@ -152,7 +145,7 @@ export class Updux {
return func;
}
addMutation(name, mutation) {
setMutation(name, mutation) {
if (typeof name === 'function') name = name.type;
this.#mutations = {
@ -160,7 +153,7 @@ export class Updux {
[name]: mutation,
};
return this;
return mutation;
}
addEffect(action, effect) {
@ -312,6 +305,3 @@ export class Updux {
return store;
}
}
const x = new Updux();
x.selectors;

View File

@ -45,13 +45,13 @@ test('basic actions', async (t) => {
t.same(Object.keys(dux.actions).sort(), ['bar', 'foo']);
});
test('addAction', async (t) => {
test('setAction', async (t) => {
const dux = new Updux({
actions: {
bar: action('bar'),
},
});
dux.addAction('foo');
dux.setAction('foo');
t.same(Object.keys(dux.actions).sort(), ['bar', 'foo']);
});
@ -74,14 +74,14 @@ test('basic selectors', async (t) => {
getBar: ({ bar }) => bar,
},
});
dux.addSelector('getFoo', ({ foo }) => foo);
dux.addSelector(
dux.setSelector('getFoo', ({ foo }) => foo);
dux.setSelector(
'getAdd',
({ foo }) =>
(add) =>
add + foo
);
dux.addAction('stuff');
dux.setAction('stuff');
t.equal(dux.selectors.getBar({ bar: 3 }), 3);
t.equal(dux.selectors.getFoo({ foo: 3 }), 3);
@ -100,8 +100,8 @@ test('mutations', async (t) => {
const alpha = new Updux({
initial: { quux: 3 },
});
alpha.addAction('add');
alpha.addMutation('add', (toAdd) => (state) => ({
alpha.setAction('add');
alpha.setMutation('add', (toAdd) => (state) => ({
quux: state.quux + toAdd,
}));
@ -112,12 +112,12 @@ test('mutations', async (t) => {
},
subduxes: { alpha },
});
dux.addAction('subtract');
dux.addMutation('add', (toAdd) => (state) => ({
dux.setAction('subtract');
dux.setMutation('add', (toAdd) => (state) => ({
...state,
foo: state.foo + toAdd,
}));
dux.addMutation('subtract', (toSubtract) => (state) => ({
dux.setMutation('subtract', (toSubtract) => (state) => ({
...state,
bar: state.bar - toSubtract,
}));

View File

@ -26,7 +26,7 @@ export function buildUpreducer(initial, mutations, subduxes = {}) {
}
}
const a = mutations[action.type] || mutations['*'];
const a = mutations[action.type] || mutations['+'];
if (!a) return newState;

50
src/documentation.test.js Normal file
View File

@ -0,0 +1,50 @@
import { test } from 'tap';
import u from 'updeep';
import add from 'lodash/fp/add.js';
import { Updux } from './index.js';
test('README.md', async (t) => {
const otherDux = new Updux({});
const dux = new Updux({
initial: {
counter: 0,
},
actions: {
inc: null,
},
subduxes: {
otherDux,
},
});
dux.setMutation('inc', (increment) => u({ counter: add(increment) }));
dux.addEffect('*', (api) => (next) => (action) => {
next(action);
});
const store = dux.createStore();
store.dispatch.inc(1);
t.equal(store.getState().counter, 1);
});
test('tutorial', async (t) => {
const todosDux = new Updux({
initial: {
next_id: 1,
todos: [],
}
});
todosDux.setAction( 'addTodo' );
todosDux.setAction( 'todoDone' );
t.same( todosDux.actions.addTodo('write tutorial') , { type: 'addTodo', payload: 'write tutorial' });
})

1
src/index.d.ts vendored Normal file
View File

@ -0,0 +1 @@
export { Updux };

2
src/index.js Normal file
View File

@ -0,0 +1,2 @@
export { Updux } from './Updux.js';
export { action } from './actions.js';

View File

@ -27,7 +27,7 @@ test('override', async (t) => {
const dux = new Updux({
initial: { alpha: [] },
mutations: {
'*': (payload, action) => (state) => ({
'+': (payload, action) => (state) => ({
...state,
alpha: [...state.alpha, action.type],
}),
@ -81,7 +81,7 @@ test('order of processing', async (t) => {
t.same(dux.reducer(undefined, foo()), { x: ['subdux', 'main'] });
});
test('addMutation', async (t) => {
test('setMutation', async (t) => {
const foo = action('foo');
const dux = new Updux({
@ -90,13 +90,13 @@ test('addMutation', async (t) => {
t.equal(dux.reducer(undefined, foo()), '', 'noop');
dux.addMutation('foo', () => () => 'foo');
dux.setMutation('foo', () => () => 'foo');
t.equal(dux.reducer(undefined, foo()), 'foo', 'foo was added');
await t.test('name as function', async (t) => {
const bar = action('bar');
dux.addMutation(bar, () => () => 'bar');
dux.setMutation(bar, () => () => 'bar');
t.equal(dux.reducer(undefined, bar()), 'bar', 'bar was added');
});

74
tsconfig.json Normal file
View File

@ -0,0 +1,74 @@
{
"include": ["./types/*.d.ts"],
"exclude": [ "types/index.test-d.ts" ],
"compilerOptions": {
/* Visit https://aka.ms/tsconfig.json to read more about this file */
/* Basic Options */
"incremental": false, /* Enable incremental compilation */
"target": "es5", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', 'ES2021', or 'ESNEXT'. */
"module": "ESNext", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */
// "lib": [], /* Specify library files to be included in the compilation. */
"allowJs": true, /* Allow javascript files to be compiled. */
// "checkJs": true, /* Report errors in .js files. */
// "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', 'react', 'react-jsx' or 'react-jsxdev'. */
"declaration": true, /* Generates corresponding '.d.ts' file. */
// "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */
// "sourceMap": true, /* Generates corresponding '.map' file. */
// "outFile": "./", /* Concatenate and emit output to single file. */
"outDir": "./ts-out", /* Redirect output structure to the directory. */
// "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
// "composite": true, /* Enable project compilation */
// "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */
// "removeComments": true, /* Do not emit comments to output. */
"noEmit": true, /* Do not emit outputs. */
// "importHelpers": true, /* Import emit helpers from 'tslib'. */
// "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */
// "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */
/* Strict Type-Checking Options */
"strict": true, /* Enable all strict type-checking options. */
// "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */
// "strictNullChecks": true, /* Enable strict null checks. */
// "strictFunctionTypes": true, /* Enable strict checking of function types. */
// "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */
// "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */
// "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */
// "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */
/* Additional Checks */
// "noUnusedLocals": true, /* Report errors on unused locals. */
// "noUnusedParameters": true, /* Report errors on unused parameters. */
// "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
// "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
// "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */
// "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an 'override' modifier. */
// "noPropertyAccessFromIndexSignature": true, /* Require undeclared properties from index signatures to use element accesses. */
/* Module Resolution Options */
"moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
// "baseUrl": "./", /* Base directory to resolve non-absolute module names. */
// "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
"rootDirs": ["./types"], /* List of root folders whose combined content represents the structure of the project at runtime. */
"typeRoots": ["./types"], /* List of folders to include type definitions from. */
// "types": [], /* Type declaration files to be included in compilation. */
// "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
"esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
// "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
/* Source Map Options */
// "sourceRoot": "./types", /* Specify the location where debugger should locate TypeScript files instead of source locations. */
// "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
// "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */
// "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */
/* Experimental Options */
// "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */
// "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */
/* Advanced Options */
"skipLibCheck": true, /* Skip type checking of declaration files. */
"forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */
}
}

15
typedoc.json Normal file
View File

@ -0,0 +1,15 @@
{
"name": "Updux",
"entryPoints": [
"./types/index.d.ts"
],
"out": "docs/API",
"excludeExternals": true,
"excludePrivate": true,
"excludeProtected": true,
"disableSources": true,
"listInvalidSymbolLinks": true,
"markedOptions": {
"mangle": false
}
}

111
types/index.d.ts vendored Normal file
View File

@ -0,0 +1,111 @@
type Dict<T> = Record<string, T>;
type Mutation<TState = unknown> = (
payload: unknown,
action: Record<string, unknown>
) => (state: TState) => TState;
type ActionGenerator = { type: string } & ((...args: any[]) => {
type: string;
payload?: unknown;
});
declare module 'updux' {
/**
* Configuration object typically passed to the constructor of the class Updux.
*/
export interface UpduxConfig<TState = unknown> {
/**
* Local initial state.
* @default {}
*/
initial?: TState;
/**
* Subduxes to be merged to this dux.
*/
subduxes?: Dict<Updux | UpduxConfig>;
/**
* Local actions.
*/
actions?: Record<string, any>;
/**
* Local selectors.
*/
selectors?: Record<string, Function>;
/**
* Local mutations
*/
mutations?: Record<string, Function>;
/**
* Selectors to apply to the mapped subduxes. Only
* applicable if the dux is a mapping dux.
*/
mappedSelectors?: Record<string, Function>;
/**
* Local effects.
*/
effects?: Record<string, Function>;
/**
* Local reactions.
*/
reactions?: Record<string, Function>;
/**
* If true, enables mapped reactions. Additionally, it can be
* a reaction function, which will treated as a regular
* reaction for the mapped dux.
*/
mappedReaction?: Function | boolean;
}
export class Updux<TState = unknown> {
constructor(config: Partial<UpduxConfig<TState>>);
get initial(): TState;
get selectors(): unknown;
/**
* Sets the local mutation for the given action.
*
* The action can be specified via its type
* or its generator.
*
* Returns the same mutation function.
*/
setMutation<TMutation extends Mutation>(
actionType: string,
mutation: TMutation
): TMutation;
setMutation<TMutation extends Mutation>(
action: ActionGenerator,
mutation: TMutation
): TMutation;
/**
* Registers the action for the dux.
* If no payload function is provided, whatever is
* given as an argument to the action generator will
* be set as-is in the action's payload.
*/
setAction(actionType: string, payloadFunc?: Function);
/**
* Registers a selector for the dux.
*/
setSelector(name: string, selector: Selector);
}
/**
* Creates an action generator.
*/
export function action(
actionType: string,
payloadFunction?: Function
): ActionGenerator;
}

14
types/index.test-d.ts Normal file
View File

@ -0,0 +1,14 @@
import { printType, expectAssignable, expectType } from 'tsd';
import { Updux } from '.';
const dux = new Updux({});
expectType<unknown>( dux.initial );
() => {
const dux = new Updux<{a: string}>({});
expectAssignable<{initial: {a:string}}>(dux);
}

Some files were not shown because too many files have changed in this diff Show More