
Security News
Next.js Patches Critical Middleware Vulnerability (CVE-2025-29927)
Next.js has patched a critical vulnerability (CVE-2025-29927) that allowed attackers to bypass middleware-based authorization checks in self-hosted apps.
Do something when the conditions are right and cancel them if they're not.
Do something when the conditions are right and cancel them if they're not.
Say you want to connect to a socket when you’re logged in and want to disconnect when you’re not. This can only work once the socket url is known. Also it must be disconnected when the url is changed and connect to the new one.
Here's how you can do this with raincheck:
import { when } from 'raincheck'
const doConnectWhen = when().do((url) => {
const socket = new WebSocket()
socket.connect(url)
return () => socket.close()
})
doConnectWhen("ws://url1") // Will open the connection
doConnectWhen("ws://url2") // Will close the first connection and open an new one
doConnectWhen(false) // Will close the second connection (can be false, null or undefined)
Say you want to keep multiple connections open:
import { forEach } from 'raincheck'
const doConnectForEach = forEach().do((url) => {
const socket = new WebSocket()
socket.connect(url)
return () => socket.close()
})
doConnectForEach(['ws://url1', 'ws://url2']) // Opens 2 socket connections
doConnectForEach(['ws://url2']) // Will close the first socket
doConnectForEach([]) // Will close the second socket
The function passed into do
starts a process and returns a cancel function.
Raincheck consists of 3 function:
when
: For single objectsforEach
: For an array of objectsforEachEntry
: For an key-value based objectsEach function accepts a map function a settings object or both:
// Map function
when(s => s.value)
.do(handleFnc)
// Settings
when({
do: handleFnc
})
// Map function & settings
when(s => s.value, {
do: handleFnc
})
To identify that an object has been changed, raincheck uses keys, just like react.
when
will always cancel & call do when a object is changed, but when you add keyExtractor
it will call changed
when the key is the same as the one before.
const check = when({
keyExtractor: item => `${item.id}+${item.url}`,
do: (item) => connectToSocket(item.url),
changed: (newValue, oldValue, key) => {
// When an object with the same key has changed
}
})
check({ url: 'url1', name: '1', id: '1' }) // Will only call do
check({ url: 'url1', name: '2', id: '1' }) // Will only call changed
When you pass in an array to forEach
it will use the value as key. But if the objects are not strings you may use the keyExtractor
:
const check = forEach({
keyExtractor: item => `${item.id}+${item.url}`,
do: (item) => connectToSocket(item.url),
changed: (newValue, oldValue, key) => {
// When an object with the same key has changed
}
})
check([{ url: 'url1', name: '1', id: '1' }]) // Will only call do
check([{ url: 'url1', name: '2', id: '1' }]) // Will only call changed
With forEachEntry
the key of the object is used, so no keyExtractor is needed:
const check = forEachEntry({
do: (item) => connectToSocket(item.url),
changed: (newValue, oldValue, key) => {
// When an object with the same key has changed
}
})
check({'1': { url: '', name: '1' }}) // Will only call do
check({'1': { url: '', name: '2' }}) // Will only call changed
class Compontent extends React.Component {
doConnectWhen = when(props => props.isLoggedIn && props.url, {
do: (url) => {
// do connect
}
})
componentDidMount() {
this.doConnectWhen(this.props)
}
componentDidUpdate() {
this.doConnectWhen(this.props)
}
componentDidUnmount() {
this.doConnectWhen(null)
}
render () {
// ..
}
}
Raincheck
is particularly great when used with Redux.
To connect to a store you use createMiddleware()
, like so:
export default createMiddleware(
when(state => state.login.isLoggedIn && state.socket.url)
.do(connectToSocket)
)
Or just call it yourself inside a middleware:
export default (store) => {
let check = forEach((state) => state.sockets, {
do: item => connectToSocket(item.url),
keyExtractor: item => `${item.id}+${item.url}`,
})
(next) => (action) => {
let ret = next(action)
check(store.getState())
return ret
}
}
const selector = createSelector(
s => s.projects,
s => s.user,
(projects, user) => (projects && {projects, user}),
)
when(selector).do(({project, login}) => {
// Called when project is truthy & project & user is changed
})
Here an example to sync an array to PouchDb:
const check = forEach({
do: items => {
db.put(item)
return () => db.delete(item)
},
keyExtractor: item => item._id,
changed: (newValue, oldValue) => {
db.put(newValue)
}
})
With the mock()
function you can easily test your setup:
const connectToSocket = () => {}
const doConnectToSocketWhen = when(
({isLoggedIn, url}) => isLoggedIn && !!url
).do(connectToSocket)
it('should connect to socket', () => {
const listener = jest.fn()
const destruct = jest.fn()
let tester = doConnectToSocketWhen.mock(listener, destruct) // <--- Here's the magic :)
const url = "dfgh"
tester({
isLoggedIn: true,
url
})
expect(listener).toBeCalledWith(connectToSocket, url, expect.anything())
})
doForAll
, doForAllKeys
, doWhenTrue
& doWhenChanged
are deprecated and will be removed in 1.0.
doForAll
will be replaced by forEachEntity
doForAllKeys
will be replaced by forEach
, it doesn't have to be a string anymore the key is can now be extract with keyExtractor
Both doWhenTrue
& doWhenChanged
will be replace by when
.
Say the socket is disconnected and you fire up a timeout to reconnect. Whenever the conditions change the cancel function will be called. In this situation you don't want to call the socket.close(), but you do want to clear the timeout.
For this situation you can use the chaining API.
The second argument of the construct function contains the functions of the chaining API. This makes it possible to 'register' a new destruct function and opt-out of the current destruct function by using next()
. If the current action isn’t finished yet you can use branch()
.
See an example of loading an image after every second, it will cancel whatever process is active right now:
const getImage = (url, next, store, action) => {
const setTimer = ({next}) => {
let timer = setTimeout(() => next(sendRequest), 1000)
return () => clearTimeout(timer)
}
const loadImage = (result: string) => ({finish}) => {
// Load image for dimensions
let img = new Image()
img.onload = () => {
finish()
}
img.src = result
return () => img.src = ""
}
const readFile = (response) => ({next}) => {
let reader = new FileReader()
reader.onload = (e) => {
next(loadImage(reader.result))
}
reader.readAsDataURL(response)
return () => reader.abort()
}
const sendRequest = ({next, branch}) => {
let xhrreq = new XMLHttpRequest()
xhrreq.open('POST', url)
xhrreq.responseType = "blob";
xhrreq.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
xhrreq.onload = (ev) => {
if (xhrreq.status === 200) {
// Already start a timer
branch(setTimer)
// Start next
next(readFile(xhrreq.response))
}
}
xhrreq.send(body)
return () => xhrreq.abort()
}
return sendRequest(next)
}
createMiddleware(
when(state => state.image.isActive && state.image.url)
.do(getImage)
)
map
to apply to all entriesdoForAll
, doForAllKeys
, doWhenTrue
& doWhenChanged
branch
or fork
complete
, finish
or resolve
chain
or next
when
to send all parameters to do
instead of just the value of the selector?FAQs
Do something when the conditions are right and cancel them if they're not.
We found that raincheck demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
Next.js has patched a critical vulnerability (CVE-2025-29927) that allowed attackers to bypass middleware-based authorization checks in self-hosted apps.
Security News
A survey of 500 cybersecurity pros reveals high pay isn't enough—lack of growth and flexibility is driving attrition and risking organizational security.
Product
Socket, the leader in open source security, is now available on Google Cloud Marketplace for simplified procurement and enhanced protection against supply chain attacks.