
Research
Supply Chain Attack on Axios Pulls Malicious Dependency from npm
A supply chain attack on Axios introduced a malicious dependency, plain-crypto-js@4.2.1, published minutes earlier and absent from the project’s GitHub releases.
------ Main features: -------
------ Disclaimer: -----
// yarn add use-struct immer -D
import useStruct from 'use-struct'
import produce from 'immer'
const Calculator = useStruct({
// ======== state ======== //
val: {
greetings: 'Hello World from first struct! :)',
counter: 0,
},
// ======== selectors ======== //
get: ({ val, get }) => [{
addedCounter: additive => val.counter + additive,
multipliedCounter: multiplier => val.counter * multiplier,
}, [val.counter]],
// ======== reducer ======== //
set: ({ val, get }) => ({
counter: payload => produce(val, val => {
val.counter = payload
}),
increment: () => produce(val, val => {
val.counter++
}),
decrement: () => produce(val, val => {
val.counter--
}),
}),
// ======== callbacks ======== //
act: ({ val, get, set, act }) => [{
addBy: pld => set.add(pld),
subBy: pld => set.sub(pld),
sayHello: () => {
alert(val.greetings)
},
}, [val.counter]],
})
export default function Component() {
// like a class
const Calculator = useStruct({ ...inputs })
return (
<div>
{/* ======== STATE ======== */}
<h1>Greetings: {Calculator.greetings}</h1>
<h2>Current counter: {Calculator.counter}</h2>
<br />
{/* ======== SELECTORS ======== */}
<h3>Added counter by 3: {Calculator.get.addedCounter(3)}</h3><br />
<h3>Multiplied counter by 2: {Calculator.get.multipliedCounter(2)}</h3><br />
<br />
{/* ======== REDUCER ======== */}
<button onClick={() => Calculator.set.increment()} >Increment</button><br />
<button onClick={() => Calculator.set.decrement()} >Decrement</button><br />
<br />
{/* ======== CALLBACKS ======== */}
<button onClick={() => Calculator.addBy(4)} >Add counter by 4</button><br />
<button onClick={() => Calculator.subBy(2)} >Sub counter by 2</button><br />
</div>
)
}
// yarn add use-struct immer axios react-json-pretty -D
import useStruct from 'use-struct'
import produce from 'immer'
import axios from 'axios'
import RenderJson from 'react-json-pretty'
export default function Component() {
// ================ structs ================ //
const ForeignData = useStruct({
// ======== state ======== //
val: {
description: 'Fetch data from API',
data: [],
},
// ======== reducer ======== //
set: ({ val }) => ({
data: pld => produce(val, val => {
val.data.push(...pld)
})
}),
// ======== callbacks ======== //
act: ({ val, set }) => [{
fetchData: () => {
const call = axios.get('https://jsonplaceholder.typicode.com/todos')
call.then(({ data }) => {
set.data(data)
}).catch(error => {
console.error(error)
})
},
}, [val]]
})
// ================ render ================ //
return (
<div>
{/* ======== STATE ======== */}
<h1>Description: {ForeignData.description}</h1>
<RenderJson data={ForeignData.val} />
{/* ======== CALLBACKS ======== */}
<button onClick={() => ForeignData.fetchData()} >Fetch data</button>
</div>
)
}
use for hooks and efc for effects. **Such that constructor of a Class, the useStruct can invoke initial hooks too and provide them by prefix .use.
And even more, finally you can put side effects (such that useEffect) for react to changes of state.
Example:
export default function Componen() {
// ================ structs ================ //
const Calculator = useStruct({
// ======== hooks ======== //
// instance hooks here and call them by prefix .use
use() {
const [counter, setCounter] = useState(100)
return ({ counter, setCounter })
},
// ======== selectors ======== //
get: ({ use }) => [{
multipliedCounter: multiplier => use.counter * multiplier
}],
// ======== callbacks ======== //
act: ({ use }) => [{
increment() {
use.setCounter(x => x + 1)
}
}],
// ======== effects ======== //
// put the side effects here
efc({ use }) {
// print a log whenever the useState's counter is changed
useEffect(() => {
console.log('Counter changed. New value is:', use.counter)
}, [use.counter])
}
})
// ================ render ================ //
return (
<div>
{/* ======== HOOKS ======== */}
<h1>Counter: {Calculator.counter}</h1>
{/* ======== CALLBACKS ======== */}
<button onClick={() => Calculator.increment()} >increment</button>
</div>
)
}
./models and ./pages folders in the source code.Clone the repository and test it immediately with yarn dev in http://localhost:3000.
The library provides two extras resources:
<ScaffoldProvider /> component.useScaffold() hook."Scaffold" is just a alias for "Global Struct". These resoucers are a implementation of the <Context.Provider /> and useContext().
The value provided by <ScaffoldProvider /> is a root instance of the useStruct() hook. This instance can be recovered by any component in the application using useScaffold().
Implements it in 3 steps:
First, in the ./store folder, create a function that return a instance of useStruct() hook.
import useStruct from 'use-struct'
// the root struct
export default function Struct() {
return useStruct({
// ======== state ======== //
val: {
greetings: 'Hello World! :)'
},
})
}
Second. Wrap your application with the <ScaffoldProvider struct={struct} /> passing the function above for struct attribute: See:
import { ScaffoldProvider } from 'use-struct'
// the root struct
import struct from '../store/struct.js'
export default function ContainerApp() {
return (
<ScaffoldProvider struct={struct} >
<RootApp />
</ScaffoldProvider>
)
}
Third. Recovery the useStruct() instance in some component with the useScaffold().
import { useScaffold } from 'use-struct'
export default function SomeComponent() {
// the root struct
const Struct = useScaffold()
return (
<div>
<h1>Greetings: {Struct.greetings}</h1>
</div>
)
}
Such that useMemo and useCallback, selectors get and callbacks act can be memorized too. Just add an array of dependencies after definitions.
const Calculator = useStruct({
// ======== state ======== //
val: {
counter: 123456,
},
// ======== selectors ======== //
get: ({ val }) => [{
calculatedCounter: () => veryVerySlowFoo(val.counter),
}, [val.counter]], // all selectors will be recalculated whenever counter state changes.
// ======== callbacks ======== //
act: ({ get }) => [{
logCalculatedCounter: () => {
console.log('new value:'. val.greetings)
},
}, [get.calculatedCounter]], // all callbacks will be updated whenever that calculatedCounter selector changes.
})
If none dependencies array was added all selectors and callbacks will react at each change of state.
A struct can wrap another and all properties and methods of the wrapred structs will are avaliable inside and outside for the wraper struct. See:
const AdditionModule = useStruct({
// ======== state ======== //
val: {
description: 'This is the Addition Module',
counter: 0,
},
// ======== reducer ======== //
set: ({ val }) => ({
increment: () => produce(val, val => {
val.counter++
}),
}),
})
const SubtractionModule = useStruct({
// ======== state ======== //
val: {
description: 'This is the Subtraction Module',
counter: 100,
},
// ======== reducer ======== //
set: ({ val }) => ({
decrement: () => produce(val, val => {
val.counter--
}),
}),
})
You can access the wrapped structs by prefix .str.
str.AdditionModule.blablastr.SubtractionModule.blablaconst Calculator = useStruct({
// ======== children ======== //
str: {
AdditionModule,
SubtractionModule,
},
// ======== state ======== //
val: {
description: 'This is the Calculator Struct',
},
// ======== selectors ======== //
get: ({ str }) => [{
additionCounter: () => str.AdditionModule.counter,
subtractionCounter: () => str.SubtractionModule.counter,
}, [str.AdditionModule.val, str.SubtractionModule.val]],
// ======== callbacks ======== //
act: ({ str }) => [{
incrementAndDecrementAllCounters: () => {
str.AdditionModule.set.increment()
str.SubtractionModule.set.decrement()
},
}, [str.AdditionModule.val, str.SubtractionModule.val]]
})
You can access the wrapped structs like as child property.
Calculator.AdditionModule.blablaCalculator.SubtractionModule.blablaexport default function Component() {
const SubtractionModule = useStruct({ ...inputs })
const AdditionModule = useStruct({ ...inputs })
const Calculator = useStruct({
str: {
AdditionModule,
SubtractionModule,
},
...inputs
})
return (
<div>
{/* ======== STATES ======== */}
<h1>Description: {Calculator.description}</h1>
<h3>Addition counter: {Calculator.AdditionModule.counter}</h3>
<h3>Subtraction counter: {Calculator.SubtractionModule.counter}</h3>
{/* ======== SELECTORS ======== */}
<h3>Addition counter: {Calculator.get.additionCounter()}</h3>
<h3>Subtraction counter: {Calculator.get.subtractionCounter()}</h3>
{/* ======== CALLBACKS ======== */}
<button onClick={() => Calculator.AdditionModule.set.increment()} >Increment</button>
<button onClick={() => Calculator.SubtractionModule.set.decrement()} >Decrement</button>
<button onClick={() => Calculator.incrementAndDecrementAllCounters()} >
Increment and decrement all counters
</button>
</div>
)
}
str, val, get, set, act are as a alias for thisIn a Class others properties and methods can be accessed by prefix this. Similarly in a struct sibling properties and methods can be accessed by prefixes str, val, get, set, act.
See this full example:
const Struct = useStruct({
// ======== children ======== //
str: {
...blabla
},
// ======== state ======== //
val: {
...blabla
},
// ======== getters ======== //
get: ({ str, val, get }) => [{
...blabla
}],
// ======== reducer ======== //
set: ({ str, val, get, set }) => ({
...blabla
}),
// ======== callbacks ======== //
act: ({ str, val, get, set, act }) => [{
...blabla
}],
})
The state of each useStruct can be easy persisted in local storage adding a key: 'STRUCT_NAME' and pst: true in the hook input. See:
// persisted struct
const Struct = useStruct({
key: 'STRUCT_NAME',
pst: true,
val: {
greetings: 'Hello World from localStorage :)',
},
})
Similarly to a Class a struct can extend many others structs and all their properties and methods will are DIRECTLY available for the extended struct WITHOUT PREFIX .str.
import useStruct from 'use-struct'
import produce from 'immer'
const AdditionModule = useStruct({
val: {
addCounter: 0,
},
set: ({ val }) => ({
increment: () => produce(val, val => {
val.addCounter++
})
}),
})
const SubtractionModule = useStruct({
val: {
subCounter: 100,
},
set: ({ val }) => ({
decrement: () => produce(val, val => {
val.subCounter--
}),
}),
})
const Calculator = useStruct({
ext: [
AdditionModule,
SubtractionModule
],
get: ({ val }) => [{
diffCounter: () => val.subCounter - val.addCounter
}],
act: ({ set }) => [{
incrementAndDecrementAllCounters: () => {
set.increment()
set.decrement()
},
}],
})
export default function Component() {
// ================ structs ================ //
const SubtractionModule = useStruct({ ...inputs })
const AdditionModule = useStruct({ ...inputs })
const Calculator = useStruct({
ext: [
AdditionModule,
SubtractionModule
],
...inputs
})
// ================ render ================ //
return (
<div>
{/* ======== STATES ======== */}
<h1>Addition Module Counter: {Calculator.addCounter}</h1>
<h1>Subtraction Module Counter: {Calculator.subCounter}</h1>
<h2>Diff between subCounter and addCounter {Calculator.get.diffCounter()}</h2>
{/* ======== CALLBACKS ======== */}
<button onClick={() => Calculator.incrementAndDecrementAllCounters()} >
increment and decrement all counters
</button>
</div>
)
}
As explained, set.increment(), set.decrement(), val.subCounter and val.addCounter are directly invoked by inside. And Calculator.addCounter and Calculator.subCounter are too directly invoked by outside.
In both cases looks that these props and methods was created by Calculator struct, but actually, their was created by extended modules and inherited by Calculator struct.
** To extend a struct not means create a deepClone and modify their copy, but means TO CONTROL IT DIRECTLY. All change of state will put definitely in all original states, not in deepClones. **
** In extends operations occurs a merge of props and methods. So be careful for not create nothing with the same name. If a match occurs, the wraper struct will override the features of the extension structs . **
val, Callbacks act and Wrapped Structs str can be accessed directly by view without the prefixs .val, .act, and .str.get and reducer set needs be prefixed (Inside or outside of the hook).Example with Hello World:
export default function Component() {
const Calculator = useStruct({ ...inputs })
return (
<div>
{/* ======== STATE ======== */}
<h1>Greetings (without .val prefix): {Calculator.greetings}</h1>
<h1>Greetings (with .val prefix): {Calculator.val.greetings}</h1>
{/* ======== CALLBACKS ======== */}
<button onClick={() => Calculator.sayHello()} >Say hello (without .act prefix)</button><br />
<button onClick={() => Calculator.act.sayHello()} >Say hello (with .act prefix)</button><br />
</div>
)
}
Don't create none props or methods with the following keywords: key, pst, ext, str, val, get, set and act. They are reserved keywords for this hook and if you do that, bad things can to happen.
FAQs
Manage local and global states with one hook.
We found that use-struct 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.

Research
A supply chain attack on Axios introduced a malicious dependency, plain-crypto-js@4.2.1, published minutes earlier and absent from the project’s GitHub releases.

Research
Malicious versions of the Telnyx Python SDK on PyPI delivered credential-stealing malware via a multi-stage supply chain attack.

Security News
TeamPCP is partnering with ransomware group Vect to turn open source supply chain attacks on tools like Trivy and LiteLLM into large-scale ransomware operations.