
Security News
Browserslist-rs Gets Major Refactor, Cutting Binary Size by Over 1MB
Browserslist-rs now uses static data to reduce binary size by over 1MB, improving memory use and performance for Rust-based frontend tools.
static-tree
Advanced tools
This package is part of the static-tree
monorepo
npm install static-tree
yarn add static-tree
pnpm add static-tree
Use the idiomatic tBuild
to create a static tree. See tBuild for examples and options.
import { tBuild } from 'static-tree';
const { node: api } = tBuild('api', { // you can rename node here to whatever
pathResolver: () => 'https://api.domain.example',
build: (builder) =>
builder
.addChild('auth', {
build: (builder) =>
builder
.addChild('logout')
.addChild('oauth', {
build: (builder) => builder.addChild('google').addChild('discord'),
//...
})
})
.addChild('camelCaseKey', {
pathResolver: () => 'camel-case-key',
})
.addChild(':organization', { // notice some dynamic path here
pathResolver: (_node, arg) => arg,
build: (builder) =>
builder.addChild(':user', {
pathResolver: (_node, arg) => arg,
}),
}),
});
The declaration above will produce this tree structure:
api (resolve statically to 'https://api.domain.example' at runtime)
|
|-- auth
. |-- logout
. |-- oauth
. |
. |-- google
. |-- discord
|-- camelCaseKey (resolved statically to 'camel-case-key' at runtime)
|-- :organization (resolved dynamically to the given argument at runtime)
|
|-- :user (resolved dynamically to the given argument at runtime)
Access type-safe nested children
root.auth.oauth.google.$.path();
// -> 'https://api.domain.example/auth/oauth/google'
// note that ":" here has no special effect
// just for easier recognition as dynamic (think backend router system)
root[':organization'][':user'].$.path({
args: {
':organization': 'test-org',
':user': 'test-user',
}
});
// -> 'https://api.domain.example/test-org/test-user'
root.auth.logout.$.depth(); // -> 3
root.auth.oauth.discord.$.root() // -> point back to root node
the
$
getter returns the TNodePublicApi collection of methods.
Access type-safe data
root.$.data(); // -> { some: 'data' }
root.nestedChild.$.data().nestedChildData; // -> 101
This repo includes a full api extracted documentation generated by @microsoft/api-extractor & @microsoft/api-documenter. Please refer to said docs for examples and details.
Terminology | Description |
---|---|
static tree | a tree whose nodes are declared at build time and not likely to change at runtime |
TNode | a node of the static tree with optional inner TNodeData, optional parent, and zero or more children |
ExtendedTNode | a TNode with children inline as properties for better DX |
ExtendedTNodeBuilder | type-safe builder for ExtendedTNode |
tBuild | functional wrapper for ExtendedTNodeBuilder |
This package was derived from the solution to a specific problem I encountered frequently. Consider having this "config" object:
const AppConfig = {
urls: {
web: 'https://domain.example',
api: {
index: 'https://api.domain.example',
auth: {
index: '/auth',
logout: '/logout',
oauth: {
index: '/oauth',
google: '/google',
// ...
},
},
},
},
};
To get a full api url of google oauth, we have to do quite a lot:
const { api: { auth, index } } = AppConfig.urls;
const path = index + auth.index + auth.index.oauth.index + auth.oauth.google;
Already there are some problems:
Verbosity: lots of reference to get to something, more typing equals more typos equals less productive time.
The inconsistency of the config structure: some path will require an object index
, some path is just a string. We could refactor to something more predictable, although i think we can agree that this would quickly get out of hand and is very disorienting to look at:
const AppConfig = {
urls: {
web: {
base: 'https://domain.example',
paths: {},
},
api: {
base: 'https://api.domain.example',
paths: {
auth: {
base: '/auth',
paths: {
logout: {
base: '/logout',
},
// ...
},
},
},
},
},
};
Introducing static-tree
, arguably a better alternative to the above.
import { tBuild } from 'static-tree';
const { node: api } = tBuild('api', {
pathResolver: () => 'https://api.domain.example',
build: (builder) => builder
.addChild('auth', {
build: (builder) => builder
.addChild('logout')
.addChild('oauth', {
build: (builder) => builder
.addChild('google')
.addChild('discord'),
//...
}),
}),
});
You might say, why the ugly builder pattern? Because I have not figured out any other pattern that allows the same level of type-safety. It seems like a lot for what would be an object declaration, but consider what we can do now:
api.auth.oauth.google.$.path(); // -> 'https://api.domain.example/auth/oauth/google'
Even more cool things (and perhaps more in the future if we need to extend the api):
api.auth.oauth.google.$.path({ depth: 2 }); // -> 'oauth/google'
api.auth.oauth.google.$.path({ depth: -2 }); // -> 'https://api.domain.example/auth'
FAQs
Zero dependency builder for strongly typed static tree
We found that static-tree 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
Browserslist-rs now uses static data to reduce binary size by over 1MB, improving memory use and performance for Rust-based frontend tools.
Research
Security News
Eight new malicious Firefox extensions impersonate games, steal OAuth tokens, hijack sessions, and exploit browser permissions to spy on users.
Security News
The official Go SDK for the Model Context Protocol is in development, with a stable, production-ready release expected by August 2025.