Research
Security News
Threat Actor Exposes Playbook for Exploiting npm to Build Blockchain-Powered Botnets
A threat actor's playbook for exploiting the npm ecosystem was exposed on the dark web, detailing how to build a blockchain-powered botnet.
@amadeus-it-group/tansu
Advanced tools
tansu is a lightweight, push-based framework-agnostic state management library. It borrows the ideas and APIs originally designed and implemented by Svelte stores and extends them with computed and batch.
Tansu is a lightweight, push-based framework-agnostic state management library.
It borrows the ideas and APIs originally designed and implemented by Svelte stores
and extends them with computed
and batch
.
Main characteristics:
Implementation wise, it is a tiny (1300 LOC) library without any external dependencies.
Tansu is designed to be and to remain fully compatible with Svelte. Nevertheless, it brings several improvements:
async
pipe out of the boxInteropObservable
interfaceInteropObservable
interface) can easily be used with Tansu (e.g. in Tansu computed
or derived
).With Svelte derived
function, it is mandatory to provide explicitly a static list of dependencies when the store is created, for example:
import {writable, derived} from 'svelte/store';
const quantity = writable(2);
const unitPrice = writable(10);
const totalPrice = derived([quantity, unitPrice], ([quantity, unitPrice]) => {
console.log("computing the total price");
return quantity > 0 ? quantity * unitPrice : 0
});
totalPrice.subscribe((totalPrice) => console.log(totalPrice)); // logs any change to totalPrice
quantity.set(0);
unitPrice.set(20);
The output of this example will be:
computing the total price
20
computing the total price
0
computing the total price
Note that even when the quantity is 0, the total is recomputed when the unit price changes.
In Tansu, while the same derived function is still available, the computed function is also available, with which it is only necessary to provide a function, and the list of dependencies is detected automatically and dynamically:
import {writable, computed} from '@amadeus-it-group/tansu';
const quantity = writable(2);
const unitPrice = writable(10);
const totalPrice = computed(() => {
console.log("computing the total price");
return quantity() > 0 ? quantity() * unitPrice() : 0
});
totalPrice.subscribe((totalPrice) => console.log(totalPrice)); // logs any change to totalPrice
quantity.set(0);
unitPrice.set(20);
Note that every store created with store = writable(value)
is a function that returns the value of the store. It is equivalent to get(store)
.
When getting the value of a store (either by calling the store as a function or by using get
) from a reactive context (such as the one created by computed
) the dependency on that store is recorded and any change to that store will trigger a recomputation.
The output of this example will be:
computing the total price
20
computing the total price
0
Note that when the quantity is 0, changes to the unit price no longer trigger an update of the total price because the list of dependencies is dynamic (and unitPrice()
is not called if quantity()
is 0
).
Depending on multiple stores can lead to some issues. Let's have a look at the following example:
import {writable, derived} from 'svelte/store';
const firstName = writable('Arsène');
const lastName = writable('Lupin');
const fullName = derived([firstName, lastName], ([a, b]) => `${a} ${b}`);
fullName.subscribe((name) => console.log(name)); // logs any change to fullName
firstName.set('Sherlock');
lastName.set('Holmes');
console.log('Process end');
The output of this example will be:
Arsène Lupin
Sherlock Lupin
Sherlock Holmes
Process end
The fullName store successively went through different states, including an inconsistent one, as Sherlock Lupin
does not exist! Even if it can be seen as just an intermediate state, it is fundamental for a state management to only manage consistent data in order to prevent issues and optimize the code.
In Tansu, the batch function is available to defer synchronously (another important point) the derived (or computed) calculation and solve all kind of multiple dependencies issues.
The previous example is resolved this way:
import {writable, derived, computed, batch} from '@amadeus-it-group/tansu';
const firstName = writable('Arsène');
const lastName = writable('Lupin');
const fullName = derived([firstName, lastName], ([a, b]) => `${a} ${b}`);
// note that the fullName store could alternatively be create with computed:
// const fullName = computed(() => `${firstName()} ${lastName()}`);
fullName.subscribe((name) => console.log(name)); // logs any change to fullName
batch(() => {
firstName.set('Sherlock');
lastName.set('Holmes');
});
console.log('Process end');
With the following output:
Arsène Lupin
Sherlock Holmes
Process end
You can add Tansu to your project by installing the @amadeus-it-group/tansu
package using your favorite package manager, ex.:
yarn add @amadeus-it-group/tansu
npm install @amadeus-it-group/tansu
Here is an example of an Angular component using a Tansu store:
import { Component } from "@angular/core";
import { AsyncPipe } from '@angular/common';
import { Store, computed, get } from "@amadeus-it-group/tansu";
// A store is a class extending Store from Tansu
class CounterStore extends Store<number> {
constructor() {
super(0); // initialize store's value (state)
}
// implement state manipulation logic as regular methods
increment() {
// create new state based on the current state
this.update(value => value + 1);
}
reset() {
// replace the entire state with a new value
this.set(0);
}
}
@Component({
selector: "my-app",
template: `
<button (click)="counter$.increment()">+</button> <br />
<!-- store values can be displayed in a template with the standard async pipe -->
Counter: {{ counter$ | async }} <br />
Double counter: {{ doubleCounter$ | async }} <br />
`,
standalone: true,
imports: [AsyncPipe]
})
export class App {
// A store can be instantiated directly or registered in the DI container
counter$ = new CounterStore();
// One can easily create computed values by specifying a transformation function
doubleCounter$ = computed(() => 2 * get(this.counter$));
}
While being fairly minimal, this example demonstrates most of the Tansu APIs.
Check the documentation for the complete API and more usage examples.
Please check the DEVELOPER.md for documentation on building and testing the project on your local development machine.
FAQs
tansu is a lightweight, push-based framework-agnostic state management library. It borrows the ideas and APIs originally designed and implemented by Svelte stores and extends them with computed and batch.
We found that @amadeus-it-group/tansu demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 3 open source maintainers 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
Security News
A threat actor's playbook for exploiting the npm ecosystem was exposed on the dark web, detailing how to build a blockchain-powered botnet.
Security News
NVD’s backlog surpasses 20,000 CVEs as analysis slows and NIST announces new system updates to address ongoing delays.
Security News
Research
A malicious npm package disguised as a WhatsApp client is exploiting authentication flows with a remote kill switch to exfiltrate data and destroy files.