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.
@starbeam/service
Advanced tools
A resource is a reactive constructor function with support for cleanup.
A resource is a reactive constructor function with support for cleanup.
import { Resource, use } from "@starbeam/resource";
import { LIFETIME } from "@starbeam/runtime";
const Stopwatch = Resource(({ on }) => {
const now = Cell(Date.now());
const timer = setInterval(() => {
now.set(Date.now());
}, 1000);
on.cleanup(() => {
clearInterval(timer);
});
return Formula(() => {
return new Intl.DateTimeFormat().format(now.current);
});
});
const lifetime = {};
const stopwatch = use(Stopwatch, { within: lifetime });
// later, when the lifetime is finalized...
LIFETIME.finalize(lifetime);
// the timer is cleared and `stopwatch` will stop updating
The Resource
function takes a resource constructor function and returns a
resource blueprint. A resource blueprint is instantiated with a lifetime and
returns a resource instance.
The resource constructor function is called with a resource run object that allows the resource constructor to register cleanup functions and create child resources.
The return value of the resource constructor is called the resource's instance value.
In this case, the resource instance (stopwatch
) is a reactive value that
evaluates to the current time, formatted using Intl.DateTimeFormat
.
Resource
function is called the resource constructor.A resource instance is always a stable reactive value. Its current value is the most recent return value of its resource constructor.
If a resource constructor returns a reactive value, the resource instance will assimilate that value. This means that the evaluated value of the resource instance will be the same as the evaluated value of the returned reactive value.
const Stopwatch = Resource(({ on }) => {
const now = Cell(Date.now());
const timer = setInterval(() => {
now.set(Date.now());
}, 1000);
on.cleanup(() => {
clearInterval(timer);
});
return Formula(() => {
return new Intl.DateTimeFormat().format(now.current);
});
});
In this case, the resource run has no reactive dependencies, but the
instance value is a formulaa that depends on the now
cell.
This means that instances of Stopwatch
will be reactive values that evaluate
to the current time, formatted using Intl.DateTimeFormat
, but the resource
constructor will only be evaluated once.
Let's see what would happen if you tried to model this as a single formula:
const now = Cell(Date.now());
let timer: number;
const Stopwatch = Formula(() => {
if (timer) clearInterval(timer);
timer = setInterval(() => {
now.set(Date.now());
}, 1000);
return new Intl.DateTimeFormat().format(now.current);
});
Since the code that sets up the interval and the code that uses it is inside the same formula, the formula invalidates every time the interval ticks, and the interval will be cleaned up and recreated on every tick. Definitely not what we wanted!
In addition to state that's associated with each resource run, a resource can have metadata that is shared across all resource runs.
interface CounterMetadata {
interval: Reactive<number>;
count: number;
}
const Counter = Resource(({ on }, { metadata }: CounterMetadata)) => {
const count = Cell(metadata.count);
const interval = setInterval(() => {
count.set(++metadata.count);
}, metadata.interval.current);
on.cleanup(() => {
clearInterval(interval);
});
return count;
};
When a resource has metadata, the initial value of the metadata is passed to the
use
function as the second argument:
const interval = Cell(1000);
const counter = use(Counter, {
within: lifetime,
metadata: {
interval,
count: 0,
},
});
Now, whenever the interval
cell changes, the resource constructor will be
re-evaluated, but the count
will be preserved.
In general, it's a good idea to avoid trying to maintain state across resource runs. It's sometimes necessary, but it maakes it possible to create resources with incoherent values, so you should try to think of an alternative if possible.
While assimilation is often useful, a resource's instance value does not need to be a reactive value. For example, it could be an instance of an object that stores a cell and exposes reactive getters:
const Stopwatch = Resource(({ on }) => {
const now = Cell(Date.now());
const timer = setInterval(() => {
now.set(Date.now());
}, 1000);
on.cleanup(() => {
clearInterval(timer);
});
return {
get now() {
return new Intl.DateTimeFormat().format(now.current);
},
};
});
Instances of Stopwatch
are methods with a reactive now
property which will
change when the interval ticks.
A resource instance is a long-lived reactive value that exists until the resource is finalized. A resource instance has a number of resource runs over its lifetime.
A resource is a reactive value that evaluates to its current value.
The steps for evaluating the constructor are:
Evaluate the resource's constructor formula. Evaluating the formula:
Run finalizers for the previous resource run.
A resource's dependencies are the combination of:
This means that a resource will invalidate if its instance formula invalidates or if its constructor invalidates.
Evaluating a resource:
The use
method makes it possible for a resource to instantiate other
resources within the lifetime of the parent resource.
There are several aspects of the design that make it easier to compose resources.
use
const Channel = Resource(({ use }) => {
const socket = use(Socket);
});
In this case, the Socket constructor is evaluated during Channel
's
constructor, and its instance is linked to the current run. Whenever the current
run is finalized, the Socket instance is finalized as well.
This use
works just like the imported use
function, except that you don't
specify a lifetime. Instead, the lifetime is the current resource run.
const socket = use(Socket, { within: owner });
const Channel = Resource(({ use }) => {
const socket = use(socket);
});
In this case, socket
's lifetime is linked to the current run. Whenever the
current run is finalized, the Socket instance is finalized as well unless it
was adopted by the next run.
ResourceList
uses this feature to dynamically create child resources that are automatically cleaned up when they aren't used anymore.
FAQs
A resource is a reactive constructor function with support for cleanup.
We found that @starbeam/service demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 2 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.