Research
Security News
Quasar RAT Disguised as an npm Package for Detecting Vulnerabilities in Ethereum Smart Contracts
Socket researchers uncover a malicious npm package posing as a tool for detecting vulnerabilities in Etherium smart contracts.
UI engine for web
import {createStore, createEvent, sample} from 'effector'
import {using, spec, h} from 'forest'
using(document.body, () => {
const {change, submit, $fields} = formModel()
h('section', () => {
spec({style: {width: '15em'}})
h('form', () => {
spec({
handler: {
config: {prevent: true},
on: {submit},
},
style: {
display: 'flex',
flexDirection: 'column',
},
})
h('input', {
attr: {placeholder: 'Username'},
handler: {input: change('username')},
})
h('input', {
attr: {type: 'password', placeholder: 'Password'},
classList: ['w-full', 'py-2', 'px-4'],
handler: {input: change('password')},
})
h('button', {
text: 'Submit',
attr: {
disabled: $fields.map(
fields => !(fields.username && fields.password),
),
},
})
})
h('section', () => {
spec({style: {marginTop: '1em'}})
h('div', {text: 'Reactive form debug:'})
h('pre', {text: $fields.map(stringify)})
})
})
})
function formModel() {
const changed = createEvent()
const submit = createEvent()
const $fields = createStore({}).on(changed, (fields, {name, value}) => ({
...fields,
[name]: value,
}))
const change = name => changed.prepend(e => ({name, value: e.target.value}))
sample({
source: $fields,
clock: submit,
fn: stringify,
}).watch(alert)
return {change, submit, $fields}
}
function stringify(values) {
return JSON.stringify(values, null, 2)
}
Start an application from given root dom node. Can accept forked Scope.
Set hydrate: true
to reuse root
html content (useful for ssr)
function using(root: DOMElement, fn: () => void): void
function using(
root: DOMElement,
config: {
fn: () => void
hydrate?: boolean
scope?: Scope
},
): void
Declare single dom element.
function h(tag: string, fn: () => void): void
function h(
tag: string,
config: {
attr?: PropertyMap
style?: PropertyMap
styleVar?: PropertyMap
classList?: ClassListMap | ClassListArray
data?: PropertyMap
text?: Property | Property[]
visible?: Store<boolean>
handler?:
| {[domEvent: string]: Event<any>}
| {
config: {
passive?: boolean
capture?: boolean
prevent?: boolean
stop?: boolean
}
on: {[domEvent: string]: Event<any>}
}
fn?: () => void
},
): void
See also: PropertyMap, Property
Config fields:
attr: add HTML attributes, e.g. class
or input's value
. {value: createStore('initial')}
will
become "value"="initial"
style: add inline styles. All style
objects will be merged to single style
html attribute. Object fields in
camel case will be converted to dash-style, e.g. {borderRadius: '3px'}
will become "style"="border-radius: 3px"
.
styleVar: add css variables to inline styles. {themeColor: createStore('red')}
will
become "style"="--themeColor: red"
classList: add class names to class
attribute. {active: true}
will become "class"="active"
, ['active', 'disabled']
will become "class"="active disabled"
and so on with Store
support.
data: add data attributes. Object
fields in camel case will be converted to dash-style, e.g. {buttonType: 'outline'}
will
become "data-button-type"="outline"
and might be queried in css in this way:
[data-button-type='outline'] {
}
text: add text to element as property or array of properties
visible: node will be presented in dom tree while store value is true
. Useful for conditional rendering
handler: add event handlers to dom node. In cases when preventDefault
or stopPropagation
is needed, extended
form with config object can be used
const click = createEvent<MouseEvent>()
h('button', {
text: 'Click me',
handler: {click},
})
h('a', {
text: 'Click me',
handler: {
config: {prevent: true},
on: {click},
},
})
Handler config fields:
- passive: event handler will be defined as passive
- capture: event handler will be defined with
capture: true
- prevent: call
preventDefault()
on trigger- stop: call
stopPropagation()
on trigger
Add new properties to dom element. Designed to call from h callbacks and has the same fields as
in h(tag, config)
. Can be called as many times as needed
function spec(config: {
attr?: PropertyMap
style?: PropertyMap
styleVar?: PropertyMap
classList?: ClassListMap | ClassListArray
data?: PropertyMap
text?: Property | Property[]
visible?: Store<boolean>
handler?:
| {[domEvent: string]: Event<any>}
| {
config: {
passive?: boolean
capture?: boolean
prevent?: boolean
stop?: boolean
}
on: {[domEvent: string]: Event<any>}
}
}): void
Property classList
has two forms, each optionally reactive:
const $isEnabled = createStore(true)
spec({classList: {first: true, second: $isEnabled}})
Be careful, each array item will be treated as a single class name, so it should not have a spaces.
const $class = createStore('active')
spec({classList: ['size-big', $class]})
If spec with classList called twice or more, all enabled classes will be merged in the order of appearance.
Also, classList
will be merged with static class
attribute:
h('div', {
attr: {class: 'first second'},
classList: ['third'],
fn() {
spec({classList: {fourth: true}})
},
})
// => <div class="first second third fourth"></div>
Render array of items from store
function list<T>(source: Store<T[]>, fn: (config: {store: Store<T>, key: Store<number>}) => void): void
function list<T>(config: {
source: Store<T[]>,
key: string
fields?: string[]
fn: (config: {store: Store<T>, key: Store<any>, fields: Store<any>[]}) => void): void
}): void
Config fields:
fields
as array of stores if provided. All fields are strongly typed and inferred from config definitionfn
as array of separate stores. Useful to
avoid store.map
and remap
callsMount one of given cases by selecting a specific one by the current value of the key
field of source
store value.
Type of store
in cases
functions will be inferred from a case type. Optional default case - __
(like
in split)
function variant<T>(config: {
source: Store<T>
key: string
cases: {[caseName: string]: ({store: Store<T>}) => void
}
}):
void
Generalized route is a combination of state and visibility status. fn
content will be mounted until visible
called
with source
value will return true
. In case of store in visible
field, content will be mounted while that store
contain true
. variant is shorthand for creating several routes at once
function route<T>(config: {
source: Store<T>
visible: ((value: T) => boolean) | Store<boolean>
fn: (config: {store: Store<T>}) => void
}): void
Use template literals to add text to dom node. Accept any properties
function text(words: TemplateStringsArray, ...values: Property[]): void
Example
const $username = createStore('guest')
h('h1', () => {
text`Hello ${$username}!`
})
Provide support for recursive templates. Can be called outside from using calls
function rec<T>(config: {store: Store<T>}): (config: {store: Store<T>}) => void
Allow defining and validate template outside from using calls.
function block(config: {fn: () => void}): () => void
Method from forest/server
to render given application to string. Can accept
forked Scope, in which case fn
children must be wrapped
in block to ensure that all units are created before fork call
function renderStatic(fn: () => void): Promise<string>
function renderStatic(config: {scope?: Scope; fn: () => void}): Promise<string>
Helper for retrieving value fields from single store. Shorthand for several store.map(val => val[fieldName])
calls.
Infer types when used with either single key or
with as const
: const [id, name] = remap(user, ['id', 'name'] as const)
function remap<T>(store: Store<T>, keys: string[]): Store<any>[]
function remap<T>(store: Store<T>, key: string): Store<any>
Helper for joining properties to single string with template literals. If
only plain values are passed, the method returns string
function val(words: TemplateStringsArray, ...values: Property[]): Store<string>
function val(words: TemplateStringsArray, ...values: PlainProperty[]): string
Example
const $store = createStore(10)
const a = 20
h('g', {
attr: {
transform: val`translate(${$store} ${a})`,
},
})
Value types accepted by methods, which write values to dom properties. Strings are written as is, numbers are converted
to strings, null
and false
mean no value (property deletion), true
is used when the specific property value is not
needed.
type PlainProperty = string | number | null | boolean
In most cases dom properties can be wrapped in stores, thereby making result value dynamic
type Property = PlainProperty | Store<PlainProperty>
Object with dom properties, possibly reactive
type PropertyMap = {[field: string]: Property}
Object with class names as keys and boolean values, possibly reactive
type ClassListMap = {[className: string]: Store<boolean> | boolean}
spec({
classList: {
'class-name': true,
'class-name-2': $booleanStore,
},
})
Array with class names, possibly reactive
type ClassListArray = Array<Store<string> | string>
spec({ classList: { classList: ['class-name', $stringStore] } })
FAQs
UI engine for web
The npm package forest receives a total of 3,846 weekly downloads. As such, forest popularity was classified as popular.
We found that forest demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 5 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
Socket researchers uncover a malicious npm package posing as a tool for detecting vulnerabilities in Etherium smart contracts.
Security News
Research
A supply chain attack on Rspack's npm packages injected cryptomining malware, potentially impacting thousands of developers.
Research
Security News
Socket researchers discovered a malware campaign on npm delivering the Skuld infostealer via typosquatted packages, exposing sensitive data.