
Research
Security News
Lazarus Strikes npm Again with New Wave of Malicious Packages
The Socket Research Team has discovered six new malicious npm packages linked to North Koreaโs Lazarus Group, designed to steal credentials and deploy backdoors.
@benev/slate
Advanced tools
@benev/slate
๐ง prerelease wip under constructon subject to change
npm i @benev/slate
import {prepare_frontend, Context} from "@benev/slate"
export const {carbon, oxygen, obsidian, quartz} = (
prepare_frontend(new class extends Context {
theme = css`
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
`
})
)
import {html, css} from "@benev/slate"
you can create custom html elements that work in plain html or any web framework.
const styles = css`span {color: yellow}`
export const MyCarbon = carbon({styles}, use => {
const count = use.signal(0)
const increment = () => count.value++
return html`
<span>${count}</span>
<button @click=${increment}>increment</button>
`
})
export const MyOxygen = oxygen(use => {
const count = use.signal(0)
const increment = () => count.value++
return html`
<span>${count}</span>
<button @click=${increment}>increment</button>
`
})
import {register_to_dom} from "@benev/slate"
register_to_dom({
MyCarbon,
MyOxygen,
})
<section>
<my-carbon></my-carbon>
<my-oxygen></my-oxygen>
</section>
views are just like components, but are not registered to the dom as custom html elements.
instead, they are used via javascript.
you import them, and inject them into your lit-html templates.
they accept js parameters called props
, and are fully typescript-typed.
const styles = css`span {color: yellow}`
export const MyObsidian = obsidian({styles}, use => (start: number) => {
const count = use.signal(start)
const increment = () => count.value++
return html`
<span>${count}</span>
<button @click=${increment}>increment</button>
`
})
auto_exportparts
is enabled by default.
part
attribute, then it will automatically re-export all internal parts, using the part as a prefix.::part(search-input-icon)
.export const MyQuartz = quartz(use => (start: number) => {
const count = use.signal(start)
const increment = () => count.value++
return html`
<span>${count}</span>
<button @click=${increment}>increment</button>
`
})
html`<div>${MyQuartz(123)}</div>`
html`<div>${MyObsidian([123])}</div>`
<obsidian-view>
component, which is where the shadow root is attachedhtml`
<div>
${MyObsidian([123], {
content: html`<p>slotted content</p>`,
auto_exportparts: true,
attrs: {
part: "cool",
"data-whatever": true,
},
})}
</div>
`
use
hooksconst [count, setCount] = use.state(0)
const increment = () => setCount(count + 1)
const count = use.signal(0)
const increment = () => count.value++
you can directly inject the whole signal into html
html`<span>${count}</span>`
const count = use.signal(2)
const tripled = use.computed(() => count.value * 3)
console.log(tripled.value) //> 6
const count = use.op()
count.run(async() => fetchCount("/count"))
const state = use.flatstate({count: 0})
const increment = () => state.count++
use.setup(() => {
const interval = setInterval(increment, 1000)
return () => clearInterval(interval)
})
const random_number = use.prepare(() => Math.random())
// wait for all flatstate reactions to complete
await use.context.flat.wait
// wait for all signal tower reactons to complete
await use.context.tower.wait
by default, context has theme
, tower
, and flat
, but you specify your own context in prepare_frontend
, so you can put any app-level state in there that you might wantuse
accessuse.element.querySelector("p")
use.shadow.querySelector("slot")
const attrs = use.attrs({
start: Number,
label: String,
["data-active"]: Boolean,
})
set them like normal js properties
attrs.start = 123
attrs.label = "hello"
attrs["data-active"] = true
get them like normal js properties
console.log(attrs.start) // 123
console.log(attrs.label) // "hello"
console.log(attrs["data-active"]) // true
components rerender when any attributes change from outsideexport const MyGold = component(context => class extends GoldElement {
static styles = css`span {color: blue}`
#state = context.flat.state({
count: 0,
})
render() {
return html`
<span>${this.#state.count}</span>
<button @click=${() => this.#state.count++}>gold</button>
`
}
})
GoldElement
is a shadow-dom component base classSilverElement
is a light-dom component base classmixins
Attributes.base(this as BaseElement, {label: String})
to create attribute accessorscomponent
from prepare_frontend
to mixin the theme and state management reactivityprepare_frontend
vs deferred_frontend
prepare_frontend
"bakes" your app context into the component and view functions at import-time, "before" your components and views are defined. this makes your developer experience simple and pleasant for most cases.deferred_frontend
is an alternative designed to solve this problem by deferring the passing of context to each individual component and view.provide
function which makes it easy to pass context to a group of views for that purpose.prepare_frontend
and modifying your context
at runtime, but before you register_to_dom your components
context.theme
at runtime before register_to_domif you're using slate's frontend components and views, you'll probably be using these utilities via the use
hooks, which will provide a better developer experience.
however, the following utilities are little libraries in their own right, and can be used in a standalone capacity.
signals are a simple form of state management.
this implementation is inspired by preact signals.
import {SignalTower} from "@benev/slate"
const tower = new SignalTower()
const count = tower.signal(0)
const greeting = tower.signal("hello")
count.value++
greeting.value = "bonjour"
console.log(count.value) //> 1
console.log(greeting.value) //> "bonjour"
tower.track(() => console.log("doubled", count.value * 2))
//> doubled 2
count.value = 2
//> doubled 4
html`<p>count is ${count}</p>`
const json = tower.op<MyJson>()
console.log(json.loading) //> true
await json.run(async() => {
const data = await fetch_remote_data()
return JSON.parse(data)
})
console.log(json.ready) //> true
console.log(json.payload) //> {"your": "json data"}
count.value = 1
const tripled = tower.computed(() => count.value * 3)
console.log(tripled.value) //> 3
const tripled = tower.computed(() => count.value * 3)
console.log(tripled.value) //> 3
count.value = 10
console.log(tripled.value) //> 3 (too soon!)
await tower.wait
console.log(tripled.value) //> 30 (there we go)
flatstate help you create state objects and react when properties change.
flatstate is inspired by mobx and snapstate, but designed to be super simple: flatstate only works on flat state objects, only the direct properties of state objects are tracked for reactivity.
import {Flat} from "@benev/slate"
const flat = new Flat()
// what happens in this flat, stays in this flat.
// you probably only want one for your whole app.
const state = flat.state({count: 0})
flat.reaction(() => console.log(state.count))
//> 0
state.count++
//> 1
const flat = new Flat()
const state = flat.state({amount: 100})
state.amount = 101
console.log(state.amount) //> 100 (old value)
await flat.wait
console.log(state.amount) //> 101 (now it's ready)
const stop = flat.reaction(() => console.log(state.count))
stop() // end this particular reaction
// clear all reactions on this flat instance
flat.clear()
flat.reaction(() => console.log(state.count))
flat.reaction(
() => ({count: state.count}),
({count}) => console.log(count),
)
flat.deepReaction(() => console.log(state.count))
.auto
and .manual
reactions
discovery
and debounce
(you can turn off the debouncer)const flat1 = new Flat()
const flat2 = new Flat()
const state = flat.state({count: 0})
const rstate = Flat.readonly(state)
state.count = 1
await flat.wait
console.log(rstate.count) //> 1
rstate.count = 2 // !! ReadonlyError !!
import {apply} from "@benev/slate"
const MyElement2 = mixin.flat(flat)(MyElement)
// can also be a class decorator
const elements2 = apply.flat(flat)(elements)
utility for ui loading/error/ready states.
useful for implementing async operations that involve loading indicators.
you get a better dev-experience if you use ops via signals, but here is the documentation for plain ops on their own, without signals.
import {Op} from "@benev/slate"
Op.loading()
//= {mode: "loading"}
Op.error("a fail occurred")
//= {mode: "error", reason: "a fail occurred"}
Op.ready(123)
//= {mode: "ready", payload: 123}
let my_op = Op.loading()
await Op.run(op => my_op = op, async() => {
await nap(1000)
return 123
})
const count = use.op()
count.run(async() => {
await sleep(1000)
return 123
})
// type for op in any mode
// v
function example(op: Op.Any<number>) {
// branching based on the op's mode
Op.select(op, {
loading: () => console.log("op is loading"),
error: reason => console.log("op is error", reason),
ready: payload => console.log("op is ready", payload)
})
const payload = Op.payload(op)
// if the mode=ready, return the payload
// otherwise, return undefined
}
// bad
register_to_dom(
apply.signals(signals)(
apply.flat(flat)(
apply.css(theme)(
requirement.provide(context)(elements)
)
)
)
)
import {Pipe} from "@benev/slate"
// good
Pipe.with(elements)
.to(requirement.provide(context))
.to(apply.css(theme))
.to(apply.flat(flat))
.to(apply.signals(signals))
.to(register_to_dom)
FAQs
frontend web stuff
The npm package @benev/slate receives a total of 96 weekly downloads. As such, @benev/slate popularity was classified as not popular.
We found that @benev/slate demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago.ย It has 0 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
The Socket Research Team has discovered six new malicious npm packages linked to North Koreaโs Lazarus Group, designed to steal credentials and deploy backdoors.
Security News
Socket CEO Feross Aboukhadijeh discusses the open web, open source security, and how Socket tackles software supply chain attacks on The Pair Program podcast.
Security News
Opengrep continues building momentum with the alpha release of its Playground tool, demonstrating the project's rapid evolution just two months after its initial launch.