Comparing version 0.4.0 to 0.5.0
@@ -0,0 +0,0 @@ Copyright © 2018-present Wolfgang |
{ | ||
"name": "fxapp", | ||
"description": "Build JavaScript server apps using effects as data", | ||
"version": "0.4.0", | ||
"version": "0.5.0", | ||
"main": "./src/index.js", | ||
@@ -10,9 +10,9 @@ "files": [ | ||
"devDependencies": { | ||
"eslint": "=6.7.2", | ||
"jest": "=24.9.0", | ||
"nodemon": "=2.0.2", | ||
"prettier": "=1.19.1" | ||
"eslint": "=7.3.0", | ||
"jest": "=26.0.1", | ||
"nodemon": "=2.0.4", | ||
"prettier": "=2.0.5" | ||
}, | ||
"scripts": { | ||
"clean": "npx rimraf coverage dist node_modules", | ||
"clean": "npx --ignore-existing --quiet rimraf coverage dist node_modules", | ||
"format": "prettier --ignore-path .gitignore --write \"**/*.js\"", | ||
@@ -24,5 +24,10 @@ "format:check": "prettier --ignore-path .gitignore --list-different \"**/*.js\"", | ||
"check": "npm run format:check && npm run lint && npm t", | ||
"prepare": "npm run check", | ||
"release": "./pre-flight-tests && npm run clean && git pull && npm i && git tag $npm_package_version && git push && git push --tags && npm publish" | ||
"release:dry": "npm run clean && npm i && npm run check", | ||
"release": "node pre-flight-tests && npm run release:dry && git tag $npm_package_version && git push && git push --tags && npm publish" | ||
}, | ||
"prettier": { | ||
"endOfLine": "auto", | ||
"trailingComma": "none", | ||
"arrowParens": "avoid" | ||
}, | ||
"eslintConfig": { | ||
@@ -29,0 +34,0 @@ "extends": "eslint:recommended", |
@@ -67,2 +67,3 @@ # FX App | ||
// GET /path/other/123 | ||
// { request: { params: { id: "123" } } } | ||
$id: otherAction | ||
@@ -184,4 +185,6 @@ } | ||
```js | ||
StateUpdate = function(state: Object) => newState: Object | ||
StateUpdate = function(state: Object, props: Object) => newState: Object | ||
StateUpdateWithProps = [StateUpdate, props: Object] | ||
ReservedProps = { | ||
@@ -205,3 +208,5 @@ concurrent: boolean? = false, | ||
Dispatchable = StateUpdate | FX | [Dispatchable] | ||
FXWithProps = [FX, props: Object] | ||
Dispatchable = StateUpdate | StateUpdateWithProps | FX | FXWithProps | [Dispatchable] | ||
``` | ||
@@ -211,11 +216,11 @@ | ||
Perform an immutable state update by receiving the current state as a parameter and returning the new state. Automatically shallow merges root properties in addition to one level under `request` and `response`. | ||
Perform an immutable state update by receiving the current state as a parameter and returning the new state. Automatically shallow merges root properties in addition to one level under `request` and `response`. Optional `props` may be passed at the time of dispatch using a tuple represented as an array of `[stateMapping, props]`. | ||
### FX | ||
_FX as data_ are represented with an object containing a `run` function and additional properties that will be passed to that function. | ||
_FX as data_ are represented with an object containing a `run` function and properties that will be passed to that function. Props may also optionally be passed at the time of dispatch using a tuple represented as an array of `[fx, props]`, which will be merged with the props defined on the FX object. Props passed during dispatch will override those defined on the FX when the same key is used. | ||
The `run` function returns a `Promise` if the effect is async. Async FX are considered still running until resolved or rejected. Otherwise FX are considered sync and done once the `run` function returns. | ||
Some `props` are reserved and have special meaning: | ||
Some props are reserved and have special meaning: | ||
@@ -222,0 +227,0 @@ #### `concurrent` |
@@ -1,2 +0,2 @@ | ||
const { isFn, isArray, isFx, isObj, assign } = require("./utils"); | ||
const { isFn, isArray, isFx, isObj, isPropsTuple, assign } = require("./utils"); | ||
@@ -15,31 +15,33 @@ const makeQueue = (queue = []) => ({ | ||
} = {}) => { | ||
const concurrentFxWaiting = new Set(); | ||
const concurrentFxWithPropsWaiting = new Set(); | ||
const concurrentFxRunning = new Set(); | ||
const serialFxQueue = makeQueue(); | ||
const afterFxQueue = makeQueue(); | ||
const serialFxWithPropsQueue = makeQueue(); | ||
const afterFxWithPropsQueue = makeQueue(); | ||
const runningFx = new Set(); | ||
let state = initialState; | ||
const runFx = fx => { | ||
const runFxWithProps = ([fx, dispatchProps]) => { | ||
if (isFn(fx)) { | ||
dispatch(fx(state)); | ||
dispatch(fx(state, dispatchProps)); | ||
process.nextTick(fxLoop); | ||
return Promise.resolve(); | ||
} | ||
const props = mapProps(fx); | ||
runningFx.add(fx); | ||
const dispatchProxy = dispatched => { | ||
const dispatchProxy = (dispatched, props) => { | ||
if (runningFx.has(fx)) { | ||
dispatch(dispatched); | ||
dispatch(dispatched, props); | ||
} | ||
}; | ||
const fxPromise = | ||
fx.run(assign(props, { dispatch: dispatchProxy })) || Promise.resolve(); | ||
const runtimeProps = mapProps( | ||
assign(fx, dispatchProps, { dispatch: dispatchProxy }) | ||
); | ||
runningFx.add(fx); | ||
const fxPromise = fx.run(runtimeProps) || Promise.resolve(); | ||
return new Promise(resolve => { | ||
const done = () => { | ||
runningFx.delete(fx); | ||
if (props.cancel) { | ||
if (runtimeProps.cancel) { | ||
runningFx.clear(); | ||
concurrentFxWaiting.clear(); | ||
serialFxQueue.clear(); | ||
concurrentFxWithPropsWaiting.clear(); | ||
serialFxWithPropsQueue.clear(); | ||
} | ||
@@ -54,14 +56,15 @@ process.nextTick(fxLoop); | ||
const fxLoop = () => { | ||
for (const fx of concurrentFxWaiting) { | ||
concurrentFxWaiting.delete(fx); | ||
runFx(fx).then(() => { | ||
concurrentFxRunning.delete(fx); | ||
for (const fxWithProps of concurrentFxWithPropsWaiting) { | ||
const [concurrentFx] = fxWithProps; | ||
concurrentFxWithPropsWaiting.delete(fxWithProps); | ||
runFxWithProps(fxWithProps).then(() => { | ||
concurrentFxRunning.delete(concurrentFx); | ||
}); | ||
concurrentFxRunning.add(fx); | ||
concurrentFxRunning.add(concurrentFx); | ||
} | ||
if (concurrentFxRunning.size === 0 && runningFx.size === 0) { | ||
if (serialFxQueue.notEmpty()) { | ||
runFx(serialFxQueue.dequeue()); | ||
} else if (afterFxQueue.notEmpty()) { | ||
runFx(afterFxQueue.dequeue()); | ||
if (serialFxWithPropsQueue.notEmpty()) { | ||
runFxWithProps(serialFxWithPropsQueue.dequeue()); | ||
} else if (afterFxWithPropsQueue.notEmpty()) { | ||
runFxWithProps(afterFxWithPropsQueue.dequeue()); | ||
} | ||
@@ -71,17 +74,21 @@ } | ||
const dispatch = dispatched => { | ||
const dispatch = (dispatched, props) => { | ||
if (isFn(dispatched)) { | ||
serialFxQueue.enqueue(dispatched); | ||
serialFxWithPropsQueue.enqueue([dispatched, props]); | ||
process.nextTick(fxLoop); | ||
} else if (isArray(dispatched)) { | ||
dispatched.forEach(dispatch); | ||
if (isPropsTuple(dispatched)) { | ||
dispatch(dispatched[0], dispatched[1]); | ||
} else { | ||
dispatched.forEach(dispatch); | ||
} | ||
} else if (isFx(dispatched)) { | ||
if (dispatched.cancel) { | ||
runFx(dispatched); | ||
runFxWithProps([dispatched, props]); | ||
} else if (dispatched.concurrent) { | ||
concurrentFxWaiting.add(dispatched); | ||
concurrentFxWithPropsWaiting.add([dispatched, props]); | ||
} else if (dispatched.after) { | ||
afterFxQueue.enqueue(dispatched); | ||
afterFxWithPropsQueue.enqueue([dispatched, props]); | ||
} else { | ||
serialFxQueue.enqueue(dispatched); | ||
serialFxWithPropsQueue.enqueue([dispatched, props]); | ||
} | ||
@@ -88,0 +95,0 @@ process.nextTick(fxLoop); |
const makeServer = ({ httpApi, port, serverRuntime }) => | ||
new Promise((resolve, reject) => | ||
httpApi(serverRuntime) | ||
.listen({ port }, resolve) | ||
.on("error", reject) | ||
httpApi(serverRuntime).listen({ port }, resolve).on("error", reject) | ||
); | ||
module.exports = makeServer; |
const isArray = Array.isArray; | ||
const isFn = value => typeof value === "function"; | ||
const isObj = value => value && typeof value === "object" && !isArray(value); | ||
const isPropsTuple = value => | ||
value.length === 2 && | ||
(isFn(value[0]) || isFx(value[0])) && | ||
isObj(value[1]) && | ||
!isFx(value[1]); | ||
const isFx = value => isObj(value) && isFn(value.run); | ||
@@ -12,4 +17,5 @@ | ||
isObj, | ||
isPropsTuple, | ||
isFx, | ||
assign | ||
}; |
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
22975
363
256
1